~gtg-user/gtg/documenters-credits

« back to all changes in this revision

Viewing changes to GTG/gtk/browser/simple_color_selector.py

  • Committer: Izidor Matušov
  • Date: 2012-05-17 19:05:15 UTC
  • mfrom: (1175.2.25 new-tag-editor)
  • Revision ID: izidor.matusov@gmail.com-20120517190515-wbxxgcah6ogxp9an
New awesome tag editor made by Bertrand!

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
# pylint: disable-msg=W0201
 
3
# -----------------------------------------------------------------------------
 
4
# Getting Things Gnome! - a personal organizer for the GNOME desktop
 
5
# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau
 
6
#
 
7
# This program is free software: you can redistribute it and/or modify it under
 
8
# the terms of the GNU General Public License as published by the Free Software
 
9
# Foundation, either version 3 of the License, or (at your option) any later
 
10
# version.
 
11
#
 
12
# This program is distributed in the hope that it will be useful, but WITHOUT
 
13
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
14
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 
15
# details.
 
16
#
 
17
# You should have received a copy of the GNU General Public License along with
 
18
# this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
# -----------------------------------------------------------------------------
 
20
 
 
21
"""
 
22
simple_color_selector: a module defining a widget allowing to pick a color
 
23
from a palette. The widget also allows to define and add new colors.
 
24
"""
 
25
 
 
26
import pygtk
 
27
pygtk.require('2.0')
 
28
import gobject
 
29
import gtk
 
30
import math
 
31
 
 
32
from GTG import _
 
33
 
 
34
DEFAULT_PALETTE = [
 
35
  "#EF2929", "#AD7FA8", "#729FCF", "#8AE234", "#E9B96E",
 
36
  "#FCAF3E", "#FCE94F", "#EEEEEC", "#888A85",
 
37
  "#CC0000", "#75507B", "#3465A4", "#73D216", "#C17D11",
 
38
  "#F57900", "#EDD400", "#D3D7CF", "#555753",
 
39
  "#A40000", "#5C3566", "#204A87", "#4E9A06", "#8F5902",
 
40
  "#CE5C00", "#C4A000", "#BABDB6", "#2E3436",
 
41
]
 
42
 
 
43
BUTTON_WIDTH  = 36
 
44
BUTTON_HEIGHT = 24
 
45
 
 
46
class SimpleColorSelectorPaletteItem(gtk.DrawingArea): # pylint: disable-msg=R0904,C0301
 
47
    """An item of the color selecctor palette"""
 
48
 
 
49
    def __init__(self, color=None):
 
50
        gtk.DrawingArea.__init__(self)
 
51
        self.__gobject_init__()
 
52
        self.color = color
 
53
        self.selected = False
 
54
        self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
 
55
        # Connect callbacks
 
56
        self.connect("expose_event", self.on_expose)
 
57
        self.connect("configure_event", self.on_configure)
 
58
 
 
59
    def __draw(self):
 
60
        """Draws the widget"""
 
61
        alloc = self.get_allocation()
 
62
        alloc_w, alloc_h = alloc[2], alloc[3]
 
63
        # Drawing context
 
64
        cr_ctxt    = self.window.cairo_create() # pylint: disable-msg=E1101
 
65
        gdkcontext = gtk.gdk.CairoContext(cr_ctxt)
 
66
 
 
67
        # Draw rectangle
 
68
        if self.color is not None:
 
69
            my_color = gtk.gdk.color_parse(self.color)
 
70
            gdkcontext.set_source_color(my_color)
 
71
        else:
 
72
            gdkcontext.set_source_rgba(0, 0, 0, 0)
 
73
        gdkcontext.rectangle(0, 0, alloc_w, alloc_h)
 
74
        gdkcontext.fill()
 
75
 
 
76
        # Outer line
 
77
        gdkcontext.set_source_rgba(0, 0, 0, 0.30)
 
78
        gdkcontext.set_line_width(2.0)
 
