1 /*
2  * %kadu copyright begin%
3  * Copyright 2011 Piotr Galiszewski (piotr.galiszewski@kadu.im)
4  * Copyright 2011 Piotr Dąbrowski (ultr@ultr.pl)
5  * Copyright 2013, 2014 Bartosz Brachaczek (b.brachaczek@gmail.com)
6  * Copyright 2011, 2013, 2014 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
7  * %kadu copyright end%
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <math.h>
24 
25 #include <QtCore/QBuffer>
26 #include <QtCore/QTimer>
27 #include <QtGui/QCursor>
28 #include <QtGui/QResizeEvent>
29 #include <QtWidgets/QGraphicsPixmapItem>
30 #include <QtWidgets/QGraphicsProxyWidget>
31 
32 #include "gui/graphics-items/selection-frame-item.h"
33 #include "gui/widgets/screenshot-tool-box.h"
34 
35 #include "crop-image-widget.h"
36 
37 #define HANDLER_SIZE 10
38 #define HANDLER_HALF_SIZE (HANDLER_SIZE/2)
39 #define TOOLBOX_PADDING 10
40 
CropImageWidget(QWidget * parent)41 CropImageWidget::CropImageWidget(QWidget *parent) :
42 		QGraphicsView(parent), IsMouseButtonPressed(false)
43 {
44 	setContentsMargins(0, 0, 0, 0);
45 	setFrameShape(NoFrame);
46 	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
47 	setInteractive(true);
48 	setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
49 
50 	QGraphicsScene *graphicsScene = new QGraphicsScene(this);
51 	setScene(graphicsScene);
52 
53 	PixmapItem = new QGraphicsPixmapItem();
54 	PixmapItem->setCursor(Qt::CrossCursor);
55 	PixmapItem->setPos(0, 0);
56 
57 	scene()->addItem(PixmapItem);
58 
59 	SelectionFrame = new SelectionFrameItem();
60 	SelectionFrame->setPos(0, 0);
61 	SelectionFrame->setSize(size());
62 
63 	scene()->addItem(SelectionFrame);
64 
65 	ToolBox = new ScreenshotToolBox();
66 	connect(ToolBox, SIGNAL(crop()), this, SLOT(crop()));
67 	connect(ToolBox, SIGNAL(cancel()), this, SIGNAL(canceled()));
68 
69 	ToolBoxTimer = new QTimer(this);
70 	connect(ToolBoxTimer, SIGNAL(timeout()), this, SLOT(updateToolBoxFileSizeHint()));
71 	ToolBoxTimer->start(1000);
72 
73 	ToolBoxProxy = new QGraphicsProxyWidget();
74 	ToolBoxProxy->setWidget(ToolBox);
75 	scene()->addItem(ToolBoxProxy);
76 
77 	TopLeftHandler = new HandlerRectItem(HandlerTopLeft, HANDLER_SIZE);
78 	TopLeftHandler->setCursor(Qt::SizeFDiagCursor);
79 	connect(TopLeftHandler, SIGNAL(movedTo(HandlerType,int,int)), this, SLOT(handlerMovedTo(HandlerType,int,int)));
80 	scene()->addItem(TopLeftHandler);
81 
82 	TopHandler = new HandlerRectItem(HandlerTop, HANDLER_SIZE);
83 	TopHandler->setCursor(Qt::SizeVerCursor);
84 	connect(TopHandler, SIGNAL(movedTo(HandlerType,int,int)), this, SLOT(handlerMovedTo(HandlerType,int,int)));
85 	scene()->addItem(TopHandler);
86 
87 	TopRightHandler = new HandlerRectItem(HandlerTopRight, HANDLER_SIZE);
88 	TopRightHandler->setCursor(Qt::SizeBDiagCursor);
89 	connect(TopRightHandler, SIGNAL(movedTo(HandlerType,int,int)), this, SLOT(handlerMovedTo(HandlerType,int,int)));
90 	scene()->addItem(TopRightHandler);
91 
92 	LeftHandler = new HandlerRectItem(HandlerLeft, HANDLER_SIZE);
93 	LeftHandler->setCursor(Qt::SizeHorCursor);
94 	connect(LeftHandler, SIGNAL(movedTo(HandlerType,int,int)), this, SLOT(handlerMovedTo(HandlerType,int,int)));
95 	scene()->addItem(LeftHandler);
96 
97 	RightHandler = new HandlerRectItem(HandlerRight, HANDLER_SIZE);
98 	RightHandler->setCursor(Qt::SizeHorCursor);
99 	connect(RightHandler, SIGNAL(movedTo(HandlerType,int,int)), this, SLOT(handlerMovedTo(HandlerType,int,int)));
100 	scene()->addItem(RightHandler);
101 
102 	BottomLeftHandler = new HandlerRectItem(HandlerBottomLeft, HANDLER_SIZE);
103 	BottomLeftHandler->setCursor(Qt::SizeBDiagCursor);
104 	connect(BottomLeftHandler, SIGNAL(movedTo(HandlerType,int,int)), this, SLOT(handlerMovedTo(HandlerType,int,int)));
105 	scene()->addItem(BottomLeftHandler);
106 
107 	BottomHandler = new HandlerRectItem(HandlerBottom, HANDLER_SIZE);
108 	BottomHandler->setCursor(Qt::SizeVerCursor);
109 	connect(BottomHandler, SIGNAL(movedTo(HandlerType,int,int)), this, SLOT(handlerMovedTo(HandlerType,int,int)));
110 	scene()->addItem(BottomHandler);
111 
112 	BottomRightHandler = new HandlerRectItem(HandlerBottomRight, HANDLER_SIZE);
113 	BottomRightHandler->setCursor(Qt::SizeFDiagCursor);
114 	connect(BottomRightHandler, SIGNAL(movedTo(HandlerType,int,int)), this, SLOT(handlerMovedTo(HandlerType,int,int)));
115 	scene()->addItem(BottomRightHandler);
116 
117 	CropRect.setTopLeft(QPoint(0, 0));
118 	CropRect.setSize(size());
119 	updateCropRectDisplay();
120 }
121 
~CropImageWidget()122 CropImageWidget::~CropImageWidget()
123 {
124 }
125 
normalizeCropRect()126 void CropImageWidget::normalizeCropRect()
127 {
128 	CropRect = CropRect.normalized();
129 	updateCropRectDisplay();
130 }
131 
updateCropRectDisplay()132 void CropImageWidget::updateCropRectDisplay()
133 {
134 	QRect normalized = CropRect.normalized();
135 	SelectionFrame->setSelection(normalized);
136 
137 	// workaround for wrong cursor on negative coordinates
138 	TopLeftHandler->setPos(CropRect.left() - HANDLER_HALF_SIZE, CropRect.top() - HANDLER_HALF_SIZE);
139 	TopHandler->setPos(normalized.center().x() - HANDLER_HALF_SIZE, CropRect.top() - HANDLER_HALF_SIZE);
140 	TopRightHandler->setPos(CropRect.right() - HANDLER_HALF_SIZE, CropRect.top() - HANDLER_HALF_SIZE);
141 	LeftHandler->setPos(CropRect.left() - HANDLER_HALF_SIZE, normalized.center().y() - HANDLER_HALF_SIZE);
142 	RightHandler->setPos(CropRect.right() - HANDLER_HALF_SIZE, normalized.center().y() - HANDLER_HALF_SIZE);
143 	BottomLeftHandler->setPos(CropRect.left() - HANDLER_HALF_SIZE, CropRect.bottom() - HANDLER_HALF_SIZE);
144 	BottomHandler->setPos(normalized.center().x() - HANDLER_HALF_SIZE, CropRect.bottom() - HANDLER_HALF_SIZE);
145 	BottomRightHandler->setPos(CropRect.right() - HANDLER_HALF_SIZE, CropRect.bottom() - HANDLER_HALF_SIZE);
146 
147 	ToolBox->setGeometry(QString("%1x%2").arg(normalized.width()).arg(normalized.height()));
148 
149 	int left = normalized.right() + TOOLBOX_PADDING;
150 	if (left + ToolBox->width() > width())
151 		left = normalized.left() - TOOLBOX_PADDING - ToolBox->width();
152 	if (left < 0)
153 		left = normalized.center().x() - ToolBox->width() / 2;
154 
155 	int top = normalized.bottom() + TOOLBOX_PADDING;
156 	if (top + ToolBox->height() > height())
157 		top = normalized.top() - TOOLBOX_PADDING - ToolBox->height();
158 	if (top < 0)
159 		top = normalized.center().y() - ToolBox->height() / 2;
160 
161 	ToolBoxProxy->setPos(left, top);
162 
163 	scene()->update(scene()->sceneRect());
164 }
165 
croppedPixmap()166 QPixmap CropImageWidget::croppedPixmap()
167 {
168 	if (CropRect.normalized().isEmpty())
169 		return QPixmap();
170 	return PixmapItem->pixmap().copy(CropRect.normalized());
171 }
172 
handlerMovedTo(HandlerType type,int x,int y)173 void CropImageWidget::handlerMovedTo(HandlerType type, int x, int y)
174 {
175 	if (type == HandlerTopLeft || type == HandlerTop || type == HandlerTopRight)
176 		CropRect.setTop(y);
177 	else if (type == HandlerBottomLeft || type == HandlerBottom || type == HandlerBottomRight)
178 		CropRect.setBottom(y);
179 
180 	if (type == HandlerTopLeft || type == HandlerLeft || type == HandlerBottomLeft)
181 		CropRect.setLeft(x);
182 	else if (type == HandlerTopRight || type == HandlerRight || type == HandlerBottomRight)
183 		CropRect.setRight(x);
184 
185 	updateCropRectDisplay();
186 }
187 
crop()188 void CropImageWidget::crop()
189 {
190 	emit pixmapCropped(croppedPixmap());
191 }
192 
updateToolBoxFileSizeHint()193 void CropImageWidget::updateToolBoxFileSizeHint()
194 {
195 	if (CropRect.normalized() == OldCropRect)
196 		return;
197 	OldCropRect = CropRect.normalized();
198 
199 	QBuffer buffer;
200 	QPixmap pixmap = croppedPixmap();
201 
202 	if (pixmap.isNull())
203 	{
204 		ToolBox->setFileSize("0 KiB");
205 		return;
206 	}
207 
208 	bool ret = pixmap.save(&buffer, "png");
209 
210 	if (ret)
211 		ToolBox->setFileSize(QString::number(ceil(1.0 * buffer.size() / 1024.0)) + " KiB");
212 }
213 
keyPressEvent(QKeyEvent * e)214 void CropImageWidget::keyPressEvent(QKeyEvent *e)
215 {
216 	if (e->key() == Qt::Key_Escape)
217 	{
218 		emit canceled();
219 		e->accept();
220 	}
221 	else
222 		QWidget::keyPressEvent(e);
223 }
224 
mousePressEvent(QMouseEvent * event)225 void CropImageWidget::mousePressEvent(QMouseEvent *event)
226 {
227 	QGraphicsView::mousePressEvent(event);
228 	if (event->isAccepted())
229 		return;
230 
231 	if (event->button() != Qt::LeftButton)
232 		return;
233 
234 	IsMouseButtonPressed = true;
235 	WasDoubleClick = false;
236 
237 	NewTopLeft = event->pos();
238 
239 	updateCropRectDisplay();
240 }
241 
mouseReleaseEvent(QMouseEvent * event)242 void CropImageWidget::mouseReleaseEvent(QMouseEvent *event)
243 {
244 	QGraphicsView::mouseReleaseEvent(event);
245 	normalizeCropRect();
246 
247 	if (!IsMouseButtonPressed)
248 		return;
249 
250 	if (event->button() != Qt::LeftButton)
251 		return;
252 
253 	IsMouseButtonPressed = false;
254 
255 	CropRect.setTopLeft(NewTopLeft);
256 	CropRect.setBottomRight(event->pos());
257 	normalizeCropRect();
258 	updateToolBoxFileSizeHint();
259 }
260 
mouseMoveEvent(QMouseEvent * event)261 void CropImageWidget::mouseMoveEvent(QMouseEvent *event)
262 {
263 	QGraphicsView::mouseMoveEvent(event);
264 
265 	if (!IsMouseButtonPressed)
266 		return;
267 
268 	CropRect.setTopLeft(NewTopLeft);
269 	CropRect.setBottomRight(event->pos());
270 	normalizeCropRect();
271 }
272 
resizeEvent(QResizeEvent * event)273 void CropImageWidget::resizeEvent(QResizeEvent *event)
274 {
275 	SelectionFrame->setSize(event->size());
276     QGraphicsView::resizeEvent(event);
277 
278 	scene()->setSceneRect(QRectF(0, 0, event->size().width(), event->size().height()));
279 }
280 
showEvent(QShowEvent * event)281 void CropImageWidget::showEvent(QShowEvent *event)
282 {
283     QGraphicsView::showEvent(event);
284 
285 	setCropRect(frameGeometry());
286 }
287 
setPixmap(QPixmap pixmap)288 void CropImageWidget::setPixmap(QPixmap pixmap)
289 {
290 	PixmapItem->setPixmap(pixmap);
291 }
292 
setCropRect(const QRect & cropRect)293 void CropImageWidget::setCropRect(const QRect &cropRect)
294 {
295 	CropRect = cropRect.normalized();
296 	updateCropRectDisplay();
297 }
298 
299 #include "moc_crop-image-widget.cpp"
300