Ejemplo n.º 1
0
class ColorPicker(gtk.Widget):
    """
  A widget to select colors from the screen

  This displays the currently selected color and handles grabbing a color
  from anywhere on screen.

  It also allows dragging and dropping of colors from and onto the widget.

  Signals:
    'save-color' - save the current color
  """
    data_path = os.path.join(os.path.dirname(__file__), 'data')
    icon_path = os.path.join(data_path, 'icons')

    __gsignals__ = {
        'save-color': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
    }

    def __init__(self):
        """ Initialize color picker """
        super(ColorPicker, self).__init__()
        self.pick_rate = 60
        self.color = Color()
        self.mag = None
        self.picking = 0
        self.pick_timeout = None
        self.save_on_release = False

    def set_magnifier(self, mag):
        """
    Set the magnifier widget
    This is needed so that we can pick colors directly from the magnified
    image when possible
    """
        self.mag = mag

    def set_color(self, color):
        """
    Set the color object to store selected colors in

    This color object is updated when a new color is selected or dropped
    on the widget.

    To explicitly change the selected color, do not use this function, but
    instead call set_rgb() or set_hsv() on the current color object.
    """
        self.color = color
        self.color.connect('changed', self.color_changed)

    def color_changed(self, color):
        """ Callback for color changes """
        if not self.flags() & gtk.REALIZED: return

        r, g, b = self.color.rgb16()
        col = self.gc.get_colormap().alloc_color(r, g, b, False, False)
        self.gc.set_foreground(col)
        self.queue_draw()

    def pick_immediate(self, x, y, magnifier):
        """
    Select the color at the specified pixel

    The coordinates are relative to the screen.
    """

        if self.flags() & gtk.REALIZED == False: return

        if magnifier:
            r, g, b = self.mag.raw_pixbuf.get_pixels_array()[y, x]
        else:
            # grab raw screen data
            self.raw_pixbuf.get_from_drawable(gdk.get_default_root_window(),
                                              gdk.colormap_get_system(), x, y,
                                              0, 0, self.raw_width,
                                              self.raw_height)

            #pull out rgb value
            #XXX first time this is called generates a warning and doesn't work.
            #    all subsequent times are fine. why?
            r, g, b = self.raw_pixbuf.get_pixels_array()[0, 0]

        self.color.set_rgb(r, g, b)

    def cb_pick_timeout(self):
        """ Callback for pick timeout """
        # repeat time until we've realized the widget
        if self.flags() & gtk.REALIZED == False:
            return True

        # widget is realized, so grab data and end timer
        self.pick_immediate(self.pick_x, self.pick_y, self.pick_mag)
        self.pick_timeout = None
        return False

    def cb_drag_set_color(self, color, x, y):
        """ Drag set color callback """
        self.color.set_rgb(*color.rgb())
        return True

    def cb_drag_get_color(self):
        """ Drag get color callback """
        return self.color

    def pick(self, x, y, magnifier=False):
        """
    Select the color at the specified location

    If magnifier is True, then the location is specified in pixels relative to the magnifier's unmagnified image.
    Otherwise, the location is specified in pixels relative to the screen.

    This does not immediately select the color, but instead sets a timeout
    so that the color selection occurs at most self.pick_rate times per
    second.
    """
        self.pick_x, self.pick_y = int(x), int(y)
        self.pick_mag = magnifier

        if (self.pick_timeout == None):
            self.pick_timeout = glib.timeout_add(1000 / self.pick_rate,
                                                 self.cb_pick_timeout)

    def pick_start(self):
        self.picking = True
        gdk.pointer_grab(
            self.window, False, gdk.POINTER_MOTION_MASK | gdk.BUTTON_PRESS_MASK
            | gdk.BUTTON_RELEASE_MASK, None, self.cursor, 0L)
        self.grab_add()

    def pick_stop(self):
        self.picking = False
        gdk.pointer_ungrab()
        self.grab_remove()

    def cb_button_press(self, widget, event):
        """ Callback for mouse button press events """
        if (event.button == 1):
            if self.picking:
                self.pick_stop()
            else:
                self.save_on_release = True

    def cb_button_release(self, widget, event):
        """ Callback for mouse button release events """
        if (event.button == 1 and self.save_on_release):
            self.save_on_release = False
            self.emit('save-color')

    def cb_drag_begin(self, widget, event):
        """ Callback for mouse drag begin events """
        self.save_on_release = False

    def cb_motion_notify(self, widget, event):
        """ Callback from mouse motion notify events """
        if (self.picking):
            # check if we are over the magnified image. if so, select from it
            a = self.allocation
            r = self.mag.allocation
            ox, oy = self.mag.origin()
            x = a.x + event.x - r.x
            y = a.y + event.y - r.y
            zx = int((x - ox) / self.mag.zoom)
            zy = int((y - oy) / self.mag.zoom)

            if (self.mag.has_data and (x >= 0 and x < r.width)
                    and (y >= 0 and y < r.height)
                    and (zx >= 0 and zx < self.mag.raw_width)
                    and (zy >= 0 and zy < self.mag.raw_height)):
                self.pick(zx, zy, True)

            # otherwise, select from screen
            else:
                root_w, root_h = gdk.get_default_root_window().get_size()
                x = event.x_root
                y = event.y_root

                if (x < 0): x = 0
                if (x >= root_w): x = root_w - 1
                if (y < 0): y = 0
                if (y >= root_h): y = root_h - 1

                self.pick(x, y, False)

    def do_realize(self):
        """ Realize the widget """
        self.set_flags(self.flags() | gtk.REALIZED)

        self.window = gdk.Window(self.get_parent_window(),
                                 width=self.allocation.width,
                                 height=self.allocation.height,
                                 window_type=gdk.WINDOW_CHILD,
                                 wclass=gdk.INPUT_OUTPUT,
                                 event_mask=self.get_events()
                                 | gdk.EXPOSURE_MASK
                                 | gdk.BUTTON1_MOTION_MASK
                                 | gdk.BUTTON_PRESS_MASK
                                 | gdk.BUTTON_RELEASE_MASK
                                 | gdk.POINTER_MOTION_MASK
                                 | gdk.POINTER_MOTION_HINT_MASK)

        self.window.set_user_data(self)
        self.style.attach(self.window)
        self.style.set_background(self.window, gtk.STATE_NORMAL)
        self.window.move_resize(*self.allocation)
        self.gc = self.window.new_gc()
        self.color_changed(self.color)

        pbuf = gdk.pixbuf_new_from_file(
            os.path.join(self.icon_path, "dropper.png"))
        if pbuf:
            self.cursor = gdk.Cursor(self.window.get_display(), pbuf, 8, 21)

        self.set_tooltip_text(
            'Click and drag:\n  Left: drag-n-drop color\n\nLeft click: add to palette'
        )

        self.connect("motion-notify-event", self.cb_motion_notify)
        self.connect("button-press-event", self.cb_button_press)
        self.connect("button-release-event", self.cb_button_release)
        self.connect("drag-begin", self.cb_drag_begin)

        self.dnd_helper = ColorDndHelper(self, self.cb_drag_set_color,
                                         self.cb_drag_get_color,
                                         gtk.gdk.BUTTON1_MASK)

        self.raw_width = 1
        self.raw_height = 1
        self.raw_pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8,
                                     self.raw_width, self.raw_height)

    def do_unrealize(self):
        """ Unrealize widget """
        self.window.destroy()

    def do_size_request(self, requisition):
        """ Set default size """
        requisition.height = 40
        requisition.width = 40

    def do_size_allocation(self, allocation):
        """ Handle size changes """
        if self.flags() & gtk.REALIZED:
            self.window.move_resize(*allocation)

    def do_expose_event(self, event):
        """
    Draw the widget

    Just a big rectangle of the currently selected color
    """
        self.window.draw_rectangle(self.gc, True, event.area.x, event.area.y,
                                   event.area.width, event.area.height)
