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