# Copyright (C) 2009, Aleksey Lim # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. import gtk import gobject from sugar.graphics import style from sugar.graphics.toolbutton import ToolButton from sugar.graphics import palettegroup from toolkit.internals.palettewindow import PaletteWindow from toolkit.internals.palettewindow import ToolInvoker _ARROW_SIZE = style.zoom(24) _LINE_WIDTH = 2 class ToolbarButton(ToolButton): def __init__(self, page=None, **kwargs): ToolButton.__init__(self, **kwargs) self.page_widget = None self.set_page(page) self.connect('clicked', lambda widget: self.set_expanded(not self.is_expanded())) self.connect('size-allocate', self.__size_allocate_cb) def get_toolbar_box(self): if not hasattr(self.parent, 'owner'): return None return self.parent.owner toolbar_box = property(get_toolbar_box) def get_page(self): if self.page_widget is None: return None return _get_embedded_page(self.page_widget) def set_page(self, page): if page is None: self.page_widget = None return self.page_widget, alignment_ = _embed_page(_Box, page) w_, h = gtk.icon_size_lookup(gtk.ICON_SIZE_LARGE_TOOLBAR) page.show() if self.props.palette is None: self.props.palette = _ToolbarPalette(invoker=ToolInvoker(self)) self._move_page_to_palette() page = gobject.property(type=object, getter=get_page, setter=set_page) def is_in_palette(self): return self.page is not None and \ self.page_widget.parent == self.props.palette def is_expanded(self): return self.page is not None and \ not self.is_in_palette() def popdown(self): if self.props.palette is not None: self.props.palette.popdown(immediate=True) def set_expanded(self, expanded): self.popdown() if self.page is None or self.is_expanded() == expanded: return if not expanded: self._move_page_to_palette() return box = self.toolbar_box if box.expanded_button is not None: if box.expanded_button.window is not None: # need to redraw it to erase arrow box.expanded_button.window.invalidate_rect(None, True) box.expanded_button.set_expanded(False) box.expanded_button = self self._unparent() self.modify_bg(gtk.STATE_NORMAL, box.background) _setup_page(self.page_widget, box.background, box.props.padding) box.pack_start(self.page_widget) def _move_page_to_palette(self): if self.is_in_palette(): return self._unparent() if isinstance(self.props.palette, _ToolbarPalette): self.props.palette.add(self.page_widget) def _unparent(self): if self.page_widget.parent is None: return self.page_widget.parent.remove(self.page_widget) def do_expose_event(self, event): if not self.is_expanded() or self.props.palette is not None and \ self.props.palette.is_up(): ToolButton.do_expose_event(self, event) _paint_arrow(self, event, gtk.ARROW_DOWN) return alloc = self.allocation self.get_style().paint_box(event.window, gtk.STATE_NORMAL, gtk.SHADOW_IN, event.area, self, 'palette-invoker', alloc.x, 0, alloc.width, alloc.height + _LINE_WIDTH) if self.child.state != gtk.STATE_PRELIGHT: self.get_style().paint_box(event.window, gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, self, None, alloc.x + _LINE_WIDTH, _LINE_WIDTH, alloc.width - _LINE_WIDTH * 2, alloc.height) gtk.ToolButton.do_expose_event(self, event) _paint_arrow(self, event, gtk.ARROW_UP) def __size_allocate_cb(self, button, allocation): if self.page_widget is not None: self.page_widget.set_size_request(-1, allocation.height) class ToolbarBox(gtk.VBox): def __init__(self, padding=style.TOOLBOX_HORIZONTAL_PADDING): gtk.VBox.__init__(self) self._expanded_button_index = -1 self.background = None self._toolbar = gtk.Toolbar() self._toolbar.owner = self self._toolbar.connect('remove', self.__remove_cb) self._toolbar_widget, self._toolbar_alignment = \ _embed_page(gtk.EventBox, self._toolbar) self.pack_start(self._toolbar_widget) self.props.padding = padding self.modify_bg(gtk.STATE_NORMAL, style.COLOR_TOOLBAR_GREY.get_gdk_color()) def get_toolbar(self): return self._toolbar toolbar = property(get_toolbar) def get_expanded_button(self): if self._expanded_button_index == -1: return None return self.toolbar.get_nth_item(self._expanded_button_index) def set_expanded_button(self, button): if not button in self.toolbar: self._expanded_button_index = -1 return self._expanded_button_index = self.toolbar.get_item_index(button) expanded_button = property(get_expanded_button, set_expanded_button) def get_padding(self): return self._toolbar_alignment.props.left_padding def set_padding(self, pad): self._toolbar_alignment.set_padding(0, 0, pad, pad) padding = gobject.property(type=object, getter=get_padding, setter=set_padding) def modify_bg(self, state, color): if state == gtk.STATE_NORMAL: self.background = color self._toolbar_widget.modify_bg(state, color) self.toolbar.modify_bg(state, color) def __remove_cb(self, sender, button): if not isinstance(button, ToolbarButton): return button.popdown() if button == self.expanded_button: self.remove(button.page_widget) self._expanded_button_index = -1 class _ToolbarPalette(PaletteWindow): def __init__(self, **kwargs): PaletteWindow.__init__(self, **kwargs) self.set_border_width(0) self._has_focus = False group = palettegroup.get_group('default') group.connect('popdown', self.__group_popdown_cb) self.set_group_id('toolbarbox') def get_expanded_button(self): return self.invoker.parent expanded_button = property(get_expanded_button) def on_invoker_enter(self): PaletteWindow.on_invoker_enter(self) self._set_focus(True) def on_invoker_leave(self): PaletteWindow.on_invoker_leave(self) self._set_focus(False) def on_enter(self, event): PaletteWindow.on_enter(self, event) self._set_focus(True) def on_leave(self, event): PaletteWindow.on_enter(self, event) self._set_focus(False) def _set_focus(self, new_focus): self._has_focus = new_focus if not self._has_focus: group = palettegroup.get_group('default') if not group.is_up(): self.popdown() def do_size_request(self, requisition): gtk.Window.do_size_request(self, requisition) requisition.width = max(requisition.width, gtk.gdk.screen_width()) def popup(self, immediate=False): button = self.expanded_button if button.is_expanded(): return box = button.toolbar_box _setup_page(button.page_widget, style.COLOR_BLACK.get_gdk_color(), box.props.padding) PaletteWindow.popup(self, immediate) def __group_popdown_cb(self, group): if not self._has_focus: self.popdown(immediate=True) class _Box(gtk.EventBox): def __init__(self): gtk.EventBox.__init__(self) self.connect('expose-event', self.do_expose_event) self.set_app_paintable(True) def do_expose_event(self, widget, event): if self.parent.expanded_button is None: return alloc = self.parent.expanded_button.allocation self.get_style().paint_box(event.window, gtk.STATE_NORMAL, gtk.SHADOW_IN, event.area, self, 'palette-invoker', -_LINE_WIDTH, 0, self.allocation.width + _LINE_WIDTH * 2, self.allocation.height + _LINE_WIDTH) self.get_style().paint_box(event.window, gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, self, None, alloc.x + _LINE_WIDTH, 0, alloc.width - _LINE_WIDTH * 2, _LINE_WIDTH) def _setup_page(page_widget, color, hpad): vpad = _LINE_WIDTH page_widget.child.set_padding(vpad, vpad, hpad, hpad) page = _get_embedded_page(page_widget) page.modify_bg(gtk.STATE_NORMAL, color) if isinstance(page, gtk.Container): for i in page.get_children(): i.modify_bg(gtk.STATE_INSENSITIVE, color) page_widget.modify_bg(gtk.STATE_NORMAL, color) page_widget.modify_bg(gtk.STATE_PRELIGHT, color) def _embed_page(box_class, page): page.show() alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0) alignment.add(page) alignment.show() page_widget = box_class() page_widget.modify_bg(gtk.STATE_ACTIVE, style.COLOR_BUTTON_GREY.get_gdk_color()) page_widget.add(alignment) page_widget.show() return (page_widget, alignment) def _get_embedded_page(page_widget): return page_widget.child.child def _paint_arrow(widget, event, arrow_type): alloc = widget.allocation x = alloc.x + alloc.width / 2 - _ARROW_SIZE / 2 y = alloc.y + alloc.height - int(_ARROW_SIZE * .85) widget.get_style().paint_arrow(event.window, gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, widget, None, arrow_type, True, x, y, _ARROW_SIZE, _ARROW_SIZE)