diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 47d455dd55be855e4cc691c32f687f723d9247ee (patch) | |
tree | 52e236aaa2576bdb3840ebede26619692fed6d7d /kolourpaint/tools | |
download | tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kolourpaint/tools')
59 files changed, 14903 insertions, 0 deletions
diff --git a/kolourpaint/tools/Makefile.am b/kolourpaint/tools/Makefile.am new file mode 100644 index 00000000..9c665cb1 --- /dev/null +++ b/kolourpaint/tools/Makefile.am @@ -0,0 +1,53 @@ +INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../cursors -I$(srcdir)/../interfaces \ + -I$(srcdir)/../pixmapfx \ + -I$(srcdir)/../tools \ + -I$(srcdir)/../views \ + -I$(srcdir)/../widgets $(all_includes) + +noinst_LTLIBRARIES = libkolourpainttools.la +libkolourpainttools_la_SOURCES = kptoolaction.cpp \ + kptoolairspray.cpp \ + kptoolautocrop.cpp \ + kptoolbrush.cpp kptoolclear.cpp \ + kptoolcolorpicker.cpp kptoolcolorwasher.cpp \ + kptoolconverttograyscale.cpp \ + kptoolcrop.cpp \ + kptoolcurve.cpp \ + kptoolellipse.cpp \ + kptoolellipticalselection.cpp kptooleraser.cpp \ + kptoolflip.cpp kptoolfloodfill.cpp \ + kptoolfreeformselection.cpp \ + kptoolline.cpp kptoolpen.cpp \ + kptoolpolygon.cpp kptoolpolyline.cpp \ + kptoolpreviewdialog.cpp \ + kptoolrectangle.cpp kptoolrectselection.cpp \ + kptoolresizescale.cpp kptoolrotate.cpp \ + kptoolroundedrectangle.cpp kptoolselection.cpp \ + kptoolskew.cpp kptooltext.cpp + +# TODO: Why is this needed? Isn't linking at the toplevel enough? +libkolourpainttools_la_LIBADD = ../pixmapfx/libkolourpaintpixmapfx.la ../cursors/libkolourpaintcursors.la + +METASOURCES = kptoolaction.moc \ + kptoolairspray.moc \ + kptoolbrush.moc \ + kptoolcolorpicker.moc \ + kptoolcolorwasher.moc \ + kptoolcurve.moc \ + kptoolellipse.moc \ + kptooleraser.moc \ + kptoolflip.moc \ + kptoolfloodfill.moc \ + kptoolline.moc \ + kptoolpen.moc \ + kptoolpolygon.moc \ + kptoolpolyline.moc \ + kptoolpreviewdialog.moc \ + kptoolrectangle.moc \ + kptoolresizescale.moc \ + kptoolrotate.moc \ + kptoolroundedrectangle.moc \ + kptoolselection.moc \ + kptoolskew.moc \ + kptooltext.moc + diff --git a/kolourpaint/tools/kptoolaction.cpp b/kolourpaint/tools/kptoolaction.cpp new file mode 100644 index 00000000..ef5c8510 --- /dev/null +++ b/kolourpaint/tools/kptoolaction.cpp @@ -0,0 +1,107 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <kptoolaction.h> + +#include <kptool.h> + + +kpToolAction::kpToolAction (const QString &text, + const QString &pic, const KShortcut &shortcut, + const QObject *receiver, const char *slot, + QObject *parent, const char *name) + : KToggleAction (text, + pic, shortcut, + receiver, slot, + parent, name) +{ + updateToolTip (); +} + +kpToolAction::~kpToolAction () +{ +} + + +// protected +void kpToolAction::updateToolTip () +{ + const QString newToolTip = + kpTool::toolTipForTextAndShortcut (text (), shortcut ()); + if (newToolTip == toolTip ()) + return; + + setToolTip (newToolTip); + emit toolTipChanged (newToolTip); +} + + +// +// KToggleAction interface +// + +// public slot virtual [base KAction] +void kpToolAction::setText (const QString &text) +{ + KToggleAction::setText (text); + updateToolTip (); +} + +// public slot virtual [base KAction] +bool kpToolAction::setShortcut (const KShortcut &shortcut) +{ + bool ret = KToggleAction::setShortcut (shortcut); + updateToolTip (); + return ret; +} + + +// +// KToggleAction implements kpSingleKeyTriggersActionInterface +// + +// public virtual [base kpSingleKeyTriggersActionInterface] +const char *kpToolAction::actionName () const +{ + return name (); +} + +// public virtual [base kpSingleKeyTriggersActionInterface] +KShortcut kpToolAction::actionShortcut () const +{ + return shortcut (); +} + +// public virtual [base kpSingleKeyTriggersActionInterface] +void kpToolAction::actionSetShortcut (const KShortcut &shortcut) +{ + setShortcut (shortcut); +} + + +#include <kptoolaction.moc> diff --git a/kolourpaint/tools/kptoolaction.h b/kolourpaint/tools/kptoolaction.h new file mode 100644 index 00000000..df4e407e --- /dev/null +++ b/kolourpaint/tools/kptoolaction.h @@ -0,0 +1,78 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + +#ifndef KP_TOOL_ACTION_H +#define KP_TOOL_ACTION_H + +#include <kactionclasses.h> + +#include <kpsinglekeytriggersaction.h> + + +// Same as KToggleAction but shows the first single key trigger in the tooltip. +class kpToolAction : public KToggleAction, + public kpSingleKeyTriggersActionInterface +{ +Q_OBJECT + +public: + kpToolAction (const QString &text, + const QString &pic, const KShortcut &shortcut, + const QObject *receiver, const char *slot, + QObject *parent, const char *name); + virtual ~kpToolAction (); + + +signals: + // Not emitted when toolTip is manually overriden by setToolTip() + void toolTipChanged (const QString &string); + +protected: + void updateToolTip (); + + + // + // KToggleAction interface + // + +public slots: + virtual void setText (const QString &text); + virtual bool setShortcut (const KShortcut &shortcut); + + + // + // kpSingleKeyTriggersActionInterface + // + +public: + virtual const char *actionName () const; + virtual KShortcut actionShortcut () const; + virtual void actionSetShortcut (const KShortcut &shortcut); +}; + + +#endif // KP_TOOL_ACTION_H diff --git a/kolourpaint/tools/kptoolairspray.cpp b/kolourpaint/tools/kptoolairspray.cpp new file mode 100644 index 00000000..43f8bef3 --- /dev/null +++ b/kolourpaint/tools/kptoolairspray.cpp @@ -0,0 +1,376 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_SPRAYCAN 0 + +#include <stdlib.h> + +#include <qbitmap.h> +#include <qpainter.h> +#include <qpen.h> +#include <qpixmap.h> +#include <qpoint.h> +#include <qpointarray.h> +#include <qrect.h> +#include <qtimer.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpcommandhistory.h> +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kptoolairspray.h> +#include <kptooltoolbar.h> +#include <kptoolwidgetspraycansize.h> +#include <kpview.h> +#include <kpviewmanager.h> + + +/* + * kpToolAirSpray + */ + +kpToolAirSpray::kpToolAirSpray (kpMainWindow *mainWindow) + : kpTool (i18n ("Spraycan"), i18n ("Sprays graffiti"), + Qt::Key_Y, + mainWindow, "tool_spraycan"), + m_currentCommand (0) +{ + m_timer = new QTimer (this); + connect (m_timer, SIGNAL (timeout ()), this, SLOT (actuallyDraw ())); +} + +kpToolAirSpray::~kpToolAirSpray () +{ + delete m_currentCommand; +} + + +// private +QString kpToolAirSpray::haventBegunDrawUserMessage () const +{ + return i18n ("Click or drag to spray graffiti."); +} + +// public virtual +void kpToolAirSpray::begin () +{ + kpToolToolBar *tb = toolToolBar (); + + m_toolWidgetSpraycanSize = 0; + m_size = 10; + + if (tb) + { + m_toolWidgetSpraycanSize = tb->toolWidgetSpraycanSize (); + + if (m_toolWidgetSpraycanSize) + { + m_size = m_toolWidgetSpraycanSize->spraycanSize (); + connect (m_toolWidgetSpraycanSize, SIGNAL (spraycanSizeChanged (int)), + this, SLOT (slotSpraycanSizeChanged (int))); + + m_toolWidgetSpraycanSize->show (); + } + } + + setUserMessage (haventBegunDrawUserMessage ()); +} + +// public virtual +void kpToolAirSpray::end () +{ + if (m_toolWidgetSpraycanSize) + { + disconnect (m_toolWidgetSpraycanSize, SIGNAL (spraycanSizeChanged (int)), + this, SLOT (slotSpraycanSizeChanged (int))); + m_toolWidgetSpraycanSize = 0; + } + + setUserMessage (haventBegunDrawUserMessage ()); +} + +// private slot +void kpToolAirSpray::slotSpraycanSizeChanged (int size) +{ + m_size = size; +} + + +void kpToolAirSpray::beginDraw () +{ + m_currentCommand = new kpToolAirSprayCommand ( + color (m_mouseButton), + m_size, + mainWindow ()); + + // without delay + actuallyDraw (); + + // use a timer instead of reimplementing draw() (we don't draw all the time) + m_timer->start (25); + + setUserMessage (cancelUserMessage ()); +} + +void kpToolAirSpray::draw (const QPoint &thisPoint, const QPoint &, const QRect &) +{ + // if the user is moving the spray, make the spray line continuous + if (thisPoint != m_lastPoint) + { + // without delay + actuallyDraw (); + } + + setUserShapePoints (thisPoint); +} + +void kpToolAirSpray::actuallyDraw () +{ + QPointArray pArray (10); + int numPoints = 0; + + QPoint p = m_currentPoint; + +#if DEBUG_KP_TOOL_SPRAYCAN + kdDebug () << "kpToolAirSpray::actuallyDraw() currentPoint=" << p + << " size=" << m_size + << endl; +#endif + + int radius = m_size / 2; + + for (int i = 0; i < 10; i++) + { + int dx, dy; + + dx = (rand () % m_size) - radius; + dy = (rand () % m_size) - radius; + + // make it look circular + // OPT: can be done better + if (dx * dx + dy * dy <= radius * radius) + pArray [numPoints++] = QPoint (p.x () + dx, p.y () + dy); + } + + pArray.resize (numPoints); + + if (numPoints > 0) + { + // leave the command to draw + m_currentCommand->addPoints (pArray); + } +} + +// virtual +void kpToolAirSpray::cancelShape () +{ +#if 0 + endDraw (QPoint (), QRect ()); + mainWindow ()->commandHistory ()->undo (); +#else + m_timer->stop (); + + m_currentCommand->finalize (); + m_currentCommand->cancel (); + + delete m_currentCommand; + m_currentCommand = 0; +#endif + + setUserMessage (i18n ("Let go of all the mouse buttons.")); +} + +void kpToolAirSpray::releasedAllButtons () +{ + setUserMessage (haventBegunDrawUserMessage ()); +} + +// virtual +void kpToolAirSpray::endDraw (const QPoint &, const QRect &) +{ + m_timer->stop (); + + m_currentCommand->finalize (); + mainWindow ()->commandHistory ()->addCommand (m_currentCommand, false /* don't exec */); + + // don't delete - it's up to the commandHistory + m_currentCommand = 0; + + setUserMessage (haventBegunDrawUserMessage ()); +} + + +/* + * kpToolAirSprayCommand + */ + +kpToolAirSprayCommand::kpToolAirSprayCommand (const kpColor &color, int size, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_color (color), + m_size (size), + m_newPixmapPtr (0) +{ + m_oldPixmap = *document ()->pixmap (); +} + +kpToolAirSprayCommand::~kpToolAirSprayCommand () +{ + delete m_newPixmapPtr; +} + + +// public virtual [base kpCommand] +QString kpToolAirSprayCommand::name () const +{ + return i18n ("Spraycan"); +} + + +// public virtual [base kpCommand] +int kpToolAirSprayCommand::size () const +{ + return kpPixmapFX::pixmapSize (m_newPixmapPtr) + + kpPixmapFX::pixmapSize (m_oldPixmap); +} + + +// Redo: +// +// must not call before unexecute() as m_newPixmapPtr is null +// (one reason why we told addCommand() not to execute, +// the other being that the dots have already been draw onto the doc) +void kpToolAirSprayCommand::execute () +{ + if (m_newPixmapPtr) + { + document ()->setPixmapAt (*m_newPixmapPtr, m_boundingRect.topLeft ()); + + // (will be regenerated in unexecute() if required) + delete m_newPixmapPtr; + m_newPixmapPtr = 0; + } + else + kdError () << "kpToolAirSprayCommand::execute() has null m_newPixmapPtr" << endl; +} + +// Undo: +void kpToolAirSprayCommand::unexecute () +{ + if (!m_newPixmapPtr) + { + // the ultimate in laziness - figure out Redo info only if we Undo + m_newPixmapPtr = new QPixmap (m_boundingRect.width (), m_boundingRect.height ()); + *m_newPixmapPtr = document ()->getPixmapAt (m_boundingRect); + } + else + kdError () << "kpToolAirSprayCommand::unexecute() has non-null newPixmapPtr" << endl; + + document ()->setPixmapAt (m_oldPixmap, m_boundingRect.topLeft ()); +} + + +// public +void kpToolAirSprayCommand::addPoints (const QPointArray &points) +{ + QRect docRect = points.boundingRect (); + +#if DEBUG_KP_TOOL_SPRAYCAN + kdDebug () << "kpToolAirSprayCommand::addPoints() docRect=" << docRect + << " numPoints=" << points.count () << endl; + for (int i = 0; i < (int) points.count (); i++) + kdDebug () << "\t" << i << ": " << points [i] << endl; +#endif + + QPixmap pixmap = document ()->getPixmapAt (docRect); + QBitmap mask; + + QPainter painter, maskPainter; + + if (m_color.isOpaque ()) + { + painter.begin (&pixmap); + painter.setPen (m_color.toQColor ()); + } + + if (pixmap.mask () || m_color.isTransparent ()) + { + mask = kpPixmapFX::getNonNullMask (pixmap); + maskPainter.begin (&mask); + maskPainter.setPen (m_color.maskColor ()); + } + + for (int i = 0; i < (int) points.count (); i++) + { + QPoint pt (points [i].x () - docRect.x (), + points [i].y () - docRect.y ()); + + if (painter.isActive ()) + painter.drawPoint (pt); + + if (maskPainter.isActive ()) + maskPainter.drawPoint (pt); + } + + if (maskPainter.isActive ()) + maskPainter.end (); + + if (painter.isActive ()) + painter.end (); + + if (!mask.isNull ()) + pixmap.setMask (mask); + + viewManager ()->setFastUpdates (); + document ()->setPixmapAt (pixmap, docRect.topLeft ()); + viewManager ()->restoreFastUpdates (); + + m_boundingRect = m_boundingRect.unite (docRect); +} + +void kpToolAirSprayCommand::finalize () +{ + // store only needed part of doc pixmap + m_oldPixmap = kpTool::neededPixmap (m_oldPixmap, m_boundingRect); +} + +void kpToolAirSprayCommand::cancel () +{ + if (m_boundingRect.isValid ()) + { + viewManager ()->setFastUpdates (); + document ()->setPixmapAt (m_oldPixmap, m_boundingRect.topLeft ()); + viewManager ()->restoreFastUpdates (); + } +} + +#include <kptoolairspray.moc> diff --git a/kolourpaint/tools/kptoolairspray.h b/kolourpaint/tools/kptoolairspray.h new file mode 100644 index 00000000..24f02787 --- /dev/null +++ b/kolourpaint/tools/kptoolairspray.h @@ -0,0 +1,110 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolairspray_h__ +#define __kptoolairspray_h__ + +#include <kpcommandhistory.h> +#include <kpcolor.h> +#include <kptool.h> + +class QPixmap; +class QPoint; +class QRect; +class QString; +class QTimer; + +class kpMainWindow; +class kpToolAirSprayCommand; +class kpToolWidgetSpraycanSize; +class kpViewManager; + +class kpToolAirSpray : public kpTool +{ +Q_OBJECT + +public: + kpToolAirSpray (kpMainWindow *); + virtual ~kpToolAirSpray (); + +private: + QString haventBegunDrawUserMessage () const; + +public: + virtual void begin (); + virtual void end (); + +private slots: + void slotSpraycanSizeChanged (int size); + +public: + virtual void beginDraw (); + virtual void draw (const QPoint &thisPoint, const QPoint &, const QRect &); + virtual void cancelShape (); + virtual void releasedAllButtons (); + virtual void endDraw (const QPoint &, const QRect &); + +public slots: + void actuallyDraw (); + +private: + kpToolWidgetSpraycanSize *m_toolWidgetSpraycanSize; + kpToolAirSprayCommand *m_currentCommand; + QTimer *m_timer; + int m_size; +}; + +class kpToolAirSprayCommand : public kpCommand +{ +public: + kpToolAirSprayCommand (const kpColor &color, int size, + kpMainWindow *mainWindow); + virtual ~kpToolAirSprayCommand (); + + virtual QString name () const; + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + + // interface for KToolAirSpray + void addPoints (const QPointArray &points); + void finalize (); + void cancel (); + +private: + kpColor m_color; + int m_size; + + QPixmap *m_newPixmapPtr; + QPixmap m_oldPixmap; + QRect m_boundingRect; +}; + +#endif // __kptoolairspray_h__ diff --git a/kolourpaint/tools/kptoolautocrop.cpp b/kolourpaint/tools/kptoolautocrop.cpp new file mode 100644 index 00000000..244c192d --- /dev/null +++ b/kolourpaint/tools/kptoolautocrop.cpp @@ -0,0 +1,780 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + +// TODO: Color Similarity is obviously useful in Autocrop but it isn't +// obvious as to how to implement it. The current heuristic, +// for each side, chooses an arbitrary reference color for which +// all other candidate pixels in that side are tested against +// for similarity. But if the reference color happens to be at +// one extreme of the range of colors in that side, then pixels +// at the other extreme would not be deemed similar enough. The +// key is to find the median color as the reference but how do +// you do this if you don't know which pixels to sample in the first +// place (that's what you're trying to find)? Chicken and egg situation. +// +// The other heuristic that is in doubt is the use of the average +// color in determining the similarity of sides (it is possible +// to get vastly differently colors in both sides yet they will be +// considered similar). + +#define DEBUG_KP_TOOL_AUTO_CROP 0 + + +#include <kptoolautocrop.h> + +#include <qapplication.h> +#include <qbitmap.h> +#include <qimage.h> +#include <qpainter.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +#include <kpcolortoolbar.h> +#include <kpcommandhistory.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kpselection.h> +#include <kptool.h> +#include <kpviewmanager.h> + + +kpToolAutoCropBorder::kpToolAutoCropBorder (const QPixmap *pixmapPtr, + int processedColorSimilarity) + : m_pixmapPtr (pixmapPtr), + m_processedColorSimilarity (processedColorSimilarity) +{ + invalidate (); +} + + +// public +int kpToolAutoCropBorder::size () const +{ + return sizeof (kpToolAutoCropBorder); +} + + +// public +const QPixmap *kpToolAutoCropBorder::pixmap () const +{ + return m_pixmapPtr; +} + +// public +int kpToolAutoCropBorder::processedColorSimilarity () const +{ + return m_processedColorSimilarity; +} + +// public +QRect kpToolAutoCropBorder::rect () const +{ + return m_rect; +} + +// public +int kpToolAutoCropBorder::left () const +{ + return m_rect.left (); +} + +// public +int kpToolAutoCropBorder::right () const +{ + return m_rect.right (); +} + +// public +int kpToolAutoCropBorder::top () const +{ + return m_rect.top (); +} + +// public +int kpToolAutoCropBorder::bottom () const +{ + return m_rect.bottom (); +} + +// public +kpColor kpToolAutoCropBorder::referenceColor () const +{ + return m_referenceColor; +} + +// public +kpColor kpToolAutoCropBorder::averageColor () const +{ + if (!m_rect.isValid ()) + return kpColor::invalid; + + if (m_referenceColor.isTransparent ()) + return kpColor::transparent; + else if (m_processedColorSimilarity == 0) + return m_referenceColor; + else + { + int numPixels = (m_rect.width () * m_rect.height ()); + if (numPixels <= 0) + { + kdError () << "kpToolAutoCropBorder::averageColor() rect=" << m_rect << endl; + return kpColor::invalid; + } + + return kpColor (m_redSum / numPixels, + m_greenSum / numPixels, + m_blueSum / numPixels); + } +} + +bool kpToolAutoCropBorder::isSingleColor () const +{ + return m_isSingleColor; +} + + +// public +bool kpToolAutoCropBorder::calculate (int isX, int dir) +{ +#if DEBUG_KP_TOOL_AUTO_CROP && 1 + kdDebug () << "kpToolAutoCropBorder::calculate() CALLED!" << endl; +#endif + int maxX = m_pixmapPtr->width () - 1; + int maxY = m_pixmapPtr->height () - 1; + + QImage image = kpPixmapFX::convertToImage (*m_pixmapPtr); + if (image.isNull ()) + { + kdError () << "Border::calculate() could not convert to QImage" << endl; + return false; + } + + // (sync both branches) + if (isX) + { + int numCols = 0; + int startX = (dir > 0) ? 0 : maxX; + + kpColor col = kpPixmapFX::getColorAtPixel (image, startX, 0); + for (int x = startX; + x >= 0 && x <= maxX; + x += dir) + { + int y; + for (y = 0; y <= maxY; y++) + { + if (!kpPixmapFX::getColorAtPixel (image, x, y).isSimilarTo (col, m_processedColorSimilarity)) + break; + } + + if (y <= maxY) + break; + else + numCols++; + } + + if (numCols) + { + m_rect = QRect (QPoint (startX, 0), + QPoint (startX + (numCols - 1) * dir, maxY)).normalize (); + m_referenceColor = col; + } + } + else + { + int numRows = 0; + int startY = (dir > 0) ? 0 : maxY; + + kpColor col = kpPixmapFX::getColorAtPixel (image, 0, startY); + for (int y = startY; + y >= 0 && y <= maxY; + y += dir) + { + int x; + for (x = 0; x <= maxX; x++) + { + if (!kpPixmapFX::getColorAtPixel (image, x, y).isSimilarTo (col, m_processedColorSimilarity)) + break; + } + + if (x <= maxX) + break; + else + numRows++; + } + + if (numRows) + { + m_rect = QRect (QPoint (0, startY), + QPoint (maxX, startY + (numRows - 1) * dir)).normalize (); + m_referenceColor = col; + } + } + + + if (m_rect.isValid ()) + { + m_isSingleColor = true; + + if (m_referenceColor.isOpaque () && m_processedColorSimilarity != 0) + { + for (int y = m_rect.top (); y <= m_rect.bottom (); y++) + { + for (int x = m_rect.left (); x <= m_rect.right (); x++) + { + kpColor colAtPixel = kpPixmapFX::getColorAtPixel (image, x, y); + + if (m_isSingleColor && colAtPixel != m_referenceColor) + m_isSingleColor = false; + + m_redSum += colAtPixel.red (); + m_greenSum += colAtPixel.green (); + m_blueSum += colAtPixel.blue (); + } + } + } + } + + + return true; +} + +// public +bool kpToolAutoCropBorder::fillsEntirePixmap () const +{ + return (m_rect == m_pixmapPtr->rect ()); +} + +// public +bool kpToolAutoCropBorder::exists () const +{ + // (will use in an addition so make sure returns 1 or 0) + return (m_rect.isValid () ? 1 : 0); +} + +// public +void kpToolAutoCropBorder::invalidate () +{ + m_rect = QRect (); + m_referenceColor = kpColor::invalid; + m_redSum = m_greenSum = m_blueSum = 0; + m_isSingleColor = false; +} + + +class kpSetOverrideCursorSaver +{ +public: + kpSetOverrideCursorSaver (const QCursor &cursor) + { + QApplication::setOverrideCursor (cursor); + } + + ~kpSetOverrideCursorSaver () + { + QApplication::restoreOverrideCursor (); + } +}; + + +void showNothingToAutocropMessage (kpMainWindow *mainWindow, bool actOnSelection) +{ + kpSetOverrideCursorSaver cursorSaver (Qt::arrowCursor); + + if (actOnSelection) + { + KMessageBox::information (mainWindow, + i18n ("KolourPaint cannot remove the selection's internal border as it" + " could not be located."), + i18n ("Cannot Remove Internal Border"), + "NothingToAutoCrop"); + } + else + { + KMessageBox::information (mainWindow, + i18n ("KolourPaint cannot automatically crop the image as its" + " border could not be located."), + i18n ("Cannot Autocrop"), + "NothingToAutoCrop"); + } +} + +bool kpToolAutoCrop (kpMainWindow *mainWindow) +{ +#if DEBUG_KP_TOOL_AUTO_CROP + kdDebug () << "kpToolAutoCrop() CALLED!" << endl; +#endif + + if (!mainWindow) + { + kdError () << "kpToolAutoCrop() passed NULL mainWindow" << endl; + return false; + } + + kpDocument *doc = mainWindow->document (); + if (!doc) + { + kdError () << "kpToolAutoCrop() passed NULL document" << endl; + return false; + } + + // OPT: if already pulled selection pixmap, no need to do it again here + QPixmap pixmap = doc->selection () ? doc->getSelectedPixmap () : *doc->pixmap (); + if (pixmap.isNull ()) + { + kdError () << "kptoolAutoCrop() pased NULL pixmap" << endl; + return false; + } + + kpViewManager *vm = mainWindow->viewManager (); + if (!vm) + { + kdError () << "kpToolAutoCrop() passed NULL vm" << endl; + return false; + } + + int processedColorSimilarity = mainWindow->colorToolBar ()->processedColorSimilarity (); + kpToolAutoCropBorder leftBorder (&pixmap, processedColorSimilarity), + rightBorder (&pixmap, processedColorSimilarity), + topBorder (&pixmap, processedColorSimilarity), + botBorder (&pixmap, processedColorSimilarity); + + + kpSetOverrideCursorSaver cursorSaver (Qt::waitCursor); + + // TODO: With Colour Similarity, a lot of weird (and wonderful) things can + // happen resulting in a huge number of code paths. Needs refactoring + // and regression testing. + // + // TODO: e.g. When the top fills entire rect but bot doesn't we could + // invalidate top and continue autocrop. + int numRegions = 0; + if (!leftBorder.calculate (true/*x*/, +1/*going right*/) || + leftBorder.fillsEntirePixmap () || + !rightBorder.calculate (true/*x*/, -1/*going left*/) || + rightBorder.fillsEntirePixmap () || + !topBorder.calculate (false/*y*/, +1/*going down*/) || + topBorder.fillsEntirePixmap () || + !botBorder.calculate (false/*y*/, -1/*going up*/) || + botBorder.fillsEntirePixmap () || + ((numRegions = leftBorder.exists () + + rightBorder.exists () + + topBorder.exists () + + botBorder.exists ()) == 0)) + { + #if DEBUG_KP_TOOL_AUTO_CROP + kdDebug () << "\tcan't find border; leftBorder.rect=" << leftBorder.rect () + << " rightBorder.rect=" << rightBorder.rect () + << " topBorder.rect=" << topBorder.rect () + << " botBorder.rect=" << botBorder.rect () + << endl; + #endif + ::showNothingToAutocropMessage (mainWindow, (bool) doc->selection ()); + return false; + } + +#if DEBUG_KP_TOOL_AUTO_CROP + kdDebug () << "\tnumRegions=" << numRegions << endl; + kdDebug () << "\t\tleft=" << leftBorder.rect () + << " refCol=" << (leftBorder.exists () ? (int *) leftBorder.referenceColor ().toQRgb () : 0) + << " avgCol=" << (leftBorder.exists () ? (int *) leftBorder.averageColor ().toQRgb () : 0) + << endl; + kdDebug () << "\t\tright=" << rightBorder.rect () + << " refCol=" << (rightBorder.exists () ? (int *) rightBorder.referenceColor ().toQRgb () : 0) + << " avgCol=" << (rightBorder.exists () ? (int *) rightBorder.averageColor ().toQRgb () : 0) + << endl; + kdDebug () << "\t\ttop=" << topBorder.rect () + << " refCol=" << (topBorder.exists () ? (int *) topBorder.referenceColor ().toQRgb () : 0) + << " avgCol=" << (topBorder.exists () ? (int *) topBorder.averageColor ().toQRgb () : 0) + << endl; + kdDebug () << "\t\tbot=" << botBorder.rect () + << " refCol=" << (botBorder.exists () ? (int *) botBorder.referenceColor ().toQRgb () : 0) + << " avgCol=" << (botBorder.exists () ? (int *) botBorder.averageColor ().toQRgb () : 0) + << endl; +#endif + + + // In case e.g. the user pastes a solid, coloured-in rectangle, + // we favour killing the bottom and right regions + // (these regions probably contain the unwanted whitespace due + // to the doc being bigger than the pasted selection to start with). + // + // We also kill if they kiss or even overlap. + + if (leftBorder.exists () && rightBorder.exists ()) + { + const kpColor leftCol = leftBorder.averageColor (); + const kpColor rightCol = rightBorder.averageColor (); + + if ((numRegions == 2 && !leftCol.isSimilarTo (rightCol, processedColorSimilarity)) || + leftBorder.right () >= rightBorder.left () - 1) // kissing or overlapping + { + #if DEBUG_KP_TOOL_AUTO_CROP + kdDebug () << "\tignoring left border" << endl; + #endif + leftBorder.invalidate (); + } + } + + if (topBorder.exists () && botBorder.exists ()) + { + const kpColor topCol = topBorder.averageColor (); + const kpColor botCol = botBorder.averageColor (); + + if ((numRegions == 2 && !topCol.isSimilarTo (botCol, processedColorSimilarity)) || + topBorder.bottom () >= botBorder.top () - 1) // kissing or overlapping + { + #if DEBUG_KP_TOOL_AUTO_CROP + kdDebug () << "\tignoring top border" << endl; + #endif + topBorder.invalidate (); + } + } + + + mainWindow->addImageOrSelectionCommand ( + new kpToolAutoCropCommand ( + (bool) doc->selection (), + leftBorder, rightBorder, + topBorder, botBorder, + mainWindow)); + + + return true; +} + + +kpToolAutoCropCommand::kpToolAutoCropCommand (bool actOnSelection, + const kpToolAutoCropBorder &leftBorder, + const kpToolAutoCropBorder &rightBorder, + const kpToolAutoCropBorder &topBorder, + const kpToolAutoCropBorder &botBorder, + kpMainWindow *mainWindow) + : kpNamedCommand (name (actOnSelection, DontShowAccel), mainWindow), + m_actOnSelection (actOnSelection), + m_leftBorder (leftBorder), + m_rightBorder (rightBorder), + m_topBorder (topBorder), + m_botBorder (botBorder), + m_leftPixmap (0), + m_rightPixmap (0), + m_topPixmap (0), + m_botPixmap (0) +{ + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolAutoCropCommand::<ctor>() without doc" << endl; + m_oldWidth = 0; + m_oldHeight = 0; + return; + } + + m_oldWidth = doc->width (m_actOnSelection); + m_oldHeight = doc->height (m_actOnSelection); +} + +kpToolAutoCropCommand::~kpToolAutoCropCommand () +{ + deleteUndoPixmaps (); +} + + +// public static +QString kpToolAutoCropCommand::name (bool actOnSelection, int options) +{ + if (actOnSelection) + { + if (options & ShowAccel) + return i18n ("Remove Internal B&order"); + else + return i18n ("Remove Internal Border"); + } + else + { + if (options & ShowAccel) + return i18n ("Autocr&op"); + else + return i18n ("Autocrop"); + } +} + + +// public virtual [base kpCommand] +int kpToolAutoCropCommand::size () const +{ + return m_leftBorder.size () + + m_rightBorder.size () + + m_topBorder.size () + + m_botBorder.size () + + kpPixmapFX::pixmapSize (m_leftPixmap) + + kpPixmapFX::pixmapSize (m_rightPixmap) + + kpPixmapFX::pixmapSize (m_topPixmap) + + kpPixmapFX::pixmapSize (m_botPixmap) + + m_oldSelection.size (); +} + + +// private +void kpToolAutoCropCommand::getUndoPixmap (const kpToolAutoCropBorder &border, QPixmap **pixmap) +{ + kpDocument *doc = document (); + +#if DEBUG_KP_TOOL_AUTO_CROP && 1 + kdDebug () << "kpToolAutoCropCommand::getUndoPixmap()" << endl; + kdDebug () << "\tpixmap=" << pixmap + << " border: rect=" << border.rect () + << " isSingleColor=" << border.isSingleColor () + << endl; +#endif + + if (!doc) + return; + + if (pixmap && border.exists () && !border.isSingleColor ()) + { + if (*pixmap) + { + #if DEBUG_KP_TOOL_AUTO_CROP && 1 + kdDebug () << "\talready have *pixmap - delete it" << endl; + #endif + delete *pixmap; + } + + *pixmap = new QPixmap ( + kpPixmapFX::getPixmapAt (*doc->pixmap (m_actOnSelection), + border.rect ())); + } +} + + +// private +void kpToolAutoCropCommand::getUndoPixmaps () +{ + getUndoPixmap (m_leftBorder, &m_leftPixmap); + getUndoPixmap (m_rightBorder, &m_rightPixmap); + getUndoPixmap (m_topBorder, &m_topPixmap); + getUndoPixmap (m_botBorder, &m_botPixmap); +} + +// private +void kpToolAutoCropCommand::deleteUndoPixmaps () +{ +#if DEBUG_KP_TOOL_AUTO_CROP && 1 + kdDebug () << "kpToolAutoCropCommand::deleteUndoPixmaps()" << endl; +#endif + + delete m_leftPixmap; m_leftPixmap = 0; + delete m_rightPixmap; m_rightPixmap = 0; + delete m_topPixmap; m_topPixmap = 0; + delete m_botPixmap; m_botPixmap = 0; +} + + +// public virtual [base kpCommand] +void kpToolAutoCropCommand::execute () +{ + if (!m_contentsRect.isValid ()) + m_contentsRect = contentsRect (); + + + getUndoPixmaps (); + + + kpDocument *doc = document (); + if (!doc) + return; + + + QPixmap pixmapWithoutBorder = + kpTool::neededPixmap (*doc->pixmap (m_actOnSelection), + m_contentsRect); + + + if (!m_actOnSelection) + doc->setPixmap (pixmapWithoutBorder); + else + { + m_oldSelection = *doc->selection (); + m_oldSelection.setPixmap (QPixmap ()); + + // m_contentsRect is relative to the top of the sel + // while sel is relative to the top of the doc + QRect rect = m_contentsRect; + rect.moveBy (m_oldSelection.x (), m_oldSelection.y ()); + + kpSelection sel (kpSelection::Rectangle, + rect, + pixmapWithoutBorder, + m_oldSelection.transparency ()); + + doc->setSelection (sel); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } +} + +// public virtual [base kpCommand] +void kpToolAutoCropCommand::unexecute () +{ +#if DEBUG_KP_TOOL_AUTO_CROP && 1 + kdDebug () << "kpToolAutoCropCommand::unexecute()" << endl; +#endif + + kpDocument *doc = document (); + if (!doc) + return; + + QPixmap pixmap (m_oldWidth, m_oldHeight); + QBitmap maskBitmap; + + // restore the position of the centre image + kpPixmapFX::setPixmapAt (&pixmap, m_contentsRect, + *doc->pixmap (m_actOnSelection)); + + // draw the borders + + QPainter painter (&pixmap); + QPainter maskPainter; + + const kpToolAutoCropBorder *borders [] = + { + &m_leftBorder, &m_rightBorder, + &m_topBorder, &m_botBorder, + 0 + }; + + const QPixmap *pixmaps [] = + { + m_leftPixmap, m_rightPixmap, + m_topPixmap, m_botPixmap, + 0 + }; + + const QPixmap **p = pixmaps; + for (const kpToolAutoCropBorder **b = borders; *b; b++, p++) + { + if (!(*b)->exists ()) + continue; + + if ((*b)->isSingleColor ()) + { + kpColor col = (*b)->referenceColor (); + #if DEBUG_KP_TOOL_AUTO_CROP && 1 + kdDebug () << "\tdrawing border " << (*b)->rect () + << " rgb=" << (int *) col.toQRgb () /* %X hack */ << endl; + #endif + + if (col.isOpaque ()) + { + painter.fillRect ((*b)->rect (), col.toQColor ()); + } + else + { + if (maskBitmap.isNull ()) + { + // TODO: dangerous when a painter is active on pixmap? + maskBitmap = kpPixmapFX::getNonNullMask (pixmap); + maskPainter.begin (&maskBitmap); + } + + maskPainter.fillRect ((*b)->rect (), Qt::color0/*transparent*/); + } + } + else + { + #if DEBUG_KP_TOOL_AUTO_CROP && 1 + kdDebug () << "\trestoring border pixmap " << (*b)->rect () << endl; + #endif + // **p cannot contain a single transparent pixel because + // if it did, all other pixels must be transparent (only + // transparent pixels are similar to transparent pixels) + // and the other branch would execute. + if (*p) + { + // TODO: We should really edit the mask here. Due to good + // luck (if "maskBitmap" is initialized above, this region + // will be marked as opaque in the mask; if it's not + // initialized, we will be opaque by default), we + // don't actually have to edit the mask but this is + // highly error-prone. + painter.drawPixmap ((*b)->rect (), **p); + } + } + } + + if (maskPainter.isActive ()) + maskPainter.end (); + + painter.end (); + + if (!maskBitmap.isNull ()) + pixmap.setMask (maskBitmap); + + + if (!m_actOnSelection) + doc->setPixmap (pixmap); + else + { + kpSelection sel = m_oldSelection; + sel.setPixmap (pixmap); + + doc->setSelection (sel); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + + + deleteUndoPixmaps (); +} + + +// private +QRect kpToolAutoCropCommand::contentsRect () const +{ + const QPixmap *pixmap = document ()->pixmap (m_actOnSelection); + + QPoint topLeft (m_leftBorder.exists () ? + m_leftBorder.rect ().right () + 1 : + 0, + m_topBorder.exists () ? + m_topBorder.rect ().bottom () + 1 : + 0); + QPoint botRight (m_rightBorder.exists () ? + m_rightBorder.rect ().left () - 1 : + pixmap->width () - 1, + m_botBorder.exists () ? + m_botBorder.rect ().top () - 1 : + pixmap->height () - 1); + + return QRect (topLeft, botRight); +} diff --git a/kolourpaint/tools/kptoolautocrop.h b/kolourpaint/tools/kptoolautocrop.h new file mode 100644 index 00000000..4d016a1d --- /dev/null +++ b/kolourpaint/tools/kptoolautocrop.h @@ -0,0 +1,127 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolautocrop_h__ +#define __kptoolautocrop_h__ + +#include <qrect.h> + +#include <kpcommandhistory.h> + +#include <kpcolor.h> +#include <kpselection.h> + +class QPixmap; +class kpDocument; +class kpMainWindow; +class kpViewManager; + + +// (returns true on success (even if it did nothing) or false on error) +bool kpToolAutoCrop (kpMainWindow *mainWindow); + + +class kpToolAutoCropBorder +{ +public: + kpToolAutoCropBorder (const QPixmap *pixmapPtr, int processedColorSimilarity); + + int size () const; + + const QPixmap *pixmap () const; + int processedColorSimilarity () const; + QRect rect () const; + int left () const; + int right () const; + int top () const; + int bottom () const; + kpColor referenceColor () const; + kpColor averageColor () const; + bool isSingleColor () const; + + // (returns true on success (even if no rect) or false on error) + bool calculate (int isX, int dir); + + bool fillsEntirePixmap () const; + bool exists () const; + void invalidate (); + +private: + const QPixmap *m_pixmapPtr; + int m_processedColorSimilarity; + + QRect m_rect; + kpColor m_referenceColor; + int m_redSum, m_greenSum, m_blueSum; + bool m_isSingleColor; +}; + + +class kpToolAutoCropCommand : public kpNamedCommand +{ +public: + kpToolAutoCropCommand (bool actOnSelection, + const kpToolAutoCropBorder &leftBorder, + const kpToolAutoCropBorder &rightBorder, + const kpToolAutoCropBorder &topBorder, + const kpToolAutoCropBorder &botBorder, + kpMainWindow *mainWindow); + virtual ~kpToolAutoCropCommand (); + + enum NameOptions + { + DontShowAccel = 0, + ShowAccel = 1 + }; + + static QString name (bool actOnSelection, int options); + + virtual int size () const; + +private: + void getUndoPixmap (const kpToolAutoCropBorder &border, QPixmap **pixmap); + void getUndoPixmaps (); + void deleteUndoPixmaps (); + +public: + virtual void execute (); + virtual void unexecute (); + +private: + QRect contentsRect () const; + + bool m_actOnSelection; + kpToolAutoCropBorder m_leftBorder, m_rightBorder, m_topBorder, m_botBorder; + QPixmap *m_leftPixmap, *m_rightPixmap, *m_topPixmap, *m_botPixmap; + + QRect m_contentsRect; + int m_oldWidth, m_oldHeight; + kpSelection m_oldSelection; +}; + +#endif // __kptoolautocrop_h__ diff --git a/kolourpaint/tools/kptoolbrush.cpp b/kolourpaint/tools/kptoolbrush.cpp new file mode 100644 index 00000000..6e684ed9 --- /dev/null +++ b/kolourpaint/tools/kptoolbrush.cpp @@ -0,0 +1,45 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <klocale.h> +#include <kptoolbrush.h> + +kpToolBrush::kpToolBrush (kpMainWindow *mainWindow) + : kpToolPen (kpToolPen::Brush, + i18n ("Brush"), + i18n ("Draw using brushes of different shapes and sizes"), + Qt::Key_B, + mainWindow, "tool_brush") +{ +} + +kpToolBrush::~kpToolBrush () +{ +} + +#include <kptoolbrush.moc> diff --git a/kolourpaint/tools/kptoolbrush.h b/kolourpaint/tools/kptoolbrush.h new file mode 100644 index 00000000..69498495 --- /dev/null +++ b/kolourpaint/tools/kptoolbrush.h @@ -0,0 +1,43 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolbrush_h__ +#define __kptoolbrush_h__ + +#include <kptoolpen.h> + +class kpToolBrush : public kpToolPen +{ +Q_OBJECT + +public: + kpToolBrush (kpMainWindow *mainWindow); + virtual ~kpToolBrush (); +}; + +#endif // __kptoolbrush_h__ diff --git a/kolourpaint/tools/kptoolclear.cpp b/kolourpaint/tools/kptoolclear.cpp new file mode 100644 index 00000000..230e54a3 --- /dev/null +++ b/kolourpaint/tools/kptoolclear.cpp @@ -0,0 +1,135 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <kptoolclear.h> + +#include <qpixmap.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kpselection.h> + + +kpToolClearCommand::kpToolClearCommand (bool actOnSelection, + const kpColor &newColor, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_actOnSelection (actOnSelection), + m_newColor (newColor), + m_oldPixmapPtr (0) +{ +} + +kpToolClearCommand::kpToolClearCommand (bool actOnSelection, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_actOnSelection (actOnSelection), + m_newColor (mainWindow ? mainWindow->backgroundColor () : kpColor::invalid), + m_oldPixmapPtr (0) +{ +} + +kpToolClearCommand::~kpToolClearCommand () +{ + delete m_oldPixmapPtr; +} + + +// public virtual [base kpCommand] +QString kpToolClearCommand::name () const +{ + QString opName = i18n ("Clear"); + + if (m_actOnSelection) + return i18n ("Selection: %1").arg (opName); + else + return opName; +} + + +// public virtual [base kpCommand] +int kpToolClearCommand::size () const +{ + return kpPixmapFX::pixmapSize (m_oldPixmapPtr); +} + + +// public virtual [base kpCommand] +void kpToolClearCommand::execute () +{ + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolClearCommand::execute() without doc" << endl; + return; + } + + + m_oldPixmapPtr = new QPixmap (); + *m_oldPixmapPtr = *doc->pixmap (m_actOnSelection); + + + if (m_actOnSelection) + { + // OPT: could just edit pixmap directly and signal change + kpSelection *sel = doc->selection (); + + QPixmap newPixmap (sel->width (), sel->height ()); + kpPixmapFX::fill (&newPixmap, m_newColor); + // TODO: maybe disable Image/Clear if transparent colour + if (m_newColor.isOpaque ()) + newPixmap.setMask (sel->maskForOwnType ()); + + sel->setPixmap (newPixmap); + } + else + doc->fill (m_newColor); +} + +// public virtual [base kpCommand] +void kpToolClearCommand::unexecute () +{ + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolClearCommand::execute() without doc" << endl; + return; + } + + + doc->setPixmap (m_actOnSelection, *m_oldPixmapPtr); + + + delete m_oldPixmapPtr; + m_oldPixmapPtr = 0; +} diff --git a/kolourpaint/tools/kptoolclear.h b/kolourpaint/tools/kptoolclear.h new file mode 100644 index 00000000..ccf3697f --- /dev/null +++ b/kolourpaint/tools/kptoolclear.h @@ -0,0 +1,68 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolclear_h__ +#define __kptoolclear_h__ + +#include <kpcommandhistory.h> + +#include <kpcolor.h> + +class QPixmap; +class QString; + +class kpDocument; +class kpMainWindow; + + +class kpToolClearCommand : public kpCommand +{ +public: + kpToolClearCommand (bool actOnSelection, + const kpColor &newColor, + kpMainWindow *mainWindow); + kpToolClearCommand (bool actOnSelection, + kpMainWindow *mainWindow); + virtual ~kpToolClearCommand (); + + virtual QString name () const; + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + bool m_actOnSelection; + + kpColor m_newColor; + QPixmap *m_oldPixmapPtr; +}; + + +#endif // __kptoolclear_h__ diff --git a/kolourpaint/tools/kptoolcolorpicker.cpp b/kolourpaint/tools/kptoolcolorpicker.cpp new file mode 100644 index 00000000..1050b1cf --- /dev/null +++ b/kolourpaint/tools/kptoolcolorpicker.cpp @@ -0,0 +1,197 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_COLOR_PICKER 0 + + +#include <kptoolcolorpicker.h> + +#include <qimage.h> +#include <qpixmap.h> +#include <qpoint.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpcolortoolbar.h> +#include <kpcommandhistory.h> +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> + + +/* + * kpToolColorPicker + */ + +kpToolColorPicker::kpToolColorPicker (kpMainWindow *mainWindow) + : kpTool (i18n ("Color Picker"), i18n ("Lets you select a color from the image"), + Qt::Key_C, + mainWindow, "tool_color_picker") +{ +} + +kpToolColorPicker::~kpToolColorPicker () +{ +} + +kpColor kpToolColorPicker::colorAtPixel (const QPoint &p) +{ +#if DEBUG_KP_TOOL_COLOR_PICKER && 0 + kdDebug () << "kpToolColorPicker::colorAtPixel" << p << endl; +#endif + + return kpPixmapFX::getColorAtPixel (*document ()->pixmap (), p); +} + + +QString kpToolColorPicker::haventBegunDrawUserMessage () const +{ + return i18n ("Click to select a color."); +} + +void kpToolColorPicker::begin () +{ + setUserMessage (haventBegunDrawUserMessage ()); +} + +// virtual +void kpToolColorPicker::beginDraw () +{ + m_oldColor = color (m_mouseButton); + + setUserMessage (cancelUserMessage ()); +} + +// virtual +void kpToolColorPicker::draw (const QPoint &thisPoint, const QPoint &, const QRect &) +{ + const kpColor color = colorAtPixel (thisPoint); + + if (color.isValid ()) + { + mainWindow ()->colorToolBar ()->setColor (m_mouseButton, color); + setUserShapePoints (thisPoint); + } + else + { + mainWindow ()->colorToolBar ()->setColor (m_mouseButton, m_oldColor); + setUserShapePoints (); + } +} + +// virtual +void kpToolColorPicker::cancelShape () +{ + mainWindow ()->colorToolBar ()->setColor (m_mouseButton, m_oldColor); + + setUserMessage (i18n ("Let go of all the mouse buttons.")); +} + +void kpToolColorPicker::releasedAllButtons () +{ + setUserMessage (haventBegunDrawUserMessage ()); + +} + +// virtual +void kpToolColorPicker::endDraw (const QPoint &thisPoint, const QRect &) +{ + const kpColor color = colorAtPixel (thisPoint); + + if (color.isValid ()) + { + kpToolColorPickerCommand *cmd = new kpToolColorPickerCommand ( + m_mouseButton, + color, m_oldColor, + mainWindow ()); + + mainWindow ()->commandHistory ()->addCommand (cmd, false /* no exec */); + setUserMessage (haventBegunDrawUserMessage ()); + } + else + { + cancelShape (); + } +} + +/* + * kpToolColorPickerCommand + */ + +kpToolColorPickerCommand::kpToolColorPickerCommand (int mouseButton, + const kpColor &newColor, + const kpColor &oldColor, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_mouseButton (mouseButton), + m_newColor (newColor), + m_oldColor (oldColor) +{ +} + +kpToolColorPickerCommand::~kpToolColorPickerCommand () +{ +} + + +// public virtual [base kpCommand] +QString kpToolColorPickerCommand::name () const +{ + return i18n ("Color Picker"); +} + + +// public virtual [base kpCommand] +int kpToolColorPickerCommand::size () const +{ + return 0; +} + + +// public virtual [base kpCommand] +void kpToolColorPickerCommand::execute () +{ + colorToolBar ()->setColor (m_mouseButton, m_newColor); +} + +// public virtual [base kpCommand] +void kpToolColorPickerCommand::unexecute () +{ + colorToolBar ()->setColor (m_mouseButton, m_oldColor); +} + + +// private +kpColorToolBar *kpToolColorPickerCommand::colorToolBar () const +{ + return m_mainWindow ? m_mainWindow->colorToolBar () : 0; +} + +#include <kptoolcolorpicker.moc> diff --git a/kolourpaint/tools/kptoolcolorpicker.h b/kolourpaint/tools/kptoolcolorpicker.h new file mode 100644 index 00000000..46fc94be --- /dev/null +++ b/kolourpaint/tools/kptoolcolorpicker.h @@ -0,0 +1,95 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolcolorpicker_h__ +#define __kptoolcolorpicker_h__ + +#include <kpcommandhistory.h> + +#include <kpcolor.h> +#include <kptool.h> + +class QPoint; +class QRect; + +class kpColorToolBar; + +class kpToolColorPicker : public kpTool +{ +Q_OBJECT + +public: + kpToolColorPicker (kpMainWindow *); + virtual ~kpToolColorPicker (); + + // generally the user goes to pick a color but wants to return to using + // his/her previous drawing tool + virtual bool returnToPreviousToolAfterEndDraw () const { return true; } + +private: + QString haventBegunDrawUserMessage () const; + +public: + virtual void begin (); + virtual void beginDraw (); + virtual void draw (const QPoint &thisPoint, const QPoint &, const QRect &); + virtual void cancelShape (); + virtual void releasedAllButtons (); + virtual void endDraw (const QPoint &thisPoint, const QRect &); + +private: + kpColor colorAtPixel (const QPoint &p); + + kpColor m_oldColor; +}; + +class kpToolColorPickerCommand : public kpCommand +{ +public: + kpToolColorPickerCommand (int mouseButton, + const kpColor &newColor, const kpColor &oldColor, + kpMainWindow *mainWindow); + virtual ~kpToolColorPickerCommand (); + + virtual QString name () const; + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + kpColorToolBar *colorToolBar () const; + +private: + int m_mouseButton; + kpColor m_newColor; + kpColor m_oldColor; +}; + +#endif // __kptoolcolorpicker_h__ diff --git a/kolourpaint/tools/kptoolcolorwasher.cpp b/kolourpaint/tools/kptoolcolorwasher.cpp new file mode 100644 index 00000000..6c2d091f --- /dev/null +++ b/kolourpaint/tools/kptoolcolorwasher.cpp @@ -0,0 +1,45 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <klocale.h> +#include <kptoolcolorwasher.h> + +kpToolColorWasher::kpToolColorWasher (kpMainWindow *mainWindow) + : kpToolPen (kpToolPen::ColorWasher, + i18n ("Color Eraser"), + i18n ("Replaces pixels of the foreground color with the background color"), + Qt::Key_O, + mainWindow, "tool_color_washer") +{ +} + +kpToolColorWasher::~kpToolColorWasher () +{ +} + +#include <kptoolcolorwasher.moc> diff --git a/kolourpaint/tools/kptoolcolorwasher.h b/kolourpaint/tools/kptoolcolorwasher.h new file mode 100644 index 00000000..1a707c3e --- /dev/null +++ b/kolourpaint/tools/kptoolcolorwasher.h @@ -0,0 +1,43 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolcolorwasher_h__ +#define __kptoolcolorwasher_h__ + +#include <kptoolpen.h> + +class kpToolColorWasher : public kpToolPen +{ +Q_OBJECT + +public: + kpToolColorWasher (kpMainWindow *mainWindow); + virtual ~kpToolColorWasher (); +}; + +#endif // __kptoolcolorwasher_h__ diff --git a/kolourpaint/tools/kptoolconverttograyscale.cpp b/kolourpaint/tools/kptoolconverttograyscale.cpp new file mode 100644 index 00000000..a80ef8fa --- /dev/null +++ b/kolourpaint/tools/kptoolconverttograyscale.cpp @@ -0,0 +1,106 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <qapplication.h> +#include <qpixmap.h> + +#include <klocale.h> + +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kpselection.h> +#include <kptoolconverttograyscale.h> + + +kpToolConvertToGrayscaleCommand::kpToolConvertToGrayscaleCommand (bool actOnSelection, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_actOnSelection (actOnSelection), + m_oldPixmapPtr (0) +{ +} + +kpToolConvertToGrayscaleCommand::~kpToolConvertToGrayscaleCommand () +{ + delete m_oldPixmapPtr; +} + + +// public virtual [base kpCommand] +QString kpToolConvertToGrayscaleCommand::name () const +{ + QString opName = i18n ("Reduce to Grayscale"); + + if (m_actOnSelection) + return i18n ("Selection: %1").arg (opName); + else + return opName; +} + + +// public virtual [base kpCommand] +int kpToolConvertToGrayscaleCommand::size () const +{ + return kpPixmapFX::pixmapSize (m_oldPixmapPtr); +} + + +// public virtual [base kpCommand] +void kpToolConvertToGrayscaleCommand::execute () +{ + kpDocument *doc = document (); + if (!doc) + return; + + QApplication::setOverrideCursor (Qt::waitCursor); + + m_oldPixmapPtr = new QPixmap (); + *m_oldPixmapPtr = *doc->pixmap (m_actOnSelection); + + QPixmap newPixmap = kpPixmapFX::convertToGrayscale (*doc->pixmap (m_actOnSelection)); + + doc->setPixmap (m_actOnSelection, newPixmap); + + QApplication::restoreOverrideCursor (); +} + +// public virtual [base kpCommand] +void kpToolConvertToGrayscaleCommand::unexecute () +{ + kpDocument *doc = document (); + if (!doc) + return; + + doc->setPixmap (m_actOnSelection, *m_oldPixmapPtr); + + delete m_oldPixmapPtr; + m_oldPixmapPtr = 0; +} + diff --git a/kolourpaint/tools/kptoolconverttograyscale.h b/kolourpaint/tools/kptoolconverttograyscale.h new file mode 100644 index 00000000..6ea5e515 --- /dev/null +++ b/kolourpaint/tools/kptoolconverttograyscale.h @@ -0,0 +1,57 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolconverttograyscale_h__ +#define __kptoolconverttograyscale_h__ + +#include <kpcommandhistory.h> + +class QPixmap; +class QString; + +class kpMainWindow; + +class kpToolConvertToGrayscaleCommand : public kpCommand +{ +public: + kpToolConvertToGrayscaleCommand (bool actOnSelection, + kpMainWindow *mainWindow); + virtual ~kpToolConvertToGrayscaleCommand (); + + virtual QString name () const; + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + bool m_actOnSelection; + QPixmap *m_oldPixmapPtr; +}; + +#endif // __kptoolconverttograyscale_h__ diff --git a/kolourpaint/tools/kptoolcrop.cpp b/kolourpaint/tools/kptoolcrop.cpp new file mode 100644 index 00000000..8cc6e880 --- /dev/null +++ b/kolourpaint/tools/kptoolcrop.cpp @@ -0,0 +1,335 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_CROP 0 + + +#include <kptoolcrop.h> + +#include <qpixmap.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpcolor.h> +#include <kpcommandhistory.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kpselection.h> +#include <kptoolclear.h> +#include <kptoolresizescale.h> +#include <kptoolselection.h> +#include <kpviewmanager.h> + + +kpSelection selectionBorderAndMovedTo0_0 (const kpSelection &sel) +{ + kpSelection borderSel = sel; + + borderSel.setPixmap (QPixmap ()); // only interested in border + borderSel.moveTo (QPoint (0, 0)); + + return borderSel; +} + + +// +// kpToolCropSetImageCommand +// + +class kpToolCropSetImageCommand : public kpCommand +{ +public: + kpToolCropSetImageCommand (kpMainWindow *mainWindow); + virtual ~kpToolCropSetImageCommand (); + + /* (uninteresting child of macro cmd) */ + virtual QString name () const { return QString::null; } + + virtual int size () const + { + return kpPixmapFX::pixmapSize (m_oldPixmap) + + kpPixmapFX::selectionSize (m_fromSelection) + + kpPixmapFX::pixmapSize (m_pixmapIfFromSelectionDoesntHaveOne); + } + + virtual void execute (); + virtual void unexecute (); + +protected: + kpColor m_backgroundColor; + QPixmap m_oldPixmap; + kpSelection m_fromSelection; + QPixmap m_pixmapIfFromSelectionDoesntHaveOne; +}; + + +kpToolCropSetImageCommand::kpToolCropSetImageCommand (kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_backgroundColor (mainWindow ? mainWindow->backgroundColor () : kpColor::invalid), + m_fromSelection (*mainWindow->document ()->selection ()), + m_pixmapIfFromSelectionDoesntHaveOne ( + m_fromSelection.pixmap () ? + QPixmap () : + mainWindow->document ()->getSelectedPixmap ()) +{ +} + +kpToolCropSetImageCommand::~kpToolCropSetImageCommand () +{ +} + + +// public virtual [base kpCommand] +void kpToolCropSetImageCommand::execute () +{ +#if DEBUG_KP_TOOL_CROP + kdDebug () << "kpToolCropSetImageCommand::execute()" << endl; +#endif + + viewManager ()->setQueueUpdates (); + { + m_oldPixmap = kpPixmapFX::getPixmapAt (*document ()->pixmap (), + QRect (0, 0, m_fromSelection.width (), m_fromSelection.height ())); + + + // + // e.g. original elliptical selection: + // + // t/---\ T = original transparent selection pixel + // | TT | t = outside the selection region + // t\__/t [every other character] = original opaque selection pixel + // + // Afterwards, the _document_ image becomes: + // + // b/---\ T = [unchanged] + // | TT | b = background color + // b\__/b [every other character] = [unchanged] + // + // The selection is deleted. + // + // TODO: Do not introduce a mask if the result will not contain + // any transparent pixels. + // + + QPixmap newDocPixmap (m_fromSelection.width (), m_fromSelection.height ()); + kpPixmapFX::fill (&newDocPixmap, m_backgroundColor); + + #if DEBUG_KP_TOOL_CROP + kdDebug () << "\tsel: rect=" << m_fromSelection.boundingRect () + << " pm=" << m_fromSelection.pixmap () + << endl; + #endif + QPixmap selTransparentPixmap; + + if (m_fromSelection.pixmap ()) + { + selTransparentPixmap = m_fromSelection.transparentPixmap (); + #if DEBUG_KP_TOOL_CROP + kdDebug () << "\thave pixmap; rect=" + << selTransparentPixmap.rect () + << endl; + #endif + } + else + { + selTransparentPixmap = m_pixmapIfFromSelectionDoesntHaveOne; + #if DEBUG_KP_TOOL_CROP + kdDebug () << "\tno pixmap in sel - get it; rect=" + << selTransparentPixmap.rect () + << endl; + #endif + } + + kpPixmapFX::paintMaskTransparentWithBrush (&newDocPixmap, + QPoint (0, 0), + m_fromSelection.maskForOwnType ()); + + kpPixmapFX::paintPixmapAt (&newDocPixmap, + QPoint (0, 0), + selTransparentPixmap); + + + document ()->setPixmapAt (newDocPixmap, QPoint (0, 0)); + document ()->selectionDelete (); + + + if (mainWindow ()->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + viewManager ()->restoreQueueUpdates (); +} + +// public virtual [base kpCommand] +void kpToolCropSetImageCommand::unexecute () +{ +#if DEBUG_KP_TOOL_CROP + kdDebug () << "kpToolCropSetImageCommand::unexecute()" << endl; +#endif + + viewManager ()->setQueueUpdates (); + { + document ()->setPixmapAt (m_oldPixmap, QPoint (0, 0)); + m_oldPixmap.resize (0, 0); + + #if DEBUG_KP_TOOL_CROP + kdDebug () << "\tsel: rect=" << m_fromSelection.boundingRect () + << " pm=" << m_fromSelection.pixmap () + << endl; + #endif + document ()->setSelection (m_fromSelection); + + if (mainWindow ()->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + viewManager ()->restoreQueueUpdates (); +} + + +// +// kpToolCropCommand +// + + +class kpToolCropCommand : public kpMacroCommand +{ +public: + kpToolCropCommand (kpMainWindow *mainWindow); + virtual ~kpToolCropCommand (); +}; + + +kpToolCropCommand::kpToolCropCommand (kpMainWindow *mainWindow) + : kpMacroCommand (i18n ("Set as Image"), mainWindow) +{ +#if DEBUG_KP_TOOL_CROP + kdDebug () << "kpToolCropCommand::<ctor>()" << endl; +#endif + + if (!mainWindow || + !mainWindow->document () || + !mainWindow->document ()->selection ()) + { + kdError () << "kpToolCropCommand::kpToolCropCommand() without sel" << endl; + return; + } + + kpSelection *sel = mainWindow->document ()->selection (); + + +#if DEBUG_KP_TOOL_CROP + kdDebug () << "\tsel: w=" << sel->width () + << " h=" << sel->height () + << " <- resizing doc to these dimen" << endl; +#endif + + // (must resize doc _before_ kpToolCropSetImageCommand in case doc + // needs to gets bigger - else pasted down pixmap may not fit) + addCommand ( + new kpToolResizeScaleCommand ( + false/*act on doc, not sel*/, + sel->width (), sel->height (), + kpToolResizeScaleCommand::Resize, + mainWindow)); + + + if (sel->isText ()) + { + #if DEBUG_KP_TOOL_CROP + kdDebug () << "\tisText" << endl; + kdDebug () << "\tclearing doc with trans cmd" << endl; + #endif + addCommand ( + new kpToolClearCommand ( + false/*act on doc*/, + kpColor::transparent, + mainWindow)); + + #if DEBUG_KP_TOOL_CROP + kdDebug () << "\tmoving sel to (0,0) cmd" << endl; + #endif + kpToolSelectionMoveCommand *moveCmd = + new kpToolSelectionMoveCommand ( + QString::null/*uninteresting child of macro cmd*/, + mainWindow); + moveCmd->moveTo (QPoint (0, 0), true/*move on exec, not now*/); + moveCmd->finalize (); + addCommand (moveCmd); + } + else + { + #if DEBUG_KP_TOOL_CROP + kdDebug () << "\tis pixmap sel" << endl; + kdDebug () << "\tcreating SetImage cmd" << endl; + #endif + addCommand (new kpToolCropSetImageCommand (mainWindow)); + + #if 0 + addCommand ( + new kpToolSelectionCreateCommand ( + QString::null/*uninteresting child of macro cmd*/, + selectionBorderAndMovedTo0_0 (*sel), + mainWindow)); + #endif + } +} + +kpToolCropCommand::~kpToolCropCommand () +{ +} + + +void kpToolCrop (kpMainWindow *mainWindow) +{ + kpDocument *doc = mainWindow->document (); + if (!doc) + return; + + kpSelection *sel = doc ? doc->selection () : 0; + if (!sel) + return; + + + bool selWasText = sel->isText (); + kpSelection borderSel = selectionBorderAndMovedTo0_0 (*sel); + + + mainWindow->addImageOrSelectionCommand ( + new kpToolCropCommand (mainWindow), + true/*add create cmd*/, + false/*don't add pull cmd*/); + + + if (!selWasText) + { + mainWindow->commandHistory ()->addCommand ( + new kpToolSelectionCreateCommand ( + i18n ("Selection: Create"), + borderSel, + mainWindow)); + } +} diff --git a/kolourpaint/tools/kptoolcrop.h b/kolourpaint/tools/kptoolcrop.h new file mode 100644 index 00000000..c710a041 --- /dev/null +++ b/kolourpaint/tools/kptoolcrop.h @@ -0,0 +1,39 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef KP_TOOL_CROP_H +#define KP_TOOL_CROP_H + + +class kpMainWindow; + + +void kpToolCrop (kpMainWindow *mainWindow); + + +#endif // KP_TOOL_CROP_H diff --git a/kolourpaint/tools/kptoolcurve.cpp b/kolourpaint/tools/kptoolcurve.cpp new file mode 100644 index 00000000..f889c1ba --- /dev/null +++ b/kolourpaint/tools/kptoolcurve.cpp @@ -0,0 +1,47 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <kptoolcurve.h> + +#include <klocale.h> + + +kpToolCurve::kpToolCurve (kpMainWindow *mainWindow) + : kpToolPolygon (Curve, + i18n ("Curve"), + i18n ("Draws curves"), + Qt::Key_V, + mainWindow, "tool_curve") +{ +} + +kpToolCurve::~kpToolCurve () +{ +} + +#include <kptoolcurve.moc> diff --git a/kolourpaint/tools/kptoolcurve.h b/kolourpaint/tools/kptoolcurve.h new file mode 100644 index 00000000..489ce1fb --- /dev/null +++ b/kolourpaint/tools/kptoolcurve.h @@ -0,0 +1,45 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolcurve_h__ +#define __kptoolcurve_h__ + +#include <kptoolpolygon.h> + +class kpMainWindow; + +class kpToolCurve : public kpToolPolygon +{ +Q_OBJECT + +public: + kpToolCurve (kpMainWindow *mainWindow); + virtual ~kpToolCurve (); +}; + +#endif // __kptoolcurve_h__ diff --git a/kolourpaint/tools/kptoolellipse.cpp b/kolourpaint/tools/kptoolellipse.cpp new file mode 100644 index 00000000..f3b31dbb --- /dev/null +++ b/kolourpaint/tools/kptoolellipse.cpp @@ -0,0 +1,45 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <klocale.h> +#include <kptoolellipse.h> + +kpToolEllipse::kpToolEllipse (kpMainWindow *mainWindow) + : kpToolRectangle (Ellipse, + i18n ("Ellipse"), + i18n ("Draws ellipses and circles"), + Qt::Key_E, + mainWindow, "tool_ellipse") +{ +} + +kpToolEllipse::~kpToolEllipse () +{ +} + +#include <kptoolellipse.moc> diff --git a/kolourpaint/tools/kptoolellipse.h b/kolourpaint/tools/kptoolellipse.h new file mode 100644 index 00000000..fc9bf798 --- /dev/null +++ b/kolourpaint/tools/kptoolellipse.h @@ -0,0 +1,45 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolellipse_h__ +#define __kptoolellipse_h__ + +#include <kptoolrectangle.h> + +class kpMainWindow; + +class kpToolEllipse : public kpToolRectangle +{ +Q_OBJECT + +public: + kpToolEllipse (kpMainWindow *); + virtual ~kpToolEllipse (); +}; + +#endif // __kptoolellipse_h__ diff --git a/kolourpaint/tools/kptoolellipticalselection.cpp b/kolourpaint/tools/kptoolellipticalselection.cpp new file mode 100644 index 00000000..13daf799 --- /dev/null +++ b/kolourpaint/tools/kptoolellipticalselection.cpp @@ -0,0 +1,46 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <kptoolellipticalselection.h> + +#include <klocale.h> + + +kpToolEllipticalSelection::kpToolEllipticalSelection (kpMainWindow *mainWindow) + : kpToolSelection (Ellipse, + i18n ("Selection (Elliptical)"), + i18n ("Makes an elliptical or circular selection"), + Qt::Key_I, + mainWindow, "tool_elliptical_selection") +{ +} + +kpToolEllipticalSelection::~kpToolEllipticalSelection () +{ +} + diff --git a/kolourpaint/tools/kptoolellipticalselection.h b/kolourpaint/tools/kptoolellipticalselection.h new file mode 100644 index 00000000..9dbd643e --- /dev/null +++ b/kolourpaint/tools/kptoolellipticalselection.h @@ -0,0 +1,43 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolellipticalselection_h__ +#define __kptoolellipticalselection_h__ + +#include <kptoolselection.h> + +class kpMainWindow; + +class kpToolEllipticalSelection : public kpToolSelection +{ +public: + kpToolEllipticalSelection (kpMainWindow *); + virtual ~kpToolEllipticalSelection (); +}; + +#endif // __kptoolellipticalselection_h__ diff --git a/kolourpaint/tools/kptooleraser.cpp b/kolourpaint/tools/kptooleraser.cpp new file mode 100644 index 00000000..1acbf66e --- /dev/null +++ b/kolourpaint/tools/kptooleraser.cpp @@ -0,0 +1,44 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <klocale.h> +#include <kptooleraser.h> + +kpToolEraser::kpToolEraser (kpMainWindow *mainWindow) + : kpToolPen (kpToolPen::Eraser, + i18n ("Eraser"), i18n ("Lets you rub out mistakes"), + Qt::Key_A, + mainWindow, "tool_eraser") +{ +} + +kpToolEraser::~kpToolEraser () +{ +} + +#include <kptooleraser.moc> diff --git a/kolourpaint/tools/kptooleraser.h b/kolourpaint/tools/kptooleraser.h new file mode 100644 index 00000000..4dd7704a --- /dev/null +++ b/kolourpaint/tools/kptooleraser.h @@ -0,0 +1,43 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptooleraser_h__ +#define __kptooleraser_h__ + +#include <kptoolpen.h> + +class kpToolEraser : public kpToolPen +{ +Q_OBJECT + +public: + kpToolEraser (kpMainWindow *mainWindow); + virtual ~kpToolEraser (); +}; + +#endif // __kptooleraser_h__ diff --git a/kolourpaint/tools/kptoolflip.cpp b/kolourpaint/tools/kptoolflip.cpp new file mode 100644 index 00000000..58eeb66d --- /dev/null +++ b/kolourpaint/tools/kptoolflip.cpp @@ -0,0 +1,213 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <kptoolflip.h> + +#include <qapplication.h> +#include <qradiobutton.h> +#include <qvbox.h> +#include <qvbuttongroup.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpdefs.h> +#include <kpdocument.h> +#include <kppixmapfx.h> +#include <kpselection.h> +#include <kptool.h> +#include <kpmainwindow.h> + + +/* + * kpToolFlipCommand + */ + +kpToolFlipCommand::kpToolFlipCommand (bool actOnSelection, + bool horiz, bool vert, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_actOnSelection (actOnSelection), + m_horiz (horiz), m_vert (vert) +{ +} + +kpToolFlipCommand::~kpToolFlipCommand () +{ +} + + +// public virtual [base kpCommand] +QString kpToolFlipCommand::name () const +{ + QString opName; + + +#if 1 + opName = i18n ("Flip"); +#else // re-enable when giving full descriptions for all actions + if (m_horiz && m_vert) + opName = i18n ("Flip horizontally and vertically"); + else if (m_horiz) + opName = i18n ("Flip horizontally"); + else if (m_vert) + opName = i18n ("Flip vertically"); + else + { + kdError () << "kpToolFlipCommand::name() not asked to flip" << endl; + return QString::null; + } +#endif + + + if (m_actOnSelection) + return i18n ("Selection: %1").arg (opName); + else + return opName; +} + + +// public virtual [base kpCommand] +int kpToolFlipCommand::size () const +{ + return 0; +} + + +// public virtual [base kpCommand] +void kpToolFlipCommand::execute () +{ + flip (); +} + +// public virtual [base kpCommand] +void kpToolFlipCommand::unexecute () +{ + flip (); +} + + +// private +void kpToolFlipCommand::flip () +{ + kpDocument *doc = document (); + if (!doc) + return; + + + QApplication::setOverrideCursor (Qt::waitCursor); + + + if (m_actOnSelection) + { + doc->selection ()->flip (m_horiz, m_vert); + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + else + { + QPixmap newPixmap = kpPixmapFX::flip (*doc->pixmap (), m_horiz, m_vert); + + doc->setPixmap (newPixmap); + } + + + QApplication::restoreOverrideCursor (); +} + + +/* + * kpToolFlipDialog + */ + +// private static +bool kpToolFlipDialog::s_lastIsVerticalFlip = true; + + +kpToolFlipDialog::kpToolFlipDialog (bool actOnSelection, QWidget *parent) + : KDialogBase (parent, 0/*name*/, true/*modal*/, + actOnSelection ? i18n ("Flip Selection") : i18n ("Flip Image"), + KDialogBase::Ok | KDialogBase::Cancel) +{ + QVBox *vbox = makeVBoxMainWidget (); + + if (!vbox) + { + kdError () << "kpToolFlipDialog::kpToolFlipDialog() received NULL vbox" << endl; + } + else + { + QVButtonGroup *buttonGroup = new QVButtonGroup (i18n ("Direction"), vbox); + + // I'm sure vert flipping is much more common than horiz flipping so make it come first + m_verticalFlipRadioButton = new QRadioButton (i18n ("&Vertical (upside-down)"), buttonGroup); + m_horizontalFlipRadioButton = new QRadioButton (i18n ("&Horizontal"), buttonGroup); + + m_verticalFlipRadioButton->setChecked (s_lastIsVerticalFlip); + m_horizontalFlipRadioButton->setChecked (!s_lastIsVerticalFlip); + + connect (m_verticalFlipRadioButton, SIGNAL (toggled (bool)), + this, SLOT (slotIsVerticalFlipChanged ())); + connect (m_horizontalFlipRadioButton, SIGNAL (toggled (bool)), + this, SLOT (slotIsVerticalFlipChanged ())); + } +} + +kpToolFlipDialog::~kpToolFlipDialog () +{ +} + + +// public slot +void kpToolFlipDialog::slotIsVerticalFlipChanged () +{ + s_lastIsVerticalFlip = m_verticalFlipRadioButton->isChecked (); +} + + +// public +bool kpToolFlipDialog::getHorizontalFlip () const +{ + return m_horizontalFlipRadioButton->isChecked (); +} + +// public +bool kpToolFlipDialog::getVerticalFlip () const +{ + return m_verticalFlipRadioButton->isChecked (); +} + +// public +bool kpToolFlipDialog::isNoOp () const +{ + return !getHorizontalFlip () && !getVerticalFlip (); +} + + +#include <kptoolflip.moc> + diff --git a/kolourpaint/tools/kptoolflip.h b/kolourpaint/tools/kptoolflip.h new file mode 100644 index 00000000..c287c320 --- /dev/null +++ b/kolourpaint/tools/kptoolflip.h @@ -0,0 +1,88 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolflip_h__ +#define __kptoolflip_h__ + +#include <kpcommandhistory.h> +#include <kdialogbase.h> + +class QRadioButton; +class QString; + +class kpDocument; +class kpMainWindow; + + +class kpToolFlipCommand : public kpCommand +{ +public: + kpToolFlipCommand (bool actOnSelection, + bool horiz, bool vert, + kpMainWindow *mainWindow); + virtual ~kpToolFlipCommand (); + + virtual QString name () const; + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + void flip (); + + bool m_actOnSelection; + bool m_horiz, m_vert; +}; + + +class kpToolFlipDialog : public KDialogBase +{ +Q_OBJECT + +public: + kpToolFlipDialog (bool actOnSelection, QWidget *parent); + ~kpToolFlipDialog (); + +private: + static bool s_lastIsVerticalFlip; + +public slots: + void slotIsVerticalFlipChanged (); + +public: + bool getHorizontalFlip () const; + bool getVerticalFlip () const; + bool isNoOp () const; + +private: + QRadioButton *m_horizontalFlipRadioButton, *m_verticalFlipRadioButton; +}; + +#endif // __kptoolflip_h__ diff --git a/kolourpaint/tools/kptoolfloodfill.cpp b/kolourpaint/tools/kptoolfloodfill.cpp new file mode 100644 index 00000000..bb17d701 --- /dev/null +++ b/kolourpaint/tools/kptoolfloodfill.cpp @@ -0,0 +1,261 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_FLOOD_FILL 0 + + +#include <kptoolfloodfill.h> + +#include <qapplication.h> +#include <qcursor.h> +#include <qpainter.h> +#include <qpixmap.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpcommandhistory.h> +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kpview.h> +#include <kpviewmanager.h> + + +/* + * kpToolFloodFill + */ + +kpToolFloodFill::kpToolFloodFill (kpMainWindow *mainWindow) + : kpTool (i18n ("Flood Fill"), i18n ("Fills regions in the image"), + Qt::Key_F, + mainWindow, "tool_flood_fill"), + m_currentCommand (0) +{ +} + +kpToolFloodFill::~kpToolFloodFill () +{ +} + +QString kpToolFloodFill::haventBegunDrawUserMessage () const +{ + return i18n ("Click to fill a region."); +} + +void kpToolFloodFill::begin () +{ + setUserMessage (haventBegunDrawUserMessage ()); +} + +// virtual +void kpToolFloodFill::beginDraw () +{ +#if DEBUG_KP_TOOL_FLOOD_FILL && 1 + kdDebug () << "kpToolFloodFill::beginDraw()" << endl; +#endif + + QApplication::setOverrideCursor (Qt::waitCursor); + + // Flood Fill is an expensive CPU operation so we only fill at a + // mouse click (beginDraw ()), not on mouse move (virtually draw()) + m_currentCommand = new kpToolFloodFillCommand (m_currentPoint.x (), m_currentPoint.y (), + color (m_mouseButton), processedColorSimilarity (), + mainWindow ()); + + if (m_currentCommand->prepareColorToChange ()) + { + #if DEBUG_KP_TOOL_FLOOD_FILL && 1 + kdDebug () << "\tperforming new-doc-corner-case check" << endl; + #endif + if (document ()->url ().isEmpty () && !document ()->isModified ()) + { + m_currentCommand->setFillEntirePixmap (); + m_currentCommand->execute (); + } + else if (m_currentCommand->prepare ()) + { + m_currentCommand->execute (); + } + else + { + kdError () << "kpToolFloodFill::beginDraw() could not fill!" << endl; + } + } + else + { + kdError () << "kpToolFloodFill::beginDraw() could not prepareColorToChange!" << endl; + } + + QApplication::restoreOverrideCursor (); + + setUserMessage (cancelUserMessage ()); +} + +// virtual +void kpToolFloodFill::draw (const QPoint &thisPoint, const QPoint &, const QRect &) +{ + setUserShapePoints (thisPoint); +} + +// virtual +void kpToolFloodFill::cancelShape () +{ +#if 0 + endDraw (QPoint (), QRect ()); + mainWindow ()->commandHistory ()->undo (); +#else + m_currentCommand->unexecute (); + + delete m_currentCommand; + m_currentCommand = 0; +#endif + + setUserMessage (i18n ("Let go of all the mouse buttons.")); +} + +void kpToolFloodFill::releasedAllButtons () +{ + setUserMessage (haventBegunDrawUserMessage ()); +} + +// virtual +void kpToolFloodFill::endDraw (const QPoint &, const QRect &) +{ + mainWindow ()->commandHistory ()->addCommand (m_currentCommand, + false /* no exec - we already did it up there */); + + // don't delete + m_currentCommand = 0; + setUserMessage (haventBegunDrawUserMessage ()); +} + + +/* + * kpToolFloodFillCommand + */ + +kpToolFloodFillCommand::kpToolFloodFillCommand (int x, int y, + const kpColor &color, int processedColorSimilarity, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + kpFloodFill (document ()->pixmap (), x, y, color, processedColorSimilarity), + m_fillEntirePixmap (false) +{ +} + +kpToolFloodFillCommand::~kpToolFloodFillCommand () +{ +} + + +// public virtual [base kpCommand] +QString kpToolFloodFillCommand::name () const +{ + return i18n ("Flood Fill"); +} + +// public virtual [base kpCommand] +int kpToolFloodFillCommand::size () const +{ + return kpFloodFill::size () + kpPixmapFX::pixmapSize (m_oldPixmap); +} + + +void kpToolFloodFillCommand::setFillEntirePixmap (bool yes) +{ + m_fillEntirePixmap = yes; +} + + +// virtual +void kpToolFloodFillCommand::execute () +{ +#if DEBUG_KP_TOOL_FLOOD_FILL && 1 + kdDebug () << "kpToolFloodFillCommand::execute() m_fillEntirePixmap=" << m_fillEntirePixmap << endl; +#endif + + kpDocument *doc = document (); + if (!doc) + return; + + + if (m_fillEntirePixmap) + { + doc->fill (kpFloodFill::color ()); + } + else + { + QRect rect = kpFloodFill::boundingRect (); + if (rect.isValid ()) + { + QApplication::setOverrideCursor (QCursor::waitCursor); + + m_oldPixmap = doc->getPixmapAt (rect); + + kpFloodFill::fill (); + doc->slotContentsChanged (rect); + + QApplication::restoreOverrideCursor (); + } + else + { + #if DEBUG_KP_TOOL_FLOOD_FILL && 1 + kdDebug () << "\tinvalid boundingRect - must be NOP case" << endl; + #endif + } + } +} + +// virtual +void kpToolFloodFillCommand::unexecute () +{ + kpDocument *doc = document (); + if (!doc) + return; + + + if (m_fillEntirePixmap) + { + doc->fill (kpFloodFill::colorToChange ()); + } + else + { + QRect rect = kpFloodFill::boundingRect (); + if (rect.isValid ()) + { + doc->setPixmapAt (m_oldPixmap, rect.topLeft ()); + + m_oldPixmap.resize (0, 0); + + doc->slotContentsChanged (rect); + } + } +} + +#include <kptoolfloodfill.moc> diff --git a/kolourpaint/tools/kptoolfloodfill.h b/kolourpaint/tools/kptoolfloodfill.h new file mode 100644 index 00000000..a2eeaa5a --- /dev/null +++ b/kolourpaint/tools/kptoolfloodfill.h @@ -0,0 +1,94 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolfloodfill_h__ +#define __kptoolfloodfill_h__ + +#include <qpixmap.h> + +#include <kpcommandhistory.h> + +#include <kpfloodfill.h> +#include <kptool.h> + + +class QString; + +class kpColor; + +class kpMainWindow; +class kpToolFloodFillCommand; + + +class kpToolFloodFill : public kpTool +{ +Q_OBJECT + +public: + kpToolFloodFill (kpMainWindow *); + virtual ~kpToolFloodFill (); + +private: + QString haventBegunDrawUserMessage () const; + +public: + virtual void begin (); + virtual void beginDraw (); + virtual void draw (const QPoint &thisPoint, const QPoint &, const QRect &); + virtual void cancelShape (); + virtual void releasedAllButtons (); + virtual void endDraw (const QPoint &, const QRect &); + +private: + kpToolFloodFillCommand *m_currentCommand; +}; + + +class kpToolFloodFillCommand : public kpCommand, public kpFloodFill +{ +public: + kpToolFloodFillCommand (int x, int y, + const kpColor &color, int processedColorSimilarity, + kpMainWindow *mainWindow); + virtual ~kpToolFloodFillCommand (); + + virtual QString name () const; + + virtual int size () const; + + void setFillEntirePixmap (bool yes = true); + + virtual void execute (); + virtual void unexecute (); + +private: + QPixmap m_oldPixmap; + bool m_fillEntirePixmap; +}; + +#endif // __kptoolfloodfill_h__ diff --git a/kolourpaint/tools/kptoolfreeformselection.cpp b/kolourpaint/tools/kptoolfreeformselection.cpp new file mode 100644 index 00000000..7c736728 --- /dev/null +++ b/kolourpaint/tools/kptoolfreeformselection.cpp @@ -0,0 +1,46 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <kptoolfreeformselection.h> + +#include <klocale.h> + + +kpToolFreeFormSelection::kpToolFreeFormSelection (kpMainWindow *mainWindow) + : kpToolSelection (kpToolSelection::FreeForm, + i18n ("Selection (Free-Form)"), + i18n ("Makes a free-form selection"), + Qt::Key_M, + mainWindow, "tool_free_form_selection") +{ +} + +kpToolFreeFormSelection::~kpToolFreeFormSelection () +{ +} + diff --git a/kolourpaint/tools/kptoolfreeformselection.h b/kolourpaint/tools/kptoolfreeformselection.h new file mode 100644 index 00000000..28f1e5ec --- /dev/null +++ b/kolourpaint/tools/kptoolfreeformselection.h @@ -0,0 +1,43 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolfreeformselection_h__ +#define __kptoolfreeformselection_h__ + +#include <kptoolselection.h> + +class kpMainWindow; + +class kpToolFreeFormSelection : public kpToolSelection +{ +public: + kpToolFreeFormSelection (kpMainWindow *); + virtual ~kpToolFreeFormSelection (); +}; + +#endif // __kptoolfreeformselection_h__ diff --git a/kolourpaint/tools/kptoolline.cpp b/kolourpaint/tools/kptoolline.cpp new file mode 100644 index 00000000..809824d9 --- /dev/null +++ b/kolourpaint/tools/kptoolline.cpp @@ -0,0 +1,47 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <kptoolline.h> + +#include <klocale.h> + + +kpToolLine::kpToolLine (kpMainWindow *mainWindow) + : kpToolPolygon (Line, + i18n ("Line"), + i18n ("Draws lines"), + Qt::Key_L, + mainWindow, "tool_line") +{ +} + +kpToolLine::~kpToolLine () +{ +} + +#include <kptoolline.moc> diff --git a/kolourpaint/tools/kptoolline.h b/kolourpaint/tools/kptoolline.h new file mode 100644 index 00000000..7a956245 --- /dev/null +++ b/kolourpaint/tools/kptoolline.h @@ -0,0 +1,45 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolline_h__ +#define __kptoolline_h__ + +#include <kptoolpolygon.h> + +class kpMainWindow; + +class kpToolLine : public kpToolPolygon +{ +Q_OBJECT + +public: + kpToolLine (kpMainWindow *); + virtual ~kpToolLine (); +}; + +#endif // __kptoolline_h__ diff --git a/kolourpaint/tools/kptoolpen.cpp b/kolourpaint/tools/kptoolpen.cpp new file mode 100644 index 00000000..eb731ceb --- /dev/null +++ b/kolourpaint/tools/kptoolpen.cpp @@ -0,0 +1,1145 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_PEN 0 + +#include <qapplication.h> +#include <qbitmap.h> +#include <qcursor.h> +#include <qimage.h> +#include <qpainter.h> +#if DEBUG_KP_TOOL_PEN + #include <qdatetime.h> +#endif + +#include <kdebug.h> +#include <klocale.h> + +#include <kpcolor.h> +#include <kpcommandhistory.h> +#include <kpcursorprovider.h> +#include <kptoolpen.h> +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kptemppixmap.h> +#include <kptoolclear.h> +#include <kptooltoolbar.h> +#include <kptoolwidgetbrush.h> +#include <kptoolwidgeterasersize.h> +#include <kpviewmanager.h> + +/* + * kpToolPen + */ + +kpToolPen::kpToolPen (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_toolWidgetBrush (0), + m_toolWidgetEraserSize (0), + m_currentCommand (0) +{ +} + +kpToolPen::kpToolPen (kpMainWindow *mainWindow) + : kpTool (i18n ("Pen"), i18n ("Draws dots and freehand strokes"), + Qt::Key_P, + mainWindow, "tool_pen"), + m_mode (Pen), + m_toolWidgetBrush (0), + m_toolWidgetEraserSize (0), + m_currentCommand (0) +{ +} + +void kpToolPen::setMode (Mode mode) +{ + int usesPixmaps = (mode & (DrawsPixmaps | WashesPixmaps)); + int usesBrushes = (mode & (SquareBrushes | DiverseBrushes)); + + if ((usesPixmaps && !usesBrushes) || + (usesBrushes && !usesPixmaps)) + { + kdError () << "kpToolPen::setMode() passed invalid mode" << endl; + return; + } + + m_mode = mode; +} + +kpToolPen::~kpToolPen () +{ +} + + +// private +QString kpToolPen::haventBegunDrawUserMessage () const +{ + switch (m_mode) + { + case Pen: + case Brush: + return i18n ("Click to draw dots or drag to draw strokes."); + return i18n ("Click to draw dots or drag to draw strokes."); + case Eraser: + return i18n ("Click or drag to erase."); + case ColorWasher: + return i18n ("Click or drag to erase pixels of the foreground color."); + default: + return QString::null; + } +} + +// virtual +void kpToolPen::begin () +{ + m_toolWidgetBrush = 0; + m_brushIsDiagonalLine = false; + + kpToolToolBar *tb = toolToolBar (); + if (!tb) + return; + + if (m_mode & SquareBrushes) + { + m_toolWidgetEraserSize = tb->toolWidgetEraserSize (); + connect (m_toolWidgetEraserSize, SIGNAL (eraserSizeChanged (int)), + this, SLOT (slotEraserSizeChanged (int))); + m_toolWidgetEraserSize->show (); + + slotEraserSizeChanged (m_toolWidgetEraserSize->eraserSize ()); + + viewManager ()->setCursor (kpCursorProvider::lightCross ()); + } + + if (m_mode & DiverseBrushes) + { + m_toolWidgetBrush = tb->toolWidgetBrush (); + connect (m_toolWidgetBrush, SIGNAL (brushChanged (const QPixmap &, bool)), + this, SLOT (slotBrushChanged (const QPixmap &, bool))); + m_toolWidgetBrush->show (); + + slotBrushChanged (m_toolWidgetBrush->brush (), + m_toolWidgetBrush->brushIsDiagonalLine ()); + + viewManager ()->setCursor (kpCursorProvider::lightCross ()); + } + + setUserMessage (haventBegunDrawUserMessage ()); +} + +// virtual +void kpToolPen::end () +{ + if (m_toolWidgetEraserSize) + { + disconnect (m_toolWidgetEraserSize, SIGNAL (eraserSizeChanged (int)), + this, SLOT (slotEraserSizeChanged (int))); + m_toolWidgetEraserSize = 0; + } + + if (m_toolWidgetBrush) + { + disconnect (m_toolWidgetBrush, SIGNAL (brushChanged (const QPixmap &, bool)), + this, SLOT (slotBrushChanged (const QPixmap &, bool))); + m_toolWidgetBrush = 0; + } + + kpViewManager *vm = viewManager (); + if (vm) + { + if (vm->tempPixmap () && vm->tempPixmap ()->isBrush ()) + vm->invalidateTempPixmap (); + + if (m_mode & (SquareBrushes | DiverseBrushes)) + vm->unsetCursor (); + } + + // save memory + for (int i = 0; i < 2; i++) + m_brushPixmap [i].resize (0, 0); + m_cursorPixmap.resize (0, 0); +} + +// virtual +void kpToolPen::beginDraw () +{ + switch (m_mode) + { + case Pen: + m_currentCommand = new kpToolPenCommand (i18n ("Pen"), mainWindow ()); + break; + case Brush: + m_currentCommand = new kpToolPenCommand (i18n ("Brush"), mainWindow ()); + break; + case Eraser: + m_currentCommand = new kpToolPenCommand (i18n ("Eraser"), mainWindow ()); + break; + case ColorWasher: + m_currentCommand = new kpToolPenCommand (i18n ("Color Eraser"), mainWindow ()); + break; + + default: + m_currentCommand = new kpToolPenCommand (i18n ("Custom Pen or Brush"), mainWindow ()); + break; + } + + // we normally show the Brush pix in the foreground colour but if the + // user starts drawing in the background color, we don't want to leave + // the cursor in the foreground colour -- just hide it in all cases + // to avoid confusion + viewManager ()->invalidateTempPixmap (); + + setUserMessage (cancelUserMessage ()); +} + +// virtual +void kpToolPen::hover (const QPoint &point) +{ +#if DEBUG_KP_TOOL_PEN && 0 + kdDebug () << "kpToolPen::hover(" << point << ")" + << " hasBegun=" << hasBegun () + << " hasBegunDraw=" << hasBegunDraw () + << " cursorPixmap.isNull=" << m_cursorPixmap.isNull () + << endl; +#endif + if (point != KP_INVALID_POINT && !m_cursorPixmap.isNull ()) + { + // (for hotPoint() as m_mouseButton is not normally defined in hover()) + m_mouseButton = 0; + + kpTempPixmap::RenderMode renderMode; + QPixmap cursorPixmapForTempPixmap = m_cursorPixmap; + + if (m_mode & SquareBrushes) + renderMode = kpTempPixmap::SetPixmap; + else if (m_mode & DiverseBrushes) + { + if (color (0).isOpaque ()) + renderMode = kpTempPixmap::PaintPixmap; + else + { + renderMode = kpTempPixmap::PaintMaskTransparentWithBrush; + cursorPixmapForTempPixmap = kpPixmapFX::getNonNullMask (m_cursorPixmap); + } + } + + viewManager ()->setFastUpdates (); + + viewManager ()->setTempPixmap ( + kpTempPixmap (true/*brush*/, + renderMode, + hotPoint (), + cursorPixmapForTempPixmap)); + + viewManager ()->restoreFastUpdates (); + } + +#if DEBUG_KP_TOOL_PEN && 0 + if (document ()->rect ().contains (point)) + { + QImage image = kpPixmapFX::convertToImage (*document ()->pixmap ()); + + QRgb v = image.pixel (point.x (), point.y ()); + kdDebug () << "(" << point << "): r=" << qRed (v) + << " g=" << qGreen (v) + << " b=" << qBlue (v) + << " a=" << qAlpha (v) + << endl; + } +#endif + + setUserShapePoints (point); +} + +bool kpToolPen::wash (QPainter *painter, QPainter *maskPainter, + const QImage &image, + const kpColor &colorToReplace, + const QRect &imageRect, int plotx, int ploty) +{ + return wash (painter, maskPainter, image, colorToReplace, imageRect, hotRect (plotx, ploty)); +} + +bool kpToolPen::wash (QPainter *painter, QPainter *maskPainter, + const QImage &image, + const kpColor &colorToReplace, + const QRect &imageRect, const QRect &drawRect) +{ + bool didSomething = false; + +#if DEBUG_KP_TOOL_PEN && 0 + kdDebug () << "kpToolPen::wash(imageRect=" << imageRect + << ",drawRect=" << drawRect + << ")" << endl; +#endif + +// make use of scanline coherence +#define FLUSH_LINE() \ +{ \ + if (painter && painter->isActive ()) \ + painter->drawLine (startDrawX, y, x - 1, y); \ + if (maskPainter && maskPainter->isActive ()) \ + maskPainter->drawLine (startDrawX, y, x - 1, y); \ + didSomething = true; \ + startDrawX = -1; \ +} + + const int maxY = drawRect.bottom () - imageRect.top (); + + const int minX = drawRect.left () - imageRect.left (); + const int maxX = drawRect.right () - imageRect.left (); + + for (int y = drawRect.top () - imageRect.top (); + y <= maxY; + y++) + { + int startDrawX = -1; + + int x; // for FLUSH_LINE() + for (x = minX; x <= maxX; x++) + { + #if DEBUG_KP_TOOL_PEN && 0 + fprintf (stderr, "y=%i x=%i colorAtPixel=%08X colorToReplace=%08X ... ", + y, x, + kpPixmapFX::getColorAtPixel (image, QPoint (x, y)).toQRgb (), + colorToReplace.toQRgb ()); + #endif + if (kpPixmapFX::getColorAtPixel (image, QPoint (x, y)).isSimilarTo (colorToReplace, processedColorSimilarity ())) + { + #if DEBUG_KP_TOOL_PEN && 0 + fprintf (stderr, "similar\n"); + #endif + if (startDrawX < 0) + startDrawX = x; + } + else + { + #if DEBUG_KP_TOOL_PEN && 0 + fprintf (stderr, "different\n"); + #endif + if (startDrawX >= 0) + FLUSH_LINE (); + } + } + + if (startDrawX >= 0) + FLUSH_LINE (); + } + +#undef FLUSH_LINE + + return didSomething; +} + +// virtual +void kpToolPen::globalDraw () +{ + // it's easiest to reimplement globalDraw() here rather than in + // all the relevant subclasses + + if (m_mode == Eraser) + { + #if DEBUG_KP_TOOL_PEN + kdDebug () << "kpToolPen::globalDraw() eraser" << endl; + #endif + mainWindow ()->commandHistory ()->addCommand ( + new kpToolClearCommand (false/*act on doc, not sel*/, mainWindow ())); + } + else if (m_mode == ColorWasher) + { + #if DEBUG_KP_TOOL_PEN + kdDebug () << "kpToolPen::globalDraw() colour eraser" << endl; + #endif + if (foregroundColor () == backgroundColor () && processedColorSimilarity () == 0) + return; + + QApplication::setOverrideCursor (Qt::waitCursor); + + kpToolPenCommand *cmd = new kpToolPenCommand ( + i18n ("Color Eraser"), mainWindow ()); + + QPainter painter, maskPainter; + QBitmap maskBitmap; + + if (backgroundColor ().isOpaque ()) + { + painter.begin (document ()->pixmap ()); + painter.setPen (backgroundColor ().toQColor ()); + } + + if (backgroundColor ().isTransparent () || + document ()->pixmap ()->mask ()) + { + maskBitmap = kpPixmapFX::getNonNullMask (*document ()->pixmap ()); + maskPainter.begin (&maskBitmap); + + maskPainter.setPen (backgroundColor ().maskColor ()); + } + + const QImage image = kpPixmapFX::convertToImage (*document ()->pixmap ()); + QRect rect = document ()->rect (); + + const bool didSomething = wash (&painter, &maskPainter, image, + foregroundColor ()/*replace foreground*/, + rect, rect); + + // flush + if (painter.isActive ()) + painter.end (); + + if (maskPainter.isActive ()) + maskPainter.end (); + + if (didSomething) + { + if (!maskBitmap.isNull ()) + document ()->pixmap ()->setMask (maskBitmap); + + + document ()->slotContentsChanged (rect); + + + cmd->updateBoundingRect (rect); + cmd->finalize (); + + mainWindow ()->commandHistory ()->addCommand (cmd, false /* don't exec */); + + // don't delete - it's up to the commandHistory + cmd = 0; + } + else + { + #if DEBUG_KP_TOOL_PEN + kdDebug () << "\tisNOP" << endl; + #endif + delete cmd; + cmd = 0; + } + + QApplication::restoreOverrideCursor (); + } +} + +// virtual +// TODO: refactor! +void kpToolPen::draw (const QPoint &thisPoint, const QPoint &lastPoint, const QRect &) +{ + if ((m_mode & WashesPixmaps) && (foregroundColor () == backgroundColor ()) && processedColorSimilarity () == 0) + return; + + // sync: remember to restoreFastUpdates() in all exit paths + viewManager ()->setFastUpdates (); + + if (m_brushIsDiagonalLine ? currentPointCardinallyNextToLast () : currentPointNextToLast ()) + { + if (m_mode & DrawsPixels) + { + QPixmap pixmap (1, 1); + + const kpColor c = color (m_mouseButton); + + // OPT: this seems hopelessly inefficient + if (c.isOpaque ()) + { + pixmap.fill (c.toQColor ()); + } + else + { + QBitmap mask (1, 1); + mask.fill (Qt::color0/*transparent*/); + + pixmap.setMask (mask); + } + + // draw onto doc + document ()->setPixmapAt (pixmap, thisPoint); + + m_currentCommand->updateBoundingRect (thisPoint); + } + // Brush & Eraser + else if (m_mode & DrawsPixmaps) + { + if (color (m_mouseButton).isOpaque ()) + document ()->paintPixmapAt (m_brushPixmap [m_mouseButton], hotPoint ()); + else + { + kpPixmapFX::paintMaskTransparentWithBrush (document ()->pixmap (), + hotPoint (), + kpPixmapFX::getNonNullMask (m_brushPixmap [m_mouseButton])); + document ()->slotContentsChanged (hotRect ()); + } + + m_currentCommand->updateBoundingRect (hotRect ()); + } + else if (m_mode & WashesPixmaps) + { + #if DEBUG_KP_TOOL_PEN + kdDebug () << "Washing pixmap (immediate)" << endl; + QTime timer; + #endif + QRect rect = hotRect (); + #if DEBUG_KP_TOOL_PEN + timer.start (); + #endif + QPixmap pixmap = document ()->getPixmapAt (rect); + #if DEBUG_KP_TOOL_PEN + kdDebug () << "\tget from doc: " << timer.restart () << "ms" << endl; + #endif + const QImage image = kpPixmapFX::convertToImage (pixmap); + #if DEBUG_KP_TOOL_PEN + kdDebug () << "\tconvert to image: " << timer.restart () << "ms" << endl; + #endif + QPainter painter, maskPainter; + QBitmap maskBitmap; + + if (color (m_mouseButton).isOpaque ()) + { + painter.begin (&pixmap); + painter.setPen (color (m_mouseButton).toQColor ()); + } + + if (color (m_mouseButton).isTransparent () || + pixmap.mask ()) + { + maskBitmap = kpPixmapFX::getNonNullMask (pixmap); + maskPainter.begin (&maskBitmap); + maskPainter.setPen (color (m_mouseButton).maskColor ()); + } + + bool didSomething = wash (&painter, &maskPainter, + image, + color (1 - m_mouseButton)/*color to replace*/, + rect, rect); + + if (painter.isActive ()) + painter.end (); + + if (maskPainter.isActive ()) + maskPainter.end (); + + if (didSomething) + { + if (!maskBitmap.isNull ()) + pixmap.setMask (maskBitmap); + + #if DEBUG_KP_TOOL_PEN + kdDebug () << "\twashed: " << timer.restart () << "ms" << endl; + #endif + document ()->setPixmapAt (pixmap, hotPoint ()); + #if DEBUG_KP_TOOL_PEN + kdDebug () << "\tset doc: " << timer.restart () << "ms" << endl; + #endif + m_currentCommand->updateBoundingRect (hotRect ()); + #if DEBUG_KP_TOOL_PEN + kdDebug () << "\tupdate boundingRect: " << timer.restart () << "ms" << endl; + kdDebug () << "\tdone" << endl; + #endif + } + + #if DEBUG_KP_TOOL_PEN && 1 + kdDebug () << endl; + #endif + } + } + // in reality, the system is too slow to give us all the MouseMove events + // so we "interpolate" the missing points :) + else + { + // find bounding rectangle + QRect rect = QRect (thisPoint, lastPoint).normalize (); + if (m_mode != DrawsPixels) + rect = neededRect (rect, m_brushPixmap [m_mouseButton].width ()); + + #if DEBUG_KP_TOOL_PEN + if (m_mode & WashesPixmaps) + { + kdDebug () << "Washing pixmap (w=" << rect.width () + << ",h=" << rect.height () << ")" << endl; + } + QTime timer; + int convAndWashTime; + #endif + + const kpColor c = color (m_mouseButton); + bool transparent = c.isTransparent (); + + QPixmap pixmap = document ()->getPixmapAt (rect); + QBitmap maskBitmap; + + QPainter painter, maskPainter; + + if (m_mode & (DrawsPixels | WashesPixmaps)) + { + if (!transparent) + { + painter.begin (&pixmap); + painter.setPen (c.toQColor ()); + } + + if (transparent || pixmap.mask ()) + { + maskBitmap = kpPixmapFX::getNonNullMask (pixmap); + maskPainter.begin (&maskBitmap); + maskPainter.setPen (c.maskColor ()); + } + } + + QImage image; + if (m_mode & WashesPixmaps) + { + #if DEBUG_KP_TOOL_PEN + timer.start (); + #endif + image = kpPixmapFX::convertToImage (pixmap); + #if DEBUG_KP_TOOL_PEN + convAndWashTime = timer.restart (); + kdDebug () << "\tconvert to image: " << convAndWashTime << " ms" << endl; + #endif + } + + bool didSomething = false; + + if (m_mode & DrawsPixels) + { + QPoint sp = lastPoint - rect.topLeft (), ep = thisPoint - rect.topLeft (); + if (painter.isActive ()) + painter.drawLine (sp, ep); + + if (maskPainter.isActive ()) + maskPainter.drawLine (sp, ep); + + didSomething = true; + } + // Brush & Eraser + else if (m_mode & (DrawsPixmaps | WashesPixmaps)) + { + kpColor colorToReplace; + + if (m_mode & WashesPixmaps) + colorToReplace = color (1 - m_mouseButton); + + // Sweeps a pixmap along a line (modified Bresenham's line algorithm, + // see MODIFIED comment below). + // + // Derived from the zSprite2 Graphics Engine + + const int x1 = (thisPoint - rect.topLeft ()).x (), + y1 = (thisPoint - rect.topLeft ()).y (), + x2 = (lastPoint - rect.topLeft ()).x (), + y2 = (lastPoint - rect.topLeft ()).y (); + + // Difference of x and y values + int dx = x2 - x1; + int dy = y2 - y1; + + // Absolute values of differences + int ix = kAbs (dx); + int iy = kAbs (dy); + + // Larger of the x and y differences + int inc = ix > iy ? ix : iy; + + // Plot location + int plotx = x1; + int ploty = y1; + + int x = 0; + int y = 0; + + if (m_mode & WashesPixmaps) + { + if (wash (&painter, &maskPainter, image, + colorToReplace, + rect, plotx + rect.left (), ploty + rect.top ())) + { + didSomething = true; + } + } + else + { + if (!transparent) + { + kpPixmapFX::paintPixmapAt (&pixmap, + hotPoint (plotx, ploty), + m_brushPixmap [m_mouseButton]); + } + else + { + kpPixmapFX::paintMaskTransparentWithBrush (&pixmap, + hotPoint (plotx, ploty), + kpPixmapFX::getNonNullMask (m_brushPixmap [m_mouseButton])); + } + + didSomething = true; + } + + for (int i = 0; i <= inc; i++) + { + // oldplotx is equally as valid but would look different + // (but nobody will notice which one it is) + int oldploty = ploty; + int plot = 0; + + x += ix; + y += iy; + + if (x > inc) + { + plot++; + x -= inc; + + if (dx < 0) + plotx--; + else + plotx++; + } + + if (y > inc) + { + plot++; + y -= inc; + + if (dy < 0) + ploty--; + else + ploty++; + } + + if (plot) + { + if (m_brushIsDiagonalLine && plot == 2) + { + // MODIFIED: every point is + // horizontally or vertically adjacent to another point (if there + // is more than 1 point, of course). This is in contrast to the + // ordinary line algorithm which can create diagonal adjacencies. + + if (m_mode & WashesPixmaps) + { + if (wash (&painter, &maskPainter, image, + colorToReplace, + rect, plotx + rect.left (), oldploty + rect.top ())) + { + didSomething = true; + } + } + else + { + if (!transparent) + { + kpPixmapFX::paintPixmapAt (&pixmap, + hotPoint (plotx, oldploty), + m_brushPixmap [m_mouseButton]); + } + else + { + kpPixmapFX::paintMaskTransparentWithBrush (&pixmap, + hotPoint (plotx, oldploty), + kpPixmapFX::getNonNullMask (m_brushPixmap [m_mouseButton])); + } + + didSomething = true; + } + } + + if (m_mode & WashesPixmaps) + { + if (wash (&painter, &maskPainter, image, + colorToReplace, + rect, plotx + rect.left (), ploty + rect.top ())) + { + didSomething = true; + } + } + else + { + if (!transparent) + { + kpPixmapFX::paintPixmapAt (&pixmap, + hotPoint (plotx, ploty), + m_brushPixmap [m_mouseButton]); + } + else + { + kpPixmapFX::paintMaskTransparentWithBrush (&pixmap, + hotPoint (plotx, ploty), + kpPixmapFX::getNonNullMask (m_brushPixmap [m_mouseButton])); + } + + didSomething = true; + } + } + } + + } + + if (painter.isActive ()) + painter.end (); + + if (maskPainter.isActive ()) + maskPainter.end (); + + #if DEBUG_KP_TOOL_PEN + if (m_mode & WashesPixmaps) + { + int ms = timer.restart (); + kdDebug () << "\ttried to wash: " << ms << "ms" + << " (" << (ms ? (rect.width () * rect.height () / ms) : -1234) + << " pixels/ms)" + << endl; + convAndWashTime += ms; + } + #endif + + + if (didSomething) + { + if (!maskBitmap.isNull ()) + pixmap.setMask (maskBitmap); + + // draw onto doc + document ()->setPixmapAt (pixmap, rect.topLeft ()); + + #if DEBUG_KP_TOOL_PEN + if (m_mode & WashesPixmaps) + { + int ms = timer.restart (); + kdDebug () << "\tset doc: " << ms << "ms" << endl; + convAndWashTime += ms; + } + #endif + + m_currentCommand->updateBoundingRect (rect); + + #if DEBUG_KP_TOOL_PEN + if (m_mode & WashesPixmaps) + { + int ms = timer.restart (); + kdDebug () << "\tupdate boundingRect: " << ms << "ms" << endl; + convAndWashTime += ms; + kdDebug () << "\tdone (" << (convAndWashTime ? (rect.width () * rect.height () / convAndWashTime) : -1234) + << " pixels/ms)" + << endl; + } + #endif + } + + #if DEBUG_KP_TOOL_PEN + if (m_mode & WashesPixmaps) + kdDebug () << endl; + #endif + } + + viewManager ()->restoreFastUpdates (); + setUserShapePoints (thisPoint); +} + +// virtual +void kpToolPen::cancelShape () +{ + m_currentCommand->finalize (); + m_currentCommand->cancel (); + + delete m_currentCommand; + m_currentCommand = 0; + + updateBrushCursor (false/*no recalc*/); + + setUserMessage (i18n ("Let go of all the mouse buttons.")); +} + +void kpToolPen::releasedAllButtons () +{ + setUserMessage (haventBegunDrawUserMessage ()); +} + +// virtual +void kpToolPen::endDraw (const QPoint &, const QRect &) +{ + m_currentCommand->finalize (); + mainWindow ()->commandHistory ()->addCommand (m_currentCommand, false /* don't exec */); + + // don't delete - it's up to the commandHistory + m_currentCommand = 0; + + updateBrushCursor (false/*no recalc*/); + + setUserMessage (haventBegunDrawUserMessage ()); +} + + +// TODO: maybe the base should be virtual? +kpColor kpToolPen::color (int which) +{ +#if DEBUG_KP_TOOL_PEN && 0 + kdDebug () << "kpToolPen::color (" << which << ")" << endl; +#endif + + // Pen & Brush + if ((m_mode & SwappedColors) == 0) + return kpTool::color (which); + // only the (Color) Eraser uses the opposite color + else + return kpTool::color (which ? 0 : 1); // don't trust !0 == 1 +} + +// virtual private slot +void kpToolPen::slotForegroundColorChanged (const kpColor &col) +{ +#if DEBUG_KP_TOOL_PEN + kdDebug () << "kpToolPen::slotForegroundColorChanged()" << endl; +#endif + if (col.isOpaque ()) + m_brushPixmap [(m_mode & SwappedColors) ? 1 : 0].fill (col.toQColor ()); + + updateBrushCursor (); +} + +// virtual private slot +void kpToolPen::slotBackgroundColorChanged (const kpColor &col) +{ +#if DEBUG_KP_TOOL_PEN + kdDebug () << "kpToolPen::slotBackgroundColorChanged()" << endl; +#endif + + if (col.isOpaque ()) + m_brushPixmap [(m_mode & SwappedColors) ? 0 : 1].fill (col.toQColor ()); + + updateBrushCursor (); +} + +// private slot +void kpToolPen::slotBrushChanged (const QPixmap &pixmap, bool isDiagonalLine) +{ +#if DEBUG_KP_TOOL_PEN + kdDebug () << "kpToolPen::slotBrushChanged()" << endl; +#endif + for (int i = 0; i < 2; i++) + { + m_brushPixmap [i] = pixmap; + if (color (i).isOpaque ()) + m_brushPixmap [i].fill (color (i).toQColor ()); + } + + m_brushIsDiagonalLine = isDiagonalLine; + + updateBrushCursor (); +} + +// private slot +void kpToolPen::slotEraserSizeChanged (int size) +{ +#if DEBUG_KP_TOOL_PEN + kdDebug () << "KpToolPen::slotEraserSizeChanged(size=" << size << ")" << endl; +#endif + + for (int i = 0; i < 2; i++) + { + // Note: No matter what, the eraser's brush pixmap is never given + // a mask. + // + // With a transparent color, since we don't fill anything, the + // resize by itself will leave us with garbage pixels. This + // doesn't matter because: + // + // 1. The hover cursor will ask kpToolWidgetEraserSize for a proper + // cursor pixmap. + // 2. We will draw using kpPixmapFX::paintMaskTransparentWithBrush() + // which only cares about the opaqueness. + m_brushPixmap [i].resize (size, size); + if (color (i).isOpaque ()) + m_brushPixmap [i].fill (color (i).toQColor ()); + } + + updateBrushCursor (); +} + +QPoint kpToolPen::hotPoint () const +{ + return hotPoint (m_currentPoint); +} + +QPoint kpToolPen::hotPoint (int x, int y) const +{ + return hotPoint (QPoint (x, y)); +} + +QPoint kpToolPen::hotPoint (const QPoint &point) const +{ + /* + * e.g. + * Width 5: + * 0 1 2 3 4 + * ^ + * | + * Center + */ + return point - + QPoint (m_brushPixmap [m_mouseButton].width () / 2, + m_brushPixmap [m_mouseButton].height () / 2); +} + +QRect kpToolPen::hotRect () const +{ + return hotRect (m_currentPoint); +} + +QRect kpToolPen::hotRect (int x, int y) const +{ + return hotRect (QPoint (x, y)); +} + +QRect kpToolPen::hotRect (const QPoint &point) const +{ + QPoint topLeft = hotPoint (point); + return QRect (topLeft.x (), + topLeft.y (), + m_brushPixmap [m_mouseButton].width (), + m_brushPixmap [m_mouseButton].height ()); +} + +// private +void kpToolPen::updateBrushCursor (bool recalc) +{ +#if DEBUG_KP_TOOL_PEN && 1 + kdDebug () << "kpToolPen::updateBrushCursor(recalc=" << recalc << ")" << endl; +#endif + + if (recalc) + { + if (m_mode & SquareBrushes) + m_cursorPixmap = m_toolWidgetEraserSize->cursorPixmap (color (0)); + else if (m_mode & DiverseBrushes) + m_cursorPixmap = m_brushPixmap [0]; + } + + hover (hasBegun () ? m_currentPoint : currentPoint ()); +} + + +/* + * kpToolPenCommand + */ + +kpToolPenCommand::kpToolPenCommand (const QString &name, kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_pixmap (*document ()->pixmap ()) +{ +} + +kpToolPenCommand::~kpToolPenCommand () +{ +} + + +// public virtual [base kpCommand] +int kpToolPenCommand::size () const +{ + return kpPixmapFX::pixmapSize (m_pixmap); +} + + +// public virtual [base kpCommand] +void kpToolPenCommand::execute () +{ + swapOldAndNew (); +} + +// public virtual [base kpCommand] +void kpToolPenCommand::unexecute () +{ + swapOldAndNew (); +} + + +// private +void kpToolPenCommand::swapOldAndNew () +{ + if (m_boundingRect.isValid ()) + { + QPixmap oldPixmap = document ()->getPixmapAt (m_boundingRect); + + document ()->setPixmapAt (m_pixmap, m_boundingRect.topLeft ()); + + m_pixmap = oldPixmap; + } +} + +// public +void kpToolPenCommand::updateBoundingRect (const QPoint &point) +{ + updateBoundingRect (QRect (point, point)); +} + +// public +void kpToolPenCommand::updateBoundingRect (const QRect &rect) +{ +#if DEBUG_KP_TOOL_PEN & 0 + kdDebug () << "kpToolPenCommand::updateBoundingRect() existing=" + << m_boundingRect + << " plus=" + << rect + << endl; +#endif + m_boundingRect = m_boundingRect.unite (rect); +#if DEBUG_KP_TOOL_PEN & 0 + kdDebug () << "\tresult=" << m_boundingRect << endl; +#endif +} + +// public +void kpToolPenCommand::finalize () +{ + if (m_boundingRect.isValid ()) + { + // store only needed part of doc pixmap + m_pixmap = kpTool::neededPixmap (m_pixmap, m_boundingRect); + } + else + { + m_pixmap.resize (0, 0); + } +} + +// public +void kpToolPenCommand::cancel () +{ + if (m_boundingRect.isValid ()) + { + viewManager ()->setFastUpdates (); + document ()->setPixmapAt (m_pixmap, m_boundingRect.topLeft ()); + viewManager ()->restoreFastUpdates (); + } +} + +#include <kptoolpen.moc> diff --git a/kolourpaint/tools/kptoolpen.h b/kolourpaint/tools/kptoolpen.h new file mode 100644 index 00000000..f57eb367 --- /dev/null +++ b/kolourpaint/tools/kptoolpen.h @@ -0,0 +1,160 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolpen_h__ +#define __kptoolpen_h__ + +#include <qpixmap.h> +#include <qrect.h> + +#include <kpcommandhistory.h> +#include <kptool.h> + +class QPoint; +class QString; + +class kpColor; +class kpMainWindow; +class kpToolPenCommand; +class kpToolWidgetBrush; +class kpToolWidgetEraserSize; +class kpViewManager; + +class kpToolPen : public kpTool +{ +Q_OBJECT + +public: + enum Mode + { + // tool properties + DrawsPixels = (1 << 0), DrawsPixmaps = (1 << 1), WashesPixmaps = (1 << 2), + NoBrushes = 0, SquareBrushes = (1 << 3), DiverseBrushes = (1 << 4), + NormalColors = 0, SwappedColors = (1 << 5), + + // tools: + // + // Pen = draws pixels, "interpolates" by "sweeping" pixels along a line (no brushes) + // Brush = draws pixmaps, "interpolates" by "sweeping" pixmaps along a line (interesting brushes) + // Eraser = Brush but with foreground & background colors swapped (a few square brushes) + // Color Washer = Brush that replaces/washes the foreground color with the background color + // + // (note the capitalization of "brush" here :)) + Pen = DrawsPixels | NoBrushes | NormalColors, + Brush = DrawsPixmaps | DiverseBrushes | NormalColors, + Eraser = DrawsPixmaps | SquareBrushes | SwappedColors, + ColorWasher = WashesPixmaps | SquareBrushes | SwappedColors + }; + + kpToolPen (Mode mode, const QString &text, const QString &description, + int key, + kpMainWindow *mainWindow, const char *name); + kpToolPen (kpMainWindow *mainWindow); + virtual ~kpToolPen (); + + void setMode (Mode mode); + +private: + QString haventBegunDrawUserMessage () const; + +public: + virtual void begin (); + virtual void end (); + + virtual void beginDraw (); + virtual void hover (const QPoint &point); + virtual void globalDraw (); + virtual void draw (const QPoint &thisPoint, const QPoint &lastPoint, const QRect &); + virtual void cancelShape (); + virtual void releasedAllButtons (); + virtual void endDraw (const QPoint &, const QRect &); + +private slots: + virtual void slotForegroundColorChanged (const kpColor &col); + virtual void slotBackgroundColorChanged (const kpColor &col); + + void slotBrushChanged (const QPixmap &pixmap, bool isDiagonalLine); + void slotEraserSizeChanged (int size); + +private: + bool wash (QPainter *painter, QPainter *maskPainter, + const QImage &image, + const kpColor &colorToReplace, + const QRect &imageRect, int plotx, int ploty); + bool wash (QPainter *painter, QPainter *maskPainter, + const QImage &image, + const kpColor &colorToReplace, + const QRect &imageRect, const QRect &drawRect); + + kpColor color (int which); + + QPoint hotPoint () const; + QPoint hotPoint (int x, int y) const; + QPoint hotPoint (const QPoint &point) const; + QRect hotRect () const; + QRect hotRect (int x, int y) const; + QRect hotRect (const QPoint &point) const; + + Mode m_mode; + + void updateBrushCursor (bool recalc = true); + + kpToolWidgetBrush *m_toolWidgetBrush; + kpToolWidgetEraserSize *m_toolWidgetEraserSize; + QPixmap m_brushPixmap [2]; + QPixmap m_cursorPixmap; + bool m_brushIsDiagonalLine; + + kpToolPenCommand *m_currentCommand; +}; + +class kpToolPenCommand : public kpNamedCommand +{ +public: + kpToolPenCommand (const QString &name, kpMainWindow *mainWindow); + virtual ~kpToolPenCommand (); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + + // interface for KToolPen + void updateBoundingRect (const QPoint &point); + void updateBoundingRect (const QRect &rect); + void finalize (); + void cancel (); + +private: + void swapOldAndNew (); + + QPixmap m_pixmap; + QRect m_boundingRect; +}; + +#endif // __kptoolpen_h__ diff --git a/kolourpaint/tools/kptoolpolygon.cpp b/kolourpaint/tools/kptoolpolygon.cpp new file mode 100644 index 00000000..fb68745c --- /dev/null +++ b/kolourpaint/tools/kptoolpolygon.cpp @@ -0,0 +1,895 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_POLYGON 0 + +#include <kptoolpolygon.h> + +#include <float.h> +#include <math.h> + +#include <qbitmap.h> +#include <qcursor.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qpoint.h> +#include <qpushbutton.h> +#include <qrect.h> +#include <qtooltip.h> +#include <qvbuttongroup.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpcommandhistory.h> +#include <kpdocument.h> +#include <kpdefs.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kptemppixmap.h> +#include <kptooltoolbar.h> +#include <kptoolwidgetlinewidth.h> +#include <kpviewmanager.h> + + +#if DEBUG_KP_TOOL_POLYGON +static const char *pointArrayToString (const QPointArray &pointArray) +{ + static char string [1000]; + string [0] = '\0'; + + for (QPointArray::ConstIterator it = pointArray.begin (); + it != pointArray.end (); + it++) + { + QString ps = QString (" (%1, %2)").arg ((*it).x ()).arg ((*it).y ()); + const char *pss = ps.latin1 (); + if (strlen (string) + strlen (pss) + 1 > sizeof (string) / sizeof (string [0])) + break; + strcat (string, pss); + } + + return string; +} +#endif + + +static QPen makeMaskPen (const kpColor &color, int lineWidth, Qt::PenStyle lineStyle) +{ + return QPen (color.maskColor (), + lineWidth == 1 ? 0/*closer to looking width 1*/ : lineWidth, lineStyle, + Qt::RoundCap, Qt::RoundJoin); +} + +static QPen makePen (const kpColor &color, int lineWidth, Qt::PenStyle lineStyle) +{ + if (color.isOpaque ()) + { + return QPen (color.toQColor (), + lineWidth == 1 ? 0/*closer to looking width 1*/ : lineWidth, lineStyle, + Qt::RoundCap, Qt::RoundJoin); + } + else + return Qt::NoPen; +} + +static QBrush makeMaskBrush (const kpColor &foregroundColor, + const kpColor &backgroundColor, + kpToolWidgetFillStyle *toolWidgetFillStyle) +{ + if (toolWidgetFillStyle) + return toolWidgetFillStyle->maskBrush (foregroundColor, backgroundColor); + else + return Qt::NoBrush; +} + +static QBrush makeBrush (const kpColor &foregroundColor, + const kpColor &backgroundColor, + kpToolWidgetFillStyle *toolWidgetFillStyle) +{ + if (toolWidgetFillStyle) + return toolWidgetFillStyle->brush (foregroundColor, backgroundColor); + else + return Qt::NoBrush; +} + +static bool only1PixelInPointArray (const QPointArray &points) +{ + if (points.count () == 0) + return false; + + for (int i = 1; i < (int) points.count (); i++) + { + if (points [i] != points [0]) + return false; + } + + return true; +} + +static QPixmap pixmap (const QPixmap &oldPixmap, + const QPointArray &points, const QRect &rect, + const kpColor &foregroundColor, kpColor backgroundColor, + int lineWidth, Qt::PenStyle lineStyle, + kpToolWidgetFillStyle *toolWidgetFillStyle, + enum kpToolPolygon::Mode mode, bool final = true) +{ + // + // figure out points to draw relative to topLeft of oldPixmap + + QPointArray pointsInRect = points; + pointsInRect.detach (); + pointsInRect.translate (-rect.x (), -rect.y ()); + +#if DEBUG_KP_TOOL_POLYGON && 0 + kdDebug () << "kptoolpolygon.cpp: pixmap(): points=" << pointArrayToString (points) << endl; +#endif + + + // + // draw + + QPen pen = makePen (foregroundColor, lineWidth, lineStyle), + maskPen = makeMaskPen (foregroundColor, lineWidth, lineStyle); + QBrush brush = makeBrush (foregroundColor, backgroundColor, toolWidgetFillStyle), + maskBrush = makeMaskBrush (foregroundColor, backgroundColor, toolWidgetFillStyle); + + QPixmap pixmap = oldPixmap; + QBitmap maskBitmap; + + QPainter painter, maskPainter; + + 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 DEBUG_KP_TOOL_POLYGON && 0 + kdDebug () << "\tmaskPainter begin because:" << endl + << "\t\tpixmap.mask=" << pixmap.mask () << endl + << "\t\t(maskPenStyle!=NoPen)=" << (maskPen.style () != Qt::NoPen) << endl + << "\t\t(maskPenColor==trans)=" << (maskPen.color () == Qt::color0) << endl + << "\t\t(maskBrushStyle!=NoBrush)=" << (maskBrush.style () != Qt::NoBrush) << endl + << "\t\t(maskBrushColor==trans)=" << (maskBrush.color () == Qt::color0) << endl; + #endif + } + + if (pen.style () != Qt::NoPen || + brush.style () != Qt::NoBrush) + { + painter.begin (&pixmap); + painter.setPen (pen); + painter.setBrush (brush); + + #if DEBUG_KP_TOOL_POLYGON && 0 + kdDebug () << "\tpainter begin pen.rgb=" + << (int *) painter.pen ().color ().rgb () + << endl; + #endif + } + +#define PAINTER_CALL(cmd) \ +{ \ + if (painter.isActive ()) \ + painter . cmd ; \ + \ + if (maskPainter.isActive ()) \ + maskPainter . cmd ; \ +} + + // SYNC: Qt bug + if (only1PixelInPointArray (pointsInRect)) + { + PAINTER_CALL (drawPoint (pointsInRect [0])); + } + else + { + switch (mode) + { + case kpToolPolygon::Line: + case kpToolPolygon::Polyline: + PAINTER_CALL (drawPolyline (pointsInRect)); + break; + case kpToolPolygon::Polygon: + // TODO: why aren't the ends rounded? + PAINTER_CALL (drawPolygon (pointsInRect)); + + if (!final && 0/*HACK for TODO*/) + { + int count = pointsInRect.count (); + + if (count > 2) + { + if (painter.isActive ()) + { + QPen XORpen = painter.pen (); + XORpen.setColor (Qt::white); + + painter.setPen (XORpen); + painter.setRasterOp (Qt::XorROP); + } + + if (maskPainter.isActive ()) + { + QPen XORpen = maskPainter.pen (); + + // TODO??? + #if 0 + if (kpTool::isColorTransparent (foregroundColor)) + XORpen.setColor (Qt::color1/*opaque*/); + else + XORpen.setColor (Qt::color0/*transparent*/); + #endif + + maskPainter.setPen (XORpen); + } + + PAINTER_CALL (drawLine (pointsInRect [0], pointsInRect [count - 1])); + } + } + break; + case kpToolPolygon::Curve: + int numPoints = pointsInRect.count (); + QPointArray pa (4); + + pa [0] = pointsInRect [0]; + pa [3] = pointsInRect [1]; + + switch (numPoints) + { + case 2: + pa [1] = pointsInRect [0]; + pa [2] = pointsInRect [1]; + break; + case 3: + pa [1] = pa [2] = pointsInRect [2]; + break; + case 4: + pa [1] = pointsInRect [2]; + pa [2] = pointsInRect [3]; + } + + PAINTER_CALL (drawCubicBezier (pa)); + } + } +#undef PAINTER_CALL + + if (painter.isActive ()) + painter.end (); + + if (maskPainter.isActive ()) + maskPainter.end (); + + if (!maskBitmap.isNull ()) + pixmap.setMask (maskBitmap); + + return pixmap; +} + + +/* + * kpToolPolygon + */ + +kpToolPolygon::kpToolPolygon (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_toolWidgetFillStyle (0), + m_toolWidgetLineWidth (0) +{ +} + +kpToolPolygon::kpToolPolygon (kpMainWindow *mainWindow) + : kpTool (i18n ("Polygon"), i18n ("Draws polygons"), + Qt::Key_G, + mainWindow, "tool_polygon"), + m_mode (Polygon), + m_toolWidgetFillStyle (0), + m_toolWidgetLineWidth (0) +{ +} + +kpToolPolygon::~kpToolPolygon () +{ +} + +void kpToolPolygon::setMode (Mode m) +{ + m_mode = m; +} + + +// private +QString kpToolPolygon::haventBegunShapeUserMessage () const +{ + switch (m_mode) + { + case Line: + return i18n ("Drag to draw."); + case Polygon: + case Polyline: + return i18n ("Drag to draw the first line."); + case Curve: + return i18n ("Drag out the start and end points."); + default: + return QString::null; + } +} + +// virtual +void kpToolPolygon::begin () +{ + kpToolToolBar *tb = toolToolBar (); + +#if DEBUG_KP_TOOL_POLYGON + kdDebug () << "kpToolPolygon::begin() tb=" << tb << endl; +#endif + + if (tb) + { + if (m_mode == Polygon) + m_toolWidgetFillStyle = tb->toolWidgetFillStyle (); + else + m_toolWidgetFillStyle = 0; + + m_toolWidgetLineWidth = tb->toolWidgetLineWidth (); + + if (m_toolWidgetFillStyle) + { + connect (m_toolWidgetFillStyle, SIGNAL (fillStyleChanged (kpToolWidgetFillStyle::FillStyle)), + this, SLOT (slotFillStyleChanged (kpToolWidgetFillStyle::FillStyle))); + } + connect (m_toolWidgetLineWidth, SIGNAL (lineWidthChanged (int)), + this, SLOT (slotLineWidthChanged (int))); + + if (m_toolWidgetFillStyle) + m_toolWidgetFillStyle->show (); + m_toolWidgetLineWidth->show (); + + m_lineWidth = m_toolWidgetLineWidth->lineWidth (); + } + else + { + m_toolWidgetFillStyle = 0; + m_toolWidgetLineWidth = 0; + + m_lineWidth = 1; + } + + viewManager ()->setCursor (QCursor (CrossCursor)); + + m_originatingMouseButton = -1; + + setUserMessage (haventBegunShapeUserMessage ()); +} + +// virtual +void kpToolPolygon::end () +{ + endShape (); + + if (m_toolWidgetFillStyle) + { + disconnect (m_toolWidgetFillStyle, SIGNAL (fillStyleChanged (kpToolWidgetFillStyle::FillStyle)), + this, SLOT (slotFillStyleChanged (kpToolWidgetFillStyle::FillStyle))); + m_toolWidgetFillStyle = 0; + } + + if (m_toolWidgetLineWidth) + { + disconnect (m_toolWidgetLineWidth, SIGNAL (lineWidthChanged (int)), + this, SLOT (slotLineWidthChanged (int))); + m_toolWidgetLineWidth = 0; + } + + viewManager ()->unsetCursor (); +} + + +void kpToolPolygon::beginDraw () +{ +#if DEBUG_KP_TOOL_POLYGON + kdDebug () << "kpToolPolygon::beginDraw() m_points=" << pointArrayToString (m_points) + << ", startPoint=" << m_startPoint << endl; +#endif + + bool endedShape = false; + + // starting with a line... + if (m_points.count () == 0) + { + m_originatingMouseButton = m_mouseButton; + m_points.putPoints (m_points.count (), 2, + m_startPoint.x (), m_startPoint.y (), + m_startPoint.x (), m_startPoint.y ()); + } + // continuing poly* + else + { + if (m_mouseButton != m_originatingMouseButton) + { + m_mouseButton = m_originatingMouseButton; + endShape (); + endedShape = true; + } + else + { + int count = m_points.count (); + m_points.putPoints (count, 1, + m_startPoint.x (), m_startPoint.y ()); + + // start point = last end point; + // _not_ the new/current start point + // (which is disregarded in a poly* as only the end points count + // after the initial line) + // + // Curve Tool ignores m_startPoint (doesn't call applyModifiers()) + // after the initial has been defined. + m_startPoint = m_points [count - 1]; + } + } + +#if DEBUG_KP_TOOL_POLYGON + kdDebug () << "\tafterwards, m_points=" << pointArrayToString (m_points) << endl; +#endif + + if (!endedShape) + { + switch (m_mode) + { + case Line: + case Curve: + case Polygon: + case Polyline: + setUserMessage (cancelUserMessage ()); + break; + + default: + kdError () << "kpToolPolygon::beginDraw() shape" << endl; + break; + } + } +} + +// private +void kpToolPolygon::applyModifiers () +{ + int count = m_points.count (); + + m_toolLineStartPoint = m_startPoint; /* also correct for poly* tool (see beginDraw()) */ + m_toolLineEndPoint = m_currentPoint; + +#if DEBUG_KP_TOOL_POLYGON && 1 + kdDebug () << "kpToolPolygon::applyModifiers() #pts=" << count + << " line: startPt=" << m_toolLineStartPoint + << " endPt=" << m_toolLineEndPoint + << " modifiers: shift=" << m_shiftPressed + << " alt=" << m_altPressed + << " ctrl=" << m_controlPressed + << endl; +#endif + + // angles + if (m_shiftPressed || m_controlPressed) + { + int diffx = m_toolLineEndPoint.x () - m_toolLineStartPoint.x (); + int diffy = m_toolLineEndPoint.y () - m_toolLineStartPoint.y (); + + double ratio; + if (diffx == 0) + ratio = DBL_MAX; + else + ratio = fabs (double (diffy) / double (diffx)); + #if DEBUG_KP_TOOL_POLYGON && 1 + kdDebug () << "\tdiffx=" << diffx << " diffy=" << diffy + << " ratio=" << ratio + << endl; + #endif + + // Shift = 0, 45, 90 + // Alt = 0, 30, 60, 90 + // Shift + Alt = 0, 30, 45, 60, 90 + double angles [10]; // "ought to be enough for anybody" + int numAngles = 0; + angles [numAngles++] = 0; + if (m_controlPressed) + angles [numAngles++] = KP_PI / 6; + if (m_shiftPressed) + angles [numAngles++] = KP_PI / 4; + if (m_controlPressed) + angles [numAngles++] = KP_PI / 3; + angles [numAngles++] = KP_PI / 2; + + double angle = angles [numAngles - 1]; + for (int i = 0; i < numAngles - 1; i++) + { + double acceptingRatio = tan ((angles [i] + angles [i + 1]) / 2.0); + if (ratio < acceptingRatio) + { + angle = angles [i]; + break; + } + } + + // horizontal (dist from start !maintained) + if (fabs (KP_RADIANS_TO_DEGREES (angle) - 0) + < kpPixmapFX::AngleInDegreesEpsilon) + { + m_toolLineEndPoint = QPoint (m_toolLineEndPoint.x (), m_toolLineStartPoint.y ()); + } + // vertical (dist from start !maintained) + else if (fabs (KP_RADIANS_TO_DEGREES (angle) - 90) + < kpPixmapFX::AngleInDegreesEpsilon) + { + m_toolLineEndPoint = QPoint (m_toolLineStartPoint.x (), m_toolLineEndPoint.y ()); + } + // diagonal (dist from start maintained) + else + { + const double dist = sqrt (diffx * diffx + diffy * diffy); + + #define sgn(a) ((a)<0?-1:1) + // Round distances _before_ adding to any coordinate + // (ensures consistent rounding behaviour in x & y directions) + const int newdx = qRound (dist * cos (angle) * sgn (diffx)); + const int newdy = qRound (dist * sin (angle) * sgn (diffy)); + #undef sgn + + m_toolLineEndPoint = QPoint (m_toolLineStartPoint.x () + newdx, + m_toolLineStartPoint.y () + newdy); + + #if DEBUG_KP_TOOL_POLYGON && 1 + kdDebug () << "\t\tdiagonal line: dist=" << dist + << " angle=" << (angle * 180 / KP_PI) + << " endPoint=" << m_toolLineEndPoint + << endl; + #endif + } + } // if (m_shiftPressed || m_controlPressed) { + + // centring + if (m_altPressed && 0/*ALT is unreliable*/) + { + // start = start - diff + // = start - (end - start) + // = start - end + start + // = 2 * start - end + if (count == 2) + m_toolLineStartPoint += (m_toolLineStartPoint - m_toolLineEndPoint); + else + m_toolLineEndPoint += (m_toolLineEndPoint - m_toolLineStartPoint); + } // if (m_altPressed) { + + m_points [count - 2] = m_toolLineStartPoint; + m_points [count - 1] = m_toolLineEndPoint; + + m_toolLineRect = kpTool::neededRect (QRect (m_toolLineStartPoint, m_toolLineEndPoint).normalize (), + m_lineWidth); +} + +// virtual +void kpToolPolygon::draw (const QPoint &, const QPoint &, const QRect &) +{ + if (m_points.count () == 0) + return; + +#if DEBUG_KP_TOOL_POLYGON + kdDebug () << "kpToolPolygon::draw() m_points=" << pointArrayToString (m_points) + << ", endPoint=" << m_currentPoint << endl; +#endif + + bool drawingALine = (m_mode != Curve) || + (m_mode == Curve && m_points.count () == 2); + + if (drawingALine) + applyModifiers (); + else + m_points [m_points.count () - 1] = m_currentPoint; + +#if DEBUG_KP_TOOL_POLYGON + kdDebug () << "\tafterwards, m_points=" << pointArrayToString (m_points) << endl; +#endif + + updateShape (); + + if (drawingALine) + setUserShapePoints (m_toolLineStartPoint, m_toolLineEndPoint); + else + setUserShapePoints (m_currentPoint); +} + +// private slot +void kpToolPolygon::updateShape () +{ + if (m_points.count () == 0) + return; + + QRect boundingRect = kpTool::neededRect (m_points.boundingRect (), m_lineWidth); + +#if DEBUG_KP_TOOL_POLYGON + kdDebug () << "kpToolPolygon::updateShape() boundingRect=" + << boundingRect + << " lineWidth=" + << m_lineWidth + << endl; +#endif + + QPixmap oldPixmap = document ()->getPixmapAt (boundingRect); + QPixmap newPixmap = pixmap (oldPixmap, + m_points, boundingRect, + color (m_mouseButton), color (1 - m_mouseButton), + m_lineWidth, Qt::SolidLine, + m_toolWidgetFillStyle, + m_mode, false/*not final*/); + + viewManager ()->setFastUpdates (); + viewManager ()->setTempPixmap (kpTempPixmap (false/*always display*/, + kpTempPixmap::SetPixmap/*render mode*/, + boundingRect.topLeft (), + newPixmap)); + viewManager ()->restoreFastUpdates (); +} + +// virtual +void kpToolPolygon::cancelShape () +{ +#if 0 + endDraw (QPoint (), QRect ()); + commandHistory ()->undo (); +#else + viewManager ()->invalidateTempPixmap (); +#endif + m_points.resize (0); + + setUserMessage (i18n ("Let go of all the mouse buttons.")); +} + +void kpToolPolygon::releasedAllButtons () +{ + if (!hasBegunShape ()) + setUserMessage (haventBegunShapeUserMessage ()); + + // --- else case already handled by endDraw() --- +} + +// virtual +void kpToolPolygon::endDraw (const QPoint &, const QRect &) +{ +#if DEBUG_KP_TOOL_POLYGON + kdDebug () << "kpToolPolygon::endDraw() m_points=" << pointArrayToString (m_points) << endl; +#endif + + if (m_points.count () == 0) + return; + + if (m_mode == Line || + (m_mode == Curve && m_points.count () >= 4) || + m_points.count () >= 50) + { + endShape (); + } + else + { + switch (m_mode) + { + case Line: + kdError () << "kpToolPolygon::endDraw() - line not ended" << endl; + setUserMessage (); + break; + + case Polygon: + case Polyline: + if (m_points.isEmpty ()) + { + kdError () << "kpToolPolygon::endDraw() exception - poly without points" << endl; + setUserMessage (); + } + else + { + if (m_mouseButton == 0) + { + setUserMessage (i18n ("Left drag another line or right click to finish.")); + } + else + { + setUserMessage (i18n ("Right drag another line or left click to finish.")); + } + } + + break; + + case Curve: + if (m_points.size () == 2) + { + if (m_mouseButton == 0) + { + setUserMessage (i18n ("Left drag to set the first control point or right click to finish.")); + } + else + { + setUserMessage (i18n ("Right drag to set the first control point or left click to finish.")); + } + } + else if (m_points.size () == 3) + { + if (m_mouseButton == 0) + { + setUserMessage (i18n ("Left drag to set the last control point or right click to finish.")); + } + else + { + setUserMessage (i18n ("Right drag to set the last control point or left click to finish.")); + } + } + else + { + kdError () << "kpToolPolygon::endDraw() exception - points" << endl; + setUserMessage (); + } + + break; + + default: + kdError () << "kpToolPolygon::endDraw() - clueless" << endl; + setUserMessage (); + break; + } + } +} + +// public virtual +void kpToolPolygon::endShape (const QPoint &, const QRect &) +{ +#if DEBUG_KP_TOOL_POLYGON + kdDebug () << "kpToolPolygon::endShape() m_points=" << pointArrayToString (m_points) << endl; +#endif + + if (!hasBegunShape ()) + return; + + viewManager ()->invalidateTempPixmap (); + + QRect boundingRect = kpTool::neededRect (m_points.boundingRect (), m_lineWidth); + + kpToolPolygonCommand *lineCommand = + new kpToolPolygonCommand + (text (), + m_points, boundingRect, + color (m_mouseButton), color (1 - m_mouseButton), + m_lineWidth, Qt::SolidLine, + m_toolWidgetFillStyle, + document ()->getPixmapAt (boundingRect), + m_mode, + mainWindow ()); + + commandHistory ()->addCommand (lineCommand); + + m_points.resize (0); + setUserMessage (haventBegunShapeUserMessage ()); + +} + +// public virtual +bool kpToolPolygon::hasBegunShape () const +{ + return (m_points.count () > 0); +} + + +// public slot +void kpToolPolygon::slotLineWidthChanged (int width) +{ + m_lineWidth = width; + updateShape (); +} + +// public slot +void kpToolPolygon::slotFillStyleChanged (kpToolWidgetFillStyle::FillStyle /*fillStyle*/) +{ + updateShape (); +} + +// virtual protected slot +void kpToolPolygon::slotForegroundColorChanged (const kpColor &) +{ + updateShape (); +} + +// virtual protected slot +void kpToolPolygon::slotBackgroundColorChanged (const kpColor &) +{ + updateShape (); +} + + +/* + * kpToolPolygonCommand + */ + +kpToolPolygonCommand::kpToolPolygonCommand (const QString &name, + const QPointArray &points, + const QRect &normalizedRect, + const kpColor &foregroundColor, const kpColor &backgroundColor, + int lineWidth, Qt::PenStyle lineStyle, + kpToolWidgetFillStyle *toolWidgetFillStyle, + const QPixmap &originalArea, + enum kpToolPolygon::Mode mode, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_points (points), + m_normalizedRect (normalizedRect), + m_foregroundColor (foregroundColor), m_backgroundColor (backgroundColor), + m_lineWidth (lineWidth), m_lineStyle (lineStyle), + m_toolWidgetFillStyle (toolWidgetFillStyle), + m_originalArea (originalArea), + m_mode (mode) +{ + m_points.detach (); +} + +kpToolPolygonCommand::~kpToolPolygonCommand () +{ +} + + +// public virtual [base kpCommand] +int kpToolPolygonCommand::size () const +{ + return kpPixmapFX::pointArraySize (m_points) + + kpPixmapFX::pixmapSize (m_originalArea); +} + + +// public virtual [base kpCommand] +void kpToolPolygonCommand::execute () +{ + QPixmap p = pixmap (m_originalArea, + m_points, m_normalizedRect, + m_foregroundColor, m_backgroundColor, + m_lineWidth, m_lineStyle, + m_toolWidgetFillStyle, + m_mode); + document ()->setPixmapAt (p, m_normalizedRect.topLeft ()); +} + +// public virtual [base kpCommand] +void kpToolPolygonCommand::unexecute () +{ + document ()->setPixmapAt (m_originalArea, m_normalizedRect.topLeft ()); +} + +#include <kptoolpolygon.moc> diff --git a/kolourpaint/tools/kptoolpolygon.h b/kolourpaint/tools/kptoolpolygon.h new file mode 100644 index 00000000..456dc4c0 --- /dev/null +++ b/kolourpaint/tools/kptoolpolygon.h @@ -0,0 +1,157 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolpolygon_h__ +#define __kptoolpolygon_h__ + +#include <qbrush.h> +#include <qpen.h> +#include <qobject.h> +#include <qpixmap.h> +#include <qpoint.h> +#include <qpointarray.h> +#include <qrect.h> + +#include <kpcommandhistory.h> + +#include <kpcolor.h> +#include <kptool.h> +#include <kptoolwidgetfillstyle.h> + +class QMouseEvent; +class QPen; +class QPoint; +class QRect; +class QString; + +class kpView; +class kpDocument; +class kpMainWindow; + +class kpToolWidgetFillStyle; +class kpToolWidgetLineWidth; +class kpViewManager; + +class kpToolPolygon : public kpTool +{ +Q_OBJECT + +public: + enum Mode + { + Polygon, Polyline, Line, Curve + }; + + kpToolPolygon (Mode mode, const QString &text, const QString &description, + int key, + kpMainWindow *mainWindow, const char *name); + kpToolPolygon (kpMainWindow *mainWindow); + virtual ~kpToolPolygon (); + + void setMode (Mode mode); + + virtual bool careAboutModifierState () const { return true; } + +private: + QString haventBegunShapeUserMessage () const; + +public: + virtual void begin (); + virtual void end (); + + virtual void beginDraw (); + virtual void draw (const QPoint &, const QPoint &, const QRect &); + virtual void cancelShape (); + virtual void releasedAllButtons (); + virtual void endDraw (const QPoint &, const QRect &); + virtual void endShape (const QPoint & = QPoint (), const QRect & = QRect ()); + + virtual bool hasBegunShape () const; + +public slots: + void slotLineWidthChanged (int width); + void slotFillStyleChanged (kpToolWidgetFillStyle::FillStyle fillStyle); + +protected slots: + virtual void slotForegroundColorChanged (const kpColor &); + virtual void slotBackgroundColorChanged (const kpColor &); + +private slots: + void updateShape (); + +private: + Mode m_mode; + + kpToolWidgetFillStyle *m_toolWidgetFillStyle; + + int m_lineWidth; + kpToolWidgetLineWidth *m_toolWidgetLineWidth; + + int m_originatingMouseButton; + + void applyModifiers (); + + QPoint m_toolLineStartPoint, m_toolLineEndPoint; + QRect m_toolLineRect; + + QPointArray m_points; +}; + +class kpToolPolygonCommand : public kpNamedCommand +{ +public: + kpToolPolygonCommand (const QString &name, + const QPointArray &points, + const QRect &normalizedRect, + const kpColor &foregroundColor, const kpColor &backgroundColor, + int lineWidth, Qt::PenStyle lineStyle, + kpToolWidgetFillStyle *toolWidgetFillStyle, + const QPixmap &originalArea, + kpToolPolygon::Mode mode, + kpMainWindow *mainWindow); + virtual ~kpToolPolygonCommand (); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + QPointArray m_points; + QRect m_normalizedRect; + + kpColor m_foregroundColor, m_backgroundColor; + int m_lineWidth; + Qt::PenStyle m_lineStyle; + kpToolWidgetFillStyle *m_toolWidgetFillStyle; + + QPixmap m_originalArea; + kpToolPolygon::Mode m_mode; +}; + +#endif // __kptoolpolygon_h__ diff --git a/kolourpaint/tools/kptoolpolyline.cpp b/kolourpaint/tools/kptoolpolyline.cpp new file mode 100644 index 00000000..6299b5b7 --- /dev/null +++ b/kolourpaint/tools/kptoolpolyline.cpp @@ -0,0 +1,47 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <kptoolpolyline.h> + +#include <klocale.h> + + +kpToolPolyline::kpToolPolyline (kpMainWindow *mainWindow) + : kpToolPolygon (Polyline, + i18n ("Connected Lines"), + i18n ("Draws connected lines"), + Qt::Key_N, + mainWindow, "tool_polyline") +{ +} + +kpToolPolyline::~kpToolPolyline () +{ +} + +#include <kptoolpolyline.moc> diff --git a/kolourpaint/tools/kptoolpolyline.h b/kolourpaint/tools/kptoolpolyline.h new file mode 100644 index 00000000..f76a3959 --- /dev/null +++ b/kolourpaint/tools/kptoolpolyline.h @@ -0,0 +1,46 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolpolyline_h__ +#define __kptoolpolyline_h__ + +#include <kptoolpolygon.h> + +class kpMainWindow; + +class kpToolPolyline : public kpToolPolygon +{ +Q_OBJECT + +public: + kpToolPolyline (kpMainWindow *); + virtual ~kpToolPolyline (); +}; + +#endif // __kptoolpolyline_h__ + diff --git a/kolourpaint/tools/kptoolpreviewdialog.cpp b/kolourpaint/tools/kptoolpreviewdialog.cpp new file mode 100644 index 00000000..23149232 --- /dev/null +++ b/kolourpaint/tools/kptoolpreviewdialog.cpp @@ -0,0 +1,431 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_PREVIEW_DIALOG 0 + +#include <kptoolpreviewdialog.h> + +#include <qapplication.h> +#include <qlayout.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qpushbutton.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpcolor.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kpresizesignallinglabel.h> +#include <kpselection.h> + + +kpToolPreviewDialog::kpToolPreviewDialog (Features features, + bool reserveTopRow, + const QString &caption, + const QString &afterActionText, + bool actOnSelection, + kpMainWindow *parent, + const char *name) + : KDialogBase (parent, name, true/*modal*/, + caption, + KDialogBase::Ok | KDialogBase::Cancel), + m_afterActionText (afterActionText), + m_actOnSelection (actOnSelection), + m_mainWindow (parent), + m_dimensionsGroupBox (0), + m_afterTransformDimensionsLabel (0), + m_previewGroupBox (0), + m_previewPixmapLabel (0), + m_gridLayout (0) +{ + QWidget *baseWidget = new QWidget (this); + setMainWidget (baseWidget); + + + if (document ()) + { + m_oldWidth = document ()->width (actOnSelection); + m_oldHeight = document ()->height (actOnSelection); + } + else + { + m_oldWidth = m_oldHeight = 1; + } + + + if (features & Dimensions) + createDimensionsGroupBox (); + + if (features & Preview) + createPreviewGroupBox (); + + + m_gridLayout = new QGridLayout (baseWidget, 4, 2, + 0/*margin*/, spacingHint ()); + m_gridNumRows = reserveTopRow ? 1 : 0; + if (m_dimensionsGroupBox || m_previewGroupBox) + { + if (m_dimensionsGroupBox && m_previewGroupBox) + { + m_gridLayout->addWidget (m_dimensionsGroupBox, m_gridNumRows, 0); + m_gridLayout->addWidget (m_previewGroupBox, m_gridNumRows, 1); + + m_gridLayout->setColStretch (1, 1); + } + else if (m_dimensionsGroupBox) + { + m_gridLayout->addMultiCellWidget (m_dimensionsGroupBox, + m_gridNumRows, m_gridNumRows, 0, 1); + } + else if (m_previewGroupBox) + { + m_gridLayout->addMultiCellWidget (m_previewGroupBox, + m_gridNumRows, m_gridNumRows, 0, 1); + } + + m_gridLayout->setRowStretch (m_gridNumRows, 1); + m_gridNumRows++;; + } +} + +kpToolPreviewDialog::~kpToolPreviewDialog () +{ +} + + +// private +void kpToolPreviewDialog::createDimensionsGroupBox () +{ + m_dimensionsGroupBox = new QGroupBox (i18n ("Dimensions"), mainWidget ()); + + QLabel *originalLabel = new QLabel (i18n ("Original:"), m_dimensionsGroupBox); + QString originalDimensions; + if (document ()) + { + originalDimensions = i18n ("%1 x %2") + .arg (m_oldWidth) + .arg (m_oldHeight); + + // Stop the Dimensions Group Box from resizing so often + const QString minimumLengthString ("100000 x 100000"); + const int padLength = minimumLengthString.length (); + for (int i = originalDimensions.length (); i < padLength; i++) + originalDimensions += " "; + } + QLabel *originalDimensionsLabel = new QLabel (originalDimensions, m_dimensionsGroupBox); + + QLabel *afterTransformLabel = new QLabel (m_afterActionText, m_dimensionsGroupBox); + m_afterTransformDimensionsLabel = new QLabel (m_dimensionsGroupBox); + + + QGridLayout *dimensionsLayout = new QGridLayout (m_dimensionsGroupBox, + 2, 2, + marginHint () * 2, spacingHint ()); + + dimensionsLayout->addWidget (originalLabel, 0, 0, Qt::AlignBottom); + dimensionsLayout->addWidget (originalDimensionsLabel, 0, 1, Qt::AlignBottom); + dimensionsLayout->addWidget (afterTransformLabel, 1, 0, Qt::AlignTop); + dimensionsLayout->addWidget (m_afterTransformDimensionsLabel, 1, 1, Qt::AlignTop); +} + +// private +void kpToolPreviewDialog::createPreviewGroupBox () +{ + m_previewGroupBox = new QGroupBox (i18n ("Preview"), mainWidget ()); + + m_previewPixmapLabel = new kpResizeSignallingLabel (m_previewGroupBox); + m_previewPixmapLabel->setMinimumSize (150, 110); + connect (m_previewPixmapLabel, SIGNAL (resized ()), + this, SLOT (updatePreview ())); + + QPushButton *updatePushButton = new QPushButton (i18n ("&Update"), + m_previewGroupBox); + connect (updatePushButton, SIGNAL (clicked ()), + this, SLOT (slotUpdateWithWaitCursor ())); + + + QVBoxLayout *previewLayout = new QVBoxLayout (m_previewGroupBox, + marginHint () * 2, + QMAX (1, spacingHint () / 2)); + + previewLayout->addWidget (m_previewPixmapLabel, 1/*stretch*/); + previewLayout->addWidget (updatePushButton, 0/*stretch*/, Qt::AlignHCenter); +} + + +// protected +kpDocument *kpToolPreviewDialog::document () const +{ + return m_mainWindow ? m_mainWindow->document () : 0; +} + + +// protected +void kpToolPreviewDialog::addCustomWidgetToFront (QWidget *w) +{ + m_gridLayout->addMultiCellWidget (w, 0, 0, 0, 1); +} + +// protected +void kpToolPreviewDialog::addCustomWidget (QWidget *w) +{ + m_gridLayout->addMultiCellWidget (w, m_gridNumRows, m_gridNumRows, 0, 1); + m_gridNumRows++; +} + + +// private +void kpToolPreviewDialog::updateDimensions () +{ + if (!m_dimensionsGroupBox) + return; + + kpDocument *doc = document (); + if (!doc) + return; + + QSize newDim = newDimensions (); +#if DEBUG_KP_TOOL_PREVIEW_DIALOG + kdDebug () << "kpToolPreviewDialog::updateDimensions(): newDim=" << newDim << endl; +#endif + + QString newDimString = i18n ("%1 x %2") + .arg (newDim.width ()) + .arg (newDim.height ()); + m_afterTransformDimensionsLabel->setText (newDimString); +} + + +// public static +double kpToolPreviewDialog::aspectScale (int newWidth, int newHeight, + int oldWidth, int oldHeight) +{ + double widthScale = double (newWidth) / double (oldWidth); + double heightScale = double (newHeight) / double (oldHeight); + + // Keeps aspect ratio + return QMIN (widthScale, heightScale); +} + +// public static +int kpToolPreviewDialog::scaleDimension (int dimension, double scale, int min, int max) +{ + return QMAX (min, + QMIN (max, + qRound (dimension * scale))); +} + + +// private +void kpToolPreviewDialog::updateShrukenDocumentPixmap () +{ +#if DEBUG_KP_TOOL_PREVIEW_DIALOG + kdDebug () << "kpToolPreviewDialog::updateShrukenDocumentPixmap()" + << " shrunkenDocPixmap.size=" + << m_shrunkenDocumentPixmap.size () + << " previewPixmapLabelSizeWhenUpdatedPixmap=" + << m_previewPixmapLabelSizeWhenUpdatedPixmap + << " previewPixmapLabel.size=" + << m_previewPixmapLabel->size () + << endl; +#endif + + if (!m_previewGroupBox) + return; + + + kpDocument *doc = document (); + if (!doc || !doc->pixmap ()) + { + kdError () << "kpToolPreviewDialog::updateShrunkenDocumentPixmap() doc=" + << doc << endl; + return; + } + + if (m_shrunkenDocumentPixmap.isNull () || + m_previewPixmapLabel->size () != m_previewPixmapLabelSizeWhenUpdatedPixmap) + { + #if DEBUG_KP_TOOL_PREVIEW_DIALOG + kdDebug () << "\tupdating shrunkenDocPixmap" << endl; + #endif + + // TODO: Why the need to keep aspect ratio here? + // Isn't scaling the skewed result maintaining aspect enough? + double keepsAspectScale = aspectScale (m_previewPixmapLabel->width (), + m_previewPixmapLabel->height (), + m_oldWidth, + m_oldHeight); + + QPixmap pixmap; + + if (m_actOnSelection) + { + kpSelection sel = *doc->selection (); + if (!sel.pixmap ()) + sel.setPixmap (doc->getSelectedPixmap ()); + + pixmap = sel.transparentPixmap (); + } + else + { + pixmap = *doc->pixmap (); + } + + m_shrunkenDocumentPixmap = kpPixmapFX::scale ( + pixmap, + scaleDimension (m_oldWidth, + keepsAspectScale, + 1, m_previewPixmapLabel->width ()), + scaleDimension (m_oldHeight, + keepsAspectScale, + 1, m_previewPixmapLabel->height ())); + #if 0 + m_shrunkenDocumentPixmap = kpPixmapFX::scale ( + m_actOnSelection ? doc->getSelectedPixmap () : *doc->pixmap (), + m_previewPixmapLabel->width (), + m_previewPixmapLabel->height ()); + #endif + + m_previewPixmapLabelSizeWhenUpdatedPixmap = m_previewPixmapLabel->size (); + } +} + + +// private +void kpToolPreviewDialog::updatePreview () +{ +#if DEBUG_KP_TOOL_PREVIEW_DIALOG + kdDebug () << "kpToolPreviewDialog::updatePreview()" << endl; +#endif + + if (!m_previewGroupBox) + return; + + + kpDocument *doc = document (); + if (!doc) + return; + + updateShrukenDocumentPixmap (); + + if (!m_shrunkenDocumentPixmap.isNull ()) + { + QSize newDim = newDimensions (); + double keepsAspectScale = aspectScale (m_previewPixmapLabel->width (), + m_previewPixmapLabel->height (), + newDim.width (), + newDim.height ()); + + int targetWidth = scaleDimension (newDim.width (), + keepsAspectScale, + 1, // min + m_previewPixmapLabel->width ()); // max + int targetHeight = scaleDimension (newDim.height (), + keepsAspectScale, + 1, // min + m_previewPixmapLabel->height ()); // max + + // TODO: Some effects work directly on QImage; so could cache the + // QImage so that transformPixmap() is faster + QPixmap transformedShrunkenDocumentPixmap = + transformPixmap (m_shrunkenDocumentPixmap, targetWidth, targetHeight); + + QPixmap previewPixmap (m_previewPixmapLabel->width (), + m_previewPixmapLabel->height ()); + kpPixmapFX::fill (&previewPixmap, kpColor::transparent); + kpPixmapFX::setPixmapAt (&previewPixmap, + (previewPixmap.width () - transformedShrunkenDocumentPixmap.width ()) / 2, + (previewPixmap.height () - transformedShrunkenDocumentPixmap.height ()) / 2, + transformedShrunkenDocumentPixmap); + +#if DEBUG_KP_TOOL_PREVIEW_DIALOG + kdDebug () << "kpToolPreviewDialog::updatePreview ():" + << " shrunkenDocumentPixmap: w=" + << m_shrunkenDocumentPixmap.width () + << " h=" + << m_shrunkenDocumentPixmap.height () + << " previewPixmapLabel: w=" + << m_previewPixmapLabel->width () + << " h=" + << m_previewPixmapLabel->height () + << " transformedShrunkenDocumentPixmap: w=" + << transformedShrunkenDocumentPixmap.width () + << " h=" + << transformedShrunkenDocumentPixmap.height () + << " previewPixmap: w=" + << previewPixmap.width () + << " h=" + << previewPixmap.height () + << endl; +#endif + + m_previewPixmapLabel->setPixmap (previewPixmap); + + // immediate update esp. for expensive previews + m_previewPixmapLabel->repaint (false/*no erase*/); + +#if DEBUG_KP_TOOL_PREVIEW_DIALOG + kdDebug () << "\tafter QLabel::setPixmap() previewPixmapLabel: w=" + << m_previewPixmapLabel->width () + << " h=" + << m_previewPixmapLabel->height () + << endl; +#endif + } +} + + +// protected slot virtual +void kpToolPreviewDialog::slotUpdate () +{ +#if DEBUG_KP_TOOL_PREVIEW_DIALOG + kdDebug () << "kpToolPreviewDialog::slotUpdate()" << endl; +#endif + updateDimensions (); + updatePreview (); +} + +// protected slot virtual +void kpToolPreviewDialog::slotUpdateWithWaitCursor () +{ +#if DEBUG_KP_TOOL_PREVIEW_DIALOG + kdDebug () << "kpToolPreviewDialog::slotUpdateWithWaitCursor()" + << endl; +#endif + + QApplication::setOverrideCursor (Qt::waitCursor); + + slotUpdate (); + + QApplication::restoreOverrideCursor (); +} + + +#include <kptoolpreviewdialog.moc> diff --git a/kolourpaint/tools/kptoolpreviewdialog.h b/kolourpaint/tools/kptoolpreviewdialog.h new file mode 100644 index 00000000..35efdc38 --- /dev/null +++ b/kolourpaint/tools/kptoolpreviewdialog.h @@ -0,0 +1,131 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + +#ifndef __kp_tool_preview_dialog_h__ +#define __kp_tool_preview_dialog_h__ + + +#include <qpixmap.h> + +#include <kdialogbase.h> + + +class QLabel; +class QGridLayout; +class QGroupBox; + +class kpDocument; +class kpMainWindow; +class kpResizeSignallingLabel; + + +class kpToolPreviewDialog : public KDialogBase +{ +Q_OBJECT + +public: + enum Features + { + Dimensions = 1, Preview = 2, + AllFeatures = Dimensions | Preview + }; + + // You must call slotUpdate() in your constructor + kpToolPreviewDialog (Features features, + bool reserveTopRow, + // e.g. "Skew (Image|Selection)" + const QString &caption, + // (in the Dimensions Group Box) e.g. "After Skew:" + const QString &afterActionText, + bool actOnSelection, + kpMainWindow *parent, + const char *name = 0); + virtual ~kpToolPreviewDialog (); + +private: + void createDimensionsGroupBox (); + void createPreviewGroupBox (); + +public: + virtual bool isNoOp () const = 0; + +protected: + kpDocument *document () const; + + // All widgets must have mainWidget() as their parent + void addCustomWidgetToFront (QWidget *w); // see <reserveTopRow> in ctor + void addCustomWidget (QWidget *w); + void addCustomWidgetToBack (QWidget *w) + { + addCustomWidget (w); + } + + virtual QSize newDimensions () const = 0; + virtual QPixmap transformPixmap (const QPixmap &pixmap, + int targetWidth, int targetHeight) const = 0; + +private: + void updateDimensions (); + +public: + static double aspectScale (int newWidth, int newHeight, + int oldWidth, int oldHeight); + static int scaleDimension (int dimension, double scale, int min, int max); + +private: + void updateShrukenDocumentPixmap (); + +protected slots: + void updatePreview (); + + // Call this whenever a value (e.g. an angle) changes + // and the Dimensions & Preview need to be updated + virtual void slotUpdate (); + + virtual void slotUpdateWithWaitCursor (); + +protected: + QString m_afterActionText; + bool m_actOnSelection; + kpMainWindow *m_mainWindow; + + int m_oldWidth, m_oldHeight; + + QGroupBox *m_dimensionsGroupBox; + QLabel *m_afterTransformDimensionsLabel; + + QGroupBox *m_previewGroupBox; + kpResizeSignallingLabel *m_previewPixmapLabel; + QSize m_previewPixmapLabelSizeWhenUpdatedPixmap; + QPixmap m_shrunkenDocumentPixmap; + + QGridLayout *m_gridLayout; + int m_gridNumRows; +}; + + +#endif // __kp_tool_preview_dialog_h__ 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 <dang@kde.org> + 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> diff --git a/kolourpaint/tools/kptoolrectangle.h b/kolourpaint/tools/kptoolrectangle.h new file mode 100644 index 00000000..0fcf5ff4 --- /dev/null +++ b/kolourpaint/tools/kptoolrectangle.h @@ -0,0 +1,142 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolrectangle_h__ +#define __kptoolrectangle_h__ + +#include <qbrush.h> +#include <qpen.h> +#include <qpixmap.h> +#include <qpoint.h> +#include <qrect.h> + +#include <kpcommandhistory.h> + +#include <kptool.h> + +class QString; + +class kpColor; +class kpMainWindow; +class kpToolWidgetFillStyle; +class kpToolWidgetLineWidth; +class kpViewManager; + +class kpToolRectangle : public kpTool +{ +Q_OBJECT + +public: + // it turns out that these shapes are all really the same thing + // (same options, same feel) - the only real difference is the + // drawing functions (a one line change) + enum Mode {Rectangle, RoundedRectangle, Ellipse}; + + kpToolRectangle (Mode mode, + const QString &text, const QString &description, + int key, + kpMainWindow *mainWindow, + const char *name); + kpToolRectangle (kpMainWindow *); + virtual ~kpToolRectangle (); + + void setMode (Mode mode); + + virtual bool careAboutModifierState () const { return true; } + +private: + QString haventBegunDrawUserMessage () const; + +public: + virtual void begin (); + virtual void end (); + + virtual void beginDraw (); +private: + void updateShape (); +public: + virtual void draw (const QPoint &, const QPoint &, const QRect &); + virtual void cancelShape (); + virtual void releasedAllButtons (); + virtual void endDraw (const QPoint &, const QRect &); + +private slots: + void updatePens (); + void updateBrushes (); + + virtual void slotForegroundColorChanged (const kpColor &); + virtual void slotBackgroundColorChanged (const kpColor &); + + virtual void slotLineWidthChanged (); + virtual void slotFillStyleChanged (); + +private: + Mode m_mode; + + kpToolWidgetLineWidth *m_toolWidgetLineWidth; + kpToolWidgetFillStyle *m_toolWidgetFillStyle; + + void updatePen (int mouseButton); + QPen m_pen [2], m_maskPen [2]; + + void updateBrush (int mouseButton); + QBrush m_brush [2], m_maskBrush [2]; + + void applyModifiers (); + QPoint m_toolRectangleStartPoint, m_toolRectangleEndPoint; + QRect m_toolRectangleRectWithoutLineWidth, m_toolRectangleRect; +}; + +class kpToolRectangleCommand : public kpCommand +{ +public: + 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); + virtual ~kpToolRectangleCommand (); + + virtual QString name () const; + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + kpToolRectangle::Mode m_mode; + QPen m_pen, m_maskPen; + QBrush m_brush, m_maskBrush; + QRect m_rect; + QPoint m_startPoint, m_endPoint; + QPixmap *m_oldPixmapPtr; +}; + +#endif // __kptoolrectangle_h__ diff --git a/kolourpaint/tools/kptoolrectselection.cpp b/kolourpaint/tools/kptoolrectselection.cpp new file mode 100644 index 00000000..3726cbfe --- /dev/null +++ b/kolourpaint/tools/kptoolrectselection.cpp @@ -0,0 +1,46 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <kptoolrectselection.h> + +#include <klocale.h> + + +kpToolRectSelection::kpToolRectSelection (kpMainWindow *mainWindow) + : kpToolSelection (Rectangle, + i18n ("Selection (Rectangular)"), + i18n ("Makes a rectangular selection"), + Qt::Key_S, + mainWindow, "tool_rect_selection") +{ +} + +kpToolRectSelection::~kpToolRectSelection () +{ +} + diff --git a/kolourpaint/tools/kptoolrectselection.h b/kolourpaint/tools/kptoolrectselection.h new file mode 100644 index 00000000..0d66b7a5 --- /dev/null +++ b/kolourpaint/tools/kptoolrectselection.h @@ -0,0 +1,43 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolrectselection_h__ +#define __kptoolrectselection_h__ + +#include <kptoolselection.h> + +class kpMainWindow; + +class kpToolRectSelection : public kpToolSelection +{ +public: + kpToolRectSelection (kpMainWindow *); + virtual ~kpToolRectSelection (); +}; + +#endif // __kptoolrectselection_h__ diff --git a/kolourpaint/tools/kptoolresizescale.cpp b/kolourpaint/tools/kptoolresizescale.cpp new file mode 100644 index 00000000..ce9c9059 --- /dev/null +++ b/kolourpaint/tools/kptoolresizescale.cpp @@ -0,0 +1,1222 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_RESIZE_SCALE_COMMAND 0 +#define DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG 0 + + +#include <kptoolresizescale.h> + +#include <math.h> + +#include <qaccel.h> +#include <qapplication.h> +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <qhbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpoint.h> +#include <qpointarray.h> +#include <qpushbutton.h> +#include <qrect.h> +#include <qsize.h> +#include <qtoolbutton.h> +#include <qwhatsthis.h> +#include <qwmatrix.h> + +#include <kapplication.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kiconeffect.h> +#include <kiconloader.h> +#include <klocale.h> +#include <knuminput.h> + +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kpselection.h> +#include <kptool.h> + + +/* + * kpToolResizeScaleCommand + */ + +kpToolResizeScaleCommand::kpToolResizeScaleCommand (bool actOnSelection, + int newWidth, int newHeight, + Type type, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_actOnSelection (actOnSelection), + m_type (type), + m_backgroundColor (mainWindow ? mainWindow->backgroundColor () : kpColor::invalid), + m_oldSelection (0) +{ + kpDocument *doc = document (); + + m_oldWidth = doc->width (m_actOnSelection); + m_oldHeight = doc->height (m_actOnSelection); + + m_actOnTextSelection = (m_actOnSelection && + doc && doc->selection () && + doc->selection ()->isText ()); + + resize (newWidth, newHeight); + + // If we have a selection _border_ (but not a floating selection), + // then scale the selection with the document + m_scaleSelectionWithImage = (!m_actOnSelection && + (m_type == Scale || m_type == SmoothScale) && + document ()->selection () && + !document ()->selection ()->pixmap ()); +} + +kpToolResizeScaleCommand::~kpToolResizeScaleCommand () +{ + delete m_oldSelection; +} + + +// public virtual [base kpCommand] +QString kpToolResizeScaleCommand::name () const +{ + if (m_actOnSelection) + { + if (m_actOnTextSelection) + { + if (m_type == Resize) + return i18n ("Text: Resize Box"); + } + else + { + if (m_type == Scale) + return i18n ("Selection: Scale"); + else if (m_type == SmoothScale) + return i18n ("Selection: Smooth Scale"); + } + } + else + { + switch (m_type) + { + case Resize: + return i18n ("Resize"); + case Scale: + return i18n ("Scale"); + case SmoothScale: + return i18n ("Smooth Scale"); + } + } + + return QString::null; +} + +// public virtual [base kpCommand] +int kpToolResizeScaleCommand::size () const +{ + return kpPixmapFX::pixmapSize (m_oldPixmap) + + kpPixmapFX::pixmapSize (m_oldRightPixmap) + + kpPixmapFX::pixmapSize (m_oldBottomPixmap) + + (m_oldSelection ? m_oldSelection->size () : 0); +} + + +// public +int kpToolResizeScaleCommand::newWidth () const +{ + return m_newWidth; +} + +// public +void kpToolResizeScaleCommand::setNewWidth (int width) +{ + resize (width, newHeight ()); +} + + +// public +int kpToolResizeScaleCommand::newHeight () const +{ + return m_newHeight; +} + +// public +void kpToolResizeScaleCommand::setNewHeight (int height) +{ + resize (newWidth (), height); +} + + +// public +QSize kpToolResizeScaleCommand::newSize () const +{ + return QSize (newWidth (), newHeight ()); +} + +// public virtual +void kpToolResizeScaleCommand::resize (int width, int height) +{ + m_newWidth = width; + m_newHeight = height; + + m_isLosslessScale = ((m_type == Scale) && + (m_newWidth / m_oldWidth * m_oldWidth == m_newWidth) && + (m_newHeight / m_oldHeight * m_oldHeight == m_newHeight)); +} + + +// public +bool kpToolResizeScaleCommand::scaleSelectionWithImage () const +{ + return m_scaleSelectionWithImage; +} + + +// private +void kpToolResizeScaleCommand::scaleSelectionRegionWithDocument () +{ +#if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND + kdDebug () << "kpToolResizeScaleCommand::scaleSelectionRegionWithDocument" + << endl; +#endif + + if (!m_oldSelection) + { + kdError () << "kpToolResizeScaleCommand::scaleSelectionRegionWithDocument()" + << " without old sel" << endl; + return; + } + + if (m_oldSelection->pixmap ()) + { + kdError () << "kpToolResizeScaleCommand::scaleSelectionRegionWithDocument()" + << " old sel has pixmap" << endl; + return; + } + + + const double horizScale = double (m_newWidth) / double (m_oldWidth); + const double vertScale = double (m_newHeight) / double (m_oldHeight); + + const int newX = (int) (m_oldSelection->x () * horizScale); + const int newY = (int) (m_oldSelection->y () * vertScale); + + + QPointArray currentPoints = m_oldSelection->points (); + currentPoints.detach (); + + currentPoints.translate (-currentPoints.boundingRect ().x (), + -currentPoints.boundingRect ().y ()); + + // TODO: refactor into kpPixmapFX + QWMatrix scaleMatrix; + scaleMatrix.scale (horizScale, vertScale); + currentPoints = scaleMatrix.map (currentPoints); + + currentPoints.translate ( + -currentPoints.boundingRect ().x () + newX, + -currentPoints.boundingRect ().y () + newY); + + document ()->setSelection (kpSelection (currentPoints, QPixmap (), + m_oldSelection->transparency ())); + + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); +} + + +// public virtual [base kpCommand] +void kpToolResizeScaleCommand::execute () +{ +#if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND + kdDebug () << "kpToolResizeScaleCommand::execute() type=" + << (int) m_type + << " oldWidth=" << m_oldWidth + << " oldHeight=" << m_oldHeight + << " newWidth=" << m_newWidth + << " newHeight=" << m_newHeight + << endl; +#endif + + if (m_oldWidth == m_newWidth && m_oldHeight == m_newHeight) + return; + + if (m_type == Resize) + { + if (m_actOnSelection) + { + if (!m_actOnTextSelection) + { + kdError () << "kpToolResizeScaleCommand::execute() resizing sel doesn't make sense" << endl; + return; + } + else + { + QApplication::setOverrideCursor (Qt::waitCursor); + document ()->selection ()->textResize (m_newWidth, m_newHeight); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + + QApplication::restoreOverrideCursor (); + } + } + else + { + QApplication::setOverrideCursor (Qt::waitCursor); + + + if (m_newWidth < m_oldWidth) + { + m_oldRightPixmap = kpPixmapFX::getPixmapAt ( + *document ()->pixmap (), + QRect (m_newWidth, 0, + m_oldWidth - m_newWidth, m_oldHeight)); + } + + if (m_newHeight < m_oldHeight) + { + m_oldBottomPixmap = kpPixmapFX::getPixmapAt ( + *document ()->pixmap (), + QRect (0, m_newHeight, + m_newWidth, m_oldHeight - m_newHeight)); + } + + document ()->resize (m_newWidth, m_newHeight, m_backgroundColor); + + + QApplication::restoreOverrideCursor (); + } + } + else + { + QApplication::setOverrideCursor (Qt::waitCursor); + + + QPixmap oldPixmap = *document ()->pixmap (m_actOnSelection); + + if (!m_isLosslessScale) + m_oldPixmap = oldPixmap; + + QPixmap newPixmap = kpPixmapFX::scale (oldPixmap, m_newWidth, m_newHeight, + m_type == SmoothScale); + + + if (!m_oldSelection && document ()->selection ()) + { + // Save sel border + m_oldSelection = new kpSelection (*document ()->selection ()); + m_oldSelection->setPixmap (QPixmap ()); + } + + if (m_actOnSelection) + { + QRect newRect = QRect (m_oldSelection->x (), m_oldSelection->y (), + newPixmap.width (), newPixmap.height ()); + + // Not possible to retain non-rectangular selection borders on scale + // (think about e.g. a 45 deg line as part of the border & 2x scale) + document ()->setSelection ( + kpSelection (kpSelection::Rectangle, newRect, newPixmap, + m_oldSelection->transparency ())); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + else + { + document ()->setPixmap (newPixmap); + + if (m_scaleSelectionWithImage) + { + scaleSelectionRegionWithDocument (); + } + } + + + QApplication::restoreOverrideCursor (); + } +} + +// public virtual [base kpCommand] +void kpToolResizeScaleCommand::unexecute () +{ +#if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND + kdDebug () << "kpToolResizeScaleCommand::unexecute() type=" + << m_type << endl; +#endif + + if (m_oldWidth == m_newWidth && m_oldHeight == m_newHeight) + return; + + kpDocument *doc = document (); + if (!doc) + return; + + if (m_type == Resize) + { + if (m_actOnSelection) + { + if (!m_actOnTextSelection) + { + kdError () << "kpToolResizeScaleCommand::unexecute() resizing sel doesn't make sense" << endl; + return; + } + else + { + QApplication::setOverrideCursor (Qt::waitCursor); + document ()->selection ()->textResize (m_oldWidth, m_oldHeight); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + + QApplication::restoreOverrideCursor (); + } + } + else + { + QApplication::setOverrideCursor (Qt::waitCursor); + + + QPixmap newPixmap (m_oldWidth, m_oldHeight); + + kpPixmapFX::setPixmapAt (&newPixmap, QPoint (0, 0), + *doc->pixmap ()); + + if (m_newWidth < m_oldWidth) + { + kpPixmapFX::setPixmapAt (&newPixmap, + QPoint (m_newWidth, 0), + m_oldRightPixmap); + } + + if (m_newHeight < m_oldHeight) + { + kpPixmapFX::setPixmapAt (&newPixmap, + QPoint (0, m_newHeight), + m_oldBottomPixmap); + } + + doc->setPixmap (newPixmap); + + + QApplication::restoreOverrideCursor (); + } + } + else + { + QApplication::setOverrideCursor (Qt::waitCursor); + + + QPixmap oldPixmap; + + if (!m_isLosslessScale) + oldPixmap = m_oldPixmap; + else + oldPixmap = kpPixmapFX::scale (*doc->pixmap (m_actOnSelection), + m_oldWidth, m_oldHeight); + + + if (m_actOnSelection) + { + kpSelection oldSelection = *m_oldSelection; + oldSelection.setPixmap (oldPixmap); + doc->setSelection (oldSelection); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + else + { + doc->setPixmap (oldPixmap); + + if (m_scaleSelectionWithImage) + { + doc->setSelection (*m_oldSelection); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + } + + + QApplication::restoreOverrideCursor (); + } +} + + +/* + * kpToolResizeScaleDialog + */ + +#define SET_VALUE_WITHOUT_SIGNAL_EMISSION(knuminput_instance,value) \ +{ \ + knuminput_instance->blockSignals (true); \ + knuminput_instance->setValue (value); \ + knuminput_instance->blockSignals (false); \ +} + +#define IGNORE_KEEP_ASPECT_RATIO(cmd) \ +{ \ + m_ignoreKeepAspectRatio++; \ + cmd; \ + m_ignoreKeepAspectRatio--; \ +} + + +// private static +kpToolResizeScaleCommand::Type kpToolResizeScaleDialog::s_lastType = + kpToolResizeScaleCommand::Resize; + +// private static +double kpToolResizeScaleDialog::s_lastPercentWidth = 100, + kpToolResizeScaleDialog::s_lastPercentHeight = 100; + + +kpToolResizeScaleDialog::kpToolResizeScaleDialog (kpMainWindow *mainWindow) + : KDialogBase ((QWidget *) mainWindow, + 0/*name*/, + true/*modal*/, + i18n ("Resize / Scale")/*caption*/, + KDialogBase::Ok | KDialogBase::Cancel), + m_mainWindow (mainWindow), + m_ignoreKeepAspectRatio (0) +{ + // Using the percentage from last time become too confusing so disable for now + s_lastPercentWidth = 100, s_lastPercentHeight = 100; + + + QWidget *baseWidget = new QWidget (this); + setMainWidget (baseWidget); + + + createActOnBox (baseWidget); + createOperationGroupBox (baseWidget); + createDimensionsGroupBox (baseWidget); + + + QVBoxLayout *baseLayout = new QVBoxLayout (baseWidget, 0/*margin*/, spacingHint ()); + baseLayout->addWidget (m_actOnBox); + baseLayout->addWidget (m_operationGroupBox); + baseLayout->addWidget (m_dimensionsGroupBox); + + + slotActOnChanged (); + + m_newWidthInput->setEditFocus (); + + //enableButtonOK (!isNoOp ()); +} + +kpToolResizeScaleDialog::~kpToolResizeScaleDialog () +{ +} + + +// private +kpDocument *kpToolResizeScaleDialog::document () const +{ + return m_mainWindow ? m_mainWindow->document () : 0; +} + +// private +kpSelection *kpToolResizeScaleDialog::selection () const +{ + return document () ? document ()->selection () : 0; +} + + +// private +void kpToolResizeScaleDialog::createActOnBox (QWidget *baseWidget) +{ + m_actOnBox = new QHBox (baseWidget); + m_actOnBox->setSpacing (spacingHint () * 2); + + + m_actOnLabel = new QLabel (i18n ("Ac&t on:"), m_actOnBox); + m_actOnCombo = new KComboBox (m_actOnBox); + + + m_actOnLabel->setBuddy (m_actOnCombo); + + m_actOnCombo->insertItem (i18n ("Entire Image"), Image); + if (selection ()) + { + QString selName = i18n ("Selection"); + + if (selection ()->isText ()) + selName = i18n ("Text Box"); + + m_actOnCombo->insertItem (selName, Selection); + m_actOnCombo->setCurrentItem (Selection); + } + else + { + m_actOnLabel->setEnabled (false); + m_actOnCombo->setEnabled (false); + } + + + m_actOnBox->setStretchFactor (m_actOnCombo, 1); + + + connect (m_actOnCombo, SIGNAL (activated (int)), + this, SLOT (slotActOnChanged ())); +} + + +static QIconSet toolButtonIconSet (const QString &iconName) +{ + QIconSet iconSet = UserIconSet (iconName); + + + // No "disabled" pixmap is generated by UserIconSet() so generate it + // ourselves: + + QPixmap disabledIcon = KGlobal::iconLoader ()->iconEffect ()->apply ( + UserIcon (iconName), + KIcon::Toolbar, KIcon::DisabledState); + + const QPixmap iconSetNormalIcon = iconSet.pixmap (QIconSet::Small, + QIconSet::Normal); + + // I bet past or future versions of KIconEffect::apply() resize the + // disabled icon if we claim it's in group KIcon::Toolbar. So resize + // it to match the QIconSet::Normal icon, just in case. + disabledIcon = kpPixmapFX::scale (disabledIcon, + iconSetNormalIcon.width (), + iconSetNormalIcon.height (), + true/*smooth scale*/); + + + iconSet.setPixmap (disabledIcon, + QIconSet::Small, QIconSet::Disabled); + + return iconSet; +} + +static void toolButtonSetLook (QToolButton *button, + const QString &iconName, + const QString &name) +{ + button->setIconSet (toolButtonIconSet (iconName)); + button->setUsesTextLabel (true); + button->setTextLabel (name, false/*no tooltip*/); + button->setAccel (QAccel::shortcutKey (name)); + button->setFocusPolicy (QWidget::StrongFocus); + button->setToggleButton (true); +} + + +// private +void kpToolResizeScaleDialog::createOperationGroupBox (QWidget *baseWidget) +{ + m_operationGroupBox = new QGroupBox (i18n ("Operation"), baseWidget); + QWhatsThis::add (m_operationGroupBox, + i18n ("<qt>" + "<ul>" + "<li><b>Resize</b>: The size of the picture will be" + " increased" + " by creating new areas to the right and/or bottom" + " (filled in with the background color) or" + " decreased by cutting" + " it at the right and/or bottom.</li>" + + "<li><b>Scale</b>: The picture will be expanded" + " by duplicating pixels or squashed by dropping pixels.</li>" + + "<li><b>Smooth Scale</b>: This is the same as" + " <i>Scale</i> except that it blends neighboring" + " pixels to produce a smoother looking picture.</li>" + "</ul>" + "</qt>")); + + // TODO: ALT+R doesn't select the button. + m_resizeButton = new QToolButton (m_operationGroupBox); + toolButtonSetLook (m_resizeButton, + QString::fromLatin1 ("resize"), + i18n ("&Resize")); + + m_scaleButton = new QToolButton (m_operationGroupBox); + toolButtonSetLook (m_scaleButton, + QString::fromLatin1 ("scale"), + i18n ("&Scale")); + + m_smoothScaleButton = new QToolButton (m_operationGroupBox); + toolButtonSetLook (m_smoothScaleButton, + QString::fromLatin1 ("smooth_scale"), + i18n ("S&mooth Scale")); + + + //m_resizeLabel = new QLabel (i18n ("&Resize"), m_operationGroupBox); + //m_scaleLabel = new QLabel (i18n ("&Scale"), m_operationGroupBox); + //m_smoothScaleLabel = new QLabel (i18n ("S&mooth scale"), m_operationGroupBox); + + + //m_resizeLabel->setAlignment (m_resizeLabel->alignment () | Qt::ShowPrefix); + //m_scaleLabel->setAlignment (m_scaleLabel->alignment () | Qt::ShowPrefix); + //m_smoothScaleLabel->setAlignment (m_smoothScaleLabel->alignment () | Qt::ShowPrefix); + + + QButtonGroup *resizeScaleButtonGroup = new QButtonGroup (baseWidget); + resizeScaleButtonGroup->setExclusive (true); + resizeScaleButtonGroup->hide (); + + resizeScaleButtonGroup->insert (m_resizeButton); + resizeScaleButtonGroup->insert (m_scaleButton); + resizeScaleButtonGroup->insert (m_smoothScaleButton); + + + QGridLayout *operationLayout = new QGridLayout (m_operationGroupBox, + 1, 2, + marginHint () * 2/*don't overlap groupbox title*/, + spacingHint ()); + + operationLayout->addWidget (m_resizeButton, 0, 0, Qt::AlignCenter); + //operationLayout->addWidget (m_resizeLabel, 1, 0, Qt::AlignCenter); + + operationLayout->addWidget (m_scaleButton, 0, 1, Qt::AlignCenter); + //operationLayout->addWidget (m_scaleLabel, 1, 1, Qt::AlignCenter); + + operationLayout->addWidget (m_smoothScaleButton, 0, 2, Qt::AlignCenter); + //operationLayout->addWidget (m_smoothScaleLabel, 1, 2, Qt::AlignCenter); + + + connect (m_resizeButton, SIGNAL (toggled (bool)), + this, SLOT (slotTypeChanged ())); + connect (m_scaleButton, SIGNAL (toggled (bool)), + this, SLOT (slotTypeChanged ())); + connect (m_smoothScaleButton, SIGNAL (toggled (bool)), + this, SLOT (slotTypeChanged ())); +} + +// private +void kpToolResizeScaleDialog::createDimensionsGroupBox (QWidget *baseWidget) +{ + m_dimensionsGroupBox = new QGroupBox (i18n ("Dimensions"), baseWidget); + + QLabel *widthLabel = new QLabel (i18n ("Width:"), m_dimensionsGroupBox); + widthLabel->setAlignment (widthLabel->alignment () | Qt::AlignHCenter); + QLabel *heightLabel = new QLabel (i18n ("Height:"), m_dimensionsGroupBox); + heightLabel->setAlignment (heightLabel->alignment () | Qt::AlignHCenter); + + QLabel *originalLabel = new QLabel (i18n ("Original:"), m_dimensionsGroupBox); + m_originalWidthInput = new KIntNumInput ( + document ()->width ((bool) selection ()), + m_dimensionsGroupBox); + QLabel *xLabel0 = new QLabel (i18n ("x"), m_dimensionsGroupBox); + m_originalHeightInput = new KIntNumInput ( + document ()->height ((bool) selection ()), + m_dimensionsGroupBox); + + QLabel *newLabel = new QLabel (i18n ("&New:"), m_dimensionsGroupBox); + m_newWidthInput = new KIntNumInput (m_dimensionsGroupBox); + QLabel *xLabel1 = new QLabel (i18n ("x"), m_dimensionsGroupBox); + m_newHeightInput = new KIntNumInput (m_dimensionsGroupBox); + + QLabel *percentLabel = new QLabel (i18n ("&Percent:"), m_dimensionsGroupBox); + m_percentWidthInput = new KDoubleNumInput (0.01/*lower*/, 1000000/*upper*/, + 100/*value*/, 1/*step*/, + 2/*precision*/, + m_dimensionsGroupBox); + m_percentWidthInput->setSuffix (i18n ("%")); + QLabel *xLabel2 = new QLabel (i18n ("x"), m_dimensionsGroupBox); + m_percentHeightInput = new KDoubleNumInput (0.01/*lower*/, 1000000/*upper*/, + 100/*value*/, 1/*step*/, + 2/*precision*/, + m_dimensionsGroupBox); + m_percentHeightInput->setSuffix (i18n ("%")); + + m_keepAspectRatioCheckBox = new QCheckBox (i18n ("Keep &aspect ratio"), + m_dimensionsGroupBox); + + + m_originalWidthInput->setEnabled (false); + m_originalHeightInput->setEnabled (false); + originalLabel->setBuddy (m_originalWidthInput); + newLabel->setBuddy (m_newWidthInput); + m_percentWidthInput->setValue (s_lastPercentWidth); + m_percentHeightInput->setValue (s_lastPercentHeight); + percentLabel->setBuddy (m_percentWidthInput); + + + QGridLayout *dimensionsLayout = new QGridLayout (m_dimensionsGroupBox, + 5, 4, marginHint () * 2, spacingHint ()); + dimensionsLayout->setColStretch (1/*column*/, 1); + dimensionsLayout->setColStretch (3/*column*/, 1); + + + dimensionsLayout->addWidget (widthLabel, 0, 1); + dimensionsLayout->addWidget (heightLabel, 0, 3); + + dimensionsLayout->addWidget (originalLabel, 1, 0); + dimensionsLayout->addWidget (m_originalWidthInput, 1, 1); + dimensionsLayout->addWidget (xLabel0, 1, 2); + dimensionsLayout->addWidget (m_originalHeightInput, 1, 3); + + dimensionsLayout->addWidget (newLabel, 2, 0); + dimensionsLayout->addWidget (m_newWidthInput, 2, 1); + dimensionsLayout->addWidget (xLabel1, 2, 2); + dimensionsLayout->addWidget (m_newHeightInput, 2, 3); + + dimensionsLayout->addWidget (percentLabel, 3, 0); + dimensionsLayout->addWidget (m_percentWidthInput, 3, 1); + dimensionsLayout->addWidget (xLabel2, 3, 2); + dimensionsLayout->addWidget (m_percentHeightInput, 3, 3); + + dimensionsLayout->addMultiCellWidget (m_keepAspectRatioCheckBox, 4, 4, 0, 3); + dimensionsLayout->setRowStretch (4/*row*/, 1); + dimensionsLayout->setRowSpacing (4/*row*/, dimensionsLayout->rowSpacing (4) * 2); + + + connect (m_newWidthInput, SIGNAL (valueChanged (int)), + this, SLOT (slotWidthChanged (int))); + connect (m_newHeightInput, SIGNAL (valueChanged (int)), + this, SLOT (slotHeightChanged (int))); + + connect (m_percentWidthInput, SIGNAL (valueChanged (double)), + this, SLOT (slotPercentWidthChanged (double))); + connect (m_percentHeightInput, SIGNAL (valueChanged (double)), + this, SLOT (slotPercentHeightChanged (double))); + + connect (m_keepAspectRatioCheckBox, SIGNAL (toggled (bool)), + this, SLOT (setKeepAspectRatio (bool))); +} + + +// private +void kpToolResizeScaleDialog::widthFitHeightToAspectRatio () +{ + if (m_keepAspectRatioCheckBox->isChecked () && !m_ignoreKeepAspectRatio) + { + // width / height = oldWidth / oldHeight + // height = width * oldHeight / oldWidth + const int newHeight = qRound (double (imageWidth ()) * double (originalHeight ()) + / double (originalWidth ())); + IGNORE_KEEP_ASPECT_RATIO (m_newHeightInput->setValue (newHeight)); + } +} + +// private +void kpToolResizeScaleDialog::heightFitWidthToAspectRatio () +{ + if (m_keepAspectRatioCheckBox->isChecked () && !m_ignoreKeepAspectRatio) + { + // width / height = oldWidth / oldHeight + // width = height * oldWidth / oldHeight + const int newWidth = qRound (double (imageHeight ()) * double (originalWidth ()) + / double (originalHeight ())); + IGNORE_KEEP_ASPECT_RATIO (m_newWidthInput->setValue (newWidth)); + } +} + + +// private +bool kpToolResizeScaleDialog::resizeEnabled () const +{ + return (!actOnSelection () || + (actOnSelection () && selection ()->isText ())); +} + +// private +bool kpToolResizeScaleDialog::scaleEnabled () const +{ + return (!(actOnSelection () && selection ()->isText ())); +} + +// private +bool kpToolResizeScaleDialog::smoothScaleEnabled () const +{ + return scaleEnabled (); +} + + +// public slot +void kpToolResizeScaleDialog::slotActOnChanged () +{ +#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 + kdDebug () << "kpToolResizeScaleDialog::slotActOnChanged()" << endl; +#endif + + m_resizeButton->setEnabled (resizeEnabled ()); + //m_resizeLabel->setEnabled (resizeEnabled ()); + + m_scaleButton->setEnabled (scaleEnabled ()); + //m_scaleLabel->setEnabled (scaleEnabled ()); + + m_smoothScaleButton->setEnabled (smoothScaleEnabled ()); + //m_smoothScaleLabel->setEnabled (smoothScaleEnabled ()); + + + // TODO: somehow share logic with (resize|*scale)Enabled() + if (actOnSelection ()) + { + if (selection ()->isText ()) + { + m_resizeButton->setOn (true); + } + else + { + if (s_lastType == kpToolResizeScaleCommand::Scale) + m_scaleButton->setOn (true); + else + m_smoothScaleButton->setOn (true); + } + } + else + { + if (s_lastType == kpToolResizeScaleCommand::Resize) + m_resizeButton->setOn (true); + else if (s_lastType == kpToolResizeScaleCommand::Scale) + m_scaleButton->setOn (true); + else + m_smoothScaleButton->setOn (true); + } + + + m_originalWidthInput->setValue (originalWidth ()); + m_originalHeightInput->setValue (originalHeight ()); + + + m_newWidthInput->blockSignals (true); + m_newHeightInput->blockSignals (true); + + m_newWidthInput->setMinValue (actOnSelection () ? + selection ()->minimumWidth () : + 1); + m_newHeightInput->setMinValue (actOnSelection () ? + selection ()->minimumHeight () : + 1); + + m_newWidthInput->blockSignals (false); + m_newHeightInput->blockSignals (false); + + + IGNORE_KEEP_ASPECT_RATIO (slotPercentWidthChanged (m_percentWidthInput->value ())); + IGNORE_KEEP_ASPECT_RATIO (slotPercentHeightChanged (m_percentHeightInput->value ())); + + setKeepAspectRatio (m_keepAspectRatioCheckBox->isChecked ()); +} + + +// public slot +void kpToolResizeScaleDialog::slotTypeChanged () +{ + s_lastType = type (); +} + +// public slot +void kpToolResizeScaleDialog::slotWidthChanged (int width) +{ +#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 + kdDebug () << "kpToolResizeScaleDialog::slotWidthChanged(" + << width << ")" << endl; +#endif + const double newPercentWidth = double (width) * 100 / double (originalWidth ()); + + SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_percentWidthInput, newPercentWidth); + + widthFitHeightToAspectRatio (); + + //enableButtonOK (!isNoOp ()); + s_lastPercentWidth = newPercentWidth; +} + +// public slot +void kpToolResizeScaleDialog::slotHeightChanged (int height) +{ +#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 + kdDebug () << "kpToolResizeScaleDialog::slotHeightChanged(" + << height << ")" << endl; +#endif + const double newPercentHeight = double (height) * 100 / double (originalHeight ()); + + SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_percentHeightInput, newPercentHeight); + + heightFitWidthToAspectRatio (); + + //enableButtonOK (!isNoOp ()); + s_lastPercentHeight = newPercentHeight; +} + +// public slot +void kpToolResizeScaleDialog::slotPercentWidthChanged (double percentWidth) +{ +#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 + kdDebug () << "kpToolResizeScaleDialog::slotPercentWidthChanged(" + << percentWidth << ")" << endl; +#endif + + SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_newWidthInput, + qRound (percentWidth * originalWidth () / 100.0)); + + widthFitHeightToAspectRatio (); + + //enableButtonOK (!isNoOp ()); + s_lastPercentWidth = percentWidth; +} + +// public slot +void kpToolResizeScaleDialog::slotPercentHeightChanged (double percentHeight) +{ +#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 + kdDebug () << "kpToolResizeScaleDialog::slotPercentHeightChanged(" + << percentHeight << ")" << endl; +#endif + + SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_newHeightInput, + qRound (percentHeight * originalHeight () / 100.0)); + + heightFitWidthToAspectRatio (); + + //enableButtonOK (!isNoOp ()); + s_lastPercentHeight = percentHeight; +} + +// public +bool kpToolResizeScaleDialog::keepAspectRatio () const +{ + return m_keepAspectRatioCheckBox->isChecked (); +} + +// public slot +void kpToolResizeScaleDialog::setKeepAspectRatio (bool on) +{ +#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 + kdDebug () << "kpToolResizeScaleDialog::setKeepAspectRatio(" + << on << ")" << endl; +#endif + if (on != m_keepAspectRatioCheckBox->isChecked ()) + m_keepAspectRatioCheckBox->setChecked (on); + + if (on) + widthFitHeightToAspectRatio (); +} + +#undef IGNORE_KEEP_ASPECT_RATIO +#undef SET_VALUE_WITHOUT_SIGNAL_EMISSION + + +// private +int kpToolResizeScaleDialog::originalWidth () const +{ + return document ()->width (actOnSelection ()); +} + +// private +int kpToolResizeScaleDialog::originalHeight () const +{ + return document ()->height (actOnSelection ()); +} + + +// public +int kpToolResizeScaleDialog::imageWidth () const +{ + return m_newWidthInput->value (); +} + +// public +int kpToolResizeScaleDialog::imageHeight () const +{ + return m_newHeightInput->value (); +} + +// public +bool kpToolResizeScaleDialog::actOnSelection () const +{ + return (m_actOnCombo->currentItem () == Selection); +} + +// public +kpToolResizeScaleCommand::Type kpToolResizeScaleDialog::type () const +{ + if (m_resizeButton->isOn ()) + return kpToolResizeScaleCommand::Resize; + else if (m_scaleButton->isOn ()) + return kpToolResizeScaleCommand::Scale; + else + return kpToolResizeScaleCommand::SmoothScale; +} + +// public +bool kpToolResizeScaleDialog::isNoOp () const +{ + return (imageWidth () == originalWidth () && + imageHeight () == originalHeight ()); +} + + +// private slot virtual [base KDialogBase] +void kpToolResizeScaleDialog::slotOk () +{ + enum { eText, eSelection, eImage } actionTarget = eText; + + if (actOnSelection ()) + { + if (selection ()->isText ()) + { + actionTarget = eText; + } + else + { + actionTarget = eSelection; + } + } + else + { + actionTarget = eImage; + } + + + QString message, caption, continueButtonText; + + // Note: If eText, can't Scale nor SmoothScale. + // If eSelection, can't Resize. + + switch (type ()) + { + default: + case kpToolResizeScaleCommand::Resize: + if (actionTarget == eText) + { + message = + i18n ("<qt><p>Resizing the text box to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure you want to resize the text box?</p></qt>"); + + caption = i18n ("Resize Text Box?"); + continueButtonText = i18n ("R&esize Text Box"); + } + else if (actionTarget == eImage) + { + message = + i18n ("<qt><p>Resizing the image to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure you want to resize the image?</p></qt>"); + + caption = i18n ("Resize Image?"); + continueButtonText = i18n ("R&esize Image"); + } + + break; + + case kpToolResizeScaleCommand::Scale: + if (actionTarget == eImage) + { + message = + i18n ("<qt><p>Scaling the image to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure you want to scale the image?</p></qt>"); + + caption = i18n ("Scale Image?"); + continueButtonText = i18n ("Scal&e Image"); + } + else if (actionTarget == eSelection) + { + message = + i18n ("<qt><p>Scaling the selection to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure you want to scale the selection?</p></qt>"); + + caption = i18n ("Scale Selection?"); + continueButtonText = i18n ("Scal&e Selection"); + } + + break; + + case kpToolResizeScaleCommand::SmoothScale: + if (actionTarget == eImage) + { + message = + i18n ("<qt><p>Smooth Scaling the image to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure you want to smooth scale the image?</p></qt>"); + + caption = i18n ("Smooth Scale Image?"); + continueButtonText = i18n ("Smooth Scal&e Image"); + } + else if (actionTarget == eSelection) + { + message = + i18n ("<qt><p>Smooth Scaling the selection to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure you want to smooth scale the selection?</p></qt>"); + + caption = i18n ("Smooth Scale Selection?"); + continueButtonText = i18n ("Smooth Scal&e Selection"); + } + + break; + } + + + if (kpTool::warnIfBigImageSize (originalWidth (), + originalHeight (), + imageWidth (), imageHeight (), + message.arg (imageWidth ()).arg (imageHeight ()), + caption, + continueButtonText, + this)) + { + KDialogBase::slotOk (); + } +} + + +#include <kptoolresizescale.moc> diff --git a/kolourpaint/tools/kptoolresizescale.h b/kolourpaint/tools/kptoolresizescale.h new file mode 100644 index 00000000..e23b040e --- /dev/null +++ b/kolourpaint/tools/kptoolresizescale.h @@ -0,0 +1,196 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolresizescale_h__ +#define __kptoolresizescale_h__ + +#include <qpixmap.h> + +#include <kpcommandhistory.h> +#include <kdialogbase.h> + +#include <kpcolor.h> +#include <kpselection.h> + +class QCheckBox; +class QGroupBox; +class QHBox; +class QRadioButton; +class QSize; +class QString; +class QToolButton; + +class KComboBox; +class KDoubleNumInput; +class KIntNumInput; + +class kpDocument; +class kpMainWindow; +class kpViewManager; + +class kpToolResizeScaleCommand : public kpCommand +{ +public: + enum Type + { + Resize, Scale, SmoothScale + }; + + kpToolResizeScaleCommand (bool actOnSelection, + int newWidth, int newHeight, + Type type, + kpMainWindow *mainWindow); + virtual ~kpToolResizeScaleCommand (); + + virtual QString name () const; + virtual int size () const; + +public: + int newWidth () const; + void setNewWidth (int width); + + int newHeight () const; + void setNewHeight (int height); + + QSize newSize () const; + virtual void resize (int width, int height); + +public: + bool scaleSelectionWithImage () const; + +private: + void scaleSelectionRegionWithDocument (); + +public: + virtual void execute (); + virtual void unexecute (); + +protected: + bool m_actOnSelection; + int m_newWidth, m_newHeight; + Type m_type; + bool m_isLosslessScale; + bool m_scaleSelectionWithImage; + kpColor m_backgroundColor; + + int m_oldWidth, m_oldHeight; + bool m_actOnTextSelection; + QPixmap m_oldPixmap, m_oldRightPixmap, m_oldBottomPixmap; + kpSelection *m_oldSelection; +}; + +class kpToolResizeScaleDialog : public KDialogBase +{ +Q_OBJECT + +public: + kpToolResizeScaleDialog (kpMainWindow *mainWindow); + virtual ~kpToolResizeScaleDialog (); + + enum ActOn + { + Image, Selection + }; + +private: + static kpToolResizeScaleCommand::Type s_lastType; + static double s_lastPercentWidth, s_lastPercentHeight; + +private: + kpDocument *document () const; + kpSelection *selection () const; + + void createActOnBox (QWidget *baseWidget); + void createOperationGroupBox (QWidget *baseWidget); + void createDimensionsGroupBox (QWidget *baseWidget); + + void widthFitHeightToAspectRatio (); + void heightFitWidthToAspectRatio (); + +private: + bool resizeEnabled () const; + bool scaleEnabled () const; + bool smoothScaleEnabled () const; + +public slots: + void slotActOnChanged (); + void slotTypeChanged (); + + void slotWidthChanged (int width); + void slotHeightChanged (int height); + + void slotPercentWidthChanged (double percentWidth); + void slotPercentHeightChanged (double percentHeight); + +public: + // (refers only to the state of the checkbox - user of dialog does + // not have to do extra calculations) + bool keepAspectRatio () const; +public slots: + void setKeepAspectRatio (bool on); + +private: + int originalWidth () const; + int originalHeight () const; + +public: + int imageWidth () const; + int imageHeight () const; + bool actOnSelection () const; + kpToolResizeScaleCommand::Type type () const; + + bool isNoOp () const; + +private slots: + virtual void slotOk (); + +private: + kpMainWindow *m_mainWindow; + + QHBox *m_actOnBox; + QLabel *m_actOnLabel; + KComboBox *m_actOnCombo; + + QGroupBox *m_operationGroupBox; + QToolButton *m_resizeButton, + *m_scaleButton, + *m_smoothScaleButton; + QLabel *m_resizeLabel, + *m_scaleLabel, + *m_smoothScaleLabel; + + QGroupBox *m_dimensionsGroupBox; + KIntNumInput *m_originalWidthInput, *m_originalHeightInput, + *m_newWidthInput, *m_newHeightInput; + KDoubleNumInput *m_percentWidthInput, *m_percentHeightInput; + QCheckBox *m_keepAspectRatioCheckBox; + + int m_ignoreKeepAspectRatio; +}; + +#endif // __kptoolresizescale_h__ diff --git a/kolourpaint/tools/kptoolrotate.cpp b/kolourpaint/tools/kptoolrotate.cpp new file mode 100644 index 00000000..8a37b673 --- /dev/null +++ b/kolourpaint/tools/kptoolrotate.cpp @@ -0,0 +1,500 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_ROTATE 0 + + +#include <kptoolrotate.h> + +#include <qapplication.h> +#include <qbuttongroup.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qwmatrix.h> + +#include <kdebug.h> +#include <kiconloader.h> +#include <knuminput.h> +#include <klocale.h> + +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kpselection.h> +#include <kptool.h> +#include <kpviewmanager.h> + + +kpToolRotateCommand::kpToolRotateCommand (bool actOnSelection, + double angle, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_actOnSelection (actOnSelection), + m_angle (angle), + m_backgroundColor (mainWindow ? mainWindow->backgroundColor (actOnSelection) : kpColor::invalid), + m_losslessRotation (kpPixmapFX::isLosslessRotation (angle)) +{ +} + +kpToolRotateCommand::~kpToolRotateCommand () +{ +} + + +// public virtual [base kpCommand] +QString kpToolRotateCommand::name () const +{ + QString opName = i18n ("Rotate"); + + if (m_actOnSelection) + return i18n ("Selection: %1").arg (opName); + else + return opName; +} + + +// public virtual [base kpCommand] +int kpToolRotateCommand::size () const +{ + return kpPixmapFX::pixmapSize (m_oldPixmap) + + m_oldSelection.size (); +} + + +// public virtual [base kpCommand] +void kpToolRotateCommand::execute () +{ + kpDocument *doc = document (); + if (!doc) + return; + + + QApplication::setOverrideCursor (Qt::waitCursor); + + + if (!m_losslessRotation) + m_oldPixmap = *doc->pixmap (m_actOnSelection); + + + QPixmap newPixmap = kpPixmapFX::rotate (*doc->pixmap (m_actOnSelection), + m_angle, + m_backgroundColor); + + + if (m_actOnSelection) + { + kpSelection *sel = doc->selection (); + + // Save old selection + m_oldSelection = *sel; + m_oldSelection.setPixmap (QPixmap ()); + + + // Calculate new top left (so selection rotates about centre) + // (the Times2 trickery is used to reduce integer division error without + // resorting to the troublesome world of floating point) + QPoint oldCenterTimes2 (sel->x () * 2 + sel->width (), + sel->y () * 2 + sel->height ()); + QPoint newTopLeftTimes2 (oldCenterTimes2 - QPoint (newPixmap.width (), newPixmap.height ())); + QPoint newTopLeft (newTopLeftTimes2.x () / 2, newTopLeftTimes2.y () / 2); + + + // Calculate rotated points + QPointArray currentPoints = sel->points (); + currentPoints.translate (-currentPoints.boundingRect ().x (), + -currentPoints.boundingRect ().y ()); + QWMatrix rotateMatrix = kpPixmapFX::rotateMatrix (*doc->pixmap (m_actOnSelection), m_angle); + currentPoints = rotateMatrix.map (currentPoints); + currentPoints.translate (-currentPoints.boundingRect ().x () + newTopLeft.x (), + -currentPoints.boundingRect ().y () + newTopLeft.y ()); + + + if (currentPoints.boundingRect ().width () == newPixmap.width () && + currentPoints.boundingRect ().height () == newPixmap.height ()) + { + doc->setSelection (kpSelection (currentPoints, newPixmap, + m_oldSelection.transparency ())); + } + else + { + // TODO: fix the latter "victim of" problem in kpSelection by + // allowing the border width & height != pixmap width & height + // Or maybe autocrop? + #if DEBUG_KP_TOOL_ROTATE + kdDebug () << "kpToolRotateCommand::execute() currentPoints.boundingRect=" + << currentPoints.boundingRect () + << " newPixmap: w=" << newPixmap.width () + << " h=" << newPixmap.height () + << " (victim of rounding error and/or rotated-a-(rectangular)-pixmap-that-was-transparent-in-the-corners-making-sel-uselessly-bigger-than-needs-be)" + << endl; + #endif + doc->setSelection (kpSelection (kpSelection::Rectangle, + QRect (newTopLeft.x (), newTopLeft.y (), + newPixmap.width (), newPixmap.height ()), + newPixmap, + m_oldSelection.transparency ())); + } + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + else + doc->setPixmap (newPixmap); + + + QApplication::restoreOverrideCursor (); +} + +// public virtual [base kpCommand] +void kpToolRotateCommand::unexecute () +{ + kpDocument *doc = document (); + if (!doc) + return; + + + QApplication::setOverrideCursor (Qt::waitCursor); + + + QPixmap oldPixmap; + + if (!m_losslessRotation) + { + oldPixmap = m_oldPixmap; + m_oldPixmap.resize (0, 0); + } + else + { + oldPixmap = kpPixmapFX::rotate (*doc->pixmap (m_actOnSelection), + 360 - m_angle, + m_backgroundColor); + } + + + if (!m_actOnSelection) + doc->setPixmap (oldPixmap); + else + { + kpSelection oldSelection = m_oldSelection; + oldSelection.setPixmap (oldPixmap); + doc->setSelection (oldSelection); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + + + QApplication::restoreOverrideCursor (); +} + + +/* + * kpToolRotateDialog + */ + + +// private static +int kpToolRotateDialog::s_lastWidth = -1, + kpToolRotateDialog::s_lastHeight = -1; + +// private static +bool kpToolRotateDialog::s_lastIsClockwise = true; +int kpToolRotateDialog::s_lastAngleRadioButtonID = 3; +int kpToolRotateDialog::s_lastAngleCustom = 0; + + +kpToolRotateDialog::kpToolRotateDialog (bool actOnSelection, + kpMainWindow *mainWindow, + const char *name) + : kpToolPreviewDialog (kpToolPreviewDialog::AllFeatures, + false/*don't reserve top row*/, + actOnSelection ? i18n ("Rotate Selection") : i18n ("Rotate Image"), + i18n ("After Rotate:"), + actOnSelection, mainWindow, name) +{ + // Too confusing - disable for now + s_lastAngleRadioButtonID = 3; + s_lastAngleCustom = 0; + + + createDirectionGroupBox (); + createAngleGroupBox (); + + + if (s_lastWidth > 0 && s_lastHeight > 0) + resize (s_lastWidth, s_lastHeight); + + + slotAngleCustomRadioButtonToggled (m_angleCustomRadioButton->isChecked ()); + slotUpdate (); +} + +kpToolRotateDialog::~kpToolRotateDialog () +{ + s_lastWidth = width (), s_lastHeight = height (); +} + + +// private +void kpToolRotateDialog::createDirectionGroupBox () +{ + QGroupBox *directionGroupBox = new QGroupBox (i18n ("Direction"), mainWidget ()); + addCustomWidget (directionGroupBox); + + + QLabel *antiClockwisePixmapLabel = new QLabel (directionGroupBox); + antiClockwisePixmapLabel->setPixmap (UserIcon ("image_rotate_anticlockwise")); + + QLabel *clockwisePixmapLabel = new QLabel (directionGroupBox); + clockwisePixmapLabel->setPixmap (UserIcon ("image_rotate_clockwise")); + + + m_antiClockwiseRadioButton = new QRadioButton (i18n ("Cou&nterclockwise"), directionGroupBox); + m_clockwiseRadioButton = new QRadioButton (i18n ("C&lockwise"), directionGroupBox); + + + m_antiClockwiseRadioButton->setChecked (!s_lastIsClockwise); + m_clockwiseRadioButton->setChecked (s_lastIsClockwise); + + + QButtonGroup *buttonGroup = new QButtonGroup (directionGroupBox); + buttonGroup->hide (); + + buttonGroup->insert (m_antiClockwiseRadioButton); + buttonGroup->insert (m_clockwiseRadioButton); + + + QGridLayout *directionLayout = new QGridLayout (directionGroupBox, + 2, 2, marginHint () * 2, spacingHint ()); + directionLayout->addWidget (antiClockwisePixmapLabel, 0, 0, Qt::AlignCenter); + directionLayout->addWidget (clockwisePixmapLabel, 0, 1, Qt::AlignCenter); + directionLayout->addWidget (m_antiClockwiseRadioButton, 1, 0, Qt::AlignCenter); + directionLayout->addWidget (m_clockwiseRadioButton, 1, 1, Qt::AlignCenter); + + + connect (m_antiClockwiseRadioButton, SIGNAL (toggled (bool)), + this, SLOT (slotUpdate ())); + connect (m_clockwiseRadioButton, SIGNAL (toggled (bool)), + this, SLOT (slotUpdate ())); +} + +// private +void kpToolRotateDialog::createAngleGroupBox () +{ + QGroupBox *angleGroupBox = new QGroupBox (i18n ("Angle"), mainWidget ()); + addCustomWidget (angleGroupBox); + + + m_angle90RadioButton = new QRadioButton (i18n ("90 °rees"), angleGroupBox); + m_angle180RadioButton = new QRadioButton (i18n ("180 d&egrees"), angleGroupBox); + m_angle270RadioButton = new QRadioButton (i18n ("270 de&grees"), angleGroupBox); + + m_angleCustomRadioButton = new QRadioButton (i18n ("C&ustom:"), angleGroupBox); + m_angleCustomInput = new KIntNumInput (s_lastAngleCustom, angleGroupBox); + m_angleCustomInput->setMinValue (-359); + m_angleCustomInput->setMaxValue (+359); + QLabel *degreesLabel = new QLabel (i18n ("degrees"), angleGroupBox); + + + m_angleButtonGroup = new QButtonGroup (angleGroupBox); + m_angleButtonGroup->hide (); + + m_angleButtonGroup->insert (m_angle90RadioButton); + m_angleButtonGroup->insert (m_angle180RadioButton); + m_angleButtonGroup->insert (m_angle270RadioButton); + + m_angleButtonGroup->insert (m_angleCustomRadioButton); + + m_angleButtonGroup->setButton (s_lastAngleRadioButtonID); + + + QGridLayout *angleLayout = new QGridLayout (angleGroupBox, + 6, 3, + marginHint () * 2, spacingHint ()); + + angleLayout->addMultiCellWidget (m_angle90RadioButton, 0, 0, 0, 2); + angleLayout->addMultiCellWidget (m_angle180RadioButton, 1, 1, 0, 2); + angleLayout->addMultiCellWidget (m_angle270RadioButton, 2, 2, 0, 2); + + angleLayout->addWidget (m_angleCustomRadioButton, 3, 0); + angleLayout->addWidget (m_angleCustomInput, 3, 1); + angleLayout->addWidget (degreesLabel, 3, 2); + + angleLayout->setColStretch (1, 2); // Stretch Custom Angle Input + + + connect (m_angle90RadioButton, SIGNAL (toggled (bool)), + this, SLOT (slotUpdate ())); + connect (m_angle180RadioButton, SIGNAL (toggled (bool)), + this, SLOT (slotUpdate ())); + connect (m_angle270RadioButton, SIGNAL (toggled (bool)), + this, SLOT (slotUpdate ())); + + connect (m_angleCustomRadioButton, SIGNAL (toggled (bool)), + this, SLOT (slotAngleCustomRadioButtonToggled (bool))); + connect (m_angleCustomRadioButton, SIGNAL (toggled (bool)), + this, SLOT (slotUpdate ())); + + connect (m_angleCustomInput, SIGNAL (valueChanged (int)), + this, SLOT (slotUpdate ())); +} + + +// public virtual [base kpToolPreviewDialog] +bool kpToolRotateDialog::isNoOp () const +{ + return (angle () == 0); +} + +// public +int kpToolRotateDialog::angle () const +{ + int retAngle; + + + if (m_angle90RadioButton->isChecked ()) + retAngle = 90; + else if (m_angle180RadioButton->isChecked ()) + retAngle = 180; + else if (m_angle270RadioButton->isChecked ()) + retAngle = 270; + else // if (m_angleCustomRadioButton->isChecked ()) + retAngle = m_angleCustomInput->value (); + + + if (m_antiClockwiseRadioButton->isChecked ()) + retAngle *= -1; + + + if (retAngle < 0) + retAngle += ((0 - retAngle) / 360 + 1) * 360; + + if (retAngle >= 360) + retAngle -= ((retAngle - 360) / 360 + 1) * 360; + + + return retAngle; +} + + +// private virtual [base kpToolPreviewDialog] +QSize kpToolRotateDialog::newDimensions () const +{ + QWMatrix matrix = kpPixmapFX::rotateMatrix (m_oldWidth, m_oldHeight, angle ()); + // TODO: Should we be using QWMatrix::Areas? + QRect rect = matrix.map (QRect (0, 0, m_oldWidth, m_oldHeight)); + return rect.size (); +} + +// private virtual [base kpToolPreviewDialog] +QPixmap kpToolRotateDialog::transformPixmap (const QPixmap &pixmap, + int targetWidth, int targetHeight) const +{ + return kpPixmapFX::rotate (pixmap, angle (), + m_mainWindow ? m_mainWindow->backgroundColor (m_actOnSelection) : kpColor::invalid, + targetWidth, targetHeight); +} + + +// private slot +void kpToolRotateDialog::slotAngleCustomRadioButtonToggled (bool isChecked) +{ + m_angleCustomInput->setEnabled (isChecked); + + if (isChecked) + m_angleCustomInput->setEditFocus (); +} + +// private slot virtual [base kpToolPreviewDialog] +void kpToolRotateDialog::slotUpdate () +{ + s_lastIsClockwise = m_clockwiseRadioButton->isChecked (); + s_lastAngleRadioButtonID = m_angleButtonGroup->selectedId (); + s_lastAngleCustom = m_angleCustomInput->value (); + + kpToolPreviewDialog::slotUpdate (); +} + + +// private slot virtual [base KDialogBase] +void kpToolRotateDialog::slotOk () +{ + QString message, caption, continueButtonText; + + if (document ()->selection ()) + { + if (!document ()->selection ()->isText ()) + { + message = + i18n ("<qt><p>Rotating the selection to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure want to rotate the selection?</p></qt>"); + + caption = i18n ("Rotate Selection?"); + continueButtonText = i18n ("Rotat&e Selection"); + } + } + else + { + message = + i18n ("<qt><p>Rotating the image to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure want to rotate the image?</p></qt>"); + + caption = i18n ("Rotate Image?"); + continueButtonText = i18n ("Rotat&e Image"); + } + + + const int newWidth = newDimensions ().width (); + const int newHeight = newDimensions ().height (); + + if (kpTool::warnIfBigImageSize (m_oldWidth, + m_oldHeight, + newWidth, newHeight, + message.arg (newWidth).arg (newHeight), + caption, + continueButtonText, + this)) + { + KDialogBase::slotOk (); + } +} + +#include <kptoolrotate.moc> diff --git a/kolourpaint/tools/kptoolrotate.h b/kolourpaint/tools/kptoolrotate.h new file mode 100644 index 00000000..887473dc --- /dev/null +++ b/kolourpaint/tools/kptoolrotate.h @@ -0,0 +1,129 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolrotate_h__ +#define __kptoolrotate_h__ + +#include <qpixmap.h> +#include <qpoint.h> + +#include <kdialogbase.h> + +#include <kpcolor.h> +#include <kpcommandhistory.h> +#include <kpselection.h> +#include <kptoolpreviewdialog.h> + + +class QButtonGroup; +class QRadioButton; +class QString; + +class KIntNumInput; + +class kpDocument; +class kpViewManager; +class kpMainWindow; + + +class kpToolRotateCommand : public kpCommand +{ +public: + kpToolRotateCommand (bool actOnSelection, + double angle, // 0 <= angle < 360 (clockwise) + kpMainWindow *mainWindow); + virtual ~kpToolRotateCommand (); + + virtual QString name () const; + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + bool m_actOnSelection; + double m_angle; + + kpColor m_backgroundColor; + + bool m_losslessRotation; + QPixmap m_oldPixmap; + kpSelection m_oldSelection; +}; + + +class kpToolRotateDialog : public kpToolPreviewDialog +{ +Q_OBJECT + +public: + kpToolRotateDialog (bool actOnSelection, + kpMainWindow *parent, + const char *name = 0); + virtual ~kpToolRotateDialog (); + +private: + static int s_lastWidth, s_lastHeight; + static bool s_lastIsClockwise; + static int s_lastAngleRadioButtonID; + static int s_lastAngleCustom; + + void createDirectionGroupBox (); + void createAngleGroupBox (); + +public: + virtual bool isNoOp () const; + int angle () const; // 0 <= angle < 360 (clockwise); + +private: + virtual QSize newDimensions () const; + virtual QPixmap transformPixmap (const QPixmap &pixmap, + int targetWidth, int targetHeight) const; + +private slots: + void slotAngleCustomRadioButtonToggled (bool isChecked); + virtual void slotUpdate (); + +private slots: + virtual void slotOk (); + +private: + QRadioButton *m_antiClockwiseRadioButton, + *m_clockwiseRadioButton; + + QButtonGroup *m_angleButtonGroup; + QRadioButton *m_angle90RadioButton, + *m_angle180RadioButton, + *m_angle270RadioButton, + *m_angleCustomRadioButton; + KIntNumInput *m_angleCustomInput; +}; + + +#endif // __kptoolrotate_h__ diff --git a/kolourpaint/tools/kptoolroundedrectangle.cpp b/kolourpaint/tools/kptoolroundedrectangle.cpp new file mode 100644 index 00000000..b0f4ba05 --- /dev/null +++ b/kolourpaint/tools/kptoolroundedrectangle.cpp @@ -0,0 +1,45 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#include <klocale.h> +#include <kptoolroundedrectangle.h> + +kpToolRoundedRectangle::kpToolRoundedRectangle (kpMainWindow *mainWindow) + : kpToolRectangle (RoundedRectangle, + i18n ("Rounded Rectangle"), + i18n ("Draws rectangles and squares with rounded corners"), + Qt::Key_U, + mainWindow, "tool_rounded_rectangle") +{ +} + +kpToolRoundedRectangle::~kpToolRoundedRectangle () +{ +} + +#include <kptoolroundedrectangle.moc> diff --git a/kolourpaint/tools/kptoolroundedrectangle.h b/kolourpaint/tools/kptoolroundedrectangle.h new file mode 100644 index 00000000..924c1b34 --- /dev/null +++ b/kolourpaint/tools/kptoolroundedrectangle.h @@ -0,0 +1,45 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptoolroundedrectangle_h__ +#define __kptoolroundedrectangle_h__ + +#include <kptoolrectangle.h> + +class kpMainWindow; + +class kpToolRoundedRectangle : public kpToolRectangle +{ +Q_OBJECT + +public: + kpToolRoundedRectangle (kpMainWindow *); + virtual ~kpToolRoundedRectangle (); +}; + +#endif // __kptoolroundedrectangle_h__ diff --git a/kolourpaint/tools/kptoolselection.cpp b/kolourpaint/tools/kptoolselection.cpp new file mode 100644 index 00000000..f664f01b --- /dev/null +++ b/kolourpaint/tools/kptoolselection.cpp @@ -0,0 +1,2371 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_SELECTION 0 + + +#include <kptoolselection.h> + +#include <qapplication.h> +#include <qbitmap.h> +#include <qcursor.h> +#include <qpainter.h> +#include <qpopupmenu.h> +#include <qtimer.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpcommandhistory.h> +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kpselection.h> +#include <kptooltoolbar.h> +#include <kptoolwidgetopaqueortransparent.h> +#include <kpview.h> +#include <kpviewmanager.h> + + +kpToolSelection::kpToolSelection (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_currentPullFromDocumentCommand (0), + m_currentMoveCommand (0), + m_currentResizeScaleCommand (0), + m_toolWidgetOpaqueOrTransparent (0), + m_currentCreateTextCommand (0), + m_createNOPTimer (new QTimer (this)), + m_RMBMoveUpdateGUITimer (new QTimer (this)) +{ + connect (m_createNOPTimer, SIGNAL (timeout ()), + this, SLOT (delayedDraw ())); + connect (m_RMBMoveUpdateGUITimer, SIGNAL (timeout ()), + this, SLOT (slotRMBMoveUpdateGUI ())); +} + +kpToolSelection::~kpToolSelection () +{ +} + + +// private +void kpToolSelection::pushOntoDocument () +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelection::pushOntoDocument() CALLED" << endl; +#endif + mainWindow ()->slotDeselect (); +} + + +// protected +bool kpToolSelection::onSelectionToMove () const +{ + kpView *v = viewManager ()->viewUnderCursor (); + if (!v) + return 0; + + return v->mouseOnSelectionToMove (m_currentViewPoint); +} + +// protected +int kpToolSelection::onSelectionResizeHandle () const +{ + kpView *v = viewManager ()->viewUnderCursor (); + if (!v) + return 0; + + return v->mouseOnSelectionResizeHandle (m_currentViewPoint); +} + +// protected +bool kpToolSelection::onSelectionToSelectText () const +{ + kpView *v = viewManager ()->viewUnderCursor (); + if (!v) + return 0; + + return v->mouseOnSelectionToSelectText (m_currentViewPoint); +} + + +// public +QString kpToolSelection::haventBegunDrawUserMessage () const +{ +#if DEBUG_KP_TOOL_SELECTION && 0 + kdDebug () << "kpToolSelection::haventBegunDrawUserMessage()" + " cancelledShapeButStillHoldingButtons=" + << m_cancelledShapeButStillHoldingButtons + << endl; +#endif + + if (m_cancelledShapeButStillHoldingButtons) + return i18n ("Let go of all the mouse buttons."); + + kpSelection *sel = document ()->selection (); + if (sel && onSelectionResizeHandle () && !controlOrShiftPressed ()) + { + if (m_mode == Text) + return i18n ("Left drag to resize text box."); + else + return i18n ("Left drag to scale selection."); + } + else if (sel && sel->contains (m_currentPoint)) + { + if (m_mode == Text) + { + if (onSelectionToSelectText () && !controlOrShiftPressed ()) + return i18n ("Left click to change cursor position."); + else + return i18n ("Left drag to move text box."); + } + else + { + return i18n ("Left drag to move selection."); + } + } + else + { + if (m_mode == Text) + return i18n ("Left drag to create text box."); + else + return i18n ("Left drag to create selection."); + } +} + + +// virtual +void kpToolSelection::begin () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelection::begin()" << endl; +#endif + + kpToolToolBar *tb = toolToolBar (); + + if (tb) + { + m_toolWidgetOpaqueOrTransparent = tb->toolWidgetOpaqueOrTransparent (); + + if (m_toolWidgetOpaqueOrTransparent) + { + connect (m_toolWidgetOpaqueOrTransparent, SIGNAL (isOpaqueChanged (bool)), + this, SLOT (slotIsOpaqueChanged ())); + m_toolWidgetOpaqueOrTransparent->show (); + } + } + else + { + m_toolWidgetOpaqueOrTransparent = 0; + } + + viewManager ()->setQueueUpdates (); + { + viewManager ()->setSelectionBorderVisible (true); + viewManager ()->setSelectionBorderFinished (true); + } + viewManager ()->restoreQueueUpdates (); + + m_startDragFromSelectionTopLeft = QPoint (); + m_dragType = Unknown; + m_dragHasBegun = false; + m_hadSelectionBeforeDrag = false; // arbitrary + m_resizeScaleType = 0; + + m_currentPullFromDocumentCommand = 0; + m_currentMoveCommand = 0; + m_currentResizeScaleCommand = 0; + m_currentCreateTextCommand = 0; + + m_cancelledShapeButStillHoldingButtons = false; + + setUserMessage (haventBegunDrawUserMessage ()); +} + +// virtual +void kpToolSelection::end () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelection::end()" << endl; +#endif + + if (document ()->selection ()) + pushOntoDocument (); + + if (m_toolWidgetOpaqueOrTransparent) + { + disconnect (m_toolWidgetOpaqueOrTransparent, SIGNAL (isOpaqueChanged (bool)), + this, SLOT (slotIsOpaqueChanged ())); + m_toolWidgetOpaqueOrTransparent = 0; + } + + viewManager ()->unsetCursor (); +} + +// virtual +void kpToolSelection::reselect () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelection::reselect()" << endl; +#endif + + if (document ()->selection ()) + pushOntoDocument (); +} + + +// virtual +void kpToolSelection::beginDraw () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelection::beginDraw() m_startPoint=" + << m_startPoint + << " QCursor::pos() view startPoint=" + << m_viewUnderStartPoint->mapFromGlobal (QCursor::pos ()) + << endl; +#endif + + m_createNOPTimer->stop (); + m_RMBMoveUpdateGUITimer->stop (); + + + // In case the cursor was wrong to start with + // (forgot to call kpTool::somethingBelowTheCursorChanged()), + // make sure it is correct during this operation. + hover (m_currentPoint); + + // Currently used only to end the current text + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint/* TODO: wrong */, m_currentPoint).normalize ()); + + m_dragType = Create; + m_dragHasBegun = false; + + kpSelection *sel = document ()->selection (); + m_hadSelectionBeforeDrag = bool (sel); + + if (sel) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\thas sel region rect=" << sel->boundingRect () << endl; + #endif + QRect selectionRect = sel->boundingRect (); + + if (onSelectionResizeHandle () && !controlOrShiftPressed ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\t\tis resize/scale" << endl; + #endif + + m_startDragFromSelectionTopLeft = m_currentPoint - selectionRect.topLeft (); + m_dragType = ResizeScale; + m_resizeScaleType = onSelectionResizeHandle (); + + viewManager ()->setQueueUpdates (); + { + viewManager ()->setSelectionBorderVisible (true); + viewManager ()->setSelectionBorderFinished (true); + viewManager ()->setTextCursorEnabled (false); + } + viewManager ()->restoreQueueUpdates (); + } + else if (sel->contains (m_currentPoint)) + { + if (m_mode == Text && onSelectionToSelectText () && !controlOrShiftPressed ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\t\tis select cursor pos" << endl; + #endif + + m_dragType = SelectText; + + viewManager ()->setTextCursorPosition (sel->textRowForPoint (m_currentPoint), + sel->textColForPoint (m_currentPoint)); + } + else + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\t\tis move" << endl; + #endif + + m_startDragFromSelectionTopLeft = m_currentPoint - selectionRect.topLeft (); + m_dragType = Move; + + if (m_mouseButton == 0) + { + setSelectionBorderForMove (); + } + else + { + // Don't hide sel border momentarily if user is just + // right _clicking_ selection + m_RMBMoveUpdateGUITimer->start (100, true/*single shot*/); + } + } + } + else + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\t\tis new sel" << endl; + #endif + + pushOntoDocument (); + } + } + + // creating new selection? + if (m_dragType == Create) + { + viewManager ()->setQueueUpdates (); + { + viewManager ()->setSelectionBorderVisible (true); + viewManager ()->setSelectionBorderFinished (false); + viewManager ()->setTextCursorEnabled (false); + } + viewManager ()->restoreQueueUpdates (); + + m_createNOPTimer->start (200, true/*single shot*/); + } + + if (m_dragType != SelectText) + { + setUserMessage (cancelUserMessage ()); + } +} + + +// protected +const QCursor &kpToolSelection::cursor () const +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelection::cursor()" + << " m_currentPoint=" << m_currentPoint + << " QCursor::pos() view under cursor=" + << (viewUnderCursor () ? + viewUnderCursor ()->mapFromGlobal (QCursor::pos ()) : + KP_INVALID_POINT) + << " controlOrShiftPressed=" << controlOrShiftPressed () + << endl; +#endif + + kpSelection *sel = document () ? document ()->selection () : 0; + + if (sel && onSelectionResizeHandle () && !controlOrShiftPressed ()) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tonSelectionResizeHandle=" + << onSelectionResizeHandle () << endl; + #endif + switch (onSelectionResizeHandle ()) + { + case (kpView::Top | kpView::Left): + case (kpView::Bottom | kpView::Right): + return Qt::sizeFDiagCursor; + + case (kpView::Bottom | kpView::Left): + case (kpView::Top | kpView::Right): + return Qt::sizeBDiagCursor; + + case kpView::Top: + case kpView::Bottom: + return Qt::sizeVerCursor; + + case kpView::Left: + case kpView::Right: + return Qt::sizeHorCursor; + } + + return Qt::arrowCursor; + } + else if (sel && sel->contains (m_currentPoint)) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tsel contains currentPoint; selecting text? " + << onSelectionToSelectText () << endl; + #endif + + if (m_mode == Text && onSelectionToSelectText () && !controlOrShiftPressed ()) + return Qt::ibeamCursor; + else + return Qt::sizeAllCursor; + } + else + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tnot on sel" << endl; + #endif + return Qt::crossCursor; + } +} + +// virtual +void kpToolSelection::hover (const QPoint &point) +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelection::hover" << point << endl; +#endif + + viewManager ()->setCursor (cursor ()); + + setUserShapePoints (point, KP_INVALID_POINT, false/*don't set size*/); + if (document () && document ()->selection ()) + { + setUserShapeSize (document ()->selection ()->width (), + document ()->selection ()->height ()); + } + else + { + setUserShapeSize (KP_INVALID_SIZE); + } + + QString mess = haventBegunDrawUserMessage (); + if (mess != userMessage ()) + setUserMessage (mess); +} + +// protected +void kpToolSelection::popupRMBMenu () +{ + QPopupMenu *pop = mainWindow () ? mainWindow ()->selectionToolRMBMenu () : 0; + if (!pop) + return; + + // WARNING: enters event loop - may re-enter view/tool event handlers + pop->exec (QCursor::pos ()); + + // Cursor may have moved while menu up, triggering mouseMoveEvents + // for the menu - not the view. Update cursor position now. + somethingBelowTheCursorChanged (); +} + +// protected +void kpToolSelection::setSelectionBorderForMove () +{ + // don't show border while moving + viewManager ()->setQueueUpdates (); + { + viewManager ()->setSelectionBorderVisible (false); + viewManager ()->setSelectionBorderFinished (true); + viewManager ()->setTextCursorEnabled (false); + } + viewManager ()->restoreQueueUpdates (); +} + +// protected slot +void kpToolSelection::slotRMBMoveUpdateGUI () +{ + // (just in case not called from single shot) + m_RMBMoveUpdateGUITimer->stop (); + + setSelectionBorderForMove (); + + kpSelection * const sel = document () ? document ()->selection () : 0; + if (sel) + setUserShapePoints (sel->topLeft ()); +} + +// protected slot +void kpToolSelection::delayedDraw () +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelection::delayedDraw() hasBegunDraw=" + << hasBegunDraw () + << " currentPoint=" << m_currentPoint + << " lastPoint=" << m_lastPoint + << " startPoint=" << m_startPoint + << endl; +#endif + + // (just in case not called from single shot) + m_createNOPTimer->stop (); + + if (hasBegunDraw ()) + { + draw (m_currentPoint, m_lastPoint, + QRect (m_startPoint, m_currentPoint).normalize ()); + } +} + +// virtual +void kpToolSelection::draw (const QPoint &inThisPoint, const QPoint & /*lastPoint*/, + const QRect &inNormalizedRect) +{ + QPoint thisPoint = inThisPoint; + QRect normalizedRect = inNormalizedRect; + +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelection::draw" << thisPoint + << " startPoint=" << m_startPoint + << " normalizedRect=" << normalizedRect << endl; +#endif + + + // OPT: return when thisPoint == m_lastPoint so that e.g. when creating + // Points sel, press modifiers doesn't add multiple points in same + // place + + + bool nextDragHasBegun = true; + + + if (m_dragType == Create) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tnot moving - resizing rect to" << normalizedRect + << endl; + kdDebug () << "\t\tcreateNOPTimer->isActive()=" + << m_createNOPTimer->isActive () + << " viewManhattanLength from startPoint=" + << m_viewUnderStartPoint->transformDocToViewX ((thisPoint - m_startPoint).manhattanLength ()) + << endl; + #endif + + if (m_createNOPTimer->isActive ()) + { + if (m_viewUnderStartPoint->transformDocToViewX ((thisPoint - m_startPoint).manhattanLength ()) <= 6) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\t\tsuppress accidental movement" << endl; + #endif + thisPoint = m_startPoint; + } + else + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\t\tit's a \"big\" intended move - stop timer" << endl; + #endif + m_createNOPTimer->stop (); + } + } + + + // Prevent unintentional 1-pixel selections + if (!m_dragHasBegun && thisPoint == m_startPoint) + { + if (m_mode != kpToolSelection::Text) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tnon-text NOP - return" << endl; + #endif + setUserShapePoints (thisPoint); + return; + } + else // m_mode == kpToolSelection::Text + { + // Attempt to deselect text box by clicking? + if (m_hadSelectionBeforeDrag) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\ttext box deselect - NOP - return" << endl; + #endif + setUserShapePoints (thisPoint); + return; + } + + // Drag-wise, this is a NOP so we'd normally return (hence + // m_dragHasBegun would not change). However, as a special + // case, allow user to create a text box using a single + // click. But don't set m_dragHasBegun for next iteration + // since it would be untrue. + // + // This makes sure that a single click creation of text box + // works even if draw() is invoked more than once at the + // same position (esp. with accidental drag suppression + // (above)). + nextDragHasBegun = false; + } + } + + + switch (m_mode) + { + case kpToolSelection::Rectangle: + { + const QRect usefulRect = normalizedRect.intersect (document ()->rect ()); + document ()->setSelection (kpSelection (kpSelection::Rectangle, usefulRect, + mainWindow ()->selectionTransparency ())); + + setUserShapePoints (m_startPoint, + QPoint (QMAX (0, QMIN (m_currentPoint.x (), document ()->width () - 1)), + QMAX (0, QMIN (m_currentPoint.y (), document ()->height () - 1)))); + break; + } + case kpToolSelection::Text: + { + const kpTextStyle textStyle = mainWindow ()->textStyle (); + + int minimumWidth, minimumHeight; + + // Just a click? + if (!m_dragHasBegun && thisPoint == m_startPoint) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tclick creating text box" << endl; + #endif + + // (Click creating text box with RMB would not be obvious + // since RMB menu most likely hides text box immediately + // afterwards) + if (m_mouseButton == 1) + break; + + + minimumWidth = kpSelection::preferredMinimumWidthForTextStyle (textStyle); + if (thisPoint.x () >= m_startPoint.x ()) + { + if (m_startPoint.x () + minimumWidth - 1 >= document ()->width ()) + { + minimumWidth = QMAX (kpSelection::minimumWidthForTextStyle (textStyle), + document ()->width () - m_startPoint.x ()); + } + } + else + { + if (m_startPoint.x () - minimumWidth + 1 < 0) + { + minimumWidth = QMAX (kpSelection::minimumWidthForTextStyle (textStyle), + m_startPoint.x () + 1); + } + } + + minimumHeight = kpSelection::preferredMinimumHeightForTextStyle (textStyle); + if (thisPoint.y () >= m_startPoint.y ()) + { + if (m_startPoint.y () + minimumHeight - 1 >= document ()->height ()) + { + minimumHeight = QMAX (kpSelection::minimumHeightForTextStyle (textStyle), + document ()->height () - m_startPoint.y ()); + } + } + else + { + if (m_startPoint.y () - minimumHeight + 1 < 0) + { + minimumHeight = QMAX (kpSelection::minimumHeightForTextStyle (textStyle), + m_startPoint.y () + 1); + } + } + } + else + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tdrag creating text box" << endl; + #endif + minimumWidth = kpSelection::minimumWidthForTextStyle (textStyle); + minimumHeight = kpSelection::minimumHeightForTextStyle (textStyle); + } + + + if (normalizedRect.width () < minimumWidth) + { + if (thisPoint.x () >= m_startPoint.x ()) + normalizedRect.setWidth (minimumWidth); + else + normalizedRect.setX (normalizedRect.right () - minimumWidth + 1); + } + + if (normalizedRect.height () < minimumHeight) + { + if (thisPoint.y () >= m_startPoint.y ()) + normalizedRect.setHeight (minimumHeight); + else + normalizedRect.setY (normalizedRect.bottom () - minimumHeight + 1); + } + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\t\tnormalizedRect=" << normalizedRect + << " kpSelection::preferredMinimumSize=" + << QSize (minimumWidth, minimumHeight) + << endl; + #endif + + QValueVector <QString> textLines (1, QString ()); + kpSelection sel (normalizedRect, textLines, textStyle); + + if (!m_currentCreateTextCommand) + { + m_currentCreateTextCommand = new kpToolSelectionCreateCommand ( + i18n ("Text: Create Box"), + sel, + mainWindow ()); + } + else + m_currentCreateTextCommand->setFromSelection (sel); + + viewManager ()->setTextCursorPosition (0, 0); + document ()->setSelection (sel); + + QPoint actualEndPoint = KP_INVALID_POINT; + if (m_startPoint == normalizedRect.topLeft ()) + actualEndPoint = normalizedRect.bottomRight (); + else if (m_startPoint == normalizedRect.bottomRight ()) + actualEndPoint = normalizedRect.topLeft (); + else if (m_startPoint == normalizedRect.topRight ()) + actualEndPoint = normalizedRect.bottomLeft (); + else if (m_startPoint == normalizedRect.bottomLeft ()) + actualEndPoint = normalizedRect.topRight (); + + setUserShapePoints (m_startPoint, actualEndPoint); + break; + } + case kpToolSelection::Ellipse: + document ()->setSelection (kpSelection (kpSelection::Ellipse, normalizedRect, + mainWindow ()->selectionTransparency ())); + setUserShapePoints (m_startPoint, m_currentPoint); + break; + case kpToolSelection::FreeForm: + QPointArray points; + + if (document ()->selection ()) + points = document ()->selection ()->points (); + + + // (not detached so will modify "points" directly but + // still need to call kpDocument::setSelection() to + // update screen) + + if (!m_dragHasBegun) + { + // We thought the drag at startPoint was a NOP + // but it turns out that it wasn't... + points.putPoints (points.count (), 1, m_startPoint.x (), m_startPoint.y ()); + } + + // TODO: there should be an upper limit on this before drawing the + // polygon becomes too slow + points.putPoints (points.count (), 1, thisPoint.x (), thisPoint.y ()); + + + document ()->setSelection (kpSelection (points, mainWindow ()->selectionTransparency ())); + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\t\tfreeform; #points=" << document ()->selection ()->points ().count () << endl; + #endif + + setUserShapePoints (m_currentPoint); + break; + } + + viewManager ()->setSelectionBorderVisible (true); + } + else if (m_dragType == Move) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tmoving selection" << endl; + #endif + + kpSelection *sel = document ()->selection (); + + QRect targetSelRect = QRect (thisPoint.x () - m_startDragFromSelectionTopLeft.x (), + thisPoint.y () - m_startDragFromSelectionTopLeft.y (), + sel->width (), + sel->height ()); + + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\t\tstartPoint=" << m_startPoint + << " thisPoint=" << thisPoint + << " startDragFromSel=" << m_startDragFromSelectionTopLeft + << " targetSelRect=" << targetSelRect + << endl; + #endif + + // Try to make sure selection still intersects document so that it's + // reachable. + + if (targetSelRect.right () < 0) + targetSelRect.moveBy (-targetSelRect.right (), 0); + else if (targetSelRect.left () >= document ()->width ()) + targetSelRect.moveBy (document ()->width () - targetSelRect.left () - 1, 0); + + if (targetSelRect.bottom () < 0) + targetSelRect.moveBy (0, -targetSelRect.bottom ()); + else if (targetSelRect.top () >= document ()->height ()) + targetSelRect.moveBy (0, document ()->height () - targetSelRect.top () - 1); + + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\t\t\tafter ensure sel rect clickable=" << targetSelRect << endl; + #endif + + + if (!m_dragHasBegun && + targetSelRect.topLeft () + m_startDragFromSelectionTopLeft == m_startPoint) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\t\t\t\tnop" << endl; + #endif + + + if (!m_RMBMoveUpdateGUITimer->isActive ()) + { + // (slotRMBMoveUpdateGUI() calls similar line) + setUserShapePoints (sel->topLeft ()); + } + + // Prevent both NOP drag-moves + return; + } + + + if (m_RMBMoveUpdateGUITimer->isActive ()) + { + m_RMBMoveUpdateGUITimer->stop (); + slotRMBMoveUpdateGUI (); + } + + + if (!sel->pixmap () && !m_currentPullFromDocumentCommand) + { + m_currentPullFromDocumentCommand = new kpToolSelectionPullFromDocumentCommand ( + QString::null/*uninteresting child of macro cmd*/, + mainWindow ()); + m_currentPullFromDocumentCommand->execute (); + } + + if (!m_currentMoveCommand) + { + m_currentMoveCommand = new kpToolSelectionMoveCommand ( + QString::null/*uninteresting child of macro cmd*/, + mainWindow ()); + m_currentMoveCommandIsSmear = false; + } + + + //viewManager ()->setQueueUpdates (); + //viewManager ()->setFastUpdates (); + + if (m_shiftPressed) + m_currentMoveCommandIsSmear = true; + + if (!m_dragHasBegun && (m_controlPressed || m_shiftPressed)) + m_currentMoveCommand->copyOntoDocument (); + + m_currentMoveCommand->moveTo (targetSelRect.topLeft ()); + + if (m_shiftPressed) + m_currentMoveCommand->copyOntoDocument (); + + //viewManager ()->restoreFastUpdates (); + //viewManager ()->restoreQueueUpdates (); + + QPoint start = m_currentMoveCommand->originalSelection ().topLeft (); + QPoint end = targetSelRect.topLeft (); + setUserShapePoints (start, end, false/*don't set size*/); + setUserShapeSize (end.x () - start.x (), end.y () - start.y ()); + } + else if (m_dragType == ResizeScale) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tresize/scale" << endl; + #endif + + kpSelection *sel = document ()->selection (); + + if (!m_dragHasBegun && thisPoint == m_startPoint) + { + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\t\tnop" << endl; + #endif + + setUserShapePoints (QPoint (sel->width (), sel->height ())); + return; + } + + + if (!sel->pixmap () && !m_currentPullFromDocumentCommand) + { + m_currentPullFromDocumentCommand = new kpToolSelectionPullFromDocumentCommand ( + QString::null/*uninteresting child of macro cmd*/, + mainWindow ()); + m_currentPullFromDocumentCommand->execute (); + } + + if (!m_currentResizeScaleCommand) + { + m_currentResizeScaleCommand = new kpToolSelectionResizeScaleCommand (mainWindow ()); + } + + + kpSelection originalSelection = m_currentResizeScaleCommand->originalSelection (); + const int oldWidth = originalSelection.width (); + const int oldHeight = originalSelection.height (); + + + // Determine new width. + + int userXSign = 0; + if (m_resizeScaleType & kpView::Left) + userXSign = -1; + else if (m_resizeScaleType & kpView::Right) + userXSign = +1; + + int newWidth = oldWidth + userXSign * (thisPoint.x () - m_startPoint.x ()); + + newWidth = QMAX (originalSelection.minimumWidth (), newWidth); + + + // Determine new height. + + int userYSign = 0; + if (m_resizeScaleType & kpView::Top) + userYSign = -1; + else if (m_resizeScaleType & kpView::Bottom) + userYSign = +1; + + int newHeight = oldHeight + userYSign * (thisPoint.y () - m_startPoint.y ()); + + newHeight = QMAX (originalSelection.minimumHeight (), newHeight); + + + // Keep aspect ratio? + if (m_shiftPressed && !sel->isText ()) + { + // Width changed more than height? At equality, favour width. + // Fix width, change height. + if ((userXSign ? double (newWidth) / oldWidth : 0) >= + (userYSign ? double (newHeight) / oldHeight : 0)) + { + newHeight = newWidth * oldHeight / oldWidth; + newHeight = QMAX (originalSelection.minimumHeight (), + newHeight); + } + // Height changed more than width? + // Fix height, change width. + else + { + newWidth = newHeight * oldWidth / oldHeight; + newWidth = QMAX (originalSelection.minimumWidth (), newWidth); + } + } + + + // Adjust x/y to new width/height for left/top resizes. + + int newX = originalSelection.x (); + int newY = originalSelection.y (); + + if (m_resizeScaleType & kpView::Left) + { + newX -= (newWidth - originalSelection.width ()); + } + + if (m_resizeScaleType & kpView::Top) + { + newY -= (newHeight - originalSelection.height ()); + } + + #if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\t\tnewX=" << newX + << " newY=" << newY + << " newWidth=" << newWidth + << " newHeight=" << newHeight + << endl; + #endif + + + viewManager ()->setFastUpdates (); + m_currentResizeScaleCommand->resizeAndMoveTo (newWidth, newHeight, + QPoint (newX, newY), + true/*smooth scale delayed*/); + viewManager ()->restoreFastUpdates (); + + setUserShapePoints (QPoint (originalSelection.width (), + originalSelection.height ()), + QPoint (newWidth, + newHeight), + false/*don't set size*/); + setUserShapeSize (newWidth - originalSelection.width (), + newHeight - originalSelection.height ()); + } + + + m_dragHasBegun = nextDragHasBegun; +} + +// virtual +void kpToolSelection::cancelShape () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelection::cancelShape() mouseButton=" << m_mouseButton << endl; +#endif + + m_createNOPTimer->stop (); + m_RMBMoveUpdateGUITimer->stop (); + + + viewManager ()->setQueueUpdates (); + { + if (m_dragType == Move) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\twas drag moving - undo drag and undo acquire" << endl; + #endif + + if (m_currentMoveCommand) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\t\tundo currentMoveCommand" << endl; + #endif + m_currentMoveCommand->finalize (); + m_currentMoveCommand->unexecute (); + delete m_currentMoveCommand; + m_currentMoveCommand = 0; + + if (document ()->selection ()->isText ()) + viewManager ()->setTextCursorBlinkState (true); + } + } + else if (m_dragType == Create) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\twas creating sel - kill" << endl; + #endif + + // TODO: should we give the user back the selection s/he had before (if any)? + document ()->selectionDelete (); + + if (m_currentCreateTextCommand) + { + delete m_currentCreateTextCommand; + m_currentCreateTextCommand = 0; + } + } + else if (m_dragType == ResizeScale) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\twas resize/scale sel - kill" << endl; + #endif + + if (m_currentResizeScaleCommand) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\t\tundo currentResizeScaleCommand" << endl; + #endif + m_currentResizeScaleCommand->finalize (); // (unneeded but let's be safe) + m_currentResizeScaleCommand->unexecute (); + delete m_currentResizeScaleCommand; + m_currentResizeScaleCommand = 0; + + if (document ()->selection ()->isText ()) + viewManager ()->setTextCursorBlinkState (true); + } + } + + + if (m_currentPullFromDocumentCommand) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\t\tundo pullFromDocumentCommand" << endl; + #endif + m_currentPullFromDocumentCommand->unexecute (); + delete m_currentPullFromDocumentCommand; + m_currentPullFromDocumentCommand = 0; + } + + + viewManager ()->setSelectionBorderVisible (true); + viewManager ()->setSelectionBorderFinished (true); + viewManager ()->setTextCursorEnabled (m_mode == Text && true); + } + viewManager ()->restoreQueueUpdates (); + + + m_dragType = Unknown; + m_cancelledShapeButStillHoldingButtons = true; + setUserMessage (i18n ("Let go of all the mouse buttons.")); +} + +// virtual +void kpToolSelection::releasedAllButtons () +{ + m_cancelledShapeButStillHoldingButtons = false; + setUserMessage (haventBegunDrawUserMessage ()); +} + +// virtual +void kpToolSelection::endDraw (const QPoint & /*thisPoint*/, const QRect & /*normalizedRect*/) +{ + m_createNOPTimer->stop (); + m_RMBMoveUpdateGUITimer->stop (); + + + viewManager ()->setQueueUpdates (); + { + if (m_currentCreateTextCommand) + { + commandHistory ()->addCommand (m_currentCreateTextCommand, false/*no exec*/); + m_currentCreateTextCommand = 0; + } + + kpMacroCommand *cmd = 0; + if (m_currentMoveCommand) + { + if (m_currentMoveCommandIsSmear) + { + cmd = new kpMacroCommand (i18n ("%1: Smear") + .arg (document ()->selection ()->name ()), + mainWindow ()); + } + else + { + cmd = new kpMacroCommand ((document ()->selection ()->isText () ? + i18n ("Text: Move Box") : + i18n ("Selection: Move")), + mainWindow ()); + } + + if (document ()->selection ()->isText ()) + viewManager ()->setTextCursorBlinkState (true); + } + else if (m_currentResizeScaleCommand) + { + cmd = new kpMacroCommand (m_currentResizeScaleCommand->kpNamedCommand::name (), + mainWindow ()); + + if (document ()->selection ()->isText ()) + viewManager ()->setTextCursorBlinkState (true); + } + + if (m_currentPullFromDocumentCommand) + { + if (!m_currentMoveCommand && !m_currentResizeScaleCommand) + { + kdError () << "kpToolSelection::endDraw() pull without move nor resize/scale" << endl; + delete m_currentPullFromDocumentCommand; + m_currentPullFromDocumentCommand = 0; + } + else + { + kpSelection selection; + + if (m_currentMoveCommand) + selection = m_currentMoveCommand->originalSelection (); + else if (m_currentResizeScaleCommand) + selection = m_currentResizeScaleCommand->originalSelection (); + + // just the border + selection.setPixmap (QPixmap ()); + + kpCommand *createCommand = new kpToolSelectionCreateCommand ( + i18n ("Selection: Create"), + selection, + mainWindow ()); + + if (kpToolSelectionCreateCommand::nextUndoCommandIsCreateBorder (commandHistory ())) + commandHistory ()->setNextUndoCommand (createCommand); + else + commandHistory ()->addCommand (createCommand, + false/*no exec - user already dragged out sel*/); + + + cmd->addCommand (m_currentPullFromDocumentCommand); + m_currentPullFromDocumentCommand = 0; + } + } + + if (m_currentMoveCommand) + { + m_currentMoveCommand->finalize (); + cmd->addCommand (m_currentMoveCommand); + m_currentMoveCommand = 0; + + if (document ()->selection ()->isText ()) + viewManager ()->setTextCursorBlinkState (true); + } + + if (m_currentResizeScaleCommand) + { + m_currentResizeScaleCommand->finalize (); + cmd->addCommand (m_currentResizeScaleCommand); + m_currentResizeScaleCommand = 0; + + if (document ()->selection ()->isText ()) + viewManager ()->setTextCursorBlinkState (true); + } + + if (cmd) + commandHistory ()->addCommand (cmd, false/*no exec*/); + + viewManager ()->setSelectionBorderVisible (true); + viewManager ()->setSelectionBorderFinished (true); + viewManager ()->setTextCursorEnabled (m_mode == Text && true); + } + viewManager ()->restoreQueueUpdates (); + + + m_dragType = Unknown; + setUserMessage (haventBegunDrawUserMessage ()); + + + if (m_mouseButton == 1/*right*/) + popupRMBMenu (); +} + + +// protected virtual [base kpTool] +void kpToolSelection::keyPressEvent (QKeyEvent *e) +{ +#if DEBUG_KP_TOOL_SELECTION && 0 + kdDebug () << "kpToolSelection::keyPressEvent(e->text='" << e->text () << "')" << endl; +#endif + + + e->ignore (); + + + if (document ()->selection () && + !hasBegunDraw () && + e->key () == Qt::Key_Escape) + { + #if DEBUG_KP_TOOL_SELECTION && 0 + kdDebug () << "\tescape pressed with sel when not begun draw - deselecting" << endl; + #endif + + pushOntoDocument (); + e->accept (); + } + + + if (!e->isAccepted ()) + { + #if DEBUG_KP_TOOL_SELECTION && 0 + kdDebug () << "\tkey processing did not accept (text was '" + << e->text () + << "') - passing on event to kpTool" + << endl; + #endif + + kpTool::keyPressEvent (e); + return; + } +} + + +// private slot +void kpToolSelection::selectionTransparencyChanged (const QString & /*name*/) +{ +#if 0 +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelection::selectionTransparencyChanged(" << name << ")" << endl; +#endif + + if (mainWindow ()->settingSelectionTransparency ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\trecursion - abort setting selection transparency: " + << mainWindow ()->settingSelectionTransparency () << endl; + #endif + return; + } + + if (document ()->selection ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\thave sel - set transparency" << endl; + #endif + + kpSelectionTransparency oldST = document ()->selection ()->transparency (); + kpSelectionTransparency st = mainWindow ()->selectionTransparency (); + + // TODO: This "NOP" check causes us a great deal of trouble e.g.: + // + // Select a solid red rectangle. + // Switch to transparent and set red as the background colour. + // (the selection is now invisible) + // Invert Colours. + // (the selection is now cyan) + // Change the background colour to green. + // (no command is added to undo this as the selection does not change) + // Undo. + // The rectangle is no longer invisible. + // + //if (document ()->selection ()->setTransparency (st, true/*check harder for no change in mask*/)) + + document ()->selection ()->setTransparency (st); + if (true) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\t\twhich changed the pixmap" << endl; + #endif + + commandHistory ()->addCommand (new kpToolSelectionTransparencyCommand ( + i18n ("Selection: Transparency"), // name, + st, oldST, + mainWindow ()), + false/* no exec*/); + } + } +#endif + + // TODO: I've duplicated the code (see below 3x) to make sure + // kpSelectionTransparency(oldST)::transparentColor() is defined + // and not taken from kpDocument (where it may not be defined because + // the transparency may be opaque). + // + // That way kpToolSelectionTransparencyCommand can force set colours. +} + + +// protected slot virtual +void kpToolSelection::slotIsOpaqueChanged () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelection::slotIsOpaqueChanged()" << endl; +#endif + + if (mainWindow ()->settingSelectionTransparency ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\trecursion - abort setting selection transparency: " + << mainWindow ()->settingSelectionTransparency () << endl; + #endif + return; + } + + if (document ()->selection ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\thave sel - set transparency" << endl; + #endif + + QApplication::setOverrideCursor (Qt::waitCursor); + + if (hasBegunShape ()) + endShapeInternal (); + + kpSelectionTransparency st = mainWindow ()->selectionTransparency (); + kpSelectionTransparency oldST = st; + oldST.setOpaque (!oldST.isOpaque ()); + + document ()->selection ()->setTransparency (st); + commandHistory ()->addCommand (new kpToolSelectionTransparencyCommand ( + st.isOpaque () ? + i18n ("Selection: Opaque") : + i18n ("Selection: Transparent"), + st, oldST, + mainWindow ()), + false/* no exec*/); + + QApplication::restoreOverrideCursor (); + } +} + +// protected slot virtual [base kpTool] +void kpToolSelection::slotBackgroundColorChanged (const kpColor &) +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelection::slotBackgroundColorChanged()" << endl; +#endif + + if (mainWindow ()->settingSelectionTransparency ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\trecursion - abort setting selection transparency: " + << mainWindow ()->settingSelectionTransparency () << endl; + #endif + return; + } + + if (document ()->selection ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\thave sel - set transparency" << endl; + #endif + + QApplication::setOverrideCursor (Qt::waitCursor); + + kpSelectionTransparency st = mainWindow ()->selectionTransparency (); + kpSelectionTransparency oldST = st; + oldST.setTransparentColor (oldBackgroundColor ()); + + document ()->selection ()->setTransparency (st); + commandHistory ()->addCommand (new kpToolSelectionTransparencyCommand ( + i18n ("Selection: Transparency Color"), + st, oldST, + mainWindow ()), + false/* no exec*/); + + QApplication::restoreOverrideCursor (); + } +} + +// protected slot virtual [base kpTool] +void kpToolSelection::slotColorSimilarityChanged (double, int) +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelection::slotColorSimilarityChanged()" << endl; +#endif + + if (mainWindow ()->settingSelectionTransparency ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\trecursion - abort setting selection transparency: " + << mainWindow ()->settingSelectionTransparency () << endl; + #endif + return; + } + + if (document ()->selection ()) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\thave sel - set transparency" << endl; + #endif + + QApplication::setOverrideCursor (Qt::waitCursor); + + kpSelectionTransparency st = mainWindow ()->selectionTransparency (); + kpSelectionTransparency oldST = st; + oldST.setColorSimilarity (oldColorSimilarity ()); + + document ()->selection ()->setTransparency (st); + commandHistory ()->addCommand (new kpToolSelectionTransparencyCommand ( + i18n ("Selection: Transparency Color Similarity"), + st, oldST, + mainWindow ()), + false/* no exec*/); + + QApplication::restoreOverrideCursor (); + } +} + + +/* + * kpToolSelectionCreateCommand + */ + +kpToolSelectionCreateCommand::kpToolSelectionCreateCommand (const QString &name, + const kpSelection &fromSelection, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_fromSelection (0), + m_textRow (0), m_textCol (0) +{ + setFromSelection (fromSelection); +} + +kpToolSelectionCreateCommand::~kpToolSelectionCreateCommand () +{ + delete m_fromSelection; +} + + +// public virtual [base kpCommand] +int kpToolSelectionCreateCommand::size () const +{ + return kpPixmapFX::selectionSize (m_fromSelection); +} + + +// public static +bool kpToolSelectionCreateCommand::nextUndoCommandIsCreateBorder ( + kpCommandHistory *commandHistory) +{ + if (!commandHistory) + return false; + + kpCommand *cmd = commandHistory->nextUndoCommand (); + if (!cmd) + return false; + + kpToolSelectionCreateCommand *c = dynamic_cast <kpToolSelectionCreateCommand *> (cmd); + if (!c) + return false; + + const kpSelection *sel = c->fromSelection (); + if (!sel) + return false; + + return (!sel->pixmap ()); +} + + +// public +const kpSelection *kpToolSelectionCreateCommand::fromSelection () const +{ + return m_fromSelection; +} + +// public +void kpToolSelectionCreateCommand::setFromSelection (const kpSelection &fromSelection) +{ + delete m_fromSelection; + m_fromSelection = new kpSelection (fromSelection); +} + +// public virtual [base kpCommand] +void kpToolSelectionCreateCommand::execute () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelectionCreateCommand::execute()" << endl; +#endif + + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolSelectionCreateCommand::execute() without doc" << endl; + return; + } + + if (m_fromSelection) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\tusing fromSelection" << endl; + kdDebug () << "\t\thave sel=" << doc->selection () + << " pixmap=" << (doc->selection () ? doc->selection ()->pixmap () : 0) + << endl; + #endif + if (!m_fromSelection->isText ()) + { + if (m_fromSelection->transparency () != m_mainWindow->selectionTransparency ()) + m_mainWindow->setSelectionTransparency (m_fromSelection->transparency ()); + } + else + { + if (m_fromSelection->textStyle () != m_mainWindow->textStyle ()) + m_mainWindow->setTextStyle (m_fromSelection->textStyle ()); + } + + m_mainWindow->viewManager ()->setTextCursorPosition (m_textRow, m_textCol); + doc->setSelection (*m_fromSelection); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } +} + +// public virtual [base kpCommand] +void kpToolSelectionCreateCommand::unexecute () +{ + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolSelectionCreateCommand::unexecute() without doc" << endl; + return; + } + + if (!doc->selection ()) + { + // Was just a border that got deselected? + if (m_fromSelection && !m_fromSelection->pixmap ()) + return; + + kdError () << "kpToolSelectionCreateCommand::unexecute() without sel region" << endl; + return; + } + + m_textRow = m_mainWindow->viewManager ()->textCursorRow (); + m_textCol = m_mainWindow->viewManager ()->textCursorCol (); + + doc->selectionDelete (); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); +} + + +/* + * kpToolSelectionPullFromDocumentCommand + */ + +kpToolSelectionPullFromDocumentCommand::kpToolSelectionPullFromDocumentCommand (const QString &name, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_backgroundColor (mainWindow ? mainWindow->backgroundColor () : kpColor::invalid), + m_originalSelectionRegion (0) +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelectionPullFromDocumentCommand::<ctor>() mainWindow=" + << m_mainWindow + << endl; +#endif +} + +kpToolSelectionPullFromDocumentCommand::~kpToolSelectionPullFromDocumentCommand () +{ + delete m_originalSelectionRegion; +} + + +// public virtual [base kpCommand] +int kpToolSelectionPullFromDocumentCommand::size () const +{ + return kpPixmapFX::selectionSize (m_originalSelectionRegion); +} + + +// public virtual [base kpCommand] +void kpToolSelectionPullFromDocumentCommand::execute () +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelectionPullFromDocumentCommand::execute()" << endl; +#endif + + kpDocument *doc = document (); + + if (!doc) + { + kdError () << "kpToolSelectionPullFromDocumentCommand::execute() without doc" << endl; + return; + } + + kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0; + if (vm) + vm->setQueueUpdates (); + + // In case the user CTRL+Z'ed, selected a random region to throw us off + // and then CTRL+Shift+Z'ed putting us here. Make sure we pull from the + // originally requested region - not the random one. + if (m_originalSelectionRegion) + { + if (m_originalSelectionRegion->transparency () != m_mainWindow->selectionTransparency ()) + m_mainWindow->setSelectionTransparency (m_originalSelectionRegion->transparency ()); + + doc->setSelection (*m_originalSelectionRegion); + } + else + { + // must have selection region but not pixmap + if (!doc->selection () || doc->selection ()->pixmap ()) + { + kdError () << "kpToolSelectionPullFromDocumentCommand::execute() sel=" + << doc->selection () + << " pixmap=" + << (doc->selection () ? doc->selection ()->pixmap () : 0) + << endl; + if (vm) + vm->restoreQueueUpdates (); + return; + } + } + + doc->selectionPullFromDocument (m_backgroundColor); + + if (vm) + vm->restoreQueueUpdates (); +} + +// public virtual [base kpCommand] +void kpToolSelectionPullFromDocumentCommand::unexecute () +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelectionPullFromDocumentCommand::unexecute()" << endl; +#endif + + kpDocument *doc = document (); + + if (!doc) + { + kdError () << "kpToolSelectionPullFromDocumentCommand::unexecute() without doc" << endl; + return; + } + + // must have selection pixmap + if (!doc->selection () || !doc->selection ()->pixmap ()) + { + kdError () << "kpToolSelectionPullFromDocumentCommand::unexecute() sel=" + << doc->selection () + << " pixmap=" + << (doc->selection () ? doc->selection ()->pixmap () : 0) + << endl; + return; + } + + + // We can have faith that this is the state of the selection after + // execute(), rather than after the user tried to throw us off by + // simply selecting another region as to do that, a destroy command + // must have been used. + doc->selectionCopyOntoDocument (false/*use opaque pixmap*/); + doc->selection ()->setPixmap (QPixmap ()); + + delete m_originalSelectionRegion; + m_originalSelectionRegion = new kpSelection (*doc->selection ()); +} + + +/* + * kpToolSelectionTransparencyCommand + */ + +kpToolSelectionTransparencyCommand::kpToolSelectionTransparencyCommand (const QString &name, + const kpSelectionTransparency &st, + const kpSelectionTransparency &oldST, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_st (st), + m_oldST (oldST) +{ +} + +kpToolSelectionTransparencyCommand::~kpToolSelectionTransparencyCommand () +{ +} + + +// public virtual [base kpCommand] +int kpToolSelectionTransparencyCommand::size () const +{ + return 0; +} + + +// public virtual [base kpCommand] +void kpToolSelectionTransparencyCommand::execute () +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelectionTransparencyCommand::execute()" << endl; +#endif + kpDocument *doc = document (); + if (!doc) + return; + + QApplication::setOverrideCursor (Qt::waitCursor); + + m_mainWindow->setSelectionTransparency (m_st, true/*force colour change*/); + + if (doc->selection ()) + doc->selection ()->setTransparency (m_st); + + QApplication::restoreOverrideCursor (); +} + +// public virtual [base kpCommand] +void kpToolSelectionTransparencyCommand::unexecute () +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelectionTransparencyCommand::unexecute()" << endl; +#endif + + kpDocument *doc = document (); + if (!doc) + return; + + QApplication::setOverrideCursor (Qt::waitCursor); + + m_mainWindow->setSelectionTransparency (m_oldST, true/*force colour change*/); + + if (doc->selection ()) + doc->selection ()->setTransparency (m_oldST); + + QApplication::restoreOverrideCursor (); +} + + +/* + * kpToolSelectionMoveCommand + */ + +kpToolSelectionMoveCommand::kpToolSelectionMoveCommand (const QString &name, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow) +{ + kpDocument *doc = document (); + if (doc && doc->selection ()) + { + m_startPoint = m_endPoint = doc->selection ()->topLeft (); + } +} + +kpToolSelectionMoveCommand::~kpToolSelectionMoveCommand () +{ +} + + +// public +kpSelection kpToolSelectionMoveCommand::originalSelection () const +{ + kpDocument *doc = document (); + if (!doc || !doc->selection ()) + { + kdError () << "kpToolSelectionMoveCommand::originalSelection() doc=" + << doc + << " sel=" + << (doc ? doc->selection () : 0) + << endl; + return kpSelection (kpSelection::Rectangle, QRect ()); + } + + kpSelection selection = *doc->selection(); + selection.moveTo (m_startPoint); + + return selection; +} + + +// public virtual [base kpComand] +int kpToolSelectionMoveCommand::size () const +{ + return kpPixmapFX::pixmapSize (m_oldDocumentPixmap) + + kpPixmapFX::pointArraySize (m_copyOntoDocumentPoints); +} + + +// public virtual [base kpCommand] +void kpToolSelectionMoveCommand::execute () +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelectionMoveCommand::execute()" << endl; +#endif + + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolSelectionMoveCommand::execute() no doc" << endl; + return; + } + + kpSelection *sel = doc->selection (); + + // have to have pulled pixmap by now + if (!sel || !sel->pixmap ()) + { + kdError () << "kpToolSelectionMoveCommand::execute() but haven't pulled pixmap yet: " + << "sel=" << sel << " sel->pixmap=" << (sel ? sel->pixmap () : 0) + << endl; + return; + } + + kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0; + + if (vm) + vm->setQueueUpdates (); + + QPointArray::ConstIterator copyOntoDocumentPointsEnd = m_copyOntoDocumentPoints.end (); + for (QPointArray::ConstIterator it = m_copyOntoDocumentPoints.begin (); + it != copyOntoDocumentPointsEnd; + it++) + { + sel->moveTo (*it); + doc->selectionCopyOntoDocument (); + } + + sel->moveTo (m_endPoint); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + + if (vm) + vm->restoreQueueUpdates (); +} + +// public virtual [base kpCommand] +void kpToolSelectionMoveCommand::unexecute () +{ +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "kpToolSelectionMoveCommand::unexecute()" << endl; +#endif + + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolSelectionMoveCommand::unexecute() no doc" << endl; + return; + } + + kpSelection *sel = doc->selection (); + + // have to have pulled pixmap by now + if (!sel || !sel->pixmap ()) + { + kdError () << "kpToolSelectionMoveCommand::unexecute() but haven't pulled pixmap yet: " + << "sel=" << sel << " sel->pixmap=" << (sel ? sel->pixmap () : 0) + << endl; + return; + } + + kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0; + + if (vm) + vm->setQueueUpdates (); + + if (!m_oldDocumentPixmap.isNull ()) + doc->setPixmapAt (m_oldDocumentPixmap, m_documentBoundingRect.topLeft ()); +#if DEBUG_KP_TOOL_SELECTION && 1 + kdDebug () << "\tmove to startPoint=" << m_startPoint << endl; +#endif + sel->moveTo (m_startPoint); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + + if (vm) + vm->restoreQueueUpdates (); +} + +// public +void kpToolSelectionMoveCommand::moveTo (const QPoint &point, bool moveLater) +{ +#if DEBUG_KP_TOOL_SELECTION && 0 + kdDebug () << "kpToolSelectionMoveCommand::moveTo" << point + << " moveLater=" << moveLater + <<endl; +#endif + + if (!moveLater) + { + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolSelectionMoveCommand::moveTo() without doc" << endl; + return; + } + + kpSelection *sel = doc->selection (); + + // have to have pulled pixmap by now + if (!sel) + { + kdError () << "kpToolSelectionMoveCommand::moveTo() no sel region" << endl; + return; + } + + if (!sel->pixmap ()) + { + kdError () << "kpToolSelectionMoveCommand::moveTo() no sel pixmap" << endl; + return; + } + + if (point == sel->topLeft ()) + return; + + sel->moveTo (point); + } + + m_endPoint = point; +} + +// public +void kpToolSelectionMoveCommand::moveTo (int x, int y, bool moveLater) +{ + moveTo (QPoint (x, y), moveLater); +} + +// public +void kpToolSelectionMoveCommand::copyOntoDocument () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelectionMoveCommand::copyOntoDocument()" << endl; +#endif + + kpDocument *doc = document (); + if (!doc) + return; + + kpSelection *sel = doc->selection (); + + // have to have pulled pixmap by now + if (!sel) + { + kdError () << "\tkpToolSelectionMoveCommand::copyOntoDocument() without sel region" << endl; + return; + } + + if (!sel->pixmap ()) + { + kdError () << "kpToolSelectionMoveCommand::moveTo() no sel pixmap" << endl; + return; + } + + if (m_oldDocumentPixmap.isNull ()) + m_oldDocumentPixmap = *doc->pixmap (); + + QRect selBoundingRect = sel->boundingRect (); + m_documentBoundingRect.unite (selBoundingRect); + + doc->selectionCopyOntoDocument (); + + m_copyOntoDocumentPoints.putPoints (m_copyOntoDocumentPoints.count (), + 1, + selBoundingRect.x (), + selBoundingRect.y ()); +} + +// public +void kpToolSelectionMoveCommand::finalize () +{ + if (!m_oldDocumentPixmap.isNull () && !m_documentBoundingRect.isNull ()) + { + m_oldDocumentPixmap = kpTool::neededPixmap (m_oldDocumentPixmap, + m_documentBoundingRect); + } +} + + +/* + * kpToolSelectionResizeScaleCommand + */ + +kpToolSelectionResizeScaleCommand::kpToolSelectionResizeScaleCommand ( + kpMainWindow *mainWindow) + : kpNamedCommand (mainWindow->document ()->selection ()->isText () ? + i18n ("Text: Resize Box") : + i18n ("Selection: Smooth Scale"), + mainWindow), + m_smoothScaleTimer (new QTimer (this)) +{ + m_originalSelection = *selection (); + + m_newTopLeft = selection ()->topLeft (); + m_newWidth = selection ()->width (); + m_newHeight = selection ()->height (); + + connect (m_smoothScaleTimer, SIGNAL (timeout ()), + this, SLOT (resizeScaleAndMove ())); +} + +kpToolSelectionResizeScaleCommand::~kpToolSelectionResizeScaleCommand () +{ +} + + +// public virtual +int kpToolSelectionResizeScaleCommand::size () const +{ + return m_originalSelection.size (); +} + + +// public +kpSelection kpToolSelectionResizeScaleCommand::originalSelection () const +{ + return m_originalSelection; +} + + +// public +QPoint kpToolSelectionResizeScaleCommand::topLeft () const +{ + return m_newTopLeft; +} + +// public +void kpToolSelectionResizeScaleCommand::moveTo (const QPoint &point) +{ + if (point == m_newTopLeft) + return; + + m_newTopLeft = point; + selection ()->moveTo (m_newTopLeft); +} + + +// public +int kpToolSelectionResizeScaleCommand::width () const +{ + return m_newWidth; +} + +// public +int kpToolSelectionResizeScaleCommand::height () const +{ + return m_newHeight; +} + +// public +void kpToolSelectionResizeScaleCommand::resize (int width, int height, + bool delayed) +{ + if (width == m_newWidth && height == m_newHeight) + return; + + m_newWidth = width; + m_newHeight = height; + + resizeScaleAndMove (delayed); +} + + +// public +void kpToolSelectionResizeScaleCommand::resizeAndMoveTo (int width, int height, + const QPoint &point, + bool delayed) +{ + if (width == m_newWidth && height == m_newHeight && + point == m_newTopLeft) + { + return; + } + + m_newWidth = width; + m_newHeight = height; + m_newTopLeft = point; + + resizeScaleAndMove (delayed); +} + + +// protected +void kpToolSelectionResizeScaleCommand::killSmoothScaleTimer () +{ + m_smoothScaleTimer->stop (); +} + + +// protected +void kpToolSelectionResizeScaleCommand::resizeScaleAndMove (bool delayed) +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelectionResizeScaleCommand::resizeScaleAndMove(delayed=" + << delayed << ")" << endl; +#endif + + killSmoothScaleTimer (); + + kpSelection newSel; + + if (selection ()->isText ()) + { + newSel = m_originalSelection; + newSel.textResize (m_newWidth, m_newHeight); + } + else + { + newSel = kpSelection (kpSelection::Rectangle, + QRect (m_originalSelection.x (), + m_originalSelection.y (), + m_newWidth, + m_newHeight), + kpPixmapFX::scale (*m_originalSelection.pixmap (), + m_newWidth, m_newHeight, + !delayed/*if not delayed, smooth*/), + m_originalSelection.transparency ()); + + if (delayed) + { + // Call self with delayed==false in 200ms + m_smoothScaleTimer->start (200/*ms*/, true/*single shot*/); + } + } + + newSel.moveTo (m_newTopLeft); + + m_mainWindow->document ()->setSelection (newSel); +} + +// protected slots +void kpToolSelectionResizeScaleCommand::resizeScaleAndMove () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelectionResizeScaleCommand::resizeScaleAndMove()" << endl; +#endif + resizeScaleAndMove (false/*no delay*/); +} + + +// public +void kpToolSelectionResizeScaleCommand::finalize () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelectionResizeScaleCommand::finalize()" + << " smoothScaleTimer->isActive=" + << m_smoothScaleTimer->isActive () + << endl; +#endif + + // Make sure the selection contains the final image and the timer won't + // fire afterwards. + if (m_smoothScaleTimer->isActive ()) + { + resizeScaleAndMove (); + Q_ASSERT (!m_smoothScaleTimer->isActive ()); + } +} + + +// public virtual [base kpToolResizeScaleCommand] +void kpToolSelectionResizeScaleCommand::execute () +{ + QApplication::setOverrideCursor (Qt::waitCursor); + + killSmoothScaleTimer (); + + resizeScaleAndMove (); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + + QApplication::restoreOverrideCursor (); +} + +// public virtual [base kpToolResizeScaleCommand] +void kpToolSelectionResizeScaleCommand::unexecute () +{ + QApplication::setOverrideCursor (Qt::waitCursor); + + killSmoothScaleTimer (); + + m_mainWindow->document ()->setSelection (m_originalSelection); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + + QApplication::restoreOverrideCursor (); +} + + +/* + * kpToolSelectionDestroyCommand + */ + +kpToolSelectionDestroyCommand::kpToolSelectionDestroyCommand (const QString &name, + bool pushOntoDocument, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_pushOntoDocument (pushOntoDocument), + m_oldSelection (0) +{ +} + +kpToolSelectionDestroyCommand::~kpToolSelectionDestroyCommand () +{ + delete m_oldSelection; +} + + +// public virtual [base kpCommand] +int kpToolSelectionDestroyCommand::size () const +{ + return kpPixmapFX::pixmapSize (m_oldDocPixmap) + + kpPixmapFX::selectionSize (m_oldSelection); +} + + +// public virtual [base kpCommand] +void kpToolSelectionDestroyCommand::execute () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelectionDestroyCommand::execute () CALLED" << endl; +#endif + + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolSelectionDestroyCommand::execute() without doc" << endl; + return; + } + + if (!doc->selection ()) + { + kdError () << "kpToolSelectionDestroyCommand::execute() without sel region" << endl; + return; + } + + m_textRow = m_mainWindow->viewManager ()->textCursorRow (); + m_textCol = m_mainWindow->viewManager ()->textCursorCol (); + + m_oldSelection = new kpSelection (*doc->selection ()); + if (m_pushOntoDocument) + { + m_oldDocPixmap = doc->getPixmapAt (doc->selection ()->boundingRect ()); + doc->selectionPushOntoDocument (); + } + else + doc->selectionDelete (); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); +} + +// public virtual [base kpCommand] +void kpToolSelectionDestroyCommand::unexecute () +{ +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelectionDestroyCommand::unexecute () CALLED" << endl; +#endif + + kpDocument *doc = document (); + if (!doc) + { + kdError () << "kpToolSelectionDestroyCommand::unexecute() without doc" << endl; + return; + } + + if (doc->selection ()) + { + // not error because it's possible that the user dragged out a new + // region (without pulling pixmap), and then CTRL+Z + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "kpToolSelectionDestroyCommand::unexecute() already has sel region" << endl; + #endif + + if (doc->selection ()->pixmap ()) + { + kdError () << "kpToolSelectionDestroyCommand::unexecute() already has sel pixmap" << endl; + return; + } + } + + if (!m_oldSelection) + { + kdError () << "kpToolSelectionDestroyCommand::unexecute() without old sel" << endl; + return; + } + + if (m_pushOntoDocument) + { + #if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\tunpush oldDocPixmap onto doc first" << endl; + #endif + doc->setPixmapAt (m_oldDocPixmap, m_oldSelection->topLeft ()); + } + +#if DEBUG_KP_TOOL_SELECTION + kdDebug () << "\tsetting selection to: rect=" << m_oldSelection->boundingRect () + << " pixmap=" << m_oldSelection->pixmap () + << " pixmap.isNull()=" << (m_oldSelection->pixmap () + ? + m_oldSelection->pixmap ()->isNull () + : + true) + << endl; +#endif + if (!m_oldSelection->isText ()) + { + if (m_oldSelection->transparency () != m_mainWindow->selectionTransparency ()) + m_mainWindow->setSelectionTransparency (m_oldSelection->transparency ()); + } + else + { + if (m_oldSelection->textStyle () != m_mainWindow->textStyle ()) + m_mainWindow->setTextStyle (m_oldSelection->textStyle ()); + } + + m_mainWindow->viewManager ()->setTextCursorPosition (m_textRow, m_textCol); + doc->setSelection (*m_oldSelection); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + + delete m_oldSelection; + m_oldSelection = 0; +} + +#include <kptoolselection.moc> diff --git a/kolourpaint/tools/kptoolselection.h b/kolourpaint/tools/kptoolselection.h new file mode 100644 index 00000000..ee978a15 --- /dev/null +++ b/kolourpaint/tools/kptoolselection.h @@ -0,0 +1,313 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kp_tool_selection_h__ +#define __kp_tool_selection_h__ + + +#include <qpixmap.h> +#include <qpoint.h> +#include <qpointarray.h> +#include <qrect.h> + +#include <kpcolor.h> +#include <kpcommandhistory.h> +#include <kpselection.h> +#include <kpselectiontransparency.h> +#include <kptool.h> + + +class QPoint; +class QRect; +class QTimer; + +class kpMainWindow; +class kpSelection; + +class kpToolSelectionCreateCommand; +class kpToolSelectionMoveCommand; +class kpToolSelectionPullFromDocumentCommand; +class kpToolSelectionResizeScaleCommand; +class kpToolWidgetOpaqueOrTransparent; + + +class kpToolSelection : public kpTool +{ +Q_OBJECT + +public: + enum Mode {Rectangle, Ellipse, FreeForm, Text}; + + kpToolSelection (Mode mode, + const QString &text, const QString &description, + int key, + kpMainWindow *mainWindow, const char *name); + virtual ~kpToolSelection (); + + void setMode (Mode mode) { m_mode = mode; } + +private: + void pushOntoDocument (); + +protected: + bool onSelectionToMove () const; + int onSelectionResizeHandle () const; + bool onSelectionToSelectText () const; + +public: + QString haventBegunDrawUserMessage () const; + + virtual void begin (); + virtual void end (); + virtual void reselect (); + + virtual bool careAboutModifierState () const { return true; } + bool controlOrShiftPressed () const { return (m_controlPressed || m_shiftPressed); } + + virtual void beginDraw (); +protected: + const QCursor &cursor () const; +public: + virtual void hover (const QPoint &point); +protected: + void popupRMBMenu (); + void setSelectionBorderForMove (); +protected slots: + void slotRMBMoveUpdateGUI (); + void delayedDraw (); +public: + virtual void draw (const QPoint &thisPoint, const QPoint &lastPoint, + const QRect &normalizedRect); + virtual void cancelShape (); + virtual void releasedAllButtons (); + virtual void endDraw (const QPoint &thisPoint, const QRect &normalizedRect); + +protected: + virtual void keyPressEvent (QKeyEvent *e); + +protected: + void selectionTransparencyChanged (const QString &name); + +protected slots: + virtual void slotIsOpaqueChanged (); + virtual void slotBackgroundColorChanged (const kpColor &color); + virtual void slotColorSimilarityChanged (double similarity, int); + +protected: + Mode m_mode; + + QPoint m_startDragFromSelectionTopLeft; + enum DragType + { + Unknown, Create, Move, SelectText, ResizeScale + }; + DragType m_dragType; + bool m_dragHasBegun; + bool m_hadSelectionBeforeDrag; + int m_resizeScaleType; + + kpToolSelectionPullFromDocumentCommand *m_currentPullFromDocumentCommand; + kpToolSelectionMoveCommand *m_currentMoveCommand; + bool m_currentMoveCommandIsSmear; + kpToolSelectionResizeScaleCommand *m_currentResizeScaleCommand; + kpToolWidgetOpaqueOrTransparent *m_toolWidgetOpaqueOrTransparent; + + kpToolSelectionCreateCommand *m_currentCreateTextCommand; + bool m_cancelledShapeButStillHoldingButtons; + + QTimer *m_createNOPTimer, *m_RMBMoveUpdateGUITimer; +}; + +class kpToolSelectionCreateCommand : public kpNamedCommand +{ +public: + // (if fromSelection doesn't have a pixmap, it will only recreate the region) + kpToolSelectionCreateCommand (const QString &name, const kpSelection &fromSelection, + kpMainWindow *mainWindow); + virtual ~kpToolSelectionCreateCommand (); + + virtual int size () const; + + static bool nextUndoCommandIsCreateBorder (kpCommandHistory *commandHistory); + + const kpSelection *fromSelection () const; + void setFromSelection (const kpSelection &fromSelection); + + virtual void execute (); + virtual void unexecute (); + +private: + kpSelection *m_fromSelection; + + int m_textRow, m_textCol; +}; + +class kpToolSelectionPullFromDocumentCommand : public kpNamedCommand +{ +public: + kpToolSelectionPullFromDocumentCommand (const QString &name, kpMainWindow *mainWindow); + virtual ~kpToolSelectionPullFromDocumentCommand (); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + kpColor m_backgroundColor; + kpSelection *m_originalSelectionRegion; +}; + +class kpToolSelectionTransparencyCommand : public kpNamedCommand +{ +public: + kpToolSelectionTransparencyCommand (const QString &name, + const kpSelectionTransparency &st, + const kpSelectionTransparency &oldST, + kpMainWindow *mainWindow); + virtual ~kpToolSelectionTransparencyCommand (); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + kpSelectionTransparency m_st, m_oldST; +}; + +class kpToolSelectionMoveCommand : public kpNamedCommand +{ +public: + kpToolSelectionMoveCommand (const QString &name, kpMainWindow *mainWindow); + virtual ~kpToolSelectionMoveCommand (); + + kpSelection originalSelection () const; + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + + void moveTo (const QPoint &point, bool moveLater = false); + void moveTo (int x, int y, bool moveLater = false); + void copyOntoDocument (); + void finalize (); + +private: + QPoint m_startPoint, m_endPoint; + + QPixmap m_oldDocumentPixmap; + + // area of document affected (not the bounding rect of the sel) + QRect m_documentBoundingRect; + + QPointArray m_copyOntoDocumentPoints; +}; + +// You could subclass kpToolResizeScaleCommand and/or +// kpToolSelectionMoveCommand instead if want a disaster. +// This is different to kpToolResizeScaleCommand in that: +// +// 1. This only works for selections. +// 2. This is designed for the size and position to change several times +// before execute(). +// +class kpToolSelectionResizeScaleCommand : public QObject, + public kpNamedCommand +{ +Q_OBJECT + +public: + kpToolSelectionResizeScaleCommand (kpMainWindow *mainWindow); + virtual ~kpToolSelectionResizeScaleCommand (); + + virtual int size () const; + +public: + kpSelection originalSelection () const; + + QPoint topLeft () const; + void moveTo (const QPoint &point); + + int width () const; + int height () const; + void resize (int width, int height, bool delayed = false); + + // (equivalent to resize() followed by moveTo() but faster) + void resizeAndMoveTo (int width, int height, const QPoint &point, + bool delayed = false); + +protected: + void killSmoothScaleTimer (); + + // If <delayed>, does a fast, low-quality scale and then calls itself + // with <delayed> unset for a smooth scale, a short time later. + // If acting on a text box, <delayed> is ignored. + void resizeScaleAndMove (bool delayed); + +protected slots: + void resizeScaleAndMove (/*delayed = false*/); + +public: + void finalize (); + +public: + virtual void execute (); + virtual void unexecute (); + +protected: + kpSelection m_originalSelection; + + QPoint m_newTopLeft; + int m_newWidth, m_newHeight; + + QTimer *m_smoothScaleTimer; +}; + +class kpToolSelectionDestroyCommand : public kpNamedCommand +{ +public: + kpToolSelectionDestroyCommand (const QString &name, bool pushOntoDocument, + kpMainWindow *mainWindow); + virtual ~kpToolSelectionDestroyCommand (); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + bool m_pushOntoDocument; + QPixmap m_oldDocPixmap; + kpSelection *m_oldSelection; + + int m_textRow, m_textCol; +}; + +#endif // __kp_tool_selection_h__ diff --git a/kolourpaint/tools/kptoolskew.cpp b/kolourpaint/tools/kptoolskew.cpp new file mode 100644 index 00000000..f1e446be --- /dev/null +++ b/kolourpaint/tools/kptoolskew.cpp @@ -0,0 +1,449 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_SKEW 0 +#define DEBUG_KP_TOOL_SKEW_DIALOG 0 + + +#include <kptoolskew.h> + +#include <qapplication.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qwmatrix.h> + +#include <kdebug.h> +#include <kiconloader.h> +#include <klocale.h> +#include <knuminput.h> + +#include <kpdefs.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kppixmapfx.h> +#include <kpselection.h> +#include <kptool.h> + + +/* + * kpToolSkewCommand + */ + +kpToolSkewCommand::kpToolSkewCommand (bool actOnSelection, + int hangle, int vangle, + kpMainWindow *mainWindow) + : kpCommand (mainWindow), + m_actOnSelection (actOnSelection), + m_hangle (hangle), m_vangle (vangle), + m_backgroundColor (mainWindow ? mainWindow->backgroundColor (actOnSelection) : kpColor::invalid), + m_oldPixmapPtr (0) +{ +} + +kpToolSkewCommand::~kpToolSkewCommand () +{ + delete m_oldPixmapPtr; +} + + +// public virtual [base kpCommand] +QString kpToolSkewCommand::name () const +{ + QString opName = i18n ("Skew"); + + if (m_actOnSelection) + return i18n ("Selection: %1").arg (opName); + else + return opName; +} + + +// public virtual [base kpCommand] +int kpToolSkewCommand::size () const +{ + return kpPixmapFX::pixmapSize (m_oldPixmapPtr) + + m_oldSelection.size (); +} + + +// public virtual [base kpCommand] +void kpToolSkewCommand::execute () +{ + kpDocument *doc = document (); + if (!doc) + return; + + + QApplication::setOverrideCursor (Qt::waitCursor); + + + m_oldPixmapPtr = new QPixmap (); + *m_oldPixmapPtr = *doc->pixmap (m_actOnSelection); + + + QPixmap newPixmap = kpPixmapFX::skew (*doc->pixmap (m_actOnSelection), + kpToolSkewDialog::horizontalAngleForPixmapFX (m_hangle), + kpToolSkewDialog::verticalAngleForPixmapFX (m_vangle), + m_backgroundColor); + + if (m_actOnSelection) + { + kpSelection *sel = doc->selection (); + + // Save old selection + m_oldSelection = *sel; + + + // Calculate skewed points + QPointArray currentPoints = sel->points (); + currentPoints.translate (-currentPoints.boundingRect ().x (), + -currentPoints.boundingRect ().y ()); + QWMatrix skewMatrix = kpPixmapFX::skewMatrix ( + *doc->pixmap (m_actOnSelection), + kpToolSkewDialog::horizontalAngleForPixmapFX (m_hangle), + kpToolSkewDialog::verticalAngleForPixmapFX (m_vangle)); + currentPoints = skewMatrix.map (currentPoints); + currentPoints.translate (-currentPoints.boundingRect ().x () + m_oldSelection.x (), + -currentPoints.boundingRect ().y () + m_oldSelection.y ()); + + + if (currentPoints.boundingRect ().width () == newPixmap.width () && + currentPoints.boundingRect ().height () == newPixmap.height ()) + { + doc->setSelection (kpSelection (currentPoints, newPixmap, + m_oldSelection.transparency ())); + } + else + { + // TODO: fix the latter "victim of" problem in kpSelection by + // allowing the border width & height != pixmap width & height + // Or maybe autocrop? + #if DEBUG_KP_TOOL_SKEW + kdDebug () << "kpToolSkewCommand::execute() currentPoints.boundingRect=" + << currentPoints.boundingRect () + << " newPixmap: w=" << newPixmap.width () + << " h=" << newPixmap.height () + << " (victim of rounding error and/or skewed-a-(rectangular)-pixmap-that-was-transparent-in-the-corners-making-sel-uselessly-bigger-than-needs-be))" + << endl; + #endif + doc->setSelection (kpSelection (kpSelection::Rectangle, + QRect (currentPoints.boundingRect ().x (), + currentPoints.boundingRect ().y (), + newPixmap.width (), + newPixmap.height ()), + newPixmap, + m_oldSelection.transparency ())); + } + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + else + { + doc->setPixmap (newPixmap); + } + + + QApplication::restoreOverrideCursor (); +} + +// public virtual [base kpCommand] +void kpToolSkewCommand::unexecute () +{ + kpDocument *doc = document (); + if (!doc) + return; + + + QApplication::setOverrideCursor (Qt::waitCursor); + + + QPixmap oldPixmap = *m_oldPixmapPtr; + delete m_oldPixmapPtr; m_oldPixmapPtr = 0; + + + if (!m_actOnSelection) + doc->setPixmap (oldPixmap); + else + { + kpSelection oldSelection = m_oldSelection; + doc->setSelection (oldSelection); + + if (m_mainWindow->tool ()) + m_mainWindow->tool ()->somethingBelowTheCursorChanged (); + } + + + QApplication::restoreOverrideCursor (); +} + + +/* + * kpToolSkewDialog + */ + + +// private static +int kpToolSkewDialog::s_lastWidth = -1, + kpToolSkewDialog::s_lastHeight = -1; + +// private static +int kpToolSkewDialog::s_lastHorizontalAngle = 0, + kpToolSkewDialog::s_lastVerticalAngle = 0; + + +kpToolSkewDialog::kpToolSkewDialog (bool actOnSelection, kpMainWindow *parent, + const char *name) + : kpToolPreviewDialog (kpToolPreviewDialog::AllFeatures, + false/*don't reserve top row*/, + actOnSelection ? i18n ("Skew Selection") : i18n ("Skew Image"), + i18n ("After Skew:"), + actOnSelection, parent, name) +{ + // Too confusing - disable for now + s_lastHorizontalAngle = s_lastVerticalAngle = 0; + + + createAngleGroupBox (); + + + if (s_lastWidth > 0 && s_lastHeight > 0) + resize (s_lastWidth, s_lastHeight); + + + slotUpdate (); + + + m_horizontalSkewInput->setEditFocus (); +} + +kpToolSkewDialog::~kpToolSkewDialog () +{ + s_lastWidth = width (), s_lastHeight = height (); +} + + +// private +void kpToolSkewDialog::createAngleGroupBox () +{ + QGroupBox *angleGroupBox = new QGroupBox (i18n ("Angle"), mainWidget ()); + addCustomWidget (angleGroupBox); + + + QLabel *horizontalSkewPixmapLabel = new QLabel (angleGroupBox); + horizontalSkewPixmapLabel->setPixmap (UserIcon ("image_skew_horizontal")); + + QLabel *horizontalSkewLabel = new QLabel (i18n ("&Horizontal:"), angleGroupBox); + m_horizontalSkewInput = new KIntNumInput (s_lastHorizontalAngle, angleGroupBox); + m_horizontalSkewInput->setMinValue (-89); + m_horizontalSkewInput->setMaxValue (+89); + + QLabel *horizontalSkewDegreesLabel = new QLabel (i18n ("degrees"), angleGroupBox); + + + QLabel *verticalSkewPixmapLabel = new QLabel (angleGroupBox); + verticalSkewPixmapLabel->setPixmap (UserIcon ("image_skew_vertical")); + + QLabel *verticalSkewLabel = new QLabel (i18n ("&Vertical:"), angleGroupBox); + m_verticalSkewInput = new KIntNumInput (s_lastVerticalAngle, angleGroupBox); + m_verticalSkewInput->setMinValue (-89); + m_verticalSkewInput->setMaxValue (+89); + + QLabel *verticalSkewDegreesLabel = new QLabel (i18n ("degrees"), angleGroupBox); + + + horizontalSkewLabel->setBuddy (m_horizontalSkewInput); + verticalSkewLabel->setBuddy (m_verticalSkewInput); + + + QGridLayout *angleLayout = new QGridLayout (angleGroupBox, 4, 4, + marginHint () * 2, spacingHint ()); + + angleLayout->addWidget (horizontalSkewPixmapLabel, 0, 0); + angleLayout->addWidget (horizontalSkewLabel, 0, 1); + angleLayout->addWidget (m_horizontalSkewInput, 0, 2); + angleLayout->addWidget (horizontalSkewDegreesLabel, 0, 3); + + angleLayout->addWidget (verticalSkewPixmapLabel, 1, 0); + angleLayout->addWidget (verticalSkewLabel, 1, 1); + angleLayout->addWidget (m_verticalSkewInput, 1, 2); + angleLayout->addWidget (verticalSkewDegreesLabel, 1, 3); + + + connect (m_horizontalSkewInput, SIGNAL (valueChanged (int)), + this, SLOT (slotUpdate ())); + connect (m_verticalSkewInput, SIGNAL (valueChanged (int)), + this, SLOT (slotUpdate ())); +} + + +// private virtual [base kpToolPreviewDialog] +QSize kpToolSkewDialog::newDimensions () const +{ + kpDocument *doc = document (); + if (!doc) + return QSize (); + + QWMatrix skewMatrix = kpPixmapFX::skewMatrix (*doc->pixmap (), + horizontalAngleForPixmapFX (), + verticalAngleForPixmapFX ()); + // TODO: Should we be using QWMatrix::Areas? + QRect skewRect = skewMatrix.mapRect (doc->rect (m_actOnSelection)); + + return QSize (skewRect.width (), skewRect.height ()); +} + +// private virtual [base kpToolPreviewDialog] +QPixmap kpToolSkewDialog::transformPixmap (const QPixmap &pixmap, + int targetWidth, int targetHeight) const +{ + return kpPixmapFX::skew (pixmap, + horizontalAngleForPixmapFX (), + verticalAngleForPixmapFX (), + m_mainWindow ? m_mainWindow->backgroundColor (m_actOnSelection) : kpColor::invalid, + targetWidth, + targetHeight); +} + + +// private +void kpToolSkewDialog::updateLastAngles () +{ + s_lastHorizontalAngle = horizontalAngle (); + s_lastVerticalAngle = verticalAngle (); +} + +// private slot virtual [base kpToolPreviewDialog] +void kpToolSkewDialog::slotUpdate () +{ + updateLastAngles (); + kpToolPreviewDialog::slotUpdate (); +} + + +// public +int kpToolSkewDialog::horizontalAngle () const +{ + return m_horizontalSkewInput->value (); +} + +// public +int kpToolSkewDialog::verticalAngle () const +{ + return m_verticalSkewInput->value (); +} + + +// public static +int kpToolSkewDialog::horizontalAngleForPixmapFX (int hangle) +{ + return -hangle; +} + +// public static +int kpToolSkewDialog::verticalAngleForPixmapFX (int vangle) +{ + return -vangle; +} + + +// public +int kpToolSkewDialog::horizontalAngleForPixmapFX () const +{ + return kpToolSkewDialog::horizontalAngleForPixmapFX (horizontalAngle ()); +} + +// public +int kpToolSkewDialog::verticalAngleForPixmapFX () const +{ + return kpToolSkewDialog::verticalAngleForPixmapFX (verticalAngle ()); +} + + +// public virtual [base kpToolPreviewDialog] +bool kpToolSkewDialog::isNoOp () const +{ + return (horizontalAngle () == 0) && (verticalAngle () == 0); +} + + +// private slot virtual [base KDialogBase] +void kpToolSkewDialog::slotOk () +{ + QString message, caption, continueButtonText; + + if (document ()->selection ()) + { + if (!document ()->selection ()->isText ()) + { + message = + i18n ("<qt><p>Skewing the selection to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure want to skew the selection?</p></qt>"); + + caption = i18n ("Skew Selection?"); + continueButtonText = i18n ("Sk&ew Selection"); + } + } + else + { + message = + i18n ("<qt><p>Skewing the image to %1x%2" + " may take a substantial amount of memory." + " This can reduce system" + " responsiveness and cause other application resource" + " problems.</p>" + + "<p>Are you sure want to skew the image?</p></qt>"); + + caption = i18n ("Skew Image?"); + continueButtonText = i18n ("Sk&ew Image"); + } + + + const int newWidth = newDimensions ().width (); + const int newHeight = newDimensions ().height (); + + if (kpTool::warnIfBigImageSize (m_oldWidth, + m_oldHeight, + newWidth, newHeight, + message.arg (newWidth).arg (newHeight), + caption, + continueButtonText, + this)) + { + KDialogBase::slotOk (); + } +} + +#include <kptoolskew.moc> diff --git a/kolourpaint/tools/kptoolskew.h b/kolourpaint/tools/kptoolskew.h new file mode 100644 index 00000000..570909c2 --- /dev/null +++ b/kolourpaint/tools/kptoolskew.h @@ -0,0 +1,121 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kptool_skew_h__ +#define __kptool_skew_h__ + +#include <qpixmap.h> + +#include <kpcommandhistory.h> +#include <kdialogbase.h> + +#include <kpcolor.h> +#include <kpselection.h> +#include <kptoolpreviewdialog.h> + +class QGroupBox; +class QLabel; +class QPixmap; + +class KIntNumInput; + +class kpDocument; +class kpMainWindow; + + +class kpToolSkewCommand : public kpCommand +{ +public: + kpToolSkewCommand (bool actOnSelection, + int hangle, int vangle, + kpMainWindow *mainWindow); + virtual ~kpToolSkewCommand (); + + virtual QString name () const; + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +private: + bool m_actOnSelection; + int m_hangle, m_vangle; + + kpColor m_backgroundColor; + QPixmap *m_oldPixmapPtr; + kpSelection m_oldSelection; +}; + + +class kpToolSkewDialog : public kpToolPreviewDialog +{ +Q_OBJECT + +public: + kpToolSkewDialog (bool actOnSelection, kpMainWindow *parent, + const char *name = 0); + virtual ~kpToolSkewDialog (); + +private: + static int s_lastWidth, s_lastHeight; + static int s_lastHorizontalAngle, s_lastVerticalAngle; + + void createAngleGroupBox (); + + virtual QSize newDimensions () const; + virtual QPixmap transformPixmap (const QPixmap &pixmap, + int targetWidth, int targetHeight) const; + + void updateLastAngles (); + +private slots: + virtual void slotUpdate (); + +public: + // These are the angles the users sees in the dialog and... + int horizontalAngle () const; + int verticalAngle () const; + + // ...these functions translate them for use in kpPixmapFX::skew(). + static int horizontalAngleForPixmapFX (int hangle); + static int verticalAngleForPixmapFX (int vangle); + + int horizontalAngleForPixmapFX () const; + int verticalAngleForPixmapFX () const; + + virtual bool isNoOp () const; + +private slots: + virtual void slotOk (); + +private: + KIntNumInput *m_horizontalSkewInput, *m_verticalSkewInput; +}; + +#endif // __kptool_skew_h__ diff --git a/kolourpaint/tools/kptooltext.cpp b/kolourpaint/tools/kptooltext.cpp new file mode 100644 index 00000000..73a60e66 --- /dev/null +++ b/kolourpaint/tools/kptooltext.cpp @@ -0,0 +1,1394 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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_TEXT 0 + + +#include <kptooltext.h> + +#include <qvaluevector.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpcommandhistory.h> +#include <kpdocument.h> +#include <kpmainwindow.h> +#include <kpselection.h> +#include <kptoolwidgetopaqueortransparent.h> +#include <kpviewmanager.h> + + +kpToolText::kpToolText (kpMainWindow *mainWindow) + : kpToolSelection (Text, + i18n ("Text"), i18n ("Writes text"), + Qt::Key_T, + mainWindow, "tool_text"), + m_isIMStarted (false), + m_IMStartCursorRow (0), + m_IMStartCursorCol (0), + m_IMPreeditStr (0) +{ +} + +kpToolText::~kpToolText () +{ +} + + +// public virtual [base kpToolSelection] +void kpToolText::begin () +{ +#if DEBUG_KP_TOOL_TEXT && 1 + kdDebug () << "kpToolText::begin()" << endl; +#endif + + mainWindow ()->enableTextToolBarActions (true); + viewManager ()->setTextCursorEnabled (true); + + m_insertCommand = 0; + m_enterCommand = 0; + m_backspaceCommand = 0; + m_deleteCommand = 0; + + kpToolSelection::begin (); +} + +// public virtual [base kpToolSelection] +void kpToolText::end () +{ +#if DEBUG_KP_TOOL_TEXT && 1 + kdDebug () << "kpToolText::end()" << endl; +#endif + + kpToolSelection::end (); + + viewManager ()->setTextCursorEnabled (false); + mainWindow ()->enableTextToolBarActions (false); +} + + +// public +bool kpToolText::hasBegunText () const +{ + return (m_insertCommand || + m_enterCommand || + m_backspaceCommand || + m_deleteCommand); +} + +// public virtual [base kpTool] +bool kpToolText::hasBegunShape () const +{ + return (hasBegunDraw () || hasBegunText ()); +} + + +// public virtual [base kpToolSelection] +void kpToolText::cancelShape () +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::cancelShape()" << endl; +#endif + + if (m_dragType != Unknown) + kpToolSelection::cancelShape (); + else if (hasBegunText ()) + { + m_insertCommand = 0; + m_enterCommand = 0; + m_backspaceCommand = 0; + m_deleteCommand = 0; + + commandHistory ()->undo (); + } + else + kpToolSelection::cancelShape (); +} + +// public virtual [base kpTool] +void kpToolText::endShape (const QPoint &thisPoint, const QRect &normalizedRect) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::endShape()" << endl; +#endif + + if (m_dragType != Unknown) + kpToolSelection::endDraw (thisPoint, normalizedRect); + else if (hasBegunText ()) + { + m_insertCommand = 0; + m_enterCommand = 0; + m_backspaceCommand = 0; + m_deleteCommand = 0; + } + else + kpToolSelection::endDraw (thisPoint, normalizedRect); +} + + +// protected virtual [base kpTool] +void kpToolText::keyPressEvent (QKeyEvent *e) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::keyPressEvent(e->text='" << e->text () << "')" << endl; +#endif + + + e->ignore (); + + + if (hasBegunDraw ()) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\talready began draw with mouse - passing on event to kpTool" << endl; + #endif + kpToolSelection::keyPressEvent (e); + return; + } + + + kpSelection *sel = document ()->selection (); + + if (!sel || !sel->isText ()) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tno text sel - passing on event to kpTool" << endl; + #endif + //if (hasBegunShape ()) + // endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + kpToolSelection::keyPressEvent (e); + return; + } + + + const QValueVector <QString> textLines = sel->textLines (); + int cursorRow = viewManager ()->textCursorRow (); + int cursorCol = viewManager ()->textCursorCol (); + + +#define IS_SPACE(c) ((c).isSpace () || (c).isNull ()) + if (e->key () == Qt::Key_Enter || e->key () == Qt::Key_Return) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tenter pressed" << endl; + #endif + if (!m_enterCommand) + { + // TODO: why not endShapeInternal(); ditto for everywhere else in this file? + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + m_enterCommand = new kpToolTextEnterCommand (i18n ("Text: New Line"), + viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), + mainWindow ()); + commandHistory ()->addCommand (m_enterCommand, false/*no exec*/); + } + else + m_enterCommand->addEnter (); + + e->accept (); + } + else if (e->key () == Qt::Key_Backspace) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tbackspace pressed" << endl; + #endif + + if (!m_backspaceCommand) + { + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + m_backspaceCommand = new kpToolTextBackspaceCommand (i18n ("Text: Backspace"), + viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), + mainWindow ()); + commandHistory ()->addCommand (m_backspaceCommand, false/*no exec*/); + } + else + m_backspaceCommand->addBackspace (); + + e->accept (); + } + else if (e->key () == Qt::Key_Delete) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tdelete pressed" << endl; + #endif + + if (!m_deleteCommand) + { + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + m_deleteCommand = new kpToolTextDeleteCommand (i18n ("Text: Delete"), + viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), + mainWindow ()); + commandHistory ()->addCommand (m_deleteCommand, false/*no exec*/); + } + else + m_deleteCommand->addDelete (); + + e->accept (); + } + else if (e->key () == Qt::Key_Up) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tup pressed" << endl; + #endif + + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + if (cursorRow > 0) + { + cursorRow--; + cursorCol = QMIN (cursorCol, (int) textLines [cursorRow].length ()); + viewManager ()->setTextCursorPosition (cursorRow, cursorCol); + } + + e->accept (); + } + else if (e->key () == Qt::Key_Down) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tdown pressed" << endl; + #endif + + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + if (cursorRow < (int) textLines.size () - 1) + { + cursorRow++; + cursorCol = QMIN (cursorCol, (int) textLines [cursorRow].length ()); + viewManager ()->setTextCursorPosition (cursorRow, cursorCol); + } + + e->accept (); + } + else if (e->key () == Qt::Key_Left) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tleft pressed" << endl; + #endif + + #define MOVE_CURSOR_LEFT() \ + { \ + cursorCol--; \ + \ + if (cursorCol < 0) \ + { \ + cursorRow--; \ + if (cursorRow < 0) \ + { \ + cursorRow = 0; \ + cursorCol = 0; \ + } \ + else \ + cursorCol = textLines [cursorRow].length (); \ + } \ + } + + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + if ((e->state () & Qt::ControlButton) == 0) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tmove single char" << endl; + #endif + + MOVE_CURSOR_LEFT (); + viewManager ()->setTextCursorPosition (cursorRow, cursorCol); + } + else + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tmove to start of word" << endl; + #endif + + // (these comments will exclude the row=0,col=0 boundary case) + + #define IS_ON_ANCHOR() (!IS_SPACE (textLines [cursorRow][cursorCol]) && \ + (cursorCol == 0 || IS_SPACE (textLines [cursorRow][cursorCol - 1]))) + if (IS_ON_ANCHOR ()) + MOVE_CURSOR_LEFT (); + + // --- now we're not on an anchor point (start of word) --- + + // End up on a letter... + while (!(cursorRow == 0 && cursorCol == 0) && + (IS_SPACE (textLines [cursorRow][cursorCol]))) + { + MOVE_CURSOR_LEFT (); + } + + // --- now we're on a letter --- + + // Find anchor point + while (!(cursorRow == 0 && cursorCol == 0) && !IS_ON_ANCHOR ()) + { + MOVE_CURSOR_LEFT (); + } + + #undef IS_ON_ANCHOR + + viewManager ()->setTextCursorPosition (cursorRow, cursorCol); + } + + #undef MOVE_CURSOR_LEFT + + e->accept (); + + } + else if (e->key () == Qt::Key_Right) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tright pressed" << endl; + #endif + + #define MOVE_CURSOR_RIGHT() \ + { \ + cursorCol++; \ + \ + if (cursorCol > (int) textLines [cursorRow].length ()) \ + { \ + cursorRow++; \ + if (cursorRow > (int) textLines.size () - 1) \ + { \ + cursorRow = textLines.size () - 1; \ + cursorCol = textLines [cursorRow].length (); \ + } \ + else \ + cursorCol = 0; \ + } \ + } + + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + if ((e->state () & Qt::ControlButton) == 0) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tmove single char" << endl; + #endif + + MOVE_CURSOR_RIGHT (); + viewManager ()->setTextCursorPosition (cursorRow, cursorCol); + } + else + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tmove to start of word" << endl; + #endif + + // (these comments will exclude the last row,end col boundary case) + + #define IS_AT_END() (cursorRow == (int) textLines.size () - 1 && \ + cursorCol == (int) textLines [cursorRow].length ()) + + // Find space + while (!IS_AT_END () && !IS_SPACE (textLines [cursorRow][cursorCol])) + { + MOVE_CURSOR_RIGHT (); + } + + // --- now we're on a space --- + + // Find letter + while (!IS_AT_END () && IS_SPACE (textLines [cursorRow][cursorCol])) + { + MOVE_CURSOR_RIGHT (); + } + + // --- now we're on a letter --- + + viewManager ()->setTextCursorPosition (cursorRow, cursorCol); + + #undef IS_AT_END + } + + #undef MOVE_CURSOR_RIGHT + + e->accept (); + } + else if (e->key () == Qt::Key_Home) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\thome pressed" << endl; + #endif + + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + if (e->state () & Qt::ControlButton) + cursorRow = 0; + + cursorCol = 0; + + viewManager ()->setTextCursorPosition (cursorRow, cursorCol); + + e->accept (); + } + else if (e->key () == Qt::Key_End) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tend pressed" << endl; + #endif + + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + if (e->state () & Qt::ControlButton) + cursorRow = textLines.size () - 1; + + cursorCol = textLines [cursorRow].length (); + + viewManager ()->setTextCursorPosition (cursorRow, cursorCol); + + e->accept (); + } + else + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\ttext='" << e->text () << "'" << endl; + #endif + QString usableText; + for (int i = 0; i < (int) e->text ().length (); i++) + { + if (e->text ().at (i).isPrint ()) + usableText += e->text ().at (i); + } + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tusableText='" << usableText << "'" << endl; + #endif + + if (usableText.length () > 0) + { + if (!m_insertCommand) + { + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + m_insertCommand = new kpToolTextInsertCommand (i18n ("Text: Write"), + viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), + usableText, + mainWindow ()); + commandHistory ()->addCommand (m_insertCommand, false/*no exec*/); + } + else + m_insertCommand->addText (usableText); + + e->accept (); + } + } +#undef IS_SPACE + + + if (!e->isAccepted ()) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tkey processing did not accept (text was '" + << e->text () + << "') - passing on event to kpToolSelection" + << endl; + #endif + //if (hasBegunShape ()) + // endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + kpToolSelection::keyPressEvent (e); + return; + } +} + +void kpToolText::imStartEvent (QIMEvent *e) +{ +#if DEBUG_KP_TOOL_TEXT && 1 + kdDebug () << "kpToolText::imStartEvent() text='" << e->text () + << " cursorPos=" << e->cursorPos () + << " selectionLength=" << e->selectionLength () + << endl; +#endif + + kpSelection *sel = document ()->selection (); + if (hasBegunDraw() || !sel || !sel->isText ()) + { + e->ignore(); + return; + } + + m_IMStartCursorRow = viewManager ()->textCursorRow (); + m_IMStartCursorCol = viewManager ()->textCursorCol (); + m_IMPreeditStr = QString::null; +} + +void kpToolText::imComposeEvent (QIMEvent *e) +{ +#if DEBUG_KP_TOOL_TEXT && 1 + kdDebug () << "kpToolText::imComposeEvent() text='" << e->text () + << " cursorPos=" << e->cursorPos () + << " selectionLength=" << e->selectionLength () + << endl; +#endif + + kpSelection *sel = document ()->selection (); + if (hasBegunDraw() || !sel || !sel->isText ()) + { + e->ignore(); + return; + } + + // remove old preedit + if (m_IMPreeditStr.length() > 0 ) + { + // set cursor at the start input point + viewManager ()->setTextCursorPosition (m_IMStartCursorRow, m_IMStartCursorCol); + for (unsigned int i = 0; i < m_IMPreeditStr.length(); i++) + { + if (!m_deleteCommand) + { + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + m_deleteCommand = new kpToolTextDeleteCommand (i18n ("Text: Delete"), + viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), + mainWindow ()); + commandHistory ()->addCommand (m_deleteCommand, false/*no exec*/); + } + else + m_deleteCommand->addDelete (); + } + } + + // insert new preedit + m_IMPreeditStr = e->text(); + if (m_IMPreeditStr.length() > 0) + { + if (!m_insertCommand) + { + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + m_insertCommand = new kpToolTextInsertCommand (i18n ("Text: Write"), + viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), + m_IMPreeditStr, + mainWindow ()); + commandHistory ()->addCommand (m_insertCommand, false/*no exec*/); + } + else + m_insertCommand->addText (m_IMPreeditStr); + } + + // set cursor pos + if (m_IMStartCursorRow >= 0) + { + int row = m_IMStartCursorRow; + int col = m_IMStartCursorCol + e->cursorPos () /* + e->selectionLength()*/; + viewManager ()->setTextCursorPosition (row, col, true /* update MicroFocusHint */); + } +} + +void kpToolText::imEndEvent (QIMEvent *e) +{ +#if DEBUG_KP_TOOL_TEXT && 1 + kdDebug () << "kpToolText::imEndEvent() text='" << e->text () + << " cursorPos=" << e->cursorPos () + << " selectionLength=" << e->selectionLength () + << endl; +#endif + + kpSelection *sel = document ()->selection (); + if (hasBegunDraw() || !sel || !sel->isText ()) + { + e->ignore(); + return; + } + + // remove old preedit + if (m_IMPreeditStr.length() > 0 ) + { + // set cursor at the start input point + viewManager ()->setTextCursorPosition (m_IMStartCursorRow, m_IMStartCursorCol); + for (unsigned int i = 0; i < m_IMPreeditStr.length(); i++) + { + if (!m_deleteCommand) + { + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + m_deleteCommand = new kpToolTextDeleteCommand (i18n ("Text: Delete"), + viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), + mainWindow ()); + commandHistory ()->addCommand (m_deleteCommand, false/*no exec*/); + } + else + m_deleteCommand->addDelete (); + } + } + m_IMPreeditStr = QString::null; + + // commit string + QString inputStr = e->text(); + if (inputStr.length() > 0) + { + if (!m_insertCommand) + { + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + m_insertCommand = new kpToolTextInsertCommand (i18n ("Text: Write"), + viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), + inputStr, + mainWindow ()); + commandHistory ()->addCommand (m_insertCommand, false/*no exec*/); + } + else + m_insertCommand->addText (inputStr); + } +} + + +// protected +bool kpToolText::shouldChangeTextStyle () const +{ + if (mainWindow ()->settingTextStyle ()) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\trecursion - abort setting text style: " + << mainWindow ()->settingTextStyle () + << endl; + #endif + return false; + } + + if (!document ()->selection () || + !document ()->selection ()->isText ()) + { + #if DEBUG_KP_TOOL_TEXT + kdDebug () << "\tno text selection - abort setting text style" << endl; + #endif + return false; + } + + return true; +} + +// protected +void kpToolText::changeTextStyle (const QString &name, + const kpTextStyle &newTextStyle, + const kpTextStyle &oldTextStyle) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::changeTextStyle(" << name << ")" << endl; +#endif + + if (hasBegunShape ()) + endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ()); + + commandHistory ()->addCommand ( + new kpToolTextChangeStyleCommand ( + name, + newTextStyle, + oldTextStyle, + mainWindow ())); +} + + +// protected slot virtual [base kpToolSelection] +void kpToolText::slotIsOpaqueChanged () +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotIsOpaqueChanged()" << endl; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setBackgroundOpaque (!m_toolWidgetOpaqueOrTransparent->isOpaque ()); + + changeTextStyle (newTextStyle.isBackgroundOpaque () ? + i18n ("Text: Opaque Background") : + i18n ("Text: Transparent Background"), + newTextStyle, + oldTextStyle); +} + +// protected slot virtual [base kpTool] +void kpToolText::slotColorsSwapped (const kpColor &newForegroundColor, + const kpColor &newBackgroundColor) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotColorsSwapped()" << endl; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setForegroundColor (newBackgroundColor); + oldTextStyle.setBackgroundColor (newForegroundColor); + + changeTextStyle (i18n ("Text: Swap Colors"), + newTextStyle, + oldTextStyle); +} + +// protected slot virtual [base kpTool] +void kpToolText::slotForegroundColorChanged (const kpColor & /*color*/) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotForegroundColorChanged()" << endl; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setForegroundColor (oldForegroundColor ()); + + changeTextStyle (i18n ("Text: Foreground Color"), + newTextStyle, + oldTextStyle); +} + +// protected slot virtual [base kpToolSelection] +void kpToolText::slotBackgroundColorChanged (const kpColor & /*color*/) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotBackgroundColorChanged()" << endl; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setBackgroundColor (oldBackgroundColor ()); + + changeTextStyle (i18n ("Text: Background Color"), + newTextStyle, + oldTextStyle); +} + +// protected slot virtual [base kpToolSelection] +void kpToolText::slotColorSimilarityChanged (double, int) +{ + // --- don't pass on event to kpToolSelection which would have set the + // SelectionTransparency - not relevant to the Text Tool --- +} + + +// public slot +void kpToolText::slotFontFamilyChanged (const QString &fontFamily, + const QString &oldFontFamily) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotFontFamilyChanged() new=" + << fontFamily + << " old=" + << oldFontFamily + << endl; +#else + (void) fontFamily; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setFontFamily (oldFontFamily); + + changeTextStyle (i18n ("Text: Font"), + newTextStyle, + oldTextStyle); +} + +// public slot +void kpToolText::slotFontSizeChanged (int fontSize, int oldFontSize) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotFontSizeChanged() new=" + << fontSize + << " old=" + << oldFontSize + << endl; +#else + (void) fontSize; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setFontSize (oldFontSize); + + changeTextStyle (i18n ("Text: Font Size"), + newTextStyle, + oldTextStyle); +} + + +// public slot +void kpToolText::slotBoldChanged (bool isBold) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotBoldChanged(" << isBold << ")" << endl; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setBold (!isBold); + + changeTextStyle (i18n ("Text: Bold"), + newTextStyle, + oldTextStyle); +} + +// public slot +void kpToolText::slotItalicChanged (bool isItalic) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotItalicChanged(" << isItalic << ")" << endl; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setItalic (!isItalic); + + changeTextStyle (i18n ("Text: Italic"), + newTextStyle, + oldTextStyle); +} + +// public slot +void kpToolText::slotUnderlineChanged (bool isUnderline) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotUnderlineChanged(" << isUnderline << ")" << endl; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setUnderline (!isUnderline); + + changeTextStyle (i18n ("Text: Underline"), + newTextStyle, + oldTextStyle); +} + +// public slot +void kpToolText::slotStrikeThruChanged (bool isStrikeThru) +{ +#if DEBUG_KP_TOOL_TEXT + kdDebug () << "kpToolText::slotStrikeThruChanged(" << isStrikeThru << ")" << endl; +#endif + + if (!shouldChangeTextStyle ()) + return; + + kpTextStyle newTextStyle = mainWindow ()->textStyle (); + kpTextStyle oldTextStyle = newTextStyle; + oldTextStyle.setStrikeThru (!isStrikeThru); + + changeTextStyle (i18n ("Text: Strike Through"), + newTextStyle, + oldTextStyle); +} + + +/* + * kpToolTextChangeStyleCommand + */ + +kpToolTextChangeStyleCommand::kpToolTextChangeStyleCommand (const QString &name, + const kpTextStyle &newTextStyle, const kpTextStyle &oldTextStyle, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_newTextStyle (newTextStyle), + m_oldTextStyle (oldTextStyle) +{ +} + +kpToolTextChangeStyleCommand::~kpToolTextChangeStyleCommand () +{ +} + + +// public virtual [base kpCommand] +int kpToolTextChangeStyleCommand::size () const +{ + return 0; +} + + +// public virtual [base kpCommand] +void kpToolTextChangeStyleCommand::execute () +{ +#if DEBUG_KP_TOOL_TEXT && 1 + kdDebug () << "kpToolTextChangeStyleCommand::execute()" + << " font=" << m_newTextStyle.fontFamily () + << " fontSize=" << m_newTextStyle.fontSize () + << " isBold=" << m_newTextStyle.isBold () + << " isItalic=" << m_newTextStyle.isItalic () + << " isUnderline=" << m_newTextStyle.isUnderline () + << " isStrikeThru=" << m_newTextStyle.isStrikeThru () + << endl; +#endif + + m_mainWindow->setTextStyle (m_newTextStyle); + if (selection ()) + selection ()->setTextStyle (m_newTextStyle); + else + kdError () << "kpToolTextChangeStyleCommand::execute() without sel" << endl; +} + +// public virtual [base kpCommand] +void kpToolTextChangeStyleCommand::unexecute () +{ +#if DEBUG_KP_TOOL_TEXT && 1 + kdDebug () << "kpToolTextChangeStyleCommand::unexecute()" + << " font=" << m_newTextStyle.fontFamily () + << " fontSize=" << m_newTextStyle.fontSize () + << " isBold=" << m_newTextStyle.isBold () + << " isItalic=" << m_newTextStyle.isItalic () + << " isUnderline=" << m_newTextStyle.isUnderline () + << " isStrikeThru=" << m_newTextStyle.isStrikeThru () + << endl; +#endif + + m_mainWindow->setTextStyle (m_oldTextStyle); + if (selection ()) + selection ()->setTextStyle (m_oldTextStyle); + else + kdError () << "kpToolTextChangeStyleCommand::unexecute() without sel" << endl; +} + + +/* + * kpToolTextInsertCommand + */ + +kpToolTextInsertCommand::kpToolTextInsertCommand (const QString &name, + int row, int col, QString newText, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_row (row), m_col (col) +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + addText (newText); +} + +kpToolTextInsertCommand::~kpToolTextInsertCommand () +{ +} + + +// public +void kpToolTextInsertCommand::addText (const QString &moreText) +{ + if (moreText.isEmpty ()) + return; + + QValueVector <QString> textLines = selection ()->textLines (); + const QString leftHalf = textLines [m_row].left (m_col); + const QString rightHalf = textLines [m_row].mid (m_col); + textLines [m_row] = leftHalf + moreText + rightHalf; + selection ()->setTextLines (textLines); + + m_newText += moreText; + m_col += moreText.length (); + + viewManager ()->setTextCursorPosition (m_row, m_col); +} + + +// public virtual [base kpCommand] +int kpToolTextInsertCommand::size () const +{ + return m_newText.length () * sizeof (QChar); +} + + +// public virtual [base kpCommand] +void kpToolTextInsertCommand::execute () +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + + QString text = m_newText; + m_newText = QString::null; + addText (text); +} + +// public virtual [base kpCommand] +void kpToolTextInsertCommand::unexecute () +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + + QValueVector <QString> textLines = selection ()->textLines (); + const QString leftHalf = textLines [m_row].left (m_col - m_newText.length ()); + const QString rightHalf = textLines [m_row].mid (m_col); + textLines [m_row] = leftHalf + rightHalf; + selection ()->setTextLines (textLines); + + m_col -= m_newText.length (); + + viewManager ()->setTextCursorPosition (m_row, m_col); +} + + +/* + * kpToolTextEnterCommand + */ + +kpToolTextEnterCommand::kpToolTextEnterCommand (const QString &name, + int row, int col, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_row (row), m_col (col), + m_numEnters (0) +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + addEnter (); +} + +kpToolTextEnterCommand::~kpToolTextEnterCommand () +{ +} + + +// public +void kpToolTextEnterCommand::addEnter () +{ + QValueVector <QString> textLines = selection ()->textLines (); + + const QString rightHalf = textLines [m_row].mid (m_col); + + textLines [m_row].truncate (m_col); + textLines.insert (textLines.begin () + m_row + 1, rightHalf); + + selection ()->setTextLines (textLines); + + m_row++; + m_col = 0; + + viewManager ()->setTextCursorPosition (m_row, m_col); + + m_numEnters++; +} + + +// public virtual [base kpCommand] +int kpToolTextEnterCommand::size () const +{ + return 0; +} + + +// public virtual [base kpCommand] +void kpToolTextEnterCommand::execute () +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + int oldNumEnters = m_numEnters; + m_numEnters = 0; + + for (int i = 0; i < oldNumEnters; i++) + addEnter (); +} + +// public virtual [base kpCommand] +void kpToolTextEnterCommand::unexecute () +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + + QValueVector <QString> textLines = selection ()->textLines (); + + for (int i = 0; i < m_numEnters; i++) + { + if (m_col != 0) + { + kdError () << "kpToolTextEnterCommand::unexecute() col=" << m_col << endl; + break; + } + + if (m_row <= 0) + break; + + int newRow = m_row - 1; + int newCol = textLines [newRow].length (); + + textLines [newRow] += textLines [m_row]; + + textLines.erase (textLines.begin () + m_row); + + m_row = newRow; + m_col = newCol; + } + + selection ()->setTextLines (textLines); + + viewManager ()->setTextCursorPosition (m_row, m_col); +} + + +/* + * kpToolTextBackspaceCommand + */ + +kpToolTextBackspaceCommand::kpToolTextBackspaceCommand (const QString &name, + int row, int col, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_row (row), m_col (col), + m_numBackspaces (0) +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + addBackspace (); +} + +kpToolTextBackspaceCommand::~kpToolTextBackspaceCommand () +{ +} + + +// public +void kpToolTextBackspaceCommand::addBackspace () +{ + QValueVector <QString> textLines = selection ()->textLines (); + + if (m_col > 0) + { + m_deletedText.prepend (textLines [m_row][m_col - 1]); + + textLines [m_row] = textLines [m_row].left (m_col - 1) + + textLines [m_row].mid (m_col); + m_col--; + } + else + { + if (m_row > 0) + { + int newCursorRow = m_row - 1; + int newCursorCol = textLines [newCursorRow].length (); + + m_deletedText.prepend ('\n'); + + textLines [newCursorRow] += textLines [m_row]; + + textLines.erase (textLines.begin () + m_row); + + m_row = newCursorRow; + m_col = newCursorCol; + } + } + + selection ()->setTextLines (textLines); + + viewManager ()->setTextCursorPosition (m_row, m_col); + + m_numBackspaces++; +} + + +// public virtual [base kpCommand] +int kpToolTextBackspaceCommand::size () const +{ + return m_deletedText.length () * sizeof (QChar); +} + + +// public virtual [base kpCommand] +void kpToolTextBackspaceCommand::execute () +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + + m_deletedText = QString::null; + int oldNumBackspaces = m_numBackspaces; + m_numBackspaces = 0; + + for (int i = 0; i < oldNumBackspaces; i++) + addBackspace (); +} + +// public virtual [base kpCommand] +void kpToolTextBackspaceCommand::unexecute () +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + + QValueVector <QString> textLines = selection ()->textLines (); + + for (int i = 0; i < (int) m_deletedText.length (); i++) + { + if (m_deletedText [i] == '\n') + { + const QString rightHalf = textLines [m_row].mid (m_col); + + textLines [m_row].truncate (m_col); + textLines.insert (textLines.begin () + m_row + 1, rightHalf); + + m_row++; + m_col = 0; + } + else + { + const QString leftHalf = textLines [m_row].left (m_col); + const QString rightHalf = textLines [m_row].mid (m_col); + + textLines [m_row] = leftHalf + m_deletedText [i] + rightHalf; + m_col++; + } + } + + m_deletedText = QString::null; + + selection ()->setTextLines (textLines); + + viewManager ()->setTextCursorPosition (m_row, m_col); +} + + +/* + * kpToolTextDeleteCommand + */ + +kpToolTextDeleteCommand::kpToolTextDeleteCommand (const QString &name, + int row, int col, + kpMainWindow *mainWindow) + : kpNamedCommand (name, mainWindow), + m_row (row), m_col (col), + m_numDeletes (0) +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + addDelete (); +} + +kpToolTextDeleteCommand::~kpToolTextDeleteCommand () +{ +} + + +// public +void kpToolTextDeleteCommand::addDelete () +{ + QValueVector <QString> textLines = selection ()->textLines (); + + if (m_col < (int) textLines [m_row].length ()) + { + m_deletedText.prepend (textLines [m_row][m_col]); + + textLines [m_row] = textLines [m_row].left (m_col) + + textLines [m_row].mid (m_col + 1); + } + else + { + if (m_row < (int) textLines.size () - 1) + { + m_deletedText.prepend ('\n'); + + textLines [m_row] += textLines [m_row + 1]; + textLines.erase (textLines.begin () + m_row + 1); + } + } + + selection ()->setTextLines (textLines); + + viewManager ()->setTextCursorPosition (m_row, m_col); + + m_numDeletes++; +} + + +// public virtual [base kpCommand] +int kpToolTextDeleteCommand::size () const +{ + return m_deletedText.length () * sizeof (QChar); +} + + +// public virtual [base kpCommand] +void kpToolTextDeleteCommand::execute () +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + + m_deletedText = QString::null; + int oldNumDeletes = m_numDeletes; + m_numDeletes = 0; + + for (int i = 0; i < oldNumDeletes; i++) + addDelete (); +} + +// public virtual [base kpCommand] +void kpToolTextDeleteCommand::unexecute () +{ + viewManager ()->setTextCursorPosition (m_row, m_col); + + QValueVector <QString> textLines = selection ()->textLines (); + + for (int i = 0; i < (int) m_deletedText.length (); i++) + { + if (m_deletedText [i] == '\n') + { + const QString rightHalf = textLines [m_row].mid (m_col); + + textLines [m_row].truncate (m_col); + textLines.insert (textLines.begin () + m_row + 1, rightHalf); + } + else + { + const QString leftHalf = textLines [m_row].left (m_col); + const QString rightHalf = textLines [m_row].mid (m_col); + + textLines [m_row] = leftHalf + m_deletedText [i] + rightHalf; + } + } + + m_deletedText = QString::null; + + selection ()->setTextLines (textLines); + + viewManager ()->setTextCursorPosition (m_row, m_col); +} + + +#include <kptooltext.moc> diff --git a/kolourpaint/tools/kptooltext.h b/kolourpaint/tools/kptooltext.h new file mode 100644 index 00000000..a99654b7 --- /dev/null +++ b/kolourpaint/tools/kptooltext.h @@ -0,0 +1,203 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org> + 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. +*/ + + +#ifndef __kp_tool_text_h__ +#define __kp_tool_text_h__ + +#include <qstring.h> + +#include <kpcommandhistory.h> + +#include <kptextstyle.h> +#include <kptoolselection.h> + +class kpColor; +class kpMainWindow; +class kpSelection; +class kpViewManager; + +class kpToolText : public kpToolSelection +{ +Q_OBJECT + +public: + kpToolText (kpMainWindow *mainWindow); + virtual ~kpToolText (); + + virtual bool careAboutColorsSwapped () const { return true; } + + virtual void begin (); + virtual void end (); + + bool hasBegunText () const; + virtual bool hasBegunShape () const; + virtual void cancelShape (); + virtual void endShape (const QPoint &thisPoint, const QRect &normalizedRect); + +protected: + virtual void keyPressEvent (QKeyEvent *e); + virtual void imStartEvent (QIMEvent *e); + virtual void imComposeEvent (QIMEvent *e); + virtual void imEndEvent (QIMEvent *e); + +protected: + bool shouldChangeTextStyle () const; + void changeTextStyle (const QString &name, + const kpTextStyle &newTextStyle, + const kpTextStyle &oldTextStyle); + +protected slots: + virtual void slotIsOpaqueChanged (); + virtual void slotColorsSwapped (const kpColor &newForegroundColor, + const kpColor &newBackgroundColor); + virtual void slotForegroundColorChanged (const kpColor &color); + virtual void slotBackgroundColorChanged (const kpColor &color); + virtual void slotColorSimilarityChanged (double, int); + +public slots: + void slotFontFamilyChanged (const QString &fontFamily, const QString &oldFontFamily); + void slotFontSizeChanged (int fontSize, int oldFontSize); + void slotBoldChanged (bool isBold); + void slotItalicChanged (bool isItalic); + void slotUnderlineChanged (bool isUnderline); + void slotStrikeThruChanged (bool isStrikeThru); + +protected: + class kpToolTextInsertCommand *m_insertCommand; + class kpToolTextEnterCommand *m_enterCommand; + class kpToolTextBackspaceCommand *m_backspaceCommand; + class kpToolTextDeleteCommand *m_deleteCommand; + + bool m_isIMStarted; + int m_IMStartCursorRow; + int m_IMStartCursorCol; + QString m_IMPreeditStr; +}; + + +class kpToolTextChangeStyleCommand : public kpNamedCommand +{ +public: + kpToolTextChangeStyleCommand (const QString &name, + const kpTextStyle &newTextStyle, const kpTextStyle &oldTextStyle, + kpMainWindow *mainWindow); + virtual ~kpToolTextChangeStyleCommand (); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +protected: + kpTextStyle m_newTextStyle, m_oldTextStyle; +}; + +class kpToolTextInsertCommand : public kpNamedCommand +{ +public: + kpToolTextInsertCommand (const QString &name, + int row, int col, QString newText, + kpMainWindow *mainWindow); + virtual ~kpToolTextInsertCommand (); + + void addText (const QString &moreText); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +protected: + int m_row, m_col; + QString m_newText; +}; + +class kpToolTextEnterCommand : public kpNamedCommand +{ +public: + kpToolTextEnterCommand (const QString &name, + int row, int col, + kpMainWindow *mainWindow); + virtual ~kpToolTextEnterCommand (); + + void addEnter (); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +protected: + int m_row, m_col; + int m_numEnters; +}; + +class kpToolTextBackspaceCommand : public kpNamedCommand +{ +public: + kpToolTextBackspaceCommand (const QString &name, + int row, int col, + kpMainWindow *mainWindow); + virtual ~kpToolTextBackspaceCommand (); + + void addBackspace (); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +protected: + int m_row, m_col; + int m_numBackspaces; + QString m_deletedText; +}; + +class kpToolTextDeleteCommand : public kpNamedCommand +{ +public: + kpToolTextDeleteCommand (const QString &name, + int row, int col, + kpMainWindow *mainWindow); + virtual ~kpToolTextDeleteCommand (); + + void addDelete (); + + virtual int size () const; + + virtual void execute (); + virtual void unexecute (); + +protected: + int m_row, m_col; + int m_numDeletes; + QString m_deletedText; +}; + +#endif // __kp_tool_text_h__ + |