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 }
146 }
148 void
149 SnapIndicator::remove_snaptarget()
150 {
151 if (_snaptarget) {
152 _desktop->remove_temporary_canvasitem(_snaptarget);
153 _snaptarget = NULL;
154 }
156 remove_tooltip();
157 }
159 void
160 SnapIndicator::set_new_snapsource(Geom::Point const p)
161 {
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 }
183 }
185 void
186 SnapIndicator::remove_snapsource()
187 {
188 if (_snapsource) {
189 _desktop->remove_temporary_canvasitem(_snapsource);
190 _snapsource = NULL;
191 }
192 }
194 // Shows or hides the tooltip
195 void SnapIndicator::update_tooltip() const
196 {
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 }
218 }
220 // Can be called either directly or through a timer
221 bool
222 SnapIndicator::remove_tooltip() const
223 {
224 gtk_widget_set_has_tooltip (&(_desktop->canvas->widget), false);
225 gtk_widget_trigger_tooltip_query(&(_desktop->canvas->widget));
226 return false;
227 }
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 :