summaryrefslogtreecommitdiffstats
path: root/kolourpaint/tools/kptoolrectangle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kolourpaint/tools/kptoolrectangle.cpp')
-rw-r--r--kolourpaint/tools/kptoolrectangle.cpp638
1 files changed, 638 insertions, 0 deletions
diff --git a/kolourpaint/tools/kptoolrectangle.cpp b/kolourpaint/tools/kptoolrectangle.cpp
new file mode 100644
index 00000000..275db667
--- /dev/null
+++ b/kolourpaint/tools/kptoolrectangle.cpp
@@ -0,0 +1,638 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <[email protected]>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_RECTANGLE 0
+
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolor.h>
+#include <kpcommandhistory.h>
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kptemppixmap.h>
+#include <kptoolrectangle.h>
+#include <kptooltoolbar.h>
+#include <kptoolwidgetfillstyle.h>
+#include <kptoolwidgetlinewidth.h>
+#include <kpview.h>
+#include <kpviewmanager.h>
+
+static QPixmap pixmap (const kpToolRectangle::Mode mode,
+ kpDocument *document, const QRect &rect,
+ const QPoint &startPoint, const QPoint &endPoint,
+ const QPen &pen, const QPen &maskPen,
+ const QBrush &brush, const QBrush &maskBrush)
+{
+ QPixmap pixmap = document->getPixmapAt (rect);
+ QBitmap maskBitmap;
+
+ QPainter painter, maskPainter;
+
+#if DEBUG_KP_TOOL_RECTANGLE && 1
+ kdDebug () << "pixmap: rect=" << rect
+ << " startPoint=" << startPoint
+ << " endPoint=" << endPoint
+ << endl;
+ kdDebug () << "\tm: p=" << (maskPen.style () != Qt::NoPen)
+ << " b=" << (maskBrush.style () != Qt::NoBrush)
+ << " o: p=" << (pen.style () != Qt::NoPen)
+ << " b=" << (brush.style () != Qt::NoBrush)
+ << endl;
+ kdDebug () << "\tmaskPen.color()=" << (int *) maskPen.color ().rgb ()
+ << " transparent=" << (int *) Qt::color0.rgb ()/*transparent*/
+ << endl;
+#endif
+
+ if (pixmap.mask () ||
+ (maskPen.style () != Qt::NoPen &&
+ maskPen.color () == Qt::color0/*transparent*/) ||
+ (maskBrush.style () != Qt::NoBrush &&
+ maskBrush.color () == Qt::color0/*transparent*/))
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (pixmap);
+ maskPainter.begin (&maskBitmap);
+ maskPainter.setPen (maskPen);
+ maskPainter.setBrush (maskBrush);
+ }
+
+ if (pen.style () != Qt::NoPen ||
+ brush.style () != Qt::NoBrush)
+ {
+ painter.begin (&pixmap);
+ painter.setPen (pen);
+ painter.setBrush (brush);
+ }
+
+#define PAINTER_CALL(cmd) \
+{ \
+ if (painter.isActive ()) \
+ painter . cmd ; \
+ \
+ if (maskPainter.isActive ()) \
+ maskPainter . cmd ; \
+}
+
+ if (startPoint != endPoint)
+ {
+ #if DEBUG_KP_TOOL_RECTANGLE && 1
+ kdDebug () << "\tdraw shape" << endl;
+ #endif
+
+ // TODO: Rectangle of pen width 1, height 1 and width X is rendered
+ // as width X - 1.
+ switch (mode)
+ {
+ case kpToolRectangle::Rectangle:
+ PAINTER_CALL (drawRect (QRect (startPoint - rect.topLeft (), endPoint - rect.topLeft ())));
+ break;
+ case kpToolRectangle::RoundedRectangle:
+ PAINTER_CALL (drawRoundRect (QRect (startPoint - rect.topLeft (), endPoint - rect.topLeft ())));
+ break;
+ case kpToolRectangle::Ellipse:
+ PAINTER_CALL (drawEllipse (QRect (startPoint - rect.topLeft (), endPoint - rect.topLeft ())));
+ break;
+ default:
+ kdError () << "kptoolrectangle.cpp::pixmap() passed unknown mode: " << int (mode) << endl;
+ break;
+ }
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_RECTANGLE && 1
+ kdDebug () << "\tstartPoint == endPoint" << endl;
+ #endif
+ // SYNC: Work around Qt bug: can't draw 1x1 rectangle
+ // Not strictly correct for border width > 1
+ // but better than not drawing at all
+ PAINTER_CALL (drawPoint (startPoint - rect.topLeft ()));
+ }
+#undef PAINTER_CALL
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (!maskBitmap.isNull ())
+ pixmap.setMask (maskBitmap);
+
+ return pixmap;
+}
+
+
+/*
+ * kpToolRectangle
+ */
+
+kpToolRectangle::kpToolRectangle (Mode mode,
+ const QString &text,
+ const QString &description,
+ int key,
+ kpMainWindow *mainWindow,
+ const char *name)
+ : kpTool (text, description, key, mainWindow, name),
+ m_mode (mode),
+ m_toolWidgetLineWidth (0),
+ m_toolWidgetFillStyle (0)
+{
+}
+
+kpToolRectangle::kpToolRectangle (kpMainWindow *mainWindow)
+ : kpTool (i18n ("Rectangle"), i18n ("Draws rectangles and squares"),
+ Qt::Key_R,
+ mainWindow, "tool_rectangle"),
+ m_mode (Rectangle),
+ m_toolWidgetLineWidth (0),
+ m_toolWidgetFillStyle (0)
+{
+}
+
+kpToolRectangle::~kpToolRectangle ()
+{
+}
+
+void kpToolRectangle::setMode (Mode mode)
+{
+ m_mode = mode;
+}
+
+
+// private
+void kpToolRectangle::updatePens ()
+{
+ for (int i = 0; i < 2; i++)
+ updatePen (i);
+}
+
+// private
+void kpToolRectangle::updateBrushes ()
+{
+ for (int i = 0; i < 2; i++)
+ updateBrush (i);
+}
+
+// virtual private slot
+void kpToolRectangle::slotForegroundColorChanged (const kpColor &)
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::slotForegroundColorChanged()" << endl;
+#endif
+ updatePen (0);
+ updateBrush (0); // brush may be in foreground color
+ updateBrush (1);
+}
+
+// virtual private slot
+void kpToolRectangle::slotBackgroundColorChanged (const kpColor &)
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::slotBackgroundColorChanged()" << endl;
+ kdDebug () << "\tm_toolWidgetFillStyle=" << m_toolWidgetFillStyle << endl;
+#endif
+ updatePen (1);
+ updateBrush (0);
+ updateBrush (1); // brush may be in background color
+}
+
+// private
+void kpToolRectangle::updatePen (int mouseButton)
+{
+ QColor maskPenColor = color (mouseButton).maskColor ();
+
+ if (!m_toolWidgetLineWidth)
+ {
+ if (color (mouseButton).isOpaque ())
+ m_pen [mouseButton] = QPen (color (mouseButton).toQColor ());
+ else
+ m_pen [mouseButton] = Qt::NoPen;
+ m_maskPen [mouseButton] = QPen (maskPenColor);
+ }
+ else
+ {
+ if (color (mouseButton).isOpaque ())
+ {
+ m_pen [mouseButton] = QPen (color (mouseButton).toQColor (),
+ m_toolWidgetLineWidth->lineWidth (),
+ Qt::SolidLine);
+ }
+ else
+ m_pen [mouseButton] = Qt::NoPen;
+ m_maskPen [mouseButton] = QPen (maskPenColor,
+ m_toolWidgetLineWidth->lineWidth (),
+ Qt::SolidLine);
+ }
+}
+
+void kpToolRectangle::updateBrush (int mouseButton)
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::brush () mouseButton=" << mouseButton
+ << " m_toolWidgetFillStyle=" << m_toolWidgetFillStyle
+ << endl;
+#endif
+ if (m_toolWidgetFillStyle)
+ {
+ m_brush [mouseButton] = m_toolWidgetFillStyle->brush (
+ color (mouseButton)/*foreground colour*/,
+ color (1 - mouseButton)/*background colour*/);
+
+ m_maskBrush [mouseButton] = m_toolWidgetFillStyle->maskBrush (
+ color (mouseButton)/*foreground colour*/,
+ color (1 - mouseButton)/*background colour*/);
+ }
+ else
+ {
+ m_brush [mouseButton] = Qt::NoBrush;
+ m_maskBrush [mouseButton] = Qt::NoBrush;
+ }
+}
+
+
+// private slot virtual
+void kpToolRectangle::slotLineWidthChanged ()
+{
+ updatePens ();
+
+ if (hasBegunDraw ())
+ updateShape ();
+}
+
+// private slot virtual
+void kpToolRectangle::slotFillStyleChanged ()
+{
+ updateBrushes ();
+
+ if (hasBegunDraw ())
+ updateShape ();
+}
+
+
+// private
+QString kpToolRectangle::haventBegunDrawUserMessage () const
+{
+ return i18n ("Drag to draw.");
+}
+
+// virtual
+void kpToolRectangle::begin ()
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::begin ()" << endl;
+#endif
+
+ kpToolToolBar *tb = toolToolBar ();
+
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "\ttoolToolBar=" << tb << endl;
+#endif
+
+ if (tb)
+ {
+ m_toolWidgetLineWidth = tb->toolWidgetLineWidth ();
+ connect (m_toolWidgetLineWidth, SIGNAL (lineWidthChanged (int)),
+ this, SLOT (slotLineWidthChanged ()));
+ m_toolWidgetLineWidth->show ();
+
+ updatePens ();
+
+
+ m_toolWidgetFillStyle = tb->toolWidgetFillStyle ();
+ connect (m_toolWidgetFillStyle, SIGNAL (fillStyleChanged (kpToolWidgetFillStyle::FillStyle)),
+ this, SLOT (slotFillStyleChanged ()));
+ m_toolWidgetFillStyle->show ();
+
+ updateBrushes ();
+ }
+
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "\t\tm_toolWidgetFillStyle=" << m_toolWidgetFillStyle << endl;
+#endif
+
+ viewManager ()->setCursor (QCursor (CrossCursor));
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolRectangle::end ()
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::end ()" << endl;
+#endif
+
+ if (m_toolWidgetLineWidth)
+ {
+ disconnect (m_toolWidgetLineWidth, SIGNAL (lineWidthChanged (int)),
+ this, SLOT (slotLineWidthChanged ()));
+ m_toolWidgetLineWidth = 0;
+ }
+
+ if (m_toolWidgetFillStyle)
+ {
+ disconnect (m_toolWidgetFillStyle, SIGNAL (fillStyleChanged (kpToolWidgetFillStyle::FillStyle)),
+ this, SLOT (slotFillStyleChanged ()));
+ m_toolWidgetFillStyle = 0;
+ }
+
+ viewManager ()->unsetCursor ();
+}
+
+void kpToolRectangle::applyModifiers ()
+{
+ QRect rect = QRect (m_startPoint, m_currentPoint).normalize ();
+
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::applyModifiers(" << rect
+ << ") shift=" << m_shiftPressed
+ << " ctrl=" << m_controlPressed
+ << endl;
+#endif
+
+ // user wants to m_startPoint == centre
+ if (m_controlPressed)
+ {
+ int xdiff = kAbs (m_startPoint.x () - m_currentPoint.x ());
+ int ydiff = kAbs (m_startPoint.y () - m_currentPoint.y ());
+ rect = QRect (m_startPoint.x () - xdiff, m_startPoint.y () - ydiff,
+ xdiff * 2 + 1, ydiff * 2 + 1);
+ }
+
+ // user wants major axis == minor axis:
+ // rectangle --> square
+ // rounded rectangle --> rounded square
+ // ellipse --> circle
+ if (m_shiftPressed)
+ {
+ if (!m_controlPressed)
+ {
+ if (rect.width () < rect.height ())
+ {
+ if (m_startPoint.y () == rect.y ())
+ rect.setHeight (rect.width ());
+ else
+ rect.setY (rect.bottom () - rect.width () + 1);
+ }
+ else
+ {
+ if (m_startPoint.x () == rect.x ())
+ rect.setWidth (rect.height ());
+ else
+ rect.setX (rect.right () - rect.height () + 1);
+ }
+ }
+ // have to maintain the centre
+ else
+ {
+ if (rect.width () < rect.height ())
+ {
+ QPoint center = rect.center ();
+ rect.setHeight (rect.width ());
+ rect.moveCenter (center);
+ }
+ else
+ {
+ QPoint center = rect.center ();
+ rect.setWidth (rect.height ());
+ rect.moveCenter (center);
+ }
+ }
+ }
+
+ m_toolRectangleStartPoint = rect.topLeft ();
+ m_toolRectangleEndPoint = rect.bottomRight ();
+
+ m_toolRectangleRectWithoutLineWidth = rect;
+ m_toolRectangleRect = kpTool::neededRect (rect, QMAX (m_pen [m_mouseButton].width (),
+ m_maskPen [m_mouseButton].width ()));
+}
+
+void kpToolRectangle::beginDraw ()
+{
+ setUserMessage (cancelUserMessage ());
+}
+
+void kpToolRectangle::updateShape ()
+{
+ viewManager ()->setFastUpdates ();
+
+ QPixmap newPixmap = pixmap (m_mode, document (), m_toolRectangleRect,
+ m_toolRectangleStartPoint, m_toolRectangleEndPoint,
+ m_pen [m_mouseButton], m_maskPen [m_mouseButton],
+ m_brush [m_mouseButton], m_maskBrush [m_mouseButton]);
+ kpTempPixmap newTempPixmap (false/*always display*/,
+ kpTempPixmap::SetPixmap/*render mode*/,
+ m_toolRectangleRect.topLeft (),
+ newPixmap);
+ viewManager ()->setTempPixmap (newTempPixmap);
+
+ viewManager ()->restoreFastUpdates ();
+}
+
+void kpToolRectangle::draw (const QPoint &, const QPoint &, const QRect &)
+{
+ applyModifiers ();
+
+
+ updateShape ();
+
+
+ // Recover the start and end points from the transformed & normalized m_toolRectangleRect
+
+ // S. or S or SC or S == C
+ // .C C
+ if (m_currentPoint.x () >= m_startPoint.x () &&
+ m_currentPoint.y () >= m_startPoint.y ())
+ {
+ setUserShapePoints (m_toolRectangleRectWithoutLineWidth.topLeft (),
+ m_toolRectangleRectWithoutLineWidth.bottomRight ());
+ }
+ // .C or C
+ // S. S
+ else if (m_currentPoint.x () >= m_startPoint.x () &&
+ m_currentPoint.y () < m_startPoint.y ())
+ {
+ setUserShapePoints (m_toolRectangleRectWithoutLineWidth.bottomLeft (),
+ m_toolRectangleRectWithoutLineWidth.topRight ());
+ }
+ // .S or CS
+ // C.
+ else if (m_currentPoint.x () < m_startPoint.x () &&
+ m_currentPoint.y () >= m_startPoint.y ())
+ {
+ setUserShapePoints (m_toolRectangleRectWithoutLineWidth.topRight (),
+ m_toolRectangleRectWithoutLineWidth.bottomLeft ());
+ }
+ // C.
+ // .S
+ else
+ {
+ setUserShapePoints (m_toolRectangleRectWithoutLineWidth.bottomRight (),
+ m_toolRectangleRectWithoutLineWidth.topLeft ());
+ }
+}
+
+void kpToolRectangle::cancelShape ()
+{
+#if 0
+ endDraw (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+ mainWindow ()->commandHistory ()->undo ();
+#else
+ viewManager ()->invalidateTempPixmap ();
+#endif
+
+ setUserMessage (i18n ("Let go of all the mouse buttons."));
+}
+
+void kpToolRectangle::releasedAllButtons ()
+{
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+void kpToolRectangle::endDraw (const QPoint &, const QRect &)
+{
+ applyModifiers ();
+
+ // TODO: flicker
+ viewManager ()->invalidateTempPixmap ();
+
+ mainWindow ()->commandHistory ()->addCommand (
+ new kpToolRectangleCommand
+ (m_mode,
+ m_pen [m_mouseButton], m_maskPen [m_mouseButton],
+ m_brush [m_mouseButton], m_maskBrush [m_mouseButton],
+ m_toolRectangleRect, m_toolRectangleStartPoint, m_toolRectangleEndPoint,
+ mainWindow ()));
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+
+/*
+ * kpToolRectangleCommand
+ */
+
+kpToolRectangleCommand::kpToolRectangleCommand (kpToolRectangle::Mode mode,
+ const QPen &pen, const QPen &maskPen,
+ const QBrush &brush, const QBrush &maskBrush,
+ const QRect &rect,
+ const QPoint &startPoint, const QPoint &endPoint,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_mode (mode),
+ m_pen (pen), m_maskPen (maskPen),
+ m_brush (brush), m_maskBrush (maskBrush),
+ m_rect (rect),
+ m_startPoint (startPoint),
+ m_endPoint (endPoint),
+ m_oldPixmapPtr (0)
+{
+}
+
+kpToolRectangleCommand::~kpToolRectangleCommand ()
+{
+ delete m_oldPixmapPtr;
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolRectangleCommand::name () const
+{
+ switch (m_mode)
+ {
+ case kpToolRectangle::Rectangle:
+ return i18n ("Rectangle");
+ case kpToolRectangle::RoundedRectangle:
+ return i18n ("Rounded Rectangle");
+ case kpToolRectangle::Ellipse:
+ return i18n ("Ellipse");
+ default:
+ kdError () << "kpToolRectangleCommand::name() passed unknown mode: " << int (m_mode) << endl;
+ return QString::null;
+ }
+}
+
+
+// public virtual [base kpCommand]
+int kpToolRectangleCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldPixmapPtr);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolRectangleCommand::execute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ // store Undo info
+ if (!m_oldPixmapPtr)
+ {
+ // OPT: I can do better with no brush
+ m_oldPixmapPtr = new QPixmap ();
+ *m_oldPixmapPtr = doc->getPixmapAt (m_rect);
+ }
+ else
+ kdError () << "kpToolRectangleCommand::execute() m_oldPixmapPtr not null" << endl;
+
+ doc->setPixmapAt (pixmap (m_mode, doc,
+ m_rect, m_startPoint, m_endPoint,
+ m_pen, m_maskPen,
+ m_brush, m_maskBrush),
+ m_rect.topLeft ());
+}
+
+// public virtual [base kpCommand]
+void kpToolRectangleCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ if (m_oldPixmapPtr)
+ {
+ doc->setPixmapAt (*m_oldPixmapPtr, m_rect.topLeft ());
+
+ delete m_oldPixmapPtr;
+ m_oldPixmapPtr = 0;
+ }
+ else
+ kdError () << "kpToolRectangleCommand::unexecute() m_oldPixmapPtr null" << endl;
+}
+
+#include <kptoolrectangle.moc>