Ejemplo n.º 2
0
class ColorPicker(gtk.Widget):
  """
  A widget to select colors from the screen

  This displays the currently selected color and handles grabbing a color
  from anywhere on screen.

  It also allows dragging and dropping of colors from and onto the widget.

  Signals:
    'save-color' - save the current color
  """
  data_path = os.path.join(os.path.dirname(__file__), 'data')
  icon_path = os.path.join(data_path, 'icons')

  __gsignals__ = {
      'save-color': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
  }

  def __init__(self):
    """ Initialize color picker """
    super(ColorPicker, self).__init__()
    self.pick_rate = 60
    self.color = Color()
    self.mag = None
    self.picking = 0
    self.pick_timeout = None
    self.save_on_release = False

  def set_magnifier(self, mag):
    """
    Set the magnifier widget
    This is needed so that we can pick colors directly from the magnified
    image when possible
    """
    self.mag = mag

  def set_color(self, color):
    """
    Set the color object to store selected colors in

    This color object is updated when a new color is selected or dropped
    on the widget.

    To explicitly change the selected color, do not use this function, but
    instead call set_rgb() or set_hsv() on the current color object.
    """
    self.color = color
    self.color.connect('changed', self.color_changed)

  def color_changed(self, color):
    """ Callback for color changes """
    if not self.flags() & gtk.REALIZED: return

    r,g,b = self.color.rgb16()
    col = self.gc.get_colormap().alloc_color(r, g, b, False, False)
    self.gc.set_foreground(col)
    self.queue_draw()

  def pick_immediate(self, x, y, magnifier):
    """
    Select the color at the specified pixel

    The coordinates are relative to the screen.
    """

    if self.flags() & gtk.REALIZED == False: return

    if magnifier:
      r,g,b = self.mag.raw_pixbuf.get_pixels_array()[y,x]
    else:
      # grab raw screen data
      self.raw_pixbuf.get_from_drawable(
          gdk.get_default_root_window(),
          gdk.colormap_get_system(),
          x, y,
          0, 0,
          self.raw_width, self.raw_height)

      #pull out rgb value
      #XXX first time this is called generates a warning and doesn't work.
      #    all subsequent times are fine. why?
      r,g,b = self.raw_pixbuf.get_pixels_array()[0,0]

    self.color.set_rgb(r,g,b)

  def cb_pick_timeout(self):
    """ Callback for pick timeout """
    # repeat time until we've realized the widget
    if self.flags() & gtk.REALIZED == False:
      return True

    # widget is realized, so grab data and end timer
    self.pick_immediate(self.pick_x, self.pick_y, self.pick_mag)
    self.pick_timeout = None
    return False

  def cb_drag_set_color(self, color, x, y):
    """ Drag set color callback """
    self.color.set_rgb(*color.rgb())
    return True

  def cb_drag_get_color(self):
    """ Drag get color callback """
    return self.color

  def pick(self, x, y, magnifier = False):
    """
    Select the color at the specified location

    If magnifier is True, then the location is specified in pixels relative to the magnifier's unmagnified image.
    Otherwise, the location is specified in pixels relative to the screen.

    This does not immediately select the color, but instead sets a timeout
    so that the color selection occurs at most self.pick_rate times per
    second.
    """
    self.pick_x, self.pick_y = int(x), int(y)
    self.pick_mag = magnifier

    if (self.pick_timeout == None):
      self.pick_timeout = glib.timeout_add(1000 / self.pick_rate, self.cb_pick_timeout)


  def pick_start(self):
    self.picking = True
    gdk.pointer_grab(self.window, False, gdk.POINTER_MOTION_MASK | gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK, None, self.cursor, 0L)
    self.grab_add()

  def pick_stop(self):
    self.picking = False
    gdk.pointer_ungrab()
    self.grab_remove()

  def cb_button_press(self, widget, event):
    """ Callback for mouse button press events """
    if (event.button == 1):
      if self.picking:
        self.pick_stop()
      else:
        self.save_on_release = True

  def cb_button_release(self, widget, event):
    """ Callback for mouse button release events """
    if (event.button == 1 and self.save_on_release):
      self.save_on_release = False
      self.emit('save-color')

  def cb_drag_begin(self, widget, event):
    """ Callback for mouse drag begin events """
    self.save_on_release = False

  def cb_motion_notify(self, widget, event):
    """ Callback from mouse motion notify events """
    if (self.picking):
      # check if we are over the magnified image. if so, select from it
      a = self.allocation
      r = self.mag.allocation
      ox,oy = self.mag.origin()
      x = a.x + event.x - r.x
      y = a.y + event.y - r.y
      zx = int((x-ox)/self.mag.zoom)
      zy = int((y-oy)/self.mag.zoom)

      if (self.mag.has_data and
          (x >= 0 and x < r.width) and
          (y >= 0 and y < r.height) and
          (zx >= 0 and zx < self.mag.raw_width) and
          (zy >= 0 and zy < self.mag.raw_height)):
        self.pick(zx, zy, True)

      # otherwise, select from screen
      else:
        root_w, root_h = gdk.get_default_root_window().get_size()
        x = event.x_root
        y = event.y_root

        if (x < 0): x = 0
        if (x >= root_w): x = root_w - 1
        if (y < 0): y = 0
        if (y >= root_h): y = root_h - 1

        self.pick(x, y, False)

  def do_realize(self):
    """ Realize the widget """
    self.set_flags(self.flags() | gtk.REALIZED)

    self.window = gdk.Window(
        self.get_parent_window(),
        width = self.allocation.width,
        height = self.allocation.height,
        window_type = gdk.WINDOW_CHILD,
        wclass = gdk.INPUT_OUTPUT,
        event_mask = self.get_events()
          | gdk.EXPOSURE_MASK 
          | gdk.BUTTON1_MOTION_MASK
          | gdk.BUTTON_PRESS_MASK
          | gdk.BUTTON_RELEASE_MASK
          | gdk.POINTER_MOTION_MASK
          | gdk.POINTER_MOTION_HINT_MASK)


    self.window.set_user_data(self)
    self.style.attach(self.window)
    self.style.set_background(self.window, gtk.STATE_NORMAL)
    self.window.move_resize(*self.allocation)
    self.gc = self.window.new_gc()
    self.color_changed(self.color)

    pbuf = gdk.pixbuf_new_from_file(os.path.join(self.icon_path, "dropper.png"))
    if pbuf:
      self.cursor = gdk.Cursor(self.window.get_display(), pbuf, 8, 21);

    self.set_tooltip_text('Click and drag:\n  Left: drag-n-drop color\n\nLeft click: add to palette')

    self.connect("motion-notify-event", self.cb_motion_notify)
    self.connect("button-press-event", self.cb_button_press)
    self.connect("button-release-event", self.cb_button_release)
    self.connect("drag-begin", self.cb_drag_begin)

    self.dnd_helper = ColorDndHelper(self, self.cb_drag_set_color, self.cb_drag_get_color, gtk.gdk.BUTTON1_MASK)

    self.raw_width = 1
    self.raw_height = 1
    self.raw_pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8, self.raw_width, self.raw_height)

  def do_unrealize(self):
    """ Unrealize widget """
    self.window.destroy()

  def do_size_request(self, requisition):
    """ Set default size """
    requisition.height = 40
    requisition.width = 40

  def do_size_allocation(self, allocation):
    """ Handle size changes """
    if self.flags() & gtk.REALIZED:
      self.window.move_resize(*allocation)

  def do_expose_event(self, event):
    """
    Draw the widget

    Just a big rectangle of the currently selected color
    """
    self.window.draw_rectangle(self.gc, True, 
        event.area.x, event.area.y,
        event.area.width, event.area.height)
