1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *
4  * diarrowchooser.c -- Copyright (C) 1999 James Henstridge.
5  *                     Copyright (C) 2004 Hubert Figuiere
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 /** \file diaarrowchooser.c  A widget to choose arrowhead.  This only select arrowhead, not width  and height.
23  * \ingroup diawidgets
24  */
25 #undef GTK_DISABLE_DEPRECATED /* GtkTooltips */
26 
27 #include <gtk/gtk.h>
28 #include <config.h>
29 #include "intl.h"
30 #include "widgets.h"
31 #include "diaarrowchooser.h"
32 #include "render_pixmap.h"
33 
34 static const char *button_menu_key = "dia-button-menu";
35 static const char *menuitem_enum_key = "dia-menuitem-value";
36 
37 static gchar*
_dia_translate(const gchar * term,gpointer data)38 _dia_translate (const gchar* term, gpointer data)
39 {
40   const gchar* trans = term;
41 
42   if (term && *term) {
43     /* first try our own ... */
44     trans = dgettext (GETTEXT_PACKAGE, term);
45     /* ... than gtk */
46     if (term == trans)
47       trans = dgettext ("gtk20", term);
48 #if 0
49     /* FIXME: final fallback */
50     if (term == trans) { /* FIXME: translation to be updated */
51       gchar* kludge = g_strdup_printf ("/%s", term);
52       trans = dgettext (GETTEXT_PACKAGE, kludge);
53       if (kludge == trans)
54 	trans = term;
55       else
56 	++trans;
57       g_free (kludge);
58     }
59     if (term == trans)
60       trans = g_strdup_printf ("XXX: %s", term);
61 #endif
62   }
63   return trans;
64 }
65 
66 /* --------------- DiaArrowPreview -------------------------------- */
67 static void dia_arrow_preview_set(DiaArrowPreview *arrow,
68                                   ArrowType atype, gboolean left);
69 
70 static void dia_arrow_preview_class_init (DiaArrowPreviewClass  *klass);
71 static void dia_arrow_preview_init       (DiaArrowPreview       *arrow);
72 static gint dia_arrow_preview_expose     (GtkWidget      *widget,
73 					  GdkEventExpose *event);
74 
75 /** Get the class information for the arrow preview widget.
76  * @return A type object (statically allocated) for the arrow preview object.
77  */
78 GType
dia_arrow_preview_get_type(void)79 dia_arrow_preview_get_type(void)
80 {
81   static GType type = 0;
82 
83   if (!type) {
84     static const GTypeInfo info = {
85       sizeof (DiaArrowPreviewClass),
86       (GBaseInitFunc) NULL,
87       (GBaseFinalizeFunc) NULL,
88       (GClassInitFunc) dia_arrow_preview_class_init,
89       (GClassFinalizeFunc) NULL,
90       NULL,
91       sizeof (DiaArrowPreview),
92       0,
93       (GInstanceInitFunc) dia_arrow_preview_init
94     };
95 
96     type = g_type_register_static(GTK_TYPE_MISC, "DiaArrowPreview", &info, 0);
97   }
98 
99   return type;
100 }
101 
102 /** Initialize class information for the arrow preview class.
103  * @param class The class object to initialize/
104  */
105 static void
dia_arrow_preview_class_init(DiaArrowPreviewClass * class)106 dia_arrow_preview_class_init(DiaArrowPreviewClass *class)
107 {
108   GtkWidgetClass *widget_class;
109 
110   widget_class = GTK_WIDGET_CLASS (class);
111   widget_class->expose_event = dia_arrow_preview_expose;
112 }
113 
114 /** Initialize an arrow preview widget.
115  * @param arrow The widget to initialize.
116  */
117 static void
dia_arrow_preview_init(DiaArrowPreview * arrow)118 dia_arrow_preview_init(DiaArrowPreview *arrow)
119 {
120   GTK_WIDGET_SET_FLAGS (arrow, GTK_NO_WINDOW);
121 
122   GTK_WIDGET (arrow)->requisition.width = 40 + GTK_MISC (arrow)->xpad * 2;
123   GTK_WIDGET (arrow)->requisition.height = 20 + GTK_MISC (arrow)->ypad * 2;
124 
125   arrow->atype = ARROW_NONE;
126   arrow->left = TRUE;
127 }
128 
129 /** Create a new arrow preview widget.
130  * @param atype The type of arrow to start out selected with.
131  * @param left If TRUE, this preview will point to the left.
132  * @return A new widget.
133  */
134 GtkWidget *
dia_arrow_preview_new(ArrowType atype,gboolean left)135 dia_arrow_preview_new(ArrowType atype, gboolean left)
136 {
137   DiaArrowPreview *arrow = g_object_new(DIA_TYPE_ARROW_PREVIEW, NULL);
138 
139   arrow->atype = atype;
140   arrow->left = left;
141   return GTK_WIDGET(arrow);
142 }
143 
144 /** Set the values shown by an arrow preview widget.
145  * @param arrow Preview widget to change.
146  * @param atype New arrow type to use.
147  * @param left If TRUE, the preview should point to the left.
148  */
149 static void
dia_arrow_preview_set(DiaArrowPreview * arrow,ArrowType atype,gboolean left)150 dia_arrow_preview_set(DiaArrowPreview *arrow, ArrowType atype, gboolean left)
151 {
152   if (arrow->atype != atype || arrow->left != left) {
153     arrow->atype = atype;
154     arrow->left = left;
155     if (GTK_WIDGET_DRAWABLE(arrow))
156       gtk_widget_queue_draw(GTK_WIDGET(arrow));
157   }
158 }
159 
160 /** Expose handle for the arrow preview widget.
161  * @param widget The widget to display.
162  * @param event The event that caused the call.
163  * @return TRUE always.
164  * The expose handler gets called when the Arrow needs to be drawn.
165  */
166 static gint
dia_arrow_preview_expose(GtkWidget * widget,GdkEventExpose * event)167 dia_arrow_preview_expose(GtkWidget *widget, GdkEventExpose *event)
168 {
169   if (GTK_WIDGET_DRAWABLE(widget)) {
170     Point from, to;
171     Point move_arrow, move_line, arrow_head;
172     DiaRenderer *renderer;
173     DiaArrowPreview *arrow = DIA_ARROW_PREVIEW(widget);
174     Arrow arrow_type;
175     GtkMisc *misc = GTK_MISC(widget);
176     gint width, height;
177     gint x, y;
178     GdkWindow *win;
179     int linewidth = 2;
180     DiaRendererClass *renderer_ops;
181 
182     width = widget->allocation.width - misc->xpad * 2;
183     height = widget->allocation.height - misc->ypad * 2;
184     x = (widget->allocation.x + misc->xpad);
185     y = (widget->allocation.y + misc->ypad);
186 
187     win = widget->window;
188 
189     to.y = from.y = height/2;
190     if (arrow->left) {
191       from.x = width-linewidth;
192       to.x = 0;
193     } else {
194       from.x = 0;
195       to.x = width-linewidth;
196     }
197 
198     /* here we must do some acrobaticts and construct Arrow type
199      * variable
200      */
201     arrow_type.type = arrow->atype;
202     arrow_type.length = .75*((real)height-linewidth);
203     arrow_type.width = .75*((real)height-linewidth);
204 
205     /* and here we calculate new arrow start and end of line points */
206     calculate_arrow_point(&arrow_type, &from, &to,
207                           &move_arrow, &move_line,
208 			  linewidth);
209     arrow_head = to;
210     point_add(&arrow_head, &move_arrow);
211     point_add(&to, &move_line);
212 
213     renderer = new_pixmap_renderer(win, width, height);
214     renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
215     renderer_pixmap_set_pixmap(renderer, win, x, y, width, height);
216     renderer_ops->begin_render(renderer);
217     renderer_ops->set_linewidth(renderer, linewidth);
218     {
219       Color color_bg, color_fg;
220       /* the text colors are the best approximation to what we had */
221       GdkColor bg = widget->style->base[GTK_WIDGET_STATE(widget)];
222       GdkColor fg = widget->style->text[GTK_WIDGET_STATE(widget)];
223 
224       GDK_COLOR_TO_DIA(bg, color_bg);
225       GDK_COLOR_TO_DIA(fg, color_fg);
226       renderer_ops->draw_line(renderer, &from, &to, &color_fg);
227       arrow_draw (renderer, arrow_type.type,
228                   &arrow_head, &from,
229 		  arrow_type.length,
230 		  arrow_type.width,
231                   linewidth, &color_fg, &color_bg);
232     }
233     renderer_ops->end_render(renderer);
234     g_object_unref(renderer);
235   }
236 
237   return TRUE;
238 }
239 
240 
241 /* ------- Code for DiaArrowChooser ----------------------- */
242 
243 static void dia_arrow_chooser_class_init (DiaArrowChooserClass  *klass);
244 static void dia_arrow_chooser_init       (DiaArrowChooser       *arrow);
245 
246 /** Get the class info for the arrow chooser.
247  * @return GType structure filled in for arrow chooser (statically allocated).
248  */
249 GType
dia_arrow_chooser_get_type(void)250 dia_arrow_chooser_get_type(void)
251 {
252   static GType type = 0;
253 
254   if (!type) {
255     static const GTypeInfo info = {
256       sizeof (DiaArrowChooserClass),
257       (GBaseInitFunc) NULL,
258       (GBaseFinalizeFunc) NULL,
259       (GClassInitFunc) dia_arrow_chooser_class_init,
260       (GClassFinalizeFunc) NULL,
261       NULL,
262       sizeof (DiaArrowChooser),
263       0,
264       (GInstanceInitFunc) dia_arrow_chooser_init
265     };
266 
267     type = g_type_register_static(GTK_TYPE_BUTTON, "DiaArrowChooser", &info, 0);
268   }
269 
270   return type;
271 }
272 
273 /** Generic event handle for the arrow choose.
274  * This just handles popping up the arrowhead menu when the button is clicked.
275  * @param widget The arrow chooser widget.
276  * @param event An event affecting the arrow chooser.
277  * @return TRUE if we handled the event, FALSE otherwise.
278  */
279 static gint
dia_arrow_chooser_event(GtkWidget * widget,GdkEvent * event)280 dia_arrow_chooser_event(GtkWidget *widget, GdkEvent *event)
281 {
282   if (event->type == GDK_BUTTON_PRESS && event->button.button == 1) {
283     GtkMenu *menu = gtk_object_get_data(GTK_OBJECT(widget), button_menu_key);
284     gtk_menu_popup(menu, NULL, NULL, NULL, NULL,
285 		   event->button.button, event->button.time);
286     return TRUE;
287   }
288 
289   return FALSE;
290 }
291 
292 /** Initialize class information for the arrow choose.
293  * @param class Class structure to initialize private fields of.
294  */
295 static void
dia_arrow_chooser_class_init(DiaArrowChooserClass * class)296 dia_arrow_chooser_class_init(DiaArrowChooserClass *class)
297 {
298   GtkWidgetClass *widget_class;
299 
300   widget_class = GTK_WIDGET_CLASS(class);
301   widget_class->event = dia_arrow_chooser_event;
302 }
303 
304 /** Initialize an arrow choose object.
305  * @param arrow Newly allocated arrow choose object.
306  */
307 static void
dia_arrow_chooser_init(DiaArrowChooser * arrow)308 dia_arrow_chooser_init(DiaArrowChooser *arrow)
309 {
310   GtkWidget *wid;
311 
312   arrow->left = FALSE;
313   arrow->arrow.type = ARROW_NONE;
314   arrow->arrow.length = DEFAULT_ARROW_LENGTH;
315   arrow->arrow.width = DEFAULT_ARROW_WIDTH;
316 
317   wid = dia_arrow_preview_new(ARROW_NONE, arrow->left);
318   gtk_container_add(GTK_CONTAINER(arrow), wid);
319   gtk_widget_show(wid);
320   arrow->preview = DIA_ARROW_PREVIEW(wid);
321 
322   arrow->dialog = NULL;
323 }
324 
325 /** Handle the "ressponse" event for the arrow chooser dialog.
326  * @param dialog The dialog that got a response.
327  * @param response_id The ID of the response (e.g. GTK_RESPONSE_OK)
328  * @param chooser The arrowchooser widget (userdata)
329  */
330 static void
dia_arrow_chooser_dialog_response(GtkWidget * dialog,gint response_id,DiaArrowChooser * chooser)331 dia_arrow_chooser_dialog_response(GtkWidget *dialog,
332 				  gint response_id,
333 				  DiaArrowChooser *chooser)
334 {
335   if (response_id == GTK_RESPONSE_OK) {
336     Arrow new_arrow = dia_arrow_selector_get_arrow(chooser->selector);
337 
338     if (new_arrow.type   != chooser->arrow.type   ||
339         new_arrow.length != chooser->arrow.length ||
340         new_arrow.width  != chooser->arrow.width) {
341       chooser->arrow = new_arrow;
342       dia_arrow_preview_set(chooser->preview, new_arrow.type, chooser->left);
343       if (chooser->callback)
344         (* chooser->callback)(chooser->arrow, chooser->user_data);
345     }
346   } else {
347     dia_arrow_selector_set_arrow(chooser->selector, chooser->arrow);
348   }
349   gtk_widget_hide(chooser->dialog);
350 }
351 
352 /** Create a new arrow chooser dialog.
353  * @param chooser The widget to attach a dialog to.  The dialog will be placed
354  * in chooser->dialog.
355  */
356 static void
dia_arrow_chooser_dialog_new(DiaArrowChooser * chooser)357 dia_arrow_chooser_dialog_new(DiaArrowChooser *chooser)
358 {
359   GtkWidget *wid;
360 
361   chooser->dialog = gtk_dialog_new_with_buttons(_("Arrow Properties"),
362                                                 NULL,
363                                                 GTK_DIALOG_NO_SEPARATOR,
364                                                 GTK_STOCK_CANCEL,
365                                                 GTK_RESPONSE_CANCEL,
366                                                 GTK_STOCK_OK,
367                                                 GTK_RESPONSE_OK,
368                                                 NULL);
369   gtk_dialog_set_default_response(GTK_DIALOG(chooser->dialog),
370                                   GTK_RESPONSE_OK);
371   g_signal_connect(G_OBJECT(chooser->dialog), "response",
372                    G_CALLBACK(dia_arrow_chooser_dialog_response), chooser);
373   g_signal_connect(G_OBJECT(chooser->dialog), "destroy",
374                    G_CALLBACK(gtk_widget_destroyed), &chooser->dialog);
375 
376   wid = dia_arrow_selector_new();
377   gtk_container_set_border_width(GTK_CONTAINER(wid), 5);
378   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(chooser->dialog)->vbox), wid,
379                      TRUE, TRUE, 0);
380   gtk_widget_show(wid);
381   chooser->selector = DIA_ARROW_SELECTOR(wid);
382 }
383 
384 /** Display an arrow chooser dialog, creating one if necessary.
385  * @param widget Ignored
386  * @param chooser An arrowchooser widget to display in a dialog.  This may
387  * get the dialog field set as a sideeffect.
388  */
389 static void
dia_arrow_chooser_dialog_show(GtkWidget * widget,DiaArrowChooser * chooser)390 dia_arrow_chooser_dialog_show(GtkWidget *widget, DiaArrowChooser *chooser)
391 {
392   if (chooser->dialog) {
393     gtk_window_present(GTK_WINDOW(chooser->dialog));
394     return;
395   }
396 
397   dia_arrow_chooser_dialog_new(chooser);
398   dia_arrow_selector_set_arrow(chooser->selector, chooser->arrow);
399   gtk_widget_show(chooser->dialog);
400 }
401 
402 /** Set a new arrow type for an arrow chooser, as selected from a menu.
403  * @param mi The menu item currently selected in the arrow chooser menu.
404  * @param chooser The arrow chooser to update.
405  */
406 static void
dia_arrow_chooser_change_arrow_type(GtkMenuItem * mi,DiaArrowChooser * chooser)407 dia_arrow_chooser_change_arrow_type(GtkMenuItem *mi, DiaArrowChooser *chooser)
408 {
409   ArrowType atype = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mi),
410 						      menuitem_enum_key));
411   Arrow arrow;
412   arrow.width = chooser->arrow.width;
413   arrow.length = chooser->arrow.length;
414   arrow.type = atype;
415   dia_arrow_chooser_set_arrow(chooser, &arrow);
416 }
417 
418 /** Create a new arrow chooser object.
419  * @param left If TRUE, this chooser will point its arrowheads to the left.
420  * @param callback void (*callback)(Arrow *arrow, gpointer user_data) which
421  *                 will be called when the arrow type or dimensions change.
422  * @param user_data Any user data.  This will be stored in chooser->user_data.
423  * @param tool_tips An object to set arrow names with.
424  * @return A new DiaArrowChooser widget.
425  */
426 GtkWidget *
dia_arrow_chooser_new(gboolean left,DiaChangeArrowCallback callback,gpointer user_data,GtkTooltips * tool_tips)427 dia_arrow_chooser_new(gboolean left, DiaChangeArrowCallback callback,
428 		      gpointer user_data, GtkTooltips *tool_tips)
429 {
430   DiaArrowChooser *chooser = g_object_new(DIA_TYPE_ARROW_CHOOSER, NULL);
431   GtkWidget *menu, *mi, *ar;
432   gint i;
433 
434   chooser->left = left;
435   dia_arrow_preview_set(chooser->preview, chooser->preview->atype, left);
436   chooser->callback = callback;
437   chooser->user_data = user_data;
438 
439   menu = gtk_menu_new();
440   g_object_ref(G_OBJECT(menu));
441   gtk_object_sink(GTK_OBJECT(menu));
442   g_object_set_data_full(G_OBJECT(chooser), button_menu_key, menu,
443 			 (GtkDestroyNotify)gtk_widget_unref);
444   /* although from ARROW_NONE to MAX_ARROW_TYPE-1 this is sorted by *index* to keep the order consistent with earlier releases */
445   for (i = ARROW_NONE; i < MAX_ARROW_TYPE; ++i) {
446     ArrowType arrow_type = arrow_type_from_index(i);
447     mi = gtk_menu_item_new();
448     g_object_set_data(G_OBJECT(mi), menuitem_enum_key,
449 		      GINT_TO_POINTER(arrow_type));
450     if (tool_tips) {
451       gtk_tooltips_set_tip(tool_tips, mi, _dia_translate(arrow_get_name_from_type(arrow_type), NULL), NULL);
452     }
453     ar = dia_arrow_preview_new(arrow_type, left);
454 
455     gtk_container_add(GTK_CONTAINER(mi), ar);
456     gtk_widget_show(ar);
457     g_signal_connect(G_OBJECT(mi), "activate",
458 		     G_CALLBACK(dia_arrow_chooser_change_arrow_type), chooser);
459     gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
460     gtk_widget_show(mi);
461   }
462   mi = gtk_menu_item_new_with_label(_dia_translate("Details...", NULL));
463   g_signal_connect(G_OBJECT(mi), "activate",
464 		   G_CALLBACK(dia_arrow_chooser_dialog_show), chooser);
465   gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
466   gtk_widget_show(mi);
467 
468   return GTK_WIDGET(chooser);
469 }
470 
471 /** Set the type of arrow shown by the arrow chooser.  If the arrow type
472  * changes, the callback function will be called.
473  * @param chooser The chooser to update.
474  * @param arrow The arrow type and dimensions the chooser will dispaly.
475  * Should it be called as well when the dimensions change?
476  */
477 void
dia_arrow_chooser_set_arrow(DiaArrowChooser * chooser,Arrow * arrow)478 dia_arrow_chooser_set_arrow(DiaArrowChooser *chooser, Arrow *arrow)
479 {
480   if (chooser->arrow.type != arrow->type) {
481     dia_arrow_preview_set(chooser->preview, arrow->type, chooser->left);
482     chooser->arrow.type = arrow->type;
483     if (chooser->dialog != NULL)
484       dia_arrow_selector_set_arrow(chooser->selector, chooser->arrow);
485     if (chooser->callback)
486       (* chooser->callback)(chooser->arrow, chooser->user_data);
487   }
488   chooser->arrow.width = arrow->width;
489   chooser->arrow.length = arrow->length;
490 }
491 
492 /** Get the currently selected arrow type from an arrow chooser.
493  * @param arrow An arrow chooser to query.
494  * @return The arrow type that is currently selected in the chooser.
495  */
496 ArrowType
dia_arrow_chooser_get_arrow_type(DiaArrowChooser * arrow)497 dia_arrow_chooser_get_arrow_type(DiaArrowChooser *arrow)
498 {
499   return arrow->arrow.type;
500 }
501 
502