Esempio n. 1
0
class ColorToolButton(Gtk.ToolItem):
    # This not ideal. It would be better to subclass Gtk.ToolButton, however
    # the python bindings do not seem to be powerfull enough for that.
    # (As we need to change a variable in the class structure.)

    __gtype_name__ = 'SugarColorToolButton'
    __gsignals__ = {'color-set': (GObject.SignalFlags.RUN_FIRST, None,
                                  tuple())}

    def __init__(self, icon_name='color-preview', **kwargs):
        self._accelerator = None
        self._tooltip = None
        self._palette_invoker = ToolInvoker()
        self._palette = None

        GObject.GObject.__init__(self, **kwargs)

        # The Gtk.ToolButton has already added a normal button.
        # Replace it with a ColorButton
        color_button = _ColorButton(icon_name=icon_name, has_invoker=False)
        self.add(color_button)
        color_button.show()

        # The following is so that the behaviour on the toolbar is correct.
        color_button.set_relief(Gtk.ReliefStyle.NONE)
        color_button.icon_size = style.STANDARD_ICON_SIZE

        self._palette_invoker.attach_tool(self)
        self._palette_invoker.props.toggle_palette = True
        self._palette_invoker.props.lock_palette = True

        # This widget just proxies the following properties to the colorbutton
        color_button.connect('notify::color', self.__notify_change)
        color_button.connect('notify::icon-name', self.__notify_change)
        color_button.connect('notify::icon-size', self.__notify_change)
        color_button.connect('notify::title', self.__notify_change)
        color_button.connect('color-set', self.__color_set_cb)
        color_button.connect('can-activate-accel',
                             self.__button_can_activate_accel_cb)

    def __button_can_activate_accel_cb(self, button, signal_id):
        # Accept activation via accelerators regardless of this widget's state
        return True

    def set_accelerator(self, accelerator):
        '''
        Sets keyboard shortcut that activates this button.

        Args:
            accelerator(string): accelerator to be set. Should be in
            form <modifier>Letter
            Find about format here :
            https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html#gtk-accelerator-parse

        Example:
            set_accelerator(self, 'accel')
        '''
        self._accelerator = accelerator
        setup_accelerator(self)

    def get_accelerator(self):
        '''
        Returns the above accelerator string.
        '''
        return self._accelerator

    accelerator = GObject.Property(type=str, setter=set_accelerator,
                                   getter=get_accelerator)

    def create_palette(self):
        '''
        The create_palette function is called when the palette needs to be
        invoked.  For example, when the user has right clicked the icon or
        the user has hovered over the icon for a long time.

        The create_palette will only be called once or zero times.  The palette
        returned will be stored and re-used if the user invokes the palette
        multiple times.

        Your create_palette implementation does not need to
        :any:`Gtk.Widget.show` the palette, as this will be done by the
        invoker.  However, you still need to show
        the menu items, etc that you place in the palette.

        Returns:

            sugar3.graphics.palette.Palette, or None to indicate that you
            do not want a palette shown

        The default implementation returns None, to indicate no palette should
        be shown.
        '''
        self._palette = self.get_child().create_palette()
        return self._palette

    def get_palette_invoker(self):
        return self._palette_invoker

    def set_palette_invoker(self, palette_invoker):
        self._palette_invoker.detach()
        self._palette_invoker = palette_invoker

    palette_invoker = GObject.Property(
        type=object, setter=set_palette_invoker, getter=get_palette_invoker)

    def set_expanded(self, expanded):
        box = self.toolbar_box
        if not box:
            return

        if not expanded:
            self._palette_invoker.notify_popdown()
            return

        if box.expanded_button is not None:
            box.expanded_button.queue_draw()
            if box.expanded_button != self:
                box.expanded_button.set_expanded(False)
        box.expanded_button = self

    def get_toolbar_box(self):
        parent = self.get_parent()
        if not hasattr(parent, 'owner'):
            return None
        return parent.owner

    toolbar_box = property(get_toolbar_box)

    def set_color(self, color):
        '''
        Sets the color of the colorbutton
        '''
        self.get_child().props.color = color

    def get_color(self):
        '''
        Gets the above set color string.
        '''
        return self.get_child().props.color

    color = GObject.Property(type=object, getter=get_color, setter=set_color)

    def set_icon_name(self, icon_name):
        '''
        Sets the icon for the tool button from a named themed icon.
        If it is none then no icon will be shown.

        Args:
            icon_name(string): The name for a themed icon.
            It can be set as 'None' too.

        Example:
            set_icon_name('view-radial')
        '''
        self.get_child().props.icon_name = icon_name

    def get_icon_name(self):
        '''
        The get_icon_name() method returns the value of the icon_name
        property that contains the name of a themed icon or None.
        '''
        return self.get_child().props.icon_name

    icon_name = GObject.Property(type=str,
                                 getter=get_icon_name, setter=set_icon_name)

    def set_icon_size(self, icon_size):
        '''
        Sets the size of icons in the colorbutton.
        '''
        self.get_child().props.icon_size = icon_size

    def get_icon_size(self):
        '''
        Gets the size of icons in the colorbutton.
        '''
        return self.get_child().props.icon_size

    icon_size = GObject.Property(type=int,
                                 getter=get_icon_size, setter=set_icon_size)

    def set_title(self, title):
        '''
        The set_title() method sets the "title" property to the value of
        title. The "title" property contains the string that is used to
        set the colorbutton title.
        '''
        self.get_child().props.title = title

    def get_title(self):
        '''
        Return the above title string.
        '''
        return self.get_child().props.title

    title = GObject.Property(type=str, getter=get_title, setter=set_title)

    def do_draw(self, cr):
        if self._palette and self._palette.is_up():
            allocation = self.get_allocation()
            # draw a black background, has been done by the engine before
            cr.set_source_rgb(0, 0, 0)
            cr.rectangle(0, 0, allocation.width, allocation.height)
            cr.paint()

        Gtk.ToolItem.do_draw(self, cr)

        if self._palette and self._palette.is_up():
            invoker = self._palette.props.invoker
            invoker.draw_rectangle(cr, self._palette)

        return False

    def __notify_change(self, widget, pspec):
        self.notify(pspec.name)

    def __color_set_cb(self, widget):
        self.emit('color-set')
