tab.py

Go to the documentation of this file.
00001 # Copyright 2005, 2006  Timo Savola
00002 #
00003 # This program is free software; you can redistribute it and/or modify
00004 # it under the terms of the GNU Lesser General Public License as published
00005 # by the Free Software Foundation; either version 2.1 of the License, or
00006 # (at your option) any later version.
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 ## Action Area
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 ## Button
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 ## Tab
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 ## Tab bars
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                 # pack_start() doesn't seem to emit 'add' signal
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 ## Tab books
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])

Generated on Thu Jan 18 09:47:40 2007 for Encode by  doxygen 1.4.7