1 /* $Id: tool_palette.c,v 1.4 2004/11/22 02:59:53 meffie Exp $
2  *
3  * GNU Paint
4  * Copyright 2000-2003, 2007  Li-Cheng (Andy) Tai
5  *
6  * Authors: Li-Cheng (Andy) Tai
7  *          Michael A. Meffie III <meffiem@neo.rr.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 3
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be
15  * useful, but WITHOUT ANY WARRANTY; without even the implied
16  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
17  * PURPOSE. See the GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26 
27 #include <string.h>
28 #include <stdlib.h>
29 #include "tool_palette.h"
30 #include "debug.h"
31 #include "util.h"
32 #include "pixmaps.h"
33 
34 #include <gtk/gtk.h>
35 #include <gdk/gdkx.h>
36 #include <glib.h>
37 
38 /* tools */
39 #include "pen.h"
40 #include "freehand.h"
41 #include "brush.h"
42 #include "shape.h"
43 #include "lasso.h"
44 #include "polyselect.h"
45 #include "rectselect.h"
46 #include "fill.h"
47 #include "text.h"
48 
49 /*
50  * Tool palette state data.
51  */
52 typedef struct _gpaint_tool_palette
53 {
54     GtkToggleButton  *selected;
55     GHashTable       *tool_hash;
56 } gpaint_tool_palette;
57 
58 /*
59  * Tool palette toggle button state.
60  */
61 typedef struct _gpaint_tool_button
62 {
63     GtkToggleButton  *widget;
64     void             (*handle_click)(struct _gpaint_tool_button*);
65     gpaint_tool      *tool;
66 } gpaint_tool_button;
67 
68 /*
69  * Shape fill/unfill toggle button state.
70  */
71 typedef struct _gpaint_fill_button
72 {
73     GtkToggleButton     *button;
74     gpaint_tool_palette *tool_palette;
75     gboolean             fill;
76 } gpaint_fill_button;
77 
78 
79 /*
80  * Button name, create function record.
81  */
82 typedef struct _gpaint_tool_create
83 {
84     const gchar *button_name;
85     const gchar *tool_name;
86     const gchar **icon;
87     ToolCreate   create;
88 } gpaint_tool_create;
89 
90 
91 /*
92  * Function table for the tool palette buttons.
93  */
94 static const gpaint_tool_create tool_table[] =
95 {
96     {"erase_button",            "eraser",     eraseOp_xpm,    eraser_create},
97     {"lasso_button",            "lasso",      lassoOp_xpm,    lasso_select_create},
98     {"fill_button",             "fill",       fillOp_xpm,     fill_create},
99     {"line_button",             "line",       lineOp_xpm,     line_shape_create},
100     {"multiline_button",        "mline",      clineOp_xpm,    multiline_shape_create},
101     {"rectangle_button",        "rectangle",  boxOp_xpm,      rectangle_shape_create},
102     {"closed_freehand_button",  "freehand",   freehandOp_xpm, closed_freehand_create},
103     {"pen_button",              "pen",        pencilOp_xpm,   pen_create},
104     {"polselect_button",        "polyselect", selpolyOp_xpm,  polygon_select_create},
105     {"rectselect_button",       "rectselect", selrectOp_xpm,  rectangle_select_create},
106     {"text_button",             "text",       textOp_xpm,     text_create},
107     {"arc_button",              "arc",        arcOp_xpm,      arc_shape_create},
108     {"curve_button",            "",           curveOp_xpm,    NULL},
109     {"oval_button",             "oval",       ovalOp_xpm,     oval_shape_create},
110     {"brush_button",            "brush",      brushOp_xpm,    paint_brush_create},
111     {0, 0, 0, 0} /* end sentinel */
112 };
113 
114 /* Local functions */
115 static void tool_palette_destroy(gpaint_tool_palette *tp);
116 static void tool_button_destroy(gpaint_tool_button *tb);
117 static const gpaint_tool_create* lookup_tool_create(const gchar *button_name);
118 static gpaint_tool_palette* lookup_tool_palette_data(GtkWidget *widget);
119 static void on_tool_select(gpaint_tool_button* tb);
120 static void on_tool_reselect(gpaint_tool_button* tb);
121 static void on_tool_deselect(gpaint_tool_button* tb);
122 static void set_button_pixmap(GtkToggleButton *button, const char **pixmap);
123 static guint tool_hash_function(gconstpointer key);
124 static gint  tool_hash_compare(gconstpointer a, gconstpointer b);
125 static void  tool_hash_change_fill(gpointer key, gpointer value, gpointer user_data);
126 
127 static void filled_button_destroy(gpaint_fill_button *fb);
128 
129 /*
130  * Look up the tool palette widget in the widget tree.
131  */
132 GtkWidget*
lookup_tool_palette(GtkWidget * widget)133 lookup_tool_palette(GtkWidget *widget)
134 {
135     GtkWidget *tp = lookup_widget(widget, "tool_palette_table");
136     g_assert(tp);
137     return tp;
138 }
139 
140 gpaint_tool*
tool_palette_get_tool(GtkWidget * widget,const gchar * name)141 tool_palette_get_tool(GtkWidget *widget, const gchar* name)
142 {
143     gpaint_tool_palette *tp;
144     gpaint_tool *tool;
145 
146     tp = lookup_tool_palette_data(widget);
147     tool = (gpaint_tool*)g_hash_table_lookup(tp->tool_hash, name);
148     return tool;
149 }
150 
151 /*
152  * Lookup the tool_palette data object. Create the initial object
153  * on the first time this function is called.
154  */
155 static gpaint_tool_palette*
lookup_tool_palette_data(GtkWidget * widget)156 lookup_tool_palette_data(GtkWidget *widget)
157 {
158     GtkObject *object = GTK_OBJECT(lookup_tool_palette(widget));
159     gpaint_tool_palette *tp =
160         (gpaint_tool_palette*)gtk_object_get_data(object, "tool_palette");
161 
162     debug_fn();
163 
164     /* create the tool palette data object the first time requested.
165      * This function may be called before the tool palette is realized. */
166     if (!tp)
167     {
168         tp = g_new(gpaint_tool_palette, 1);
169         g_assert(tp);
170         debug1("new tool_palette=%p", tp);
171         tp->selected = NULL;
172         tp->tool_hash = g_hash_table_new(tool_hash_function, tool_hash_compare);
173         gtk_object_set_data_full(
174                 object,
175                 "tool_palette",
176                 tp,
177                 (GtkDestroyNotify)tool_palette_destroy);
178     }
179     return tp;
180 }
181 
182 /*
183  * Initialize the tool palette widget.
184  */
185 void
on_tool_palette_realize(GtkWidget * widget,gpointer user_data)186 on_tool_palette_realize                 (GtkWidget       *widget,
187                                         gpointer         user_data)
188 {
189      debug_fn();
190 }
191 
192 static void
tool_palette_destroy(gpaint_tool_palette * tp)193 tool_palette_destroy(gpaint_tool_palette *tp)
194 {
195     debug_fn1("tool_palette=%p", tp);
196     g_hash_table_destroy(tp->tool_hash);
197     memset(tp, 0xBEBE, sizeof(gpaint_tool_palette));
198     g_free(tp);
199     debug("tool_palette_destroy() returning");
200 }
201 
202 /*
203  * Initialize a tool button.
204  */
205 void
on_tool_button_realize(GtkWidget * widget,gpointer user_data)206 on_tool_button_realize                 (GtkWidget       *widget,
207                                         gpointer         user_data)
208 {
209     const char *button_name = gtk_widget_get_name(widget);
210     const gpaint_tool_create *tc;
211     gpaint_tool_button *tb;
212     gpaint_tool_palette *tp;
213 
214     debug_fn1("button_name=%s", button_name);
215 
216     tc = lookup_tool_create(button_name);
217     tb = g_new(gpaint_tool_button, 1);
218 
219     tb->widget = GTK_TOGGLE_BUTTON(widget);
220     tb->handle_click = on_tool_select;
221     g_assert(tc);
222     if (!tc->create)
223     {
224         tb->tool = NULL;
225     }
226     else
227     {
228         g_assert(tc->tool_name);
229         debug2("creating %s for %s", tc->tool_name, button_name);
230         tb->tool = (*tc->create)(tc->tool_name);
231         g_assert(tb->tool);
232         g_assert(tb->tool->name);
233         g_assert(tb->tool->destroy);
234 
235         /* Add the tool object to the tool palette hash table
236          * The hash table is destroyed when the tool_palette is
237          * destroyed. */
238         tp = lookup_tool_palette_data(widget);
239         g_hash_table_insert(tp->tool_hash,
240                 (gpointer)tb->tool->name, (gpointer)tb->tool);
241     }
242 
243     gtk_object_set_data_full(
244             GTK_OBJECT(widget),
245             "tool_button", tb, (GtkDestroyNotify)tool_button_destroy);
246 
247     /* place the tool icon in the button */
248     if (tc->icon)
249     {
250         set_button_pixmap(tb->widget, tc->icon);
251     }
252     else
253     {
254 	    g_warning("missing icon data");
255     }
256 
257 }
258 
259 static void
tool_button_destroy(gpaint_tool_button * tb)260 tool_button_destroy(gpaint_tool_button *tb)
261 {
262     debug_fn1("tool_button=%p", tb);
263 
264     /* Remove the tool object if there is one for this
265      * button. Some buttons are just placeholders until
266      * the tool object has been implemented for it. */
267     g_assert(tb);
268     if (tb->tool)
269     {
270         gpaint_tool *tool = tb->tool;
271         g_assert(tool->name);
272         g_assert(tool->destroy);
273         debug1("destroying %s", tool->name);
274 
275         /* destroy the drawing tool object associated with this button */
276         (*tool->destroy)(tool);
277     }
278     memset(tb, 0xBEBE, sizeof(gpaint_tool_button));
279     g_free(tb);
280 }
281 
282 /*
283  * Handle tool button click events.
284  */
285 void
on_tool_button_clicked(GtkButton * button,gpointer user_data)286 on_tool_button_clicked                 (GtkButton       *button,
287                                         gpointer         user_data)
288 {
289     gpaint_tool_button *tb =
290         (gpaint_tool_button*)gtk_object_get_data(GTK_OBJECT(button), "tool_button");
291     g_assert(tb);
292     (*tb->handle_click)(tb);
293 }
294 
295 /*
296  * Initialize the shape fill toggle button.
297  */
298 void
on_filled_button_realize(GtkWidget * widget,gpointer user_data)299 on_filled_button_realize                 (GtkWidget       *widget,
300                                         gpointer         user_data)
301 {
302     gpaint_fill_button *fb;
303 
304     /* attach state data object */
305     fb = g_new(gpaint_fill_button, 1);
306     fb->button = GTK_TOGGLE_BUTTON(widget);
307     fb->fill = FALSE;      /* start as unfill */
308     gtk_object_set_data_full(GTK_OBJECT(widget), "fill_button", fb,
309                                 (GtkDestroyNotify)filled_button_destroy);
310 
311     /* set the unfill shape icon in the button */
312     set_button_pixmap(fb->button, (const char **)unfilled_xpm);
313 }
314 
315 static void
filled_button_destroy(gpaint_fill_button * fb)316 filled_button_destroy(gpaint_fill_button *fb)
317 {
318     debug_fn();
319 }
320 
321 /*
322  * Invert the shape fill/unfill state for each tool object.
323  */
324 void
on_filled_button_toggled(GtkToggleButton * togglebutton,gpointer user_data)325 on_filled_button_toggled               (GtkToggleButton *togglebutton,
326                                         gpointer         user_data)
327 {
328     gpaint_tool_palette *tp = lookup_tool_palette_data(GTK_WIDGET(togglebutton));
329     gpaint_fill_button *fb = gtk_object_get_data(GTK_OBJECT(togglebutton), "fill_button");
330     const char **icon;
331 
332     if (fb->fill)
333     {
334         fb->fill = FALSE;
335         icon = unfilled_xpm;
336     }
337     else
338     {
339         fb->fill = TRUE;
340         icon = filled_xpm;
341     }
342     g_hash_table_foreach(tp->tool_hash, tool_hash_change_fill, (gpointer)(&(fb->fill)));
343     set_button_pixmap(togglebutton, icon);
344 }
345 
346 static void
tool_hash_change_fill(gpointer key,gpointer value,gpointer user_data)347 tool_hash_change_fill(gpointer key, gpointer value, gpointer user_data)
348 {
349     gpaint_tool *tool = (gpaint_tool*)value;
350     if (tool && tool->attribute)
351     {
352         (*tool->attribute)(tool, GpaintFillShape, user_data);
353     }
354 }
355 
356 /*
357  * Look up the tool create function for a tool button.
358  */
359 static const gpaint_tool_create*
lookup_tool_create(const gchar * button_name)360 lookup_tool_create(const gchar *button_name)
361 {
362     const gpaint_tool_create *p = tool_table;
363     g_assert(button_name);
364     while (p->button_name)
365     {
366         if (strcmp(p->button_name,button_name)==0)
367         {
368             return p;
369         }
370         p++;
371     }
372     debug1("Unexpected tool button name: %s", button_name);
373     return NULL;
374 }
375 
376 /*
377  * Handle the selection of a tool.
378  */
379 static void
on_tool_select(gpaint_tool_button * tb)380 on_tool_select(gpaint_tool_button* tb)
381 {
382     /* deselect the previous tool, if one */
383     gpaint_tool_palette *palette = lookup_tool_palette_data(GTK_WIDGET(tb->widget));
384     GtkToggleButton *selected = palette->selected;
385     if (selected)
386     {
387         gpaint_tool_button *p =
388             (gpaint_tool_button*)gtk_object_get_data(GTK_OBJECT(selected), "tool_button");
389         g_assert(p);
390         p->handle_click = on_tool_deselect;
391 
392         /* Toggle off the previous button. This will generate a click event
393          * which is handled in on_tool_deselect(). */
394         gtk_toggle_button_set_active(palette->selected, FALSE);
395     }
396 
397     /* select the new tool */
398     palette->selected = tb->widget;
399     tb->handle_click = on_tool_reselect;
400     canvas_set_tool(canvas_lookup(GTK_WIDGET(tb->widget)), tb->tool);
401 }
402 
403 /*
404  * The user clicked on a tool button that was already selected.
405  * The mouse click will reset the toggle button, so toggle it
406  * back to the selected state.
407  */
408 static void
on_tool_reselect(gpaint_tool_button * tb)409 on_tool_reselect(gpaint_tool_button* tb)
410 {
411     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb->widget), TRUE);
412 }
413 
414 /*
415  * Transitional state while the button is being deselected.
416  */
417 static void
on_tool_deselect(gpaint_tool_button * tb)418 on_tool_deselect(gpaint_tool_button* tb)
419 {
420     tb->handle_click = on_tool_select;
421 }
422 
423 /*
424  * Helper function for set_button_pixmap().
425  */
426 static void
gtk_container_remove_callback(GtkWidget * widget,gpointer data)427 gtk_container_remove_callback(GtkWidget *widget, gpointer data)
428 {
429     gtk_container_remove(GTK_CONTAINER(data), widget);
430 }
431 
432 static
widget_get_toplevel_parent(GtkWidget * widget)433 GtkWidget *widget_get_toplevel_parent(GtkWidget *widget)
434 {
435     GtkWidget *parent, *found_widget;
436     found_widget = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(widget), "toplevelparent");
437     if (found_widget)
438         return found_widget;
439     found_widget = widget;
440     for (;;)
441     {
442         if (GTK_IS_MENU (found_widget))
443             parent = gtk_menu_get_attach_widget (GTK_MENU (found_widget));
444         else
445             parent = found_widget->parent;
446         if (parent == NULL)
447             break;
448         found_widget = parent;
449     }
450     gtk_object_set_data(GTK_OBJECT(widget), "toplevelparent", (gpointer) found_widget);
451     return found_widget;
452 }
453 
454 /*
455  * Set the pixmap icon in a button.
456  */
457 static void
set_button_pixmap(GtkToggleButton * button,const char ** pixmap)458 set_button_pixmap(GtkToggleButton *button, const char **pixmap)
459 {
460     GdkPixmap *gdkpixmap = 0;
461     GdkBitmap *mask = 0;
462     GtkWidget *gtkpixmap = 0;
463     static GtkWidget *widget = 0;
464 
465     if (!widget)
466     {
467         widget = widget_get_toplevel_parent(GTK_WIDGET(button));
468     }
469     if (widget)
470     {
471         gdkpixmap = gdk_pixmap_create_from_xpm_d(widget->window, &mask, NULL, (gchar**)pixmap);
472         g_assert(gdkpixmap);
473 
474         gtkpixmap = gtk_pixmap_new(gdkpixmap, mask);
475         g_assert(gtkpixmap);
476 
477 	gtk_container_foreach(GTK_CONTAINER(button), (GtkCallback)gtk_container_remove_callback, button);
478         gtk_container_add(GTK_CONTAINER(button), (gtkpixmap));
479 
480         gtk_widget_show(gtkpixmap);
481 
482 	gdk_pixmap_unref(gdkpixmap);
483         gdk_pixmap_unref(mask);
484     }
485 }
486 
487 static guint
tool_hash_function(gconstpointer key)488 tool_hash_function(gconstpointer key)
489 {
490     const char *s_key = (const char*)key;
491     guint hash = 0;
492     if (s_key)
493     {
494         int i;
495         int length = strlen(s_key);
496         for (i=0; i<length; i++)
497         {
498             hash = (hash<<4) + (hash ^ (guint)s_key[i]);
499         }
500     }
501     return hash;
502 }
503 
504 static gint
tool_hash_compare(gconstpointer a,gconstpointer b)505 tool_hash_compare(gconstpointer a, gconstpointer b)
506 {
507     return (!strcmp((const char*)a, (const char*)b));
508 }
509 
510 
tool_palette_set_active_button(GtkWidget * widget,const char * button_name)511 void tool_palette_set_active_button(GtkWidget *widget, const char *button_name)
512 {
513 
514     GtkWidget *button = lookup_widget(widget, button_name);
515     gpaint_tool_button *tb =
516         (gpaint_tool_button*)gtk_object_get_data(GTK_OBJECT(button), "tool_button");
517 
518     on_tool_select(tb);
519 
520 
521 }
522