Esempio n. 2
0
class ColorToolButton(Gtk.ToolItem):
    # This not ideal. It would be better to subclass Gtk.ToolButton, however
    # the python bindings do not seem to be powerfull enough for that.
    # (As we need to change a variable in the class structure.)

    __gtype_name__ = 'SugarColorToolButton'
    __gsignals__ = {
        'color-set': (GObject.SignalFlags.RUN_FIRST, None, tuple())
    }

    def __init__(self, icon_name='color-preview', **kwargs):
        self._accelerator = None
        self._tooltip = None
        self._palette_invoker = ToolInvoker()
        self._palette = None

        GObject.GObject.__init__(self, **kwargs)

        # The Gtk.ToolButton has already added a normal button.
        # Replace it with a ColorButton
        color_button = _ColorButton(icon_name=icon_name, has_invoker=False)
        self.add(color_button)
        color_button.show()

        # The following is so that the behaviour on the toolbar is correct.
        color_button.set_relief(Gtk.ReliefStyle.NONE)
        color_button.icon_size = Gtk.IconSize.LARGE_TOOLBAR

        self._palette_invoker.attach_tool(self)
        self._palette_invoker.props.toggle_palette = True
        self._palette_invoker.props.lock_palette = True

        # This widget just proxies the following properties to the colorbutton
        color_button.connect('notify::color', self.__notify_change)
        color_button.connect('notify::icon-name', self.__notify_change)
        color_button.connect('notify::icon-size', self.__notify_change)
        color_button.connect('notify::title', self.__notify_change)
        color_button.connect('color-set', self.__color_set_cb)
        color_button.connect('can-activate-accel',
                             self.__button_can_activate_accel_cb)

    def __button_can_activate_accel_cb(self, button, signal_id):
        # Accept activation via accelerators regardless of this widget's state
        return True

    def set_accelerator(self, accelerator):
        self._accelerator = accelerator
        setup_accelerator(self)

    def get_accelerator(self):
        return self._accelerator

    accelerator = GObject.property(type=str,
                                   setter=set_accelerator,
                                   getter=get_accelerator)

    def create_palette(self):
        self._palette = self.get_child().create_palette()
        return self._palette

    def get_palette_invoker(self):
        return self._palette_invoker

    def set_palette_invoker(self, palette_invoker):
        self._palette_invoker.detach()
        self._palette_invoker = palette_invoker

    palette_invoker = GObject.property(type=object,
                                       setter=set_palette_invoker,
                                       getter=get_palette_invoker)

    def set_expanded(self, expanded):
        box = self.toolbar_box
        if not box:
            return

        if not expanded:
            self._palette_invoker.notify_popdown()
            return

        if box.expanded_button is not None:
            box.expanded_button.queue_draw()
            if box.expanded_button != self:
                box.expanded_button.set_expanded(False)
        box.expanded_button = self

    def get_toolbar_box(self):
        parent = self.get_parent()
        if not hasattr(parent, 'owner'):
            return None
        return parent.owner

    toolbar_box = property(get_toolbar_box)

    def set_color(self, color):
        self.get_child().props.color = color

    def get_color(self):
        return self.get_child().props.color

    color = GObject.property(type=object, getter=get_color, setter=set_color)

    def set_icon_name(self, icon_name):
        self.get_child().props.icon_name = icon_name

    def get_icon_name(self):
        return self.get_child().props.icon_name

    icon_name = GObject.property(type=str,
                                 getter=get_icon_name,
                                 setter=set_icon_name)

    def set_icon_size(self, icon_size):
        self.get_child().props.icon_size = icon_size

    def get_icon_size(self):
        return self.get_child().props.icon_size

    icon_size = GObject.property(type=int,
                                 getter=get_icon_size,
                                 setter=set_icon_size)

    def set_title(self, title):
        self.get_child().props.title = title

    def get_title(self):
        return self.get_child().props.title

    title = GObject.property(type=str, getter=get_title, setter=set_title)

    def do_draw(self, cr):
        if self._palette and self._palette.is_up():
            allocation = self.get_allocation()
            # draw a black background, has been done by the engine before
            cr.set_source_rgb(0, 0, 0)
            cr.rectangle(0, 0, allocation.width, allocation.height)
            cr.paint()

        Gtk.ToolItem.do_draw(self, cr)

        if self._palette and self._palette.is_up():
            invoker = self._palette.props.invoker
            invoker.draw_rectangle(cr, self._palette)

        return False

    def __notify_change(self, widget, pspec):
        self.notify(pspec.name)

    def __color_set_cb(self, widget):
        self.emit('color-set')