Ejemplo n.º 3
0
class Elicit:

  def save(self):
    old_filename = self.palette.filename
    self.palette.save()
    if old_filename == None:
      self.gconf.set_string('/apps/elicit/palette', os.path.basename(self.palette.filename))

  def quit(self, widget=None, data=None):
    self.save()
    gtk.main_quit()

  def main(self):
    gtk.main()

  def show(self):
    self.win.show_all()

  def hide(self):
    self.win.hide()

  def grab(self, x, y, w ,h):
    self.mag.grab(x,y,w,h)

  def mag_grid_toggled(self, mag):
    self.grid_check.set_active(mag.show_grid)

  def mag_zoom_changed(self, mag):
    self.gconf.set_int('/apps/elicit/zoom_level', mag.zoom)

  def mag_measure_changed(self, mag):
    if mag.measure_rect:
      text = "[%dx%d] (%.1f diag) " % (mag.measure_rect.width,
          mag.measure_rect.height,
          math.sqrt(mag.measure_rect.width**2 + mag.measure_rect.height**2)
          )
    else:
      text = ""

    self.measure_label.set_text(text)

  def mag_location_changed(self, mag):
    if mag.screen_rect:
      text = "  (%d,%d) %dx%d" % (mag.screen_rect.x, mag.screen_rect.y,
          mag.screen_rect.width, mag.screen_rect.height)
    else:
      text = ""

    self.mag_label.set_text(text)

  def grid_check_toggled(self, check):
    self.gconf.set_bool('/apps/elicit/show_grid', check.get_active())

  def zoom_spin_value_changed(self, spin):
    self.gconf.set_int('/apps/elicit/zoom_level', int(spin.get_value()))

  def picker_save_color(self, picker):
    c = Color()
    c.set_rgb(*picker.color.rgb())
    self.palette.append(c)
    self.palette_view.select(c)
    self.palette.save()

  def palette_view_select_color(self, palette_view, color):
    if color == None:
      self.color_name_entry.set_text("")
      self.color_name_entry.set_sensitive(False)
    else:
      self.gconf.set_string('/apps/elicit/color', color.hex())
      self.color_name_entry.set_text(color.name)
      self.color_name_entry.set_sensitive(True)

  def palette_view_delete_color(self, palette_view, color):
    self.palette.remove(color)
    self.palette.save()

  def color_name_entry_changed(self, color_name_entry):
    if self.palette and self.palette_view.selected:
      self.palette_view.selected.name = color_name_entry.get_text()
      self.palette.save()

  def color_changed(self, color):
    self.colorspin['r'].set_value(self.color.r)
    self.colorspin['g'].set_value(self.color.g)
    self.colorspin['b'].set_value(self.color.b)
    self.colorspin['h'].set_value(self.color.h)
    self.colorspin['s'].set_value(self.color.s)
    self.colorspin['v'].set_value(self.color.v)
    self.hex_entry.set_text(self.color.hex())
    if self.palette_view.selected and color.hex() != self.palette_view.selected.hex():
      self.palette_view.select(None)

    h,s,v = self.color.hsv()
    if self.wheel:
      self.wheel.set_color(h/360.,s,v)
    self.gconf.set_string('/apps/elicit/color', self.color.hex())

  def color_spin_rgb_changed(self, spin):
    r,g,b = self.color.rgb()
    if spin == self.colorspin['r']:
      r = spin.get_value()
    elif spin == self.colorspin['g']:
      g = spin.get_value()
    elif spin == self.colorspin['b']:
      b = spin.get_value()
    self.color.set_rgb(r,g,b)

  def color_spin_hsv_changed(self, spin):
    h,s,v = self.color.hsv()
    if spin == self.colorspin['h']:
      h = spin.get_value()
    elif spin == self.colorspin['s']:
      s = spin.get_value()
    elif spin == self.colorspin['v']:
      v = spin.get_value()
    self.color.set_hsv(h,s,v)

  def hex_entry_changed(self, entry):
    text = entry.get_text()
    if not text: return

    if ((text[0] == '#' and len(text) == 7) or
        (text[0] != '#' and len(text) == 6)):
      try:
        self.color.set_hex(text)
      except ValueError:
        #XXX indicate that the value is bad somehow
        pass

  def palette_combo_selected(self, combo, palette):
    if (self.palette != palette):
      if (palette.filename):
        self.gconf.set_string('/apps/elicit/palette', os.path.basename(palette.filename))
      if self.palette:
        old_filename = self.palette.filename
        self.palette.save()

        #update palette list with new filename
        if old_filename != self.palette.filename:
          index = self.palette_list.index_of_palette(self.palette)
          if index != None:
            self.palette_list[index][1] = self.palette.filename

      self.palette = palette
      self.palette_view.set_palette(self.palette)
      self.color_name_entry.set_sensitive(False)

  def add_palette(self, button):
    p = Palette()
    p.name = "Untitled Palette"
    self.palette_list.append(p)
    self.palette_combo.select(self.palette_list.index_of_palette(p))

  def delete_palette(self, button):
    if not self.palette: return

    d = gtk.MessageDialog(self.win,
        gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING,
        gtk.BUTTONS_OK_CANCEL, None)

    d.set_markup("Are you sure you want to delete the palette <b>%s</b>? This cannot be undone." % self.palette.name)
    response = d.run()
    d.destroy()
    if response == gtk.RESPONSE_OK:
      p = self.palette
      self.palette_combo.remove(self.palette_list.index_of_palette(p))
      p.delete()

  def select_color_clicked(self, button):
    sel_action = self.actiongroup.get_action("Select Color")
    sel_action.activate()

  def magnify_clicked(self, button):
    mag_action = self.actiongroup.get_action("Magnify")
    mag_action.activate()

  def build_menu(self):
    uimanager = gtk.UIManager()
    accelgroup = uimanager.get_accel_group()
    self.accelgroup = accelgroup
    self.win.add_accel_group(accelgroup)

    actiongroup = gtk.ActionGroup('ElicitActions')
    self.actiongroup = actiongroup

    actiongroup.add_actions([
      ('Quit', gtk.STOCK_QUIT, '_Quit', '<Ctrl>q', 'Quit Elicit', self.action_quit),
      ('Save', gtk.STOCK_QUIT, '_Save Palette', '<Ctrl>s', 'Save Palette', self.action_save),
      ('About', gtk.STOCK_ABOUT, '_About', None, 'About Elicit', self.action_about),
      ('Magnify', gtk.STOCK_ABOUT, '_Magnify', '<Ctrl>z', 'Start Magnifying', self.action_magnify),
      ('Select Color', gtk.STOCK_ABOUT, 'Select _Color', '<Ctrl>d', 'Start Selecting Color', self.action_pick_color),
      ('File', None, '_File'),
      ('Action', None, '_Action'),
      ('Help', None, '_Help')
      ])

    uimanager.insert_action_group(actiongroup)
    uimanager.add_ui_from_string("""
    <ui>
      <menubar name="ElicitMain">
        <menu action="File">
          <menuitem action="Save"/>
          <menuitem action="Quit"/>
        </menu>
        <menu action="Action">
          <menuitem action="Magnify"/>
          <menuitem action="Select Color"/>
        </menu>
        <menu action="Help">
          <menuitem action="About"/>
        </menu>
      </menubar>
    </ui>
    """)
    menubar = uimanager.get_widget("/ElicitMain")
    return menubar

  def build_gui(self):
    self.win = gtk.Window()
    self.win.set_title("Elicit")
    self.win.set_icon_name('rephorm-elicit')
    self.win.connect('destroy', self.quit, None)

    vbox = gtk.VBox(False, 2)
    self.win.add(vbox)

    menubar = self.build_menu()
    vbox.pack_start(menubar, False)

    # notebook with magnifier, etc
    hbox = gtk.HBox(False, 0)
    vbox.pack_start(hbox, True, True)
    notebook = gtk.Notebook()
    self.notebook = notebook
    hbox.pack_start(notebook, True, True, padding=HPAD)

    # magnifier tab
    mag_vbox = gtk.VBox(False, 2)
    mag_tab_icon = gtk.Image()
    mag_tab_icon.set_from_file(os.path.join(self.icon_path, "magnify-16.png"))
    notebook.append_page(mag_vbox, mag_tab_icon)

    # the magnifier
    hbox = gtk.HBox(False, 0)
    mag_vbox.pack_start(hbox, True, True)

    frame = gtk.Frame()
    frame.set_shadow_type(gtk.SHADOW_IN)
    hbox.pack_start(frame, True, True, padding=HPAD)

    self.mag = Magnifier()
    frame.add(self.mag)
    self.mag.connect('zoom-changed', self.mag_zoom_changed)
    self.mag.connect('grid-toggled', self.mag_grid_toggled)
    self.mag.connect('measure-changed', self.mag_measure_changed)
    self.mag.connect('location-changed', self.mag_location_changed)

    # magnifier information (coordinates)
    hbox = gtk.HBox(False, 0)
    mag_vbox.pack_start(hbox, False)

    self.mag_label = gtk.Label()
    hbox.pack_start(self.mag_label, False, padding=HPAD)

    self.measure_label = gtk.Label()
    hbox.pack_end(self.measure_label, False)

    # magnifier tools
    hbox = gtk.HBox(False, 0)
    mag_vbox.pack_start(hbox, False)

    button = gtk.Button()
    button.set_relief(gtk.RELIEF_NONE)
    img = gtk.Image()
    img.set_from_file(os.path.join(self.icon_path, "magnify-button.png"));
    button.set_image(img)
    button.set_tooltip_text("Start Magnifying\n(Left Click to stop)")
    button.connect('clicked', self.magnify_clicked);
    hbox.pack_end(button, False, padding=HPAD)

    check = gtk.CheckButton("Show Grid")
    check.set_active(self.mag.show_grid)
    check.connect('toggled', self.grid_check_toggled)
    hbox.pack_start(check, padding=HPAD)
    self.grid_check = check

    spin = gtk.SpinButton()
    spin.set_range(1,50)
    spin.set_increments(1,10)
    spin.set_value(self.mag.zoom)
    spin.connect('value-changed', self.zoom_spin_value_changed)
    hbox.pack_end(spin, False, padding=HPAD)
    self.zoom_spin = spin

    zoom_label = gtk.Label("Zoom:")
    hbox.pack_end(zoom_label, False)

    # color wheel
    wheel_frame = gtk.Frame()
    wheel_tab_icon = gtk.Image()
    wheel_tab_icon.set_from_file(os.path.join(self.icon_path, "color-wheel-16.png"))
    notebook.append_page(wheel_frame, wheel_tab_icon)

    if hasattr(gtk, 'HSV'):
      wheel = gtk.HSV()
      self.wheel = wheel
      wheel_frame.add(wheel)

      wheel_frame.connect('size-allocate', self.wheel_size_allocate)
      wheel_frame.connect('size-request', self.wheel_size_request)
      wheel.connect('changed', self.wheel_changed)
    else:
      self.wheel = None
      label = gtk.Label("The color wheel requires gtk ver. 2.28 or higher.")
      wheel_frame.add(label)

    # swatch and eyedropper button
    hbox = gtk.HBox(False, 0)
    vbox.pack_start(hbox, False)

    button = gtk.Button()
    button.set_relief(gtk.RELIEF_NONE)
    img = gtk.Image()
    img.set_from_file(os.path.join(self.icon_path, "dropper-button.png"));
    button.set_image(img)
    button.set_tooltip_text("Start Selecting Color\n(Left Click to stop)")
    button.connect('clicked', self.select_color_clicked);
    hbox.pack_end(button, False, padding=HPAD)

    frame = gtk.Frame()
    frame.set_shadow_type(gtk.SHADOW_IN)
    hbox.pack_start(frame, True, True, padding=HPAD)

    self.colorpicker = ColorPicker()
    frame.add(self.colorpicker)
    self.colorpicker.connect('save-color', self.picker_save_color)
    self.colorpicker.set_magnifier(self.mag)

    # color values (sliders and spinboxes)
    hbox = gtk.HBox(False, 5)
    vbox.pack_start(hbox, False)

    self.colorspin = {}

    table = gtk.Table(6,4)
    hbox.pack_start(table, True, True, padding=HPAD)

    row = 0
    for type in ("r","g","b"):
      label = gtk.Label(type.upper())
      table.attach(label, 0,1,row,row+1,0,0,2,2)
      cslider = CSlider(self.color, type)
      table.attach(cslider, 1,2,row,row+1,gtk.FILL|gtk.EXPAND,gtk.EXPAND,2,2)
      spin = gtk.SpinButton()
      spin.set_range(0,255)
      spin.set_increments(1,10)
      spin.connect('value-changed', self.color_spin_rgb_changed)
      table.attach(spin, 2,3,row,row+1,0,gtk.EXPAND,2,2)
      self.colorspin[type] = spin
      row += 1

    row = 0
    for type in ("h","s","v"):
      label = gtk.Label(type.upper())
      table.attach(label, 3,4,row,row+1,0,0,2,2)
      cslider = CSlider(self.color, type)
      table.attach(cslider, 4,5,row,row+1,gtk.FILL|gtk.EXPAND,gtk.EXPAND,2,2)
      spin = gtk.SpinButton()
      if type == 'h':
        spin.set_range(0,360)
        spin.set_increments(1,10)
      else:
        spin.set_digits(2)
        spin.set_range(0,1.0)
        spin.set_increments(.01,.1)
      spin.connect('value-changed', self.color_spin_hsv_changed)
      table.attach(spin, 5,6,row,row+1,0,gtk.EXPAND,2,2)
      self.colorspin[type] = spin
      row += 1

    self.hex_label = gtk.Label("Hex")
    table.attach(self.hex_label,0,1,3,4,gtk.FILL,gtk.EXPAND,2,2)

    self.hex_entry = gtk.Entry()
    table.attach(self.hex_entry,1,6,3,4,gtk.FILL,gtk.EXPAND,2,2)
    self.hex_entry.connect('changed', self.hex_entry_changed)

    sep = gtk.HSeparator()
    vbox.pack_start(sep, False)

    # palette tools
    hbox = gtk.HBox(False, 5)
    vbox.pack_start(hbox, False)

    hbox.pack_start(gtk.Label("Palette:"), False, padding=HPAD)

    self.palette_combo = PaletteCombo()
    hbox.pack_start(self.palette_combo)
    self.palette_combo.connect('selected', self.palette_combo_selected)

    button = gtk.Button()
    button.set_image(gtk.image_new_from_stock(gtk.STOCK_ADD,gtk.ICON_SIZE_BUTTON))
    button.set_tooltip_text("Add Palette")
    button.set_relief(gtk.RELIEF_NONE)
    button.connect('clicked', self.add_palette)
    hbox.pack_start(button, False)

    button = gtk.Button()
    button.set_image(gtk.image_new_from_stock(gtk.STOCK_DELETE,gtk.ICON_SIZE_BUTTON))
    button.set_tooltip_text("Delete Palette")
    button.set_relief(gtk.RELIEF_NONE)
    button.connect('clicked', self.delete_palette)
    hbox.pack_start(button, False, padding=HPAD)


    # palette view
    hbox = gtk.HBox(False, 5)
    vbox.pack_start(hbox, False)

    frame = gtk.Frame()
    frame.set_shadow_type(gtk.SHADOW_IN)
    hbox.pack_start(frame, True, padding=HPAD)

    self.palette_view = PaletteView()
    self.palette_view.connect('select-color', self.palette_view_select_color)
    self.palette_view.connect('delete-color', self.palette_view_delete_color)
    frame.add(self.palette_view)

    # color name entry
    hbox = gtk.HBox(False, 5)
    vbox.pack_start(hbox, False, padding=VPAD)

    hbox.pack_start(gtk.Label("Color Name:"), False, padding=HPAD)

    self.color_name_entry = gtk.Entry()
    self.color_name_entry.set_sensitive(False)
    self.color_name_entry.connect('changed', self.color_name_entry_changed)
    hbox.pack_start(self.color_name_entry, True, True, padding=HPAD)

  def init_config(self):
    self.gconf = gconf.client_get_default()
    self.gconf.add_dir('/apps/elicit', preload=True)
    self.gconf_id = self.gconf.notify_add("/apps/elicit", self.config_changed)

  def load_config(self):
    color = self.gconf.get_string('/apps/elicit/color')
    if color: self.color.set_hex(color)

    zoom_level = self.gconf.get_int('/apps/elicit/zoom_level')
    self.mag.set_zoom(zoom_level)
    self.zoom_spin.set_value(self.mag.zoom)

    grab_rate = self.gconf.get_int('/apps/elicit/grab_rate')
    if grab_rate > 0: self.mag.grab_rate = grab_rate

    show_grid = self.gconf.get_bool('/apps/elicit/show_grid')
    self.mag.set_show_grid(show_grid)
    self.grid_check.set_active(self.mag.show_grid)

    palette = self.gconf.get_string('/apps/elicit/palette')
    if not palette: palette = 'default.gpl'
    index = self.palette_list.index_of_file(palette)
    if index == None:
      index = 0
    self.palette_combo.select(index)

  def wheel_size_allocate(self, frame, allocation):
    style = frame.get_style()
    focus_width = frame.style_get_property('focus-line-width')
    focus_padding = frame.style_get_property('focus-padding')
    size = (min (allocation.width, allocation.height) -
            2 * max (style.xthickness, style.ythickness) -
            2 * (focus_width + focus_padding))

    self.wheel.set_metrics(int(size), int(size / 10))

  def wheel_size_request(self, frame, requisition):
    focus_width = frame.style_get_property('focus-line-width')
    focus_padding = frame.style_get_property('focus-padding')
    requisition.width = 2 * (focus_width + focus_padding)
    requisition.height = 2 * (focus_width + focus_padding)

  def wheel_changed(self, wheel):
    h,s,v = wheel.get_color()
    h *= 360
    self.color.set_hsv(h,s,v)

  def config_changed(self, client, gconf_id, entry, user_data):
    key = entry.key[13:]

    if key == 'color':
      hex = entry.value.get_string()
      self.color.set_hex(hex)
    elif key == 'zoom_level':
      self.mag.set_zoom(entry.value.get_int())
      self.zoom_spin.set_value(self.mag.zoom)
    elif key == 'show_grid':
      self.mag.set_show_grid(entry.value.get_bool())
      self.grid_check.set_active(self.mag.show_grid)
    elif key == 'palette':
      palette = entry.value.get_string()
      if not palette: palette = 'default.gpl'
      self.palette_combo.select(self.palette_list.index_of_file(palette))
    elif key == 'grab_rate':
      self.mag.set_grab_rate(entry.value.get_int())

  def action_quit(self, action):
    self.quit()

  def action_save(self, action):
    self.save()

  def action_about(self, action):
    a = gtk.AboutDialog()
    a.set_name(appinfo.name)
    a.set_version(appinfo.version)
    a.set_website(appinfo.website)
    a.set_authors([appinfo.author])
    a.set_logo_icon_name('rephorm-elicit')

    a.connect('response', lambda dialog,respons: dialog.destroy())
    a.show()

  def action_magnify(self, action):
    if self.mag.grabbing:
      self.mag.grab_stop()

    if self.colorpicker.picking:
      self.colorpicker.pick_stop()

    self.mag.grab_start()

  def action_pick_color(self, action):
    if self.colorpicker.picking:
      self.colorpicker.pick_stop()

    if self.mag.grabbing:
      self.mag.grab_stop()

    self.colorpicker.pick_start()

  def __init__(self):
    self.palette = None
    self.color = Color()
    self.color.connect('changed', self.color_changed)

    self.icon_path = os.path.join(os.path.dirname(__file__), 'data', 'icons')

    self.init_config()
    self.build_gui()

    self.palette_dir = os.path.join(base.save_config_path(appinfo.pkgname), 'palettes')
    Palette.PaletteDir = self.palette_dir

    self.palette_list = PaletteList()
    self.palette_list.load(self.palette_dir)
    self.palette_combo.set_model(self.palette_list)

    # no palettes, so create default
    if len(self.palette_list) == 0:
      palette = Palette()
      palette.name = "Default Palette"
      palette.filename = os.path.join(self.palette_dir, 'default.gpl')
      self.palette_list.append(palette)

      self.palette_combo.select(0)

    self.colorpicker.set_color(self.color)

    self.load_config()
