summaryrefslogtreecommitdiffstats
path: root/kig/kig/kig_view.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kig/kig/kig_view.cpp')
-rw-r--r--kig/kig/kig_view.cpp593
1 files changed, 593 insertions, 0 deletions
diff --git a/kig/kig/kig_view.cpp b/kig/kig/kig_view.cpp
new file mode 100644
index 00000000..7d36bc14
--- /dev/null
+++ b/kig/kig/kig_view.cpp
@@ -0,0 +1,593 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002 Dominique Devriese <devriese@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 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 "kig_view.h"
+#include "kig_view.moc"
+
+#include "kig_part.h"
+#include "kig_document.h"
+#include "kig_commands.h"
+#include "../misc/coordinate_system.h"
+#include "../misc/kiginputdialog.h"
+#include "../misc/kigpainter.h"
+#include "../modes/mode.h"
+#include "../modes/dragrectmode.h"
+
+#include <qdialog.h>
+#include <qevent.h>
+#include <qwhatsthis.h>
+#include <qlayout.h>
+#include <qscrollbar.h>
+
+#include <kdebug.h>
+#include <kcursor.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <kstdaction.h>
+#include <kaction.h>
+#include <kiconloader.h>
+
+#include <cmath>
+#include <algorithm>
+
+kdbgstream& operator<< ( kdbgstream& s, const QPoint& t )
+{
+ s << "x: " << t.x() << " y: " << t.y();
+ return s;
+}
+
+KigWidget::KigWidget( KigPart* part,
+ KigView* view,
+ QWidget* parent,
+ const char* name,
+ bool fullscreen )
+ : QWidget( parent, name,
+ fullscreen ? WStyle_Customize | WStyle_NoBorder : 0 ),
+ mpart( part ),
+ mview( view ),
+ stillPix(size()),
+ curPix(size()),
+ msi( Rect(), rect() ),
+ misfullscreen( fullscreen )
+{
+ part->addWidget(this);
+
+ setFocusPolicy(ClickFocus);
+ setBackgroundMode( Qt::NoBackground );
+ setMouseTracking(true);
+
+ curPix.resize( size() );
+ stillPix.resize( size() );
+}
+
+KigWidget::~KigWidget()
+{
+ mpart->delWidget( this );
+}
+
+void KigWidget::paintEvent(QPaintEvent*)
+{
+ updateEntireWidget();
+}
+
+void KigWidget::mousePressEvent (QMouseEvent* e)
+{
+ if( e->button() & Qt::LeftButton )
+ return mpart->mode()->leftClicked( e, this );
+ if ( e->button() & Qt::MidButton )
+ return mpart->mode()->midClicked( e, this );
+ if ( e->button() & Qt::RightButton )
+ return mpart->mode()->rightClicked( e, this );
+}
+
+void KigWidget::mouseMoveEvent (QMouseEvent* e)
+{
+ if( e->state() & Qt::LeftButton )
+ return mpart->mode()->leftMouseMoved( e, this );
+ if ( e->state() & Qt::MidButton )
+ return mpart->mode()->midMouseMoved( e, this );
+ if ( e->state() & Qt::RightButton )
+ return mpart->mode()->rightMouseMoved( e, this );
+ return mpart->mode()->mouseMoved( e, this );
+}
+
+void KigWidget::mouseReleaseEvent (QMouseEvent* e)
+{
+ if( e->state() & Qt::LeftButton )
+ return mpart->mode()->leftReleased( e, this );
+ if ( e->state() & Qt::MidButton )
+ return mpart->mode()->midReleased( e, this );
+ if ( e->state() & Qt::RightButton )
+ return mpart->mode()->rightReleased( e, this );
+}
+
+void KigWidget::updateWidget( const std::vector<QRect>& overlay )
+{
+#undef SHOW_OVERLAY_RECTS
+#ifdef SHOW_OVERLAY_RECTS
+ QPainter debug (this, this);
+ debug.setPen(Qt::yellow);
+#endif // SHOW_OVERLAY_RECTS
+ // we undo our old changes...
+ for ( std::vector<QRect>::const_iterator i = oldOverlay.begin(); i != oldOverlay.end(); ++i )
+ bitBlt( this, i->topLeft(), &curPix, *i );
+ // we add our new changes...
+ for ( std::vector<QRect>::const_iterator i = overlay.begin(); i != overlay.end(); ++i )
+ {
+ bitBlt( this, i->topLeft(), &curPix, *i );
+#ifdef SHOW_OVERLAY_RECTS
+ debug.drawRect(*i);
+#endif
+ };
+ oldOverlay = overlay;
+}
+
+void KigWidget::updateEntireWidget()
+{
+ std::vector<QRect> overlay;
+ overlay.push_back( QRect( QPoint( 0, 0 ), size() ) );
+ updateWidget( overlay );
+}
+
+void KigWidget::resizeEvent( QResizeEvent* e )
+{
+ QSize osize = e->oldSize();
+ QSize nsize = e->size();
+ Rect orect = msi.shownRect();
+
+ curPix.resize( nsize );
+ stillPix.resize( nsize );
+ msi.setViewRect( rect() );
+
+ Rect nrect( 0., 0.,
+ orect.width() * nsize.width() / osize.width(),
+ orect.height() * nsize.height() / osize.height() );
+ nrect = matchScreenShape( nrect );
+ nrect.setCenter( orect.center() );
+ msi.setShownRect( nrect );
+
+ // horrible hack... We need to somehow differentiate between the
+ // resizeEvents we get on startup, and the ones generated by the
+ // user. The first require recentering the screen, the latter
+ // don't..
+ if ( nsize.width() / osize.width() > 4 ) recenterScreen();
+
+ mpart->redrawScreen( this );
+ updateScrollBars();
+}
+
+void KigWidget::updateCurPix( const std::vector<QRect>& ol )
+{
+ // we make curPix look like stillPix again...
+ for ( std::vector<QRect>::const_iterator i = oldOverlay.begin(); i != oldOverlay.end(); ++i )
+ bitBlt( &curPix, i->topLeft(), &stillPix, *i );
+ for ( std::vector<QRect>::const_iterator i = ol.begin(); i != ol.end(); ++i )
+ bitBlt( &curPix, i->topLeft(), &stillPix, *i );
+
+ // we add ol to oldOverlay, so that part of the widget will be
+ // updated too in updateWidget...
+ std::copy( ol.begin(), ol.end(), std::back_inserter( oldOverlay ) );
+}
+
+void KigWidget::recenterScreen()
+{
+ msi.setShownRect( matchScreenShape( mpart->document().suggestedRect() ) );
+}
+
+Rect KigWidget::matchScreenShape( const Rect& r ) const
+{
+ return r.matchShape( Rect::fromQRect( rect() ) );
+}
+
+void KigWidget::slotZoomIn()
+{
+ Rect nr = msi.shownRect();
+ Coordinate c = nr.center();
+ nr /= 2;
+ nr.setCenter( c );
+ KigCommand* cd =
+ new KigCommand( *mpart,
+ i18n( "Zoom In" ) );
+ cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
+ mpart->history()->addCommand( cd );
+}
+
+void KigWidget::slotZoomOut()
+{
+ Rect nr = msi.shownRect();
+ Coordinate c = nr.center();
+ nr *= 2;
+ nr.setCenter( c );
+
+ // zooming in is undoable.. I know this isn't really correct,
+ // because the current view doesn't really belong to the document (
+ // althought KGeo and KSeg both save them along, iirc ). However,
+ // undoing a zoom or another operation affecting the window seems a
+ // bit too useful to not be available. Please try to convince me if
+ // you feel otherwise ;-)
+ KigCommand* cd =
+ new KigCommand( *mpart,
+ i18n( "Zoom Out" ) );
+ cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
+ mpart->history()->addCommand( cd );
+}
+
+void KigWidget::clearStillPix()
+{
+ stillPix.fill(Qt::white);
+ oldOverlay.clear();
+ oldOverlay.push_back ( QRect( QPoint(0,0), size() ) );
+}
+
+void KigWidget::redrawScreen( const std::vector<ObjectHolder*>& selection, bool dos )
+{
+ std::vector<ObjectHolder*> nonselection;
+ std::set<ObjectHolder*> objs = mpart->document().objectsSet();
+ std::set_difference( objs.begin(), objs.end(), selection.begin(), selection.end(),
+ std::back_inserter( nonselection ) );
+
+ // update the screen...
+ clearStillPix();
+ KigPainter p( msi, &stillPix, mpart->document() );
+ p.drawGrid( mpart->document().coordinateSystem(), mpart->document().grid(),
+ mpart->document().axes() );
+ p.drawObjects( selection, true );
+ p.drawObjects( nonselection, false );
+ updateCurPix( p.overlay() );
+ if ( dos ) updateEntireWidget();
+}
+
+const ScreenInfo& KigWidget::screenInfo() const
+{
+ return msi;
+}
+
+const Rect KigWidget::showingRect() const
+{
+ return msi.shownRect();
+}
+
+const Coordinate KigWidget::fromScreen( const QPoint& p )
+{
+ return msi.fromScreen( p );
+}
+
+double KigWidget::pixelWidth() const
+{
+ return msi.pixelWidth();
+}
+
+const Rect KigWidget::fromScreen( const QRect& r )
+{
+ return msi.fromScreen( r );
+}
+
+
+void KigWidget::updateScrollBars()
+{
+ mview->updateScrollBars();
+}
+
+KigView::KigView( KigPart* part,
+ bool fullscreen,
+ QWidget* parent,
+ const char* name )
+ : QWidget( parent, name ),
+ mlayout( 0 ), mrightscroll( 0 ), mbottomscroll( 0 ),
+ mupdatingscrollbars( false ),
+ mrealwidget( 0 ), mpart( part )
+{
+ connect( part, SIGNAL( recenterScreen() ), this, SLOT( slotInternalRecenterScreen() ) );
+
+ mlayout = new QGridLayout( this, 2, 2 );
+ mrightscroll = new QScrollBar( Vertical, this, "Right Scrollbar" );
+ // TODO: make this configurable...
+ mrightscroll->setTracking( true );
+ connect( mrightscroll, SIGNAL( valueChanged( int ) ),
+ this, SLOT( slotRightScrollValueChanged( int ) ) );
+ connect( mrightscroll, SIGNAL( sliderReleased() ),
+ this, SLOT( updateScrollBars() ) );
+ mbottomscroll = new QScrollBar( Horizontal, this, "Bottom Scrollbar" );
+ connect( mbottomscroll, SIGNAL( valueChanged( int ) ),
+ this, SLOT( slotBottomScrollValueChanged( int ) ) );
+ connect( mbottomscroll, SIGNAL( sliderReleased() ),
+ this, SLOT( updateScrollBars() ) );
+ mrealwidget = new KigWidget( part, this, this, "Kig Widget", fullscreen );
+ mlayout->addWidget( mbottomscroll, 1, 0 );
+ mlayout->addWidget( mrealwidget, 0, 0 );
+ mlayout->addWidget( mrightscroll, 0, 1 );
+
+ resize( sizeHint() );
+ mrealwidget->recenterScreen();
+ part->redrawScreen( mrealwidget );
+ updateScrollBars();
+}
+
+void KigView::updateScrollBars()
+{
+ // we update the scrollbars to reflect the new "total size" of the
+ // document... The total size is calced in entireDocumentRect().
+ // ( it is calced as a rect that contains all the points in the
+ // document, and then enlarged a bit, and scaled to match the screen
+ // width/height ratio...
+ // What we do here is tell the scroll bars what they should show as
+ // their total size..
+
+ // see the doc of this variable in the header for this...
+ mupdatingscrollbars = true;
+
+ Rect er = mrealwidget->entireDocumentRect();
+ Rect sr = mrealwidget->screenInfo().shownRect();
+
+ // we define the total rect to be the smallest rect that contains
+ // both er and sr...
+ er |= sr;
+
+ // we need ints, not doubles, so since "pixelwidth == widgetcoord /
+ // internalcoord", we use "widgetcoord/pixelwidth", which would then
+ // equal "internalcoord", which has to be an int ( by definition.. )
+ // i know, i'm a freak to think about these sorts of things... ;)
+ double pw = mrealwidget->screenInfo().pixelWidth();
+
+ // what the scrollbars reflect is the bottom resp. the left side of
+ // the shown rect. This is why the maximum value is not er.top()
+ // (which would be the maximum value of the top of the shownRect),
+ // but er.top() - sr.height(), which is the maximum value the bottom of
+ // the shownRect can reach...
+
+ int rightmin = static_cast<int>( er.bottom() / pw );
+ int rightmax = static_cast<int>( ( er.top() - sr.height() ) / pw );
+
+ mrightscroll->setMinValue( rightmin );
+ mrightscroll->setMaxValue( rightmax );
+ mrightscroll->setLineStep( (int)( sr.height() / pw / 10 ) );
+ mrightscroll->setPageStep( (int)( sr.height() / pw / 1.2 ) );
+
+ // note that since Qt has a coordinate system with the lowest y
+ // values at the top, and we have it the other way around ( i know i
+ // shouldn't have done this.. :( ), we invert the value that the
+ // scrollbar shows. This is inverted again in
+ // slotRightScrollValueChanged()...
+ mrightscroll->setValue( (int) ( rightmin + ( rightmax - ( sr.bottom() / pw ) ) ) );
+
+ mbottomscroll->setMinValue( (int)( er.left() / pw ) );
+ mbottomscroll->setMaxValue( (int)( ( er.right() - sr.width() ) / pw ) );
+ mbottomscroll->setLineStep( (int)( sr.width() / pw / 10 ) );
+ mbottomscroll->setPageStep( (int)( sr.width() / pw / 1.2 ) );
+ mbottomscroll->setValue( (int)( sr.left() / pw ) );
+
+ mupdatingscrollbars = false;
+}
+
+Rect KigWidget::entireDocumentRect() const
+{
+ return matchScreenShape( mpart->document().suggestedRect() );
+}
+
+void KigView::slotRightScrollValueChanged( int v )
+{
+ if ( ! mupdatingscrollbars )
+ {
+ // we invert the inversion that was done in updateScrollBars() (
+ // check the documentation there..; )
+ v = mrightscroll->minValue() + ( mrightscroll->maxValue() - v );
+ double pw = mrealwidget->screenInfo().pixelWidth();
+ double nb = double( v ) * pw;
+ mrealwidget->scrollSetBottom( nb );
+ };
+}
+
+void KigView::slotBottomScrollValueChanged( int v )
+{
+ if ( ! mupdatingscrollbars )
+ {
+ double pw = mrealwidget->screenInfo().pixelWidth();
+ double nl = double( v ) * pw;
+ mrealwidget->scrollSetLeft( nl );
+ };
+}
+
+void KigWidget::scrollSetBottom( double rhs )
+{
+ Rect sr = msi.shownRect();
+ Coordinate bl = sr.bottomLeft();
+ bl.y = rhs;
+ sr.setBottomLeft( bl );
+ msi.setShownRect( sr );
+ mpart->redrawScreen( this );
+}
+
+void KigWidget::scrollSetLeft( double rhs )
+{
+ Rect sr = msi.shownRect();
+ Coordinate bl = sr.bottomLeft();
+ bl.x = rhs;
+ sr.setBottomLeft( bl );
+ msi.setShownRect( sr );
+ mpart->redrawScreen( this );
+}
+
+const ScreenInfo& KigView::screenInfo() const
+{
+ return mrealwidget->screenInfo();
+}
+
+KigView::~KigView()
+{
+}
+
+KigWidget* KigView::realWidget() const
+{
+ return mrealwidget;
+}
+
+const KigDocument& KigWidget::document() const
+{
+ return mpart->document();
+}
+
+QSize KigWidget::sizeHint() const
+{
+ return QSize( 630, 450 );
+}
+
+void KigWidget::wheelEvent( QWheelEvent* e )
+{
+ int delta = e->delta();
+ Qt::Orientation orient = e->orientation();
+ if ( orient == Qt::Vertical )
+ mview->scrollVertical( delta );
+ else
+ mview->scrollHorizontal( delta );
+}
+
+void KigView::scrollHorizontal( int delta )
+{
+ if ( delta >= 0 )
+ for ( int i = 0; i < delta; i += 120 )
+ mbottomscroll->subtractLine();
+ else
+ for ( int i = 0; i >= delta; i -= 120 )
+ mbottomscroll->addLine();
+}
+
+void KigView::scrollVertical( int delta )
+{
+ if ( delta >= 0 )
+ for ( int i = 0; i < delta; i += 120 )
+ mrightscroll->subtractLine();
+ else
+ for ( int i = 0; i >= delta; i -= 120 )
+ mrightscroll->addLine();
+}
+
+bool KigWidget::isFullScreen() const
+{
+ return misfullscreen;
+}
+
+void KigView::slotZoomIn()
+{
+ mrealwidget->slotZoomIn();
+}
+
+void KigView::slotZoomOut()
+{
+ mrealwidget->slotZoomOut();
+}
+
+void KigWidget::slotRecenterScreen()
+{
+ Rect nr = mpart->document().suggestedRect();
+ KigCommand* cd =
+ new KigCommand( *mpart,
+ i18n( "Recenter View" ) );
+
+ cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
+ mpart->history()->addCommand( cd );
+}
+
+void KigView::toggleFullScreen()
+{
+ mrealwidget->setFullScreen( ! mrealwidget->isFullScreen() );
+ if ( mrealwidget->isFullScreen() )
+ topLevelWidget()->showFullScreen();
+ else
+ topLevelWidget()->showNormal();
+}
+
+void KigWidget::setFullScreen( bool f )
+{
+ misfullscreen = f;
+}
+
+void KigWidget::zoomRect()
+{
+ mpart->emitStatusBarText( i18n( "Select the rectangle that should be shown." ) );
+ DragRectMode d( *mpart, *this );
+ mpart->runMode( &d );
+ if ( ! d.cancelled() )
+ {
+ Rect nr = d.rect();
+ KigCommand* cd =
+ new KigCommand( *mpart,
+ i18n( "Change Shown Part of Screen" ) );
+
+ cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
+ mpart->history()->addCommand( cd );
+ };
+
+ mpart->redrawScreen( this );
+ updateScrollBars();
+}
+
+void KigView::zoomRect()
+{
+ mrealwidget->zoomRect();
+}
+
+void KigWidget::setShowingRect( const Rect& r )
+{
+ msi.setShownRect( r.matchShape( Rect::fromQRect( rect() ) ) );
+}
+
+void KigView::slotRecenterScreen()
+{
+ mrealwidget->slotRecenterScreen();
+}
+
+void KigView::slotInternalRecenterScreen()
+{
+ mrealwidget->recenterScreen();
+}
+
+void KigWidget::zoomArea()
+{
+// mpart->emitStatusBarText( i18n( "Select the area that should be shown." ) );
+ Rect oldrect = showingRect();
+ Coordinate tl = oldrect.topLeft();
+ Coordinate br = oldrect.bottomRight();
+ bool ok = true;
+ KigInputDialog::getTwoCoordinates( i18n( "Select Zoom Area" ),
+ i18n( "Select the zoom area by entering the coordinates of "
+ "the upper left corner and the lower right corner." ) +
+ QString::fromLatin1("<br>") +
+ mpart->document().coordinateSystem().coordinateFormatNoticeMarkup(),
+ this, &ok, mpart->document(), &tl, &br );
+ if ( ok )
+ {
+ Coordinate nc1( tl.x, br.y );
+ Coordinate nc2( br.x, tl.y );
+ Rect nr( nc1, nc2 );
+ KigCommand* cd = new KigCommand( *mpart, i18n( "Change Shown Part of Screen" ) );
+
+ cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
+ mpart->history()->addCommand( cd );
+ }
+
+ mpart->redrawScreen( this );
+ updateScrollBars();
+}
+
+void KigView::zoomArea()
+{
+ mrealwidget->zoomArea();
+}
+