]> git.sesse.net Git - kdenlive/blobdiff - src/regiongrabber.cpp
NEW: Kdenlive can now create screencasts.
[kdenlive] / src / regiongrabber.cpp
diff --git a/src/regiongrabber.cpp b/src/regiongrabber.cpp
new file mode 100644 (file)
index 0000000..a8292e3
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ *   Copyright (C) 2007 Luca Gugelmann <lucag@student.ethz.ch>
+ *   Adapted for Kdenlive by Jean-Baptiste Mardelle (2008) jb@kdenlive.org
+ *
+ *   This program is free software; you can redistribute it and/or modify it
+ *   under the terms of the GNU Library General Public License version 2 as
+ *   published by the Free Software Foundation
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "regiongrabber.h"
+
+#include <QPainter>
+#include <QMouseEvent>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QToolTip>
+
+#include <klocale.h>
+#include <KWindowSystem>
+
+RegionGrabber::RegionGrabber( ) :
+    QWidget( 0 ), selection(), mouseDown( false ), newSelection( false ),
+    handleSize( 10 ), mouseOverHandle( 0 ), idleTimer(),
+    showHelp( true ), grabbing( false ),
+    TLHandle(0,0,handleSize,handleSize), TRHandle(0,0,handleSize,handleSize),
+    BLHandle(0,0,handleSize,handleSize), BRHandle(0,0,handleSize,handleSize),
+    LHandle(0,0,handleSize,handleSize), THandle(0,0,handleSize,handleSize),
+    RHandle(0,0,handleSize,handleSize), BHandle(0,0,handleSize,handleSize)
+{
+    handles << &TLHandle << &TRHandle << &BLHandle << &BRHandle
+            << &LHandle << &THandle << &RHandle << &BHandle;
+    setMouseTracking( true );
+    setWindowFlags( Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
+    int timeout = KWindowSystem::compositingActive() ? 200 : 50;
+    QTimer::singleShot( timeout, this, SLOT(init()) );
+    connect( &idleTimer, SIGNAL( timeout() ), this, SLOT( displayHelp() ) );
+    idleTimer.start( 3000 );
+}
+
+RegionGrabber::~RegionGrabber()
+{
+}
+
+void RegionGrabber::init()
+{
+    pixmap = QPixmap::grabWindow( QApplication::desktop()->winId() );
+    showFullScreen();
+    resize( pixmap.size() );
+    move(0, 0);
+    setCursor( Qt::CrossCursor );
+}
+
+void RegionGrabber::displayHelp()
+{
+    showHelp = true;
+    update();
+}
+
+void RegionGrabber::paintEvent( QPaintEvent* e )
+{
+    Q_UNUSED( e );
+    if ( grabbing ) // grabWindow() should just get the background
+        return;
+
+    QPainter painter( this );
+
+    QPalette pal(QToolTip::palette());
+    QFont font = QToolTip::font();
+
+    QColor handleColor = pal.color( QPalette::Active, QPalette::Highlight );
+    handleColor.setAlpha( 160 );
+    QColor overlayColor( 0, 0, 0, 160 );
+    QColor textColor = pal.color( QPalette::Active, QPalette::Text );
+    QColor textBackgroundColor = pal.color( QPalette::Active, QPalette::Base );
+    painter.drawPixmap(0, 0, pixmap);
+    painter.setFont(font);
+    
+    QRect r = selection.normalized().adjusted( 0, 0, -1, -1 );
+    if ( !selection.isNull() )
+    {
+        QRegion grey( rect() );
+        grey = grey.subtracted( r );
+        painter.setPen( handleColor );
+        painter.setBrush( overlayColor );
+        painter.setClipRegion( grey );
+        painter.drawRect( -1, -1, rect().width() + 1, rect().height() + 1 );
+        painter.setClipRect( rect() );
+        painter.setBrush( Qt::NoBrush );
+        painter.drawRect( r );
+    }
+
+    if ( showHelp )
+    {
+        painter.setPen( textColor );
+        painter.setBrush( textBackgroundColor );
+        QString helpText = i18n( "Select a region using the mouse. To take the snapshot, press the Enter key. Press Esc to quit." );
+        QRect textRect = painter.boundingRect( rect().adjusted( 2, 2, -2, -2 ), Qt::TextWordWrap, helpText );
+        textRect.adjust( -2, -2, 4, 2 );
+        painter.drawRect( textRect );
+        textRect.moveTopLeft( QPoint( 3, 3 ) );
+        painter.drawText( textRect, helpText );
+    }
+
+    if ( selection.isNull() )
+    {
+        return;
+    }
+
+    // The grabbed region is everything which is covered by the drawn
+    // rectangles (border included). This means that there is no 0px
+    // selection, since a 0px wide rectangle will always be drawn as a line.
+    QString txt = QString( "%1x%2" ).arg( selection.width() == 0 ? 2 : selection.width() )
+                  .arg( selection.height() == 0 ? 2 : selection.height() );
+    QRect textRect = painter.boundingRect( rect(), Qt::AlignLeft, txt );
+    QRect boundingRect = textRect.adjusted( -4, 0, 0, 0);
+
+    if ( textRect.width() < r.width() - 2*handleSize &&
+         textRect.height() < r.height() - 2*handleSize &&
+         ( r.width() > 100 && r.height() > 100 ) ) // center, unsuitable for small selections
+    {
+        boundingRect.moveCenter( r.center() );
+        textRect.moveCenter( r.center() );
+    }
+    else if ( r.y() - 3 > textRect.height() &&
+              r.x() + textRect.width() < rect().right() ) // on top, left aligned
+    {
+        boundingRect.moveBottomLeft( QPoint( r.x(), r.y() - 3 ) );
+        textRect.moveBottomLeft( QPoint( r.x() + 2, r.y() - 3 ) );
+    }
+    else if ( r.x() - 3 > textRect.width() ) // left, top aligned
+    {
+        boundingRect.moveTopRight( QPoint( r.x() - 3, r.y() ) );
+        textRect.moveTopRight( QPoint( r.x() - 5, r.y() ) );
+    }
+    else if ( r.bottom() + 3 + textRect.height() < rect().bottom() &&
+              r.right() > textRect.width() ) // at bottom, right aligned
+    {
+        boundingRect.moveTopRight( QPoint( r.right(), r.bottom() + 3 ) );
+        textRect.moveTopRight( QPoint( r.right() - 2, r.bottom() + 3 ) );
+    }
+    else if ( r.right() + textRect.width() + 3 < rect().width() ) // right, bottom aligned
+    {
+        boundingRect.moveBottomLeft( QPoint( r.right() + 3, r.bottom() ) );
+        textRect.moveBottomLeft( QPoint( r.right() + 5, r.bottom() ) );
+    }
+    // if the above didn't catch it, you are running on a very tiny screen...
+    painter.setPen( textColor );
+    painter.setBrush( textBackgroundColor );
+    painter.drawRect( boundingRect );
+    painter.drawText( textRect, txt );
+
+    if ( ( r.height() > handleSize*2 && r.width() > handleSize*2 )
+         || !mouseDown )
+    {
+        updateHandles();
+        painter.setPen( handleColor );
+        handleColor.setAlpha( 60 );
+        painter.setBrush( handleColor );
+        painter.drawRects( handleMask().rects() );
+    }
+}
+
+void RegionGrabber::resizeEvent( QResizeEvent* e )
+{
+    Q_UNUSED( e );
+    if ( selection.isNull() )
+        return;
+    QRect r = selection;
+    r.setTopLeft( limitPointToRect( r.topLeft(), rect() ) );
+    r.setBottomRight( limitPointToRect( r.bottomRight(), rect() ) );
+    if ( r.width() <= 1 || r.height() <= 1 ) //this just results in ugly drawing...
+        r = QRect();
+    selection = r;
+}
+
+void RegionGrabber::mousePressEvent( QMouseEvent* e )
+{
+    showHelp = false;
+    idleTimer.stop();
+    if ( e->button() == Qt::LeftButton )
+    {
+        mouseDown = true;
+        dragStartPoint = e->pos();
+        selectionBeforeDrag = selection;
+        if ( !selection.contains( e->pos() ) )
+        {
+            newSelection = true;
+            selection = QRect();
+            showHelp = true;
+        }
+        else
+        {
+            setCursor( Qt::ClosedHandCursor );
+        }
+    }
+    else if ( e->button() == Qt::RightButton )
+    {
+        newSelection = false;
+        selection = QRect();
+        setCursor( Qt::CrossCursor );
+    }
+    update();
+}
+
+void RegionGrabber::mouseMoveEvent( QMouseEvent* e )
+{
+    if ( mouseDown )
+    {
+        if ( newSelection )
+        {
+            QPoint p = e->pos();
+            QRect r = rect();
+            selection = QRect( dragStartPoint, limitPointToRect( p, r ) ).normalized();
+        }
+        else if ( mouseOverHandle == 0 ) // moving the whole selection
+        {
+            QRect r = rect().normalized(), s = selectionBeforeDrag.normalized();
+            QPoint p = s.topLeft() + e->pos() - dragStartPoint;
+            r.setBottomRight( r.bottomRight() - QPoint( s.width(), s.height() ) );
+            if ( !r.isNull() && r.isValid() )
+                selection.moveTo( limitPointToRect( p, r ) );
+        }
+        else // dragging a handle
+        {
+            QRect r = selectionBeforeDrag;
+            QPoint offset = e->pos() - dragStartPoint;
+
+            if ( mouseOverHandle == &TLHandle || mouseOverHandle == &THandle
+                 || mouseOverHandle == &TRHandle ) // dragging one of the top handles
+            {
+                r.setTop( r.top() + offset.y() );
+            }
+
+            if ( mouseOverHandle == &TLHandle || mouseOverHandle == &LHandle
+                 || mouseOverHandle == &BLHandle ) // dragging one of the left handles
+            {
+                r.setLeft( r.left() + offset.x() );
+            }
+
+            if ( mouseOverHandle == &BLHandle || mouseOverHandle == &BHandle
+                 || mouseOverHandle == &BRHandle ) // dragging one of the bottom handles
+            {
+                r.setBottom( r.bottom() + offset.y() );
+            }
+
+            if ( mouseOverHandle == &TRHandle || mouseOverHandle == &RHandle
+                 || mouseOverHandle == &BRHandle ) // dragging one of the right handles
+            {
+                r.setRight( r.right() + offset.x() );
+            }
+            r = r.normalized();
+            r.setTopLeft( limitPointToRect( r.topLeft(), rect() ) );
+            r.setBottomRight( limitPointToRect( r.bottomRight(), rect() ) );
+            selection = r;
+        }
+        update();
+    }
+    else
+    {
+        if ( selection.isNull() )
+            return;
+        bool found = false;
+        foreach( QRect* r, handles )
+        {
+            if ( r->contains( e->pos() ) )
+            {
+                mouseOverHandle = r;
+                found = true;
+                break;
+            }
+        }
+        if ( !found )
+        {
+            mouseOverHandle = 0;
+            if ( selection.contains( e->pos() ) )
+                setCursor( Qt::OpenHandCursor );
+            else
+                setCursor( Qt::CrossCursor );
+        }
+        else
+        {
+            if ( mouseOverHandle == &TLHandle || mouseOverHandle == &BRHandle )
+                setCursor( Qt::SizeFDiagCursor );
+            if ( mouseOverHandle == &TRHandle || mouseOverHandle == &BLHandle )
+                setCursor( Qt::SizeBDiagCursor );
+            if ( mouseOverHandle == &LHandle || mouseOverHandle == &RHandle )
+                setCursor( Qt::SizeHorCursor );
+            if ( mouseOverHandle == &THandle || mouseOverHandle == &BHandle )
+                setCursor( Qt::SizeVerCursor );
+        }
+    }
+}
+
+void RegionGrabber::mouseReleaseEvent( QMouseEvent* e )
+{
+    mouseDown = false;
+    newSelection = false;
+    idleTimer.start();
+    if ( mouseOverHandle == 0 && selection.contains( e->pos() ) )
+        setCursor( Qt::OpenHandCursor );
+    update();
+}
+
+void RegionGrabber::mouseDoubleClickEvent( QMouseEvent* )
+{
+    grabRect();
+}
+
+void RegionGrabber::keyPressEvent( QKeyEvent* e )
+{
+    if ( e->key() == Qt::Key_Escape )
+    {
+        emit regionGrabbed( QRect() );
+               close();
+    }
+    else if ( e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return )
+    {
+        grabRect();
+    }
+    else
+    {
+        e->ignore();
+    }
+}
+
+void RegionGrabber::grabRect()
+{
+    QRect r = selection.normalized();
+    if ( !r.isNull() && r.isValid() )
+    {
+       grabbing = true;
+        emit regionGrabbed( r );
+    }
+       close();
+}
+
+void RegionGrabber::updateHandles()
+{
+    QRect r = selection.normalized().adjusted( 0, 0, -1, -1 );
+    int s2 = handleSize / 2;
+
+    TLHandle.moveTopLeft( r.topLeft() );
+    TRHandle.moveTopRight( r.topRight() );
+    BLHandle.moveBottomLeft( r.bottomLeft() );
+    BRHandle.moveBottomRight( r.bottomRight() );
+
+    LHandle.moveTopLeft( QPoint( r.x(), r.y() + r.height() / 2 - s2) );
+    THandle.moveTopLeft( QPoint( r.x() + r.width() / 2 - s2, r.y() ) );
+    RHandle.moveTopRight( QPoint( r.right(), r.y() + r.height() / 2 - s2 ) );
+    BHandle.moveBottomLeft( QPoint( r.x() + r.width() / 2 - s2, r.bottom() ) );
+}
+
+QRegion RegionGrabber::handleMask() const
+{
+    // note: not normalized QRects are bad here, since they will not be drawn
+    QRegion mask;
+    foreach( QRect* rect, handles ) mask += QRegion( *rect );
+    return mask;
+}
+
+QPoint RegionGrabber::limitPointToRect( const QPoint &p, const QRect &r ) const
+{
+    QPoint q;
+    q.setX( p.x() < r.x() ? r.x() : p.x() < r.right() ? p.x() : r.right() );
+    q.setY( p.y() < r.y() ? r.y() : p.y() < r.bottom() ? p.y() : r.bottom() );
+    return q;
+}
+
+#include "regiongrabber.moc"