Esempio n. 3
0
class ButtonStrokeColor(Gtk.ToolItem):
    """Class to manage the Stroke Color of a Button"""

    __gtype_name__ = 'BrushColorToolButton'
    __gsignals__ = {
        'color-set': (GObject.SignalFlags.RUN_FIRST, None, tuple())
    }

    def __init__(self, activity, **kwargs):
        Gtk.ToolItem.__init__(self, **kwargs)

        self._activity = activity
        self.properties = self._activity.area.tool
        self._accelerator = None
        self._tooltip = None
        self._palette_invoker = ToolInvoker()
        self._palette = None
        self._selected_tool = None

        # The Gtk.ToolButton has already added a normal button.
        # Replace it with a ColorButton
        self.color_button = BrushButton(has_invoker=False)
        self.add(self.color_button)
        self.color_button.set_brush_size(self.properties['line size'])
        self.color_button.set_brush_shape(self.properties['line shape'])
        self.color_button.set_stamp_size(20)

        # The following is so that the behaviour on the toolbar is correct.
        self.color_button.set_relief(Gtk.ReliefStyle.NONE)

        self._palette_invoker.attach_tool(self)
        self._palette_invoker.props.toggle_palette = True
        self._palette_invoker.props.lock_palette = True

        # This widget just proxies the following properties to the colorbutton
        self.color_button.connect('notify::color', self.__notify_change)
        self.color_button.connect('notify::icon-name', self.__notify_change)
        self.color_button.connect('notify::icon-size', self.__notify_change)
        self.color_button.connect('notify::title', self.__notify_change)
        self.color_button.connect('can-activate-accel',
                                  self.__button_can_activate_accel_cb)

        self.create_palette()

    def __button_can_activate_accel_cb(self, button, signal_id):
        # Accept activation via accelerators regardless of this widget's state
        return True

    def __notify_change(self, widget, pspec):
        self.color_button.set_color(self.get_color())
        self.notify(pspec.name)

    def _color_button_cb(self, widget, pspec):
        color = self.get_color()
        self.set_stroke_color(color)

    def create_palette(self):
        self._palette = self.get_child().create_palette()

        color_palette_hbox = self._palette._picker_hbox
        self.custom_box = Gtk.VBox()

        self.vbox_brush_options = Gtk.VBox()

        # This is where we set restrictions for size:
        # Initial value, minimum value, maximum value, step

        adj = Gtk.Adjustment(self.properties['line size'], 1.0, 100.0, 1.0)
        self.size_scale = Gtk.HScale()
        self.size_scale.set_adjustment(adj)
        self.size_scale.set_draw_value(False)
        self.size_scale.set_size_request(style.zoom(200), -1)
        self.size_label = Gtk.Label(label=_('Size'))
        self.size_label.props.halign = Gtk.Align.START
        self.vbox_brush_options.pack_start(self.size_label, True, True, 0)
        self.vbox_brush_options.pack_start(self.size_scale, True, True, 0)

        self.size_scale.connect('value-changed', self._on_value_changed)

        # Control alpha
        alpha = self.properties['alpha'] * 100
        adj_alpha = Gtk.Adjustment(alpha, 10.0, 100.0, 1.0)
        self.alpha_scale = Gtk.HScale()
        self.alpha_scale.set_adjustment(adj_alpha)
        self.alpha_scale.set_draw_value(False)
        self.alpha_scale.set_size_request(style.zoom(200), -1)
        self.alpha_label = Gtk.Label(label=_('Opacity'))
        self.alpha_label.props.halign = Gtk.Align.START
        self.vbox_brush_options.pack_start(self.alpha_label, True, True, 0)
        self.vbox_brush_options.pack_start(self.alpha_scale, True, True, 0)

        self.alpha_scale.connect('value-changed', self._on_alpha_changed)

        # User is able to choose Shapes for 'Brush' and 'Eraser'
        self.shape_box = Gtk.HBox()
        self.custom_box.pack_start(self.vbox_brush_options, True, True, 0)
        item1 = RadioToolButton()
        item1.set_icon_name('tool-shape-ellipse')

        item2 = RadioToolButton()
        item2.set_icon_name('tool-shape-rectangle')
        item2.props.group = item1

        if self.properties['line shape'] == 'circle':
            item1.set_active(True)
        else:
            item2.set_active(True)

        item1.connect('toggled', self._on_toggled, self.properties, 'circle')
        item2.connect('toggled', self._on_toggled, self.properties, 'square')

        self.shape_box.pack_start(Gtk.Label(_('Shape')), True, True, 0)
        self.shape_box.pack_start(item1, True, True, 0)
        self.shape_box.pack_start(item2, True, True, 0)

        self.vbox_brush_options.pack_start(self.shape_box, True, True, 0)

        self.keep_aspect_checkbutton = Gtk.CheckButton(_('Keep aspect'))
        ratio = self._activity.area.keep_aspect_ratio
        self.keep_aspect_checkbutton.set_active(ratio)
        self.keep_aspect_checkbutton.connect(
            'toggled', self._keep_aspect_checkbutton_toggled)
        self.vbox_brush_options.pack_start(self.keep_aspect_checkbutton, True,
                                           True, 0)

        self.custom_separator = Gtk.VSeparator()
        color_palette_hbox.pack_start(self.custom_separator,
                                      True,
                                      True,
                                      padding=style.DEFAULT_SPACING)
        color_palette_hbox.pack_start(self.custom_box,
                                      True,
                                      True,
                                      padding=style.DEFAULT_SPACING)
        color_palette_hbox.show_all()
        self._update_palette()
        return self._palette

    def _keep_aspect_checkbutton_toggled(self, checkbutton):
        self._activity.area.keep_aspect_ratio = checkbutton.get_active()

    def _update_palette(self):
        tool_name = self._selected_tool
        show_controls = ()
        show_colors = False
        if tool_name == 'brush' or tool_name is None:
            title = _('Brush properties')
            show_colors = True
            show_controls = (self.size_label, self.size_scale, self.shape_box,
                             self.alpha_label, self.alpha_scale)
        elif tool_name in ('stamp', 'load-stamp'):
            show_controls = (self.size_label, self.size_scale)
            title = _('Stamp properties')
        elif tool_name == 'eraser':
            show_controls = (self.size_label, self.size_scale, self.shape_box)
            title = _('Eraser properties')
        elif tool_name == 'bucket':
            show_colors = True
            title = _('Bucket properties')
        elif tool_name == 'picker':
            title = _('Picker properties')
        elif tool_name == 'marquee-rectangular':
            title = _('Select Area')
            show_controls = (self.keep_aspect_checkbutton, )
        else:
            title = ''

        self._palette._picker_hbox.show_all()
        # Hide palette color widgets except size:
        if not show_colors:
            palette_children = self._palette._picker_hbox.get_children()
            for ch in palette_children:
                if ch != self.custom_box:
                    ch.hide()
        elif not show_controls:
            self.custom_separator.hide()

        self.vbox_brush_options.show_all()
        controls = self.vbox_brush_options.get_children()
        for control in controls:
            if control not in show_controls:
                control.hide()

        # Change title:
        self.set_title(title)

        self._palette._picker_hbox.resize_children()
        self._palette._picker_hbox.queue_draw()

    def update_stamping(self):
        if self.color_button.is_stamping():
            self.size_scale.set_value(self.color_button.stamp_size)
        else:
            self.size_scale.set_value(self.color_button.brush_size)
        self.size_scale.queue_draw()
        self._update_palette()

    def _on_alpha_changed(self, scale):
        alpha = scale.get_value() / 100.0
        self._activity.area.set_alpha(alpha)
        self.color_button.set_alpha(alpha)

    def _on_value_changed(self, scale):
        size = int(scale.get_value())
        if self.color_button.is_stamping():
            # avoid stamps too small
            if size > 5:
                self.properties['stamp size'] = size
                resized_stamp = self._activity.area.resize_stamp(size)
                self.color_button.set_resized_stamp(resized_stamp)
                self.color_button.set_stamp_size(self.properties['stamp size'])
        else:
            self.properties['line size'] = size
            self.color_button.set_brush_size(self.properties['line size'])
        self._activity.area.set_tool(self.properties)

    def _on_toggled(self, radiobutton, tool, shape):
        if radiobutton.get_active():
            self.properties['line shape'] = shape
            self.color_button.set_brush_shape(shape)
            self.color_button.set_brush_size(self.properties['line size'])

    def get_palette_invoker(self):
        return self._palette_invoker

    def set_palette_invoker(self, palette_invoker):
        self._palette_invoker.detach()
        self._palette_invoker = palette_invoker

    palette_invoker = GObject.Property(type=object,
                                       setter=set_palette_invoker,
                                       getter=get_palette_invoker)

    def set_expanded(self, expanded):
        box = self.toolbar_box
        if not box:
            return

        if not expanded:
            self._palette_invoker.notify_popdown()
            return

        if box.expanded_button is not None:
            box.expanded_button.queue_draw()
            if box.expanded_button != self:
                box.expanded_button.set_expanded(False)
        box.expanded_button = self

    def get_toolbar_box(self):
        parent = self.get_parent()
        if not hasattr(parent, 'owner'):
            return None
        return parent.owner

    toolbar_box = property(get_toolbar_box)

    def set_color(self, color):
        self.color_button.set_color(color)

    def get_color(self):
        return self.get_child().props.color

    color = GObject.Property(type=object, getter=get_color, setter=set_color)

    def set_title(self, title):
        self.get_child().props.title = title

    def get_title(self):
        return self.get_child().props.title

    title = GObject.Property(type=str, getter=get_title, setter=set_title)

    def get_selected_tool(self):
        return self._selected_tool

    def set_selected_tool(self, tool_name):
        self._selected_tool = tool_name

    def do_draw(self, cr):
        if self._palette and self._palette.is_up():
            allocation = self.get_allocation()
            # draw a black background, has been done by the engine before
            cr.set_source_rgb(0, 0, 0)
            cr.rectangle(0, 0, allocation.width, allocation.height)
            cr.paint()

        Gtk.ToolItem.do_draw(self, cr)

        if self._palette and self._palette.is_up():
            invoker = self._palette.props.invoker
            invoker.draw_rectangle(cr, self._palette)

        return False