79
        gdkcontext.rectangle(0, 0, alloc_w, alloc_h)
 
80
        gdkcontext.stroke()
 
81
 
 
82
          # If selected draw a symbol
 
83
        if(self.selected):
 
84
            size = alloc_h * 0.50 - 3
 
85
            pos_x = math.floor((alloc_w-size)/2)
 
86
            pos_y = math.floor((alloc_h-size)/2)
 
87
            gdkcontext.set_source_rgba(255, 255, 255, 0.80)
 
88
            gdkcontext.arc(alloc_w/2, alloc_h/2, size/2 + 3, 0, 2*math.pi)
 
89
            gdkcontext.fill()
 
90
            gdkcontext.set_line_width(1.0)
 
91
            gdkcontext.set_source_rgba(0, 0, 0, 0.20)
 
92
            gdkcontext.arc(alloc_w/2, alloc_h/2, size/2 + 3, 0, 2*math.pi)
 
93
            gdkcontext.stroke()
 
94
            gdkcontext.set_source_rgba(0, 0, 0, 0.50)
 
95
            gdkcontext.set_line_width(3.0)
 
96
            gdkcontext.move_to(pos_x       , pos_y+size/2)
 
97
            gdkcontext.line_to(pos_x+size/2, pos_y+size)
 
98
            gdkcontext.line_to(pos_x+size  , pos_y)
 
99
            gdkcontext.stroke()
 
100
 
 
101
    ### callbacks ###
 
102
 
 
103
    def on_expose(self, widget, params): # pylint: disable-msg=W0613
 
104
        """Callback: redraws the widget when it is exposed"""
 
105
        self.__draw()
 
106
 
 
107
    def on_configure(self, widget, params): # pylint: disable-msg=W0613
 
108
        """Callback: redraws the widget when it is exposed"""
 
109
        self.__draw()
 
110
 
 
111
    ### PUBLIC IF ###
 
112
 
 
113
    def set_color(self, color):
 
114
        """Defines the widget color"""
 
115
        self.color = color
 
116
 
 
117
    def set_selected(self, sel):
 
118
        """Toggle the selected state of the widget"""
 
119
        self.selected = sel
 
120
        self.queue_draw()
 
121
 
 
122
    def get_selected(self):
 
123
        """Returns the selected state of the widget"""
 
124
        return self.selected
 
125
 
 
126
 
 
127
class SimpleColorSelector(gtk.VBox): # pylint: disable-msg=R0904,C0301
 
128
    """Widget displaying a palette of colors, possibly with a button allowing to
 
129
    define new colors."""
 
130
 
 
131
    def __init__(self, width=9, colors=None, custom_colors=None):
 
132
        gtk.VBox.__init__(self)
 
133
        self.__gobject_init__()
 
134
        self.width = width
 
135
        # widget model
 
136
        if colors is None:
 
137
            self.colors = DEFAULT_PALETTE
 
138
        else:
 
139
            self.colors = colors
 
140
        if custom_colors is None:
 
141
            self.custom_colors = []
 
142
        else:
 
143
            self.custom_colors = custom_colors
 
144
        self.buttons = []
 
145
        self.cc_buttons = []
 
146
        self.buttons_lookup = {}
 
147
        self.selected_col = None
 
148
        # Build up the widget
 
149
        self.palette = None
 
150
        self.custom_palette = None
 
151
        self.__build_palette()
 
152
        self.__build_custom_palette()
 
153
        # Show toplevel
 
154
        self.show()
 
155
 
 
156
    def __reset_palette(self):
 
157
        """Destroy existing widget and reset model for base palette color"""
 
158
        if self.palette is not None:
 
159
            if self.selected_col is not None and  \
 
160
               self.selected_col.color in self.colors:
 
161
                self.selected_col = None
 
162
            for button in self.buttons:
 
163
                self.buttons_lookup.pop(button.color)
 
164
            self.buttons = []
 
165
            self.palette.destroy()
 
166
 
 
167
    def __build_palette(self):
 
