1 #define __SP_DESKTOP_EVENTS_C__
3 /*
4 * Event handlers for SPDesktop
5 *
6 * Author:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 *
9 * Copyright (C) 1999-2002 Lauris Kaplinski
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
14 #ifdef HAVE_CONFIG_H
15 # include <config.h>
16 #endif
17 #include <gtk/gtkdialog.h>
18 #include <gtk/gtkspinbutton.h>
19 #include <gtk/gtkhbox.h>
20 #include <gtk/gtkvbox.h>
21 #include <gtk/gtklabel.h>
22 #include <gtk/gtkstock.h>
23 #include "display/guideline.h"
24 #include "helper/unit-menu.h"
25 #include "helper/units.h"
26 #include "desktop.h"
27 #include "document.h"
28 #include "sp-guide.h"
29 #include "sp-namedview.h"
30 #include "desktop-handles.h"
31 #include "event-context.h"
32 #include "widgets/desktop-widget.h"
33 #include "sp-metrics.h"
34 #include <glibmm/i18n.h>
35 #include "dialogs/dialog-events.h"
36 #include "message-context.h"
37 #include "xml/repr.h"
39 static void sp_dt_simple_guide_dialog(SPGuide *guide, SPDesktop *desktop);
42 /* Root item handler */
45 int sp_desktop_root_handler(SPCanvasItem *item, GdkEvent *event, SPDesktop *desktop)
46 {
47 return sp_event_context_root_handler(desktop->event_context, event);
48 }
50 /*
51 * fixme: this conatins a hack, to deal with deleting a view, which is
52 * completely on another view, in which case active_desktop will not be updated
53 *
54 */
56 int sp_desktop_item_handler(SPCanvasItem *item, GdkEvent *event, gpointer data)
57 {
58 gpointer ddata = gtk_object_get_data(GTK_OBJECT(item->canvas), "SPDesktop");
59 g_return_val_if_fail(ddata != NULL, FALSE);
61 SPDesktop *desktop = static_cast<SPDesktop*>(ddata);
63 return sp_event_context_item_handler(desktop->event_context, SP_ITEM(data), event);
64 }
67 static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw, bool horiz)
68 {
69 static bool dragging = false;
70 static SPCanvasItem *guide = NULL;
71 int wx, wy;
73 SPDesktop *desktop = dtw->desktop;
74 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
76 gdk_window_get_pointer(GTK_WIDGET(dtw->canvas)->window, &wx, &wy, NULL);
77 NR::Point const event_win(wx, wy);
79 switch (event->type) {
80 case GDK_BUTTON_PRESS:
81 if (event->button.button == 1) {
82 dragging = true;
83 NR::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win));
84 NR::Point const event_dt(desktop->w2d(event_w));
86 // explicitly show guidelines; if I draw a guide, I want them on
87 sp_repr_set_boolean(repr, "showguides", TRUE);
88 sp_repr_set_boolean(repr, "inkscape:guide-bbox", TRUE);
90 double const guide_pos_dt = event_dt[ horiz
91 ? NR::Y
92 : NR::X ];
93 guide = sp_guideline_new(desktop->guides, guide_pos_dt, !horiz);
94 sp_guideline_set_color(SP_GUIDELINE(guide), desktop->namedview->guidehicolor);
95 gdk_pointer_grab(widget->window, FALSE,
96 (GdkEventMask)(GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK),
97 NULL, NULL,
98 event->button.time);
99 }
100 break;
101 case GDK_MOTION_NOTIFY:
102 if (dragging) {
103 NR::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win));
104 NR::Point const event_dt(desktop->w2d(event_w));
105 double const guide_pos_dt = event_dt[ horiz
106 ? NR::Y
107 : NR::X ];
108 sp_guideline_set_position(SP_GUIDELINE(guide), guide_pos_dt);
109 desktop->set_coordinate_status(event_dt);
110 desktop->setPosition (event_dt);
111 }
112 break;
113 case GDK_BUTTON_RELEASE:
114 if (dragging && event->button.button == 1) {
115 gdk_pointer_ungrab(event->button.time);
116 NR::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win));
117 NR::Point const event_dt(desktop->w2d(event_w));
118 dragging = false;
119 gtk_object_destroy(GTK_OBJECT(guide));
120 guide = NULL;
121 if ( ( horiz
122 ? wy
123 : wx )
124 >= 0 )
125 {
126 Inkscape::XML::Node *repr = sp_repr_new("sodipodi:guide");
127 repr->setAttribute("orientation", (horiz) ? "horizontal" : "vertical");
128 double const guide_pos_dt = event_dt[ horiz
129 ? NR::Y
130 : NR::X ];
131 sp_repr_set_svg_double(repr, "position", guide_pos_dt);
132 SP_OBJECT_REPR(desktop->namedview)->appendChild(repr);
133 Inkscape::GC::release(repr);
134 sp_document_done(SP_DT_DOCUMENT(desktop));
135 }
136 desktop->set_coordinate_status(event_dt);
137 }
138 default:
139 break;
140 }
142 return FALSE;
143 }
145 int sp_dt_hruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw)
146 {
147 return sp_dt_ruler_event(widget, event, dtw, true);
148 }
150 int sp_dt_vruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw)
151 {
152 return sp_dt_ruler_event(widget, event, dtw, false);
153 }
155 /* Guides */
157 gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data)
158 {
159 static bool dragging = false;
160 static bool moved = false;
161 gint ret = FALSE;
163 SPGuide *guide = SP_GUIDE(data);
164 SPDesktop *desktop = static_cast<SPDesktop*>(gtk_object_get_data(GTK_OBJECT(item->canvas), "SPDesktop"));
166 switch (event->type) {
167 case GDK_2BUTTON_PRESS:
168 if (event->button.button == 1) {
169 dragging = false;
170 sp_canvas_item_ungrab(item, event->button.time);
171 sp_dt_simple_guide_dialog(guide, desktop);
172 ret = TRUE;
173 }
174 break;
175 case GDK_BUTTON_PRESS:
176 if (event->button.button == 1) {
177 dragging = true;
178 sp_canvas_item_grab(item,
179 ( GDK_BUTTON_RELEASE_MASK |
180 GDK_BUTTON_PRESS_MASK |
181 GDK_POINTER_MOTION_MASK |
182 GDK_POINTER_MOTION_HINT_MASK ),
183 NULL,
184 event->button.time);
185 ret = TRUE;
186 }
187 break;
188 case GDK_MOTION_NOTIFY:
189 if (dragging) {
190 NR::Point const motion_w(event->motion.x,
191 event->motion.y);
192 NR::Point const motion_dt(desktop->w2d(motion_w));
193 sp_guide_moveto(*guide, sp_guide_position_from_pt(guide, motion_dt), false);
194 moved = true;
195 desktop->set_coordinate_status(motion_dt);
196 desktop->setPosition (motion_dt);
197 ret = TRUE;
198 }
199 break;
200 case GDK_BUTTON_RELEASE:
201 if (dragging && event->button.button == 1) {
202 if (moved) {
203 NR::Point const event_w(event->button.x,
204 event->button.y);
205 NR::Point const event_dt(desktop->w2d(event_w));
206 if (sp_canvas_world_pt_inside_window(item->canvas, event_w)) {
207 sp_guide_moveto(*guide, sp_guide_position_from_pt(guide, event_dt), true);
208 } else {
209 /* Undo movement of any attached shapes. */
210 sp_guide_moveto(*guide, guide->position, false);
211 sp_guide_remove(guide);
212 }
213 moved = false;
214 sp_document_done(SP_DT_DOCUMENT(desktop));
215 desktop->set_coordinate_status(event_dt);
216 desktop->setPosition (event_dt);
217 }
218 dragging = false;
219 sp_canvas_item_ungrab(item, event->button.time);
220 ret=TRUE;
221 }
222 case GDK_ENTER_NOTIFY:
223 {
225 sp_guideline_set_color(SP_GUIDELINE(item), guide->hicolor);
227 GString *position_string = SP_PX_TO_METRIC_STRING(guide->position, desktop->namedview->getDefaultMetric());
228 char *guide_description = sp_guide_description(guide);
230 desktop->guidesMessageContext()->setF(Inkscape::NORMAL_MESSAGE, _("%s at %s"), guide_description, position_string->str);
232 g_free(guide_description);
233 g_string_free(position_string, TRUE);
234 break;
235 }
236 case GDK_LEAVE_NOTIFY:
237 sp_guideline_set_color(SP_GUIDELINE(item), guide->color);
238 desktop->guidesMessageContext()->clear();
239 break;
240 default:
241 break;
242 }
244 return ret;
245 }
249 /*
250 * simple guideline dialog
251 */
253 static GtkWidget *d = NULL;
254 static GtkWidget *l1;
255 static GtkWidget *l2;
256 static GtkWidget *e;
257 static GtkWidget *u;
258 static GtkWidget *m;
259 static gdouble oldpos;
260 static bool mode;
261 static gpointer g;
264 static void guide_dialog_mode_changed(GtkWidget *widget)
265 {
266 if (mode) {
267 // TRANSLATORS: This string appears when double-clicking on a guide.
268 // This is the distance by which the guide is to be moved.
269 gtk_label_set_text(GTK_LABEL(m), _(" relative by "));
270 mode = false;
271 } else {
272 // TRANSLATORS: This string appears when double-clicking on a guide.
273 // This is the target location where the guide is to be moved.
274 gtk_label_set_text(GTK_LABEL(m), _(" absolute to "));
275 mode = true;
276 }
277 }
279 static void guide_dialog_close(GtkWidget *widget, gpointer data)
280 {
281 gtk_widget_hide(GTK_WIDGET(d));
282 }
284 static void guide_dialog_apply(SPGuide &guide)
285 {
286 gdouble const raw_dist = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(e));
287 SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u));
288 gdouble const points = sp_units_get_pixels(raw_dist, unit);
289 gdouble const newpos = ( mode
290 ? points
291 : guide.position + points );
292 sp_guide_moveto(guide, newpos, true);
293 sp_document_done(SP_OBJECT_DOCUMENT(&guide));
294 }
296 static void guide_dialog_ok(GtkWidget *widget, gpointer g)
297 {
298 SPGuide &guide = **static_cast<SPGuide**>(g);
299 guide_dialog_apply(guide);
300 guide_dialog_close(NULL, GTK_DIALOG(widget));
301 }
303 static void guide_dialog_delete(GtkWidget *widget, SPGuide **guide)
304 {
305 SPDocument *doc = SP_OBJECT_DOCUMENT(*guide);
306 sp_guide_remove(*guide);
307 sp_document_done(doc);
308 guide_dialog_close(NULL, GTK_DIALOG(widget));
309 }
311 static void guide_dialog_response(GtkDialog *dialog, gint response, gpointer data)
312 {
313 GtkWidget *widget = GTK_WIDGET(dialog);
315 switch (response) {
316 case GTK_RESPONSE_OK:
317 guide_dialog_ok(widget, data);
318 break;
319 case -12:
320 guide_dialog_delete(widget, (SPGuide**) data);
321 break;
322 case GTK_RESPONSE_CLOSE:
323 guide_dialog_close(widget, (GtkDialog*) data);
324 break;
325 case GTK_RESPONSE_DELETE_EVENT:
326 break;
327 /* case GTK_RESPONSE_APPLY:
328 guide_dialog_apply(widget, data);
329 break;
330 */
331 default:
332 g_assert_not_reached();
333 }
334 }
336 static void sp_dt_simple_guide_dialog(SPGuide *guide, SPDesktop *desktop)
337 {
338 if (!GTK_IS_WIDGET(d)) {
339 // create dialog
340 d = gtk_dialog_new_with_buttons(_("Guideline"),
341 NULL,
342 GTK_DIALOG_MODAL,
343 GTK_STOCK_OK,
344 GTK_RESPONSE_OK,
345 GTK_STOCK_DELETE,
346 -12, /* DELETE */
347 GTK_STOCK_CLOSE,
348 GTK_RESPONSE_CLOSE,
349 NULL);
350 sp_transientize(d);
351 gtk_widget_hide(d);
353 GtkWidget *b1 = gtk_hbox_new(FALSE, 4);
354 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(d)->vbox), b1, FALSE, FALSE, 0);
355 gtk_container_set_border_width(GTK_CONTAINER(b1), 4);
356 gtk_widget_show(b1);
358 GtkWidget *b2 = gtk_vbox_new(FALSE, 4);
359 gtk_box_pack_end(GTK_BOX(b1), b2, TRUE, TRUE, 0);
360 gtk_widget_show(b2);
362 //labels
363 GtkWidget *b3 = gtk_hbox_new(FALSE, 4);
364 gtk_box_pack_start(GTK_BOX(b2), b3, TRUE, TRUE, 0);
365 gtk_widget_show(b3);
367 l1 = gtk_label_new("foo1");
368 gtk_box_pack_start(GTK_BOX(b3), l1, TRUE, TRUE, 0);
369 gtk_misc_set_alignment(GTK_MISC(l1), 1.0, 0.5);
370 gtk_widget_show(l1);
372 l2 = gtk_label_new("foo2");
373 gtk_box_pack_start(GTK_BOX(b3), l2, TRUE, TRUE, 0);
374 gtk_misc_set_alignment(GTK_MISC(l2), 0.0, 0.5);
375 gtk_widget_show(l2);
377 GtkWidget *b4 = gtk_hbox_new(FALSE, 4);
378 gtk_box_pack_start(GTK_BOX(b2), b4, FALSE, FALSE, 0);
379 gtk_widget_show(b4);
380 // mode button
381 GtkWidget *but = gtk_button_new();
382 gtk_button_set_relief(GTK_BUTTON(but), GTK_RELIEF_NONE);
383 gtk_box_pack_start(GTK_BOX(b4), but, FALSE, TRUE, 0);
384 gtk_signal_connect_while_alive(GTK_OBJECT(but), "clicked", GTK_SIGNAL_FUNC(guide_dialog_mode_changed),
385 NULL , GTK_OBJECT(but));
386 gtk_widget_show(but);
387 m = gtk_label_new(_(" absolute to "));
388 mode = true;
389 gtk_container_add(GTK_CONTAINER(but), m);
390 gtk_widget_show(m);
392 // unitmenu
393 /* fixme: We should allow percents here too, as percents of the canvas size */
394 u = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
395 sp_unit_selector_set_unit(SP_UNIT_SELECTOR(u), desktop->namedview->doc_units);
397 // spinbutton
398 GtkObject *a = gtk_adjustment_new(0.0, -SP_DESKTOP_SCROLL_LIMIT, SP_DESKTOP_SCROLL_LIMIT, 1.0, 10.0, 10.0);
399 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(u), GTK_ADJUSTMENT(a));
400 e = gtk_spin_button_new(GTK_ADJUSTMENT(a), 1.0 , 2);
401 gtk_widget_show(e);
402 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(e), TRUE);
403 gtk_box_pack_start(GTK_BOX(b4), e, TRUE, TRUE, 0);
404 gtk_signal_connect_object(GTK_OBJECT(e), "activate",
405 GTK_SIGNAL_FUNC(gtk_window_activate_default),
406 GTK_OBJECT(d));
407 /* gnome_dialog_editable_enters(GNOME_DIALOG(d), GTK_EDITABLE(e)); */
409 gtk_widget_show(u);
410 gtk_box_pack_start(GTK_BOX(b4), u, FALSE, FALSE, 0);
413 // dialog
414 gtk_dialog_set_default_response(GTK_DIALOG(d), GTK_RESPONSE_OK);
415 gtk_signal_connect(GTK_OBJECT(d), "response", GTK_SIGNAL_FUNC(guide_dialog_response), &g);
416 gtk_signal_connect(GTK_OBJECT(d), "delete_event", GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete), GTK_WIDGET(d));
417 }
419 // initialize dialog
420 g = guide;
421 oldpos = guide->position;
422 {
423 char *guide_description = sp_guide_description(guide);
424 char *label = g_strdup_printf(_("Move %s"), guide_description);
425 g_free(guide_description);
426 gtk_label_set(GTK_LABEL(l1), label);
427 g_free(label);
428 }
430 SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u));
431 gdouble const val = sp_pixels_get_units(oldpos, unit);
432 gtk_spin_button_set_value(GTK_SPIN_BUTTON(e), val);
433 gtk_spin_button_set_value(GTK_SPIN_BUTTON(e), val);
434 gtk_widget_grab_focus(e);
435 gtk_editable_select_region(GTK_EDITABLE(e), 0, 20);
436 gtk_window_set_position(GTK_WINDOW(d), GTK_WIN_POS_MOUSE);
438 gtk_widget_show(d);
439 }
442 /*
443 Local Variables:
444 mode:c++
445 c-file-style:"stroustrup"
446 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
447 indent-tabs-mode:nil
448 fill-column:99
449 End:
450 */
451 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :