2 * Copyright (C) 2007 Luca Gugelmann <lucag@student.ethz.ch>
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Library General Public License version 2 as
6 * published by the Free Software Foundation
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details
13 * You should have received a copy of the GNU Library General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "regiongrabber.h"
22 #include <QMouseEvent>
23 #include <QApplication>
24 #include <QDesktopWidget>
28 #include <KWindowSystem>
30 RegionGrabber::RegionGrabber( ) :
31 QWidget( 0 ), selection(), mouseDown( false ), newSelection( false ),
32 handleSize( 10 ), mouseOverHandle( 0 ), idleTimer(),
33 showHelp( true ), grabbing( false ),
34 TLHandle(0,0,handleSize
,handleSize
), TRHandle(0,0,handleSize
,handleSize
),
35 BLHandle(0,0,handleSize
,handleSize
), BRHandle(0,0,handleSize
,handleSize
),
36 LHandle(0,0,handleSize
,handleSize
), THandle(0,0,handleSize
,handleSize
),
37 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()
53 void RegionGrabber::init()
55 pixmap
= QPixmap::grabWindow( QApplication::desktop()->winId() );
57 resize( pixmap
.size() );
59 setCursor( Qt::CrossCursor
);
62 void RegionGrabber::displayHelp()
68 void RegionGrabber::paintEvent( QPaintEvent
* e
)
71 if ( grabbing
) // grabWindow() should just get the background
74 QPainter
painter( this );
76 QPalette
pal(QToolTip::palette());
77 QFont font
= QToolTip::font();
79 QColor handleColor
= pal
.color( QPalette::Active
, QPalette::Highlight
);
80 handleColor
.setAlpha( 160 );
81 QColor
overlayColor( 0, 0, 0, 160 );
82 QColor textColor
= pal
.color( QPalette::Active
, QPalette::Text
);
83 QColor textBackgroundColor
= pal
.color( QPalette::Active
, QPalette::Base
);
84 painter
.drawPixmap(0, 0, pixmap
);
85 painter
.setFont(font
);
87 QRect r
= selection
.normalized().adjusted( 0, 0, -1, -1 );
88 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
);
103 painter
.setPen( textColor
);
104 painter
.setBrush( textBackgroundColor
);
105 QString helpText
= i18n( "Select a region using the mouse. To take the snapshot, press the Enter key. Press Esc to quit." );
106 QRect textRect
= painter
.boundingRect( rect().adjusted( 2, 2, -2, -2 ), Qt::TextWordWrap
, helpText
);
107 textRect
.adjust( -2, -2, 4, 2 );
108 painter
.drawRect( textRect
);
109 textRect
.moveTopLeft( QPoint( 3, 3 ) );
110 painter
.drawText( textRect
, helpText
);
113 if ( selection
.isNull() )
118 // The grabbed region is everything which is covered by the drawn
119 // rectangles (border included). This means that there is no 0px
120 // selection, since a 0px wide rectangle will always be drawn as a line.
121 QString txt
= QString( "%1x%2" ).arg( selection
.width() == 0 ? 2 : selection
.width() )
122 .arg( selection
.height() == 0 ? 2 : selection
.height() );
123 QRect textRect
= painter
.boundingRect( rect(), Qt::AlignLeft
, txt
);
124 QRect boundingRect
= textRect
.adjusted( -4, 0, 0, 0);
126 if ( textRect
.width() < r
.width() - 2*handleSize
&&
127 textRect
.height() < r
.height() - 2*handleSize
&&
128 ( r
.width() > 100 && r
.height() > 100 ) ) // center, unsuitable for small selections
130 boundingRect
.moveCenter( r
.center() );
131 textRect
.moveCenter( r
.center() );
133 else if ( r
.y() - 3 > textRect
.height() &&
134 r
.x() + textRect
.width() < rect().right() ) // on top, left aligned
136 boundingRect
.moveBottomLeft( QPoint( r
.x(), r
.y() - 3 ) );
137 textRect
.moveBottomLeft( QPoint( r
.x() + 2, r
.y() - 3 ) );
139 else if ( r
.x() - 3 > textRect
.width() ) // left, top aligned
141 boundingRect
.moveTopRight( QPoint( r
.x() - 3, r
.y() ) );
142 textRect
.moveTopRight( QPoint( r
.x() - 5, r
.y() ) );
144 else if ( r
.bottom() + 3 + textRect
.height() < rect().bottom() &&
145 r
.right() > textRect
.width() ) // at bottom, right aligned
147 boundingRect
.moveTopRight( QPoint( r
.right(), r
.bottom() + 3 ) );
148 textRect
.moveTopRight( QPoint( r
.right() - 2, r
.bottom() + 3 ) );
150 else if ( r
.right() + textRect
.width() + 3 < rect().width() ) // right, bottom aligned
152 boundingRect
.moveBottomLeft( QPoint( r
.right() + 3, r
.bottom() ) );
153 textRect
.moveBottomLeft( QPoint( r
.right() + 5, r
.bottom() ) );
155 // if the above didn't catch it, you are running on a very tiny screen...
156 painter
.setPen( textColor
);
157 painter
.setBrush( textBackgroundColor
);
158 painter
.drawRect( boundingRect
);
159 painter
.drawText( textRect
, txt
);
161 if ( ( r
.height() > handleSize
*2 && r
.width() > handleSize
*2 )
165 painter
.setPen( handleColor
);
166 handleColor
.setAlpha( 60 );
167 painter
.setBrush( handleColor
);
168 painter
.drawRects( handleMask().rects() );
172 void RegionGrabber::resizeEvent( QResizeEvent
* e
)
175 if ( selection
.isNull() )
178 r
.setTopLeft( limitPointToRect( r
.topLeft(), rect() ) );
179 r
.setBottomRight( limitPointToRect( r
.bottomRight(), rect() ) );
180 if ( r
.width() <= 1 || r
.height() <= 1 ) //this just results in ugly drawing...
185 void RegionGrabber::mousePressEvent( QMouseEvent
* e
)
189 if ( e
->button() == Qt::LeftButton
)
192 dragStartPoint
= e
->pos();
193 selectionBeforeDrag
= selection
;
194 if ( !selection
.contains( e
->pos() ) )
202 setCursor( Qt::ClosedHandCursor
);
205 else if ( e
->button() == Qt::RightButton
)
207 newSelection
= false;
209 setCursor( Qt::CrossCursor
);
214 void RegionGrabber::mouseMoveEvent( QMouseEvent
* e
)
222 selection
= QRect( dragStartPoint
, limitPointToRect( p
, r
) ).normalized();
224 else if ( mouseOverHandle
== 0 ) // moving the whole selection
226 QRect r
= rect().normalized(), s
= selectionBeforeDrag
.normalized();
227 QPoint p
= s
.topLeft() + e
->pos() - dragStartPoint
;
228 r
.setBottomRight( r
.bottomRight() - QPoint( s
.width(), s
.height() ) );
229 if ( !r
.isNull() && r
.isValid() )
230 selection
.moveTo( limitPointToRect( p
, r
) );
232 else // dragging a handle
234 QRect r
= selectionBeforeDrag
;
235 QPoint offset
= e
->pos() - dragStartPoint
;
237 if ( mouseOverHandle
== &TLHandle
|| mouseOverHandle
== &THandle
238 || mouseOverHandle
== &TRHandle
) // dragging one of the top handles
240 r
.setTop( r
.top() + offset
.y() );
243 if ( mouseOverHandle
== &TLHandle
|| mouseOverHandle
== &LHandle
244 || mouseOverHandle
== &BLHandle
) // dragging one of the left handles
246 r
.setLeft( r
.left() + offset
.x() );
249 if ( mouseOverHandle
== &BLHandle
|| mouseOverHandle
== &BHandle
250 || mouseOverHandle
== &BRHandle
) // dragging one of the bottom handles
252 r
.setBottom( r
.bottom() + offset
.y() );
255 if ( mouseOverHandle
== &TRHandle
|| mouseOverHandle
== &RHandle
256 || mouseOverHandle
== &BRHandle
) // dragging one of the right handles
258 r
.setRight( r
.right() + offset
.x() );
261 r
.setTopLeft( limitPointToRect( r
.topLeft(), rect() ) );
262 r
.setBottomRight( limitPointToRect( r
.bottomRight(), rect() ) );
269 if ( selection
.isNull() )
272 foreach( QRect
* r
, handles
)
274 if ( r
->contains( e
->pos() ) )
284 if ( selection
.contains( e
->pos() ) )
285 setCursor( Qt::OpenHandCursor
);
287 setCursor( Qt::CrossCursor
);
291 if ( mouseOverHandle
== &TLHandle
|| mouseOverHandle
== &BRHandle
)
292 setCursor( Qt::SizeFDiagCursor
);
293 if ( mouseOverHandle
== &TRHandle
|| mouseOverHandle
== &BLHandle
)
294 setCursor( Qt::SizeBDiagCursor
);
295 if ( mouseOverHandle
== &LHandle
|| mouseOverHandle
== &RHandle
)
296 setCursor( Qt::SizeHorCursor
);
297 if ( mouseOverHandle
== &THandle
|| mouseOverHandle
== &BHandle
)
298 setCursor( Qt::SizeVerCursor
);
303 void RegionGrabber::mouseReleaseEvent( QMouseEvent
* e
)
306 newSelection
= false;
308 if ( mouseOverHandle
== 0 && selection
.contains( e
->pos() ) )
309 setCursor( Qt::OpenHandCursor
);
313 void RegionGrabber::mouseDoubleClickEvent( QMouseEvent
* )
318 void RegionGrabber::keyPressEvent( QKeyEvent
* e
)
320 if ( e
->key() == Qt::Key_Escape
)
322 emit
regionGrabbed( QPixmap() );
324 else if ( e
->key() == Qt::Key_Enter
|| e
->key() == Qt::Key_Return
)
334 void RegionGrabber::grabRect()
336 QRect r
= selection
.normalized();
337 if ( !r
.isNull() && r
.isValid() )
340 emit
regionGrabbed( pixmap
.copy(r
) );
344 void RegionGrabber::updateHandles()
346 QRect r
= selection
.normalized().adjusted( 0, 0, -1, -1 );
347 int s2
= handleSize
/ 2;
349 TLHandle
.moveTopLeft( r
.topLeft() );
350 TRHandle
.moveTopRight( r
.topRight() );
351 BLHandle
.moveBottomLeft( r
.bottomLeft() );
352 BRHandle
.moveBottomRight( r
.bottomRight() );
354 LHandle
.moveTopLeft( QPoint( r
.x(), r
.y() + r
.height() / 2 - s2
) );
355 THandle
.moveTopLeft( QPoint( r
.x() + r
.width() / 2 - s2
, r
.y() ) );
356 RHandle
.moveTopRight( QPoint( r
.right(), r
.y() + r
.height() / 2 - s2
) );
357 BHandle
.moveBottomLeft( QPoint( r
.x() + r
.width() / 2 - s2
, r
.bottom() ) );
360 QRegion
RegionGrabber::handleMask() const
362 // note: not normalized QRects are bad here, since they will not be drawn
364 foreach( QRect
* rect
, handles
) mask
+= QRegion( *rect
);
368 QPoint
RegionGrabber::limitPointToRect( const QPoint
&p
, const QRect
&r
) const
371 q
.setX( p
.x() < r
.x() ? r
.x() : p
.x() < r
.right() ? p
.x() : r
.right() );
372 q
.setY( p
.y() < r
.y() ? r
.y() : p
.y() < r
.bottom() ? p
.y() : r
.bottom() );
376 #include "regiongrabber.moc"