]> git.sesse.net Git - kdenlive/blob - src/regiongrabber.cpp
NEW: Kdenlive can now create screencasts.
[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     {
91         QRegion grey( rect() );
92         grey = grey.subtracted( r );
93         painter.setPen( handleColor );
94         painter.setBrush( overlayColor );
95         painter.setClipRegion( grey );
96         painter.drawRect( -1, -1, rect().width() + 1, rect().height() + 1 );
97         painter.setClipRect( rect() );
98         painter.setBrush( Qt::NoBrush );
99         painter.drawRect( r );
100     }
101
102     if ( showHelp )
103     {
104         painter.setPen( textColor );
105         painter.setBrush( textBackgroundColor );
106         QString helpText = i18n( "Select a region using the mouse. To take the snapshot, press the Enter key. Press Esc to quit." );
107         QRect textRect = painter.boundingRect( rect().adjusted( 2, 2, -2, -2 ), Qt::TextWordWrap, helpText );
108         textRect.adjust( -2, -2, 4, 2 );
109         painter.drawRect( textRect );
110         textRect.moveTopLeft( QPoint( 3, 3 ) );
111         painter.drawText( textRect, helpText );
112     }
113
114     if ( selection.isNull() )
115     {
116         return;
117     }
118
119     // The grabbed region is everything which is covered by the drawn
120     // rectangles (border included). This means that there is no 0px
121     // selection, since a 0px wide rectangle will always be drawn as a line.
122     QString txt = QString( "%1x%2" ).arg( selection.width() == 0 ? 2 : selection.width() )
123                   .arg( selection.height() == 0 ? 2 : selection.height() );
124     QRect textRect = painter.boundingRect( rect(), Qt::AlignLeft, txt );
125     QRect boundingRect = textRect.adjusted( -4, 0, 0, 0);
126
127     if ( textRect.width() < r.width() - 2*handleSize &&
128          textRect.height() < r.height() - 2*handleSize &&
129          ( r.width() > 100 && r.height() > 100 ) ) // center, unsuitable for small selections
130     {
131         boundingRect.moveCenter( r.center() );
132         textRect.moveCenter( r.center() );
133     }
134     else if ( r.y() - 3 > textRect.height() &&
135               r.x() + textRect.width() < rect().right() ) // on top, left aligned
136     {
137         boundingRect.moveBottomLeft( QPoint( r.x(), r.y() - 3 ) );
138         textRect.moveBottomLeft( QPoint( r.x() + 2, r.y() - 3 ) );
139     }
140     else if ( r.x() - 3 > textRect.width() ) // left, top aligned
141     {
142         boundingRect.moveTopRight( QPoint( r.x() - 3, r.y() ) );
143         textRect.moveTopRight( QPoint( r.x() - 5, r.y() ) );
144     }
145     else if ( r.bottom() + 3 + textRect.height() < rect().bottom() &&
146               r.right() > textRect.width() ) // at bottom, right aligned
147     {
148         boundingRect.moveTopRight( QPoint( r.right(), r.bottom() + 3 ) );
149         textRect.moveTopRight( QPoint( r.right() - 2, r.bottom() + 3 ) );
150     }
151     else if ( r.right() + textRect.width() + 3 < rect().width() ) // right, bottom aligned
152     {
153         boundingRect.moveBottomLeft( QPoint( r.right() + 3, r.bottom() ) );
154         textRect.moveBottomLeft( QPoint( r.right() + 5, r.bottom() ) );
155     }
156     // if the above didn't catch it, you are running on a very tiny screen...
157     painter.setPen( textColor );
158     painter.setBrush( textBackgroundColor );
159     painter.drawRect( boundingRect );
160     painter.drawText( textRect, txt );
161
162     if ( ( r.height() > handleSize*2 && r.width() > handleSize*2 )
163          || !mouseDown )
164     {
165         updateHandles();
166         painter.setPen( handleColor );
167         handleColor.setAlpha( 60 );
168         painter.setBrush( handleColor );
169         painter.drawRects( handleMask().rects() );
170     }
171 }
172
173 void RegionGrabber::resizeEvent( QResizeEvent* e )
174 {
175     Q_UNUSED( e );
176     if ( selection.isNull() )
177         return;
178     QRect r = selection;
179     r.setTopLeft( limitPointToRect( r.topLeft(), rect() ) );
180     r.setBottomRight( limitPointToRect( r.bottomRight(), rect() ) );
181     if ( r.width() <= 1 || r.height() <= 1 ) //this just results in ugly drawing...
182         r = QRect();
183     selection = r;
184 }
185
186 void RegionGrabber::mousePressEvent( QMouseEvent* e )
187 {
188     showHelp = false;
189     idleTimer.stop();
190     if ( e->button() == Qt::LeftButton )
191     {
192         mouseDown = true;
193         dragStartPoint = e->pos();
194         selectionBeforeDrag = selection;
195         if ( !selection.contains( e->pos() ) )
196         {
197             newSelection = true;
198             selection = QRect();
199             showHelp = true;
200         }
201         else
202         {
203             setCursor( Qt::ClosedHandCursor );
204         }
205     }
206     else if ( e->button() == Qt::RightButton )
207     {
208         newSelection = false;
209         selection = QRect();
210         setCursor( Qt::CrossCursor );
211     }
212     update();
213 }
214
215 void RegionGrabber::mouseMoveEvent( QMouseEvent* e )
216 {
217     if ( mouseDown )
218     {
219         if ( newSelection )
220         {
221             QPoint p = e->pos();
222             QRect r = rect();
223             selection = QRect( dragStartPoint, limitPointToRect( p, r ) ).normalized();
224         }
225         else if ( mouseOverHandle == 0 ) // moving the whole selection
226         {
227             QRect r = rect().normalized(), s = selectionBeforeDrag.normalized();
228             QPoint p = s.topLeft() + e->pos() - dragStartPoint;
229             r.setBottomRight( r.bottomRight() - QPoint( s.width(), s.height() ) );
230             if ( !r.isNull() && r.isValid() )
231                 selection.moveTo( limitPointToRect( p, r ) );
232         }
233         else // dragging a handle
234         {
235             QRect r = selectionBeforeDrag;
236             QPoint offset = e->pos() - dragStartPoint;
237
238             if ( mouseOverHandle == &TLHandle || mouseOverHandle == &THandle
239                  || mouseOverHandle == &TRHandle ) // dragging one of the top handles
240             {
241                 r.setTop( r.top() + offset.y() );
242             }
243
244             if ( mouseOverHandle == &TLHandle || mouseOverHandle == &LHandle
245                  || mouseOverHandle == &BLHandle ) // dragging one of the left handles
246             {
247                 r.setLeft( r.left() + offset.x() );
248             }
249
250             if ( mouseOverHandle == &BLHandle || mouseOverHandle == &BHandle
251                  || mouseOverHandle == &BRHandle ) // dragging one of the bottom handles
252             {
253                 r.setBottom( r.bottom() + offset.y() );
254             }
255
256             if ( mouseOverHandle == &TRHandle || mouseOverHandle == &RHandle
257                  || mouseOverHandle == &BRHandle ) // dragging one of the right handles
258             {
259                 r.setRight( r.right() + offset.x() );
260             }
261             r = r.normalized();
262             r.setTopLeft( limitPointToRect( r.topLeft(), rect() ) );
263             r.setBottomRight( limitPointToRect( r.bottomRight(), rect() ) );
264             selection = r;
265         }
266         update();
267     }
268     else
269     {
270         if ( selection.isNull() )
271             return;
272         bool found = false;
273         foreach( QRect* r, handles )
274         {
275             if ( r->contains( e->pos() ) )
276             {
277                 mouseOverHandle = r;
278                 found = true;
279                 break;
280             }
281         }
282         if ( !found )
283         {
284             mouseOverHandle = 0;
285             if ( selection.contains( e->pos() ) )
286                 setCursor( Qt::OpenHandCursor );
287             else
288                 setCursor( Qt::CrossCursor );
289         }
290         else
291         {
292             if ( mouseOverHandle == &TLHandle || mouseOverHandle == &BRHandle )
293                 setCursor( Qt::SizeFDiagCursor );
294             if ( mouseOverHandle == &TRHandle || mouseOverHandle == &BLHandle )
295                 setCursor( Qt::SizeBDiagCursor );
296             if ( mouseOverHandle == &LHandle || mouseOverHandle == &RHandle )
297                 setCursor( Qt::SizeHorCursor );
298             if ( mouseOverHandle == &THandle || mouseOverHandle == &BHandle )
299                 setCursor( Qt::SizeVerCursor );
300         }
301     }
302 }
303
304 void RegionGrabber::mouseReleaseEvent( QMouseEvent* e )
305 {
306     mouseDown = false;
307     newSelection = false;
308     idleTimer.start();
309     if ( mouseOverHandle == 0 && selection.contains( e->pos() ) )
310         setCursor( Qt::OpenHandCursor );
311     update();
312 }
313
314 void RegionGrabber::mouseDoubleClickEvent( QMouseEvent* )
315 {
316     grabRect();
317 }
318
319 void RegionGrabber::keyPressEvent( QKeyEvent* e )
320 {
321     if ( e->key() == Qt::Key_Escape )
322     {
323         emit regionGrabbed( QRect() );
324                 close();
325     }
326     else if ( e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return )
327     {
328         grabRect();
329     }
330     else
331     {
332         e->ignore();
333     }
334 }
335
336 void RegionGrabber::grabRect()
337 {
338     QRect r = selection.normalized();
339     if ( !r.isNull() && r.isValid() )
340     {
341         grabbing = true;
342         emit regionGrabbed( r );
343     }
344         close();
345 }
346
347 void RegionGrabber::updateHandles()
348 {
349     QRect r = selection.normalized().adjusted( 0, 0, -1, -1 );
350     int s2 = handleSize / 2;
351
352     TLHandle.moveTopLeft( r.topLeft() );
353     TRHandle.moveTopRight( r.topRight() );
354     BLHandle.moveBottomLeft( r.bottomLeft() );
355     BRHandle.moveBottomRight( r.bottomRight() );
356
357     LHandle.moveTopLeft( QPoint( r.x(), r.y() + r.height() / 2 - s2) );
358     THandle.moveTopLeft( QPoint( r.x() + r.width() / 2 - s2, r.y() ) );
359     RHandle.moveTopRight( QPoint( r.right(), r.y() + r.height() / 2 - s2 ) );
360     BHandle.moveBottomLeft( QPoint( r.x() + r.width() / 2 - s2, r.bottom() ) );
361 }
362
363 QRegion RegionGrabber::handleMask() const
364 {
365     // note: not normalized QRects are bad here, since they will not be drawn
366     QRegion mask;
367     foreach( QRect* rect, handles ) mask += QRegion( *rect );
368     return mask;
369 }
370
371 QPoint RegionGrabber::limitPointToRect( const QPoint &p, const QRect &r ) const
372 {
373     QPoint q;
374     q.setX( p.x() < r.x() ? r.x() : p.x() < r.right() ? p.x() : r.right() );
375     q.setY( p.y() < r.y() ? r.y() : p.y() < r.bottom() ? p.y() : r.bottom() );
376     return q;
377 }
378
379 #include "regiongrabber.moc"