1 /*
2 Copyright (c) 2011 John Darrington
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <config.h>
19
20 #include "swatch.h"
21 #include <gtk/gtk.h>
22
23 #include "colour-dialog.h"
24
25 #include <libintl.h>
26 #define _(String) gettext (String)
27 #define N_(String) (String)
28
29
30 static GtkWidgetClass *parent_class = NULL;
31
32
33 enum
34 {
35 PROP_0 = 0,
36 PROP_COLOR,
37 PROP_TEXTURE,
38 PROP_SURFACE
39 };
40
41
42
43 static void
swatch_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)44 swatch_set_property (GObject * object,
45 guint prop_id, const GValue * value, GParamSpec * pspec)
46 {
47 GbkSwatch *sw = GBK_SWATCH (object);
48
49 switch (prop_id)
50 {
51 case PROP_COLOR:
52 {
53 GdkColor *new_color = g_value_get_boxed (value);
54
55 if (new_color)
56 {
57 if (sw->color)
58 gdk_color_free (sw->color);
59
60 sw->color = gdk_color_copy (new_color);
61
62 if (gtk_widget_get_realized (GTK_WIDGET (sw)))
63 {
64 gdk_gc_set_rgb_fg_color (sw->gc, sw->color);
65 gtk_widget_queue_draw (GTK_WIDGET (sw));
66 }
67 }
68 else
69 g_warning ("%s", "Invalid color property for swatch");
70 }
71 break;
72 case PROP_TEXTURE:
73 sw->pixbuf = g_value_get_object (value);
74
75 if (sw->pixbuf)
76 {
77 if (sw->stype == SURFACE_COLOURED)
78 sw->stype = SURFACE_TILED;
79 }
80 else
81 sw->stype = SURFACE_COLOURED;
82
83 gtk_widget_queue_draw (GTK_WIDGET (sw));
84 break;
85 case PROP_SURFACE:
86 sw->stype = g_value_get_enum (value);
87 if (sw->stype == SURFACE_COLOURED)
88 sw->pixbuf = NULL;
89
90 gtk_widget_queue_draw (GTK_WIDGET (sw));
91 break;
92 default:
93 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
94 break;
95 };
96 }
97
98
99 static void
swatch_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)100 swatch_get_property (GObject * object,
101 guint prop_id, GValue * value, GParamSpec * pspec)
102 {
103 GbkSwatch *sw = GBK_SWATCH (object);
104
105 switch (prop_id)
106 {
107 case PROP_COLOR:
108 g_value_set_boxed (value, sw->color);
109 break;
110 case PROP_TEXTURE:
111 g_value_set_object (value, sw->pixbuf);
112 break;
113 case PROP_SURFACE:
114 g_value_set_enum (value, sw->stype);
115 break;
116 default:
117 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118 break;
119 };
120 }
121
122
123 static void
gbk_swatch_init(GbkSwatch * sw)124 gbk_swatch_init (GbkSwatch * sw)
125 {
126 sw->da = gtk_drawing_area_new ();
127
128 gtk_container_add (GTK_CONTAINER (sw), sw->da);
129
130 gtk_widget_set_tooltip_text (GTK_WIDGET (sw),
131 _
132 ("A sample of the colour. You can click and select a new colour, or drag one to this space."));
133
134 sw->color = NULL;
135 sw->pixbuf = NULL;
136 sw->stype = SURFACE_COLOURED;
137 }
138
139
140
141 static gboolean
on_da_expose(GtkWidget * w,GdkEventExpose * event,gpointer data)142 on_da_expose (GtkWidget * w, GdkEventExpose * event, gpointer data)
143 {
144 GtkAllocation allocation;
145 GbkSwatch *sw = GBK_SWATCH (data);
146 gtk_widget_get_allocation (w, &allocation);
147
148 if (sw->pixbuf == NULL)
149 {
150 gdk_draw_rectangle (gtk_widget_get_window (w),
151 sw->gc,
152 TRUE,
153 0, 0, allocation.width, allocation.height);
154 }
155 else
156 {
157 GdkPixbuf *scaled_pixbuf = 0;
158 gint width, height;
159
160 gdk_drawable_get_size (gtk_widget_get_window (w), &width, &height);
161
162 scaled_pixbuf = gdk_pixbuf_scale_simple (sw->pixbuf,
163 width, height,
164 GDK_INTERP_NEAREST);
165
166 g_assert (scaled_pixbuf);
167
168 gdk_draw_pixbuf (gtk_widget_get_window (w),
169 sw->gc,
170 scaled_pixbuf,
171 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NONE, 0, 0);
172
173 g_object_unref (scaled_pixbuf);
174 }
175
176 return FALSE;
177 }
178
179
180 #define SWATCH_WIDTH 64
181 #define SWATCH_HEIGHT 64
182
183 enum
184 {
185 GBK_DRAG_FILELIST,
186 GBK_DRAG_COLOUR
187 };
188
189 static const GtkTargetEntry targets[2] =
190 {
191 {"text/uri-list", 0, GBK_DRAG_FILELIST},
192 {"application/x-color", 0, GBK_DRAG_COLOUR},
193 };
194
195 static void
on_drag_data_rx(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time,gpointer user_data)196 on_drag_data_rx (GtkWidget * widget,
197 GdkDragContext * drag_context,
198 gint x,
199 gint y,
200 GtkSelectionData * selection_data,
201 guint info, guint time, gpointer user_data)
202 {
203 gboolean success = TRUE;
204
205 if (gtk_selection_data_get_length (selection_data) < 0)
206 {
207 g_warning ("%s", "Empty drag data");
208 success = FALSE;
209 goto end;
210 }
211
212 switch (info)
213 {
214 case GBK_DRAG_COLOUR:
215 {
216 guint16 *vals;
217 GdkColor colour;
218
219 if ((gtk_selection_data_get_format (selection_data) != 16) || (gtk_selection_data_get_length (selection_data) != 8))
220 {
221 success = FALSE;
222 g_warning ("%s", "Received invalid color data");
223 goto end;
224 }
225
226 vals = (guint16 *) gtk_selection_data_get_data (selection_data);
227
228 colour.red = vals[0];
229 colour.green = vals[1];
230 colour.blue = vals[2];
231
232 g_object_set (widget, "color", &colour, NULL);
233 break;
234 }
235 case GBK_DRAG_FILELIST:
236 {
237 gchar **s =
238 g_strsplit ((const gchar *) gtk_selection_data_get_data (selection_data), "\r\n", 0);
239
240 while (*s)
241 {
242 GdkPixbuf *pixbuf = NULL;
243 gchar *utf8;
244 gchar *filename;
245
246 GError *gerr = 0;
247
248 if (strcmp (*s, "") == 0)
249 {
250 s++;
251 continue;
252 }
253
254 /* Convert to utf8. Is this necessary ?? */
255 utf8 = g_locale_to_utf8 (*s, -1, 0, 0, &gerr);
256 if (gerr)
257 {
258 g_warning ("%s", gerr->message);
259 g_clear_error (&gerr);
260 gerr = 0;
261 continue;
262 }
263
264 /* Extract the filename from the uri */
265 filename = g_filename_from_uri (utf8, 0, &gerr);
266 if (gerr)
267 {
268 g_warning ("%s", gerr->message);
269 g_clear_error (&gerr);
270 continue;
271 }
272 g_free (utf8);
273
274 pixbuf = create_pixbuf_from_file (filename, &gerr);
275
276 g_free (filename);
277
278 g_object_set (widget, "texture", pixbuf, NULL);
279
280 /* For now, just use the first one.
281 Later, we'll add some method for disambiguating multiple files
282 */
283 break;
284 s++;
285 }
286 }
287 break;
288 default:
289 g_warning ("%s", "Unsupported drag data type");
290 break;
291 }
292
293 end:
294 gtk_drag_finish (drag_context, success, FALSE, time);
295 }
296
297 static void
realize(GtkWidget * w)298 realize (GtkWidget * w)
299 {
300 GbkSwatch *sw = GBK_SWATCH (w);
301
302 if (GTK_WIDGET_CLASS (parent_class)->realize)
303 GTK_WIDGET_CLASS (parent_class)->realize (w);
304
305 gtk_widget_set_size_request (GTK_WIDGET (sw), SWATCH_WIDTH, SWATCH_HEIGHT);
306
307 GdkWindow *pw = gtk_widget_get_parent_window (sw->da);
308 sw->gc = gdk_gc_new (pw);
309
310 if (sw->color)
311 gdk_gc_set_rgb_fg_color (sw->gc, sw->color);
312
313 gtk_drag_dest_set (w, GTK_DEST_DEFAULT_ALL, targets, 2, GDK_ACTION_COPY);
314
315 g_signal_connect (sw->da, "expose-event", G_CALLBACK (on_da_expose), sw);
316 g_signal_connect (sw, "drag-data-received", G_CALLBACK (on_drag_data_rx),
317 0);
318 }
319
320
321 static void
gbk_swatch_class_init(GbkSwatchClass * klass)322 gbk_swatch_class_init (GbkSwatchClass * klass)
323 {
324 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
325 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
326 GParamSpec *color_param_spec;
327 GParamSpec *texture_param_spec;
328 GParamSpec *surface_param_spec;
329
330 parent_class = g_type_class_peek_parent (klass);
331
332 gobject_class->set_property = swatch_set_property;
333 gobject_class->get_property = swatch_get_property;
334
335 widget_class->realize = realize;
336
337 color_param_spec = g_param_spec_boxed ("color",
338 "Colour",
339 "The colour of the swatch",
340 GDK_TYPE_COLOR, G_PARAM_READWRITE);
341
342 texture_param_spec = g_param_spec_object ("texture",
343 "Texture",
344 "A pixbuf representing the texture of the swatch",
345 GDK_TYPE_PIXBUF,
346 G_PARAM_READWRITE);
347
348 surface_param_spec = g_param_spec_enum ("surface",
349 "Surface",
350 "Tiled or Mosaic",
351 GBK_TYPE_SURFACE,
352 SURFACE_COLOURED,
353 G_PARAM_READWRITE);
354
355
356 g_object_class_install_property (gobject_class,
357 PROP_COLOR, color_param_spec);
358
359 g_object_class_install_property (gobject_class,
360 PROP_TEXTURE, texture_param_spec);
361
362 g_object_class_install_property (gobject_class,
363 PROP_SURFACE, surface_param_spec);
364 }
365
366 G_DEFINE_TYPE (GbkSwatch, gbk_swatch, GTK_TYPE_TOGGLE_BUTTON);
367
368 GtkWidget *
gbk_swatch_new(void)369 gbk_swatch_new (void)
370 {
371 return g_object_new (gbk_swatch_get_type (), NULL);
372 }
373