/* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KoViewPrivate { public: KoViewPrivate() { m_inOperation = false; m_zoom = 1.0; m_children.setAutoDelete( true ); m_manager = 0L; m_tempActiveWidget = 0L; m_dcopObject = 0; m_registered=false; m_documentDeleted=false; } ~KoViewPrivate() { } TQGuardedPtr m_doc; // our KoDocument TQGuardedPtr m_manager; double m_zoom; TQPtrList m_children; TQWidget *m_tempActiveWidget; KoViewIface *m_dcopObject; bool m_registered; // are we registered at the part manager? bool m_documentDeleted; // true when m_doc gets deleted [can't use m_doc==0 // since this only happens in ~TQObject, and views // get deleted by ~KoDocument]. TQTimer *m_scrollTimer; // Hmm sorry for polluting the private class with such a big inner class. // At the beginning it was a little struct :) class StatusBarItem { public: StatusBarItem() // for TQValueList : m_widget(0), m_visible(false) {} StatusBarItem( TQWidget * widget, int stretch, bool permanent ) : m_widget(widget), m_stretch(stretch), m_permanent(permanent), m_visible(false) {} TQWidget * widget() const { return m_widget; } void ensureItemShown( KStatusBar * sb ) { if ( !m_visible ) { sb->addWidget( m_widget, m_stretch, m_permanent ); m_visible = true; m_widget->show(); } } void ensureItemHidden( KStatusBar * sb ) { if ( m_visible ) { sb->removeWidget( m_widget ); m_visible = false; m_widget->hide(); } } private: TQWidget * m_widget; int m_stretch; bool m_permanent; bool m_visible; // true when the item has been added to the statusbar }; TQValueList m_statusBarItems; // Our statusbar items bool m_inOperation; //in the middle of an operation (no screen refreshing)? }; KoView::KoView( KoDocument *document, TQWidget *parent, const char *name ) : TQWidget( parent, name ) { Q_ASSERT( document ); //kdDebug(30003) << "KoView::KoView " << this << endl; d = new KoViewPrivate; d->m_doc = document; KParts::PartBase::setPartObject( TQT_TQOBJECT(this) ); setFocusPolicy( TQ_StrongFocus ); setMouseTracking( true ); connect( d->m_doc, TQT_SIGNAL( childChanged( KoDocumentChild * ) ), this, TQT_SLOT( slotChildChanged( KoDocumentChild * ) ) ); connect( d->m_doc, TQT_SIGNAL( sigBeginOperation() ), this, TQT_SLOT( beginOperation() ) ); connect( d->m_doc, TQT_SIGNAL( sigEndOperation() ), this, TQT_SLOT( endOperation() ) ); actionCollection()->setWidget( this ); setupGlobalActions(); KActionCollection *coll = actionCollection(); /**** not needed anymore, according to David (Werner) TQValueList docActions = document->actionCollection()->actions(); TQValueList::ConstIterator it = docActions.begin(); TQValueList::ConstIterator end = docActions.end(); for (; it != end; ++it ) coll->insert( *it ); */ KStatusBar * sb = statusBar(); if ( sb ) // No statusbar in e.g. konqueror { coll->setHighlightingEnabled( true ); connect( coll, TQT_SIGNAL( actionStatusText( const TQString & ) ), this, TQT_SLOT( slotActionStatusText( const TQString & ) ) ); connect( coll, TQT_SIGNAL( clearStatusText() ), this, TQT_SLOT( slotClearStatusText() ) ); connect( d->m_doc, TQT_SIGNAL( sigStatusBarMessage( const TQString& ) ), this, TQT_SLOT( slotActionStatusText( const TQString& ) ) ); connect( d->m_doc, TQT_SIGNAL( sigClearStatusBarMessage() ), this, TQT_SLOT( slotClearStatusText() ) ); } d->m_doc->setCurrent(); d->m_scrollTimer = new TQTimer( this ); connect (d->m_scrollTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotAutoScroll() ) ); } KoView::~KoView() { kdDebug(30003) << "KoView::~KoView " << this << endl; delete d->m_scrollTimer; delete d->m_dcopObject; if (!d->m_documentDeleted) { if ( koDocument() && !koDocument()->isSingleViewMode() ) { if ( d->m_manager && d->m_registered ) // if we aren't registered we mustn't unregister :) d->m_manager->removePart( koDocument() ); d->m_doc->removeView(this); d->m_doc->setCurrent( false ); } } delete d; } KoDocument *KoView::koDocument() const { return d->m_doc; } void KoView::setDocumentDeleted() { d->m_documentDeleted = true; } bool KoView::documentDeleted() const { return d->m_documentDeleted; } bool KoView::hasDocumentInWindow( KoDocument *doc ) { return child( doc ) != 0L; } void KoView::setPartManager( KParts::PartManager *manager ) { d->m_manager = manager; if ( !koDocument()->isSingleViewMode() && manager->parts()->containsRef( koDocument() ) == 0 ) // is there another view registered? { d->m_registered = true; // no, so we have to register now and ungregister again in the DTOR manager->addPart( koDocument(), false ); } else d->m_registered = false; // There is already another view registered for that part... } KParts::PartManager *KoView::partManager() const { return d->m_manager; } KAction *KoView::action( const TQDomElement &element ) const { static const TQString &attrName = TDEGlobal::staticQString( "name" ); TQString name = element.attribute( attrName ); KAction *act = KXMLGUIClient::action( name.utf8() ); if ( !act ) act = d->m_doc->KXMLGUIClient::action( name.utf8() ); return act; } KoDocument *KoView::hitTest( const TQPoint &viewPos ) { KoViewChild *viewChild; TQPoint pos = reverseViewTransformations( viewPos ); KoDocumentChild *docChild = selectedChild(); if ( docChild ) { if ( ( viewChild = child( docChild->document() ) ) ) { if ( viewChild->frameRegion().contains( pos ) ) return 0; } else if ( docChild->frameRegion().contains( pos ) ) return 0; } docChild = activeChild(); if ( docChild ) { if ( ( viewChild = child( docChild->document() ) ) ) { if ( viewChild->frameRegion().contains( pos ) ) return 0; } else if ( docChild->frameRegion().contains( pos ) ) return 0; } return koDocument()->hitTest( pos ); } int KoView::leftBorder() const { return 0; } int KoView::rightBorder() const { return 0; } int KoView::topBorder() const { return 0; } int KoView::bottomBorder() const { return 0; } void KoView::setZoom( double zoom ) { d->m_zoom = zoom; update(); } double KoView::zoom() const { return d->m_zoom; } TQWidget *KoView::canvas() const { //dfaure: since the view plays two roles in this method (the const means "you can modify the canvas // but not the view", it's just coincidence that the view is the canvas by default ;) return const_cast(this); } int KoView::canvasXOffset() const { return 0; } int KoView::canvasYOffset() const { return 0; } void KoView::canvasAddChild( KoViewChild * ) { } void KoView::customEvent( TQCustomEvent *ev ) { if ( KParts::PartActivateEvent::test( ev ) ) partActivateEvent( (KParts::PartActivateEvent *)ev ); else if ( KParts::PartSelectEvent::test( ev ) ) partSelectEvent( (KParts::PartSelectEvent *)ev ); else if( KParts::GUIActivateEvent::test( ev ) ) guiActivateEvent( (KParts::GUIActivateEvent*)ev ); } void KoView::partActivateEvent( KParts::PartActivateEvent *event ) { if ( event->part() != (KParts::Part *)koDocument() ) { assert( event->part()->inherits( "KoDocument" ) ); KoDocumentChild *child = koDocument()->child( (KoDocument *)event->part() ); if ( child && event->activated() ) { if ( child->isRectangle() && !child->isTransparent() ) { KoViewChild *viewChild = new KoViewChild( child, this ); d->m_children.append( viewChild ); TQApplication::setOverrideCursor(waitCursor); // This is the long operation (due to toolbar layout stuff) d->m_manager->setActivePart( child->document(), viewChild->frame()->view() ); TQApplication::restoreOverrideCursor(); // Now we can move the frame to the right place viewChild->setInitialFrameGeometry(); } else { emit regionInvalidated( child->frameRegion( matrix() ), true ); } emit childActivated( child ); } else if ( child ) { emit regionInvalidated( child->frameRegion( matrix() ), true ); emit childDeactivated( child ); } else emit invalidated(); } else emit activated( event->activated() ); } void KoView::partSelectEvent( KParts::PartSelectEvent *event ) { if ( event->part() != (KParts::Part *)koDocument() ) { assert( event->part()->inherits( "KoDocument" ) ); KoDocumentChild *child = koDocument()->child( (KoDocument *)event->part() ); if ( child && event->selected() ) { TQRegion r = child->frameRegion( matrix() ); r.translate( - canvasXOffset(), - canvasYOffset() ); emit regionInvalidated( r, true ); emit childSelected( child ); } else if ( child ) { TQRegion r = child->frameRegion( matrix() ); r.translate( - canvasXOffset(), - canvasYOffset() ); emit regionInvalidated( r, true ); emit childUnselected( child ); } else emit invalidated(); } else emit selected( event->selected() ); } void KoView::guiActivateEvent( KParts::GUIActivateEvent * ev ) { showAllStatusBarItems( ev->activated() ); } void KoView::showAllStatusBarItems( bool show ) { KStatusBar * sb = statusBar(); if ( !sb ) return; TQValueListIterator it = d->m_statusBarItems.begin(); for ( ; it != d->m_statusBarItems.end() ; ++it ) if ( show ) (*it).ensureItemShown( sb ); else (*it).ensureItemHidden( sb ); } void KoView::addStatusBarItem( TQWidget * widget, int stretch, bool permanent ) { KoViewPrivate::StatusBarItem item( widget, stretch, permanent ); d->m_statusBarItems.append(item); TQValueListIterator it = d->m_statusBarItems.fromLast(); KStatusBar * sb = statusBar(); Q_ASSERT(sb); if (sb) (*it).ensureItemShown( sb ); } void KoView::removeStatusBarItem( TQWidget * widget ) { KStatusBar * sb = statusBar(); TQValueListIterator it = d->m_statusBarItems.begin(); for ( ; it != d->m_statusBarItems.end() ; ++it ) if ( (*it).widget() == widget ) { if ( sb ) (*it).ensureItemHidden( sb ); d->m_statusBarItems.remove( it ); break; } if ( it == d->m_statusBarItems.end() ) kdWarning() << "KoView::removeStatusBarItem. Widget not found : " << widget << endl; } KoDocumentChild *KoView::selectedChild() { if ( !d->m_manager ) return 0L; KParts::Part *selectedPart = d->m_manager->selectedPart(); if ( !selectedPart || !selectedPart->inherits( "KoDocument" ) ) return 0L; return koDocument()->child( (KoDocument *)selectedPart ); } KoDocumentChild *KoView::activeChild() { if ( !d->m_manager ) return 0L; KParts::Part *activePart = d->m_manager->activePart(); if ( !activePart || !activePart->inherits( "KoDocument" ) ) return 0L; return koDocument()->child( (KoDocument *)activePart ); } void KoView::enableAutoScroll( ) { d->m_scrollTimer->start( 50 ); } void KoView::disableAutoScroll( ) { d->m_scrollTimer->stop(); } void KoView::paintEverything( TQPainter &painter, const TQRect &rect, bool transparent ) { koDocument()->paintEverything( painter, rect, transparent, this ); } KoViewChild *KoView::child( KoView *view ) { TQPtrListIterator it( d->m_children ); for (; it.current(); ++it ) if ( it.current()->frame()->view() == view ) return it.current(); return 0L; } KoViewChild *KoView::child( KoDocument *doc ) { TQPtrListIterator it( d->m_children ); for (; it.current(); ++it ) if ( it.current()->documentChild()->document() == doc ) return it.current(); return 0L; } TQWMatrix KoView::matrix() const { TQWMatrix m; m.scale( zoom(), zoom() ); //m.translate( canvasXOffset() , canvasYOffset() ); return m; } void KoView::slotChildActivated( bool a ) { // Only interested in deactivate events if ( a ) return; KoViewChild* ch = child( (KoView*)sender() ); if ( !ch ) return; KoView* view = ch->frame()->view(); TQWidget *activeWidget = view->d->m_tempActiveWidget; if ( d->m_manager->activeWidget() ) activeWidget = d->m_manager->activeWidget(); if ( !activeWidget || !activeWidget->inherits( "KoView" ) ) return; // Is the new active view a child of this one ? // In this case we may not delete! // TQObject *n = d->m_manager->activeWidget(); TQObject *n = TQT_TQOBJECT(activeWidget); while( n ) if ( n == (TQObject *)view ) return; else n = n->parent(); d->m_tempActiveWidget = activeWidget; TQApplication::setOverrideCursor(waitCursor); d->m_manager->setActivePart( 0L ); TQGuardedPtr docChild = ch->documentChild(); TQGuardedPtr chFrame = ch->frame(); if ( docChild && chFrame && chFrame->view() ) { docChild->setContentsPos( chFrame->view()->canvasXOffset(), chFrame->view()->canvasYOffset() ); docChild->document()->setViewBuildDocument( chFrame->view(), chFrame->view()->xmlguiBuildDocument() ); } d->m_children.remove( ch ); d->m_manager->addPart( docChild->document(), false ); // the destruction of the view removed the part from the partmanager. re-add it :) TQApplication::restoreOverrideCursor(); // #### HACK // We want to delete as many views as possible and this // trick is used to go upwards in the view-tree. emit activated( FALSE ); } void KoView::slotChildChanged( KoDocumentChild *child ) { TQRegion region( child->oldPointArray( matrix() ) ); emit regionInvalidated( child->frameRegion( matrix(), true ).unite( region ), true ); } int KoView::autoScrollAcceleration( int offset ) const { if(offset < 40) return offset; else return offset*offset/40; } void KoView::slotAutoScroll( ) { TQPoint scrollDistance; bool actuallyDoScroll = false; TQPoint pos( mapFromGlobal( TQCursor::pos() ) ); //Provide progressive scrolling depending on the mouse position if ( pos.y() < topBorder() ) { scrollDistance.setY ((int) - autoScrollAcceleration( - pos.y() + topBorder() )); actuallyDoScroll = true; } else if ( pos.y() > height() - bottomBorder() ) { scrollDistance.setY ((int) autoScrollAcceleration(pos.y() - height() + bottomBorder() )); actuallyDoScroll = true; } if ( pos.x() < leftBorder() ) { scrollDistance.setX ((int) - autoScrollAcceleration( - pos.x() + leftBorder() )); actuallyDoScroll = true; } else if ( pos.x() > width() - rightBorder() ) { scrollDistance.setX ((int) autoScrollAcceleration( pos.x() - width() + rightBorder() )); actuallyDoScroll = true; } if ( actuallyDoScroll ) { int state=0; #if KDE_IS_VERSION(3,4,0) state = kapp->keyboardMouseState(); #endif pos = canvas()->mapFrom(this, pos); TQMouseEvent * event = new TQMouseEvent(TQEvent::MouseMove, pos, 0, state); TQApplication::postEvent( canvas(), event ); emit autoScroll( scrollDistance ); } } void KoView::setupGlobalActions() { actionNewView = new KAction( i18n( "&New View" ), "window_new", 0, TQT_TQOBJECT(this), TQT_SLOT( newView() ), actionCollection(), "view_newview" ); } void KoView::setupPrinter( KPrinter & ) { kdDebug() << "KoView::setupPrinter not implemented by the application!" << endl; } void KoView::print( KPrinter & ) { kdDebug() << "KoView::print not implemented by the application!" << endl; } void KoView::newView() { assert( ( d!=0L && d->m_doc ) ); KoDocument *thisDocument = d->m_doc; KoMainWindow *shell = new KoMainWindow( thisDocument->instance() ); shell->setRootDocument(thisDocument); shell->show(); } bool KoView::isInOperation() const { return d->m_inOperation; } void KoView::beginOperation() { d->m_inOperation = true; canvas()->setUpdatesEnabled(FALSE); } void KoView::endOperation() { canvas()->setUpdatesEnabled(TRUE); d->m_inOperation = false; // canvas()->update(); } KoMainWindow * KoView::shell() const { return dynamic_cast( topLevelWidget() ); } KMainWindow * KoView::mainWindow() const { return dynamic_cast( topLevelWidget() ); } KStatusBar * KoView::statusBar() const { KoMainWindow *mw = shell(); return mw ? mw->statusBar() : 0L; } void KoView::slotActionStatusText( const TQString &text ) { KStatusBar *sb = statusBar(); if ( sb ) sb->message( text ); } void KoView::slotClearStatusText() { KStatusBar *sb = statusBar(); if ( sb ) sb->clear(); } DCOPObject *KoView::dcopObject() { if ( !d->m_dcopObject ) d->m_dcopObject = new KoViewIface( this ); return d->m_dcopObject; } class KoViewChild::KoViewChildPrivate { public: KoViewChildPrivate() { } ~KoViewChildPrivate() { } }; KoViewChild::KoViewChild( KoDocumentChild *child, KoView *_parentView ) { d = new KoViewChildPrivate; m_parentView = _parentView; m_child = child; m_frame = new KoFrame( parentView()->canvas() ); KoView *view = child->document()->createView( m_frame ); view->setXMLGUIBuildDocument( child->document()->viewBuildDocument( view ) ); view->setPartManager( parentView()->partManager() ); // hack? (Werner) view->setZoom( parentView()->zoom() * TQMAX(child->xScaling(), child->yScaling()) ); m_frame->setView( view ); m_frame->show(); m_frame->raise(); parentView()->canvasAddChild( this ); /* KoViewChild has basically three geometries to keep in sync. - The KoDocumentChild geometry (i.e. the embedded object's geometry, unzoomed) - Its own geometry (used for hit-test etc.) - The KoFrame geometry (the graphical widget for moving the object when active) So we need to subtract the scrollview's offset for the frame geometry, since it's a widget. The rules are (R1) frameGeometry = viewGeometry(childGeometry) "+" m_frame->{left|right|top|bottom}Border() - scrollview offset, (R2) frameGeometry = myGeometry "+" active_frame_border - scrollview offset. So: (R3, unused) myGeometry = viewGeometry(childGeometry) "+" m_frame->{left|right|top|bottom}Border() "-" active_frame_border Notes: active_frame_border is m_frame->border() (0 when inactive, 5 when active). {left|right|top|bottom}Border are the borders used in kspread (0 when inactive, big when active). "+" border means we add a border, so it's a subtraction on x, y and an addition on width, height. viewGeometry() applies the zoom as well as any other translation the app might want to do */ // Setting the frameGeometry is done in setInitialFrameGeometry, which is // also called right after activation. connect( view, TQT_SIGNAL( activated( bool ) ), parentView(), TQT_SLOT( slotChildActivated( bool ) ) ); } KoViewChild::~KoViewChild() { if ( m_frame ) { slotFrameGeometryChanged(); delete static_cast( m_frame ); } delete d; } void KoViewChild::slotFrameGeometryChanged() { // Set our geometry from the frame geometry (R2 reversed) TQRect geom = m_frame->geometry(); int b = m_frame->border(); TQRect borderRect( geom.x() + b + parentView()->canvasXOffset(), geom.y() + b + parentView()->canvasYOffset(), geom.width() - b * 2, geom.height() - b * 2 ); setGeometry( borderRect ); if(m_child) { // Set the child geometry from the frame geometry (R1 reversed) TQRect borderLessRect( geom.x() + m_frame->leftBorder() + parentView()->canvasXOffset(), geom.y() + m_frame->topBorder() + parentView()->canvasYOffset(), geom.width() - m_frame->leftBorder() - m_frame->rightBorder(), geom.height() - m_frame->topBorder() - m_frame->bottomBorder() ); // We don't want to trigger slotDocGeometryChanged again lock(); TQRect childGeom = parentView()->reverseViewTransformations( borderLessRect ); kdDebug() << "KoChild::slotFrameGeometryChanged child geometry " << ( geometry() == childGeom ? "already " : "set to " ) << childGeom << endl; m_child->setGeometry( childGeom ); unlock(); } } void KoViewChild::slotDocGeometryChanged() { if ( locked() ) return; // Set frame geometry from child geometry (R1) // The frame's resizeEvent will call slotFrameGeometryChanged. TQRect geom = parentView()->applyViewTransformations( m_child->geometry() ); TQRect borderRect( geom.x() - m_frame->leftBorder() - parentView()->canvasXOffset(), geom.y() - m_frame->topBorder() - parentView()->canvasYOffset(), geom.width() + m_frame->leftBorder() + m_frame->rightBorder(), geom.height() + m_frame->topBorder() + m_frame->bottomBorder() ); kdDebug() << "KoViewChild::slotDocGeometryChanged frame geometry " << ( m_frame->geometry() == borderRect ? "already " : "set to " ) << borderRect << endl; m_frame->setGeometry( borderRect ); } TQPoint KoView::applyViewTransformations( const TQPoint& p ) const { return TQPoint( tqRound( p.x() * zoom() ), tqRound( p.y() * zoom() ) ); } TQPoint KoView::reverseViewTransformations( const TQPoint& v ) const { return TQPoint( tqRound( v.x() / zoom() ), tqRound( v.y() / zoom() ) ); } TQRect KoView::applyViewTransformations( const TQRect& r ) const { return TQRect( applyViewTransformations( r.topLeft() ), applyViewTransformations( r.bottomRight() ) ); } TQRect KoView::reverseViewTransformations( const TQRect& r ) const { return TQRect( reverseViewTransformations( r.topLeft() ), reverseViewTransformations( r.bottomRight() ) ); } void KoViewChild::setInitialFrameGeometry() { kdDebug() << k_funcinfo << endl; // Connect only now, so that the GUI building doesn't move us around. connect( m_frame, TQT_SIGNAL( geometryChanged() ), this, TQT_SLOT( slotFrameGeometryChanged() ) ); connect( m_child, TQT_SIGNAL( changed( KoChild * ) ), this, TQT_SLOT( slotDocGeometryChanged() ) ); // Set frameGeometry from childGeometry slotDocGeometryChanged(); // Set myGeometry from frameGeometry slotFrameGeometryChanged(); } #include "KoView.moc"