1 // vim: set tabstop=4 shiftwidth=4 noexpandtab:
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.
25 #include <QMouseEvent>
33 #include "imageview.h"
35 static const int HANDLE_RADIUS
= 5;
37 static const int UNINITIALIZED_X
= -1;
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
,
56 struct CropToolPrivate
{
59 QList
<CropHandle
> mCropHandleList
;
60 CropHandle mMovingHandle
;
61 QPoint mLastMouseMovePos
;
64 QRect
handleViewportRect(CropHandle handle
) {
65 QRect viewportCropRect
= mCropTool
->imageView()->mapToViewport(mRect
);
67 if (handle
& CH_Top
) {
68 top
= viewportCropRect
.top() - HANDLE_RADIUS
;
69 } else if (handle
& CH_Bottom
) {
70 top
= viewportCropRect
.bottom() - HANDLE_RADIUS
;
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
;
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
)) {
94 QRect viewportCropRect
= mCropTool
->imageView()->mapToViewport(mRect
);
95 if (viewportCropRect
.contains(pos
)) {
101 void updateCursor(CropHandle handle
, bool buttonDown
) {
102 Qt::CursorShape shape
;
106 shape
= Qt::SizeFDiagCursor
;
111 shape
= Qt::SizeBDiagCursor
;
116 shape
= Qt::SizeHorCursor
;
121 shape
= Qt::SizeVerCursor
;
125 shape
= buttonDown
? Qt::ClosedHandCursor
: Qt::OpenHandCursor
;
129 shape
= Qt::ForbiddenCursor
;
132 mCropTool
->imageView()->viewport()->setCursor(shape
);
137 CropTool::CropTool(ImageView
* view
)
138 : AbstractImageViewTool(view
)
139 , d(new CropToolPrivate
) {
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
);
148 CropTool::~CropTool() {
153 void CropTool::setCropRatio(double ratio
) {
154 d
->mCropRatio
= ratio
;
158 void CropTool::setRect(const QRect
& rect
) {
160 imageView()->viewport()->update();
164 void CropTool::paint(QPainter
* painter
) {
165 if (d
->mRect
.x() == UNINITIALIZED_X
) {
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
);
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*/);
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,
224 if (posX
== d
->mRect
.x() || posY
== d
->mRect
.y()) {
225 // We can't figure the handle yet
228 if (posX
< d
->mRect
.x()) {
229 d
->mMovingHandle
= CH_Left
;
231 d
->mMovingHandle
= CH_Right
;
234 if (posY
< d
->mRect
.y()) {
235 d
->mMovingHandle
= CropHandle(d
->mMovingHandle
| CH_Top
);
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
) {
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
) {
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
) {
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
);