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
22 #include "kig_view.moc"
25 #include "kig_document.h"
26 #include "kig_commands.h"
27 #include "../misc/coordinate_system.h"
28 #include "../misc/kigpainter.h"
30 #include "../modes/mode.h"
31 #include "../modes/dragrectmode.h"
34 #include <qwhatsthis.h>
36 #include <qscrollbar.h>
41 #include <kapplication.h>
42 #include <kstdaction.h>
44 #include <kiconloader.h>
49 kdbgstream
& operator<< ( kdbgstream
& s
, const QPoint
& t
)
51 s
<< "x: " << t
.x() << " y: " << t
.y();
55 KigWidget::KigWidget( KigPart
* part
,
60 : QWidget( parent
, name
,
61 fullscreen
? WStyle_Customize
| WStyle_NoBorder
: 0 ),
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
*)
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
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() );
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
169 if ( nsize
.width() / osize
.width() > 4 ) recenterScreen();
171 mpart
->redrawScreen( this );
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();
205 new KigCommand( *mpart
,
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();
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 ;-)
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
);
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...
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
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
,
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
);
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
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();
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();
421 sr
.setBottomLeft( bl
);
422 msi
.setShownRect( sr
);
423 mpart
->redrawScreen( this );
426 const ScreenInfo
& KigView::screenInfo() const
428 return mrealwidget
->screenInfo();
435 KigWidget
* KigView::realWidget() const
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
);
457 mview
->scrollHorizontal( delta
);
460 void KigView::scrollHorizontal( int delta
)
463 for ( int i
= 0; i
< delta
; i
+= 120 )
464 mbottomscroll
->subtractLine();
466 for ( int i
= 0; i
>= delta
; i
-= 120 )
467 mbottomscroll
->addLine();
470 void KigView::scrollVertical( int delta
)
473 for ( int i
= 0; i
< delta
; i
+= 120 )
474 mrightscroll
->subtractLine();
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();
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();
512 topLevelWidget()->showNormal();
515 void KigWidget::setFullScreen( bool 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() )
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 );
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();
567 tc
= oldrect
.bottomRight();
571 Coordinate c1
= za
->coord0();
572 Coordinate c2
= za
->coord1();
573 Coordinate
nc1( c2
.y
, c1
.x
);
574 Coordinate
nc2( c1
.y
, c2
.x
);
576 KigCommand
* cd
= new KigCommand( *mpart
, i18n( "Change Shown Part of Screen" ) );
578 cd
->addTask( new KigViewShownRectChangeTask( *this, nr
) );
579 mpart
->history()->addCommand( cd
);
583 mpart
->redrawScreen( this );
587 void KigView::zoomArea()
589 mrealwidget
->zoomArea();