offset fixes, admit to textline, proper updating of height/font at attr set,
[dia.git] / lib / diaarrowchooser.c
blob2c638d359e9885e266392c6a98760ae8aab6c583
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * diarrowchooser.c -- Copyright (C) 1999 James Henstridge.
5 * Copyright (C) 2004 Hubert Figuiere
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.
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.
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.
22 /** \file diaarrowchooser.c A widget to choose arrowhead. This only select arrowhead, not width and height.
23 * \ingroup diawidgets
26 #include <gtk/gtk.h>
28 #include "intl.h"
29 #include "widgets.h"
30 #include "diaarrowchooser.h"
31 #include "render_pixmap.h"
33 static const char *button_menu_key = "dia-button-menu";
34 static const char *menuitem_enum_key = "dia-menuitem-value";
36 /* --------------- DiaArrowPreview -------------------------------- */
37 static void dia_arrow_preview_set(DiaArrowPreview *arrow,
38 ArrowType atype, gboolean left);
40 static void dia_arrow_preview_class_init (DiaArrowPreviewClass *klass);
41 static void dia_arrow_preview_init (DiaArrowPreview *arrow);
42 static gint dia_arrow_preview_expose (GtkWidget *widget,
43 GdkEventExpose *event);
45 /** Get the class information for the arrow preview widget.
46 * @return A type object (statically allocated) for the arrow preview object.
48 GType
49 dia_arrow_preview_get_type(void)
51 static GType type = 0;
53 if (!type) {
54 static const GTypeInfo info = {
55 sizeof (DiaArrowPreviewClass),
56 (GBaseInitFunc) NULL,
57 (GBaseFinalizeFunc) NULL,
58 (GClassInitFunc) dia_arrow_preview_class_init,
59 (GClassFinalizeFunc) NULL,
60 NULL,
61 sizeof (DiaArrowPreview),
63 (GInstanceInitFunc) dia_arrow_preview_init
66 type = g_type_register_static(GTK_TYPE_MISC, "DiaArrowPreview", &info, 0);
69 return type;
72 /** Initialize class information for the arrow preview class.
73 * @param class The class object to initialize/
75 static void
76 dia_arrow_preview_class_init(DiaArrowPreviewClass *class)
78 GtkWidgetClass *widget_class;
80 widget_class = GTK_WIDGET_CLASS (class);
81 widget_class->expose_event = dia_arrow_preview_expose;
84 /** Initialize an arrow preview widget.
85 * @param arrow The widget to initialize.
87 static void
88 dia_arrow_preview_init(DiaArrowPreview *arrow)
90 GTK_WIDGET_SET_FLAGS (arrow, GTK_NO_WINDOW);
92 GTK_WIDGET (arrow)->requisition.width = 40 + GTK_MISC (arrow)->xpad * 2;
93 GTK_WIDGET (arrow)->requisition.height = 20 + GTK_MISC (arrow)->ypad * 2;
95 arrow->atype = ARROW_NONE;
96 arrow->left = TRUE;
99 /** Create a new arrow preview widget.
100 * @param atype The type of arrow to start out selected with.
101 * @param left If TRUE, this preview will point to the left.
102 * @return A new widget.
104 GtkWidget *
105 dia_arrow_preview_new(ArrowType atype, gboolean left)
107 DiaArrowPreview *arrow = g_object_new(DIA_TYPE_ARROW_PREVIEW, NULL);
109 arrow->atype = atype;
110 arrow->left = left;
111 return GTK_WIDGET(arrow);
114 /** Set the values shown by an arrow preview widget.
115 * @param arrow Preview widget to change.
116 * @param atype New arrow type to use.
117 * @param left If TRUE, the preview should point to the left.
119 static void
120 dia_arrow_preview_set(DiaArrowPreview *arrow, ArrowType atype, gboolean left)
122 if (arrow->atype != atype || arrow->left != left) {
123 arrow->atype = atype;
124 arrow->left = left;
125 if (GTK_WIDGET_DRAWABLE(arrow))
126 gtk_widget_queue_draw(GTK_WIDGET(arrow));
130 /** Expose handle for the arrow preview widget.
131 * @param widget The widget to display.
132 * @param event The event that caused the call.
133 * @return TRUE always.
134 * The expose handler gets called when the Arrow needs to be drawn.
136 static gint
137 dia_arrow_preview_expose(GtkWidget *widget, GdkEventExpose *event)
139 if (GTK_WIDGET_DRAWABLE(widget)) {
140 Point from, to;
141 Point move_arrow, move_line, arrow_head;
142 DiaRenderer *renderer;
143 DiaArrowPreview *arrow = DIA_ARROW_PREVIEW(widget);
144 Arrow arrow_type;
145 GtkMisc *misc = GTK_MISC(widget);
146 gint width, height;
147 gint x, y;
148 GdkWindow *win;
149 int linewidth = 2;
150 DiaRendererClass *renderer_ops;
152 width = widget->allocation.width - misc->xpad * 2;
153 height = widget->allocation.height - misc->ypad * 2;
154 x = (widget->allocation.x + misc->xpad);
155 y = (widget->allocation.y + misc->ypad);
157 win = widget->window;
159 to.y = from.y = height/2;
160 if (arrow->left) {
161 from.x = width-linewidth;
162 to.x = 0;
163 } else {
164 from.x = 0;
165 to.x = width-linewidth;
168 /* here we must do some acrobaticts and construct Arrow type
169 * variable
171 arrow_type.type = arrow->atype;
172 arrow_type.length = .75*((real)height-linewidth);
173 arrow_type.width = .75*((real)height-linewidth);
175 /* and here we calculate new arrow start and end of line points */
176 calculate_arrow_point(&arrow_type, &from, &to,
177 &move_arrow, &move_line,
178 linewidth);
179 arrow_head = to;
180 point_add(&arrow_head, &move_arrow);
181 point_add(&to, &move_line);
183 renderer = new_pixmap_renderer(win, width, height);
184 renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
185 renderer_pixmap_set_pixmap(renderer, win, x, y, width, height);
186 renderer_ops->begin_render(renderer);
187 renderer_ops->set_linewidth(renderer, linewidth);
188 renderer_ops->draw_line(renderer, &from, &to, &color_black);
189 arrow_draw (renderer, arrow_type.type,
190 &arrow_head, &from,
191 arrow_type.length,
192 arrow_type.width,
193 linewidth, &color_black, &color_white);
194 renderer_ops->end_render(renderer);
195 g_object_unref(renderer);
198 return TRUE;
202 /* ------- Code for DiaArrowChooser ----------------------- */
204 static void dia_arrow_chooser_class_init (DiaArrowChooserClass *klass);
205 static void dia_arrow_chooser_init (DiaArrowChooser *arrow);
207 /** Get the class info for the arrow chooser.
208 * @return GType structure filled in for arrow chooser (statically allocated).
210 GType
211 dia_arrow_chooser_get_type(void)
213 static GType type = 0;
215 if (!type) {
216 static const GTypeInfo info = {
217 sizeof (DiaArrowChooserClass),
218 (GBaseInitFunc) NULL,
219 (GBaseFinalizeFunc) NULL,
220 (GClassInitFunc) dia_arrow_chooser_class_init,
221 (GClassFinalizeFunc) NULL,
222 NULL,
223 sizeof (DiaArrowChooser),
225 (GInstanceInitFunc) dia_arrow_chooser_init
228 type = g_type_register_static(GTK_TYPE_BUTTON, "DiaArrowChooser", &info, 0);
231 return type;
234 /** Generic event handle for the arrow choose.
235 * This just handles popping up the arrowhead menu when the button is clicked.
236 * @param widget The arrow chooser widget.
237 * @param event An event affecting the arrow chooser.
238 * @return TRUE if we handled the event, FALSE otherwise.
240 static gint
241 dia_arrow_chooser_event(GtkWidget *widget, GdkEvent *event)
243 if (event->type == GDK_BUTTON_PRESS && event->button.button == 1) {
244 GtkMenu *menu = gtk_object_get_data(GTK_OBJECT(widget), button_menu_key);
245 gtk_menu_popup(menu, NULL, NULL, NULL, NULL,
246 event->button.button, event->button.time);
247 return TRUE;
250 return FALSE;
253 /** Initialize class information for the arrow choose.
254 * @param class Class structure to initialize private fields of.
256 static void
257 dia_arrow_chooser_class_init(DiaArrowChooserClass *class)
259 GtkWidgetClass *widget_class;
261 widget_class = GTK_WIDGET_CLASS(class);
262 widget_class->event = dia_arrow_chooser_event;
265 /** Initialize an arrow choose object.
266 * @param arrow Newly allocated arrow choose object.
268 static void
269 dia_arrow_chooser_init(DiaArrowChooser *arrow)
271 GtkWidget *wid;
273 arrow->left = FALSE;
274 arrow->arrow.type = ARROW_NONE;
275 arrow->arrow.length = DEFAULT_ARROW_LENGTH;
276 arrow->arrow.width = DEFAULT_ARROW_WIDTH;
278 wid = dia_arrow_preview_new(ARROW_NONE, arrow->left);
279 gtk_container_add(GTK_CONTAINER(arrow), wid);
280 gtk_widget_show(wid);
281 arrow->preview = DIA_ARROW_PREVIEW(wid);
283 arrow->dialog = NULL;
286 /** Handle the "ressponse" event for the arrow chooser dialog.
287 * @param dialog The dialog that got a response.
288 * @param response_id The ID of the response (e.g. GTK_RESPONSE_OK)
289 * @param chooser The arrowchooser widget (userdata)
291 static void
292 dia_arrow_chooser_dialog_response(GtkWidget *dialog,
293 gint response_id,
294 DiaArrowChooser *chooser)
296 if (response_id == GTK_RESPONSE_OK) {
297 Arrow new_arrow = dia_arrow_selector_get_arrow(chooser->selector);
299 if (new_arrow.type != chooser->arrow.type ||
300 new_arrow.length != chooser->arrow.length ||
301 new_arrow.width != chooser->arrow.width) {
302 chooser->arrow = new_arrow;
303 dia_arrow_preview_set(chooser->preview, new_arrow.type, chooser->left);
304 if (chooser->callback)
305 (* chooser->callback)(chooser->arrow, chooser->user_data);
307 } else {
308 dia_arrow_selector_set_arrow(chooser->selector, chooser->arrow);
310 gtk_widget_hide(chooser->dialog);
313 /** Create a new arrow chooser dialog.
314 * @param chooser The widget to attach a dialog to. The dialog will be placed
315 * in chooser->dialog.
317 static void
318 dia_arrow_chooser_dialog_new(DiaArrowChooser *chooser)
320 GtkWidget *wid;
322 chooser->dialog = gtk_dialog_new_with_buttons(_("Arrow Properties"),
323 NULL,
324 GTK_DIALOG_NO_SEPARATOR,
325 GTK_STOCK_CANCEL,
326 GTK_RESPONSE_CANCEL,
327 GTK_STOCK_OK,
328 GTK_RESPONSE_OK,
329 NULL);
330 gtk_dialog_set_default_response(GTK_DIALOG(chooser->dialog),
331 GTK_RESPONSE_OK);
332 g_signal_connect(G_OBJECT(chooser->dialog), "response",
333 G_CALLBACK(dia_arrow_chooser_dialog_response), chooser);
334 g_signal_connect(G_OBJECT(chooser->dialog), "destroy",
335 G_CALLBACK(gtk_widget_destroyed), &chooser->dialog);
337 wid = dia_arrow_selector_new();
338 gtk_container_set_border_width(GTK_CONTAINER(wid), 5);
339 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(chooser->dialog)->vbox), wid,
340 TRUE, TRUE, 0);
341 gtk_widget_show(wid);
342 chooser->selector = DIA_ARROW_SELECTOR(wid);
345 /** Display an arrow chooser dialog, creating one if necessary.
346 * @param widget Ignored
347 * @param chooser An arrowchooser widget to display in a dialog. This may
348 * get the dialog field set as a sideeffect.
350 static void
351 dia_arrow_chooser_dialog_show(GtkWidget *widget, DiaArrowChooser *chooser)
353 if (chooser->dialog) {
354 gtk_window_present(GTK_WINDOW(chooser->dialog));
355 return;
358 dia_arrow_chooser_dialog_new(chooser);
359 dia_arrow_selector_set_arrow(chooser->selector, chooser->arrow);
360 gtk_widget_show(chooser->dialog);
363 /** Set a new arrow type for an arrow chooser, as selected from a menu.
364 * @param mi The menu item currently selected in the arrow chooser menu.
365 * @param chooser The arrow chooser to update.
367 static void
368 dia_arrow_chooser_change_arrow_type(GtkMenuItem *mi, DiaArrowChooser *chooser)
370 ArrowType atype = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mi),
371 menuitem_enum_key));
372 Arrow arrow;
373 arrow.width = chooser->arrow.width;
374 arrow.length = chooser->arrow.length;
375 arrow.type = atype;
376 dia_arrow_chooser_set_arrow(chooser, &arrow);
379 /** Create a new arrow chooser object.
380 * @param left If TRUE, this chooser will point its arrowheads to the left.
381 * @param callback void (*callback)(Arrow *arrow, gpointer user_data) which
382 * will be called when the arrow type or dimensions change.
383 * @param user_data Any user data. This will be stored in chooser->user_data.
384 * @param tool_tips An object to set arrow names with.
385 * @return A new DiaArrowChooser widget.
387 GtkWidget *
388 dia_arrow_chooser_new(gboolean left, DiaChangeArrowCallback callback,
389 gpointer user_data, GtkTooltips *tool_tips)
391 DiaArrowChooser *chooser = g_object_new(DIA_TYPE_ARROW_CHOOSER, NULL);
392 GtkWidget *menu, *mi, *ar;
393 gint i;
395 chooser->left = left;
396 dia_arrow_preview_set(chooser->preview, chooser->preview->atype, left);
397 chooser->callback = callback;
398 chooser->user_data = user_data;
400 menu = gtk_menu_new();
401 g_object_ref(G_OBJECT(menu));
402 gtk_object_sink(GTK_OBJECT(menu));
403 g_object_set_data_full(G_OBJECT(chooser), button_menu_key, menu,
404 (GtkDestroyNotify)gtk_widget_unref);
405 for (i = 0; arrow_types[i].name != NULL; i++) {
406 mi = gtk_menu_item_new();
407 g_object_set_data(G_OBJECT(mi), menuitem_enum_key,
408 GINT_TO_POINTER(arrow_types[i].enum_value));
409 if (tool_tips) {
410 gtk_tooltips_set_tip(tool_tips, mi, gettext(arrow_types[i].name), NULL);
412 ar = dia_arrow_preview_new(arrow_types[i].enum_value, left);
414 gtk_container_add(GTK_CONTAINER(mi), ar);
415 gtk_widget_show(ar);
416 g_signal_connect(G_OBJECT(mi), "activate",
417 G_CALLBACK(dia_arrow_chooser_change_arrow_type), chooser);
418 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
419 gtk_widget_show(mi);
421 mi = gtk_menu_item_new_with_label(_("Details..."));
422 g_signal_connect(G_OBJECT(mi), "activate",
423 G_CALLBACK(dia_arrow_chooser_dialog_show), chooser);
424 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
425 gtk_widget_show(mi);
427 return GTK_WIDGET(chooser);
430 /** Set the type of arrow shown by the arrow chooser. If the arrow type
431 * changes, the callback function will be called.
432 * @param chooser The chooser to update.
433 * @param arrow The arrow type and dimensions the chooser will dispaly.
434 * Should it be called as well when the dimensions change?
436 void
437 dia_arrow_chooser_set_arrow(DiaArrowChooser *chooser, Arrow *arrow)
439 if (chooser->arrow.type != arrow->type) {
440 dia_arrow_preview_set(chooser->preview, arrow->type, chooser->left);
441 chooser->arrow.type = arrow->type;
442 if (chooser->dialog != NULL)
443 dia_arrow_selector_set_arrow(chooser->selector, chooser->arrow);
444 if (chooser->callback)
445 (* chooser->callback)(chooser->arrow, chooser->user_data);
447 chooser->arrow.width = arrow->width;
448 chooser->arrow.length = arrow->length;
451 /** Get the currently selected arrow type from an arrow chooser.
452 * @param arrow An arrow chooser to query.
453 * @return The arrow type that is currently selected in the chooser.
455 ArrowType
456 dia_arrow_chooser_get_arrow_type(DiaArrowChooser *arrow)
458 return arrow->arrow.type;