moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kig / kig / kig_view.cpp
blob2d1342c456aab5fc4dc5984bb4cf9e2240241080
1 /**
2 This file is part of Kig, a KDE program for Interactive Geometry...
3 Copyright (C) 2002 Dominique Devriese <devriese@kde.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 USA
19 **/
21 #include "kig_view.h"
22 #include "kig_view.moc"
24 #include "kig_part.h"
25 #include "kig_document.h"
26 #include "kig_commands.h"
27 #include "../misc/coordinate_system.h"
28 #include "../misc/kigpainter.h"
29 #include "zoomarea.h"
30 #include "../modes/mode.h"
31 #include "../modes/dragrectmode.h"
33 #include <qdialog.h>
34 #include <qwhatsthis.h>
35 #include <qlayout.h>
36 #include <qscrollbar.h>
38 #include <kdebug.h>
39 #include <kcursor.h>
40 #include <klocale.h>
41 #include <kapplication.h>
42 #include <kstdaction.h>
43 #include <kaction.h>
44 #include <kiconloader.h>
46 #include <cmath>
47 #include <algorithm>
49 kdbgstream& operator<< ( kdbgstream& s, const QPoint& t )
51 s << "x: " << t.x() << " y: " << t.y();
52 return s;
55 KigWidget::KigWidget( KigPart* part,
56 KigView* view,
57 QWidget* parent,
58 const char* name,
59 bool fullscreen )
60 : QWidget( parent, name,
61 fullscreen ? WStyle_Customize | WStyle_NoBorder : 0 ),
62 mpart( part ),
63 mview( view ),
64 stillPix(size()),
65 curPix(size()),
66 msi( Rect(), rect() ),
67 misfullscreen( fullscreen )
69 part->addWidget(this);
71 setFocusPolicy(QWidget::ClickFocus);
72 setBackgroundMode( Qt::NoBackground );
73 setMouseTracking(true);
75 curPix.resize( size() );
76 stillPix.resize( size() );
79 KigWidget::~KigWidget()
81 mpart->delWidget( this );
84 void KigWidget::paintEvent(QPaintEvent*)
86 updateEntireWidget();
89 void KigWidget::mousePressEvent (QMouseEvent* e)
91 if( e->button() & Qt::LeftButton )
92 return mpart->mode()->leftClicked( e, this );
93 if ( e->button() & Qt::MidButton )
94 return mpart->mode()->midClicked( e, this );
95 if ( e->button() & Qt::RightButton )
96 return mpart->mode()->rightClicked( e, this );
99 void KigWidget::mouseMoveEvent (QMouseEvent* e)
101 if( e->state() & Qt::LeftButton )
102 return mpart->mode()->leftMouseMoved( e, this );
103 if ( e->state() & Qt::MidButton )
104 return mpart->mode()->midMouseMoved( e, this );
105 if ( e->state() & Qt::RightButton )
106 return mpart->mode()->rightMouseMoved( e, this );
107 return mpart->mode()->mouseMoved( e, this );
110 void KigWidget::mouseReleaseEvent (QMouseEvent* e)
112 if( e->state() & Qt::LeftButton )
113 return mpart->mode()->leftReleased( e, this );
114 if ( e->state() & Qt::MidButton )
115 return mpart->mode()->midReleased( e, this );
116 if ( e->state() & Qt::RightButton )
117 return mpart->mode()->rightReleased( e, this );
120 void KigWidget::updateWidget( const std::vector<QRect>& overlay )
122 #undef SHOW_OVERLAY_RECTS
123 #ifdef SHOW_OVERLAY_RECTS
124 QPainter debug (this, this);
125 debug.setPen(Qt::yellow);
126 #endif // SHOW_OVERLAY_RECTS
127 // we undo our old changes...
128 for ( std::vector<QRect>::const_iterator i = oldOverlay.begin(); i != oldOverlay.end(); ++i )
129 bitBlt( this, i->topLeft(), &curPix, *i );
130 // we add our new changes...
131 for ( std::vector<QRect>::const_iterator i = overlay.begin(); i != overlay.end(); ++i )
133 bitBlt( this, i->topLeft(), &curPix, *i );
134 #ifdef SHOW_OVERLAY_RECTS
135 debug.drawRect(*i);
136 #endif
138 oldOverlay = overlay;
141 void KigWidget::updateEntireWidget()
143 std::vector<QRect> overlay;
144 overlay.push_back( QRect( QPoint( 0, 0 ), size() ) );
145 updateWidget( overlay );
148 void KigWidget::resizeEvent( QResizeEvent* e )
150 QSize osize = e->oldSize();
151 QSize nsize = e->size();
152 Rect orect = msi.shownRect();
154 curPix.resize( nsize );
155 stillPix.resize( nsize );
156 msi.setViewRect( rect() );
158 Rect nrect( 0., 0.,
159 orect.width() * nsize.width() / osize.width(),
160 orect.height() * nsize.height() / osize.height() );
161 nrect = matchScreenShape( nrect );
162 nrect.setCenter( orect.center() );
163 msi.setShownRect( nrect );
165 // horrible hack... We need to somehow differentiate between the
166 // resizeEvents we get on startup, and the ones generated by the
167 // user. The first require recentering the screen, the latter
168 // don't..
169 if ( nsize.width() / osize.width() > 4 ) recenterScreen();
171 mpart->redrawScreen( this );
172 updateScrollBars();
175 void KigWidget::updateCurPix( const std::vector<QRect>& ol )
177 // we make curPix look like stillPix again...
178 for ( std::vector<QRect>::const_iterator i = oldOverlay.begin(); i != oldOverlay.end(); ++i )
179 bitBlt( &curPix, i->topLeft(), &stillPix, *i );
180 for ( std::vector<QRect>::const_iterator i = ol.begin(); i != ol.end(); ++i )
181 bitBlt( &curPix, i->topLeft(), &stillPix, *i );
183 // we add ol to oldOverlay, so that part of the widget will be
184 // updated too in updateWidget...
185 std::copy( ol.begin(), ol.end(), std::back_inserter( oldOverlay ) );
188 void KigWidget::recenterScreen()
190 msi.setShownRect( matchScreenShape( mpart->document().suggestedRect() ) );
193 Rect KigWidget::matchScreenShape( const Rect& r ) const
195 return r.matchShape( Rect::fromQRect( rect() ) );
198 void KigWidget::slotZoomIn()
200 Rect nr = msi.shownRect();
201 Coordinate c = nr.center();
202 nr /= 2;
203 nr.setCenter( c );
204 KigCommand* cd =
205 new KigCommand( *mpart,
206 i18n( "Zoom In" ) );
207 cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
208 mpart->history()->addCommand( cd );
211 void KigWidget::slotZoomOut()
213 Rect nr = msi.shownRect();
214 Coordinate c = nr.center();
215 nr *= 2;
216 nr.setCenter( c );
218 // zooming in is undoable.. I know this isn't really correct,
219 // because the current view doesn't really belong to the document (
220 // althought KGeo and KSeg both save them along, iirc ). However,
221 // undoing a zoom or another operation affecting the window seems a
222 // bit too useful to not be available. Please try to convince me if
223 // you feel otherwise ;-)
224 KigCommand* cd =
225 new KigCommand( *mpart,
226 i18n( "Zoom Out" ) );
227 cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
228 mpart->history()->addCommand( cd );
231 void KigWidget::clearStillPix()
233 stillPix.fill(Qt::white);
234 oldOverlay.clear();
235 oldOverlay.push_back ( QRect( QPoint(0,0), size() ) );
238 void KigWidget::redrawScreen( const std::vector<ObjectHolder*>& selection, bool dos )
240 std::vector<ObjectHolder*> nonselection;
241 std::set<ObjectHolder*> objs = mpart->document().objectsSet();
242 std::set_difference( objs.begin(), objs.end(), selection.begin(), selection.end(),
243 std::back_inserter( nonselection ) );
245 // update the screen...
246 clearStillPix();
247 KigPainter p( msi, &stillPix, mpart->document() );
248 p.drawGrid( mpart->document().coordinateSystem(), mpart->document().grid(),
249 mpart->document().axes() );
250 p.drawObjects( selection, true );
251 p.drawObjects( nonselection, false );
252 updateCurPix( p.overlay() );
253 if ( dos ) updateEntireWidget();
256 const ScreenInfo& KigWidget::screenInfo() const
258 return msi;
261 const Rect KigWidget::showingRect() const
263 return msi.shownRect();
266 const Coordinate KigWidget::fromScreen( const QPoint& p )
268 return msi.fromScreen( p );
271 double KigWidget::pixelWidth() const
273 return msi.pixelWidth();
276 const Rect KigWidget::fromScreen( const QRect& r )
278 return msi.fromScreen( r );
282 void KigWidget::updateScrollBars()
284 mview->updateScrollBars();
287 KigView::KigView( KigPart* part,
288 bool fullscreen,
289 QWidget* parent,
290 const char* name )
291 : QWidget( parent, name ),
292 mlayout( 0 ), mrightscroll( 0 ), mbottomscroll( 0 ),
293 mupdatingscrollbars( false ),
294 mrealwidget( 0 ), mpart( part )
296 connect( part, SIGNAL( recenterScreen() ), this, SLOT( slotInternalRecenterScreen() ) );
298 mlayout = new QGridLayout( this, 2, 2 );
299 mrightscroll = new QScrollBar( Vertical, this, "Right Scrollbar" );
300 // TODO: make this configurable...
301 mrightscroll->setTracking( true );
302 connect( mrightscroll, SIGNAL( valueChanged( int ) ),
303 this, SLOT( slotRightScrollValueChanged( int ) ) );
304 connect( mrightscroll, SIGNAL( sliderReleased() ),
305 this, SLOT( updateScrollBars() ) );
306 mbottomscroll = new QScrollBar( Horizontal, this, "Bottom Scrollbar" );
307 connect( mbottomscroll, SIGNAL( valueChanged( int ) ),
308 this, SLOT( slotBottomScrollValueChanged( int ) ) );
309 connect( mbottomscroll, SIGNAL( sliderReleased() ),
310 this, SLOT( updateScrollBars() ) );
311 mrealwidget = new KigWidget( part, this, this, "Kig Widget", fullscreen );
312 mlayout->addWidget( mbottomscroll, 1, 0 );
313 mlayout->addWidget( mrealwidget, 0, 0 );
314 mlayout->addWidget( mrightscroll, 0, 1 );
316 resize( sizeHint() );
317 mrealwidget->recenterScreen();
318 part->redrawScreen( mrealwidget );
319 updateScrollBars();
322 void KigView::updateScrollBars()
324 // we update the scrollbars to reflect the new "total size" of the
325 // document... The total size is calced in entireDocumentRect().
326 // ( it is calced as a rect that contains all the points in the
327 // document, and then enlarged a bit, and scaled to match the screen
328 // width/height ratio...
329 // What we do here is tell the scroll bars what they should show as
330 // their total size..
332 // see the doc of this variable in the header for this...
333 mupdatingscrollbars = true;
335 Rect er = mrealwidget->entireDocumentRect();
336 Rect sr = mrealwidget->screenInfo().shownRect();
338 // we define the total rect to be the smallest rect that contains
339 // both er and sr...
340 er |= sr;
342 // we need ints, not doubles, so since "pixelwidth == widgetcoord /
343 // internalcoord", we use "widgetcoord/pixelwidth", which would then
344 // equal "internalcoord", which has to be an int ( by definition.. )
345 // i know, i'm a freak to think about these sorts of things... ;)
346 double pw = mrealwidget->screenInfo().pixelWidth();
348 // what the scrollbars reflect is the bottom resp. the left side of
349 // the shown rect. This is why the maximum value is not er.top()
350 // (which would be the maximum value of the top of the shownRect),
351 // but er.top() - sr.height(), which is the maximum value the bottom of
352 // the shownRect can reach...
354 int rightmin = static_cast<int>( er.bottom() / pw );
355 int rightmax = static_cast<int>( ( er.top() - sr.height() ) / pw );
357 mrightscroll->setMinValue( rightmin );
358 mrightscroll->setMaxValue( rightmax );
359 mrightscroll->setLineStep( (int)( sr.height() / pw / 10 ) );
360 mrightscroll->setPageStep( (int)( sr.height() / pw / 1.2 ) );
362 // note that since Qt has a coordinate system with the lowest y
363 // values at the top, and we have it the other way around ( i know i
364 // shouldn't have done this.. :( ), we invert the value that the
365 // scrollbar shows. This is inverted again in
366 // slotRightScrollValueChanged()...
367 mrightscroll->setValue( (int) ( rightmin + ( rightmax - ( sr.bottom() / pw ) ) ) );
369 mbottomscroll->setMinValue( (int)( er.left() / pw ) );
370 mbottomscroll->setMaxValue( (int)( ( er.right() - sr.width() ) / pw ) );
371 mbottomscroll->setLineStep( (int)( sr.width() / pw / 10 ) );
372 mbottomscroll->setPageStep( (int)( sr.width() / pw / 1.2 ) );
373 mbottomscroll->setValue( (int)( sr.left() / pw ) );
375 mupdatingscrollbars = false;
378 Rect KigWidget::entireDocumentRect() const
380 return matchScreenShape( mpart->document().suggestedRect() );
383 void KigView::slotRightScrollValueChanged( int v )
385 if ( ! mupdatingscrollbars )
387 // we invert the inversion that was done in updateScrollBars() (
388 // check the documentation there..; )
389 v = mrightscroll->minValue() + ( mrightscroll->maxValue() - v );
390 double pw = mrealwidget->screenInfo().pixelWidth();
391 double nb = double( v ) * pw;
392 mrealwidget->scrollSetBottom( nb );
396 void KigView::slotBottomScrollValueChanged( int v )
398 if ( ! mupdatingscrollbars )
400 double pw = mrealwidget->screenInfo().pixelWidth();
401 double nl = double( v ) * pw;
402 mrealwidget->scrollSetLeft( nl );
406 void KigWidget::scrollSetBottom( double rhs )
408 Rect sr = msi.shownRect();
409 Coordinate bl = sr.bottomLeft();
410 bl.y = rhs;
411 sr.setBottomLeft( bl );
412 msi.setShownRect( sr );
413 mpart->redrawScreen( this );
416 void KigWidget::scrollSetLeft( double rhs )
418 Rect sr = msi.shownRect();
419 Coordinate bl = sr.bottomLeft();
420 bl.x = rhs;
421 sr.setBottomLeft( bl );
422 msi.setShownRect( sr );
423 mpart->redrawScreen( this );
426 const ScreenInfo& KigView::screenInfo() const
428 return mrealwidget->screenInfo();
431 KigView::~KigView()
435 KigWidget* KigView::realWidget() const
437 return mrealwidget;
440 const KigDocument& KigWidget::document() const
442 return mpart->document();
445 QSize KigWidget::sizeHint() const
447 return QSize( 630, 450 );
450 void KigWidget::wheelEvent( QWheelEvent* e )
452 int delta = e->delta();
453 Qt::Orientation orient = e->orientation();
454 if ( orient == Qt::Vertical )
455 mview->scrollVertical( delta );
456 else
457 mview->scrollHorizontal( delta );
460 void KigView::scrollHorizontal( int delta )
462 if ( delta >= 0 )
463 for ( int i = 0; i < delta; i += 120 )
464 mbottomscroll->subtractLine();
465 else
466 for ( int i = 0; i >= delta; i -= 120 )
467 mbottomscroll->addLine();
470 void KigView::scrollVertical( int delta )
472 if ( delta >= 0 )
473 for ( int i = 0; i < delta; i += 120 )
474 mrightscroll->subtractLine();
475 else
476 for ( int i = 0; i >= delta; i -= 120 )
477 mrightscroll->addLine();
480 bool KigWidget::isFullScreen() const
482 return misfullscreen;
485 void KigView::slotZoomIn()
487 mrealwidget->slotZoomIn();
490 void KigView::slotZoomOut()
492 mrealwidget->slotZoomOut();
495 void KigWidget::slotRecenterScreen()
497 Rect nr = mpart->document().suggestedRect();
498 KigCommand* cd =
499 new KigCommand( *mpart,
500 i18n( "Recenter View" ) );
502 cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
503 mpart->history()->addCommand( cd );
506 void KigView::toggleFullScreen()
508 mrealwidget->setFullScreen( ! mrealwidget->isFullScreen() );
509 if ( mrealwidget->isFullScreen() )
510 topLevelWidget()->showFullScreen();
511 else
512 topLevelWidget()->showNormal();
515 void KigWidget::setFullScreen( bool f )
517 misfullscreen = f;
520 void KigWidget::zoomRect()
522 mpart->emitStatusBarText( i18n( "Select the rectangle that should be shown." ) );
523 DragRectMode d( *mpart, *this );
524 mpart->runMode( &d );
525 if ( ! d.cancelled() )
527 Rect nr = d.rect();
528 KigCommand* cd =
529 new KigCommand( *mpart,
530 i18n( "Change Shown Part of Screen" ) );
532 cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
533 mpart->history()->addCommand( cd );
536 mpart->redrawScreen( this );
537 updateScrollBars();
540 void KigView::zoomRect()
542 mrealwidget->zoomRect();
545 void KigWidget::setShowingRect( const Rect& r )
547 msi.setShownRect( r.matchShape( Rect::fromQRect( rect() ) ) );
550 void KigView::slotRecenterScreen()
552 mrealwidget->slotRecenterScreen();
555 void KigView::slotInternalRecenterScreen()
557 mrealwidget->recenterScreen();
560 void KigWidget::zoomArea()
562 // mpart->emitStatusBarText( i18n( "Select the area that should be shown." ) );
563 ZoomArea* za = new ZoomArea( this, mpart->document() );
564 Rect oldrect = showingRect();
565 Coordinate tc = oldrect.topLeft();
566 za->setCoord0( tc );
567 tc = oldrect.bottomRight();
568 za->setCoord1( tc );
569 if ( za->exec() )
571 Coordinate c1 = za->coord0();
572 Coordinate c2 = za->coord1();
573 Coordinate nc1( c2.y, c1.x );
574 Coordinate nc2( c1.y, c2.x );
575 Rect nr( nc1, nc2 );
576 KigCommand* cd = new KigCommand( *mpart, i18n( "Change Shown Part of Screen" ) );
578 cd->addTask( new KigViewShownRectChangeTask( *this, nr ) );
579 mpart->history()->addCommand( cd );
581 delete za;
583 mpart->redrawScreen( this );
584 updateScrollBars();
587 void KigView::zoomArea()
589 mrealwidget->zoomArea();