168
        """Draws the palette of colors"""
 
169
        self.__reset_palette()
 
170
        # (re-)create the palette widget
 
171
        self.palette = gtk.Alignment()
 
172
        self.pack_start(self.palette)
 
173
        # Draw the palette
 
174
        vbox = gtk.VBox()
 
175
        self.palette.add(vbox)
 
176
        vbox.set_spacing(4)
 
177
        for i in xrange(len(self.colors)):
 
178
            if i % self.width == 0:
 
179
                cur_hbox = gtk.HBox()
 
180
                vbox.pack_start(cur_hbox)
 
181
            # add the color box
 
182
            img = SimpleColorSelectorPaletteItem()
 
183
            img.set_size_request( \
 
184
                BUTTON_WIDTH, BUTTON_HEIGHT)
 
185
            img.connect("button-press-event", self.on_color_clicked)
 
186
            img.set_color(self.colors[i])
 
187
            self.buttons_lookup[self.colors[i]] = img
 
188
            self.buttons.append(img)
 
189
            cur_hbox.pack_start(img, expand=False, fill=False)
 
190
            cur_hbox.set_spacing(4)
 
191
        # make palette visible
 
192
        self.palette.show_all()
 
193
 
 
194
    def __reset_custom_palette(self):
 
195
        """Destroy existing widget and reset model for custom colors"""
 
196
        if self.custom_palette is not None:
 
197
            if self.selected_col is not None and \
 
198
               self.selected_col.color in self.custom_colors:
 
199
                self.selected_col = None
 
200
            for button in self.cc_buttons:
 
201
                if button.color is not None:
 
202
                    self.buttons_lookup.pop(button.color)
 
203
            self.cc_buttons = []
 
204
            self.custom_palette.destroy()
 
205
 
 
206
    def __build_custom_palette(self):
 
207
        """Draws the palette of custom colors"""
 
208
        self.__reset_custom_palette()
 
209
        # (re-)create the palette widget
 
210
        self.custom_palette = gtk.Alignment(xscale=1.0)
 
211
        self.custom_palette.set_padding(10, 0, 0, 0)
 
212
        self.pack_start(self.custom_palette)
 
213
        # Draw the previous color palette: only one line
 
214
        cc_vbox = gtk.VBox()
 
215
        self.custom_palette.add(cc_vbox)
 
216
        cc_vbox.set_spacing(4)
 
217
        cc_hbox = gtk.HBox()
 
218
        cc_vbox.pack_start(cc_hbox)
 
219
        for i in xrange(len(self.custom_colors)):
 
220
            # add the color box
 
221
            img = SimpleColorSelectorPaletteItem()
 
222
            img.set_size_request( \
 
223
                BUTTON_WIDTH, BUTTON_HEIGHT)
 
224
            img.connect("button-press-event", self.on_color_clicked)
 
225
            if i < len(self.custom_colors):
 
226
                img.set_color(self.custom_colors[i])
 
227
                self.buttons_lookup[self.custom_colors[i]] = img
 
228
            cc_hbox.pack_start(img, expand=False, fill=False)
 
229
            cc_hbox.set_spacing(4)
 
230
            self.cc_buttons.append(img)
 
231
        # Draw the add button
 
232
        img = gtk.Image()
 
233
        img.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON)
 
234
        self.add_button = gtk.Button()
 
235
        self.add_button.set_image(img)
 
236
        self.add_button.set_label(_("Add custom color"))
 
237
        cc_vbox.pack_start(self.add_button, expand=True, fill=True)
 
238
        self.add_button.connect("clicked", self.on_color_add)
 
239
        # hide the custom palette if no custom color is defined
 
240
        if len(self.custom_colors) == 0:
 
241
            self.custom_palette.hide()
 
242
        else:
 
243
            self.custom_palette.show_all()
 
244
 
 
245
    # Handlers
 
246
    def on_color_clicked(self, widget, event): # pylint: disable-msg=W0613
 