Esempio n. 4
0
class ButtonStrokeColor(Gtk.ToolItem):
    """Class to manage the Stroke Color of a Button"""

    __gtype_name__ = 'BrushColorToolButton'
    __gsignals__ = {'color-set': (GObject.SignalFlags.RUN_FIRST, None,
                    tuple())}

    def __init__(self, activity, **kwargs):
        self._activity = activity
        self.properties = self._activity.area.tool
        self._accelerator = None
        self._tooltip = None
        self._palette_invoker = ToolInvoker()
        self._palette = None
        self._selected_tool = None

        GObject.GObject.__init__(self, **kwargs)

        # The Gtk.ToolButton has already added a normal button.
        # Replace it with a ColorButton
        self.color_button = BrushButton(has_invoker=False)
        self.add(self.color_button)
        self.color_button.set_brush_size(self.properties['line size'])
        self.color_button.set_brush_shape(self.properties['line shape'])
        self.color_button.set_stamp_size(20)

        # The following is so that the behaviour on the toolbar is correct.
        self.color_button.set_relief(Gtk.ReliefStyle.NONE)

        self._palette_invoker.attach_tool(self)
        self._palette_invoker.props.toggle_palette = True
        self._palette_invoker.props.lock_palette = True

        # This widget just proxies the following properties to the colorbutton
        self.color_button.connect('notify::color', self.__notify_change)
        self.color_button.connect('notify::icon-name', self.__notify_change)
        self.color_button.connect('notify::icon-size', self.__notify_change)
        self.color_button.connect('notify::title', self.__notify_change)
        self.color_button.connect('can-activate-accel',
                                  self.__button_can_activate_accel_cb)

        self.create_palette()

    def __button_can_activate_accel_cb(self, button, signal_id):
        # Accept activation via accelerators regardless of this widget's state
        return True

    def __notify_change(self, widget, pspec):
        self.color_button.set_color(self.get_color())
        self.notify(pspec.name)

    def _color_button_cb(self, widget, pspec):
        color = self.get_color()
        self.set_stroke_color(color)

    def create_palette(self):
        self._palette = self.get_child().create_palette()

        color_palette_hbox = self._palette._picker_hbox
        self.custom_box = Gtk.VBox()

        self.vbox_brush_options = Gtk.VBox()

        # This is where we set restrictions for size:
        # Initial value, minimum value, maximum value, step

        adj = Gtk.Adjustment(self.properties['line size'], 1.0, 100.0, 1.0)
        self.size_scale = Gtk.HScale()
        self.size_scale.set_adjustment(adj)
        self.size_scale.set_draw_value(False)
        self.size_scale.set_size_request(style.zoom(200), -1)
        self.size_label = Gtk.Label(label=_('Size'))
        self.size_label.props.halign = Gtk.Align.START
        self.vbox_brush_options.pack_start(self.size_label, True, True, 0)
        self.vbox_brush_options.pack_start(self.size_scale, True, True, 0)

        self.size_scale.connect('value-changed', self._on_value_changed)

        # Control alpha
        alpha = self.properties['alpha'] * 100
        adj_alpha = Gtk.Adjustment(alpha, 10.0, 100.0, 1.0)
        self.alpha_scale = Gtk.HScale()
        self.alpha_scale.set_adjustment(adj_alpha)
        self.alpha_scale.set_draw_value(False)
        self.alpha_scale.set_size_request(style.zoom(200), -1)
        self.alpha_label = Gtk.Label(label=_('Opacity'))
        self.alpha_label.props.halign = Gtk.Align.START
        self.vbox_brush_options.pack_start(self.alpha_label, True, True, 0)
        self.vbox_brush_options.pack_start(self.alpha_scale, True, True, 0)

        self.alpha_scale.connect('value-changed', self._on_alpha_changed)

        # User is able to choose Shapes for 'Brush' and 'Eraser'
        self.shape_box = Gtk.HBox()
        self.custom_box.pack_start(self.vbox_brush_options, True, True, 0)
        item1 = RadioToolButton()
        item1.set_icon_name('tool-shape-ellipse')

        item2 = RadioToolButton()
        item2.set_icon_name('tool-shape-rectangle')
        item2.props.group = item1

        if self.properties['line shape'] == 'circle':
            item1.set_active(True)
        else:
            item2.set_active(True)

        item1.connect('toggled', self._on_toggled, self.properties, 'circle')
        item2.connect('toggled', self._on_toggled, self.properties, 'square')

        self.shape_box.pack_start(Gtk.Label(_('Shape')), True, True, 0)
        self.shape_box.pack_start(item1, True, True, 0)
        self.shape_box.pack_start(item2, True, True, 0)

        self.vbox_brush_options.pack_start(self.shape_box, True, True, 0)

        self.keep_aspect_checkbutton = Gtk.CheckButton(_('Keep aspect'))
        ratio = self._activity.area.keep_aspect_ratio
        self.keep_aspect_checkbutton.set_active(ratio)
        self.keep_aspect_checkbutton.connect(
            'toggled', self._keep_aspect_checkbutton_toggled)
        self.vbox_brush_options.pack_start(self.keep_aspect_checkbutton, True,
                                           True, 0)

        self.custom_separator = Gtk.VSeparator()
        color_palette_hbox.pack_start(self.custom_separator, True, True,
                                      padding=style.DEFAULT_SPACING)
        color_palette_hbox.pack_start(self.custom_box, True, True,
                                      padding=style.DEFAULT_SPACING)
        color_palette_hbox.show_all()
        self._update_palette()
        return self._palette

    def _keep_aspect_checkbutton_toggled(self, checkbutton):
        self._activity.area.keep_aspect_ratio = checkbutton.get_active()

    def _update_palette(self):
        tool_name = self._selected_tool
        show_controls = ()
        show_colors = False
        if tool_name == 'brush' or tool_name is None:
            title = _('Brush properties')
            show_colors = True
            show_controls = (self.size_label, self.size_scale, self.shape_box,
                             self.alpha_label, self.alpha_scale)
        elif tool_name == 'stamp':
            show_controls = (self.size_label, self.size_scale)
            title = _('Stamp properties')
        elif tool_name == 'eraser':
            show_controls = (self.size_label, self.size_scale,
                             self.shape_box)
            title = _('Eraser properties')
        elif tool_name == 'bucket':
            show_colors = True
            title = _('Bucket properties')
        elif tool_name == 'picker':
            title = _('Picker properties')
        elif tool_name == 'marquee-rectangular':
            title = _('Select Area')
            show_controls = (self.keep_aspect_checkbutton,)
        else:
            title = ''

        self._palette._picker_hbox.show_all()
        # Hide palette color widgets except size:
        if not show_colors:
            palette_children = self._palette._picker_hbox.get_children()
            for ch in palette_children:
                if ch != self.custom_box:
                    ch.hide()
        elif not show_controls:
            self.custom_separator.hide()

        self.vbox_brush_options.show_all()
        controls = self.vbox_brush_options.get_children()
        for control in controls:
            if control not in show_controls:
                control.hide()

        # Change title:
        self.set_title(title)

        self._palette._picker_hbox.resize_children()
        self._palette._picker_hbox.queue_draw()

    def update_stamping(self):
        if self.color_button.is_stamping():
            self.size_scale.set_value(self.color_button.stamp_size)
        else:
            self.size_scale.set_value(self.color_button.brush_size)
        self._update_palette()

    def _on_alpha_changed(self, scale):
        alpha = scale.get_value() / 100.0
        self._activity.area.set_alpha(alpha)
        self.color_button.set_alpha(alpha)

    def _on_value_changed(self, scale):
        size = int(scale.get_value())
        if self.color_button.is_stamping():
            # avoid stamps too small
            if size > 5:
                self.properties['stamp size'] = size
                resized_stamp = self._activity.area.resize_stamp(size)
                self.color_button.set_resized_stamp(resized_stamp)
                self.color_button.set_stamp_size(self.properties['stamp size'])
        else:
            self.properties['line size'] = size
            self.color_button.set_brush_size(self.properties['line size'])
        self._activity.area.set_tool(self.properties)

    def _on_toggled(self, radiobutton, tool, shape):
        if radiobutton.get_active():
            self.properties['line shape'] = shape
            self.color_button.set_brush_shape(shape)
            self.color_button.set_brush_size(self.properties['line size'])

    def get_palette_invoker(self):
        return self._palette_invoker

    def set_palette_invoker(self, palette_invoker):
        self._palette_invoker.detach()
        self._palette_invoker = palette_invoker

    palette_invoker = GObject.property(
        type=object, setter=set_palette_invoker, getter=get_palette_invoker)

    def set_expanded(self, expanded):
        box = self.toolbar_box
        if not box:
            return

        if not expanded:
            self._palette_invoker.notify_popdown()
            return

        if box.expanded_button is not None:
            box.expanded_button.queue_draw()
            if box.expanded_button != self:
                box.expanded_button.set_expanded(False)
        box.expanded_button = self

    def get_toolbar_box(self):
        parent = self.get_parent()
        if not hasattr(parent, 'owner'):
            return None
        return parent.owner

    toolbar_box = property(get_toolbar_box)

    def set_color(self, color):
        self.color_button.set_color(color)

    def get_color(self):
        return self.get_child().props.color

    color = GObject.property(type=object, getter=get_color, setter=set_color)

    def set_title(self, title):
        self.get_child().props.title = title

    def get_title(self):
        return self.get_child().props.title

    title = GObject.property(type=str, getter=get_title, setter=set_title)

    def get_selected_tool(self):
        return self._selected_tool

    def set_selected_tool(self, tool_name):
        self._selected_tool = tool_name

    def do_draw(self, cr):
        if self._palette and self._palette.is_up():
            allocation = self.get_allocation()
            # draw a black background, has been done by the engine before
            cr.set_source_rgb(0, 0, 0)
            cr.rectangle(0, 0, allocation.width, allocation.height)
            cr.paint()

        Gtk.ToolItem.do_draw(self, cr)

        if self._palette and self._palette.is_up():
            invoker = self._palette.props.invoker
            invoker.draw_rectangle(cr, self._palette)

        return False
