Code

Move files from the src/dialogs/ directory to the places where they
[inkscape.git] / src / desktop-events.cpp
1 /** @file
2  * @brief Event handlers for SPDesktop
3  */
4 /* Author:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *
7  * Copyright (C) 1999-2002 Lauris Kaplinski
8  *
9  * Released under GNU GPL, read the file 'COPYING' for more information
10  */
12 #ifdef HAVE_CONFIG_H
13 # include <config.h>
14 #endif
15 #include <map>
16 #include <string>
17 #include <2geom/line.h>
18 #include <glibmm/i18n.h>
20 #include "desktop.h"
21 #include "desktop-handles.h"
22 #include "dialogs/dialog-events.h"
23 #include "display/canvas-axonomgrid.h"
24 #include "display/canvas-grid.h"
25 #include "display/guideline.h"
26 #include "display/snap-indicator.h"
27 #include "document.h"
28 #include "event-context.h"
29 #include "helper/action.h"
30 #include "helper/unit-menu.h"
31 #include "helper/units.h"
32 #include "message-context.h"
33 #include "preferences.h"
34 #include "snap.h"
35 #include "sp-guide.h"
36 #include "sp-metrics.h"
37 #include "sp-namedview.h"
38 #include "tools-switch.h"
39 #include "ui/dialog/guides.h"
40 #include "widgets/desktop-widget.h"
41 #include "xml/repr.h"
43 static void snoop_extended(GdkEvent* event, SPDesktop *desktop);
44 static void init_extended();
46 /* Root item handler */
48 int sp_desktop_root_handler(SPCanvasItem */*item*/, GdkEvent *event, SPDesktop *desktop)
49 {
50     static bool watch = false;
51     static bool first = true;
53     if ( first ) {
54         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
55         if ( prefs->getBool("/options/useextinput/value", true)
56             && prefs->getBool("/options/switchonextinput/value") ) {
57             watch = true;
58             init_extended();
59         }
60         first = false;
61     }
62     if ( watch ) {
63         snoop_extended(event, desktop);
64     }
66     return sp_event_context_root_handler(desktop->event_context, event);
67 }
70 static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw, bool horiz)
71 {
72     static bool dragging = false;
73     static SPCanvasItem *guide = NULL;
74     static Geom::Point normal;
75     int wx, wy;
77     SPDesktop *desktop = dtw->desktop;
78     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
80     gdk_window_get_pointer(GTK_WIDGET(dtw->canvas)->window, &wx, &wy, NULL);
81     Geom::Point const event_win(wx, wy);
83     gint width, height;
84     gdk_window_get_geometry(GTK_WIDGET(dtw->canvas)->window, NULL /*x*/, NULL /*y*/, &width, &height, NULL/*depth*/);
86     switch (event->type) {
87     case GDK_BUTTON_PRESS:
88             if (event->button.button == 1) {
89                 dragging = true;
90                 sp_canvas_set_snap_delay_active(desktop->canvas, true);
91                 Geom::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win));
92                 Geom::Point const event_dt(desktop->w2d(event_w));
94                 // explicitly show guidelines; if I draw a guide, I want them on
95                 sp_repr_set_boolean(repr, "showguides", TRUE);
96                 sp_repr_set_boolean(repr, "inkscape:guide-bbox", TRUE);
98                 // calculate the normal of the guidelines when dragged from the edges of rulers.
99                 Geom::Point normal_bl_to_tr(-1.,1.); //bottomleft to topright
100                 Geom::Point normal_tr_to_bl(1.,1.); //topright to bottomleft
101                 normal_bl_to_tr.normalize();
102                 normal_tr_to_bl.normalize();
103                 Inkscape::CanvasGrid * grid = sp_namedview_get_first_enabled_grid(desktop->namedview);
104                 if ( grid && grid->getGridType() == Inkscape::GRID_AXONOMETRIC ) {
105                     Inkscape::CanvasAxonomGrid *axonomgrid = dynamic_cast<Inkscape::CanvasAxonomGrid *>(grid);
106                     if (event->button.state & GDK_CONTROL_MASK) {
107                         // guidelines normal to gridlines
108                         normal_bl_to_tr = Geom::Point::polar(-axonomgrid->angle_rad[0], 1.0);
109                         normal_tr_to_bl = Geom::Point::polar(axonomgrid->angle_rad[2], 1.0);
110                     } else {
111                         normal_bl_to_tr = rot90(Geom::Point::polar(axonomgrid->angle_rad[2], 1.0));
112                         normal_tr_to_bl = rot90(Geom::Point::polar(-axonomgrid->angle_rad[0], 1.0));
113                     }
114                 }
115                 if (horiz) {
116                     if (wx < 50) {
117                         normal = normal_bl_to_tr;
118                     } else if (wx > width - 50) {
119                         normal = normal_tr_to_bl;
120                     } else {
121                         normal = Geom::Point(0.,1.);
122                     }
123                 } else {
124                     if (wy < 50) {
125                         normal = normal_bl_to_tr;
126                     } else if (wy > height - 50) {
127                         normal = normal_tr_to_bl;
128                     } else {
129                         normal = Geom::Point(1.,0.);
130                     }
131                 }
133                 guide = sp_guideline_new(desktop->guides, event_dt, normal);
134                 sp_guideline_set_color(SP_GUIDELINE(guide), desktop->namedview->guidehicolor);
135                 gdk_pointer_grab(widget->window, FALSE,
136                                  (GdkEventMask)(GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK ),
137                                  NULL, NULL,
138                                  event->button.time);
139             }
140             break;
141     case GDK_MOTION_NOTIFY:
142             if (dragging) {
143                 Geom::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win));
144                 Geom::Point event_dt(desktop->w2d(event_w));
146                 SnapManager &m = desktop->namedview->snap_manager;
147                 m.setup(desktop);
148                 m.guideSnap(event_dt, normal);
150                 sp_guideline_set_position(SP_GUIDELINE(guide), from_2geom(event_dt));
151                 desktop->set_coordinate_status(to_2geom(event_dt));
152                 desktop->setPosition(to_2geom(event_dt));
153             }
154             break;
155     case GDK_BUTTON_RELEASE:
156             if (dragging && event->button.button == 1) {
157                 gdk_pointer_ungrab(event->button.time);
158                 Geom::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win));
159                 Geom::Point event_dt(desktop->w2d(event_w));
161                 SnapManager &m = desktop->namedview->snap_manager;
162                 m.setup(desktop);
163                 m.guideSnap(event_dt, normal);
165                 dragging = false;
166                 sp_canvas_set_snap_delay_active(desktop->canvas, false);
167                 gtk_object_destroy(GTK_OBJECT(guide));
168                 guide = NULL;
169                 if ((horiz ? wy : wx) >= 0) {
170                     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
171                     Inkscape::XML::Node *repr = xml_doc->createElement("sodipodi:guide");
172                     sp_repr_set_point(repr, "orientation", normal);
173                     sp_repr_set_point(repr, "position", from_2geom(event_dt));
174                     SP_OBJECT_REPR(desktop->namedview)->appendChild(repr);
175                     Inkscape::GC::release(repr);
176                     sp_document_done(sp_desktop_document(desktop), SP_VERB_NONE,
177                                      _("Create guide"));
178                 }
179                 desktop->set_coordinate_status(from_2geom(event_dt));
180             }
181         default:
182             break;
183     }
185     return FALSE;
188 int sp_dt_hruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw)
190     return sp_dt_ruler_event(widget, event, dtw, true);
193 int sp_dt_vruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw)
195     return sp_dt_ruler_event(widget, event, dtw, false);
198 /* Guides */
200 static Geom::Point drag_origin;
202 enum SPGuideDragType {
203     SP_DRAG_TRANSLATE,
204     SP_DRAG_TRANSLATE_CONSTRAINED,
205     SP_DRAG_ROTATE,
206     SP_DRAG_MOVE_ORIGIN,
207     SP_DRAG_NONE
208 };
210 static SPGuideDragType drag_type = SP_DRAG_NONE;
212 gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data)
214     static bool moved = false;
215     gint ret = FALSE;
217     SPGuide *guide = SP_GUIDE(data);
218     SPDesktop *desktop = static_cast<SPDesktop*>(gtk_object_get_data(GTK_OBJECT(item->canvas), "SPDesktop"));
220     switch (event->type) {
221         case GDK_2BUTTON_PRESS:
222             if (event->button.button == 1) {
223                 drag_type = SP_DRAG_NONE;
224                 sp_canvas_set_snap_delay_active(desktop->canvas, false);
225                 sp_canvas_item_ungrab(item, event->button.time);
226                 Inkscape::UI::Dialogs::GuidelinePropertiesDialog::showDialog(guide, desktop);
227                 ret = TRUE;
228             }
229             break;
230         case GDK_BUTTON_PRESS:
231             if (event->button.button == 1) {
232                 if (event->button.state & GDK_CONTROL_MASK) {
233                     SPDocument *doc = SP_OBJECT_DOCUMENT(guide);
234                     sp_guide_remove(guide);
235                     sp_document_done(doc, SP_VERB_NONE, _("Delete guide"));
236                     ret = TRUE;
237                     break;
238                 }
240                 sp_canvas_set_snap_delay_active(desktop->canvas, true);
241                 double tol = 40.0;
242                 Geom::Point const event_w(event->button.x, event->button.y);
243                 Geom::Point const event_dt(desktop->w2d(event_w));
244                 drag_origin = event_dt;
245                 if (Geom::L2(guide->point_on_line - event_dt) < tol) {
246                     // the click was on the guide 'anchor'
247                     drag_type = (event->button.state & GDK_SHIFT_MASK) ? SP_DRAG_MOVE_ORIGIN : SP_DRAG_TRANSLATE;
248                 } else {
249                     drag_type = (event->button.state & GDK_SHIFT_MASK) ? SP_DRAG_ROTATE : SP_DRAG_TRANSLATE;
250                     sp_canvas_item_grab(item,
251                                         ( GDK_BUTTON_RELEASE_MASK  |
252                                           GDK_BUTTON_PRESS_MASK    |
253                                           GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK ),
254                                         NULL,
255                                         event->button.time);
256                 }
257                 ret = TRUE;
258             }
259             break;
260         case GDK_MOTION_NOTIFY:
261             if (drag_type != SP_DRAG_NONE) {
262                 Geom::Point const motion_w(event->motion.x,
263                                            event->motion.y);
264                 Geom::Point motion_dt(desktop->w2d(motion_w));
266                 // This is for snapping while dragging existing guidelines. New guidelines, 
267                 // which are dragged off the ruler, are being snapped in sp_dt_ruler_event
268                 SnapManager &m = desktop->namedview->snap_manager;
269                 m.setup(desktop);
270                 m.guideSnap(motion_dt, to_2geom(guide->normal_to_line));
272                 switch (drag_type) {
273                     case SP_DRAG_TRANSLATE:
274                     {
275                         sp_guide_moveto(*guide, guide->point_on_line + motion_dt - drag_origin, false);
276                         break;
277                     }
278                     case SP_DRAG_TRANSLATE_CONSTRAINED:
279                     {
280                         Geom::Point pt_constr = Geom::constrain_angle(guide->point_on_line, motion_dt);
281                         sp_guide_moveto(*guide, pt_constr, false);
282                         break;
283                     }
284                     case SP_DRAG_ROTATE:
285                     {
286                         double angle = angle_between(drag_origin - guide->point_on_line, motion_dt - guide->point_on_line);
287                         sp_guide_set_normal(*guide, guide->normal_to_line * Geom::Rotate(angle), false);
288                         break;
289                     }
290                     case SP_DRAG_MOVE_ORIGIN:
291                     {
292                         Geom::Line line(guide->point_on_line, guide->angle());
293                         Geom::Coord t = line.nearestPoint(motion_dt);
294                         sp_guide_moveto(*guide, line.pointAt(t), false);
295                         break;
296                     }
297                     case SP_DRAG_NONE:
298                         g_assert_not_reached();
299                         break;
300                 }
301                 moved = true;
302                 desktop->set_coordinate_status(from_2geom(motion_dt));
303                 desktop->setPosition(from_2geom(motion_dt));
305                 ret = TRUE;
306             }
307             break;
308     case GDK_BUTTON_RELEASE:
309             if (drag_type != SP_DRAG_NONE && event->button.button == 1) {
310                 if (moved) {
311                     Geom::Point const event_w(event->button.x,
312                                               event->button.y);
313                     Geom::Point event_dt(desktop->w2d(event_w));
315                     SnapManager &m = desktop->namedview->snap_manager;
316                     m.setup(desktop);
317                     m.guideSnap(event_dt, guide->normal_to_line);
319                     if (sp_canvas_world_pt_inside_window(item->canvas, event_w)) {
320                         switch (drag_type) {
321                             case SP_DRAG_TRANSLATE:
322                             {
323                                 sp_guide_moveto(*guide, guide->point_on_line + event_dt - drag_origin, true);
324                                 break;
325                             }
326                             case SP_DRAG_TRANSLATE_CONSTRAINED:
327                             {
328                                 Geom::Point pt_constr = Geom::constrain_angle(guide->point_on_line, event_dt);
329                                 sp_guide_moveto(*guide, pt_constr, true);
330                                 break;
331                             }
332                             case SP_DRAG_ROTATE:
333                             {
334                                 double angle = angle_between(drag_origin - guide->point_on_line, event_dt - guide->point_on_line);
335                                 sp_guide_set_normal(*guide, guide->normal_to_line * Geom::Rotate(angle), true);
336                                 break;
337                             }
338                             case SP_DRAG_MOVE_ORIGIN:
339                             {
340                                 Geom::Line line(guide->point_on_line, guide->angle());
341                                 Geom::Coord t = line.nearestPoint(event_dt);
342                                 sp_guide_moveto(*guide, line.pointAt(t), true);
343                                 break;
344                             }
345                             case SP_DRAG_NONE:
346                                 g_assert_not_reached();
347                                 break;
348                         }
349                         sp_document_done(sp_desktop_document(desktop), SP_VERB_NONE,
350                                          _("Move guide"));
351                     } else {
352                         /* Undo movement of any attached shapes. */
353                         sp_guide_moveto(*guide, guide->point_on_line, false);
354                         sp_guide_set_normal(*guide, guide->normal_to_line, false);
355                         sp_guide_remove(guide);
356                         sp_document_done(sp_desktop_document(desktop), SP_VERB_NONE,
357                                      _("Delete guide"));
358                     }
359                     moved = false;
360                     desktop->set_coordinate_status(from_2geom(event_dt));
361                     desktop->setPosition (from_2geom(event_dt));
362                 }
363                 drag_type = SP_DRAG_NONE;
364                 sp_canvas_set_snap_delay_active(desktop->canvas, false);
365                 sp_canvas_item_ungrab(item, event->button.time);
366                 ret=TRUE;
367             }
368     case GDK_ENTER_NOTIFY:
369     {
370             sp_guideline_set_color(SP_GUIDELINE(item), guide->hicolor);
372             char *guide_description = sp_guide_description(guide);
373             desktop->guidesMessageContext()->setF(Inkscape::NORMAL_MESSAGE, _("<b>Guideline</b>: %s"), guide_description);
374             g_free(guide_description);
375             break;
376     }
377     case GDK_LEAVE_NOTIFY:
378             sp_guideline_set_color(SP_GUIDELINE(item), guide->color);
379             desktop->guidesMessageContext()->clear();
380             break;
381     default:
382             break;
383     }
385     return ret;
388 //static std::map<GdkInputSource, std::string> switchMap;
389 static std::map<std::string, int> toolToUse;
390 static std::string lastName;
391 static GdkInputSource lastType = GDK_SOURCE_MOUSE;
393 static void init_extended()
395     std::string avoidName = "pad";
396     GList* devices = gdk_devices_list();
397     if ( devices ) {
398         for ( GList* curr = devices; curr; curr = g_list_next(curr) ) {
399             GdkDevice* dev = reinterpret_cast<GdkDevice*>(curr->data);
400             if ( dev->name
401                  && (avoidName != dev->name)
402                  && (dev->source != GDK_SOURCE_MOUSE) ) {
403 //                 g_message("Adding '%s' as [%d]", dev->name, dev->source);
405                 // Set the initial tool for the device
406                 switch ( dev->source ) {
407                     case GDK_SOURCE_PEN:
408                         toolToUse[dev->name] = TOOLS_CALLIGRAPHIC;
409                         break;
410                     case GDK_SOURCE_ERASER:
411                         toolToUse[dev->name] = TOOLS_ERASER;
412                         break;
413                     case GDK_SOURCE_CURSOR:
414                         toolToUse[dev->name] = TOOLS_SELECT;
415                         break;
416                     default:
417                         ; // do not add
418                 }
419 //            } else if (dev->name) {
420 //                 g_message("Skippn '%s' as [%s]", dev->name, dev->source);
421             }
422         }
423     }
427 void snoop_extended(GdkEvent* event, SPDesktop *desktop)
429     GdkInputSource source = GDK_SOURCE_MOUSE;
430     std::string name;
432     switch ( event->type ) {
433         case GDK_MOTION_NOTIFY:
434         {
435             GdkEventMotion* event2 = reinterpret_cast<GdkEventMotion*>(event);
436             if ( event2->device ) {
437                 source = event2->device->source;
438                 name = event2->device->name;
439             }
440         }
441         break;
443         case GDK_BUTTON_PRESS:
444         case GDK_2BUTTON_PRESS:
445         case GDK_3BUTTON_PRESS:
446         case GDK_BUTTON_RELEASE:
447         {
448             GdkEventButton* event2 = reinterpret_cast<GdkEventButton*>(event);
449             if ( event2->device ) {
450                 source = event2->device->source;
451                 name = event2->device->name;
452             }
453         }
454         break;
456         case GDK_SCROLL:
457         {
458             GdkEventScroll* event2 = reinterpret_cast<GdkEventScroll*>(event);
459             if ( event2->device ) {
460                 source = event2->device->source;
461                 name = event2->device->name;
462             }
463         }
464         break;
466         case GDK_PROXIMITY_IN:
467         case GDK_PROXIMITY_OUT:
468         {
469             GdkEventProximity* event2 = reinterpret_cast<GdkEventProximity*>(event);
470             if ( event2->device ) {
471                 source = event2->device->source;
472                 name = event2->device->name;
473             }
474         }
475         break;
477         default:
478             ;
479     }
481     if (!name.empty()) {
482         if ( lastType != source || lastName != name ) {
483             // The device switched. See if it is one we 'count'
484             //g_message("Changed device %s -> %s", lastName.c_str(), name.c_str());
485             std::map<std::string, int>::iterator it = toolToUse.find(lastName);
486             if (it != toolToUse.end()) {
487                 // Save the tool currently selected for next time the input
488                 // device shows up.
489                 it->second = tools_active(desktop);
490             }
492             it = toolToUse.find(name);
493             if (it != toolToUse.end() ) {
494                 tools_switch(desktop, it->second);
495             }
497             lastName = name;
498             lastType = source;
499         }
500     }
505 /*
506   Local Variables:
507   mode:c++
508   c-file-style:"stroustrup"
509   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
510   indent-tabs-mode:nil
511   fill-column:99
512   End:
513 */
514 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :