3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #define DEBUG_KP_TOOL_POLYGON 0
32 #include <kpToolPolygonalBase.h>
44 #include <qpushbutton.h>
51 #include <kpCommandHistory.h>
52 #include <kpDocument.h>
55 #include <kpPainter.h>
56 #include <kpPixmapFX.h>
57 #include <kpTempImage.h>
58 #include <kpToolEnvironment.h>
59 #include <kpToolPolygonalCommand.h>
60 #include <kpToolToolBar.h>
61 #include <kpToolWidgetLineWidth.h>
62 #include <kpViewManager.h>
65 struct kpToolPolygonalBasePrivate
67 kpToolPolygonalBasePrivate ()
68 : toolWidgetLineWidth (0)
72 kpToolPolygonalBase::DrawShapeFunc drawShapeFunc
;
74 kpToolWidgetLineWidth
*toolWidgetLineWidth
;
76 int originatingMouseButton
;
81 kpToolPolygonalBase::kpToolPolygonalBase (
83 const QString
&description
,
84 DrawShapeFunc drawShapeFunc
,
86 kpToolEnvironment
*environ
, QObject
*parent
,
89 : kpTool (text
, description
, key
, environ
, parent
, name
),
90 d (new kpToolPolygonalBasePrivate ())
92 d
->drawShapeFunc
= drawShapeFunc
;
94 d
->toolWidgetLineWidth
= 0;
96 // (hopefully cause crash if we use it before initialising it)
97 d
->originatingMouseButton
= -1;
100 kpToolPolygonalBase::~kpToolPolygonalBase ()
107 void kpToolPolygonalBase::begin ()
109 kpToolToolBar
*tb
= toolToolBar ();
112 #if DEBUG_KP_TOOL_POLYGON
113 kDebug () << "kpToolPolygonalBase::begin() tb=" << tb
;
116 d
->toolWidgetLineWidth
= tb
->toolWidgetLineWidth ();
117 connect (d
->toolWidgetLineWidth
, SIGNAL (lineWidthChanged (int)),
118 this, SLOT (updateShape ()));
119 d
->toolWidgetLineWidth
->show ();
121 viewManager ()->setCursor (QCursor (Qt::CrossCursor
));
123 d
->originatingMouseButton
= -1;
125 setUserMessage (/*virtual*/haventBegunShapeUserMessage ());
129 void kpToolPolygonalBase::end ()
134 disconnect (d
->toolWidgetLineWidth
,
135 SIGNAL (lineWidthChanged (int)),
137 SLOT (updateShape ()));
138 d
->toolWidgetLineWidth
= 0;
140 viewManager ()->unsetCursor ();
144 void kpToolPolygonalBase::beginDraw ()
146 #if DEBUG_KP_TOOL_POLYGON
147 kDebug () << "kpToolPolygonalBase::beginDraw() d->points=" << d
->points
.toList ()
148 << ", startPoint=" << startPoint () << endl
;
151 bool endedShape
= false;
153 // We now need to start with dragging out the initial line?
154 if (d
->points
.count () == 0)
156 d
->originatingMouseButton
= mouseButton ();
158 // The line starts and ends at the start point of the drag.
159 // draw() will modify the last point in d->points to reflect the
160 // mouse drag, as the drag proceeds.
161 d
->points
.append (startPoint ());
162 d
->points
.append (startPoint ());
164 // Already have control points - not dragging out initial line.
167 // Clicking the other mouse button?
168 if (mouseButton () != d
->originatingMouseButton
)
170 // Finish shape. TODO: I suspect we need to call endShapeInternal instead.
174 // Are we dragging out an extra control point?
177 // Add another control point.
178 d
->points
.append (startPoint ());
182 #if DEBUG_KP_TOOL_POLYGON
183 kDebug () << "\tafterwards, d->points=" << d
->points
.toList ();
188 // We've started dragging. Print instructions on how to cancel shape.
189 setUserMessage (cancelUserMessage ());
195 void kpToolPolygonalBase::applyModifiers ()
197 const int count
= d
->points
.count ();
199 QPoint
&lineStartPoint
= d
->points
[count
- 2];
200 QPoint
&lineEndPoint
= d
->points
[count
- 1];
202 #if DEBUG_KP_TOOL_POLYGON && 1
203 kDebug () << "kpToolPolygonalBase::applyModifiers() #pts=" << count
204 << " line: startPt=" << lineStartPoint
205 << " endPt=" << lineEndPoint
206 << " modifiers: shift=" << shiftPressed ()
207 << " alt=" << altPressed ()
208 << " ctrl=" << controlPressed ()
213 if (shiftPressed () || controlPressed ())
215 int diffx
= lineEndPoint
.x () - lineStartPoint
.x ();
216 int diffy
= lineEndPoint
.y () - lineStartPoint
.y ();
222 ratio
= fabs (double (diffy
) / double (diffx
));
223 #if DEBUG_KP_TOOL_POLYGON && 1
224 kDebug () << "\tdiffx=" << diffx
<< " diffy=" << diffy
225 << " ratio=" << ratio
230 // Ctrl = 0, 30, 60, 90
231 // Shift + Ctrl = 0, 30, 45, 60, 90
232 double angles
[10]; // "ought to be enough for anybody"
234 angles
[numAngles
++] = 0;
235 if (controlPressed ())
236 angles
[numAngles
++] = KP_PI
/ 6;
238 angles
[numAngles
++] = KP_PI
/ 4;
239 if (controlPressed ())
240 angles
[numAngles
++] = KP_PI
/ 3;
241 angles
[numAngles
++] = KP_PI
/ 2;
242 Q_ASSERT (numAngles
<= int (sizeof (angles
) / sizeof (angles
[0])));
244 double angle
= angles
[numAngles
- 1];
245 for (int i
= 0; i
< numAngles
- 1; i
++)
247 double acceptingRatio
= tan ((angles
[i
] + angles
[i
+ 1]) / 2.0);
248 if (ratio
< acceptingRatio
)
255 // horizontal (dist from start not maintained)
256 if (fabs (KP_RADIANS_TO_DEGREES (angle
) - 0)
257 < kpPixmapFX::AngleInDegreesEpsilon
)
260 QPoint (lineEndPoint
.x (), lineStartPoint
.y ());
262 // vertical (dist from start not maintained)
263 else if (fabs (KP_RADIANS_TO_DEGREES (angle
) - 90)
264 < kpPixmapFX::AngleInDegreesEpsilon
)
267 QPoint (lineStartPoint
.x (), lineEndPoint
.y ());
269 // diagonal (dist from start maintained)
272 const double dist
= sqrt ((double)(diffx
* diffx
+ diffy
* diffy
));
274 #define sgn(a) ((a)<0?-1:1)
275 // Round distances _before_ adding to any coordinate
276 // (ensures consistent rounding behaviour in x & y directions)
277 const int newdx
= qRound (dist
* cos (angle
) * sgn (diffx
));
278 const int newdy
= qRound (dist
* sin (angle
) * sgn (diffy
));
281 lineEndPoint
= QPoint (lineStartPoint
.x () + newdx
,
282 lineStartPoint
.y () + newdy
);
284 #if DEBUG_KP_TOOL_POLYGON && 1
285 kDebug () << "\t\tdiagonal line: dist=" << dist
286 << " angle=" << (angle
* 180 / KP_PI
)
287 << " endPoint=" << lineEndPoint
291 } // if (shiftPressed () || controlPressed ()) {
294 if (altPressed () && 0/*ALT is unreliable*/)
296 // start = start - diff
297 // = start - (end - start)
298 // = start - end + start
301 lineStartPoint
+= (lineStartPoint
- lineEndPoint
);
303 lineEndPoint
+= (lineEndPoint
- lineStartPoint
);
304 } // if (altPressed ()) {
309 QPolygon
*kpToolPolygonalBase::points () const
316 int kpToolPolygonalBase::originatingMouseButton () const
318 Q_ASSERT (hasBegunShape ());
319 return d
->originatingMouseButton
;
324 void kpToolPolygonalBase::draw (const QPoint
&, const QPoint
&, const QRect
&)
326 // A click of the other mouse button (to finish shape, instead of adding
327 // another control point) would have caused endShape() to have been
328 // called in kpToolPolygonalBase::beginDraw(). The points list would now
329 // be empty. We are being called by kpTool::mouseReleaseEvent().
330 if (d
->points
.count () == 0)
333 #if DEBUG_KP_TOOL_POLYGON
334 kDebug () << "kpToolPolygonalBase::draw() d->points=" << d
->points
.toList ()
335 << ", endPoint=" << currentPoint () << endl
;
338 // Update points() so that last point reflects current mouse position.
339 const int count
= d
->points
.count ();
340 d
->points
[count
- 1] = currentPoint ();
342 #if DEBUG_KP_TOOL_POLYGON
343 kDebug () << "\tafterwards, d->points=" << d
->points
.toList ();
346 // Are we drawing a line?
347 if (/*virtual*/drawingALine ())
349 // Adjust the line (end points given by the last 2 points of points())
350 // in response to keyboard modifiers.
353 // Update the preview of the shape.
356 // Inform the user that we're dragging out a line with 2 control points.
357 setUserShapePoints (d
->points
[count
- 2], d
->points
[count
- 1]);
359 // We're modifying a point.
362 // Update the preview of the shape.
365 // Informs the user that we're just modifying a point (perhaps, a control
366 // point of a Bezier).
367 setUserShapePoints (d
->points
[count
- 1]);
372 // TODO: code dup with kpToolRectangle
374 kpColor
kpToolPolygonalBase::drawingForegroundColor () const
376 return color (originatingMouseButton ());
380 kpColor
kpToolPolygonalBase::drawingBackgroundColor () const
382 return kpColor::Invalid
;
385 // TODO: code dup with kpToolRectangle
387 void kpToolPolygonalBase::updateShape ()
389 if (d
->points
.count () == 0)
392 const QRect boundingRect
= kpTool::neededRect (
393 d
->points
.boundingRect (),
394 d
->toolWidgetLineWidth
->lineWidth ());
396 #if DEBUG_KP_TOOL_POLYGON
397 kDebug () << "kpToolPolygonalBase::updateShape() boundingRect="
400 << d
->toolWidgetLineWidth
->lineWidth ()
404 kpImage image
= document ()->getImageAt (boundingRect
);
406 QPolygon pointsTranslated
= d
->points
;
407 pointsTranslated
.translate (-boundingRect
.x (), -boundingRect
.y ());
409 (*d
->drawShapeFunc
) (&image
,
411 drawingForegroundColor (), d
->toolWidgetLineWidth
->lineWidth (),
412 /*virtual*/drawingBackgroundColor (),
415 kpTempImage
newTempImage (false/*always display*/,
416 kpTempImage::SetImage
/*render mode*/,
417 boundingRect
.topLeft (),
420 viewManager ()->setFastUpdates ();
422 viewManager ()->setTempImage (newTempImage
);
424 viewManager ()->restoreFastUpdates ();
428 void kpToolPolygonalBase::cancelShape ()
430 viewManager ()->invalidateTempImage ();
431 d
->points
.resize (0);
433 setUserMessage (i18n ("Let go of all the mouse buttons."));
436 void kpToolPolygonalBase::releasedAllButtons ()
438 if (!hasBegunShape ())
439 setUserMessage (/*virtual*/haventBegunShapeUserMessage ());
441 // --- else case already handled by endDraw() ---
444 // public virtual [base kpTool]
445 void kpToolPolygonalBase::endShape (const QPoint
&, const QRect
&)
447 #if DEBUG_KP_TOOL_POLYGON
448 kDebug () << "kpToolPolygonalBase::endShape() d->points="
449 << d
->points
.toList () << endl
;
452 if (!hasBegunShape ())
455 viewManager ()->invalidateTempImage ();
457 QRect boundingRect
= kpTool::neededRect (
458 d
->points
.boundingRect (),
459 d
->toolWidgetLineWidth
->lineWidth ());
461 commandHistory ()->addCommand (
462 new kpToolPolygonalCommand (
465 d
->points
, boundingRect
,
466 drawingForegroundColor (), d
->toolWidgetLineWidth
->lineWidth (),
467 /*virtual*/drawingBackgroundColor (),
468 environ ()->commandEnvironment ()));
470 d
->points
.resize (0);
471 setUserMessage (/*virtual*/haventBegunShapeUserMessage ());
475 // public virtual [base kpTool]
476 bool kpToolPolygonalBase::hasBegunShape () const
478 return (d
->points
.count () > 0);
482 // virtual protected slot [base kpTool]
483 void kpToolPolygonalBase::slotForegroundColorChanged (const kpColor
&)
488 // virtual protected slot [base kpTool]
489 void kpToolPolygonalBase::slotBackgroundColorChanged (const kpColor
&)
495 #include <kpToolPolygonalBase.moc>