compile
[kdegraphics.git] / kolourpaint / tools / polygonal / kpToolPolygonalBase.cpp
blobc6c9c5e0ad6562cbfb27a42cc819264d6f6fd37c
2 /*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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>
34 #include <float.h>
35 #include <math.h>
37 #include <qbitmap.h>
38 #include <qcursor.h>
39 #include <qlayout.h>
40 #include <qpainter.h>
41 #include <qpixmap.h>
42 #include <qpoint.h>
43 #include <qpolygon.h>
44 #include <qpushbutton.h>
45 #include <qrect.h>
47 #include <kdebug.h>
48 #include <klocale.h>
50 #include <kpBug.h>
51 #include <kpCommandHistory.h>
52 #include <kpDocument.h>
53 #include <kpDefs.h>
54 #include <kpImage.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;
78 QPolygon points;
81 kpToolPolygonalBase::kpToolPolygonalBase (
82 const QString &text,
83 const QString &description,
84 DrawShapeFunc drawShapeFunc,
85 int key,
86 kpToolEnvironment *environ, QObject *parent,
87 const QString &name)
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 ()
102 delete d;
106 // virtual
107 void kpToolPolygonalBase::begin ()
109 kpToolToolBar *tb = toolToolBar ();
110 Q_ASSERT (tb);
112 #if DEBUG_KP_TOOL_POLYGON
113 kDebug () << "kpToolPolygonalBase::begin() tb=" << tb;
114 #endif
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 ());
128 // virtual
129 void kpToolPolygonalBase::end ()
131 // TODO: needed?
132 endShape ();
134 disconnect (d->toolWidgetLineWidth,
135 SIGNAL (lineWidthChanged (int)),
136 this,
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;
149 #endif
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.
165 else
167 // Clicking the other mouse button?
168 if (mouseButton () != d->originatingMouseButton)
170 // Finish shape. TODO: I suspect we need to call endShapeInternal instead.
171 endShape ();
172 endedShape = true;
174 // Are we dragging out an extra control point?
175 else
177 // Add another control point.
178 d->points.append (startPoint ());
182 #if DEBUG_KP_TOOL_POLYGON
183 kDebug () << "\tafterwards, d->points=" << d->points.toList ();
184 #endif
186 if (!endedShape)
188 // We've started dragging. Print instructions on how to cancel shape.
189 setUserMessage (cancelUserMessage ());
194 // protected
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 ()
209 << endl;
210 #endif
212 // angles
213 if (shiftPressed () || controlPressed ())
215 int diffx = lineEndPoint.x () - lineStartPoint.x ();
216 int diffy = lineEndPoint.y () - lineStartPoint.y ();
218 double ratio;
219 if (diffx == 0)
220 ratio = DBL_MAX;
221 else
222 ratio = fabs (double (diffy) / double (diffx));
223 #if DEBUG_KP_TOOL_POLYGON && 1
224 kDebug () << "\tdiffx=" << diffx << " diffy=" << diffy
225 << " ratio=" << ratio
226 << endl;
227 #endif
229 // Shift = 0, 45, 90
230 // Ctrl = 0, 30, 60, 90
231 // Shift + Ctrl = 0, 30, 45, 60, 90
232 double angles [10]; // "ought to be enough for anybody"
233 int numAngles = 0;
234 angles [numAngles++] = 0;
235 if (controlPressed ())
236 angles [numAngles++] = KP_PI / 6;
237 if (shiftPressed ())
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)
250 angle = angles [i];
251 break;
255 // horizontal (dist from start not maintained)
256 if (fabs (KP_RADIANS_TO_DEGREES (angle) - 0)
257 < kpPixmapFX::AngleInDegreesEpsilon)
259 lineEndPoint =
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)
266 lineEndPoint =
267 QPoint (lineStartPoint.x (), lineEndPoint.y ());
269 // diagonal (dist from start maintained)
270 else
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));
279 #undef sgn
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
288 << endl;
289 #endif
291 } // if (shiftPressed () || controlPressed ()) {
293 // centring
294 if (altPressed () && 0/*ALT is unreliable*/)
296 // start = start - diff
297 // = start - (end - start)
298 // = start - end + start
299 // = 2 * start - end
300 if (count == 2)
301 lineStartPoint += (lineStartPoint - lineEndPoint);
302 else
303 lineEndPoint += (lineEndPoint - lineStartPoint);
304 } // if (altPressed ()) {
308 // protected
309 QPolygon *kpToolPolygonalBase::points () const
311 return &d->points;
315 // protected
316 int kpToolPolygonalBase::originatingMouseButton () const
318 Q_ASSERT (hasBegunShape ());
319 return d->originatingMouseButton;
323 // virtual
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)
331 return;
333 #if DEBUG_KP_TOOL_POLYGON
334 kDebug () << "kpToolPolygonalBase::draw() d->points=" << d->points.toList ()
335 << ", endPoint=" << currentPoint () << endl;
336 #endif
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 ();
344 #endif
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.
351 applyModifiers ();
353 // Update the preview of the shape.
354 updateShape ();
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.
360 else
362 // Update the preview of the shape.
363 updateShape ();
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
373 // private
374 kpColor kpToolPolygonalBase::drawingForegroundColor () const
376 return color (originatingMouseButton ());
379 // protected virtual
380 kpColor kpToolPolygonalBase::drawingBackgroundColor () const
382 return kpColor::Invalid;
385 // TODO: code dup with kpToolRectangle
386 // protected slot
387 void kpToolPolygonalBase::updateShape ()
389 if (d->points.count () == 0)
390 return;
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="
398 << boundingRect
399 << " lineWidth="
400 << d->toolWidgetLineWidth->lineWidth ()
401 << endl;
402 #endif
404 kpImage image = document ()->getImageAt (boundingRect);
406 QPolygon pointsTranslated = d->points;
407 pointsTranslated.translate (-boundingRect.x (), -boundingRect.y ());
409 (*d->drawShapeFunc) (&image,
410 pointsTranslated,
411 drawingForegroundColor (), d->toolWidgetLineWidth->lineWidth (),
412 /*virtual*/drawingBackgroundColor (),
413 false/*not final*/);
415 kpTempImage newTempImage (false/*always display*/,
416 kpTempImage::SetImage/*render mode*/,
417 boundingRect.topLeft (),
418 image);
420 viewManager ()->setFastUpdates ();
422 viewManager ()->setTempImage (newTempImage);
424 viewManager ()->restoreFastUpdates ();
427 // virtual
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;
450 #endif
452 if (!hasBegunShape ())
453 return;
455 viewManager ()->invalidateTempImage ();
457 QRect boundingRect = kpTool::neededRect (
458 d->points.boundingRect (),
459 d->toolWidgetLineWidth->lineWidth ());
461 commandHistory ()->addCommand (
462 new kpToolPolygonalCommand (
463 text (),
464 d->drawShapeFunc,
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 &)
485 updateShape ();
488 // virtual protected slot [base kpTool]
489 void kpToolPolygonalBase::slotBackgroundColorChanged (const kpColor &)
491 updateShape ();
495 #include <kpToolPolygonalBase.moc>