Ejemplo n.º 4
0
class Elicit:
    def save(self):
        old_filename = self.palette.filename
        self.palette.save()
        if old_filename == None:
            self.gconf.set_string("/apps/elicit/palette", os.path.basename(self.palette.filename))

    def quit(self, widget=None, data=None):
        self.save()
        gtk.main_quit()

    def main(self):
        gtk.main()

    def show(self):
        self.win.show_all()

    def hide(self):
        self.win.hide()

    def grab(self, x, y, w, h):
        self.mag.grab(x, y, w, h)

    def mag_grid_toggled(self, mag):
        self.grid_check.set_active(mag.show_grid)

    def mag_zoom_changed(self, mag):
        self.gconf.set_int("/apps/elicit/zoom_level", mag.zoom)

    def mag_measure_changed(self, mag):
        if mag.measure_rect:
            text = "[%dx%d] (%.1f diag) " % (
                mag.measure_rect.width,
                mag.measure_rect.height,
                math.sqrt(mag.measure_rect.width ** 2 + mag.measure_rect.height ** 2),
            )
        else:
            text = ""

        self.measure_label.set_text(text)

    def mag_location_changed(self, mag):
        if mag.screen_rect:
            text = "  (%d,%d) %dx%d" % (
                mag.screen_rect.x,
                mag.screen_rect.y,
                mag.screen_rect.width,
                mag.screen_rect.height,
            )
        else:
            text = ""

        self.mag_label.set_text(text)

    def grid_check_toggled(self, check):
        self.gconf.set_bool("/apps/elicit/show_grid", check.get_active())

    def zoom_spin_value_changed(self, spin):
        self.gconf.set_int("/apps/elicit/zoom_level", int(spin.get_value()))

    def picker_save_color(self, picker):
        c = Color()
        c.set_rgb(*picker.color.rgb())
        self.palette.append(c)
        self.palette_view.select(c)

    def palette_view_select_color(self, palette_view, color):
        if color == None:
            self.color_name_entry.set_text("")
            self.color_name_entry.set_sensitive(False)
        else:
            self.gconf.set_string("/apps/elicit/color", color.hex())
            self.color_name_entry.set_text(color.name)
            self.color_name_entry.set_sensitive(True)

    def palette_view_delete_color(self, palette_view, color):
        self.palette.remove(color)

    def wheel_changed(self, wheel):
        self.wheel.was_adjusting = self.wheel.is_adjusting()
        h, s, v = wheel.get_color()
        h = h * 360.0
        # I need Color to emit 'changed' even when it is not changed
        # because otherwise the wheel doesn't set the color when the user
        # stops moving the cursor. Yeah, hackish.
        self.color.set_hsv(h, s, v, do_emit=True)

    def color_name_entry_changed(self, color_name_entry):
        if self.palette and self.palette_view.selected:
            self.palette_view.selected.name = color_name_entry.get_text()

    def color_changed(self, color):

        # TODO: FIXME  I think there is a bug in here
        for type in "r", "g", "b", "h", "s", "v", "c", "m", "y", "k":
            self.colorspin[type].handler_block(self.h_ids[type])
        self.hex_entry.handler_block(self.h_ids["hex"])
        self.wheel.handler_block(self.h_ids["wheel"])
        # self.name_combobox.handler_block(self.h_ids['name'])

        self.colorspin["r"].set_value(self.color.r)
        self.colorspin["g"].set_value(self.color.g)
        self.colorspin["b"].set_value(self.color.b)

        self.colorspin["h"].set_value(self.color.h)
        self.colorspin["s"].set_value(self.color.s)
        self.colorspin["v"].set_value(self.color.v)

        self.colorspin["c"].set_value(self.color.c)
        self.colorspin["m"].set_value(self.color.m)
        self.colorspin["y"].set_value(self.color.y)
        self.colorspin["k"].set_value(self.color.k)

        self.hex_entry.set_text(self.color.hex())
        if not self.wheel.was_adjusting:
            self.name_palette.closest(self.color.r, self.color.g, self.color.b)

        h, s, v = color.hsv()
        self.wheel.set_color(h / 360.0, s, v)

        if self.palette_view.selected and color.hex() != self.palette_view.selected.hex():
            self.palette_view.select(None)

        for type in "r", "g", "b", "h", "s", "v", "c", "m", "y", "k":
            self.colorspin[type].handler_unblock(self.h_ids[type])
        self.hex_entry.handler_unblock(self.h_ids["hex"])
        self.wheel.handler_unblock(self.h_ids["wheel"])
        # self.name_combobox.handler_unblock(self.h_ids['name'])

    def color_spin_rgb_changed(self, spin):
        r, g, b = self.color.rgb()
        if spin == self.colorspin["r"]:
            r = spin.get_value()
        elif spin == self.colorspin["g"]:
            g = spin.get_value()
        elif spin == self.colorspin["b"]:
            b = spin.get_value()
        self.color.set_rgb(r, g, b)

    def color_spin_cmyk_changed(self, spin):
        c, m, y, k = self.color.cmyk()
        if spin == self.colorspin["c"]:
            c = spin.get_value()
        elif spin == self.colorspin["m"]:
            m = spin.get_value()
        elif spin == self.colorspin["y"]:
            y = spin.get_value()
        elif spin == self.colorspin["k"]:
            k = spin.get_value()
        self.color.set_cmyk(c, m, y, k)

    def color_spin_hsv_changed(self, spin):
        h, s, v = self.color.hsv()
        if spin == self.colorspin["h"]:
            h = spin.get_value()
        elif spin == self.colorspin["s"]:
            s = spin.get_value()
        elif spin == self.colorspin["v"]:
            v = spin.get_value()
        self.color.set_hsv(h, s, v)

    def name_combobox_changed(self, name_combobox):
        i = name_combobox.get_active()
        color = name_combobox.get_model()[i][0]
        self.color.set_hex(color)

    def hex_entry_changed(self, entry):
        text = entry.get_text()
        if text != "":
            if (text[0] == "#" and len(text) == 7) or (text[0] != "#" and len(text) == 6):
                self.color.set_hex(text)

    def palette_combo_selected(self, combo, palette):
        if self.palette != palette:
            if palette.filename:
                self.gconf.set_string("/apps/elicit/palette", os.path.basename(palette.filename))
            if self.palette:
                old_filename = self.palette.filename
                self.palette.save()

                # update palette list with new filename
                if old_filename != self.palette.filename:
                    index = self.palette_list.index_of_palette(self.palette)
                    if index != None:
                        self.palette_list[index][1] = self.palette.filename

            self.palette = palette
            self.palette_view.set_palette(self.palette)
            self.color_name_entry.set_sensitive(False)

    def add_palette(self, button):
        p = Palette()
        p.name = "Untitled Palette"
        self.palette_list.append(p)
        self.palette_combo.select(self.palette_list.index_of_palette(p))

    def delete_palette(self, button):
        if not self.palette:
            return

        d = gtk.MessageDialog(self.win, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK_CANCEL, None)

        d.set_markup(
            "Are you sure you want to delete the palette <b>%s</b>? This cannot be undone." % self.palette.name
        )
        response = d.run()
        d.destroy()
        if response == gtk.RESPONSE_OK:
            p = self.palette
            self.palette_combo.remove(self.palette_list.index_of_palette(p))
            p.delete()

    def select_color_clicked(self, button):
        sel_action = self.actiongroup.get_action("Select Color")
        sel_action.activate()

    def magnify_clicked(self, button):
        mag_action = self.actiongroup.get_action("Magnify")
        mag_action.activate()

    def build_menu(self):
        uimanager = gtk.UIManager()
        accelgroup = uimanager.get_accel_group()
        self.accelgroup = accelgroup
        self.win.add_accel_group(accelgroup)

        actiongroup = gtk.ActionGroup("ElicitActions")
        self.actiongroup = actiongroup

        actiongroup.add_actions(
            [
                ("Quit", gtk.STOCK_QUIT, "_Quit", "<Ctrl>q", "Quit Elicit", self.action_quit),
                ("Save", gtk.STOCK_QUIT, "_Save Palette", "<Ctrl>s", "Save Palette", self.action_save),
                ("About", gtk.STOCK_ABOUT, "_About", None, "About Elicit", self.action_about),
                ("Magnify", gtk.STOCK_ABOUT, "_Magnify", "<Ctrl>z", "Start Magnifying", self.action_magnify),
                (
                    "Select Color",
                    gtk.STOCK_ABOUT,
                    "Select _Color",
                    "<Ctrl>d",
                    "Start Selecting Color",
                    self.action_pick_color,
                ),
                ("File", None, "_File"),
                ("Action", None, "_Action"),
                ("Help", None, "_Help"),
            ]
        )

        uimanager.insert_action_group(actiongroup)
        uimanager.add_ui_from_string(
            """
    <ui>
      <menubar name="ElicitMain">
        <menu action="File">
          <menuitem action="Save"/>
          <menuitem action="Quit"/>
        </menu>
        <menu action="Action">
          <menuitem action="Magnify"/>
          <menuitem action="Select Color"/>
        </menu>
        <menu action="Help">
          <menuitem action="About"/>
        </menu>
      </menubar>
    </ui>
    """
        )
        menubar = uimanager.get_widget("/ElicitMain")
        return menubar

    def build_gui(self):
        self.win = gtk.Window()
        self.win.set_title("Elicit")
        self.win.set_icon_name("rephorm-elicit")
        self.win.connect("destroy", self.quit, None)

        main_vbox = gtk.VBox(False, 2)

        menubar = self.build_menu()
        w_vbox = gtk.VBox()
        w_vbox.pack_start(menubar, False)
        w_vbox.pack_end(main_vbox, True)
        # main_vbox.pack_start(menubar, False)
        main_vbox.set_border_width(7)
        self.win.add(w_vbox)

        frame_mag = gtk.Frame()
        frame_mag.set_shadow_type(gtk.SHADOW_NONE)

        frame_mag_inner = gtk.Frame()
        frame_mag_inner.set_shadow_type(gtk.SHADOW_ETCHED_IN)
        self.mag = Magnifier()
        frame_mag_inner.add(self.mag)
        mag_vbox = gtk.VBox()
        mag_vbox.pack_start(frame_mag_inner)
        frame_mag.add(mag_vbox)
        self.mag.connect("zoom-changed", self.mag_zoom_changed)
        self.mag.connect("grid-toggled", self.mag_grid_toggled)
        self.mag.connect("measure-changed", self.mag_measure_changed)
        self.mag.connect("location-changed", self.mag_location_changed)

        hbox = gtk.HBox(False, 0)

        self.mag_label = gtk.Label()
        hbox.pack_start(self.mag_label, False)

        icon_path = os.path.join(os.path.dirname(__file__), "data", "icons")

        frame_wheel = gtk.Frame()
        frame_wheel.set_shadow_type(gtk.SHADOW_NONE)
        self.notebook = gtk.Notebook()
        frame_mag_label = gtk.Image()
        frame_mag_label.set_from_file(os.path.join(icon_path, "magnify-16.png"))
        frame_wheel_label = gtk.Image()
        frame_wheel_label.set_from_file(os.path.join(icon_path, "color-wheel-16.png"))
        frame_wheel.connect("size-allocate", self.wheel_size_allocate)
        frame_wheel.connect("size-request", self.wheel_size_request)
        self.notebook.append_page(frame_mag, frame_mag_label)
        self.notebook.append_page(frame_wheel, frame_wheel_label)

        # TODO: do not hardcode tha palette path!
        # self.name_palette = NamePalette('../ntc.txt', self.color)
        self.name_palette = NamePalette("/etc/X11/rgb.txt", self.color)
        self.name_treeview = ColorName(self.name_palette)
        # self.h_ids['name'] = self.name_combobox.connect('changed', self.name_combobox_changed)
        # name_hbox.pack_start(self.name_combobox, True, True, 2)

        frame_name_palette = gtk.Frame()
        frame_name_palette_label = gtk.Image()
        frame_name_palette_label.set_from_file(os.path.join(icon_path, "palette-16.png"))
        self.notebook.append_page(frame_name_palette, frame_name_palette_label)
        name_scroll = gtk.ScrolledWindow()
        name_scroll.add(self.name_treeview)
        frame_name_palette.add(name_scroll)

        self.wheel = gtk.HSV()
        self.wheel.was_adjusting = False
        frame_wheel.add(self.wheel)
        self.h_ids["wheel"] = self.wheel.connect("changed", self.wheel_changed)

        main_vbox.pack_start(self.notebook, True, True)
        mag_vbox.pack_start(hbox, False)
        main_vbox.set_spacing(5)

        pick_view_hbox = gtk.HBox()
        button = gtk.Button()
        button.set_relief(gtk.RELIEF_NONE)
        img = gtk.Image()
        img.set_from_file(os.path.join(icon_path, "dropper-button.png"))
        button.set_image(img)
        button.set_tooltip_text("Start Selecting Color\n(Left Click to stop)")
        button.connect("clicked", self.select_color_clicked)
        pick_view_hbox.pack_start(button, False)
        main_vbox.pack_start(pick_view_hbox)

        button = gtk.Button()
        button.set_relief(gtk.RELIEF_NONE)
        img = gtk.Image()
        img.set_from_file(os.path.join(icon_path, "magnify-button.png"))
        button.set_image(img)
        button.set_tooltip_text("Start Magnifying\n(Left Click to stop)")
        button.connect("clicked", self.magnify_clicked)
        hbox.pack_end(button, False)

        self.measure_label = gtk.Label()
        hbox.pack_end(self.measure_label, False)

        hbox = gtk.HBox(False, 5)
        mag_vbox.pack_start(hbox, False)

        check = gtk.CheckButton("Show Grid")
        check.set_active(self.mag.show_grid)
        check.connect("toggled", self.grid_check_toggled)
        hbox.pack_start(check)
        self.grid_check = check

        spin = gtk.SpinButton()
        spin.set_range(1, 50)
        spin.set_increments(1, 10)
        spin.set_value(self.mag.zoom)
        spin.connect("value-changed", self.zoom_spin_value_changed)
        hbox.pack_end(spin, False)
        self.zoom_spin = spin

        zoom_label = gtk.Label("Zoom:")
        hbox.pack_end(zoom_label, False)

        vbox = gtk.VBox(False, 5)
        main_vbox.pack_start(vbox, False)

        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_IN)
        pick_view_hbox.pack_end(frame, True, True)

        self.colorpicker = ColorPicker()
        frame.add(self.colorpicker)
        self.colorpicker.connect("save-color", self.picker_save_color)
        self.colorpicker.set_magnifier(self.mag)
        self.name_palette.connect("colorname-changed", self.colorpicker.colorname_changed)

        self.colorspin = {}
        # add RGB spinboxes
        table = gtk.Table(6, 4)
        vbox.pack_start(table, True)

        row = 0
        for type in ("r", "g", "b"):
            label = gtk.Label(type.upper())
            table.attach(label, 0, 1, row, row + 1, 0, 0, 2, 2)
            spin = gtk.SpinButton()
            spin.set_range(0, 255)
            spin.set_increments(1, 10)
            self.h_ids[type] = spin.connect("value-changed", self.color_spin_rgb_changed)
            table.attach(spin, 1, 2, row, row + 1, gtk.FILL | gtk.EXPAND, 0, 2, 2)
            self.colorspin[type] = spin
            row += 1

        row = 0
        for type in ("h", "s", "v"):
            label = gtk.Label(type.upper())
            table.attach(label, 2, 3, row, row + 1, 0, 0, 2, 2)
            spin = gtk.SpinButton()
            if type == "h":
                spin.set_range(0, 360)
                spin.set_increments(1, 10)
            else:
                spin.set_digits(2)
                spin.set_range(0, 1.0)
                spin.set_increments(0.01, 0.1)
            self.h_ids[type] = spin.connect("value-changed", self.color_spin_hsv_changed)
            table.attach(spin, 3, 4, row, row + 1, gtk.FILL | gtk.EXPAND, 0, 2, 2)
            self.colorspin[type] = spin
            row += 1

        row = 0
        for type in ("c", "m", "y", "k"):
            label = gtk.Label(type.upper())
            table.attach(label, 4, 5, row, row + 1, 0, 0, 2, 2)
            spin = gtk.SpinButton()
            spin.set_range(0, 100)
            spin.set_increments(1, 10)
            self.h_ids[type] = spin.connect("value-changed", self.color_spin_cmyk_changed)
            table.attach(spin, 5, 6, row, row + 1, gtk.FILL | gtk.EXPAND, 0, 2, 2)
            self.colorspin[type] = spin
            row += 1

        self.hex_label = gtk.Label("Hex")
        table.attach(self.hex_label, 0, 1, 3, 4, 0, 0, 2, 2)

        self.hex_entry = gtk.Entry()
        self.hex_entry.set_max_length(7)
        self.hex_entry.set_width_chars(7)
        table.attach(self.hex_entry, 1, 2, 3, 4, gtk.FILL | gtk.EXPAND, 0, 2, 2)
        self.h_ids["hex"] = self.hex_entry.connect("changed", self.hex_entry_changed)

        name_hbox = gtk.HBox()
        self.name_label = ColorLabel()
        self.name_palette.connect("colorname-changed", self.name_label.colorname_changed)
        name_hbox.pack_end(self.name_label, False, False, 2)
        vbox.pack_start(name_hbox)
        vbox.reorder_child(name_hbox, 0)

        sep = gtk.HSeparator()
        main_vbox.pack_start(sep, False)

        # palette tools
        hbox = gtk.HBox(False, 5)
        main_vbox.pack_start(hbox, False)

        # Palette tools
        hbox.pack_start(gtk.Label("Palette:"), False)

        self.palette_combo = PaletteCombo()
        hbox.pack_start(self.palette_combo)
        self.palette_combo.connect("selected", self.palette_combo_selected)

        button = gtk.Button()
        button.set_image(gtk.image_new_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON))
        button.set_tooltip_text("Add Palette")
        button.set_relief(gtk.RELIEF_NONE)
        button.connect("clicked", self.add_palette)
        hbox.pack_start(button, False)

        button = gtk.Button()
        button.set_image(gtk.image_new_from_stock(gtk.STOCK_DELETE, gtk.ICON_SIZE_BUTTON))
        button.set_tooltip_text("Delete Palette")
        button.set_relief(gtk.RELIEF_NONE)
        button.connect("clicked", self.delete_palette)
        hbox.pack_start(button, False)

        # palette view
        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_IN)
        main_vbox.pack_start(frame, False)

        self.palette_view = PaletteView()
        self.palette_view.connect("select-color", self.palette_view_select_color)
        self.palette_view.connect("delete-color", self.palette_view_delete_color)
        frame.add(self.palette_view)

        # color name entry / palette tools
        hbox = gtk.HBox(False, 5)
        main_vbox.pack_start(hbox, False)

        hbox.pack_start(gtk.Label("Color Name:"), False)

        self.color_name_entry = gtk.Entry()
        self.color_name_entry.set_sensitive(False)
        self.color_name_entry.connect("changed", self.color_name_entry_changed)
        hbox.pack_start(self.color_name_entry, True, True)

    def init_config(self):
        self.gconf = gconf.client_get_default()
        self.gconf.add_dir("/apps/elicit", preload=True)
        self.gconf_id = self.gconf.notify_add("/apps/elicit", self.config_changed)

    def load_config(self):
        color = self.gconf.get_string("/apps/elicit/color")
        if color:
            self.color.set_hex(color)

        zoom_level = self.gconf.get_int("/apps/elicit/zoom_level")
        self.mag.set_zoom(zoom_level)
        self.zoom_spin.set_value(self.mag.zoom)

        grab_rate = self.gconf.get_int("/apps/elicit/grab_rate")
        if grab_rate > 0:
            self.mag.grab_rate = grab_rate

        show_grid = self.gconf.get_bool("/apps/elicit/show_grid")
        self.mag.set_show_grid(show_grid)
        self.grid_check.set_active(self.mag.show_grid)

        palette = self.gconf.get_string("/apps/elicit/palette")
        if not palette:
            palette = "default.gpl"
        index = self.palette_list.index_of_file(palette)
        if index == None:
            index = 0
        self.palette_combo.select(index)

    def wheel_size_allocate(self, frame, allocation):
        style = frame.get_style()
        focus_width = frame.style_get_property("focus-line-width")
        focus_padding = frame.style_get_property("focus-padding")
        size = (
            min(allocation.width, allocation.height)
            - 2 * max(style.xthickness, style.ythickness)
            - 2 * (focus_width + focus_padding)
        )

        self.wheel.set_metrics(int(size), int(size / 10))

    def wheel_size_request(self, frame, requisition):
        focus_width = frame.style_get_property("focus-line-width")
        focus_padding = frame.style_get_property("focus-padding")
        requisition.width = 2 * (focus_width + focus_padding)
        requisition.height = 2 * (focus_width + focus_padding)

    def config_changed(self, client, gconf_id, entry, user_data):
        key = entry.key[13:]

        if key == "color":
            hex = entry.value.get_string()
            self.color.set_hex(hex)
        elif key == "zoom_level":
            self.mag.set_zoom(entry.value.get_int())
            self.zoom_spin.set_value(self.mag.zoom)
        elif key == "show_grid":
            self.mag.set_show_grid(entry.value.get_bool())
            self.grid_check.set_active(self.mag.show_grid)
        elif key == "palette":
            palette = entry.value.get_string()
            if not palette:
                palette = "default.gpl"
            self.palette_combo.select(self.palette_list.index_of_file(palette))
        elif key == "grab_rate":
            self.mag.set_grab_rate(entry.value.get_int())

    def action_quit(self, action):
        self.quit()

    def action_save(self, action):
        self.save()

    def action_about(self, action):
        a = gtk.AboutDialog()
        a.set_name(appinfo.name)
        a.set_version(appinfo.version)
        a.set_website(appinfo.website)
        a.set_authors([appinfo.author])
        a.set_logo_icon_name("rephorm-elicit")

        a.connect("response", lambda dialog, respons: dialog.destroy())
        a.show()

    def action_magnify(self, action):
        if self.mag.grabbing:
            self.mag.grab_stop()

        if self.colorpicker.picking:
            self.colorpicker.pick_stop()

        self.mag.grab_start()

    def action_pick_color(self, action):
        if self.colorpicker.picking:
            self.colorpicker.pick_stop()

        if self.mag.grabbing:
            self.mag.grab_stop()

        self.colorpicker.pick_start()

    def __init__(self):
        self.palette = None
        self.color = Color()
        self.h_ids = {}
        self.color.connect("changed", self.color_changed)

        self.init_config()
        self.build_gui()

        self.palette_dir = os.path.join(base.save_config_path(appinfo.pkgname), "palettes")
        Palette.PaletteDir = self.palette_dir

        self.palette_list = PaletteList()
        self.palette_list.load(self.palette_dir)
        self.palette_combo.set_model(self.palette_list)

        # no palettes, so create default
        if len(self.palette_list) == 0:
            palette = Palette()
            palette.name = "Default Palette"
            palette.filename = os.path.join(self.palette_dir, "default.gpl")
            self.palette_list.append(palette)

            self.palette_combo.select(0)

        self.colorpicker.set_color(self.color)

        self.load_config()
