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