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