class ColorToolButton(Gtk.ToolItem):
    # This not ideal. It would be better to subclass Gtk.ToolButton, however
    # the python bindings do not seem to be powerfull enough for that.
    # (As we need to change a variable in the class structure.)

    __gtype_name__ = 'SugarColorToolButton'
    __gsignals__ = {'color-set': (GObject.SignalFlags.RUN_FIRST, None,
                                  tuple())}

    def __init__(self, icon_name='color-preview', **kwargs):
        self._accelerator = None
        self._tooltip = None
        self._palette_invoker = ToolInvoker()
        self._palette = None

        GObject.GObject.__init__(self, **kwargs)

        # The Gtk.ToolButton has already added a normal button.
        # Replace it with a ColorButton
        color_button = _ColorButton(icon_name=icon_name, has_invoker=False)
        self.add(color_button)
        color_button.show()

        # The following is so that the behaviour on the toolbar is correct.
        color_button.set_relief(Gtk.ReliefStyle.NONE)
        color_button.icon_size = Gtk.IconSize.LARGE_TOOLBAR

        self._palette_invoker.attach_tool(self)
        self._palette_invoker.props.toggle_palette = True
        self._palette_invoker.props.lock_palette = True

        # This widget just proxies the following properties to the colorbutton
        color_button.connect('notify::color', self.__notify_change)
        color_button.connect('notify::icon-name', self.__notify_change)
        color_button.connect('notify::icon-size', self.__notify_change)
        color_button.connect('notify::title', self.__notify_change)
        color_button.connect('color-set', self.__color_set_cb)
        color_button.connect('can-activate-accel',
                             self.__button_can_activate_accel_cb)

    def __button_can_activate_accel_cb(self, button, signal_id):
        # Accept activation via accelerators regardless of this widget's state
        return True

    def set_accelerator(self, accelerator):
        self._accelerator = accelerator
        setup_accelerator(self)

    def get_accelerator(self):
        return self._accelerator

    accelerator = GObject.property(type=str, setter=set_accelerator,
                                   getter=get_accelerator)

    def create_palette(self):
        self._palette = self.get_child().create_palette()
        return self._palette

    def get_palette_invoker(self):
        return self._palette_invoker

    def set_palette_invoker(self, palette_invoker):
        self._palette_invoker.detach()
        self._palette_invoker = palette_invoker

    palette_invoker = GObject.property(
        type=object, setter=set_palette_invoker, getter=get_palette_invoker)

    def set_expanded(self, expanded):
        box = self.toolbar_box
        if not box:
            return

        if not expanded:
            self._palette_invoker.notify_popdown()
            return

        if box.expanded_button is not None:
            box.expanded_button.queue_draw()
            if box.expanded_button != self:
                box.expanded_button.set_expanded(False)
        box.expanded_button = self

    def get_toolbar_box(self):
        parent = self.get_parent()
        if not hasattr(parent, 'owner'):
            return None
        return parent.owner

    toolbar_box = property(get_toolbar_box)

    def set_color(self, color):
        self.get_child().props.color = color

    def get_color(self):
        return self.get_child().props.color

    color = GObject.property(type=object, getter=get_color, setter=set_color)

    def set_icon_name(self, icon_name):
        self.get_child().props.icon_name = icon_name

    def get_icon_name(self):
        return self.get_child().props.icon_name

    icon_name = GObject.property(type=str,
                                 getter=get_icon_name, setter=set_icon_name)

    def set_icon_size(self, icon_size):
        self.get_child().props.icon_size = icon_size

    def get_icon_size(self):
        return self.get_child().props.icon_size

    icon_size = GObject.property(type=int,
                                 getter=get_icon_size, setter=set_icon_size)

    def set_title(self, title):
        self.get_child().props.title = title

    def get_title(self):
        return self.get_child().props.title

    title = GObject.property(type=str, getter=get_title, setter=set_title)

    def do_draw(self, cr):
        if self._palette and self._palette.is_up():
            allocation = self.get_allocation()
            # draw a black background, has been done by the engine before
            cr.set_source_rgb(0, 0, 0)
            cr.rectangle(0, 0, allocation.width, allocation.height)
            cr.paint()

        Gtk.ToolItem.do_draw(self, cr)

        if self._palette and self._palette.is_up():
            invoker = self._palette.props.invoker
            invoker.draw_rectangle(cr, self._palette)

        return False

    def __notify_change(self, widget, pspec):
        self.notify(pspec.name)

    def __color_set_cb(self, widget):
        self.emit('color-set')
