/* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Reginald Stadlbauer Copyright (C) 2005 Thomas Zander 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 "KWFrameSet.h" #include "KWDocument.h" #include "KWViewMode.h" #include "KWCommand.h" #include "KWFrame.h" #include "KWTextFrameSet.h" #include "KWTableFrameSet.h" #include "KWAnchor.h" #include "KWordFrameSetIface.h" #include "KWFrameList.h" #include "KWPageManager.h" #include "KWPage.h" #include "KWFrameViewManager.h" #include "KWFrameView.h" #include #include #include #include #include //#define DEBUG_DRAW KWFrameSet::KWFrameSet( KWDocument *doc ) : m_doc( doc ), m_frames(), m_framesInPage(), m_firstPage( 0 ), m_emptyList(), m_info( FI_BODY ), m_groupmanager( 0L ), m_visible( true ), m_protectSize( false ), m_anchorTextFs( 0L ), m_dcop( 0L ), m_pageManager( 0 ) { // Send our "tqrepaintChanged" signals to the document. setName("KWFrameSet"); if(m_doc) { connect( this, TQT_SIGNAL( tqrepaintChanged( KWFrameSet * ) ), doc, TQT_SLOT( slotRepaintChanged( KWFrameSet * ) ) ); m_pageManager = doc->pageManager(); } m_frames.setAutoDelete( true ); m_framesInPage.setAutoDelete( true ); // autodelete the lists in the array (not the frames;) } KWordFrameSetIface* KWFrameSet::dcopObject() { if ( !m_dcop ) m_dcop = new KWordFrameSetIface( this ); return m_dcop; } KWFrameSet::~KWFrameSet() { delete m_dcop; } void KWFrameSet::addFrame( KWFrame *frame, bool recalc ) { if ( m_frames.tqfindRef( frame ) != -1 ) return; //kdDebug(32001) << k_funcinfo << name() << " adding frame" << frame << " recalc=" << recalc << endl; if(m_doc) KWFrameList::createFrameList(frame, m_doc); frame->setFrameSet(this); m_frames.append( frame ); if(recalc) updateFrames(); emit sigFrameAdded(frame); } void KWFrameSet::deleteFrame( unsigned int num, bool remove, bool recalc ) { //kdDebug(32001) << k_funcinfo << name() << " deleting frame" << num << " remove=" << remove << " recalc=" << recalc << endl; //kdBacktrace(); KWFrame *frm = m_frames.at( num ); Q_ASSERT( frm ); m_frames.take( num ); Q_ASSERT( !m_frames.tqcontains(frm) ); unsigned int index = frm->pageNumber() - m_firstPage; if(m_framesInPage.count() >= index) { TQPtrList *lst = m_framesInPage.at(index); lst->remove(frm); } KWFrameList *stack = frm->frameStack(); if( stack ) { stack->update(); // will update the other frames on the page. frm->setFrameStack(0); delete stack; } emit sigFrameRemoved(frm); if ( !remove ) frm->setFrameSet(0L); else { // ###### should something similar be done when just removing a frame from the list? frameDeleted( frm, recalc ); // inform kwtableframeset if necessary delete frm; //kdDebug(32001) << k_funcinfo << frm << " deleted. Now I have " << m_frames.count() << " m_frames" << endl; } if ( recalc ) updateFrames(); } void KWFrameSet::deleteFrame( KWFrame *frm, bool remove, bool recalc ) { //kdDebug(32001) << "KWFrameSet::deleteFrame " << frm << " remove=" << remove << endl; int num = m_frames.tqfindRef( frm ); Q_ASSERT( num != -1 ); if ( num == -1 ) return; deleteFrame( num, remove, recalc ); } void KWFrameSet::deleteAllFrames() { if ( !m_frames.isEmpty() ) { for ( TQPtrListIterator frameIt( m_frames ); frameIt.current(); ++frameIt ) emit sigFrameRemoved( *frameIt ); m_frames.clear(); updateFrames(); } } void KWFrameSet::deleteAllCopies() { if ( m_frames.count() > 1 ) { KWFrame * firstFrame = m_frames.take(0); deleteAllFrames(); m_frames.append( firstFrame ); updateFrames(); } } void KWFrameSet::createEmptyRegion( const TQRect & crect, TQRegion & emptyRegion, KWViewMode *viewMode ) { KWPage *page = m_doc->pageManager()->page( frame(0) ); if( !page ) { kdWarning(31001) << "The first frame of '" << name() << "' is outside all pages!!" << endl; return; } double paperHeight = page->height(); //kdDebug(32001) << "KWFrameSet::createEmptyRegion " << name() << endl; for (TQPtrListIterator frameIt = frameIterator(); frameIt.current(); ++frameIt ) { if ( !frameIt.current()->isTransparent() ) { TQRect outerRect( viewMode->normalToView( frameIt.current()->outerRect(viewMode) ) ); //kdDebug(32001) << "KWFrameSet::createEmptyRegion outerRect=" << outerRect << " crect=" << crect << endl; outerRect &= crect; // This is important, to avoid calling subtract with a Y difference > 65536 if ( !outerRect.isEmpty() ) { emptyRegion = emptyRegion.subtract( outerRect ); //kdDebug(32001) << "KWFrameSet::createEmptyRegion emptyRegion now: " << endl; DEBUGREGION( emptyRegion ); } if ( crect.bottom() + paperHeight < outerRect.top() ) return; // Ok, we're far below the crect, abort. } } } void KWFrameSet::drawPadding( KWFrame *frame, TQPainter *p, const TQRect &crect, const TQColorGroup &, KWViewMode *viewMode ) { TQRect outerRect( viewMode->normalToView( frame->outerRect(viewMode) ) ); //kdDebug(32001) << "KWFrameSet::drawPadding frame: " << frameFromPtr( frame ) // << " outerRect: " << outerRect // << " crect: " << crect << endl; if ( !crect.intersects( outerRect ) ) { #ifdef DEBUG_DRAW kdDebug(32001) << "KWFrameSet::drawPadding no intersection with " << crect << endl; #endif return; } TQRect frameRect( viewMode->normalToView( m_doc->zoomRect( *frame ) ) ); p->save(); TQBrush bgBrush( frame->backgroundColor() ); bgBrush.setColor( KWDocument::resolveBgColor( bgBrush.color(), p ) ); p->setBrush( bgBrush ); int leftMargin = m_doc->zoomItX(frame->paddingLeft()); int topMargin = m_doc->zoomItY(frame->paddingTop()); int rightMargin = m_doc->zoomItX(frame->paddingRight()); int bottomMargin = m_doc->zoomItY(frame->paddingBottom()); //kdDebug(32001) << "KWFrameSet::drawPadding leftMargin=" << leftMargin << " topMargin=" << topMargin << " rightMargin=" << rightMargin << " bottomMargin=" << bottomMargin << endl; if ( topMargin != 0 ) { TQRect r( frameRect.left(), frameRect.top(), frameRect.width(), topMargin ); p->fillRect( r, bgBrush ); } if ( leftMargin != 0 ) { TQRect r( frameRect.left(), frameRect.top(), leftMargin, frameRect.height() ); p->fillRect( r, bgBrush ); } if ( rightMargin != 0 ) { TQRect r( frameRect.right()-rightMargin, frameRect.top(), rightMargin, frameRect.height() ); p->fillRect( r, bgBrush ); } if ( bottomMargin != 0 ) { TQRect r( frameRect.left(), frameRect.bottom()-bottomMargin, frameRect.width(), bottomMargin ); p->fillRect( r, bgBrush ); } p->restore(); } void KWFrameSet::drawFrameBorder( TQPainter *painter, KWFrame *frame, KWFrame *settingsFrame, const TQRect &crect, KWViewMode *viewMode ) { TQRect outerRect( viewMode->normalToView( frame->outerRect( viewMode ) ) ); //kdDebug(32001) << "KWFrameSet::drawFrameBorder frame: " << frameFromPtr( frame ) // << " outerRect: " << outerRect << endl; if ( !crect.intersects( outerRect ) ) { //kdDebug(32001) << "KWFrameSet::drawFrameBorder no intersection with " << crect << endl; return; } TQRect frameRect( viewMode->normalToView( m_doc->zoomRect( *frame ) ) ); painter->save(); TQBrush bgBrush( settingsFrame->backgroundColor() ); //bool defaultColor = !bgBrush.color().isValid(); bgBrush.setColor( KWDocument::resolveBgColor( bgBrush.color(), painter ) ); painter->setBrush( bgBrush ); // Draw default borders using view settings... TQPen viewSetting( TQApplication::palette().color( TQPalette::Active, TQColorGroup::Mid ) ); int minBorder = 1; // ...except when printing, or embedded doc, or disabled. if ( !viewMode || !viewMode->drawFrameBorders() ) { viewSetting = TQPen(Qt::NoPen); minBorder = 0; } // Draw borders either as the user defined them, or using the view settings. // Borders should be drawn _outside_ of the frame area // otherwise the frames will erase the border when painting themselves. KoBorder::drawBorders( *painter, m_doc, frameRect, settingsFrame->leftBorder(), settingsFrame->rightBorder(), settingsFrame->topBorder(), settingsFrame->bottomBorder(), minBorder, viewSetting ); painter->restore(); } void KWFrameSet::setFloating() { // Find main text frame TQPtrListIterator fit = m_doc->framesetsIterator(); for ( ; fit.current() ; ++fit ) { KWTextFrameSet * frameSet = dynamic_cast( fit.current() ); if ( !frameSet || frameSet->frameSetInfo() != FI_BODY ) continue; KoTextParag* parag = 0L; int index = 0; KoPoint dPoint( m_frames.first()->topLeft() ); kdDebug(32001) << "KWFrameSet::setFloating looking for pos at " << dPoint.x() << " " << dPoint.y() << endl; frameSet->findPosition( dPoint, parag, index ); // Create anchor. TODO: refcount the anchors! setAnchored( frameSet, parag, index ); frameSet->tqlayout(); m_doc->frameChanged( m_frames.first() ); return; } } void KWFrameSet::setProtectSize( bool b) { m_protectSize = b; } void KWFrameSet::setAnchored( KWTextFrameSet* textfs, int paragId, int index, bool placeHolderExists /* = false */, bool tqrepaint ) { KWTextParag * parag = static_cast( textfs->textDocument()->paragAt( paragId ) ); Q_ASSERT( parag ); if ( parag ) setAnchored( textfs, parag, index, placeHolderExists, tqrepaint ); } void KWFrameSet::setAnchored( KWTextFrameSet* textfs, KoTextParag* parag, int index, bool placeHolderExists /* = false */, bool tqrepaint ) { kdDebug(32001) << "KWFrameSet::setAnchored " << textfs << " " << parag->paragId() << " " << index << " " << placeHolderExists << endl; Q_ASSERT( textfs ); Q_ASSERT( parag ); if ( isFloating() ) deleteAnchors(); m_anchorTextFs = textfs; KWFrameList::createFrameList(textfs, m_doc); // remove ourselves from others list now we are inline if ( parag ) createAnchors( parag, index, placeHolderExists, tqrepaint ); if ( !placeHolderExists ) // i.e. not while loading { m_doc->updateAllFrames(); // We just became floating, so we need to be removed from "frames on top/below". // TODO pass page number to updateAllFrames - hmm, we could have several frames in theory } } void KWFrameSet::setAnchored( KWTextFrameSet* textfs ) { m_anchorTextFs = textfs; m_doc->updateAllFrames(); // We just became floating, so we need to be removed from "frames on top/below". // TODO pass page number - hmm, we could have several frames in theory } // Find where our anchor is ( if we are anchored ). // We can't store a pointers to anchors, because over time we might change anchors // (Especially, undo/redo of insert/delete can reuse an old anchor and forget a newer one etc.) KWAnchor * KWFrameSet::findAnchor( int frameNum ) { Q_ASSERT( m_anchorTextFs ); // Yes, a linear search, but only among all customitems of the correct textdoc, // whose number is assumed to be quite small. TQPtrListIterator cit( m_anchorTextFs->textDocument()->allCustomItems() ); for ( ; cit.current() ; ++cit ) { KWAnchor * anchor = dynamic_cast( cit.current() ); if ( anchor && !anchor->isDeleted() && anchor->frameSet() == this && anchor->frameNum() == frameNum ) return anchor; } kdWarning() << "KWFrameSet::findAnchor anchor not found (frameset='" << name() << "' frameNum=" << frameNum << ")" << endl; return 0L; } void KWFrameSet::setFixed() { kdDebug(32001) << "KWFrameSet::setFixed" << endl; if ( isFloating() ) deleteAnchors(); m_anchorTextFs = 0L; // make sure the frames are on top // (their z-order didn't matter when they were inline) TQPtrListIterator frameIt = frameIterator(); for ( ; frameIt.current(); ++frameIt ) frameIt.current()->setZOrder( m_doc->maxZOrder( frameIt.current()->pageNumber(m_doc) ) + 1 ); m_doc->tqrepaintAllViews(); m_doc->updateRulerFrameStartEnd(); } KWAnchor * KWFrameSet::createAnchor( KoTextDocument *txt, int frameNum ) { KWAnchor * anchor = new KWAnchor( txt, this, frameNum ); return anchor; } void KWFrameSet::createAnchors( KoTextParag * parag, int index, bool placeHolderExists /*= false */ /*only used when loading*/, bool tqrepaint ) { kdDebug(32001) << "KWFrameSet::createAnchors" << endl; Q_ASSERT( m_anchorTextFs ); TQPtrListIterator frameIt = frameIterator(); for ( ; frameIt.current(); ++frameIt, ++index ) { //if ( ! frameIt.current()->anchor() ) { // Anchor this frame, after the previous one KWAnchor * anchor = createAnchor( m_anchorTextFs->textDocument(), frameFromPtr( frameIt.current() ) ); if ( !placeHolderExists ) parag->insert( index, KoTextObject::customItemChar() ); parag->setCustomItem( index, anchor, 0 ); } } parag->setChanged( true ); if ( tqrepaint ) emit tqrepaintChanged( m_anchorTextFs ); } void KWFrameSet::deleteAnchor( KWAnchor * anchor ) { // Simple deletion, no undo/redo KoTextCursor c( m_anchorTextFs->textDocument() ); c.setParag( anchor->paragraph() ); c.setIndex( anchor->index() ); anchor->setDeleted( true ); // this sets m_anchorTextFs to 0L static_cast(c.parag())->removeCustomItem(c.index()); c.remove(); // This deletes the character where the anchor was // We don't delete the anchor since it might be in a customitemmap in a text-insert command // TODO: refcount the anchors c.parag()->setChanged( true ); } void KWFrameSet::deleteAnchors() { kdDebug(32002) << "KWFrameSet::deleteAnchors" << endl; KWTextFrameSet * textfs = m_anchorTextFs; Q_ASSERT( textfs ); if ( !textfs ) return; //TQPtrListIterator frameIt = frameIterator(); int frameNum = 0; // At the moment there's only one anchor per frameset // With tables the loop below will be wrong anyway... //for ( ; frameIt.current(); ++frameIt, ++frameNum ) { /* if ( frameIt.current()->anchor() ) deleteAnchor( frameIt.current()->anchor() ); frameIt.current()->setAnchor( 0L ); */ KWAnchor * anchor = findAnchor( frameNum ); deleteAnchor( anchor ); } emit tqrepaintChanged( textfs ); } void KWFrameSet::moveFloatingFrame( int frameNum, const KoPoint &position ) { KWFrame * frame = m_frames.at( frameNum ); Q_ASSERT( frame ); if ( !frame ) return; KoPoint pos( position ); // position includes the border, we need to adjust accordingly pos.rx() += frame->leftBorder().width(); pos.ry() += frame->topBorder().width(); if ( frame->topLeft() != pos ) { kdDebug(32002) << "KWFrameSet::moveFloatingFrame " << pos.x() << "," << pos.y() << endl; int oldPageNum = frame->pageNumber(); frame->moveTopLeft( pos ); updateFrames(); if( frame->frameStack() ) frame->frameStack()->updateAfterMove( oldPageNum ); } tqinvalidate(); } KoRect KWFrameSet::floatingFrameRect( int frameNum ) { KWFrame * frame = m_frames.at( frameNum ); Q_ASSERT( frame ); Q_ASSERT( isFloating() ); KWAnchor* anchor = findAnchor( frameNum ); Q_ASSERT( anchor ); TQRect paragRect = anchor->paragraph()->rect(); int x = anchor->x() + paragRect.x(); // in LU int y = anchor->y() + paragRect.y(); // in LU KoPoint topLeft( m_doc->tqlayoutUnitToPixelX( x ), m_doc->tqlayoutUnitToPixelY( y ) ); return KoRect( topLeft, frame->outerKoRect().size() ); } KoSize KWFrameSet::floatingFrameSize( int frameNum ) { KWFrame * frame = m_frames.at( frameNum ); Q_ASSERT( frame ); return frame->outerKoRect().size(); } KCommand * KWFrameSet::anchoredObjectCreateCommand( int frameNum ) { KWFrame * frame = m_frames.at( frameNum ); Q_ASSERT( frame ); return new KWCreateFrameCommand( TQString(), frame ); } KCommand * KWFrameSet::anchoredObjectDeleteCommand( int frameNum ) { KWFrame * frame = m_frames.at( frameNum ); Q_ASSERT( frame ); return new KWDeleteFrameCommand( TQString(), frame ); } KWFrame * KWFrameSet::frameAtPos( double x, double y ) const { KoPoint docPoint( x, y ); TQPtrListIterator frameIt = frameIterator(); for ( ; frameIt.current(); ++frameIt ) if ( frameIt.current()->tqcontains( docPoint ) ) return frameIt.current(); return 0L; } KWFrame *KWFrameSet::frame( unsigned int num ) const { // TQPtrList sucks return const_cast( this )->m_frames.at( num ); } int KWFrameSet::frameFromPtr( KWFrame *frame ) { return m_frames.tqfindRef( frame ); } KWFrame * KWFrameSet::settingsFrame( const KWFrame* frame ) { if ( !frame->isCopy() ) return const_cast( frame ); KWFrame* lastRealFrame=0L; TQPtrListIterator frameIt( frame->frameSet()->frameIterator() ); for ( ; frameIt.current(); ++frameIt ) { KWFrame *curFrame = frameIt.current(); if ( curFrame == frame ) return lastRealFrame ? lastRealFrame : const_cast( frame ); if ( !lastRealFrame || !curFrame->isCopy() ) lastRealFrame = curFrame; } return const_cast( frame ); //fallback, should never happen } void KWFrameSet::updateFrames( int flags ) { if ( m_frames.isEmpty() ) return; // No frames. This happens when the frameset is deleted (still exists for undo/redo) // Not visible ? Don't bother then. if ( !isVisible() ) return; //kdDebug(32001) << "KWFrameSet::updateFrames " << this << " " << name() << endl; if ( flags & UpdateFramesInPage ) { // For each of our frames, clear old list of frames on top, and grab min/max page nums m_firstPage = m_frames.first()->pageNumber(); // we know m_frames is not empty here int lastPage = m_firstPage; TQPtrListIterator fIt( frameIterator() ); for ( ; fIt.current(); ++fIt ) { int pg = fIt.current()->pageNumber(); m_firstPage = KMIN( m_firstPage, pg ); lastPage = KMAX( lastPage, pg ); } //kdDebug(32001) << "firstPage=" << m_firstPage << " lastPage=" << lastPage << endl; // Prepare the m_framesInPage structure int oldSize = m_framesInPage.size(); m_framesInPage.resize( lastPage - m_firstPage + 1 ); // Clear the old elements int oldElements = KMIN( oldSize, (int)m_framesInPage.size() ); for ( int i = 0 ; i < oldElements ; ++i ) m_framesInPage[i]->clear(); // Initialize the new elements. for ( int i = oldElements ; i < (int)m_framesInPage.size() ; ++i ) m_framesInPage.insert( i, new TQPtrList() ); // Iterate over m_frames again, to fill the m_framesInPage array fIt.toFirst(); for ( ; fIt.current(); ++fIt ) { int pg = fIt.current()->pageNumber(); Q_ASSERT( pg <= lastPage ); m_framesInPage[pg - m_firstPage]->append( fIt.current() ); } } if ( isFloating() ) { //kdDebug(32001) << "KWFrameSet::updateFrames " << name() << " is floating" << endl; TQPtrListIterator frameIt = frameIterator(); int frameNum = 0; // At the moment there's only one anchor per frameset //for ( ; frameIt.current(); ++frameIt, ++frameNum ) { KWAnchor * anchor = findAnchor( frameNum ); //kdDebug(32001) << "KWFrameSet::updateFrames anchor=" << anchor << endl; if ( anchor ) anchor->resize(); } } } bool KWFrameSet::isPaintedBy( KWFrameSet* fs ) const { if ( fs == this ) return true; if ( isFloating() ) { KWFrameSet* tqparentFs = anchorFrameset(); if ( tqparentFs && tqparentFs->isPaintedBy( fs ) ) return true; } if ( groupmanager() ) { if ( groupmanager()->isPaintedBy( fs ) ) return true; } return false; } const TQPtrList & KWFrameSet::framesInPage( int pageNum ) const { if ( pageNum < m_firstPage || pageNum >= (int)m_framesInPage.size() + m_firstPage ) { #ifdef DEBUG_DTI kdWarning(32002) << name() << " framesInPage called for pageNum=" << pageNum << ". " << " Min value: " << m_firstPage << " Max value: " << m_framesInPage.size() + m_firstPage - 1 << endl; #endif return m_emptyList; // TQPtrList() doesn't work, it's a temporary } return * m_framesInPage[pageNum - m_firstPage]; } void KWFrameSet::drawContents( TQPainter *p, const TQRect & crect, const TQColorGroup &cg, bool onlyChanged, bool resetChanged, KWFrameSetEdit *edit, KWViewMode *viewMode, KWFrameViewManager *frameViewManager ) { #ifdef DEBUG_DRAW kdDebug(32001) << "\nKWFrameSet::drawContents " << this << " " << name() << " onlyChanged=" << onlyChanged << " resetChanged=" << resetChanged << " crect= " << crect << endl; #endif if ( !viewMode->isTextModeFrameset( this ) ) { TQPtrListIterator frameIt( frameIterator() ); KWFrame * lastRealFrame = 0L; //double lastRealFrameTop = 0; //double totalHeight = 0; // in pt, to avoid accumulating rounding errors for ( ; frameIt.current(); ) { KWFrame *frame = frameIt.current(); ++frameIt; // Point to the next one, to detect "last copy" // The settings come from this frame KWFrame * settingsFrame = ( frame->isCopy() && lastRealFrame ) ? lastRealFrame : frame; bool lastCopy = !frameIt.current() || !frameIt.current()->isCopy(); drawFrameAndBorders( frame, p, crect, cg, onlyChanged, // Only reset the changed flag in the last copy of a given frame (#60678) resetChanged && lastCopy, edit, viewMode, settingsFrame, true /*transparency & double-buffering*/ ); if(viewMode->drawSelections() && frameViewManager) { KWFrameView* view = frameViewManager->view(frame); if(view) view->paintFrameAttributes(p, crect, viewMode, m_doc); } if ( !lastRealFrame || !frame->isCopy() ) { lastRealFrame = frame; //lastRealFrameTop = totalHeight; } //totalHeight += frame->innerHeight(); } } else { // Text view mode TQRect normalRect = viewMode->viewToNormal(crect); drawFrame( 0L /*frame*/, p, normalRect, crect, TQPoint(KWViewModeText::OFFSET, 0), 0L /*settingsFrame*/, cg, onlyChanged, resetChanged, edit, viewMode, true ); } } void KWFrameSet::drawFrameAndBorders( KWFrame *frame, TQPainter *painter, const TQRect &crect, const TQColorGroup &cg, bool onlyChanged, bool resetChanged, KWFrameSetEdit *edit, KWViewMode *viewMode, KWFrame *settingsFrame, bool drawUnderlyingFrames ) { if ( !frame->isValid() ) { kdDebug(32002) << "KWFrameSet::drawFrameAndBorders " << name() << " frame " << frameFromPtr( frame ) << " " << frame << " isn't valid" << endl; return; } TQRect normalOuterFrameRect( frame->outerRect( viewMode ) ); TQRect outerFrameRect( viewMode->normalToView( normalOuterFrameRect ) ); TQRect outerCRect = crect.intersect( outerFrameRect ); #ifdef DEBUG_DRAW kdDebug(32001) << "KWFrameSet::drawFrameAndBorders " << name() << " frame " << frameFromPtr( frame ) << " " << *frame << endl; kdDebug(32001) << " (outer) normalFrameRect=" << normalOuterFrameRect << " frameRect=" << outerFrameRect << endl; kdDebug(32001) << " crect=" << crect << " intersec=" << outerCRect << " todraw=" << !outerCRect.isEmpty() << endl; #endif if ( !outerCRect.isEmpty() ) { // Determine settingsFrame if not passed (for speedup) if ( !settingsFrame ) settingsFrame = this->settingsFrame( frame ); TQRect normalInnerFrameRect( m_doc->zoomRect( frame->innerRect() ) ); TQRect innerFrameRect( viewMode->normalToView( normalInnerFrameRect ) ); // This translates the coordinates in the document contents // ( frame and r are up to here in this system ) // into the frame's own coordinate system. int offsetX = normalInnerFrameRect.left(); int offsetY = normalInnerFrameRect.top() - m_doc->zoomItY( frame->internalY() ); TQRect innerCRect = outerCRect.intersect( innerFrameRect ); if ( !innerCRect.isEmpty() ) { TQRect fcrect = viewMode->viewToNormal( innerCRect ); #ifdef DEBUG_DRAW kdDebug(32001) << " (inner) normalFrameRect=" << normalInnerFrameRect << " frameRect=" << innerFrameRect << endl; //kdDebug(32001) << " crect after view-to-normal:" << fcrect << "." << " Will move by (" << -offsetX << ", -(" << normalInnerFrameRect.top() << "-" << m_doc->zoomItY(frame->internalY()) << ") == " << -offsetY << ")." << endl; #endif fcrect.moveBy( -offsetX, -offsetY ); Q_ASSERT( fcrect.x() >= 0 ); Q_ASSERT( fcrect.y() >= 0 ); // fcrect is now the portion of the frame to be drawn, // in the frame's coordinates and in pixels #ifdef DEBUG_DRAW kdDebug(32001) << "KWFrameSet::drawFrameAndBorders in frame coords:" << fcrect << ". Will translate painter by intersec-fcrect: " << innerCRect.x()-fcrect.x() << "," << innerCRect.y()-fcrect.y() << "." << endl; #endif TQRegion reg; if ( drawUnderlyingFrames ) reg = frameClipRegion( painter, frame, outerCRect, viewMode ); else // false means we are being drawn _as_ an underlying frame, so no clipping! reg = painter->xForm( outerCRect ); if ( !reg.isEmpty() ) { painter->save(); painter->setClipRegion( reg ); drawFrame( frame, painter, fcrect, outerCRect, innerCRect.topLeft() - fcrect.topLeft(), // This assume that viewToNormal() is only a translation settingsFrame, cg, onlyChanged, resetChanged, edit, viewMode, drawUnderlyingFrames ); if( !groupmanager() ) // not for table cells drawFrameBorder( painter, frame, settingsFrame, outerCRect, viewMode ); painter->restore(); } } } } void KWFrameSet::drawFrame( KWFrame *frame, TQPainter *painter, const TQRect &fcrect, const TQRect &outerCRect, const TQPoint& translationOffset, KWFrame *settingsFrame, const TQColorGroup &cg, bool onlyChanged, bool resetChanged, KWFrameSetEdit *edit, KWViewMode* viewMode, bool drawUnderlyingFrames ) { // In this method the painter is NOT translated yet. It's still in view coordinates. if ( outerCRect.isEmpty() ) return; #ifdef DEBUG_DRAW kdDebug(32001) << "\nKWFrameSet::drawFrame " << name() << " outerCRect=" << outerCRect << " frameCrect=" << fcrect << " drawUnderlyingFrames=" << drawUnderlyingFrames << endl; #endif Q_ASSERT( fcrect.isValid() ); TQColorGroup frameColorGroup( cg ); if ( settingsFrame ) // 0L in text viewmode { TQBrush bgBrush( settingsFrame->backgroundColor() ); bgBrush.setColor( KWDocument::resolveBgColor( bgBrush.color(), painter ) ); frameColorGroup.setBrush( TQColorGroup::Base, bgBrush ); } if ( drawUnderlyingFrames && frame && frame->frameStack()) { TQValueList below = frame->frameStack()->framesBelow(); if ( !below.isEmpty() ) { // Double-buffering - not when printing TQPainter* doubleBufPainter = painter; TQPixmap* pix = 0L; if ( painter->device()->devType() != TQInternal::Printer ) { pix = m_doc->doubleBufferPixmap( outerCRect.size() ); doubleBufPainter = new TQPainter; doubleBufPainter->begin( pix ); // Initialize the pixmap to the page background color // (if the frame is over the page margins, no underlying frame will paint anything there) doubleBufPainter->fillRect( 0, 0, outerCRect.width(), outerCRect.height(), TQApplication::tqpalette().active().brush( TQColorGroup::Base ) ); // The double-buffer pixmap has (0,0) at outerCRect.topLeft(), so we need to // translate the double-buffer painter; drawFrameAndBorders will draw using view coordinates. doubleBufPainter->translate( -outerCRect.x(), -outerCRect.y() ); #ifdef DEBUG_DRAW // kdDebug(32001) << " ... using double buffering. Portion covered: " << outerCRect << endl; #endif } // Transparency handling #ifdef DEBUG_DRAW kdDebug(32001) << " below: " << below.count() << endl; #endif for (TQValueListIterator it = below.begin(); it != below.end(); ++it ) { KWFrame* f = (*it); #ifdef DEBUG_DRAW kdDebug(32001) << " looking at frame below us: " << f->frameSet()->name() << " frame " << frameFromPtr( frame ) << endl; #endif TQRect viewFrameCRect = outerCRect.intersect( viewMode->normalToView( f->outerRect( viewMode ) ) ); if ( !viewFrameCRect.isEmpty() ) { #ifdef DEBUG_DRAW kdDebug(32001) << " viewFrameRect=" << viewFrameCRect << " calling drawFrameAndBorders." << endl; #endif f->frameSet()->drawFrameAndBorders( f, doubleBufPainter, viewFrameCRect, cg, false, resetChanged, edit, viewMode, 0L, false ); } } if ( frame->paddingLeft() || frame->paddingTop() || frame->paddingRight() || frame->paddingBottom() ) drawPadding( frame, doubleBufPainter, outerCRect, cg, viewMode ); doubleBufPainter->save(); #ifdef DEBUG_DRAW kdDebug(32001) << " translating by " << translationOffset.x() << ", " << translationOffset.y() << " before drawFrameContents" << endl; #endif doubleBufPainter->translate( translationOffset.x(), translationOffset.y() ); // This assume that viewToNormal() is only a translation // We can't "tqrepaint changed parags only" if we just drew the underlying frames, hence the "false" drawFrameContents( frame, doubleBufPainter, fcrect, frameColorGroup, false, resetChanged, edit, viewMode ); doubleBufPainter->restore(); if ( painter->device()->devType() != TQInternal::Printer ) { doubleBufPainter->end(); #ifdef DEBUG_DRAW kdDebug(32001) << " painting double-buf pixmap at position " << outerCRect.topLeft() << " (real painter pos:" << painter->xForm( outerCRect.topLeft() ) << ")" << endl; #endif painter->drawPixmap( outerCRect.topLeft(), *pix ); delete doubleBufPainter; } return; // done! :) } else { // nothing below? paint a bg color then frameColorGroup.setBrush( TQColorGroup::Base, m_doc->defaultBgColor( painter ) ); } } if ( frame && (frame->paddingLeft() || frame->paddingTop() || frame->paddingRight() || frame->paddingBottom()) ) drawPadding( frame, painter, outerCRect, cg, viewMode ); painter->save(); painter->translate( translationOffset.x(), translationOffset.y() ); drawFrameContents( frame, painter, fcrect, frameColorGroup, onlyChanged, resetChanged, edit, viewMode ); painter->restore(); } void KWFrameSet::drawFrameContents( KWFrame *, TQPainter *, const TQRect &, const TQColorGroup &, bool, bool, KWFrameSetEdit*, KWViewMode * ) { kdWarning() << "Default implementation of drawFrameContents called for " << className() << " " << this << " " << name() << kdBacktrace(); } void KWFrameSet::saveCommon( TQDomElement &tqparentElem, bool saveFrames ) { if ( m_frames.isEmpty() ) // Deleted frameset -> don't save return; // Save all the common attributes for framesets. tqparentElem.setAttribute( "frameType", static_cast( type() ) ); tqparentElem.setAttribute( "frameInfo", static_cast( m_info ) ); tqparentElem.setAttribute( "name", m_name ); tqparentElem.setAttribute( "visible", static_cast( m_visible ) ); tqparentElem.setAttribute( "protectSize", static_cast( m_protectSize ) ); if ( saveFrames ) { TQPtrListIterator frameIt = frameIterator(); for ( ; frameIt.current(); ++frameIt ) { KWFrame *frame = frameIt.current(); TQDomElement frameElem = tqparentElem.ownerDocument().createElement( "FRAME" ); tqparentElem.appendChild( frameElem ); frame->save( frameElem ); if(m_doc->processingType() == KWDocument::WP) { // Assume that all header/footer frames in the same frameset are // perfect copies. This might not be the case some day though. if(frameSetInfo() == FI_FIRST_HEADER || frameSetInfo() == FI_EVEN_HEADER || frameSetInfo() == FI_ODD_HEADER || frameSetInfo() == FI_FIRST_FOOTER || frameSetInfo() == FI_EVEN_FOOTER || frameSetInfo() == FI_ODD_FOOTER || frameSetInfo() == FI_FOOTNOTE) break; } } } } // // This function is intended as a helper for all the derived classes. It reads // in all the attributes common to all framesets and loads all frames. // void KWFrameSet::load( TQDomElement &framesetElem, bool loadFrames ) { m_info = static_cast( KWDocument::getAttribute( framesetElem, "frameInfo", KWFrameSet::FI_BODY ) ); m_visible = static_cast( KWDocument::getAttribute( framesetElem, "visible", true ) ); m_protectSize=static_cast( KWDocument::getAttribute( framesetElem, "protectSize", false ) ); if ( loadFrames ) { // TQDomElement frameElem = framesetElem.firstChild().toElement(); for ( ; !frameElem.isNull() ; frameElem = frameElem.nextSibling().toElement() ) { if ( frameElem.tagName() == "FRAME" ) { KoRect rect; rect.setLeft( KWDocument::getAttribute( frameElem, "left", 0.0 ) ); rect.setTop( KWDocument::getAttribute( frameElem, "top", 0.0 ) ); rect.setRight( KWDocument::getAttribute( frameElem, "right", 0.0 ) ); rect.setBottom( KWDocument::getAttribute( frameElem, "bottom", 0.0 ) ); KWFrame * frame = new KWFrame(this, rect.x(), rect.y(), rect.width(), rect.height() ); frame->load( frameElem, this, m_doc->syntaxVersion() ); addFrame( frame, false ); m_doc->progressItemLoaded(); } } } } KWFrame* KWFrameSet::loadOasisFrame( const TQDomElement& tag, KoOasisContext& context ) { double width = 100; if ( tag.hasAttributeNS( KoXmlNS::svg, "width" ) ) { // fixed width // TODO handle percentage (of enclosing table/frame/page) width = KoUnit::parseValue( tag.attributeNS( KoXmlNS::svg, "width", TQString() ) ); } else if ( tag.hasAttributeNS( KoXmlNS::fo, "min-width" ) ) { // min-width is not supported in KWord. Let's use it as a fixed width. width = KoUnit::parseValue( tag.attributeNS( KoXmlNS::fo, "min-width", TQString() ) ); } else { kdWarning(32001) << "Error in frame " << tag.tagName() << " " << tag.attributeNS( KoXmlNS::draw, "name", TQString() ) << " : neither width nor min-width specified!" << endl; } double height = 100; if ( tag.hasAttributeNS( KoXmlNS::svg, "height" ) ) { // fixed height // TODO handle percentage (of enclosing table/frame/page) height = KoUnit::parseValue( tag.attributeNS( KoXmlNS::svg, "height", TQString() ) ); } //kdDebug(32001) << k_funcinfo << "width=" << width << " height=" << height << " pt" << endl; KWFrame * frame = new KWFrame(this, KoUnit::parseValue( tag.attributeNS( KoXmlNS::svg, "x", TQString() ) ), KoUnit::parseValue( tag.attributeNS( KoXmlNS::svg, "y", TQString() ) ), width, height ); frame->setZOrder( tag.attributeNS( KoXmlNS::draw, "z-index", TQString() ).toInt() ); // Copy-frames. // We currently ignore the value of the copy-of attribute. It probably needs to // be handled like chain-next-name (kwtextframeset.cc) but for all types of frameset. frame->setCopy( tag.hasAttributeNS( KoXmlNS::draw, "copy-of" ) ); frame->loadCommonOasisProperties( context, this, "graphic" ); addFrame( frame, false ); // Protect (OASIS 14.27.7, also in OO-1.1) // A frame with protected contents means that the frameset is protected (makes sense) // A frame with protected size means that the frameset is size-protected (hmm, kword did it that way) // TODO implement position protection TQString protectList = context.styleStack().attributeNS( KoXmlNS::style, "protect" ); if ( protectList.tqcontains( "content" ) ) setProtectContent( true ); if ( protectList.tqcontains( "size" ) ) m_protectSize = true; // TODO m_visible ? User-toggeable or internal? return frame; } void KWFrameSet::setVisible( bool v ) { m_visible = v; if ( m_visible ) // updateFrames was disabled while we were invisible updateFrames(); } bool KWFrameSet::isVisible( KWViewMode* viewMode ) const { if ( !m_visible || m_frames.isEmpty() ) return false; if ( isAHeader() && !m_doc->isHeaderVisible() ) return false; if ( isAFooter() && !m_doc->isFooterVisible() ) return false; if ( viewMode && !viewMode->isFrameSetVisible(this) ) return false; if ( isFloating() && !anchorFrameset()->isVisible( viewMode ) ) return false; KoHFType ht = m_doc != 0 ? m_doc->headerType(): HF_FIRST_DIFF; KoHFType ft = m_doc != 0 ? m_doc->footerType(): HF_FIRST_DIFF; switch( m_info ) { case FI_FIRST_HEADER: return ( ht == HF_FIRST_DIFF || ht == HF_FIRST_EO_DIFF ); case FI_ODD_HEADER: return true; case FI_EVEN_HEADER: return ( ht == HF_EO_DIFF || ht == HF_FIRST_EO_DIFF ); case FI_FIRST_FOOTER: return ( ft == HF_FIRST_DIFF || ft == HF_FIRST_EO_DIFF ); case FI_ODD_FOOTER: return true; case FI_EVEN_FOOTER: return ( ft == HF_EO_DIFF || ft == HF_FIRST_EO_DIFF ); default: return true; } } bool KWFrameSet::isAHeader() const { return ( m_info == FI_FIRST_HEADER || m_info == FI_ODD_HEADER || m_info == FI_EVEN_HEADER ); } bool KWFrameSet::isAFooter() const { return ( m_info == FI_FIRST_FOOTER || m_info == FI_ODD_FOOTER || m_info == FI_EVEN_FOOTER ); } bool KWFrameSet::isFootEndNote() const { return m_info == FI_FOOTNOTE; } bool KWFrameSet::isMainFrameset() const { return ( m_doc && m_doc->processingType() == KWDocument::WP && m_doc->frameSet( 0 ) == this ); } bool KWFrameSet::isMoveable() const { if ( isHeaderOrFooter() ) return false; return !isMainFrameset() && !isFloating(); } const char* KWFrameSet::headerFooterTag() const { switch ( m_info ) { case KWFrameSet::FI_ODD_HEADER: return "style:header"; case KWFrameSet::FI_EVEN_HEADER: return "style:header-left"; case KWFrameSet::FI_ODD_FOOTER: return "style:footer"; case KWFrameSet::FI_EVEN_FOOTER: return "style:footer-left"; case KWFrameSet::FI_FIRST_HEADER: return "style:header-first"; // NOT OASIS COMPLIANT case KWFrameSet::FI_FIRST_FOOTER: return "style:footer-first"; // NOT OASIS COMPLIANT default: // shouldn't be called for body or footnote return 0; } } void KWFrameSet::finalize() { //kdDebug(32001) << "KWFrameSet::finalize ( calls updateFrames + zoom ) " << this << endl; updateFrames(); } TQRegion KWFrameSet::frameClipRegion( TQPainter * painter, KWFrame *frame, const TQRect & crect, KWViewMode * viewMode ) { // KWDocument * doc = kWordDocument(); TQRect rc = painter->xForm( crect ); #ifdef DEBUG_DRAW //kdDebug(32002) << "KWFrameSet::frameClipRegion rc initially " << rc << endl; #endif Q_ASSERT( frame ); #if 0 // done later if ( clipFrame ) { rc &= painter->xForm( viewMode->normalToView( doc->zoomRect( (*frame) ) ) ); // intersect #ifdef DEBUG_DRAW kdDebug(32002) << "KWFrameSet::frameClipRegion frame=" << *frame << " clip region rect=" << rc << " rc.isEmpty()=" << rc.isEmpty() << endl; #endif } #endif if ( !rc.isEmpty() ) { TQRegion reg( rc ); // This breaks when a frame is under another one, it still appears if !onlyChanged. // cvs log says this is about frame borders... hmm. /// ### if ( onlyChanged ) Q_ASSERT( frame->frameStack() ); TQValueList onTop = frame->frameStack()->framesOnTop(); for (TQValueListIterator fIt = onTop.begin(); fIt != onTop.end(); ++fIt ) { KWFrame* frameOnTop = (*fIt); Q_ASSERT( frameOnTop->frameSet() ); TQRect r = painter->xForm( viewMode->normalToView( frameOnTop->outerRect( viewMode ) ) ); #ifdef DEBUG_DRAW //kdDebug(32002) << "frameClipRegion subtract rect "<< r << endl; #endif reg -= r; // subtract } #ifdef DEBUG_DRAW //kdDebug(32002) << "KWFrameSet::frameClipRegion result:" << reg << endl; #endif return reg; } return TQRegion(); } bool KWFrameSet::canRemovePage( int num ) { TQPtrListIterator frameIt( frameIterator() ); for ( ; frameIt.current(); ++frameIt ) { KWFrame * frame = frameIt.current(); if ( frame->pageNumber() == num ) // ## TODO: use framesInPage, see KWTextFrameSet { // Ok, so we have a frame on that page -> we can't remove it unless it's a copied frame if ( ! ( frame->isCopy() && frameIt.current() != m_frames.first() ) ) { kdDebug(32001) << "KWFrameSet::canRemovePage " << name() << " frame on page " << num << " -> false" << endl; return false; } } } return true; } void KWFrameSet::setFrameBehavior( KWFrame::FrameBehavior fb ) { for(KWFrame *f=m_frames.first();f;f=m_frames.next()) f->setFrameBehavior(fb); } void KWFrameSet::setNewFrameBehavior( KWFrame::NewFrameBehavior nfb ) { for(KWFrame *f=m_frames.first();f;f=m_frames.next()) f->setNewFrameBehavior(nfb); } // ## this should pass the viewmode as argument, probably. bool KWFrameSet::isFrameAtPos( const KWFrame* frame, const TQPoint& point, bool borderOfFrameOnly) const { TQRect outerRect( frame->outerRect( m_doc->tqlayoutViewMode() ) ); // Give the user a bit of margin for clicking on it :) const int margin = 2; outerRect.rLeft() -= margin; outerRect.rTop() -= margin; outerRect.rRight() += margin; outerRect.rBottom() += margin; if ( outerRect.tqcontains( point ) ) { if(borderOfFrameOnly) { TQRect innerRect( m_doc->zoomRect( *frame ) ); innerRect.rLeft() += margin; innerRect.rTop() += margin; innerRect.rRight() -= margin; innerRect.rBottom() -= margin; return (!innerRect.tqcontains(point) ); } return true; } return false; } void KWFrameSet::setZOrder() { //kdDebug(32001) << "KWFrameSet::setZOrder (to max) " << name() << endl; TQPtrListIterator fit = frameIterator(); for ( ; fit.current() ; ++fit ) fit.current()->setZOrder( m_doc->maxZOrder( fit.current()->pageNumber(m_doc) ) + 1 ); } void KWFrameSet::setName( const TQString &name ) { m_name = name; emit sigNameChanged(this); } #ifndef NDEBUG #include "KWFrameViewManager.h" #include "KWFrameView.h" void KWFrameSet::printDebug() { static const char * typeFrameset[] = { "base", "txt", "picture", "part", "formula", "clipart", "6", "7", "8", "9", "table", "ERROR" }; static const char * infoFrameset[] = { "body", "first header", "even headers", "odd headers", "first footer", "even footers", "odd footers", "footnote", "ERROR" }; static const char * frameBh[] = { "AutoExtendFrame", "AutoCreateNewFrame", "Ignore", "ERROR" }; static const char * newFrameBh[] = { "Reconnect", "NoFollowup", "Copy" }; static const char * runaround[] = { "No Runaround", "Bounding Rect", "Skip", "ERROR" }; static const char * runaroundSide[] = { "Biggest", "Left", "Right", "ERROR" }; KWFrameViewManager *fvm = 0; if ( !m_doc->getAllViews().isEmpty() ) { KWView *view = m_doc->getAllViews().first(); if(view) fvm = view->frameViewManager(); } kdDebug() << " | Visible: " << isVisible() << endl; kdDebug() << " | Type: " << typeFrameset[ type() ] << endl; kdDebug() << " | Info: " << infoFrameset[ frameSetInfo() ] << endl; kdDebug() << " | Floating: " << isFloating() << endl; kdDebug() << " | Frames in page array: " << endl; for ( uint i = 0 ; i < m_framesInPage.size() ; ++i ) { TQPtrListIterator it( *m_framesInPage[i] ); int pgNum = i + m_firstPage; for ( ; it.current() ; ++it ) kdDebug() << " | " << pgNum << ": " << it.current() << " " << *it.current() << " internalY=" << it.current()->internalY() << "pt " << " (in LU pix:" << m_doc->ptToLayoutUnitPixY( it.current()->internalY() ) << ")" << " innerHeight=" << it.current()->innerHeight() << " (in LU pix:" << m_doc->ptToLayoutUnitPixY( it.current()->innerHeight() ) << ")" << endl; } TQPtrListIterator frameIt = frameIterator(); for ( unsigned int j = 0; frameIt.current(); ++frameIt, ++j ) { KWFrame * frame = frameIt.current(); TQCString copy = frame->isCopy() ? "[copy]" : ""; kdDebug() << " +-- Frame " << j << " of "<< frameCount() << " (" << frame << ") " << copy << endl; printDebug( frame ); kdDebug() << " Rectangle : " << frame->x() << "," << frame->y() << " " << frame->width() << "x" << frame->height() << endl; kdDebug() << " RunAround: "<< runaround[ frame->runAround() ] << " side:" << runaroundSide[ frame->runAroundSide() ]<< endl; kdDebug() << " FrameBehavior: "<< frameBh[ frame->frameBehavior() ] << endl; kdDebug() << " NewFrameBehavior: "<< newFrameBh[ frame->newFrameBehavior() ] << endl; TQColor col = frame->backgroundColor().color(); kdDebug() << " BackgroundColor: "<< ( col.isValid() ? col.name().latin1() : "(default)" ) << endl; kdDebug() << " SheetSide "<< frame->sheetSide() << endl; kdDebug() << " Z Order: " << frame->zOrder() << endl; if( frame->frameStack() ) { TQValueList onTop = frame->frameStack()->framesOnTop(); TQValueList below = frame->frameStack()->framesBelow(); kdDebug() << " Frames below: " << below.count() << ", frames on top: " << onTop.count() << endl; } else kdDebug() << " no frameStack set." << endl; kdDebug() << " minFrameHeight "<< frame->minimumFrameHeight() << endl; TQString page = pageManager() && pageManager()->pageCount() > 0 ? TQString::number(frame->pageNumber()) : " [waiting for pages to be created]"; KWFrameView *fv = 0; if(fvm) fv = fvm->view(frame); if(fv && fv->selected()) kdDebug() << " * Page "<< page << endl; else kdDebug() << " Page "<< page << endl; } } void KWFrameSet::printDebug( KWFrame * ) { } #endif #include "KWFrameSet.moc"