polish
[kdegraphics.git] / gwenview / lib / croptool.cpp
bloba9bb1ebbfe1cb720a72fda734048e08adcd0c729
1 // vim: set tabstop=4 shiftwidth=4 noexpandtab:
2 /*
3 Gwenview: an image viewer
4 Copyright 2007 Aurélien Gâteau <aurelien.gateau@free.fr>
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 // Self
22 #include "croptool.h"
24 // Qt
25 #include <QMouseEvent>
26 #include <QPainter>
27 #include <QRect>
29 // KDE
30 #include <kdebug.h>
32 // Local
33 #include "imageview.h"
35 static const int HANDLE_RADIUS = 5;
37 static const int UNINITIALIZED_X = -1;
39 namespace Gwenview {
42 enum CropHandle {
43 CH_None,
44 CH_Top = 1,
45 CH_Left = 2,
46 CH_Right = 4,
47 CH_Bottom = 8,
48 CH_TopLeft = CH_Top | CH_Left,
49 CH_BottomLeft = CH_Bottom | CH_Left,
50 CH_TopRight = CH_Top | CH_Right,
51 CH_BottomRight = CH_Bottom | CH_Right,
52 CH_Content = 16
56 struct CropToolPrivate {
57 CropTool* mCropTool;
58 QRect mRect;
59 QList<CropHandle> mCropHandleList;
60 CropHandle mMovingHandle;
61 QPoint mLastMouseMovePos;
62 double mCropRatio;
64 QRect handleViewportRect(CropHandle handle) {
65 QRect viewportCropRect = mCropTool->imageView()->mapToViewport(mRect);
66 int left, top;
67 if (handle & CH_Top) {
68 top = viewportCropRect.top() - HANDLE_RADIUS;
69 } else if (handle & CH_Bottom) {
70 top = viewportCropRect.bottom() - HANDLE_RADIUS;
71 } else {
72 top = viewportCropRect.top() + viewportCropRect.height() / 2 - HANDLE_RADIUS;
75 if (handle & CH_Left) {
76 left = viewportCropRect.left() - HANDLE_RADIUS;
77 } else if (handle & CH_Right) {
78 left = viewportCropRect.right() - HANDLE_RADIUS;
79 } else {
80 left = viewportCropRect.left() + viewportCropRect.width() / 2 - HANDLE_RADIUS;
83 return QRect(left, top, HANDLE_RADIUS * 2 + 1, HANDLE_RADIUS * 2 + 1);
87 CropHandle handleAt(const QPoint& pos) {
88 Q_FOREACH(const CropHandle& handle, mCropHandleList) {
89 QRect rect = handleViewportRect(handle);
90 if (rect.contains(pos)) {
91 return handle;
94 QRect viewportCropRect = mCropTool->imageView()->mapToViewport(mRect);
95 if (viewportCropRect.contains(pos)) {
96 return CH_Content;
98 return CH_None;
101 void updateCursor(CropHandle handle, bool buttonDown) {
102 Qt::CursorShape shape;
103 switch (handle) {
104 case CH_TopLeft:
105 case CH_BottomRight:
106 shape = Qt::SizeFDiagCursor;
107 break;
109 case CH_TopRight:
110 case CH_BottomLeft:
111 shape = Qt::SizeBDiagCursor;
112 break;
114 case CH_Left:
115 case CH_Right:
116 shape = Qt::SizeHorCursor;
117 break;
119 case CH_Top:
120 case CH_Bottom:
121 shape = Qt::SizeVerCursor;
122 break;
124 case CH_Content:
125 shape = buttonDown ? Qt::ClosedHandCursor : Qt::OpenHandCursor;
126 break;
128 default:
129 shape = Qt::ForbiddenCursor;
130 break;
132 mCropTool->imageView()->viewport()->setCursor(shape);
137 CropTool::CropTool(ImageView* view)
138 : AbstractImageViewTool(view)
139 , d(new CropToolPrivate) {
140 d->mCropTool = this;
141 d->mCropHandleList << CH_Left << CH_Right << CH_Top << CH_Bottom << CH_TopLeft << CH_TopRight << CH_BottomLeft << CH_BottomRight;
142 d->mMovingHandle = CH_None;
143 d->mRect.setX(UNINITIALIZED_X);
144 d->mCropRatio = 0.;
148 CropTool::~CropTool() {
149 delete d;
153 void CropTool::setCropRatio(double ratio) {
154 d->mCropRatio = ratio;
158 void CropTool::setRect(const QRect& rect) {
159 d->mRect = rect;
160 imageView()->viewport()->update();
164 void CropTool::paint(QPainter* painter) {
165 if (d->mRect.x() == UNINITIALIZED_X) {
166 return;
168 QRect rect = imageView()->mapToViewport(d->mRect);
170 QRect imageRect = imageView()->rect();
172 QRegion outerRegion = QRegion(imageRect) - QRegion(rect);
173 Q_FOREACH(const QRect& outerRect, outerRegion.rects()) {
174 painter->fillRect(outerRect, QColor(0, 0, 0, 128));
177 painter->setPen(QPen(Qt::black));
179 rect.adjust(0, 0, -1, -1);
180 painter->drawRect(rect);
182 painter->setBrush(Qt::gray);
183 painter->setRenderHint(QPainter::Antialiasing);
184 Q_FOREACH(const CropHandle& handle, d->mCropHandleList) {
185 rect = d->handleViewportRect(handle);
186 painter->drawEllipse(rect);
191 void CropTool::mousePressEvent(QMouseEvent* event) {
192 if (d->mRect.x() == UNINITIALIZED_X) {
193 // Nothing selected, user is creating the crop rect
194 QPoint pos = imageView()->mapToImage(event->pos());
195 d->mRect = QRect(pos, QSize(0, 0));
197 imageView()->viewport()->update();
198 rectUpdated(d->mRect);
199 return;
201 d->mMovingHandle = d->handleAt(event->pos());
202 d->updateCursor(d->mMovingHandle, event->buttons() != Qt::NoButton);
204 if (d->mMovingHandle == CH_Content) {
205 d->mLastMouseMovePos = imageView()->mapToImage(event->pos());
210 void CropTool::mouseMoveEvent(QMouseEvent* event) {
211 if (event->buttons() == Qt::NoButton && d->mRect.x() != UNINITIALIZED_X) {
212 // Make sure cursor is updated when moving over handles
213 CropHandle handle = d->handleAt(event->pos());
214 d->updateCursor(handle, false/* buttonDown*/);
215 return;
218 QPoint point = imageView()->mapToImage(event->pos());
219 int posX = point.x(), posY = point.y();
221 if (d->mRect.x() != UNINITIALIZED_X && d->mRect.size() == QSize(0, 0)) {
222 // User is creating rect, thus d->mMovingHandle has not been set yet,
223 // figure it out now
224 if (posX == d->mRect.x() || posY == d->mRect.y()) {
225 // We can't figure the handle yet
226 return;
228 if (posX < d->mRect.x()) {
229 d->mMovingHandle = CH_Left;
230 } else {
231 d->mMovingHandle = CH_Right;
234 if (posY < d->mRect.y()) {
235 d->mMovingHandle = CropHandle(d->mMovingHandle | CH_Top);
236 } else {
237 d->mMovingHandle = CropHandle(d->mMovingHandle | CH_Bottom);
240 // Now that we have d->mMovingHandle, we can set the matching cursor shape
241 d->updateCursor(d->mMovingHandle, true /*buttonDown*/);
244 if (d->mMovingHandle == CH_None) {
245 return;
248 if (d->mMovingHandle & CH_Top) {
249 d->mRect.setTop( qMin(posY, d->mRect.bottom()) );
250 } else if (d->mMovingHandle & CH_Bottom) {
251 d->mRect.setBottom( qMax(posY, d->mRect.top()) );
253 if (d->mMovingHandle & CH_Left) {
254 d->mRect.setLeft( qMin(posX, d->mRect.right()) );
255 } else if (d->mMovingHandle & CH_Right) {
256 d->mRect.setRight( qMax(posX, d->mRect.left()) );
259 if (d->mCropRatio > 0.) {
260 if (d->mMovingHandle == CH_Top || d->mMovingHandle == CH_Bottom) {
261 // Top or bottom
262 int width = int(d->mRect.height() / d->mCropRatio);
263 d->mRect.setWidth(width);
264 } else if (d->mMovingHandle == CH_Left || d->mMovingHandle == CH_Right) {
265 // Left or right
266 int height = int(d->mRect.width() * d->mCropRatio);
267 d->mRect.setHeight(height);
268 } else if (d->mMovingHandle & CH_Top) {
269 // Top left or top right
270 int height = int(d->mRect.width() * d->mCropRatio);
271 d->mRect.setTop(d->mRect.bottom() - height);
272 } else if (d->mMovingHandle & CH_Bottom) {
273 // Bottom left or bottom right
274 int height = int(d->mRect.width() * d->mCropRatio);
275 d->mRect.setHeight(height);
279 if (d->mMovingHandle == CH_Content) {
280 QPoint delta = point - d->mLastMouseMovePos;
281 d->mRect.adjust(delta.x(), delta.y(), delta.x(), delta.y());
282 d->mLastMouseMovePos = imageView()->mapToImage(event->pos());
285 imageView()->viewport()->update();
286 rectUpdated(d->mRect);
290 void CropTool::mouseReleaseEvent(QMouseEvent* event) {
291 d->mMovingHandle = CH_None;
292 d->updateCursor(d->handleAt(event->pos()), false);
296 void CropTool::toolActivated() {
297 imageView()->viewport()->setCursor(Qt::CrossCursor);
301 } // namespace