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