]> git.sesse.net Git - kdenlive/blob - src/regiongrabber.cpp
Reindent the codebase using 'linux' bracket placement.
[kdenlive] / src / regiongrabber.cpp
1 /*
2  *   Copyright (C) 2007 Luca Gugelmann <lucag@student.ethz.ch>
3  *   Adapted for Kdenlive by Jean-Baptiste Mardelle (2008) jb@kdenlive.org
4  *
5  *   This program is free software; you can redistribute it and/or modify it
6  *   under the terms of the GNU Library General Public License version 2 as
7  *   published by the Free Software Foundation
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details
13  *
14  *   You should have received a copy of the GNU Library General Public
15  *   License along with this program; if not, write to the
16  *   Free Software Foundation, Inc.,
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19
20 #include "regiongrabber.h"
21
22 #include <QPainter>
23 #include <QMouseEvent>
24 #include <QApplication>
25 #include <QDesktopWidget>
26 #include <QToolTip>
27
28 #include <klocale.h>
29 #include <KWindowSystem>
30
31 RegionGrabber::RegionGrabber() :
32         QWidget(0), selection(), mouseDown(false), newSelection(false),
33         handleSize(10), mouseOverHandle(0), idleTimer(),
34         showHelp(true), grabbing(false),
35         TLHandle(0, 0, handleSize, handleSize), TRHandle(0, 0, handleSize, handleSize),
36         BLHandle(0, 0, handleSize, handleSize), BRHandle(0, 0, handleSize, handleSize),
37         LHandle(0, 0, handleSize, handleSize), THandle(0, 0, handleSize, handleSize),
38         RHandle(0, 0, handleSize, handleSize), BHandle(0, 0, handleSize, handleSize)
39 {
40     handles << &TLHandle << &TRHandle << &BLHandle << &BRHandle
41     << &LHandle << &THandle << &RHandle << &BHandle;
42     setMouseTracking(true);
43     setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
44     int timeout = KWindowSystem::compositingActive() ? 200 : 50;
45     QTimer::singleShot(timeout, this, SLOT(init()));
46     connect(&idleTimer, SIGNAL(timeout()), this, SLOT(displayHelp()));
47     idleTimer.start(3000);
48 }
49
50 RegionGrabber::~RegionGrabber()
51 {
52 }
53
54 void RegionGrabber::init()
55 {
56     pixmap = QPixmap::grabWindow(QApplication::desktop()->winId());
57     showFullScreen();
58     resize(pixmap.size());
59     move(0, 0);
60     setCursor(Qt::CrossCursor);
61 }
62
63 void RegionGrabber::displayHelp()
64 {
65     showHelp = true;
66     update();
67 }
68
69 void RegionGrabber::paintEvent(QPaintEvent* e)
70 {
71     Q_UNUSED(e);
72     if (grabbing)   // grabWindow() should just get the background
73         return;
74
75     QPainter painter(this);
76
77     QPalette pal(QToolTip::palette());
78     QFont font = QToolTip::font();
79
80     QColor handleColor = pal.color(QPalette::Active, QPalette::Highlight);
81     handleColor.setAlpha(160);
82     QColor overlayColor(0, 0, 0, 160);
83     QColor textColor = pal.color(QPalette::Active, QPalette::Text);
84     QColor textBackgroundColor = pal.color(QPalette::Active, QPalette::Base);
85     painter.drawPixmap(0, 0, pixmap);
86     painter.setFont(font);
87
88     QRect r = selection.normalized().adjusted(0, 0, -1, -1);
89     if (!selection.isNull()) {
90         QRegion grey(rect());
91         grey = grey.subtracted(r);
92         painter.setPen(handleColor);
93         painter.setBrush(overlayColor);
94         painter.setClipRegion(grey);
95         painter.drawRect(-1, -1, rect().width() + 1, rect().height() + 1);
96         painter.setClipRect(rect());
97         painter.setBrush(Qt::NoBrush);
98         painter.drawRect(r);
99     }
100
101     if (showHelp) {
102         painter.setPen(textColor);
103         painter.setBrush(textBackgroundColor);
104         QString helpText = i18n("Select a region using the mouse. To take the snapshot, press the Enter key. Press Esc to quit.");
105         QRect textRect = painter.boundingRect(rect().adjusted(2, 2, -2, -2), Qt::TextWordWrap, helpText);
106         textRect.adjust(-2, -2, 4, 2);
107         painter.drawRect(textRect);
108         textRect.moveTopLeft(QPoint(3, 3));
109         painter.drawText(textRect, helpText);
110     }
111
112     if (selection.isNull()) {
113         return;
114     }
115
116     // The grabbed region is everything which is covered by the drawn
117     // rectangles (border included). This means that there is no 0px
118     // selection, since a 0px wide rectangle will always be drawn as a line.
119     QString txt = QString("%1x%2").arg(selection.width() == 0 ? 2 : selection.width())
120                   .arg(selection.height() == 0 ? 2 : selection.height());
121     QRect textRect = painter.boundingRect(rect(), Qt::AlignLeft, txt);
122     QRect boundingRect = textRect.adjusted(-4, 0, 0, 0);
123
124     if (textRect.width() < r.width() - 2*handleSize &&
125             textRect.height() < r.height() - 2*handleSize &&
126             (r.width() > 100 && r.height() > 100)) {  // center, unsuitable for small selections
127         boundingRect.moveCenter(r.center());
128         textRect.moveCenter(r.center());
129     } else if (r.y() - 3 > textRect.height() &&
130                r.x() + textRect.width() < rect().right()) { // on top, left aligned
131         boundingRect.moveBottomLeft(QPoint(r.x(), r.y() - 3));
132         textRect.moveBottomLeft(QPoint(r.x() + 2, r.y() - 3));
133     } else if (r.x() - 3 > textRect.width()) { // left, top aligned
134         boundingRect.moveTopRight(QPoint(r.x() - 3, r.y()));
135         textRect.moveTopRight(QPoint(r.x() - 5, r.y()));
136     } else if (r.bottom() + 3 + textRect.height() < rect().bottom() &&
137                r.right() > textRect.width()) { // at bottom, right aligned
138         boundingRect.moveTopRight(QPoint(r.right(), r.bottom() + 3));
139         textRect.moveTopRight(QPoint(r.right() - 2, r.bottom() + 3));
140     } else if (r.right() + textRect.width() + 3 < rect().width()) { // right, bottom aligned
141         boundingRect.moveBottomLeft(QPoint(r.right() + 3, r.bottom()));
142         textRect.moveBottomLeft(QPoint(r.right() + 5, r.bottom()));
143     }
144     // if the above didn't catch it, you are running on a very tiny screen...
145     painter.setPen(textColor);
146     painter.setBrush(textBackgroundColor);
147     painter.drawRect(boundingRect);
148     painter.drawText(textRect, txt);
149
150     if ((r.height() > handleSize*2 && r.width() > handleSize*2)
151             || !mouseDown) {
152         updateHandles();
153         painter.setPen(handleColor);
154         handleColor.setAlpha(60);
155         painter.setBrush(handleColor);
156         painter.drawRects(handleMask().rects());
157     }
158 }
159
160 void RegionGrabber::resizeEvent(QResizeEvent* e)
161 {
162     Q_UNUSED(e);
163     if (selection.isNull())
164         return;
165     QRect r = selection;
166     r.setTopLeft(limitPointToRect(r.topLeft(), rect()));
167     r.setBottomRight(limitPointToRect(r.bottomRight(), rect()));
168     if (r.width() <= 1 || r.height() <= 1)   //this just results in ugly drawing...
169         r = QRect();
170     selection = r;
171 }
172
173 void RegionGrabber::mousePressEvent(QMouseEvent* e)
174 {
175     showHelp = false;
176     idleTimer.stop();
177     if (e->button() == Qt::LeftButton) {
178         mouseDown = true;
179         dragStartPoint = e->pos();
180         selectionBeforeDrag = selection;
181         if (!selection.contains(e->pos())) {
182             newSelection = true;
183             selection = QRect();
184             showHelp = true;
185         } else {
186             setCursor(Qt::ClosedHandCursor);
187         }
188     } else if (e->button() == Qt::RightButton) {
189         newSelection = false;
190         selection = QRect();
191         setCursor(Qt::CrossCursor);
192     }
193     update();
194 }
195
196 void RegionGrabber::mouseMoveEvent(QMouseEvent* e)
197 {
198     if (mouseDown) {
199         if (newSelection) {
200             QPoint p = e->pos();
201             QRect r = rect();
202             selection = QRect(dragStartPoint, limitPointToRect(p, r)).normalized();
203         } else if (mouseOverHandle == 0) { // moving the whole selection
204             QRect r = rect().normalized(), s = selectionBeforeDrag.normalized();
205             QPoint p = s.topLeft() + e->pos() - dragStartPoint;
206             r.setBottomRight(r.bottomRight() - QPoint(s.width(), s.height()));
207             if (!r.isNull() && r.isValid())
208                 selection.moveTo(limitPointToRect(p, r));
209         } else { // dragging a handle
210             QRect r = selectionBeforeDrag;
211             QPoint offset = e->pos() - dragStartPoint;
212
213             if (mouseOverHandle == &TLHandle || mouseOverHandle == &THandle
214                     || mouseOverHandle == &TRHandle) { // dragging one of the top handles
215                 r.setTop(r.top() + offset.y());
216             }
217
218             if (mouseOverHandle == &TLHandle || mouseOverHandle == &LHandle
219                     || mouseOverHandle == &BLHandle) { // dragging one of the left handles
220                 r.setLeft(r.left() + offset.x());
221             }
222
223             if (mouseOverHandle == &BLHandle || mouseOverHandle == &BHandle
224                     || mouseOverHandle == &BRHandle) { // dragging one of the bottom handles
225                 r.setBottom(r.bottom() + offset.y());
226             }
227
228             if (mouseOverHandle == &TRHandle || mouseOverHandle == &RHandle
229                     || mouseOverHandle == &BRHandle) { // dragging one of the right handles
230                 r.setRight(r.right() + offset.x());
231             }
232             r = r.normalized();
233             r.setTopLeft(limitPointToRect(r.topLeft(), rect()));
234             r.setBottomRight(limitPointToRect(r.bottomRight(), rect()));
235             selection = r;
236         }
237         update();
238     } else {
239         if (selection.isNull())
240             return;
241         bool found = false;
242         foreach(QRect* r, handles) {
243             if (r->contains(e->pos())) {
244                 mouseOverHandle = r;
245                 found = true;
246                 break;
247             }
248         }
249         if (!found) {
250             mouseOverHandle = 0;
251             if (selection.contains(e->pos()))
252                 setCursor(Qt::OpenHandCursor);
253             else
254                 setCursor(Qt::CrossCursor);
255         } else {
256             if (mouseOverHandle == &TLHandle || mouseOverHandle == &BRHandle)
257                 setCursor(Qt::SizeFDiagCursor);
258             if (mouseOverHandle == &TRHandle || mouseOverHandle == &BLHandle)
259                 setCursor(Qt::SizeBDiagCursor);
260             if (mouseOverHandle == &LHandle || mouseOverHandle == &RHandle)
261                 setCursor(Qt::SizeHorCursor);
262             if (mouseOverHandle == &THandle || mouseOverHandle == &BHandle)
263                 setCursor(Qt::SizeVerCursor);
264         }
265     }
266 }
267
268 void RegionGrabber::mouseReleaseEvent(QMouseEvent* e)
269 {
270     mouseDown = false;
271     newSelection = false;
272     idleTimer.start();
273     if (mouseOverHandle == 0 && selection.contains(e->pos()))
274         setCursor(Qt::OpenHandCursor);
275     update();
276 }
277
278 void RegionGrabber::mouseDoubleClickEvent(QMouseEvent*)
279 {
280     grabRect();
281 }
282
283 void RegionGrabber::keyPressEvent(QKeyEvent* e)
284 {
285     if (e->key() == Qt::Key_Escape) {
286         emit regionGrabbed(QRect());
287         close();
288     } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
289         grabRect();
290     } else {
291         e->ignore();
292     }
293 }
294
295 void RegionGrabber::grabRect()
296 {
297     QRect r = selection.normalized();
298     if (!r.isNull() && r.isValid()) {
299         grabbing = true;
300         emit regionGrabbed(r);
301     }
302     close();
303 }
304
305 void RegionGrabber::updateHandles()
306 {
307     QRect r = selection.normalized().adjusted(0, 0, -1, -1);
308     int s2 = handleSize / 2;
309
310     TLHandle.moveTopLeft(r.topLeft());
311     TRHandle.moveTopRight(r.topRight());
312     BLHandle.moveBottomLeft(r.bottomLeft());
313     BRHandle.moveBottomRight(r.bottomRight());
314
315     LHandle.moveTopLeft(QPoint(r.x(), r.y() + r.height() / 2 - s2));
316     THandle.moveTopLeft(QPoint(r.x() + r.width() / 2 - s2, r.y()));
317     RHandle.moveTopRight(QPoint(r.right(), r.y() + r.height() / 2 - s2));
318     BHandle.moveBottomLeft(QPoint(r.x() + r.width() / 2 - s2, r.bottom()));
319 }
320
321 QRegion RegionGrabber::handleMask() const
322 {
323     // note: not normalized QRects are bad here, since they will not be drawn
324     QRegion mask;
325     foreach(const QRect* rect, handles) mask += QRegion(*rect);
326     return mask;
327 }
328
329 QPoint RegionGrabber::limitPointToRect(const QPoint &p, const QRect &r) const
330 {
331     QPoint q;
332     q.setX(p.x() < r.x() ? r.x() : p.x() < r.right() ? p.x() : r.right());
333     q.setY(p.y() < r.y() ? r.y() : p.y() < r.bottom() ? p.y() : r.bottom());
334     return q;
335 }
336
337 #include "regiongrabber.moc"