class ColorToolButton(Gtk.ToolItem):
    # This not ideal. It would be better to subclass Gtk.ToolButton, however
    # the python bindings do not seem to be powerfull enough for that.
    # (As we need to change a variable in the class structure.)

    __gtype_name__ = 'SugarColorToolButton'
    __gsignals__ = {'color-set': (GObject.SignalFlags.RUN_FIRST, None,
                                  tuple())}

    def __init__(self, icon_name='color-preview', **kwargs):
        self._accelerator = None
        self._tooltip = None
        self._palette_invoker = ToolInvoker()
        self._palette = None

        GObject.GObject.__init__(self, **kwargs)

        # The Gtk.ToolButton has already added a normal button.
        # Replace it with a ColorButton
        color_button = _ColorButton(icon_name=icon_name, has_invoker=False)
        self.add(color_button)
        color_button.show()

        # The following is so that the behaviour on the toolbar is correct.
        color_button.set_relief(Gtk.ReliefStyle.NONE)
        color_button.icon_size = Gtk.IconSize.LARGE_TOOLBAR

        self._palette_invoker.attach_tool(self)
        self._palette_invoker.props.toggle_palette = True
        self._palette_invoker.props.lock_palette = True

        # This widget just proxies the following properties to the colorbutton
        color_button.connect('notify::color', self.__notify_change)
        color_button.connect('notify::icon-name', self.__notify_change)
        color_button.connect('notify::icon-size', self.__notify_change)
        color_button.connect('notify::title', self.__notify_change)
        color_button.connect('color-set', self.__color_set_cb)
        color_button.connect('can-activate-accel',
                             self.__button_can_activate_accel_cb)

    def __button_can_activate_accel_cb(self, button, signal_id):
        # Accept activation via accelerators regardless of this widget's state
        return True

    def set_accelerator(self, accelerator):
        '''
        Sets keyboard shortcut that activates this button.

        Args:
            accelerator(string): accelerator to be set. Should be in
            form <modifier>Letter
            Find about format here :
            https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html#gtk-accelerator-parse

        Example:
            set_accelerator(self, 'accel')
        '''
        self._accelerator = accelerator
        setup_accelerator(self)

    def get_accelerator(self):
        '''
        Returns the above accelerator string.
        '''
        return self._accelerator

    accelerator = GObject.Property(type=str, setter=set_accelerator,
                                   getter=get_accelerator)

    def create_palette(self):
        '''
        The create_palette function is called when the palette needs to be
        invoked.  For example, when the user has right clicked the icon or
        the user has hovered over the icon for a long time.

        The create_palette will only be called once or zero times.  The palette
        returned will be stored and re-used if the user invokes the palette
        multiple times.

        Your create_palette implementation does not need to
        :any:`Gtk.Widget.show` the palette, as this will be done by the
        invoker.  However, you still need to show
        the menu items, etc that you place in the palette.

        Returns:

            sugar3.graphics.palette.Palette, or None to indicate that you
            do not want a palette shown

        The default implementation returns None, to indicate no palette should
        be shown.
        '''
        self._palette = self.get_child().create_palette()
        return self._palette

    def get_palette_invoker(self):
        return self._palette_invoker

    def set_palette_invoker(self, palette_invoker):
        self._palette_invoker.detach()
        self._palette_invoker = palette_invoker

    palette_invoker = GObject.Property(
        type=object, setter=set_palette_invoker, getter=get_palette_invoker)

    def set_expanded(self, expanded):
        box = self.toolbar_box
        if not box:
            return

        if not expanded:
            self._palette_invoker.notify_popdown()
            return

        if box.expanded_button is not None:
            box.expanded_button.queue_draw()
            if box.expanded_button != self:
                box.expanded_button.set_expanded(False)
        box.expanded_button = self

    def get_toolbar_box(self):
        parent = self.get_parent()
        if not hasattr(parent, 'owner'):
            return None
        return parent.owner

    toolbar_box = property(get_toolbar_box)

    def set_color(self, color):
        '''
        Sets the color of the colorbutton
        '''
        self.get_child().props.color = color

    def get_color(self):
        '''
        Gets the above set color string.
        '''
        return self.get_child().props.color

    color = GObject.Property(type=object, getter=get_color, setter=set_color)

    def set_icon_name(self, icon_name):
        '''
        Sets the icon for the tool button from a named themed icon.
        If it is none then no icon will be shown.

        Args:
            icon_name(string): The name for a themed icon.
            It can be set as 'None' too.

        Example:
            set_icon_name('view-radial')
        '''
        self.get_child().props.icon_name = icon_name

    def get_icon_name(self):
        '''
        The get_icon_name() method returns the value of the icon_name
        property that contains the name of a themed icon or None.
        '''
        return self.get_child().props.icon_name

    icon_name = GObject.Property(type=str,
                                 getter=get_icon_name, setter=set_icon_name)

    def set_icon_size(self, icon_size):
        '''
        Sets the size of icons in the colorbutton.
        '''
        self.get_child().props.icon_size = icon_size

    def get_icon_size(self):
        '''
        Gets the size of icons in the colorbutton.
        '''
        return self.get_child().props.icon_size

    icon_size = GObject.Property(type=int,
                                 getter=get_icon_size, setter=set_icon_size)

    def set_title(self, title):
        '''
        The set_title() method sets the "title" property to the value of
        title. The "title" property contains the string that is used to
        set the colorbutton title.
        '''
        self.get_child().props.title = title

    def get_title(self):
        '''
        Return the above title string.
        '''
        return self.get_child().props.title

    title = GObject.Property(type=str, getter=get_title, setter=set_title)

    def do_draw(self, cr):
        if self._palette and self._palette.is_up():
            allocation = self.get_allocation()
            # draw a black background, has been done by the engine before
            cr.set_source_rgb(0, 0, 0)
            cr.rectangle(0, 0, allocation.width, allocation.height)
            cr.paint()

        Gtk.ToolItem.do_draw(self, cr)

        if self._palette and self._palette.is_up():
            invoker = self._palette.props.invoker
            invoker.draw_rectangle(cr, self._palette)

        return False

    def __notify_change(self, widget, pspec):
        self.notify(pspec.name)

    def __color_set_cb(self, widget):
        self.emit('color-set')