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