247
        """Callback: when a color is clicked, update the model and
 
248
        notify the parent"""
 
249
        # if re-click: unselect
 
250
        if self.selected_col == widget:
 
251
            self.selected_col.set_selected(False)
 
252
            self.selected_col = None
 
253
        else:
 
254
            # if previous selection: unselect
 
255
            if self.selected_col is not None:
 
256
                self.selected_col.set_selected(False)
 
257
            self.selected_col = widget
 
258
            self.selected_col.set_selected(True)
 
259
        self.emit("color-changed")
 
260
 
 
261
    def on_color_add(self, widget): # pylint: disable-msg=W0613
 
262
        """Callback: when adding a new color, show the color definition
 
263
        window, update the model, notifies the parent."""
 
264
        color_dialog = gtk.ColorSelectionDialog(_('Choose a color'))
 
265
        colorsel = color_dialog.colorsel
 
266
        if self.selected_col is not None:
 
267
            color = gtk.gdk.color_parse(self.selected_col.color)
 
268
            colorsel.set_current_color(color) # pylint: disable-msg=E1101
 
269
        response = color_dialog.run()
 
270
        new_color = colorsel.get_current_color() # pylint: disable-msg=E1101
 
271
        # Check response_id and set color if required
 
272
        if response == gtk.RESPONSE_OK and new_color:
 
273
            strcolor = gtk.color_selection_palette_to_string([new_color])
 
274
            # Add the color to the palette and notify
 
275
            if strcolor not in self.colors:
 
276
                self.add_custom_color(strcolor)
 
277
            # Select the new color and notify
 
278
            self.set_selected_color(strcolor)
 
279
            self.emit("color-changed")
 
280
        # Clean up
 
281
        color_dialog.destroy()
 
282
 
 
283
    # public IF
 
284
 
 
285
    def has_color(self, col):
 
286
        """Returns True if the color is already present"""
 
287
        return col in self.colors or col in self.custom_colors
 
288
 
 
289
    def get_custom_colors(self):
 
290
        """Return the list of custom-defined colors"""
 
291
        return self.custom_colors
 
292
 
 
293
    def set_custom_colors(self, custom_colors):
 
294
        """Defines the list of custom-defined colors"""
 
295
        self.custom_colors = []
 
296
        for col in custom_colors:
 
297
            if col not in self.colors:
 
298
                self.custom_colors.append(col)
 
299
        # Update the custom palette
 
300
        self.__build_custom_palette()
 
301
        # hide the custom palette if no custom color is defined
 
302
        if len(self.custom_colors) == 0:
 
303
            self.custom_palette.hide()
 
304
 
 
305
    def add_custom_color(self, col):
 
306
        """Add a color to the palette, at the first position"""
 
307
        self.custom_colors.insert(0, col)
 
308
        if len(self.custom_colors) > self.width:
 
309
            self.custom_colors.pop()
 
310
        self.__build_custom_palette()
 
311
        self.emit("color-added")
 
312
 
 
313
    def get_selected_color(self):
 
314
        """Return the selected state of a particular color"""
 
315
        if self.selected_col is None:
 
316
            return None
 
317
        else:
 
318
            return self.selected_col.color
 
319
 
 
320
    def set_selected_color(self, col):
 
321
        """Defines the selected state of a displayed color"""
 
322
        self.unselect_color()
 
323
        if self.has_color(col):
 
324
            self.buttons_lookup[col].set_selected(True)
 
325
            self.selected_col = self.buttons_lookup[col]
 
326
 
 
327
    def unselect_color(self):
 
328
        """Deselect all colors"""
 
329
        if self.selected_col is not None:
 
330
            self.selected_col.set_selected(False)
 
331
            self.selected_col = None
 
332
 
 
333
 
 
334
gobject.type_register(SimpleColorSelector)
 
335
gobject.signal_new("color-changed", SimpleColorSelector,
 
336
                   gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
 
337
gobject.signal_new("color-added", SimpleColorSelector,
 
338
                   gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())