00001
00002
00003
00004
00005
00006
00007
00008 import weakref
00009 import gobject
00010 import gtk
00011
00012 DRAG_TARGETS = (
00013 ('_ENCODE_TAB_MOVE', gtk.TARGET_SAME_APP, 0),
00014 )
00015
00016 drag_source = None
00017
00018
00019
00020
00021 class ActionArea (gtk.EventBox):
00022 def __init__(self, visible_window=False):
00023 gtk.EventBox.__init__(self)
00024
00025 self.set_visible_window(visible_window)
00026 self.set_above_child(False)
00027
00028 self.connect('button-press-event', self.on_press)
00029
00030 def on_press(self, area, event):
00031 if event.button == 1:
00032 self.emit('left-button-press', event)
00033 elif event.button == 3:
00034 self.emit('right-button-press', event)
00035 return False
00036
00037 gobject.type_register(ActionArea)
00038 gobject.signal_new('left-button-press', ActionArea, 0, gobject.TYPE_NONE, [gtk.gdk.Event])
00039 gobject.signal_new('right-button-press', ActionArea, 0, gobject.TYPE_NONE, [gtk.gdk.Event])
00040
00041
00042
00043
00044 class Button (ActionArea):
00045 def __init__(self, widget):
00046 ActionArea.__init__(self, visible_window=True)
00047
00048 align = gtk.Alignment(0.5, 0.5)
00049 align.show()
00050
00051 self.add(align)
00052 align.add(widget)
00053
00054 self.connect('enter-notify-event', self.on_enter)
00055 self.connect('leave-notify-event', self.on_leave)
00056
00057 def on_enter(self, button, event):
00058 if self.state == gtk.STATE_NORMAL:
00059 self.set_state(gtk.STATE_PRELIGHT)
00060 return False
00061
00062 def on_leave(self, button, event):
00063 if self.state == gtk.STATE_PRELIGHT:
00064 self.set_state(gtk.STATE_NORMAL)
00065 return False
00066
00067 gobject.type_register(Button)
00068
00069
00070
00071
00072 class Tab (Button):
00073 def __init__(self, view, x=0, y=0, angle=0):
00074 self.view = view
00075
00076 label = view.get_label()
00077 label.set_angle(angle)
00078
00079 Button.__init__(self, label)
00080
00081 align = self.get_child()
00082 align.set_padding(y, y, x, x)
00083
00084 self.connect('drag-begin', self.on_drag_begin)
00085 self.connect('drag-data-get', self.on_drag_get)
00086 self.update_drag_source()
00087
00088 self.view_notify = self.view.connect_after('notify::relocatable', self.on_view_notify)
00089
00090 def __str__(self):
00091 align = self.get_child()
00092 label = align.get_child()
00093 return label.get_label()
00094
00095 def get_view(self):
00096 return self.view
00097
00098 def pop_view(self):
00099 view = self.view
00100 self.view = None
00101
00102 if view:
00103 align = self.get_child()
00104 self.remove(align)
00105
00106 label = align.get_child()
00107 align.remove(label)
00108
00109 view.disconnect(self.view_notify)
00110 del self.view_notify
00111
00112 return view
00113
00114 def update_drag_source(self):
00115 if self.view.is_relocatable():
00116 self.drag_source_set(gtk.gdk.BUTTON1_MASK, DRAG_TARGETS, gtk.gdk.ACTION_MOVE)
00117 else:
00118 self.drag_source_unset()
00119
00120 def on_view_notify(self, view, property):
00121 self.update_drag_source()
00122
00123 def on_drag_begin(self, tab, context):
00124 width, height = self.window.get_size()
00125 colormap = self.window.get_colormap()
00126
00127 pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height)
00128 pixbuf = pixbuf.get_from_drawable(self.window, colormap, 0, 0, 0, 0, width, height)
00129
00130 if pixbuf:
00131 x = width / 2
00132 y = height / 2
00133
00134 context.set_icon_pixbuf(pixbuf, x, y)
00135
00136 def on_drag_get(self, tab, context, selection, info, timestamp):
00137 global drag_source
00138 drag_source = weakref.ref(self)
00139
00140 gobject.type_register(Tab)
00141 gobject.signal_new('selected', Tab, 0, gobject.TYPE_NONE, [])
00142
00143 class HTab (Tab):
00144 def __init__(self, view):
00145 Tab.__init__(self, view, x=6, y=1, angle=0)
00146
00147 gobject.type_register(HTab)
00148
00149 class LeftVTab (Tab):
00150 def __init__(self, view):
00151 Tab.__init__(self, view, x=1, y=6, angle=90)
00152
00153 gobject.type_register(LeftVTab)
00154
00155 class RightVTab (Tab):
00156 def __init__(self, view):
00157 Tab.__init__(self, view, x=1, y=6, angle=270)
00158
00159 gobject.type_register(RightVTab)
00160
00161
00162
00163
00164 class AbstractTabBar:
00165 def __init__(self):
00166 self.connect('add', self.on_add)
00167
00168 area = ActionArea()
00169 area.show()
00170
00171 self.add(area)
00172 self.set_child_packing(area, expand=True, fill=True, padding=0, pack_type=gtk.PACK_END)
00173
00174 self.selected = None
00175
00176 def add_tab(self, tab):
00177
00178 self.add(tab)
00179 self.set_child_packing(tab, expand=False, fill=True, padding=0, pack_type=gtk.PACK_START)
00180
00181 def on_add(self, box, child):
00182 if isinstance(child, Tab):
00183 child.connect('left-button-press', self.on_tab_left)
00184 child.connect('right-button-press', self.on_tab_right)
00185 child.connect('destroy', self.on_tab_destroy)
00186 else:
00187 child.connect('right-button-press', self.on_child_right)
00188
00189 def on_tab_left(self, tab, event):
00190 self.select(tab)
00191
00192 def on_tab_right(self, tab, event):
00193 self.emit('tab-right-button-press', tab, event)
00194
00195 def on_tab_destroy(self, tab):
00196 if tab == self.selected:
00197 self.selected = None
00198
00199 def on_child_right(self, child, event):
00200 self.emit('tab-right-button-press', None, event)
00201
00202 def select(self, tab):
00203 if tab != self.selected:
00204 old = self.selected
00205 self.selected = tab
00206
00207 tab.set_state(gtk.STATE_SELECTED)
00208
00209 if old:
00210 old.set_state(gtk.STATE_NORMAL)
00211
00212 tab.emit('selected')
00213
00214 def get_tabs(self):
00215 return [widget for widget in self.get_children() if isinstance(widget, Tab)]
00216
00217 class HTabBar (gtk.HBox, AbstractTabBar):
00218 def __init__(self):
00219 gtk.HBox.__init__(self)
00220 AbstractTabBar.__init__(self)
00221
00222 gobject.type_register(HTabBar)
00223 gobject.signal_new('tab-right-button-press', HTabBar, 0, gobject.TYPE_NONE, [Tab, gtk.gdk.Event])
00224
00225 class VTabBar (gtk.VBox, AbstractTabBar):
00226 def __init__(self):
00227 gtk.VBox.__init__(self)
00228 AbstractTabBar.__init__(self)
00229
00230 gobject.type_register(VTabBar)
00231 gobject.signal_new('tab-right-button-press', VTabBar, 0, gobject.TYPE_NONE, [Tab, gtk.gdk.Event])
00232
00233
00234
00235
00236 class AbstractTabBook:
00237 def __init__(self, tabs_first=False):
00238 self.notebook = gtk.Notebook()
00239 self.notebook.set_property('show-border', False)
00240 self.notebook.set_property('show-tabs', False)
00241 self.notebook.show()
00242
00243 self.pack_start(self.notebook, expand=True, fill=True)
00244
00245 if self.bar_type:
00246 self.bar = self.bar_type()
00247 self.bar.connect('tab-right-button-press', self.on_tab_right)
00248 self.bar.show()
00249
00250 self.pack_start(self.bar, expand=False, fill=True)
00251
00252 if tabs_first:
00253 self.child_set_property(self.notebook, 'position', 1)
00254 self.child_set_property(self.bar, 'position', 0)
00255
00256 self._set_expansion(tabs=True)
00257
00258 self.notebook.connect('remove', self.on_notebook_remove)
00259
00260 self.tabs_by_widget = {}
00261
00262 flags = gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP
00263 self.bar.drag_dest_set(flags, DRAG_TARGETS, gtk.gdk.ACTION_MOVE)
00264 self.bar.connect('drag-drop', self.on_drag_drop)
00265
00266 self.connect('next-view', self.on_change_view, 1)
00267 self.connect('previous-view', self.on_change_view, -1)
00268
00269 def on_drag_drop(self, bar, context, x, y, timestamp):
00270 global drag_source
00271 source_tab = drag_source()
00272 drag_source = None
00273
00274 source_bar = source_tab.get_parent()
00275 source_book = source_bar.get_parent()
00276
00277 if self == source_book:
00278 self.bar.remove(source_tab)
00279 self.bar.add_tab(source_tab)
00280 else:
00281 view = source_tab.pop_view()
00282 source_book.remove_view(view)
00283 self.append_view(view)
00284
00285 context.finish(True, False, timestamp)
00286 return True
00287
00288 def append_view(self, view):
00289 widget = view.get_widget()
00290 self.notebook.append_page(widget)
00291
00292 if self.bar_type:
00293 if self.notebook.get_n_pages() == 1:
00294 self._set_expansion(tabs=False)
00295
00296 tab = self.tab_type(view)
00297 tab.connect('selected', self.on_tab_selected, widget)
00298 tab.show()
00299
00300 self.tabs_by_widget[widget] = tab
00301
00302 self.bar.add_tab(tab)
00303 self.bar.select(tab)
00304
00305 def remove_view(self, view):
00306 widget = view.get_widget()
00307 self.notebook.remove(widget)
00308
00309 def set_view(self, view):
00310 widget = view.get_widget()
00311 if self.bar_type:
00312 tab = self.tabs_by_widget[widget]
00313 self.bar.select(tab)
00314 else:
00315 num = self.notebook.page_num(widget)
00316 self.notebook.set_current_page(num)
00317
00318 def first_view(self):
00319 count = self.notebook.get_n_pages()
00320 if count > 0:
00321 if self.bar_type:
00322 widget = self.notebook.get_nth_page(0)
00323 tab = self.tabs_by_widget[widget]
00324 self.bar.select(tab)
00325 else:
00326 self.notebook.set_current_page(0)
00327
00328 def change_view(self, increment):
00329 count = self.notebook.get_n_pages()
00330 if count > 0:
00331 num = self.notebook.get_current_page()
00332 num = (num + increment) % count
00333
00334 if self.bar_type:
00335 widget = self.notebook.get_nth_page(num)
00336 tab = self.tabs_by_widget[widget]
00337 self.bar.select(tab)
00338 else:
00339 self.notebook.set_current_page(num)
00340
00341 def get_tabs(self):
00342 if self.bar_type:
00343 return self.bar.get_tabs()
00344 else:
00345 return []
00346
00347 def set_tab(self, tab):
00348 if self.bar_type:
00349 self.bar.select(tab)
00350
00351 def on_change_view(self, book, increment):
00352 self.change_view(increment)
00353
00354 def on_notebook_remove(self, notebook, widget):
00355 tab = self.tabs_by_widget.pop(widget)
00356 tab.destroy()
00357
00358 active_num = self.notebook.get_current_page()
00359 if active_num >= 0:
00360 active_widget = self.notebook.get_nth_page(active_num)
00361 active_tab = self.tabs_by_widget[active_widget]
00362
00363 self.bar.select(active_tab)
00364 else:
00365 self._set_expansion(tabs=True)
00366
00367 def on_tab_selected(self, tab, widget):
00368 num = self.notebook.page_num(widget)
00369 self.notebook.set_current_page(num)
00370
00371 def on_tab_right(self, bar, tab, event):
00372 if tab:
00373 view = tab.get_view()
00374 else:
00375 view = None
00376
00377 self.emit('tab-right-button-press', view, event)
00378
00379 def _set_expansion(self, tabs):
00380 self.set_child_packing(self.notebook, not tabs, not tabs, 0, gtk.PACK_START)
00381 self.set_child_packing(self.bar, tabs, tabs, 0, gtk.PACK_START)
00382
00383 class TablessBook (gtk.HBox, AbstractTabBook):
00384 bar_type = None
00385
00386 def __init__(self):
00387 gtk.HBox.__init__(self)
00388 AbstractTabBook.__init__(self)
00389
00390 gobject.type_register(TablessBook)
00391 gobject.signal_new('next-view', TablessBook, gobject.SIGNAL_ACTION, gobject.TYPE_NONE, [])
00392 gobject.signal_new('previous-view', TablessBook, gobject.SIGNAL_ACTION, gobject.TYPE_NONE, [])
00393
00394 class HTabBook (gtk.VBox, AbstractTabBook):
00395 tab_type = HTab
00396 bar_type = HTabBar
00397
00398 def __init__(self, tabs_top=False):
00399 gtk.VBox.__init__(self)
00400 AbstractTabBook.__init__(self, tabs_top)
00401
00402 gobject.type_register(HTabBook)
00403 gobject.signal_new('next-view', HTabBook, gobject.SIGNAL_ACTION, gobject.TYPE_NONE, [])
00404 gobject.signal_new('previous-view', HTabBook, gobject.SIGNAL_ACTION, gobject.TYPE_NONE, [])
00405 gobject.signal_new('tab-right-button-press', HTabBook, 0, gobject.TYPE_NONE, [gobject.GObject, gtk.gdk.Event])
00406
00407 class VTabBook (gtk.HBox, AbstractTabBook):
00408 bar_type = VTabBar
00409
00410 def __init__(self, tabs_left=False):
00411 if tabs_left:
00412 self.tab_type = LeftVTab
00413 else:
00414 self.tab_type = RightVTab
00415
00416 gtk.HBox.__init__(self)
00417 AbstractTabBook.__init__(self, tabs_left)
00418
00419 gobject.type_register(VTabBook)
00420 gobject.signal_new('next-view', VTabBook, gobject.SIGNAL_ACTION, gobject.TYPE_NONE, [])
00421 gobject.signal_new('previous-view', VTabBook, gobject.SIGNAL_ACTION, gobject.TYPE_NONE, [])
00422 gobject.signal_new('tab-right-button-press', VTabBook, 0, gobject.TYPE_NONE, [gobject.GObject, gtk.gdk.Event])