Code

After snapping, show a tooltip together with the snap indicator
[inkscape.git] / src / display / snap-indicator.cpp
1 /** \file
2  * Provides a class that shows a temporary indicator on the canvas of where the snap was, and what kind of snap
3  *
4  * Authors:
5  *   Johan Engelen
6  *   Diederik van Lierop
7  *
8  * Copyright (C) Johan Engelen 2009 <j.b.c.engelen@utwente.nl>
9  * Copyright (C) Diederik van Lierop 2009 <mail@diedenrezi.nl>
10  *
11  * Released under GNU GPL, read the file 'COPYING' for more information
12  */
14 #include "display/snap-indicator.h"
16 #include "desktop.h"
17 #include "desktop-handles.h"
18 #include "display/sodipodi-ctrl.h"
19 #include "knot.h"
20 #include "preferences.h"
21 #include <glibmm/i18n.h>
22 #include <gtk/gtk.h>
24 namespace Inkscape {
25 namespace Display {
27 SnapIndicator::SnapIndicator(SPDesktop * desktop)
28     :   _snaptarget(NULL),
29         _snapsource(NULL),
30         _desktop(desktop)
31 {
32 }
34 SnapIndicator::~SnapIndicator()
35 {
36     // remove item that might be present
37         remove_snaptarget();
38         remove_snapsource();
39 }
41 void
42 SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p)
43 {
44         remove_snaptarget();
46     g_assert(_desktop != NULL);
48     /* Commented out for now, because this might hide any snapping bug!
49     if (!p.getSnapped()) {
50        return; // If we haven't snapped, then it is of no use to draw a snapindicator
51     }
52     */
54     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
55     bool value = prefs->getBool("/options/snapindicator/value", true);
57     if (value) {
58         gchar *target_name = _("UNDEFINED");
59         switch (p.getTarget()) {
60                         case SNAPTARGET_UNDEFINED:
61                                 target_name = _("UNDEFINED");
62                                 break;
63                         case SNAPTARGET_GRID:
64                 target_name = _("grid line");
65                 break;
66             case SNAPTARGET_GRID_INTERSECTION:
67                 target_name = _("grid intersection");
68                 break;
69             case SNAPTARGET_GUIDE:
70                 target_name = _("guide");
71                 break;
72             case SNAPTARGET_GUIDE_INTERSECTION:
73                 target_name = _("guide intersection");
74                 break;
75             case SNAPTARGET_GRID_GUIDE_INTERSECTION:
76                 target_name = _("grid-guide intersection");
77                 break;
78             case SNAPTARGET_NODE:
79                                 target_name = _("node");
80                                 break;
81                         case SNAPTARGET_PATH:
82                 target_name = _("path");
83                 break;
84             case SNAPTARGET_PATH_INTERSECTION:
85                 target_name = _("path intersection");
86                 break;
87             case SNAPTARGET_BBOX_CORNER:
88                 target_name = _("bounding box corner");
89                 break;
90             case SNAPTARGET_BBOX_EDGE:
91                 target_name = _("bounding box side");
92                 break;
93             case SNAPTARGET_GRADIENT:
94                 target_name = _("gradient");
95                 break;
96             case SNAPTARGET_PAGE_BORDER:
97                 target_name = _("page border");
98                                 break;
99             default:
100                 g_warning("Snap target has not yet been defined!");
101                 break;
102         }
103         // std::cout << "Snapped to: " << target_name << std::endl;
105         // Display the snap indicator (i.e. the cross)
106         SPCanvasItem * canvasitem = NULL;
107                 if (p.getTarget() == SNAPTARGET_NODE) {
108                         canvasitem = sp_canvas_item_new(sp_desktop_tempgroup (_desktop),
109                                                                                         SP_TYPE_CTRL,
110                                                                                         "anchor", GTK_ANCHOR_CENTER,
111                                                                                         "size", 10.0,
112                                                                                         "stroked", TRUE,
113                                                                                         "stroke_color", 0xf000f0ff,
114                                                                                         "mode", SP_KNOT_MODE_XOR,
115                                                                                         "shape", SP_KNOT_SHAPE_DIAMOND,
116                                                                                         NULL );
117                 } else {
118                         canvasitem = sp_canvas_item_new(sp_desktop_tempgroup (_desktop),
119                                                                                         SP_TYPE_CTRL,
120                                                                                         "anchor", GTK_ANCHOR_CENTER,
121                                                                                         "size", 10.0,
122                                                                                         "stroked", TRUE,
123                                                                                         "stroke_color", 0xf000f0ff,
124                                                                                         "mode", SP_KNOT_MODE_XOR,
125                                                                                         "shape", SP_KNOT_SHAPE_CROSS,
126                                                                                         NULL );
127                 }
129                 const int timeout_val = 1000; // TODO add preference for snap indicator timeout?
131                 SP_CTRL(canvasitem)->moveto(p.getPoint());
132                 remove_snapsource(); // Don't set both the source and target indicators, as these will overlap
133                 _snaptarget = _desktop->add_temporary_canvasitem(canvasitem, timeout_val);
135         // Display the tooltip
136                 GtkSettings *settings = gtk_widget_get_settings (&(_desktop->canvas->widget));
137                 // If we set the timeout too short, then the tooltip might not show at all (most noticeable when a long snap delay is active)
138                 g_object_set(settings, "gtk-tooltip-timeout", 200, NULL); // tooltip will be shown after x msec.
139         gtk_widget_set_tooltip_text(&(_desktop->canvas->widget), target_name);
140         // has_tooltip will be true by now because gtk_widget_set_has_tooltip() has been called implicitly
141         update_tooltip();
142         // The snap indicator will be removed automatically because it's a temporary canvas item; the tooltip
143         // however must be removed manually, so we'll create a timer to that end
144         Glib::signal_timeout().connect(sigc::mem_fun(*this, &SnapIndicator::remove_tooltip), timeout_val);
145         }
148 void
149 SnapIndicator::remove_snaptarget()
151     if (_snaptarget) {
152         _desktop->remove_temporary_canvasitem(_snaptarget);
153         _snaptarget = NULL;
154     }
156     remove_tooltip();
159 void
160 SnapIndicator::set_new_snapsource(Geom::Point const p)
162         remove_snapsource();
164     g_assert(_desktop != NULL);
166     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
167     bool value = prefs->getBool("/options/snapindicator/value", true);
169     if (value) {
170         SPCanvasItem * canvasitem = sp_canvas_item_new( sp_desktop_tempgroup (_desktop),
171                                                         SP_TYPE_CTRL,
172                                                         "anchor", GTK_ANCHOR_CENTER,
173                                                         "size", 10.0,
174                                                         "stroked", TRUE,
175                                                         "stroke_color", 0xf000f0ff,
176                                                         "mode", SP_KNOT_MODE_XOR,
177                                                         "shape", SP_KNOT_SHAPE_CIRCLE,
178                                                         NULL );
180         SP_CTRL(canvasitem)->moveto(p);
181         _snapsource = _desktop->add_temporary_canvasitem(canvasitem, 1000);
182     }
185 void
186 SnapIndicator::remove_snapsource()
188     if (_snapsource) {
189         _desktop->remove_temporary_canvasitem(_snapsource);
190         _snapsource = NULL;
191     }
194 // Shows or hides the tooltip
195 void SnapIndicator::update_tooltip() const
197         // When using gtk_widget_trigger_tooltip_query, the tooltip will for some reason always popup
198         // in the upper-left corner of the screen (probably at (0,0)). As a workaround we'll create
199         // a motion event instead, which will also trigger the tooltip
200         gint x, y;
201         GdkWindow *window;
203         GdkDisplay *display = gdk_display_get_default();
204         window = gdk_display_get_window_at_pointer(display, &x, &y);
205         if (window) {
206                 GdkEvent *event = gdk_event_new(GDK_MOTION_NOTIFY);
207                 event->motion.window = window;
208                 event->motion.x = x;
209                 event->motion.y = y;
210                 event->motion.is_hint = FALSE;
212                 gdk_window_get_origin(window, &x, &y);
213                 event->motion.x_root = event->motion.x + x;
214                 event->motion.y_root = event->motion.y + y;
216                 gtk_main_do_event(event);
217         }
220 // Can be called either directly or through a timer
221 bool
222 SnapIndicator::remove_tooltip() const
224     gtk_widget_set_has_tooltip (&(_desktop->canvas->widget), false);
225     gtk_widget_trigger_tooltip_query(&(_desktop->canvas->widget));
226     return false;
230 } //namespace Display
231 } /* namespace Inkscape */
234 /*
235   Local Variables:
236   mode:c++
237   c-file-style:"stroustrup"
238   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
239   indent-tabs-mode:nil
240   fill-column:99
241   End:
242 */
243 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :