1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
5 #include <glibmm/i18n.h>
6 #include "sp-item.h"
7 #include "desktop.h"
8 #include "message-context.h"
9 #include "message-stack.h"
10 #include "context-fns.h"
11 #include "snap.h"
12 #include "desktop-affine.h"
13 #include "event-context.h"
14 #include "sp-namedview.h"
15 #include "display/snap-indicator.h"
17 static const double midpt_1_goldenratio = (1 + goldenratio) / 2;
18 static const double midpt_goldenratio_2 = (goldenratio + 2) / 2;
20 /* FIXME: could probably use a template here */
22 /**
23 * Check to see if the current layer is both unhidden and unlocked. If not,
24 * set a message about it on the given context.
25 *
26 * \param desktop Desktop.
27 * \param message Message context to put messages on.
28 * \return true if the current layer is both unhidden and unlocked, otherwise false.
29 */
31 bool Inkscape::have_viable_layer(SPDesktop *desktop, MessageContext *message)
32 {
33 SPItem const *layer = SP_ITEM(desktop->currentLayer());
35 if ( !layer || desktop->itemIsHidden(layer) ) {
36 message->flash(Inkscape::ERROR_MESSAGE,
37 _("<b>Current layer is hidden</b>. Unhide it to be able to draw on it."));
38 return false;
39 }
41 if ( !layer || layer->isLocked() ) {
42 message->flash(Inkscape::ERROR_MESSAGE,
43 _("<b>Current layer is locked</b>. Unlock it to be able to draw on it."));
44 return false;
45 }
47 return true;
48 }
51 /**
52 * Check to see if the current layer is both unhidden and unlocked. If not,
53 * set a message about it on the given context.
54 *
55 * \param desktop Desktop.
56 * \param message Message context to put messages on.
57 * \return true if the current layer is both unhidden and unlocked, otherwise false.
58 */
60 bool Inkscape::have_viable_layer(SPDesktop *desktop, MessageStack *message)
61 {
62 SPItem const *layer = SP_ITEM(desktop->currentLayer());
64 if ( !layer || desktop->itemIsHidden(layer) ) {
65 message->flash(Inkscape::WARNING_MESSAGE,
66 _("<b>Current layer is hidden</b>. Unhide it to be able to draw on it."));
67 return false;
68 }
70 if ( !layer || layer->isLocked() ) {
71 message->flash(Inkscape::WARNING_MESSAGE,
72 _("<b>Current layer is locked</b>. Unlock it to be able to draw on it."));
73 return false;
74 }
76 return true;
77 }
80 NR::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item,
81 NR::Point const &pt, NR::Point const ¢er, int state)
82 {
83 NR::Point p[2];
85 bool const shift = state & GDK_SHIFT_MASK;
86 bool const control = state & GDK_CONTROL_MASK;
88 SnapManager &m = desktop->namedview->snap_manager;
89 m.setup(desktop, item);
90 Inkscape::SnappedPoint snappoint;
92 if (control) {
94 /* Control is down: we are constrained to producing integer-ratio rectangles */
96 /* Vector from the centre of the box to the point we are dragging to */
97 NR::Point delta = pt - center;
99 /* Round it so that we have an integer-ratio (or golden ratio) box */
100 if (fabs(delta[NR::X]) > fabs(delta[NR::Y]) && (delta[NR::Y] != 0.0)) {
101 double ratio = delta[NR::X] / delta[NR::Y];
102 double ratioabs = fabs (ratio);
103 double sign = (ratio < 0 ? -1 : 1);
104 if (midpt_1_goldenratio < ratioabs && ratioabs < midpt_goldenratio_2) {
105 delta[NR::X] = sign * goldenratio * delta[NR::Y];
106 } else {
107 delta[NR::X] = floor(ratio + 0.5) * delta[NR::Y];
108 }
109 } else if (delta[NR::X] != 0.0) {
110 double ratio = delta[NR::Y] / delta[NR::X];
111 double ratioabs = fabs (ratio);
112 double sign = (ratio < 0 ? -1 : 1);
113 if (midpt_1_goldenratio < ratioabs && ratioabs < midpt_goldenratio_2) {
114 delta[NR::Y] = sign * goldenratio * delta[NR::X];
115 } else {
116 delta[NR::Y] = floor(delta[NR::Y] / delta[NR::X] + 0.5) * delta[NR::X];
117 }
118 }
120 /* p[1] is the dragged point with the integer-ratio constraint */
121 p[1] = center + delta;
123 if (shift) {
125 /* Shift is down, so our origin is the centre point rather than the corner
126 ** point; this means that corner-point movements are bound to each other.
127 */
129 /* p[0] is the opposite corner of our box */
130 p[0] = center - delta;
132 Inkscape::SnappedPoint s[2];
134 /* Try to snap p[0] (the opposite corner) along the constraint vector */
135 s[0] = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, p[0],
136 Inkscape::Snapper::ConstraintLine(p[0] - p[1]));
138 /* Try to snap p[1] (the dragged corner) along the constraint vector */
139 s[1] = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, p[1],
140 Inkscape::Snapper::ConstraintLine(p[1] - p[0]));
142 /* Choose the best snap and update points accordingly */
143 if (s[0].getDistance() < s[1].getDistance()) {
144 p[0] = s[0].getPoint();
145 p[1] = 2 * center - s[0].getPoint();
146 snappoint = s[0];
147 } else {
148 p[0] = 2 * center - s[1].getPoint();
149 p[1] = s[1].getPoint();
150 snappoint = s[1];
151 }
152 desktop->snapindicator->set_new_snappoint(snappoint);
154 } else {
156 /* Our origin is the opposite corner. Snap the drag point along the constraint vector */
157 p[0] = center;
158 snappoint = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, p[1],
159 Inkscape::Snapper::ConstraintLine(p[1] - p[0]));
160 p[1] = snappoint.getPoint();
161 }
163 } else if (shift) {
165 /* Shift is down, so our origin is the centre point rather than the corner point;
166 ** this means that corner-point movements are bound to each other.
167 */
169 p[1] = pt;
170 p[0] = 2 * center - p[1];
172 Inkscape::SnappedPoint s[2];
174 s[0] = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, p[0]);
175 s[1] = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, p[1]);
177 if (s[0].getDistance() < s[1].getDistance()) {
178 p[0] = s[0].getPoint();
179 p[1] = 2 * center - s[0].getPoint();
180 snappoint = s[0];
181 } else {
182 p[0] = 2 * center - s[1].getPoint();
183 p[1] = s[1].getPoint();
184 snappoint = s[1];
185 }
187 } else {
189 /* There's no constraint on the corner point, so just snap it to anything */
190 p[0] = center;
191 snappoint = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, pt);
192 p[1] = snappoint.getPoint();
193 }
195 if (snappoint.getSnapped()) {
196 // this does not work well enough yet.
197 // desktop->snapindicator->set_new_snappoint(snappoint);
198 }
200 p[0] = sp_desktop_dt2root_xy_point(desktop, p[0]);
201 p[1] = sp_desktop_dt2root_xy_point(desktop, p[1]);
203 return NR::Rect(NR::Point(MIN(p[0][NR::X], p[1][NR::X]), MIN(p[0][NR::Y], p[1][NR::Y])),
204 NR::Point(MAX(p[0][NR::X], p[1][NR::X]), MAX(p[0][NR::Y], p[1][NR::Y])));
205 }
209 NR::Point Inkscape::setup_for_drag_start(SPDesktop *desktop, SPEventContext* ec, GdkEvent *ev)
210 {
211 ec->xp = static_cast<gint>(ev->button.x);
212 ec->yp = static_cast<gint>(ev->button.y);
213 ec->within_tolerance = true;
215 NR::Point const p(ev->button.x, ev->button.y);
216 ec->item_to_select = sp_event_context_find_item(desktop, p, ev->button.state & GDK_MOD1_MASK, TRUE);
217 return ec->desktop->w2d(p);
218 }
221 /*
222 Local Variables:
223 mode:c++
224 c-file-style:"stroustrup"
225 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
226 indent-tabs-mode:nil
227 fill-column:99
228 End:
229 */
230 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :