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
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
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
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
# -----------------------------------------------------------------------------
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.
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",
46
class SimpleColorSelectorPaletteItem(gtk.DrawingArea): # pylint: disable-msg=R0904,C0301
47
"""An item of the color selecctor palette"""
49
def __init__(self, color=None):
50
gtk.DrawingArea.__init__(self)
51
self.__gobject_init__()
54
self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
56
self.connect("expose_event", self.on_expose)
57
self.connect("configure_event", self.on_configure)
60
"""Draws the widget"""
61
alloc = self.get_allocation()
62
alloc_w, alloc_h = alloc[2], alloc[3]
64
cr_ctxt = self.window.cairo_create() # pylint: disable-msg=E1101
65
gdkcontext = gtk.gdk.CairoContext(cr_ctxt)
68
if self.color is not None:
69
my_color = gtk.gdk.color_parse(self.color)
70
gdkcontext.set_source_color(my_color)
72
gdkcontext.set_source_rgba(0, 0, 0, 0)
73
gdkcontext.rectangle(0, 0, alloc_w, alloc_h)
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)
82
# If selected draw a symbol
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)
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)
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)
103
def on_expose(self, widget, params): # pylint: disable-msg=W0613
104
"""Callback: redraws the widget when it is exposed"""
107
def on_configure(self, widget, params): # pylint: disable-msg=W0613
108
"""Callback: redraws the widget when it is exposed"""
113
def set_color(self, color):
114
"""Defines the widget color"""
117
def set_selected(self, sel):
118
"""Toggle the selected state of the widget"""
122
def get_selected(self):
123
"""Returns the selected state of the widget"""
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."""
131
def __init__(self, width=9, colors=None, custom_colors=None):
132
gtk.VBox.__init__(self)
133
self.__gobject_init__()
137
self.colors = DEFAULT_PALETTE
140
if custom_colors is None:
141
self.custom_colors = []
143
self.custom_colors = custom_colors
146
self.buttons_lookup = {}
147
self.selected_col = None
148
# Build up the widget
150
self.custom_palette = None
151
self.__build_palette()
152
self.__build_custom_palette()
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)
165
self.palette.destroy()
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)
175
self.palette.add(vbox)
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)
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()
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)
204
self.custom_palette.destroy()
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
215
self.custom_palette.add(cc_vbox)
216
cc_vbox.set_spacing(4)
218
cc_vbox.pack_start(cc_hbox)
219
for i in xrange(len(self.custom_colors)):
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
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()
243
self.custom_palette.show_all()
246
def on_color_clicked(self, widget, event): # pylint: disable-msg=W0613
247
"""Callback: when a color is clicked, update the model and
249
# if re-click: unselect
250
if self.selected_col == widget:
251
self.selected_col.set_selected(False)
252
self.selected_col = None
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")
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")
281
color_dialog.destroy()
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
289
def get_custom_colors(self):
290
"""Return the list of custom-defined colors"""
291
return self.custom_colors
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()
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")
313
def get_selected_color(self):
314
"""Return the selected state of a particular color"""
315
if self.selected_col is None:
318
return self.selected_col.color
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]
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
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, ())