2 * Copyright (C) 2007 Luca Gugelmann <lucag@student.ethz.ch>
3 * Adapted for Kdenlive by Jean-Baptiste Mardelle (2008) jb@kdenlive.org
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
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
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.
20 #include "regiongrabber.h"
23 #include <QMouseEvent>
24 #include <QApplication>
25 #include <QDesktopWidget>
29 #include <KWindowSystem>
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);
49 RegionGrabber::~RegionGrabber() {
52 void RegionGrabber::init() {
53 pixmap = QPixmap::grabWindow(QApplication::desktop()->winId());
55 resize(pixmap.size());
57 setCursor(Qt::CrossCursor);
60 void RegionGrabber::displayHelp() {
65 void RegionGrabber::paintEvent(QPaintEvent* e) {
67 if (grabbing) // grabWindow() should just get the background
70 QPainter painter(this);
72 QPalette pal(QToolTip::palette());
73 QFont font = QToolTip::font();
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);
83 QRect r = selection.normalized().adjusted(0, 0, -1, -1);
84 if (!selection.isNull()) {
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);
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);
107 if (selection.isNull()) {
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);
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()));
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);
145 if ((r.height() > handleSize*2 && r.width() > handleSize*2)
148 painter.setPen(handleColor);
149 handleColor.setAlpha(60);
150 painter.setBrush(handleColor);
151 painter.drawRects(handleMask().rects());
155 void RegionGrabber::resizeEvent(QResizeEvent* e) {
157 if (selection.isNull())
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...
167 void RegionGrabber::mousePressEvent(QMouseEvent* e) {
170 if (e->button() == Qt::LeftButton) {
172 dragStartPoint = e->pos();
173 selectionBeforeDrag = selection;
174 if (!selection.contains(e->pos())) {
179 setCursor(Qt::ClosedHandCursor);
181 } else if (e->button() == Qt::RightButton) {
182 newSelection = false;
184 setCursor(Qt::CrossCursor);
189 void RegionGrabber::mouseMoveEvent(QMouseEvent* e) {
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;
205 if (mouseOverHandle == &TLHandle || mouseOverHandle == &THandle
206 || mouseOverHandle == &TRHandle) { // dragging one of the top handles
207 r.setTop(r.top() + offset.y());
210 if (mouseOverHandle == &TLHandle || mouseOverHandle == &LHandle
211 || mouseOverHandle == &BLHandle) { // dragging one of the left handles
212 r.setLeft(r.left() + offset.x());
215 if (mouseOverHandle == &BLHandle || mouseOverHandle == &BHandle
216 || mouseOverHandle == &BRHandle) { // dragging one of the bottom handles
217 r.setBottom(r.bottom() + offset.y());
220 if (mouseOverHandle == &TRHandle || mouseOverHandle == &RHandle
221 || mouseOverHandle == &BRHandle) { // dragging one of the right handles
222 r.setRight(r.right() + offset.x());
225 r.setTopLeft(limitPointToRect(r.topLeft(), rect()));
226 r.setBottomRight(limitPointToRect(r.bottomRight(), rect()));
231 if (selection.isNull())
234 foreach(QRect* r, handles) {
235 if (r->contains(e->pos())) {
243 if (selection.contains(e->pos()))
244 setCursor(Qt::OpenHandCursor);
246 setCursor(Qt::CrossCursor);
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);
260 void RegionGrabber::mouseReleaseEvent(QMouseEvent* e) {
262 newSelection = false;
264 if (mouseOverHandle == 0 && selection.contains(e->pos()))
265 setCursor(Qt::OpenHandCursor);
269 void RegionGrabber::mouseDoubleClickEvent(QMouseEvent*) {
273 void RegionGrabber::keyPressEvent(QKeyEvent* e) {
274 if (e->key() == Qt::Key_Escape) {
275 emit regionGrabbed(QRect());
277 } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
284 void RegionGrabber::grabRect() {
285 QRect r = selection.normalized();
286 if (!r.isNull() && r.isValid()) {
288 emit regionGrabbed(r);
293 void RegionGrabber::updateHandles() {
294 QRect r = selection.normalized().adjusted(0, 0, -1, -1);
295 int s2 = handleSize / 2;
297 TLHandle.moveTopLeft(r.topLeft());
298 TRHandle.moveTopRight(r.topRight());
299 BLHandle.moveBottomLeft(r.bottomLeft());
300 BRHandle.moveBottomRight(r.bottomRight());
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()));
308 QRegion RegionGrabber::handleMask() const {
309 // note: not normalized QRects are bad here, since they will not be drawn
311 foreach(QRect* rect, handles) mask += QRegion(*rect);
315 QPoint RegionGrabber::limitPointToRect(const QPoint &p, const QRect &r) const {
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());
322 #include "regiongrabber.moc"