Ejemplo n.º 5
0
class Elicit:
    def save(self):
        old_filename = self.palette.filename
        self.palette.save()
        if old_filename == None:
            self.gconf.set_string('/apps/elicit/palette',
                                  os.path.basename(self.palette.filename))

    def quit(self, widget=None, data=None):
        self.save()
        gtk.main_quit()

    def main(self):
        gtk.main()

    def show(self):
        self.win.show_all()

    def hide(self):
        self.win.hide()

    def grab(self, x, y, w, h):
        self.mag.grab(x, y, w, h)

    def mag_grid_toggled(self, mag):
        self.grid_check.set_active(mag.show_grid)

    def mag_zoom_changed(self, mag):
        self.gconf.set_int('/apps/elicit/zoom_level', mag.zoom)

    def mag_measure_changed(self, mag):
        if mag.measure_rect:
            text = "[%dx%d] (%.1f diag) " % (
                mag.measure_rect.width, mag.measure_rect.height,
                math.sqrt(mag.measure_rect.width**2 +
                          mag.measure_rect.height**2))
        else:
            text = ""

        self.measure_label.set_text(text)

    def mag_location_changed(self, mag):
        if mag.screen_rect:
            text = "  (%d,%d) %dx%d" % (mag.screen_rect.x, mag.screen_rect.y,
                                        mag.screen_rect.width,
                                        mag.screen_rect.height)
        else:
            text = ""

        self.mag_label.set_text(text)

    def grid_check_toggled(self, check):
        self.gconf.set_bool('/apps/elicit/show_grid', check.get_active())

    def zoom_spin_value_changed(self, spin):
        self.gconf.set_int('/apps/elicit/zoom_level', int(spin.get_value()))

    def picker_save_color(self, picker):
        c = Color()
        c.set_rgb(*picker.color.rgb())
        self.palette.append(c)
        self.palette_view.select(c)
        self.palette.save()

    def palette_view_select_color(self, palette_view, color):
        if color == None:
            self.color_name_entry.set_text("")
            self.color_name_entry.set_sensitive(False)
        else:
            self.gconf.set_string('/apps/elicit/color', color.hex())
            self.color_name_entry.set_text(color.name)
            self.color_name_entry.set_sensitive(True)

    def palette_view_delete_color(self, palette_view, color):
        self.palette.remove(color)
        self.palette.save()

    def color_name_entry_changed(self, color_name_entry):
        if self.palette and self.palette_view.selected:
            self.palette_view.selected.name = color_name_entry.get_text()
            self.palette.save()

    def color_changed(self, color):
        self.colorspin['r'].set_value(self.color.r)
        self.colorspin['g'].set_value(self.color.g)
        self.colorspin['b'].set_value(self.color.b)
        self.colorspin['h'].set_value(self.color.h)
        self.colorspin['s'].set_value(self.color.s)
        self.colorspin['v'].set_value(self.color.v)
        self.hex_entry.set_text(self.color.hex())
        if self.palette_view.selected and color.hex(
        ) != self.palette_view.selected.hex():
            self.palette_view.select(None)

        h, s, v = self.color.hsv()
        if self.wheel:
            self.wheel.set_color(h / 360., s, v)
        self.gconf.set_string('/apps/elicit/color', self.color.hex())

    def color_spin_rgb_changed(self, spin):
        r, g, b = self.color.rgb()
        if spin == self.colorspin['r']:
            r = spin.get_value()
        elif spin == self.colorspin['g']:
            g = spin.get_value()
        elif spin == self.colorspin['b']:
            b = spin.get_value()
        self.color.set_rgb(r, g, b)

    def color_spin_hsv_changed(self, spin):
        h, s, v = self.color.hsv()
        if spin == self.colorspin['h']:
            h = spin.get_value()
        elif spin == self.colorspin['s']:
            s = spin.get_value()
        elif spin == self.colorspin['v']:
            v = spin.get_value()
        self.color.set_hsv(h, s, v)

    def hex_entry_changed(self, entry):
        text = entry.get_text()
        if not text: return

        if ((text[0] == '#' and len(text) == 7)
                or (text[0] != '#' and len(text) == 6)):
            try:
                self.color.set_hex(text)
            except ValueError:
                #XXX indicate that the value is bad somehow
                pass

    def palette_combo_selected(self, combo, palette):
        if (self.palette != palette):
            if (palette.filename):
                self.gconf.set_string('/apps/elicit/palette',
                                      os.path.basename(palette.filename))
            if self.palette:
                old_filename = self.palette.filename
                self.palette.save()

                #update palette list with new filename
                if old_filename != self.palette.filename:
                    index = self.palette_list.index_of_palette(self.palette)
                    if index != None:
                        self.palette_list[index][1] = self.palette.filename

            self.palette = palette
            self.palette_view.set_palette(self.palette)
            self.color_name_entry.set_sensitive(False)

    def add_palette(self, button):
        p = Palette()
        p.name = "Untitled Palette"
        self.palette_list.append(p)
        self.palette_combo.select(self.palette_list.index_of_palette(p))

    def delete_palette(self, button):
        if not self.palette: return

        d = gtk.MessageDialog(self.win, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING,
                              gtk.BUTTONS_OK_CANCEL, None)

        d.set_markup(
            "Are you sure you want to delete the palette <b>%s</b>? This cannot be undone."
            % self.palette.name)
        response = d.run()
        d.destroy()
        if response == gtk.RESPONSE_OK:
            p = self.palette
            self.palette_combo.remove(self.palette_list.index_of_palette(p))
            p.delete()

    def select_color_clicked(self, button):
        sel_action = self.actiongroup.get_action("Select Color")
        sel_action.activate()

    def magnify_clicked(self, button):
        mag_action = self.actiongroup.get_action("Magnify")
        mag_action.activate()

    def build_menu(self):
        uimanager = gtk.UIManager()
        accelgroup = uimanager.get_accel_group()
        self.accelgroup = accelgroup
        self.win.add_accel_group(accelgroup)

        actiongroup = gtk.ActionGroup('ElicitActions')
        self.actiongroup = actiongroup

        actiongroup.add_actions([
            ('Quit', gtk.STOCK_QUIT, '_Quit', '<Ctrl>q', 'Quit Elicit',
             self.action_quit),
            ('Save', gtk.STOCK_QUIT, '_Save Palette', '<Ctrl>s',
             'Save Palette', self.action_save),
            ('About', gtk.STOCK_ABOUT, '_About', None, 'About Elicit',
             self.action_about),
            ('Magnify', gtk.STOCK_ABOUT, '_Magnify', '<Ctrl>z',
             'Start Magnifying', self.action_magnify),
            ('Select Color', gtk.STOCK_ABOUT, 'Select _Color', '<Ctrl>d',
             'Start Selecting Color', self.action_pick_color),
            ('File', None, '_File'), ('Action', None, '_Action'),
            ('Help', None, '_Help')
        ])

        uimanager.insert_action_group(actiongroup)
        uimanager.add_ui_from_string("""
    <ui>
      <menubar name="ElicitMain">
        <menu action="File">
          <menuitem action="Save"/>
          <menuitem action="Quit"/>
        </menu>
        <menu action="Action">
          <menuitem action="Magnify"/>
          <menuitem action="Select Color"/>
        </menu>
        <menu action="Help">
          <menuitem action="About"/>
        </menu>
      </menubar>
    </ui>
    """)
        menubar = uimanager.get_widget("/ElicitMain")
        return menubar

    def build_gui(self):
        self.win = gtk.Window()
        self.win.set_title("Elicit")
        self.win.set_icon_name('rephorm-elicit')
        self.win.connect('destroy', self.quit, None)

        vbox = gtk.VBox(False, 2)
        self.win.add(vbox)

        menubar = self.build_menu()
        vbox.pack_start(menubar, False)

        # notebook with magnifier, etc
        hbox = gtk.HBox(False, 0)
        vbox.pack_start(hbox, True, True)
        notebook = gtk.Notebook()
        self.notebook = notebook
        hbox.pack_start(notebook, True, True, padding=HPAD)

        # magnifier tab
        mag_vbox = gtk.VBox(False, 2)
        mag_tab_icon = gtk.Image()
        mag_tab_icon.set_from_file(
            os.path.join(self.icon_path, "magnify-16.png"))
        notebook.append_page(mag_vbox, mag_tab_icon)

        # the magnifier
        hbox = gtk.HBox(False, 0)
        mag_vbox.pack_start(hbox, True, True)

        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_IN)
        hbox.pack_start(frame, True, True, padding=HPAD)

        self.mag = Magnifier()
        frame.add(self.mag)
        self.mag.connect('zoom-changed', self.mag_zoom_changed)
        self.mag.connect('grid-toggled', self.mag_grid_toggled)
        self.mag.connect('measure-changed', self.mag_measure_changed)
        self.mag.connect('location-changed', self.mag_location_changed)

        # magnifier information (coordinates)
        hbox = gtk.HBox(False, 0)
        mag_vbox.pack_start(hbox, False)

        self.mag_label = gtk.Label()
        hbox.pack_start(self.mag_label, False, padding=HPAD)

        self.measure_label = gtk.Label()
        hbox.pack_end(self.measure_label, False)

        # magnifier tools
        hbox = gtk.HBox(False, 0)
        mag_vbox.pack_start(hbox, False)

        button = gtk.Button()
        button.set_relief(gtk.RELIEF_NONE)
        img = gtk.Image()
        img.set_from_file(os.path.join(self.icon_path, "magnify-button.png"))
        button.set_image(img)
        button.set_tooltip_text("Start Magnifying\n(Left Click to stop)")
        button.connect('clicked', self.magnify_clicked)
        hbox.pack_end(button, False, padding=HPAD)

        check = gtk.CheckButton("Show Grid")
        check.set_active(self.mag.show_grid)
        check.connect('toggled', self.grid_check_toggled)
        hbox.pack_start(check, padding=HPAD)
        self.grid_check = check

        spin = gtk.SpinButton()
        spin.set_range(1, 50)
        spin.set_increments(1, 10)
        spin.set_value(self.mag.zoom)
        spin.connect('value-changed', self.zoom_spin_value_changed)
        hbox.pack_end(spin, False, padding=HPAD)
        self.zoom_spin = spin

        zoom_label = gtk.Label("Zoom:")
        hbox.pack_end(zoom_label, False)

        # color wheel
        wheel_frame = gtk.Frame()
        wheel_tab_icon = gtk.Image()
        wheel_tab_icon.set_from_file(
            os.path.join(self.icon_path, "color-wheel-16.png"))
        notebook.append_page(wheel_frame, wheel_tab_icon)

        if hasattr(gtk, 'HSV'):
            wheel = gtk.HSV()
            self.wheel = wheel
            wheel_frame.add(wheel)

            wheel_frame.connect('size-allocate', self.wheel_size_allocate)
            wheel_frame.connect('size-request', self.wheel_size_request)
            wheel.connect('changed', self.wheel_changed)
        else:
            self.wheel = None
            label = gtk.Label(
                "The color wheel requires gtk ver. 2.28 or higher.")
            wheel_frame.add(label)

        # swatch and eyedropper button
        hbox = gtk.HBox(False, 0)
        vbox.pack_start(hbox, False)

        button = gtk.Button()
        button.set_relief(gtk.RELIEF_NONE)
        img = gtk.Image()
        img.set_from_file(os.path.join(self.icon_path, "dropper-button.png"))
        button.set_image(img)
        button.set_tooltip_text("Start Selecting Color\n(Left Click to stop)")
        button.connect('clicked', self.select_color_clicked)
        hbox.pack_end(button, False, padding=HPAD)

        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_IN)
        hbox.pack_start(frame, True, True, padding=HPAD)

        self.colorpicker = ColorPicker()
        frame.add(self.colorpicker)
        self.colorpicker.connect('save-color', self.picker_save_color)
        self.colorpicker.set_magnifier(self.mag)

        # color values (sliders and spinboxes)
        hbox = gtk.HBox(False, 5)
        vbox.pack_start(hbox, False)

        self.colorspin = {}

        table = gtk.Table(6, 4)
        hbox.pack_start(table, True, True, padding=HPAD)

        row = 0
        for type in ("r", "g", "b"):
            label = gtk.Label(type.upper())
            table.attach(label, 0, 1, row, row + 1, 0, 0, 2, 2)
            cslider = CSlider(self.color, type)
            table.attach(cslider, 1, 2, row, row + 1, gtk.FILL | gtk.EXPAND,
                         gtk.EXPAND, 2, 2)
            spin = gtk.SpinButton()
            spin.set_range(0, 255)
            spin.set_increments(1, 10)
            spin.connect('value-changed', self.color_spin_rgb_changed)
            table.attach(spin, 2, 3, row, row + 1, 0, gtk.EXPAND, 2, 2)
            self.colorspin[type] = spin
            row += 1

        row = 0
        for type in ("h", "s", "v"):
            label = gtk.Label(type.upper())
            table.attach(label, 3, 4, row, row + 1, 0, 0, 2, 2)
            cslider = CSlider(self.color, type)
            table.attach(cslider, 4, 5, row, row + 1, gtk.FILL | gtk.EXPAND,
                         gtk.EXPAND, 2, 2)
            spin = gtk.SpinButton()
            if type == 'h':
                spin.set_range(0, 360)
                spin.set_increments(1, 10)
            else:
                spin.set_digits(2)
                spin.set_range(0, 1.0)
                spin.set_increments(.01, .1)
            spin.connect('value-changed', self.color_spin_hsv_changed)
            table.attach(spin, 5, 6, row, row + 1, 0, gtk.EXPAND, 2, 2)
            self.colorspin[type] = spin
            row += 1

        self.hex_label = gtk.Label("Hex")
        table.attach(self.hex_label, 0, 1, 3, 4, gtk.FILL, gtk.EXPAND, 2, 2)

        self.hex_entry = gtk.Entry()
        table.attach(self.hex_entry, 1, 6, 3, 4, gtk.FILL, gtk.EXPAND, 2, 2)
        self.hex_entry.connect('changed', self.hex_entry_changed)

        sep = gtk.HSeparator()
        vbox.pack_start(sep, False)

        # palette tools
        hbox = gtk.HBox(False, 5)
        vbox.pack_start(hbox, False)

        hbox.pack_start(gtk.Label("Palette:"), False, padding=HPAD)

        self.palette_combo = PaletteCombo()
        hbox.pack_start(self.palette_combo)
        self.palette_combo.connect('selected', self.palette_combo_selected)

        button = gtk.Button()
        button.set_image(
            gtk.image_new_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON))
        button.set_tooltip_text("Add Palette")
        button.set_relief(gtk.RELIEF_NONE)
        button.connect('clicked', self.add_palette)
        hbox.pack_start(button, False)

        button = gtk.Button()
        button.set_image(
            gtk.image_new_from_stock(gtk.STOCK_DELETE, gtk.ICON_SIZE_BUTTON))
        button.set_tooltip_text("Delete Palette")
        button.set_relief(gtk.RELIEF_NONE)
        button.connect('clicked', self.delete_palette)
        hbox.pack_start(button, False, padding=HPAD)

        # palette view
        hbox = gtk.HBox(False, 5)
        vbox.pack_start(hbox, False)

        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_IN)
        hbox.pack_start(frame, True, padding=HPAD)

        self.palette_view = PaletteView()
        self.palette_view.connect('select-color',
                                  self.palette_view_select_color)
        self.palette_view.connect('delete-color',
                                  self.palette_view_delete_color)
        frame.add(self.palette_view)

        # color name entry
        hbox = gtk.HBox(False, 5)
        vbox.pack_start(hbox, False, padding=VPAD)

        hbox.pack_start(gtk.Label("Color Name:"), False, padding=HPAD)

        self.color_name_entry = gtk.Entry()
        self.color_name_entry.set_sensitive(False)
        self.color_name_entry.connect('changed', self.color_name_entry_changed)
        hbox.pack_start(self.color_name_entry, True, True, padding=HPAD)

    def init_config(self):
        self.gconf = gconf.client_get_default()
        self.gconf.add_dir('/apps/elicit', preload=True)
        self.gconf_id = self.gconf.notify_add("/apps/elicit",
                                              self.config_changed)

    def load_config(self):
        color = self.gconf.get_string('/apps/elicit/color')
        if color: self.color.set_hex(color)

        zoom_level = self.gconf.get_int('/apps/elicit/zoom_level')
        self.mag.set_zoom(zoom_level)
        self.zoom_spin.set_value(self.mag.zoom)

        grab_rate = self.gconf.get_int('/apps/elicit/grab_rate')
        if grab_rate > 0: self.mag.grab_rate = grab_rate

        show_grid = self.gconf.get_bool('/apps/elicit/show_grid')
        self.mag.set_show_grid(show_grid)
        self.grid_check.set_active(self.mag.show_grid)

        palette = self.gconf.get_string('/apps/elicit/palette')
        if not palette: palette = 'default.gpl'
        index = self.palette_list.index_of_file(palette)
        if index == None:
            index = 0
        self.palette_combo.select(index)

    def wheel_size_allocate(self, frame, allocation):
        style = frame.get_style()
        focus_width = frame.style_get_property('focus-line-width')
        focus_padding = frame.style_get_property('focus-padding')
        size = (min(allocation.width, allocation.height) -
                2 * max(style.xthickness, style.ythickness) - 2 *
                (focus_width + focus_padding))

        self.wheel.set_metrics(int(size), int(size / 10))

    def wheel_size_request(self, frame, requisition):
        focus_width = frame.style_get_property('focus-line-width')
        focus_padding = frame.style_get_property('focus-padding')
        requisition.width = 2 * (focus_width + focus_padding)
        requisition.height = 2 * (focus_width + focus_padding)

    def wheel_changed(self, wheel):
        h, s, v = wheel.get_color()
        h *= 360
        self.color.set_hsv(h, s, v)

    def config_changed(self, client, gconf_id, entry, user_data):
        key = entry.key[13:]

        if key == 'color':
            hex = entry.value.get_string()
            self.color.set_hex(hex)
        elif key == 'zoom_level':
            self.mag.set_zoom(entry.value.get_int())
            self.zoom_spin.set_value(self.mag.zoom)
        elif key == 'show_grid':
            self.mag.set_show_grid(entry.value.get_bool())
            self.grid_check.set_active(self.mag.show_grid)
        elif key == 'palette':
            palette = entry.value.get_string()
            if not palette: palette = 'default.gpl'
            self.palette_combo.select(self.palette_list.index_of_file(palette))
        elif key == 'grab_rate':
            self.mag.set_grab_rate(entry.value.get_int())

    def action_quit(self, action):
        self.quit()

    def action_save(self, action):
        self.save()

    def action_about(self, action):
        a = gtk.AboutDialog()
        a.set_name(appinfo.name)
        a.set_version(appinfo.version)
        a.set_website(appinfo.website)
        a.set_authors([appinfo.author])
        a.set_logo_icon_name('rephorm-elicit')

        a.connect('response', lambda dialog, respons: dialog.destroy())
        a.show()

    def action_magnify(self, action):
        if self.mag.grabbing:
            self.mag.grab_stop()

        if self.colorpicker.picking:
            self.colorpicker.pick_stop()

        self.mag.grab_start()

    def action_pick_color(self, action):
        if self.colorpicker.picking:
            self.colorpicker.pick_stop()

        if self.mag.grabbing:
            self.mag.grab_stop()

        self.colorpicker.pick_start()

    def __init__(self):
        self.palette = None
        self.color = Color()
        self.color.connect('changed', self.color_changed)

        self.icon_path = os.path.join(os.path.dirname(__file__), 'data',
                                      'icons')

        self.init_config()
        self.build_gui()

        self.palette_dir = os.path.join(base.save_config_path(appinfo.pkgname),
                                        'palettes')
        Palette.PaletteDir = self.palette_dir

        self.palette_list = PaletteList()
        self.palette_list.load(self.palette_dir)
        self.palette_combo.set_model(self.palette_list)

        # no palettes, so create default
        if len(self.palette_list) == 0:
            palette = Palette()
            palette.name = "Default Palette"
            palette.filename = os.path.join(self.palette_dir, 'default.gpl')
            self.palette_list.append(palette)

            self.palette_combo.select(0)

        self.colorpicker.set_color(self.color)

        self.load_config()