812 lines
24 KiB
Python
812 lines
24 KiB
Python
import codecs
|
|
|
|
import gobject
|
|
import gtk
|
|
import gtk.gdk
|
|
import pango
|
|
|
|
from conf import conf
|
|
import parse_mirc
|
|
import windows
|
|
|
|
import servers
|
|
|
|
# Window activity Constants
|
|
HILIT = 'h'
|
|
TEXT ='t'
|
|
EVENT = 'e'
|
|
|
|
ACTIVITY_MARKUP = {
|
|
HILIT: "<span style='italic' foreground='#00F'>%s</span>",
|
|
TEXT: "<span foreground='#ca0000'>%s</span>",
|
|
EVENT: "<span foreground='#363'>%s</span>",
|
|
}
|
|
|
|
# This holds all tags for all windows ever
|
|
tag_table = gtk.TextTagTable()
|
|
|
|
link_tag = gtk.TextTag('link')
|
|
link_tag.set_property('underline', pango.UNDERLINE_SINGLE)
|
|
|
|
indent_tag = gtk.TextTag('indent')
|
|
indent_tag.set_property('indent', -20)
|
|
|
|
tag_table.add(link_tag)
|
|
tag_table.add(indent_tag)
|
|
|
|
#FIXME: MEH hates dictionaries, they remind him of the bad words
|
|
styles = {}
|
|
|
|
def style_me(widget, style):
|
|
widget.set_style(styles.get(style))
|
|
|
|
def set_style(widget_name, style):
|
|
if style:
|
|
# FIXME: find a better way...
|
|
dummy = gtk.Label()
|
|
dummy.set_style(None)
|
|
|
|
def apply_style_fg(value):
|
|
dummy.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse(value))
|
|
|
|
def apply_style_bg(value):
|
|
dummy.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(value))
|
|
|
|
def apply_style_font(value):
|
|
dummy.modify_font(pango.FontDescription(value))
|
|
|
|
style_functions = (
|
|
('fg', apply_style_fg),
|
|
('bg', apply_style_bg),
|
|
('font', apply_style_font),
|
|
)
|
|
|
|
for name, f in style_functions:
|
|
if name in style:
|
|
f(style[name])
|
|
|
|
style = dummy.rc_get_style()
|
|
else:
|
|
style = None
|
|
|
|
styles[widget_name] = style
|
|
|
|
def menu_from_list(alist):
|
|
while alist and not alist[-1]:
|
|
alist.pop(-1)
|
|
|
|
last = None
|
|
for item in alist:
|
|
if item != last:
|
|
if item:
|
|
if len(item) == 2:
|
|
name, function = item
|
|
|
|
menuitem = gtk.ImageMenuItem(name)
|
|
|
|
elif len(item) == 3:
|
|
name, stock_id, function = item
|
|
|
|
if isinstance(stock_id, bool):
|
|
menuitem = gtk.CheckMenuItem(name)
|
|
menuitem.set_active(stock_id)
|
|
else:
|
|
menuitem = gtk.ImageMenuItem(stock_id)
|
|
|
|
if isinstance(function, list):
|
|
submenu = gtk.Menu()
|
|
for subitem in menu_from_list(function):
|
|
submenu.append(subitem)
|
|
menuitem.set_submenu(submenu)
|
|
|
|
else:
|
|
menuitem.connect("activate", lambda a, f: f(), function)
|
|
|
|
yield menuitem
|
|
|
|
else:
|
|
yield gtk.SeparatorMenuItem()
|
|
|
|
last = item
|
|
|
|
class Nicklist(gtk.TreeView):
|
|
def click(self, event):
|
|
if event.button == 3:
|
|
x, y = event.get_coords()
|
|
|
|
(data,), path, x, y = self.get_path_at_pos(int(x), int(y))
|
|
|
|
c_data = self.events.data(window=self.win, data=self[data], menu=[])
|
|
|
|
self.events.trigger("ListRightClick", c_data)
|
|
|
|
if c_data.menu:
|
|
menu = gtk.Menu()
|
|
for item in menu_from_list(c_data.menu):
|
|
menu.append(item)
|
|
menu.show_all()
|
|
menu.popup(None, None, None, event.button, event.time)
|
|
|
|
elif event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
|
|
x, y = event.get_coords()
|
|
|
|
(data,), path, x, y = self.get_path_at_pos(int(x), int(y))
|
|
|
|
self.events.trigger("ListDoubleClick", window=self.win, target=self[data])
|
|
|
|
def __getitem__(self, pos):
|
|
return self.get_model()[pos][0]
|
|
|
|
def __setitem__(self, pos, name_markup):
|
|
realname, markedupname, sortkey = name_markup
|
|
|
|
self.get_model()[pos] = realname, markedupname, sortkey
|
|
|
|
def __len__(self):
|
|
return len(self.get_model())
|
|
|
|
def index(self, item):
|
|
for i, x in enumerate(self):
|
|
if x == item:
|
|
return i
|
|
|
|
return -1
|
|
|
|
def append(self, realname, markedupname, sortkey):
|
|
self.get_model().append((realname, markedupname, sortkey))
|
|
|
|
def insert(self, pos, realname, markedupname, sortkey):
|
|
self.get_model().insert(pos, (realname, markedupname, sortkey))
|
|
|
|
def replace(self, names):
|
|
self.set_model(gtk.ListStore(str, str, str))
|
|
|
|
self.insert_column_with_attributes(
|
|
0, '', gtk.CellRendererText(), markup=1
|
|
).set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
|
|
|
|
for name in names:
|
|
self.append(*name)
|
|
|
|
self.get_model().set_sort_column_id(2, gtk.SORT_ASCENDING)
|
|
|
|
def remove(self, realname):
|
|
index = self.index(realname)
|
|
|
|
if index == -1:
|
|
raise ValueError
|
|
|
|
self.get_model().remove(self.get_model().iter_nth_child(None, index))
|
|
|
|
def clear(self):
|
|
self.get_model().clear()
|
|
|
|
def __iter__(self):
|
|
return (r[0] for r in self.get_model())
|
|
|
|
def __init__(self, window, core):
|
|
self.win = window
|
|
self.core = core
|
|
self.events = core.events
|
|
|
|
gtk.TreeView.__init__(self)
|
|
|
|
self.replace(())
|
|
|
|
self.set_headers_visible(False)
|
|
self.set_property("fixed-height-mode", True)
|
|
self.connect("button-press-event", Nicklist.click)
|
|
self.connect_after("button-release-event", lambda *a: True)
|
|
|
|
style_me(self, "nicklist")
|
|
|
|
# Label used to display/edit your current nick on a network
|
|
class NickEditor(gtk.EventBox):
|
|
def nick_change(self, entry):
|
|
oldnick, newnick = self.label.get_text(), entry.get_text()
|
|
|
|
if newnick and newnick != oldnick:
|
|
self.events.run('nick %s' % newnick, self.win, self.win.network)
|
|
|
|
self.win.input.grab_focus()
|
|
|
|
def update(self, nick=None):
|
|
self.label.set_text(nick or self.win.network.me)
|
|
|
|
def to_edit_mode(self, widget, event):
|
|
if self.label not in self.get_children():
|
|
return
|
|
|
|
if getattr(event, 'button', None) == 3:
|
|
c_data = self.events.data(window=self.win, menu=[])
|
|
self.events.trigger("NickEditMenu", c_data)
|
|
|
|
if c_data.menu:
|
|
menu = gtk.Menu()
|
|
for item in menu_from_list(c_data.menu):
|
|
menu.append(item)
|
|
menu.show_all()
|
|
menu.popup(None, None, None, event.button, event.time)
|
|
|
|
else:
|
|
entry = gtk.Entry()
|
|
entry.set_text(self.label.get_text())
|
|
entry.connect('activate', self.nick_change)
|
|
entry.connect('focus-out-event', self.to_show_mode)
|
|
|
|
self.remove(self.label)
|
|
self.add(entry)
|
|
self.window.set_cursor(None)
|
|
|
|
entry.show()
|
|
entry.grab_focus()
|
|
|
|
def to_show_mode(self, widget, event):
|
|
self.remove(widget)
|
|
self.add(self.label)
|
|
self.win.input.grab_focus()
|
|
self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM))
|
|
|
|
def __init__(self, window, core):
|
|
gtk.EventBox.__init__(self)
|
|
self.events = core.events
|
|
self.win = window
|
|
|
|
self.label = gtk.Label()
|
|
self.label.set_padding(5, 0)
|
|
self.add(self.label)
|
|
|
|
self.connect("button-press-event", self.to_edit_mode)
|
|
|
|
self.update()
|
|
|
|
self.connect(
|
|
"realize",
|
|
lambda *a: self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM))
|
|
)
|
|
|
|
# The entry which you type in to send messages
|
|
class TextInput(gtk.Entry):
|
|
# Generates an input event
|
|
def entered_text(self, ctrl):
|
|
#for a in globals():
|
|
# print a
|
|
#print events.__file__
|
|
#self.core.run_command(self.text)
|
|
for line in self.text.splitlines():
|
|
if line:
|
|
e_data = self.events.data(
|
|
window=self.win, network=self.win.network,
|
|
text=line, ctrl=ctrl
|
|
)
|
|
self.events.trigger('Input', e_data)
|
|
if not e_data.done:
|
|
self.events.run(line, self.win, self.win.network)
|
|
|
|
self.text = ''
|
|
|
|
def _set_selection(self, s):
|
|
if s:
|
|
self.select_region(*s)
|
|
else:
|
|
self.select_region(self.cursor, self.cursor)
|
|
|
|
#some nice toys for the scriptors
|
|
text = property(gtk.Entry.get_text, gtk.Entry.set_text)
|
|
cursor = property(gtk.Entry.get_position, gtk.Entry.set_position)
|
|
selection = property(gtk.Entry.get_selection_bounds, _set_selection)
|
|
|
|
def insert(self, text):
|
|
self.do_insert_at_cursor(self, text)
|
|
|
|
#hack to stop it selecting the text when we focus
|
|
def do_grab_focus(self):
|
|
temp = self.text, (self.selection or (self.cursor,)*2)
|
|
self.text = ''
|
|
gtk.Entry.do_grab_focus(self)
|
|
self.text, self.selection = temp
|
|
|
|
def keypress(self, event):
|
|
keychar = (
|
|
(gtk.gdk.CONTROL_MASK, '^'),
|
|
(gtk.gdk.SHIFT_MASK, '+'),
|
|
(gtk.gdk.MOD1_MASK, '!')
|
|
)
|
|
|
|
key = ''
|
|
for keymod, char in keychar:
|
|
# we make this an int, because otherwise it leaks
|
|
if int(event.state) & keymod:
|
|
key += char
|
|
key += gtk.gdk.keyval_name(event.keyval)
|
|
|
|
self.events.trigger('KeyPress', key=key, string=event.string, window=self.win)
|
|
|
|
if key == "^Return":
|
|
self.entered_text(True)
|
|
|
|
up = gtk.gdk.keyval_from_name("Up")
|
|
down = gtk.gdk.keyval_from_name("Down")
|
|
tab = gtk.gdk.keyval_from_name("Tab")
|
|
|
|
return event.keyval in (up, down, tab)
|
|
|
|
def __init__(self, window, core):
|
|
gtk.Entry.__init__(self)
|
|
self.events = core.events
|
|
self.core = core
|
|
self.win = window
|
|
|
|
# we don't want key events to propogate so we stop them in connect_after
|
|
self.connect('key-press-event', TextInput.keypress)
|
|
self.connect_after('key-press-event', lambda *a: True)
|
|
|
|
self.connect('activate', TextInput.entered_text, False)
|
|
|
|
gobject.type_register(TextInput)
|
|
|
|
def prop_to_gtk(textview, (prop, val)):
|
|
if val == parse_mirc.BOLD:
|
|
val = pango.WEIGHT_BOLD
|
|
|
|
elif val == parse_mirc.UNDERLINE:
|
|
val = pango.UNDERLINE_SINGLE
|
|
|
|
return {prop: val}
|
|
|
|
def word_from_pos(text, pos):
|
|
if text[pos] == ' ':
|
|
return ' ', pos, pos+1
|
|
|
|
else:
|
|
fr = text[:pos].split(" ")[-1]
|
|
to = text[pos:].split(" ")[0]
|
|
|
|
return fr + to, pos - len(fr), pos + len(to)
|
|
|
|
def get_iter_at_coords(view, x, y):
|
|
return view.get_iter_at_location(
|
|
*view.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, int(x), int(y))
|
|
)
|
|
|
|
def get_event_at_iter(view, iter, core):
|
|
buffer = view.get_buffer()
|
|
|
|
line_strt = buffer.get_iter_at_line(iter.get_line())
|
|
line_end = line_strt.copy()
|
|
line_end.forward_lines(1)
|
|
|
|
pos = iter.get_line_offset()
|
|
|
|
#Caveat: text must be a unicode string, not utf-8 encoded; otherwise our
|
|
# offsets will be off when we use anything outside 7-bit ascii
|
|
#gtk.TextIter.get_text returns unicode but gtk.TextBuffer.get_text does not
|
|
text = line_strt.get_text(line_end).rstrip("\n")
|
|
|
|
word, fr, to = word_from_pos(text, pos)
|
|
|
|
return core.events.data(
|
|
window=view.win, pos=pos, text=text,
|
|
target=word, target_fr=fr, target_to=to,
|
|
)
|
|
|
|
class TextOutput(gtk.TextView):
|
|
def copy(self):
|
|
startend = self.get_buffer().get_selection_bounds()
|
|
|
|
tagsandtext = []
|
|
if startend:
|
|
start, end = startend
|
|
|
|
while not start.equal(end):
|
|
tags_at_iter = {}
|
|
for tag in start.get_tags():
|
|
try:
|
|
tagname, tagval = eval(tag.get_property('name'))
|
|
tags_at_iter[tagname] = tagval
|
|
except NameError:
|
|
continue
|
|
|
|
tagsandtext.append((dict(tags_at_iter), start.get_char()))
|
|
start.forward_char()
|
|
|
|
text = parse_mirc.unparse_mirc(tagsandtext)
|
|
|
|
gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD).set_text(text)
|
|
gtk.clipboard_get(gtk.gdk.SELECTION_PRIMARY).set_text(text)
|
|
|
|
return text
|
|
|
|
def clear(self):
|
|
self.get_buffer().set_text('')
|
|
|
|
def get_y(self):
|
|
rect = self.get_visible_rect()
|
|
return rect.y
|
|
|
|
def set_y(self,y):
|
|
iter = self.get_iter_at_location(0, y)
|
|
if self.get_iter_location(iter).y < y:
|
|
self.forward_display_line(iter)
|
|
yalign = float(self.get_iter_location(iter).y-y)/self.height
|
|
self.scroll_to_iter(iter, 0, True, 0, yalign)
|
|
|
|
self.check_autoscroll()
|
|
|
|
def get_ymax(self):
|
|
buffer = self.get_buffer()
|
|
return sum(self.get_line_yrange(buffer.get_end_iter())) - self.height
|
|
|
|
def get_height(self):
|
|
return self.get_visible_rect().height
|
|
|
|
y = property(get_y, set_y)
|
|
ymax = property(get_ymax)
|
|
height = property(get_height)
|
|
|
|
# the unknowing print weird things to our text widget function
|
|
def write(self, text, line_ending='\n', fg=None):
|
|
if not isinstance(text, unicode):
|
|
try:
|
|
text = codecs.utf_8_decode(text)[0]
|
|
except:
|
|
text = codecs.latin_1_decode(text)[0]
|
|
tags, text = parse_mirc.parse_mirc(text)
|
|
|
|
if fg:
|
|
tags.append({'data': ("foreground", isinstance(fg, basestring) and ('#%s'%fg) or parse_mirc.get_mirc_color(fg)), 'from': 0, 'to': len(text)})
|
|
|
|
buffer = self.get_buffer()
|
|
|
|
cc = buffer.get_char_count()
|
|
|
|
buffer.insert_with_tags_by_name(
|
|
buffer.get_end_iter(),
|
|
text + line_ending,
|
|
'indent'
|
|
)
|
|
|
|
for tag in tags:
|
|
tag_name = str(tag['data'])
|
|
|
|
if not tag_table.lookup(tag_name):
|
|
buffer.create_tag(tag_name, **prop_to_gtk(self, tag['data']))
|
|
|
|
buffer.apply_tag_by_name(
|
|
tag_name,
|
|
buffer.get_iter_at_offset(tag['from'] + cc),
|
|
buffer.get_iter_at_offset(tag['to'] + cc)
|
|
)
|
|
|
|
def popup(self, menu):
|
|
hover_iter = get_iter_at_coords(self, *self.hover_coords)
|
|
|
|
menuitems = []
|
|
if not hover_iter.ends_line():
|
|
c_data = get_event_at_iter(self, hover_iter)
|
|
c_data.menu = []
|
|
|
|
self.events.trigger("RightClick", c_data)
|
|
|
|
menuitems = c_data.menu
|
|
|
|
if not menuitems:
|
|
c_data = self.events.data(menu=[])
|
|
self.events.trigger("MainMenu", c_data)
|
|
|
|
menuitems = c_data.menu
|
|
|
|
for child in menu.get_children():
|
|
menu.remove(child)
|
|
|
|
for item in menu_from_list(menuitems):
|
|
menu.append(item)
|
|
|
|
menu.show_all()
|
|
|
|
def mousedown(self, event):
|
|
if event.button == 3:
|
|
self.hover_coords = event.get_coords()
|
|
|
|
def mouseup(self, event):
|
|
if not self.get_buffer().get_selection_bounds():
|
|
if event.button == 1:
|
|
hover_iter = get_iter_at_coords(self, event.x, event.y)
|
|
|
|
if not hover_iter.ends_line():
|
|
c_data = get_event_at_iter(self, hover_iter, self.core)
|
|
|
|
self.events.trigger("Click", c_data)
|
|
|
|
if self.is_focus():
|
|
self.win.focus()
|
|
|
|
def clear_hover(self, _event=None):
|
|
buffer = self.get_buffer()
|
|
|
|
for fr, to in self.linking:
|
|
buffer.remove_tag_by_name(
|
|
"link",
|
|
buffer.get_iter_at_mark(fr),
|
|
buffer.get_iter_at_mark(to)
|
|
)
|
|
|
|
self.linking = set()
|
|
self.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(None)
|
|
|
|
def hover(self, event):
|
|
if self.linking:
|
|
self.clear_hover()
|
|
|
|
hover_iter = get_iter_at_coords(self, event.x, event.y)
|
|
|
|
if not hover_iter.ends_line():
|
|
h_data = get_event_at_iter(self, hover_iter, self.core)
|
|
h_data.tolink = set()
|
|
|
|
self.events.trigger("Hover", h_data)
|
|
|
|
if h_data.tolink:
|
|
buffer = self.get_buffer()
|
|
|
|
offset = buffer.get_iter_at_line(
|
|
hover_iter.get_line()
|
|
).get_offset()
|
|
|
|
for fr, to in h_data.tolink:
|
|
fr = buffer.get_iter_at_offset(offset + fr)
|
|
to = buffer.get_iter_at_offset(offset + to)
|
|
|
|
buffer.apply_tag_by_name("link", fr, to)
|
|
|
|
self.linking.add(
|
|
(buffer.create_mark(None, fr),
|
|
buffer.create_mark(None, to))
|
|
)
|
|
|
|
self.get_window(
|
|
gtk.TEXT_WINDOW_TEXT
|
|
).set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
|
|
|
|
self.get_pointer()
|
|
|
|
def scroll(self, _allocation=None):
|
|
if self.autoscroll:
|
|
def do_scroll():
|
|
self.scroller.value = self.scroller.upper - self.scroller.page_size
|
|
self._scrolling = False
|
|
|
|
if not self._scrolling:
|
|
self._scrolling = gobject.idle_add(do_scroll)
|
|
|
|
def check_autoscroll(self, *args):
|
|
def set_to_scroll():
|
|
self.autoscroll = self.scroller.value + self.scroller.page_size >= self.scroller.upper
|
|
|
|
gobject.idle_add(set_to_scroll)
|
|
|
|
def __init__(self, core, window, buffer=None):
|
|
if not buffer:
|
|
buffer = gtk.TextBuffer(tag_table)
|
|
|
|
gtk.TextView.__init__(self, buffer)
|
|
self.core = core
|
|
self.events = core.events
|
|
self.win = window
|
|
|
|
self.set_size_request(0, -1)
|
|
|
|
self.set_wrap_mode(gtk.WRAP_WORD_CHAR)
|
|
self.set_editable(False)
|
|
self.set_cursor_visible(False)
|
|
|
|
self.set_property("left-margin", 3)
|
|
self.set_property("right-margin", 3)
|
|
|
|
self.linking = set()
|
|
|
|
self.add_events(gtk.gdk.POINTER_MOTION_HINT_MASK)
|
|
self.add_events(gtk.gdk.LEAVE_NOTIFY_MASK)
|
|
|
|
self.connect('populate-popup', TextOutput.popup)
|
|
self.connect('motion-notify-event', TextOutput.hover)
|
|
self.connect('button-press-event', TextOutput.mousedown)
|
|
self.connect('button-release-event', TextOutput.mouseup)
|
|
self.connect_after('button-release-event', lambda *a: True)
|
|
self.connect('leave-notify-event', TextOutput.clear_hover)
|
|
|
|
self.hover_coords = 0, 0
|
|
|
|
self.autoscroll = True
|
|
self._scrolling = False
|
|
self.scroller = gtk.Adjustment()
|
|
|
|
def setup_scroll(self, _adj, vadj):
|
|
self.scroller = vadj
|
|
|
|
if vadj:
|
|
def set_scroll(adj):
|
|
self.autoscroll = adj.value + adj.page_size >= adj.upper
|
|
|
|
vadj.connect("value-changed", set_scroll)
|
|
|
|
self.connect("set-scroll-adjustments", setup_scroll)
|
|
self.connect("size-allocate", TextOutput.scroll)
|
|
|
|
def set_cursor(widget):
|
|
self.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(None)
|
|
|
|
self.connect("realize", set_cursor)
|
|
|
|
style_me(self, "view")
|
|
|
|
class WindowLabel(gtk.EventBox):
|
|
def update(self):
|
|
title = self.win.get_title()
|
|
|
|
for escapes in (('&','&'), ('<','<'), ('>','>')):
|
|
title = title.replace(*escapes)
|
|
|
|
for a_type in (HILIT, TEXT, EVENT):
|
|
if a_type in self.win.activity:
|
|
title = ACTIVITY_MARKUP[a_type] % title
|
|
break
|
|
|
|
self.label.set_markup(title)
|
|
|
|
def tab_popup(self, event):
|
|
if event.button == 3: # right click
|
|
c_data = self.events.data(window=self.win, menu=[])
|
|
self.events.trigger("WindowMenu", c_data)
|
|
|
|
c_data.menu += [
|
|
None,
|
|
("Close", gtk.STOCK_CLOSE, self.win.close),
|
|
]
|
|
|
|
menu = gtk.Menu()
|
|
for item in menu_from_list(c_data.menu):
|
|
menu.append(item)
|
|
menu.show_all()
|
|
menu.popup(None, None, None, event.button, event.time)
|
|
|
|
def __init__(self, window, core):
|
|
gtk.EventBox.__init__(self)
|
|
self.core = core
|
|
self.events = core.events
|
|
|
|
self.win = window
|
|
self.connect("button-press-event", WindowLabel.tab_popup)
|
|
|
|
self.label = gtk.Label()
|
|
self.add(self.label)
|
|
|
|
self.update()
|
|
self.show_all()
|
|
|
|
class FindBox(gtk.HBox):
|
|
def remove(self, *args):
|
|
self.parent.remove(self)
|
|
self.win.focus()
|
|
|
|
def clicked(self, button, search_down=False):
|
|
text = self.textbox.get_text()
|
|
|
|
if not text:
|
|
return
|
|
|
|
buffer = self.win.output.get_buffer()
|
|
|
|
if buffer.get_selection_bounds():
|
|
if button == self.down:
|
|
_, cursor_iter = buffer.get_selection_bounds()
|
|
else:
|
|
cursor_iter, _ = buffer.get_selection_bounds()
|
|
else:
|
|
cursor_iter = buffer.get_end_iter()
|
|
|
|
if search_down:
|
|
cursor = cursor_iter.forward_search(
|
|
text, gtk.TEXT_SEARCH_VISIBLE_ONLY
|
|
)
|
|
else:
|
|
cursor = cursor_iter.backward_search(
|
|
text, gtk.TEXT_SEARCH_VISIBLE_ONLY
|
|
)
|
|
|
|
if not cursor:
|
|
return
|
|
|
|
fr, to = cursor
|
|
|
|
if button == self.up:
|
|
buffer.place_cursor(fr)
|
|
self.win.output.scroll_to_iter(fr, 0)
|
|
elif button == self.down:
|
|
buffer.place_cursor(to)
|
|
self.win.output.scroll_to_iter(to, 0)
|
|
|
|
buffer.select_range(*cursor)
|
|
|
|
cursor_iter = buffer.get_iter_at_mark(buffer.get_insert())
|
|
|
|
def __init__(self, window):
|
|
gtk.HBox.__init__(self)
|
|
|
|
self.win = window
|
|
|
|
self.up = gtk.Button(stock='gtk-go-up')
|
|
self.down = gtk.Button(stock='gtk-go-down')
|
|
|
|
self.up.connect('clicked', self.clicked)
|
|
self.down.connect('clicked', self.clicked, True)
|
|
|
|
self.up.set_property('can_focus', False)
|
|
self.down.set_property('can_focus', False)
|
|
|
|
self.textbox = gtk.Entry()
|
|
|
|
self.textbox.connect('focus-out-event', self.remove)
|
|
self.textbox.connect('activate', self.clicked)
|
|
|
|
self.pack_start(gtk.Label('Find:'), expand=False)
|
|
self.pack_start(self.textbox)
|
|
|
|
self.pack_start(self.up, expand=False)
|
|
self.pack_start(self.down, expand=False)
|
|
|
|
self.show_all()
|
|
|
|
#class UrkUITabs(gtk.Window):
|
|
class UrkUITabs(object):
|
|
def __init__(self, core):
|
|
# threading stuff
|
|
gtk.gdk.threads_init()
|
|
self.core = core
|
|
self.events = core.events
|
|
self.tabs = gtk.Notebook()
|
|
self.tabs.set_property(
|
|
"tab-pos",
|
|
conf.get("ui-gtk/tab-pos", gtk.POS_BOTTOM)
|
|
)
|
|
|
|
self.tabs.set_scrollable(True)
|
|
self.tabs.set_property("can-focus", False)
|
|
|
|
self.box = gtk.VBox(False)
|
|
self.box.pack_end(self.tabs)
|
|
|
|
def __iter__(self):
|
|
return iter(self.tabs.get_children())
|
|
|
|
def __len__(self):
|
|
return self.tabs.get_n_pages()
|
|
|
|
def exit(self, *args):
|
|
self.events.trigger("Exit")
|
|
gtk.main_level() and gtk.main_quit()
|
|
|
|
def get_active(self):
|
|
return self.tabs.get_nth_page(self.tabs.get_current_page())
|
|
|
|
def set_active(self, window):
|
|
self.tabs.set_current_page(self.tabs.page_num(window))
|
|
|
|
def add(self, window):
|
|
for pos in reversed(range(self.tabs.get_n_pages())):
|
|
if self.tabs.get_nth_page(pos).network == window.network:
|
|
break
|
|
else:
|
|
pos = self.tabs.get_n_pages() - 1
|
|
|
|
self.tabs.insert_page(window, WindowLabel(window, self.core), pos+1)
|
|
|
|
def remove(self, window):
|
|
self.tabs.remove_page(self.tabs.page_num(window))
|
|
|
|
def update(self, window):
|
|
self.tabs.get_tab_label(window).update()
|
|
|
|
def show_all(self):
|
|
self.box.show_all()
|