kword

KWTextFrameSet.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 1998, 1999, 2000 Reginald Stadlbauer <reggie@kde.org>
00003    Copyright (C) 2001-2006 David Faure <faure@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "KWTextFrameSet.h"
00022 #include "KWTableFrameSet.h"
00023 #include "KWDocument.h"
00024 #include "KWView.h"
00025 #include "KWViewMode.h"
00026 #include "KWCanvas.h"
00027 #include "KWAnchor.h"
00028 #include "KWCommand.h"
00029 #include "KWFormulaFrameSet.h"
00030 #include "KWBgSpellCheck.h"
00031 #include "KWordTextFrameSetIface.h"
00032 #include "KWordTextFrameSetEditIface.h"
00033 #include "KWordFootNoteFrameSetIface.h"
00034 #include "KWordFrameSetIface.h"
00035 #include "KWLoadingInfo.h"
00036 #include "KWInsertTOCCommand.h"
00037 #include "KWMailMergeDataBase.h"
00038 #include "KoTextBookmark.h"
00039 #include "KWVariable.h"
00040 #include "KWOasisSaver.h"
00041 #include "KWFrameList.h"
00042 #include "KWPageManager.h"
00043 #include "KWPage.h"
00044 
00045 #include <KoParagCounter.h>
00046 #include <KoCustomVariablesDia.h>
00047 #include <KoAutoFormat.h>
00048 #include <KoTextObject.h>
00049 #include <KoTextCommand.h>
00050 #include <KoTextFormatter.h>
00051 #include <KoChangeCaseDia.h>
00052 #include <KoXmlNS.h>
00053 #include <KoXmlWriter.h>
00054 #include <KoOasisContext.h>
00055 #include <KoStore.h>
00056 
00057 #include <klocale.h>
00058 #include <kaction.h>
00059 #include <kmessagebox.h>
00060 #include <kdebug.h>
00061 
00062 #include <qclipboard.h>
00063 #include <qdragobject.h>
00064 #include <qcursor.h>
00065 #include <qfile.h>
00066 #include <qprogressdialog.h>
00067 #include <qregexp.h>
00068 
00069 #include <assert.h>
00070 #include <qapplication.h>
00071 
00072 //#define DEBUG_MARGINS
00073 //#define DEBUG_FORMATVERTICALLY
00074 //#define DEBUG_FORMATS
00075 //#define DEBUG_FORMAT_MORE
00076 //#define DEBUG_VIEWAREA
00077 //#define DEBUG_CURSOR
00078 
00079 //#define DEBUG_DTI
00080 //#define DEBUG_ITD
00081 
00087 class KWTextFormatter : public KoTextFormatter
00088 {
00089 public:
00090     KWTextFormatter( KWTextFrameSet *textfs ) : m_textfs( textfs ) {}
00091     virtual ~KWTextFormatter() {}
00092 
00093     virtual int formatVertically( KoTextDocument*, KoTextParag* parag )
00094     {
00095         return m_textfs->formatVertically( parag, parag->rect() );
00096     }
00097     virtual void postFormat( KoTextParag* parag )
00098     {
00099         m_textfs->fixParagWidth( static_cast<KWTextParag *>( parag ) );
00100     }
00101 private:
00102     KWTextFrameSet *m_textfs;
00103 };
00104 
00105 KWTextFrameSet::KWTextFrameSet( KWDocument *_doc, const QString & name )
00106     : KWFrameSet( _doc )
00107 {
00108     //kdDebug() << "KWTextFrameSet::KWTextFrameSet " << this << endl;
00109     if ( name.isEmpty() )
00110         m_name = _doc->generateFramesetName( i18n( "Text Frameset %1" ) );
00111     else
00112         m_name = name;
00113 
00114     QObject::setName( m_name.utf8() ); // store name in the QObject, for DCOP users
00115     init();
00116 }
00117 
00118 KWTextFrameSet::KWTextFrameSet( KWDocument* doc, const QDomElement& tag, KoOasisContext& /*context*/ )
00119     : KWFrameSet( doc )
00120 {
00121     m_name = tag.attributeNS( KoXmlNS::draw, "name", QString::null );
00122     if ( doc->frameSetByName( m_name ) ) // already exists!
00123         m_name = doc->generateFramesetName( m_name + " %1" );
00124     init();
00125     // Note that we don't call loadOasis here. The caller wants to do it,
00126     // to get the frame it returns.
00127 }
00128 
00129 // protected constructor for testing purposes; does not do an init.
00130 KWTextFrameSet::KWTextFrameSet( const QString &name ) : KWFrameSet(0) {
00131     m_name = name;
00132 
00133     QObject::setName( m_name.utf8() ); // store name in the QObject, for DCOP users
00134     m_currentViewMode = 0L;
00135     m_currentDrawnFrame = 0L;
00136     m_lastTextDocHeight = 0;
00137     m_textobj = 0;
00138 }
00139 
00140 void KWTextFrameSet::init()
00141 {
00142     m_currentViewMode = 0L;
00143     m_currentDrawnFrame = 0L;
00144     m_lastTextDocHeight = 0;
00145     // Create the text document to set in the text object
00146     KWTextDocument* textdoc = new KWTextDocument( this,
00147         new KoTextFormatCollection( m_doc->defaultFont(), QColor(),
00148                                     m_doc->globalLanguage(),
00149                                     m_doc->globalHyphenation() ),
00150         new KWTextFormatter( this ) );
00151     textdoc->setFlow( this );
00152     textdoc->setPageBreakEnabled( true );              // get verticalBreak to be called
00153     if ( m_doc->tabStopValue() != -1 )
00154         textdoc->setTabStops( m_doc->ptToLayoutUnitPixX( m_doc->tabStopValue() ));
00155 
00156     m_textobj = new KoTextObject( textdoc, m_doc->styleCollection()->findStyle( "Standard" ),
00157                                   this, (m_name+"-textobj").utf8() );
00158     m_doc->backSpeller()->registerNewTextObject( m_textobj );
00159     connect( m_textobj, SIGNAL( availableHeightNeeded() ),
00160              SLOT( slotAvailableHeightNeeded() ) );
00161     connect( m_textobj, SIGNAL( afterFormatting( int, KoTextParag*, bool* ) ),
00162              SLOT( slotAfterFormatting( int, KoTextParag*, bool* ) ) );
00163     //connect( m_textobj, SIGNAL( formattingFirstParag() ),
00164     //         SLOT( slotFormattingFirstParag() ) );
00165     //connect( m_textobj, SIGNAL( chapterParagraphFormatted( KoTextParag * ) ),
00166     //         SLOT( slotChapterParagraphFormatted( KoTextParag * ) ) );
00167     connect( m_textobj, SIGNAL( newCommand( KCommand * ) ),
00168              SLOT( slotNewCommand( KCommand * ) ) );
00169     connect( m_textobj, SIGNAL( repaintChanged( KoTextObject* ) ),
00170              SLOT( slotRepaintChanged() ) );
00171     connect( m_textobj, SIGNAL( paragraphDeleted( KoTextParag*) ),
00172              SLOT( slotParagraphDeleted(KoTextParag*) ));
00173 
00174     connect( m_textobj, SIGNAL( paragraphCreated( KoTextParag*) ),
00175              SLOT( slotParagraphCreated(KoTextParag*) ));
00176     connect( m_textobj, SIGNAL( paragraphModified( KoTextParag*, int, int, int) ),
00177              SLOT( slotParagraphModified(KoTextParag*, int, int, int) ));
00178 }
00179 
00180 void KWTextFrameSet::slotParagraphModified(KoTextParag* _parag, int /*KoTextParag::ParagModifyType*/ _type, int start, int lenght)
00181 {
00182     kWordDocument()->paragraphModified(_parag, _type, start, lenght);
00183 }
00184 
00185 void KWTextFrameSet::slotParagraphCreated(KoTextParag* /*_parag*/)
00186 {
00187     //todo
00188 }
00189 
00190 void KWTextFrameSet::slotParagraphDeleted(KoTextParag*_parag)
00191 {
00192     kWordDocument()->paragraphDeleted( _parag, this);
00193 }
00194 
00195 KWordFrameSetIface* KWTextFrameSet::dcopObject()
00196 {
00197     if ( !m_dcop )
00198         m_dcop = new KWordTextFrameSetIface( this );
00199 
00200     return m_dcop;
00201 }
00202 
00203 KWFrameSetEdit * KWTextFrameSet::createFrameSetEdit( KWCanvas * canvas )
00204 {
00205     return new KWTextFrameSetEdit( this, canvas );
00206 }
00207 
00208 KoTextDocument * KWTextFrameSet::textDocument() const
00209 {
00210     return m_textobj->textDocument();
00211 }
00212 
00213 KWTextDocument * KWTextFrameSet::kwTextDocument() const
00214 {
00215      return static_cast<KWTextDocument *>(m_textobj->textDocument());
00216 }
00217 
00218 void KWTextFrameSet::slotAvailableHeightNeeded()
00219 {
00220     Q_ASSERT( isVisible() );
00221     kdDebug() << "KWTextFrameSet::slotAvailableHeightNeeded " << name() << endl;
00222     updateFrames( 0 ); // only do the available-height determination
00223 }
00224 
00225 KWFrame * KWTextFrameSet::documentToInternal( const KoPoint &dPoint, QPoint &iPoint ) const
00226 {
00227 #ifdef DEBUG_DTI
00228     kdDebug() << "KWTextFrameSet::documentToInternal dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
00229 #endif
00230     if ( !m_doc->layoutViewMode()->hasFrames() ) { // text viewmode
00231         iPoint = QPoint( m_doc->ptToLayoutUnitPixX( dPoint.x() ),
00232                          m_doc->ptToLayoutUnitPixY( dPoint.y() ) );
00233         return m_frames.getFirst();
00234     }
00235     // Find the frame that contains dPoint. To go fast, we look them up by page number.
00236     int pageNum = m_doc->pageManager()->pageNumber(dPoint);
00237     QPtrListIterator<KWFrame> frameIt( framesInPage( pageNum ) );
00238     for ( ; frameIt.current(); ++frameIt )
00239     {
00240         KWFrame *theFrame = frameIt.current();
00241         if ( theFrame->contains( dPoint ) )
00242         {
00243             iPoint.setX( m_doc->ptToLayoutUnitPixX( dPoint.x() - theFrame->innerRect().x() ) );
00244             iPoint.setY( m_doc->ptToLayoutUnitPixY( dPoint.y() - theFrame->innerRect().y() + theFrame->internalY() ) );
00245 #ifdef DEBUG_DTI
00246             kdDebug() << "documentToInternal: returning " << iPoint.x() << "," << iPoint.y()
00247                       << " internalY=" << theFrame->internalY() << " because frame=" << theFrame
00248                       << " contains dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
00249 #endif
00250             return theFrame;
00251         }
00252 #ifdef DEBUG_DTI
00253         //else
00254         //  kdDebug() << "DTI: " << frameRect
00255         //            << " doesn't contain nPoint:" << nPoint.x() << "," << nPoint.y() << endl;
00256 #endif
00257     }
00258     // Not found. This means the mouse isn't over any frame, in the page pageNum.
00259     iPoint = m_doc->ptToLayoutUnitPix( dPoint ); // bah
00260     return 0;
00261 }
00262 
00263 KWFrame * KWTextFrameSet::documentToInternalMouseSelection( const KoPoint &dPoint, QPoint &iPoint, RelativePosition& relPos, KWViewMode *viewMode ) const
00264 {
00265 #ifdef DEBUG_DTI
00266     kdDebug() << "KWTextFrameSet::documentToInternalMouseSelection dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
00267 #endif
00268     if ( !viewMode->hasFrames() ) { // text viewmode
00269         relPos = InsideFrame;
00270         iPoint = QPoint( m_doc->ptToLayoutUnitPixX( dPoint.x() ),
00271                          m_doc->ptToLayoutUnitPixY( dPoint.y() ) );
00272         return m_frames.getFirst();
00273     }
00274 
00275     // Find the frame that contains dPoint. To go fast, we look them up by page number.
00276     int pageNum = m_doc->pageManager()->pageNumber(dPoint);
00277     QPtrListIterator<KWFrame> frameIt( framesInPage( pageNum ) );
00278     for ( ; frameIt.current(); ++frameIt )
00279     {
00280         KWFrame *theFrame = frameIt.current();
00281         if ( theFrame->contains( dPoint ) )
00282         {
00283             iPoint.setX( m_doc->ptToLayoutUnitPixX( dPoint.x() - theFrame->innerRect().x() ) );
00284             iPoint.setY( m_doc->ptToLayoutUnitPixY( dPoint.y() - theFrame->innerRect().y() + theFrame->internalY() ) );
00285 #ifdef DEBUG_DTI
00286             kdDebug() << "documentToInternal: returning InsideFrame " << iPoint.x() << "," << iPoint.y()
00287                       << " internalY=" << theFrame->internalY() << " because frame=" << theFrame
00288                       << " contains dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
00289 #endif
00290             relPos = InsideFrame;
00291             return theFrame;
00292         }
00293     }
00294     frameIt.toFirst();
00295     for ( ; frameIt.current(); ++frameIt )
00296     {
00297         KWFrame *theFrame = frameIt.current();
00298         KoRect openLeftRect( theFrame->innerRect() );
00299         openLeftRect.setLeft( theFrame->paddingLeft() );
00300 #ifdef DEBUG_DTI
00301         kdDebug() << "documentToInternal: openLeftRect=" << openLeftRect << endl;
00302 #endif
00303         if ( openLeftRect.contains( dPoint ) )
00304         {
00305             // We are at the left of this frame (and not in any other frame of this frameset)
00306             iPoint.setX( m_doc->ptToLayoutUnitPixX(theFrame->innerRect().left() ));
00307             iPoint.setY( m_doc->ptToLayoutUnitPixY( dPoint.y() - theFrame->top() + theFrame->internalY() ) );
00308 #ifdef DEBUG_DTI
00309             kdDebug() << "documentToInternal: returning LeftOfFrame " << iPoint.x() << "," << iPoint.y()
00310                       << " internalY=" << theFrame->internalY() << " because openLeftRect=" << openLeftRect
00311                       << " contains dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
00312 #endif
00313             relPos = LeftOfFrame;
00314             return theFrame;
00315         }
00316         KoRect openTopRect( KoPoint( 0, 0 ), theFrame->innerRect().bottomRight() );
00317 #ifdef DEBUG_DTI
00318         kdDebug() << "documentToInternal: openTopRect=" << openTopRect << endl;
00319 #endif
00320         if ( openTopRect.contains( dPoint ) )
00321         {
00322             // We are at the top of this frame (...)
00323             iPoint.setX( m_doc->ptToLayoutUnitPixX( dPoint.x() - theFrame->innerRect().left() ) );
00324             iPoint.setY( m_doc->ptToLayoutUnitPixY( theFrame->internalY() ) );
00325 #ifdef DEBUG_DTI
00326             kdDebug() << "documentToInternal: returning " << iPoint.x() << "," << iPoint.y()
00327                       << " internalY=" << theFrame->internalY() << " because openTopRect=" << openTopRect
00328                       << " contains dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
00329 #endif
00330             relPos = TopOfFrame;
00331             return theFrame;
00332         }
00333     }
00334     // Not found. This means we are under (or at the right of), the frames in pageNum.
00335     // In that case, go for the first frame in the next page.
00336     if ( pageNum + 1 >= (int)m_framesInPage.size() + m_firstPage )
00337     {
00338         // Under last frame of last page!
00339         KWFrame *theFrame = m_frames.getLast();
00340         iPoint.setX( m_doc->ptToLayoutUnitPixX( theFrame->innerWidth() ) );
00341         iPoint.setY( m_doc->ptToLayoutUnitPixY( theFrame->innerHeight() ) );
00342 #ifdef DEBUG_DTI
00343         kdDebug() << "documentToInternal: returning AtEnd " << iPoint.x() << "," << iPoint.y()
00344                   << " because we are under all frames of the last page" << endl;
00345 #endif
00346         relPos = AtEnd;
00347         return theFrame;
00348     }
00349     else
00350     {
00351         QPtrListIterator<KWFrame> frameIt( framesInPage( pageNum + 1 ) );
00352         if ( frameIt.current() )
00353         {
00354             // There is a frame on the next page
00355             KWFrame *theFrame = frameIt.current();
00356             KoRect openTopRect( theFrame->innerRect() );
00357             openTopRect.setTop( 0 );
00358             if ( openTopRect.contains( dPoint ) ) // We are at the top of this frame
00359                 iPoint.setX( m_doc->ptToLayoutUnitPixX( dPoint.x() - theFrame->left() ) );
00360             else
00361                 iPoint.setX( 0 ); // We are, hmm, on the left or right of the frames
00362             iPoint.setY( m_doc->ptToLayoutUnitPixY( theFrame->internalY() ) );
00363 #ifdef DEBUG_DTI
00364             kdDebug() << "documentToInternal: returning TopOfFrame " << iPoint.x() << "," << iPoint.y()
00365                       << " because we are under all frames of page " << pageNum << endl;
00366 #endif
00367             relPos = TopOfFrame;
00368             return theFrame;
00369         } // else there is a gap (no frames on that page, but on some other further down)
00370         // This case isn't handled (and should be VERY rare I think)
00371     }
00372 
00373     iPoint = m_doc->ptToLayoutUnitPix( dPoint ); // bah
00374 #ifdef DEBUG_DTI
00375     kdDebug() << "documentToInternal: returning not found for " << iPoint.x() << "," << iPoint.y() << endl;
00376 #endif
00377     return 0;
00378 }
00379 
00380 KWFrame * KWTextFrameSet::internalToDocumentWithHint( const QPoint &iPoint, KoPoint &dPoint, const KoPoint &hintDPoint ) const
00381 {
00382 #ifdef DEBUG_ITD
00383     kdDebug() << "KWTextFrameSet::internalToDocumentWithHint hintDPoint: " << hintDPoint.x() << "," << hintDPoint.y() << endl;
00384 #endif
00385     if ( !m_doc->layoutViewMode()->hasFrames() ) { // text viewmode
00386         dPoint = m_doc->layoutUnitPtToPt( m_doc->pixelToPt( iPoint ) );
00387         return m_frames.getFirst();
00388     }
00389     KWFrame *lastFrame = 0L;
00390     QPtrListIterator<KWFrame> frameIt( frameIterator() );
00391     for ( ; frameIt.current(); ++frameIt )
00392     {
00393         KWFrame *theFrame = frameIt.current();
00394         QRect r( 0, m_doc->ptToLayoutUnitPixY( theFrame->internalY() ), m_doc->ptToLayoutUnitPixX( theFrame->innerWidth() )+1, m_doc->ptToLayoutUnitPixY( theFrame->innerHeight() )+1 );
00395 #ifdef DEBUG_ITD
00396         kdDebug() << "ITD: r=" << r << " iPoint=" << iPoint.x() << "," << iPoint.y() << endl;
00397 #endif
00398         // r is the frame in qrt coords
00399         if ( r.contains( iPoint ) ) // both r and p are in layout units (aka internal)
00400         {
00401             dPoint = internalToDocumentKnowingFrame( iPoint, theFrame );
00402 #ifdef DEBUG_ITD
00403             kdDebug() << "copy: " << theFrame->isCopy() << " hintDPoint.y()=" << hintDPoint.y() << " dPoint.y()=" << dPoint.y() << endl;
00404 #endif
00405             // First test: No "hintDPoint" specified, go for the first match
00406             // Second test: hintDPoint specified, check if we are far enough
00407             if ( hintDPoint.isNull() || hintDPoint.y() <= dPoint.y() )
00408                 return theFrame;
00409             // Remember that this frame matched, in case we find no further frame that matches
00410             lastFrame = theFrame;
00411         }
00412         else if ( lastFrame )
00413         {
00414             return lastFrame;
00415         }
00416     }
00417 
00418     // This happens when the parag is on a not-yet-created page (formatMore will notice afterwards)
00419     // So it doesn't matter much what happens here, we'll redo it anyway.
00420 #ifdef DEBUG_ITD
00421     kdDebug(32002) << "KWTextFrameSet::internalToDocumentWithHint " << iPoint.x() << "," << iPoint.y()
00422                    << " not in any frame of " << (void*)this << endl;
00423 #endif
00424     dPoint = m_doc->layoutUnitPtToPt( m_doc->pixelToPt( iPoint ) ); // bah
00425     return 0L;
00426 }
00427 
00428 // relPoint is in relative coordinates (pt)
00429 KoPoint KWTextFrameSet::internalToDocumentKnowingFrame( const KoPoint &relPoint, KWFrame* theFrame ) const
00430 {
00431     // It's ok to have theFrame == 0 in the text viewmode, but not in other modes
00432     if ( m_doc->layoutViewMode()->hasFrames() )
00433         Q_ASSERT( theFrame );
00434     if ( theFrame )
00435         return KoPoint( relPoint.x() + theFrame->innerRect().x(),
00436                         relPoint.y() - theFrame->internalY() + theFrame->innerRect().y() );
00437     else
00438         return relPoint;
00439 }
00440 
00441 KoPoint KWTextFrameSet::internalToDocumentKnowingFrame( const QPoint &iPoint, KWFrame* theFrame ) const
00442 {
00443     // Convert LU to relative coordinates (pt), then call the real internalToDocumentKnowingFrame().
00444     return internalToDocumentKnowingFrame( m_doc->layoutUnitPtToPt( m_doc->pixelToPt( iPoint ) ), theFrame );
00445 }
00446 
00447 QPoint KWTextFrameSet::moveToPage( int currentPgNum, short int direction ) const
00448 {
00449     if ( !isVisible() || m_frames.isEmpty() )
00450         return QPoint();
00451     //kdDebug() << "KWTextFrameSet::moveToPage currentPgNum=" << currentPgNum << " direction=" << direction << endl;
00452     int num = currentPgNum + direction;
00453     int pages = m_doc->pageCount();
00454     for ( ; num >= 0 && num < pages ; num += direction )
00455     {
00456         //kdDebug() << "KWTextFrameSet::moveToPage num=" << num << " pages=" << pages << endl;
00457         // Find the first frame on page num
00458         if ( num < m_firstPage || num >= (int)m_framesInPage.size() + m_firstPage )
00459             continue; // No frame on that page
00460 
00461         //kdDebug() << "KWTextFrameSet::moveToPage ok for first frame in page " << num << endl;
00462         QPtrListIterator<KWFrame> frameIt( framesInPage( num ) );
00463         return QPoint( 0, m_doc->ptToLayoutUnitPixY( frameIt.current()->internalY() ) + 2 ); // found, ok.
00464     }
00465     // Not found. Go to top of first frame or bottom of last frame, depending on direction
00466     if ( direction < 0 )
00467         return QPoint( 0, m_doc->ptToLayoutUnitPixY( m_frames.getFirst()->internalY() ) + 2 );
00468     else
00469     {
00470         KWFrame * theFrame = m_frames.getLast();
00471         return QPoint( 0, m_doc->ptToLayoutUnitPixY( theFrame->internalY() + theFrame->innerHeight() ) );
00472     }
00473 }
00474 
00475 void KWTextFrameSet::drawContents( QPainter *p, const QRect & crect, const QColorGroup &cg,
00476                                    bool onlyChanged, bool resetChanged,
00477                                    KWFrameSetEdit *edit, KWViewMode *viewMode,
00478                                    KWFrameViewManager *fvm)
00479 {
00480     m_currentViewMode = viewMode;
00481     KWFrameSet::drawContents( p, crect, cg, onlyChanged, resetChanged, edit, viewMode, fvm );
00482 
00483     // Main textframeset: draw the footnote line if there are footnotes
00484     if ( isMainFrameset() && viewMode->hasFrames() )
00485     {
00486         // We stored the info "there's a footnote in this page" in the frame[s]
00487         // of the maintextframeset for that page. Usually one, but could be more
00488         // if there are columns. However we want to draw the line only once, so we
00489         // do it here and not in drawFrame (we'd have problems with cliprect anyway).
00490         if ( m_doc->footNoteSeparatorLineWidth() ==0.0)
00491             return;
00492 
00493         int pages = m_doc->pageCount();
00494         KWPage *page = m_doc->pageManager()->page(m_doc->pageManager()->startPage());
00495         double left = page->leftMargin();
00496         double pageWidth = page->width() - page->rightMargin() - left;
00497         double width = pageWidth * m_doc->footNoteSeparatorLineLength() / 100.0;
00498         int numColumns = m_doc->numColumns();
00499         for ( int pageNum = 0; pageNum < pages; pageNum++ )
00500         {
00501             //if ( viewMode->isPageVisible( pageNum ) )
00502             {
00503                 uint frameNum = pageNum * numColumns /*+ col  0 here*/;
00504                 if ( frameNum < frameCount() ) // not true on the "endnotes-only" page
00505                 {
00506                     KWFrame* frame = this->frame( frameNum ); // ## or use framesInPage ?
00507                     //kdDebug() << " Footnote line: page " << pageNum << " found frame " << frameNum << " drawFootNoteLine=" << frame->drawFootNoteLine() << endl;
00508                     if ( frame->drawFootNoteLine() )
00509                     {
00510                         double y = frame->bottomLeft().y() + m_doc->headerFooterInfo().ptFootNoteBodySpacing / 2;
00511                         KoRect rect( left, y, width, 0 ); // this rect is flat
00512                         switch( m_doc->footNoteSeparatorLinePosition())
00513                         {
00514                         case SLP_LEFT:
00515                             break;
00516                         case SLP_CENTERED:
00517                             rect = KoRect( pageWidth/2.0+left-width/2.0, y,width,0);
00518                             break;
00519                         case SLP_RIGHT:
00520                             rect = KoRect( pageWidth+left-width, y,width,0);
00521                             break;
00522                         }
00523 
00524                         QRect flatRect = viewMode->normalToView( m_doc->zoomRect( rect ) );
00525                         //kdDebug() << " KWTextFrameSet::drawContents rect=" << rect << " zoomed:" << flatRect << endl;
00526                         flatRect.setBottom( flatRect.top() + 1 ); // #!@!@!& QRect....
00527                         if ( flatRect.intersects( crect ) ) {
00528                             p->save();
00529                             QPen pen( KoTextFormat::defaultTextColor( p ),   // always in default fg color (and black when printing)
00530                                       KoBorder::zoomWidthY( m_doc->footNoteSeparatorLineWidth(), m_doc, 1 ) ); // penwidth = zoomIt( 2 pt )
00531                             switch( m_doc->footNoteSeparatorLineType())
00532                             {
00533                             case SLT_SOLID:
00534                                 pen.setStyle( SolidLine );
00535                                 break;
00536                             case SLT_DASH:
00537                                 pen.setStyle( DashLine );
00538                                 break;
00539                             case SLT_DOT:
00540                                 pen.setStyle( DotLine );
00541                                 break;
00542                             case SLT_DASH_DOT:
00543                                 pen.setStyle( DashDotLine );
00544                                 break;
00545                             case SLT_DASH_DOT_DOT:
00546                                 pen.setStyle( DashDotDotLine );
00547                                 break;
00548                             }
00549                             p->setPen( pen );
00550                             p->drawLine( flatRect.left(), flatRect.top(), flatRect.right(), flatRect.top() );
00551                             //kdDebug() << "  drawLine( " << flatRect.left() << ", " << flatRect.top() << ", " << flatRect.right() << ", " << flatRect.top() << endl;
00552                             p->restore();
00553                         }
00554                     }
00555                 }
00556             }
00557         }
00558     }
00559 }
00560 
00561 void KWTextFrameSet::drawFrame( KWFrame *theFrame, QPainter *painter, const QRect &fcrect, const QRect &crect,
00562                                 const QPoint& translationOffset,
00563                                 KWFrame *settingsFrame, const QColorGroup &cg, bool onlyChanged, bool resetChanged,
00564                                 KWFrameSetEdit *edit, KWViewMode *viewMode, bool drawUnderlyingFrames )
00565 {
00566     // Detect if text frame needs transparency painting, to save time if it's using SolidPattern
00567     // In theory this code should be in kwFrameSet, but currently only text frames obey m_backgroundColor.
00568     if ( theFrame )
00569     {
00570         drawUnderlyingFrames &= theFrame->isTransparent();
00571     }
00572     KWFrameSet::drawFrame( theFrame, painter, fcrect, crect, translationOffset, settingsFrame, cg, onlyChanged, resetChanged, edit, viewMode, drawUnderlyingFrames );
00573 }
00574 
00575 void KWTextFrameSet::drawFrameContents( KWFrame *theFrame, QPainter *painter, const QRect &r,
00576                                         const QColorGroup &cg, bool onlyChanged, bool resetChanged,
00577                                         KWFrameSetEdit *edit, KWViewMode *viewMode )
00578 {
00579     Q_ASSERT( r.isValid() );
00580     // In this method the painter is translated to the frame's coordinate system
00581     // (in the first frame (0,0) will be its topleft, in the second frame it will be (0,internalY) etc.
00582 
00583     //kdDebug(32001) << "KWTextFrameSet::drawFrameContents " << name() << "(frame " << frameFromPtr( theFrame ) << ") crect(r)=" << r << " onlyChanged=" << onlyChanged << endl;
00584     m_currentDrawnFrame = theFrame;
00585     if ( theFrame ) // 0L in the text viewmode
00586     {
00587         // Update variables for each frame (e.g. for page-number)
00588         // If more than KWPgNumVariable need this functionality, create an intermediary base class
00589         QPtrListIterator<KoTextCustomItem> cit( textDocument()->allCustomItems() );
00590         for ( ; cit.current() ; ++cit )
00591         {
00592             KWPgNumVariable * var = dynamic_cast<KWPgNumVariable *>( cit.current() );
00593             if ( var && !var->isDeleted() )
00594             {
00595                 QSize oldSize( var->width, var->height );
00596                 const int pageNumberOffset = kWordDocument()->variableCollection()->variableSetting()->startingPageNumber() - 1;
00597                 switch ( var->subType() )
00598                 {
00599                 case KWPgNumVariable::VST_PGNUM_CURRENT:
00600                     //kdDebug() << "KWTextFrameSet::drawFrame updating pgnum variable to " << theFrame->pageNumber()
00601                     //          << " and invalidating parag " << var->paragraph() << endl;
00602                     var->setPgNum( theFrame->pageNumber() + pageNumberOffset );
00603                     break;
00604                 case KWPgNumVariable::VST_CURRENT_SECTION:
00605                     var->setSectionTitle( kWordDocument()->sectionTitle( theFrame->pageNumber() ) );
00606                     break;
00607                 case KWPgNumVariable::VST_PGNUM_PREVIOUS:
00608                     var->setPgNum( QMAX(theFrame->pageNumber()-1,0) + pageNumberOffset );
00609                     break;
00610                 case KWPgNumVariable::VST_PGNUM_NEXT:
00611                     var->setPgNum( theFrame->pageNumber() + 1 + pageNumberOffset );
00612                     break;
00613                 }
00614 
00615                 var->resize();
00616                 QSize newSize( var->width, var->height );
00617                 if ( oldSize != newSize )
00618                     var->paragraph()->invalidate( 0 ); // size has changed -> need reformatting !
00619                 var->paragraph()->setChanged( true );
00620             }
00621         }
00622     }
00623 
00624     KoTextCursor * cursor = edit ? (dynamic_cast<KWTextFrameSetEdit *>(edit) ? static_cast<KWTextFrameSetEdit *>(edit)->cursor() : 0) : 0;
00625     uint drawingFlags = 0;
00626     if ( viewMode->drawSelections() )
00627         drawingFlags |= KoTextDocument::DrawSelections;
00628     if ( !viewMode->drawFrameBackground() )
00629         drawingFlags |= KoTextDocument::TransparentBackground;
00630     if ( m_doc->backgroundSpellCheckEnabled() )
00631         drawingFlags |= KoTextDocument::DrawMisspelledLine;
00632     if ( m_doc->viewFormattingChars() )
00633         drawingFlags |= KoTextDocument::DrawFormattingChars;
00634 
00635     //kdDebug(32001) << "KWTextFrameSet::drawFrame calling drawWYSIWYG. cg base color:" << cg.brush( QColorGroup::Base) << endl;
00636     KoTextParag * lastFormatted = textDocument()->drawWYSIWYG(
00637         painter, r.x(), r.y(), r.width(), r.height(),
00638         cg, kWordDocument(),
00639         onlyChanged, false, cursor, resetChanged, drawingFlags );
00640 
00641     // The last paragraph of this frame might have a bit in the next frame too.
00642     // In that case, and if we're only drawing changed paragraphs, (and resetting changed),
00643     // we have to set changed to true again, to draw the bottom of the parag in the next frame.
00644     if ( onlyChanged && resetChanged )
00645     {
00646         // Finding the "last parag of the frame" is a bit tricky.
00647         // It's usually the one before lastFormatted, except if it's actually lastParag :}  [see KoTextDocument::draw]
00648         KoTextParag * lastDrawn = lastFormatted->prev();
00649         if ( lastFormatted == textDocument()->lastParag() && ( !lastDrawn || m_doc->layoutUnitToPixelY( lastDrawn->rect().bottom() ) < r.bottom() ) )
00650             lastDrawn = lastFormatted;
00651 
00652         //kdDebug(32002) << "KWTextFrameSet::drawFrame drawn. onlyChanged=" << onlyChanged << " resetChanged=" << resetChanged << " lastDrawn=" << lastDrawn->paragId() << " lastDrawn's bottom:" << lastDrawn->rect().bottom() << " r.bottom=" << r.bottom() << endl;
00653         if ( lastDrawn && m_doc->layoutUnitToPixelY( lastDrawn->rect().bottom() ) > r.bottom() )
00654         {
00655             //kdDebug(32002) << "KWTextFrameSet::drawFrame setting lastDrawn " << lastDrawn->paragId() << " to changed" << endl;
00656             lastDrawn->setChanged( true );
00657         }
00658     }
00659 
00660     // NOTE: QTextView sets m_lastFormatted to lastFormatted here
00661     // But when scrolling up, this causes to reformat a lot of stuff for nothing.
00662     // And updateViewArea takes care of formatting things before we even arrive here.
00663 
00664     // Blank area under the very last paragraph - QRT draws it up to textdoc->height,
00665     // we have to draw it from there up to the bottom of the last frame.
00666     if ( ( !lastFormatted || lastFormatted == textDocument()->lastParag() )
00667          && viewMode->drawFrameBackground() )
00668     {
00669         // This is drawing code, so we convert everything to pixels
00670         int docHeight = textDocument()->lastParag()->pixelRect(m_doc).bottom() + 1;
00671         //QRect frameRect = m_doc->zoomRect( (theFrame->innerRect()) );
00672 
00673         QSize availSize = viewMode->availableSizeForText( this );
00674         QRect blank( 0, docHeight, availSize.width(), availSize.height() /*+ frameRect.height() ?? */ - docHeight );
00675         //kdDebug(32002) << this << " Blank area: " << blank << endl;
00676         painter->fillRect( blank, cg.brush( QColorGroup::Base ) );
00677         // for debugging :)
00678         //painter->setPen( QPen(Qt::blue, 1, DashLine) );  painter->drawRect( blank );
00679     }
00680     m_currentDrawnFrame = 0L;
00681 }
00682 
00683 void KWTextFrameSet::drawCursor( QPainter *p, KoTextCursor *cursor, bool cursorVisible, KWCanvas *canvas, KWFrame *theFrame )
00684 {
00685     // This redraws the paragraph where the cursor is - with a small clip region around the cursor
00686     m_currentViewMode = canvas->viewMode();
00687     bool hasFrames = m_currentViewMode->hasFrames();
00688     m_currentDrawnFrame = theFrame;
00689 
00690     QRect normalFrameRect;
00691     if (hasFrames)
00692         normalFrameRect = m_doc->zoomRect( theFrame->innerRect() );
00693     else
00694         normalFrameRect = QRect( QPoint(0, 0), m_currentViewMode->contentsSize() );
00695 
00696     KoTextParag* parag = cursor->parag();
00697     QPoint topLeft = parag->rect().topLeft();         // in QRT coords
00698     int lineY;
00699     // Cursor height, in pixels
00700     int cursorHeight = m_doc->layoutUnitToPixelY( topLeft.y(), parag->lineHeightOfChar( cursor->index(), 0, &lineY ) );
00701     QPoint iPoint( topLeft.x() + cursor->x(),
00702                    topLeft.y() + lineY );
00703 
00704 #ifdef DEBUG_CURSOR
00705     kdDebug() << "KWTextFrameSet::drawCursor topLeft=" << topLeft.x() << "," << topLeft.y()
00706               << " x=" << cursor->x() << " y=" << lineY << endl;
00707     kdDebug() << "KWTextFrameSet::drawCursor iPoint=" << iPoint.x() << "," << iPoint.y()
00708               << "   cursorHeight=" << cursorHeight << endl;
00709 #endif
00710     KoPoint dPoint;
00711     KoPoint hintDPoint = theFrame ? theFrame->innerRect().topLeft() : KoPoint();
00712     if ( internalToDocumentWithHint( iPoint, dPoint, hintDPoint ) )
00713     {
00714 #ifdef DEBUG_CURSOR
00715         kdDebug() << " dPoint(doc, pts)=" << dPoint.x() << "," << dPoint.y() << endl;
00716         QPoint debugPt = m_doc->zoomPoint( dPoint );
00717         kdDebug() << " zoomed dPoint(doc, pixels)=" << debugPt.x() << "," << debugPt.y() << endl;
00718 #endif
00719         QPoint vPoint = m_currentViewMode->normalToView( m_doc->zoomPoint( dPoint ) ); // from doc to view contents
00720 #ifdef DEBUG_CURSOR
00721         kdDebug() << " vPoint(view, pixels)=" << vPoint.x() << "," << vPoint.y() << endl;
00722 #endif
00723         // from now on, iPoint will be in pixels
00724         iPoint = m_doc->layoutUnitToPixel( iPoint );
00725         //int xadj = parag->at( cursor->index() )->pixelxadj;
00726 #ifdef DEBUG_CURSOR
00727         //kdDebug() << "     iPoint in pixels : " << iPoint.x() << "," << iPoint.y() << "     will add xadj=" << xadj << endl;
00728 #endif
00729         //iPoint.rx() += xadj;
00730         //vPoint.rx() += xadj;
00731         // very small clipping around the cursor
00732         QRect clip( vPoint.x() - 5, vPoint.y(), 10, cursorHeight );
00733 
00734 #ifdef DEBUG_CURSOR
00735         kdDebug() << " clip(view, before intersect)=" << clip << endl;
00736 #endif
00737 
00738         QRect viewFrameRect = m_currentViewMode->normalToView( normalFrameRect );
00739         clip &= viewFrameRect; // clip to frame
00740 #ifdef DEBUG_CURSOR
00741         kdDebug() << "KWTextFrameSet::drawCursor normalFrameRect=" << normalFrameRect
00742                   << " clip(view, after intersect)=" << clip << endl;
00743 #endif
00744 
00745         QRegion reg;
00746         if ( hasFrames ) {
00747             reg = frameClipRegion( p, theFrame, clip, m_currentViewMode );
00748             if ( !isFloating() ) // problem with multiparent inline frames
00749                 reg &= p->xForm( viewFrameRect );
00750         }
00751 
00752         if ( !hasFrames || !reg.isEmpty() )
00753         {
00754 #ifdef DEBUG_CURSOR
00755             // for debug only!
00756             //p->fillRect( clip, QBrush( Qt::red, QBrush::Dense3Pattern ) );
00757 #endif
00758 
00759             p->save();
00760             QColorGroup cg = QApplication::palette().active();
00761 
00762             if ( hasFrames )
00763             {
00764                 p->setClipRegion( reg );
00765                 // translate to qrt coords - after setting the clip region !
00766                 // see internalToDocumentWithHint
00767                 int translationX = viewFrameRect.left();
00768                 int translationY = viewFrameRect.top() - m_doc->zoomItY( theFrame->internalY() );
00769 #ifdef DEBUG_CURSOR
00770                 kdDebug() << "        translating Y by viewFrameRect.top()-internalY in pixelY= " << viewFrameRect.top() << "-" << m_doc->zoomItY( theFrame->internalY() ) << "=" << viewFrameRect.top() - m_doc->zoomItY( theFrame->internalY() ) << endl;
00771 #endif
00772                 p->translate( translationX, translationY );
00773                 p->setBrushOrigin( p->brushOrigin().x() + translationX, p->brushOrigin().y() + translationY );
00774 
00775                 // The settings come from this frame
00776                 KWFrame * settings = settingsFrame( theFrame );
00777 
00778                 QBrush bgBrush( settings->backgroundColor() );
00779                 bgBrush.setColor( KWDocument::resolveBgColor( bgBrush.color(), p ) );
00780                 cg.setBrush( QColorGroup::Base, bgBrush );
00781                 // color of cursor, the inverse of the frame background
00782                 QColor background = bgBrush.color();
00783                 cg.setColor(QColorGroup::Text, QColor( 255 - background.red(),
00784                     255 - background.green(), 255 - background.blue()) );
00785             }
00786             else if(dynamic_cast<KWViewModeText *>(m_currentViewMode) != 0)
00787                 p->translate( KWViewModeText::OFFSET, 0 );
00788 
00789             QPixmap *pix = 0;
00790             uint drawingFlags = KoTextDocument::DrawSelections;
00791             if ( m_doc->backgroundSpellCheckEnabled() )
00792                 drawingFlags |= KoTextDocument::DrawMisspelledLine;
00793             if ( m_doc->viewFormattingChars() )
00794                 drawingFlags |= KoTextDocument::DrawFormattingChars;
00795 
00796             // To force the drawing to happen:
00797             bool wasChanged = parag->hasChanged();
00798             int oldLineChanged = parag->lineChanged();
00799             int line; // line number
00800             parag->lineStartOfChar( cursor->index(), 0, &line );
00801             parag->setChanged( false ); // not all changed, only from a given line
00802             parag->setLineChanged( line );
00803 
00804             textDocument()->drawParagWYSIWYG(
00805                 p, parag,
00806                 QMAX(0, iPoint.x() - 5), // negative values create problems
00807                 iPoint.y(), clip.width(), clip.height(),
00808                 pix, cg, m_doc, // TODO view's zoom handler
00809                 cursorVisible, cursor, FALSE /*resetChanged*/, drawingFlags );
00810 
00811             if ( wasChanged )      // Maybe we have more changes to draw, than those in the small cliprect
00812                 parag->setLineChanged( oldLineChanged ); // -1 = all
00813             else
00814                 parag->setChanged( false );
00815 
00816             p->restore();
00817 
00818             //XIM Position
00819             QPoint ximPoint = vPoint;
00820             canvas->setXimPosition( ximPoint.x(), ximPoint.y(),
00821                                     0, cursorHeight - parag->lineSpacing( line ) );
00822         }
00823     }
00824     m_currentDrawnFrame = 0L;
00825 }
00826 
00827 void KWTextFrameSet::layout()
00828 {
00829     invalidate();
00830     // Get the thing going though, repainting doesn't call formatMore
00831     m_textobj->formatMore( 2 );
00832 }
00833 
00834 void KWTextFrameSet::invalidate()
00835 {
00836     //kdDebug() << "KWTextFrameSet::invalidate " << name() << endl;
00837     m_textobj->setLastFormattedParag( textDocument()->firstParag() );
00838     textDocument()->invalidate(); // lazy layout, real update follows upon next repaint
00839 }
00840 
00841 void KWTextFrameSet::slotRepaintChanged()
00842 {
00843     emit repaintChanged( this );
00844 }
00845 
00846 int KWTextFrameSet::paragraphs()
00847 {
00848     int paragraphs = 0;
00849     KoTextParag * parag = textDocument()->firstParag();
00850     for ( ; parag ; parag = parag->next() )
00851         paragraphs++;
00852     return paragraphs;
00853 }
00854 
00855 int KWTextFrameSet::paragraphsSelected()
00856 {
00857     int paragraphs = 0;
00858     KoTextParag *parag = textDocument()->firstParag();
00859     for ( ; parag ; parag = parag->next() ) {
00860         if ( parag->hasSelection( KoTextDocument::Standard ) )
00861             paragraphs++;
00862     }
00863     return paragraphs;
00864 }
00865 
00866 
00867 bool KWTextFrameSet::statistics( QProgressDialog *progress, ulong & charsWithSpace, ulong & charsWithoutSpace, ulong & words,
00868     ulong & sentences, ulong & syllables, ulong & lines, bool selected )
00869 {
00870     return m_textobj->statistics( progress, charsWithSpace, charsWithoutSpace, words, sentences, syllables, lines, selected );
00871 }
00872 
00873 // Only interested in the body textframeset, not in header/footer
00874 #define kdDebugBody(area) if ( frameSetInfo() == FI_BODY ) kdDebug(area)
00875 
00876 QValueList<KWFrame*> KWTextFrameSet::framesFromTo( int y1, int y2 ) const
00877 {
00878     QValueList<KWFrame*> framesList;
00879     KoPoint pt;
00880     KWFrame * firstFrame = internalToDocument( QPoint(0, y1), pt );
00881     if ( !firstFrame )
00882         return framesList;
00883     framesList.append( firstFrame );
00884     uint frameIndex = const_cast<KWTextFrameSet *>(this)->m_frames.findRef( firstFrame );
00885     while ( ++frameIndex < m_frames.count() ) {
00886         KWFrame* f = frame( frameIndex );
00887         if ( f->internalY() > y2 ) // too far down, we're done
00888             break;
00889         framesList.append( f );
00890     }
00891     return framesList;
00892 }
00893 
00894 // Helper for adjust*. There are 3 ways to use this method.
00895 // 1) marginLeft and marginRight set -> determination of left and right margins for adjustMargins
00896 // 2) marginRight set -> determination of right margin for adjustRMargin
00897 // 3) breakBegin, breakEnd set -> check whether we should jump over some frames
00898 //                                                  [when there is not enough space besides them]
00899 // reqMinWidth is the width that the formatter wants to use (current char/item)
00900 // validHeight is set to the height where marginLeft/marginRight applies (TODO)
00901 void KWTextFrameSet::getMargins( int yp, int h, int reqMinWidth,
00902                                  int* marginLeft, int* marginRight, int* pageWidth,
00903                                  int* validHeight,
00904                                  int* breakBegin, int* breakEnd, KoTextParag* parag )
00905 {
00906     // paragLeftMargin will be used as the minimum width needed for the parag,
00907     // to "see" the parag.
00908     // So we only apply the first line margin if it increases that width, i.e. if > 0.
00909     // Otherwise only the first line might be visible, in a narrow passage.
00910     int paragLeftMargin = parag ? parag->leftMargin() : 0;
00911     if ( parag && !parag->string()->isRightToLeft() && parag->firstLineMargin() > 0 )
00912         paragLeftMargin += parag->firstLineMargin();
00913 #ifdef DEBUG_MARGINS
00914     kdDebugBody(32002) << "  KWTextFrameSet " << this << "(" << name() << ") getMargins yp=" << yp
00915                        << " h=" << h << " called by "
00916                        << (marginLeft && marginRight ? "adjustMargins" : "formatVertically")
00917                        << " paragLeftMargin=" << paragLeftMargin
00918                        << endl;
00919     // Both or none...
00920     if (breakBegin) assert(breakEnd);
00921     if (breakEnd) assert(breakBegin);
00922     // Idem
00923     if ( marginLeft ) { assert( marginRight ); assert( pageWidth ); }
00924 #endif
00925 
00926     // List of text frames holding the paragraph (yp,yp+h)
00927     // Usually there is only one, but you can have a paragraph
00928     // starting in one frame/column and ending in another one.
00929     QValueList<KWFrame*> textFrames = framesFromTo( yp, yp + h );
00930     if (textFrames.isEmpty())
00931     {
00932 #ifdef DEBUG_MARGINS
00933         kdDebug(32002) << "  getMargins: internalToDocument returned no text frames for y1=" << yp << " y2=" << yp + h << " ->aborting with 0 margins" << endl;
00934 #endif
00935         // frame == 0 happens when the parag is under the last frame.
00936         // On an auto-resizable frame, we know the frame will grow so we can go ahead
00937         // and use its width.
00938         if ( !m_frames.isEmpty() && m_frames.last()->frameBehavior() == KWFrame::AutoExtendFrame )
00939         {
00940             textFrames.append( m_frames.last() );
00941         }
00942         else
00943         {
00944             // On auto-create-new-frame, this means the parag is on a not-yet-created page
00945             // (formatMore will notice afterwards)
00946             // Abort then, no need to return precise values
00947             // We also abort in the third case (Ignore)
00948             if ( validHeight )
00949                 *validHeight = 0;
00950             return;
00951         }
00952     }
00953     else
00954     {
00955 
00956 #ifdef DEBUG_MARGINS
00957         kdDebugBody(32002) << "  getMargins: internalToDocument returned " << textFrames.count() << " frames holding this paragraph" << endl;
00958 #endif
00959     }
00960     if ( validHeight )
00961         *validHeight = h; // TODO
00962 
00963     // Everything from there is in layout units
00964     // Note: it is very important that this method works in internal coordinates.
00965     // Otherwise, parags broken at the line-level (e.g. between two columns) are seen
00966     // as still in one piece, and we miss the frames in the 2nd column.
00967     int from = 0;
00968     // TODO support for variable width... maybe it's enough to take the max here
00969     int to = m_doc->ptToLayoutUnitPixX( textFrames.first()->innerWidth() );
00970     if ( pageWidth )
00971         *pageWidth = to;
00972     bool init = false;
00973 
00974 #ifdef DEBUG_MARGINS
00975     kdDebugBody(32002) << "  getMargins: looking for frames between " << yp << " and " << yp+h << " (internal coords)" << endl;
00976 #endif
00977     if ( m_doc->layoutViewMode()->shouldAdjustMargins() )
00978     {
00979         // Principle: for every frame on top at this height, we'll move from and to
00980         // towards each other. The text flows between 'from' and 'to'
00981         for ( QValueList<KWFrame*>::const_iterator txtit = textFrames.begin(), txtend = textFrames.end() ; txtit != txtend ; ++txtit ) {
00982             KWFrame* theFrame = *txtit;
00983             Q_ASSERT( theFrame->frameStack() );
00984             QValueList<KWFrame*> onTop = theFrame->frameStack()->framesOnTop();
00985             for (QValueListIterator<KWFrame*> fIt = onTop.begin(); from < to && fIt != onTop.end(); ++fIt )
00986             {
00987                 if ( (*fIt)->runAround() == KWFrame::RA_BOUNDINGRECT )
00988                 {
00989                     KoRect rectOnTop = theFrame->intersect( (*fIt)->runAroundRect() );
00990 #ifdef DEBUG_MARGINS
00991                     kdDebugBody(32002) << "   getMargins found frame on top " << (*fIt)->frameSet()->name() << " with rect-on-top at (normal coords) " << rectOnTop << endl;
00992 #endif
00993                     QPoint iTop, iBottom; // top and bottom of intersection in internal coordinates
00994 
00995                     if ( documentToInternal( rectOnTop.topLeft(), iTop ) &&
00996                          iTop.y() <= yp + h && // optimization
00997                          documentToInternal( rectOnTop.bottomRight(), iBottom ) )
00998                     {
00999 #ifdef DEBUG_MARGINS
01000                         kdDebugBody(32002) << "      in internal coords: " << QRect(iTop,iBottom) << endl;
01001 #endif
01002                         // Look for intersection between yp -- yp+h  and iTop -- iBottom
01003                         if ( QMAX( yp, iTop.y() ) <= QMIN( yp+h, iBottom.y() ) )
01004                         {
01005 #ifdef DEBUG_MARGINS
01006                             kdDebugBody(32002) << "   getMargins iTop=" << iTop.x() << "," << iTop.y()
01007                                                << " iBottom=" << iBottom.x() << "," << iBottom.y() << endl;
01008 #endif
01009                             int availLeft = QMAX( 0, iTop.x() - from );
01010                             int availRight = QMAX( 0, to - iBottom.x() );
01011 #ifdef DEBUG_MARGINS
01012                             kdDebugBody(32002) << "   getMargins availLeft=" << availLeft
01013                                                << " availRight=" << availRight << endl;
01014 #endif
01015                             bool chooseLeft = false;
01016                             switch ( (*fIt)->runAroundSide() ) {
01017                             case KWFrame::RA_LEFT:
01018                                 chooseLeft = true;
01019                                 break;
01020                             case KWFrame::RA_RIGHT:
01021                                 break; // chooseLeft remains false
01022                             case KWFrame::RA_BIGGEST:
01023                                 chooseLeft = ( availLeft > availRight ); // choose the max
01024                             };
01025 
01026                             if ( chooseLeft )
01027                                 // flow text at the left of the frame
01028                                 to = QMIN( to, from + availLeft - 1 );  // can only go left -> QMIN
01029                             else
01030                                 // flow text at the right of the frame
01031                                 from = QMAX( from, to - availRight + 1 ); // can only go right -> QMAX
01032 
01033 #ifdef DEBUG_MARGINS
01034                             kdDebugBody(32002) << "   getMargins from=" << from << " to=" << to << endl;
01035 #endif
01036                             // If the available space is too small, give up on it
01037                             if ( to - from < reqMinWidth + paragLeftMargin )
01038                             {
01039 #ifdef DEBUG_MARGINS
01040                                 kdDebugBody(32002) << "   smaller than minimum=" << m_doc->ptToLayoutUnitPixX( 15 ) + paragLeftMargin << endl;
01041 #endif
01042                                 from = to;
01043                             }
01044 
01045                             if ( breakEnd && from == to ) // no-space case
01046                             {
01047                                 if ( !init ) // first time
01048                                 {
01049                                     init = true;
01050                                     *breakBegin = iTop.y();
01051                                     *breakEnd = iBottom.y();
01052                                 }
01053                                 else
01054                                 {
01055                                     *breakBegin = QMIN( *breakBegin, iTop.y() );
01056                                     *breakEnd = QMAX( *breakEnd, iBottom.y() );
01057                                 }
01058 #ifdef DEBUG_MARGINS
01059                                 kdDebugBody(32002) << "   getMargins iBottom.y=" << iBottom.y()
01060                                                    << " breakBegin=" << *breakBegin
01061                                                    << " breakEnd=" << *breakEnd << endl;
01062 #endif
01063                             }
01064                         } // else no intersection
01065                     }// else we got a 0L, or the iTop.y()<=yp+h test didn't work - wrong debug output
01066                     // kdDebugBody(32002) << "   gerMargins: normalToInternal returned 0L" << endl;
01067                 }
01068             }
01069         }
01070     }
01071     if ( marginLeft /*&& marginRight && pageWidth  -- implicit*/ )
01072     {
01073 #ifdef DEBUG_MARGINS
01074         kdDebugBody(32002) << "   getMargins done. from=" << from << " to=" << to << endl;
01075 #endif
01076         if ( from == to ) {
01077             from = 0;
01078             to = *pageWidth;
01079         }
01080 
01081         if ( marginLeft )
01082             *marginLeft += from;
01083         if ( marginRight )
01084         {
01085 #ifdef DEBUG_MARGINS
01086             kdDebug(32002) << "    getMargins " << name()
01087                            << " page width=" << *pageWidth
01088                            << " to=" << to << endl;
01089 #endif
01090             *marginRight += *pageWidth - to;
01091         }
01092     }
01093 }
01094 
01095 void KWTextFrameSet::adjustMargins( int yp, int h, int reqMinWidth, int& leftMargin, int& rightMargin, int& pageWidth, KoTextParag* parag )
01096 {
01097 #ifdef DEBUG_MARGINS
01098     kdDebugBody(32002) << "KWTextFrameSet::adjustMargins called for paragraph " << (parag?parag->paragId():-1) << endl;
01099 #endif
01100     int validHeight; // currently ignored (TODO)
01101     getMargins( yp, h, reqMinWidth, &leftMargin, &rightMargin, &pageWidth, &validHeight, 0L, 0L, parag );
01102 #ifdef DEBUG_MARGINS
01103     kdDebugBody(32002) << "KWTextFrameSet::adjustMargins(yp=" << yp << " h=" << h << " reqMinWidth=" << reqMinWidth << " returning"
01104                        << " leftMargin=" << leftMargin << " rightMargin=" << rightMargin
01105                        << " valid from " << yp << " to " << yp+validHeight << endl;
01106 #endif
01107 }
01108 
01109 // helper for formatVertically
01110 bool KWTextFrameSet::checkVerticalBreak( int & yp, int & hp, KoTextParag * parag, bool linesTogether, int breakBegin, int breakEnd )
01111 {
01112     // We need the "+1" here because when skipping a frame on top, we want to be _under_
01113     // its bottom. Without the +1, we hit the frame again on the next adjustLMargin call.
01114 
01115     // Check for intersection between the parag (yp -- yp+hp) and the break area (breakBegin -- breakEnd)
01116     if ( QMAX( yp, breakBegin ) <= QMIN( yp+hp, breakEnd ) )
01117     {
01118         if ( !parag || linesTogether ) // Paragraph-level breaking
01119         {
01120 #ifdef DEBUG_FORMATVERTICALLY
01121             kdDebug(32002) << "checkVerticalBreak ADJUSTING yp=" << yp << " hp=" << hp
01122                            << " breakEnd+2 [new value for yp]=" << breakEnd+2 << endl;
01123 #endif
01124             yp = breakEnd + 1;
01125             return true;
01126         }
01127         else // Line-level breaking
01128         {
01129             QMap<int, KoTextParagLineStart*>& lineStarts = parag->lineStartList();
01130 #ifdef DEBUG_FORMATVERTICALLY
01131             kdDebug(32002) << "checkVerticalBreak parag " << parag->paragId()
01132                            << ". lineStarts has " << lineStarts.count()
01133                            << " items" << endl;
01134 #endif
01135             int dy = 0;
01136             int line = 0;
01137             QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
01138             for ( ; it != lineStarts.end() ; ++it, ++line )
01139             {
01140                 KoTextParagLineStart * ls = it.data();
01141                 Q_ASSERT( ls );
01142                 int y = parag->rect().y() + ls->y;
01143 #ifdef DEBUG_FORMATVERTICALLY
01144                 kdDebug(32002) << "checkVerticalBreak parag " << parag->paragId()
01145                                << " line " << line << " ls->y=" << ls->y
01146                                << " ls->h=" << ls->h << " y=" << y
01147                                << " breakBegin=" << breakBegin
01148                                << " breakEnd=" << breakEnd << endl;
01149 #endif
01150                 if ( !dy )
01151                 {
01152                     if ( QMAX( y, breakBegin ) <= QMIN( y + ls->h, breakEnd ) )
01153                     {
01154                         if ( line == 0 ) // First line ? It's like a paragraph breaking then
01155                         {
01156 #ifdef DEBUG_FORMATVERTICALLY
01157                             kdDebug(32002) << "checkVerticalBreak parag " << parag->paragId()
01158                                            << " BREAKING first line -> parag break" << endl;
01159 #endif
01160                             yp = breakEnd + 1;
01161                             return true;
01162                         }
01163                         dy = breakEnd + 1 - y;
01164                         ls->y = breakEnd + 1 - parag->rect().y();
01165 #ifdef DEBUG_FORMATVERTICALLY
01166                         kdDebug(32002) << "checkVerticalBreak parag " << parag->paragId()
01167                                        << " BREAKING at line " << line << " dy=" << dy << "  Setting ls->y to " << ls->y << ", y=" << breakEnd << endl;
01168 #endif
01169                     }
01170                 }
01171                 else
01172                 {
01173                     ls->y += dy;
01174 #ifdef DEBUG_FORMATVERTICALLY
01175                     if ( dy )
01176                         kdDebug(32002) << "                   moving down to position ls->y=" << ls->y << endl;
01177 #endif
01178                 }
01179             }
01180             parag->setMovedDown( true );
01181             parag->setHeight( hp + dy );
01182 #ifdef DEBUG_FORMATVERTICALLY
01183             kdDebug(32002) << "Paragraph height set to " << hp+dy << endl;
01184 #endif
01185             hp += dy;
01186             return true;
01187         } // End of line-level breaking
01188     }
01189     return false;
01190 }
01191 
01192 int KWTextFrameSet::formatVertically( KoTextParag * _parag, const QRect& paragRect )
01193 {
01194     // WARNING: in this whole method parag can be 0. See adjustFlow()
01195     KWTextParag *parag = static_cast<KWTextParag *>( _parag );
01196     if ( !m_doc->layoutViewMode()->shouldFormatVertically() )
01197     {
01198         return 0;
01199     }
01200 
01201 #ifdef DEBUG_FORMATVERTICALLY
01202     kdDebugBody(32002) << "KWTextFrameSet::formatVertically called for paragraph " << (parag?parag->paragId():-1) << endl;
01203 #endif
01204 
01205     int yp = paragRect.y();
01206     int hp = paragRect.height();
01207     int oldHeight = hp;
01208     int oldY = yp;
01209 
01210     // This is called by KoTextFormatter to apply "vertical breaks".
01211     // End of frames/pages lead to those "vertical breaks".
01212     // What we do, is adjust the Y accordingly,
01213     // to implement page-break at the paragraph level and at the line level.
01214     // It's cumulative (the space of one break will be included in the further
01215     // paragraph's y position), which makes it easy to implement.
01216     // But don't forget that formatVertically is called twice for every parag, since the formatting
01217     // is re-done after moving down.
01218 
01219     bool linesTogether = parag ? parag->linesTogether() : true;
01220     bool hardFrameBreak = parag ? parag->hardFrameBreakBefore() : false;
01221     if ( !hardFrameBreak && parag && parag->prev() )
01222         hardFrameBreak = static_cast<KWTextParag *>(parag->prev())->hardFrameBreakAfter();
01223 
01224 #ifdef DEBUG_FORMATVERTICALLY
01225     kdDebugBody(32002) << "KWTextFrameSet::formatVertically parag=" << parag
01226                        << " linesTogether=" << linesTogether << " hardFrameBreak=" << hardFrameBreak
01227                        << " yp=" << yp
01228                        << " hp=" << hp << endl;
01229 #endif
01230 
01231     int totalHeight = 0;
01232     QPtrListIterator<KWFrame> frameIt( frameIterator() );
01233     for ( ; frameIt.current(); ++frameIt )
01234     {
01235         int frameHeight = kWordDocument()->ptToLayoutUnitPixY( frameIt.current()->innerHeight() );
01236         int bottom = totalHeight + frameHeight;
01237         // Only skip bottom of frame if there is a next one or if there'll be another one created.
01238         // ( Not for header/footer, for instance. )
01239         bool check = frameIt.atLast() && frameIt.current()->frameBehavior() == KWFrame::AutoCreateNewFrame;
01240         if ( !check )
01241         {
01242             // ## TODO optimize this [maybe we should simply start from the end in the main loop?]
01243             // Or cache the attribute ( e.g. "frame->hasCopy()" ).
01244             QPtrListIterator<KWFrame> nextFrame( frameIt );
01245             while ( !check && !nextFrame.atLast() )
01246             {
01247                 ++nextFrame;
01248                 if ( !nextFrame.current()->isCopy() )
01249                     check = true; // Found a frame after us that isn't a copy => we have somewhere for our overflow
01250             }
01251         }
01252 
01253         if ( check )
01254         {
01255             if ( hardFrameBreak && yp > totalHeight && yp < bottom && !parag->wasMovedDown() )
01256             {
01257                 // The paragraph wants a frame break before it, and is in the current frame
01258                 // The last check is for whether we did the frame break already
01259                 // (formatVertically is called twice for each paragraph, if a break was done)
01260                 yp = bottom /*+ 2*/;
01261 #ifdef DEBUG_FORMATVERTICALLY
01262                 kdDebug(32002) << "KWTextFrameSet::formatVertically -> HARD FRAME BREAK" << endl;
01263                 kdDebug(32002) << "KWTextFrameSet::formatVertically yp now " << yp << endl;
01264 #endif
01265                 break;
01266             }
01267 
01268 #ifdef DEBUG_FORMATVERTICALLY
01269             kdDebug(32002) << " formatVertically: frameHeight=" << frameHeight << " bottom=" << bottom << endl;
01270 #endif
01271             // don't move down parags that have only one line and are bigger than the page (e.g. floating tables)
01272             if ( hp < frameHeight || ( parag && parag->lineStartList().count() > 1 ) )
01273             {
01274                 // breakBegin==breakEnd==bottom, since the next frame's top is the same as bottom, in QRT coords.
01275                 (void) checkVerticalBreak( yp, hp, parag, linesTogether, bottom, bottom );
01276                 // Some people write a single paragraph over 3 frames! So we have to keep looking, that's why we ignore the return value
01277             }
01278 
01279         }
01280         if ( yp+hp < bottom )
01281             break; // we've been past the parag, so stop here
01282         totalHeight = bottom;
01283     }
01284 
01285 #ifdef DEBUG_FORMATVERTICALLY
01286     kdDebug(32002) << " formatVertically: now looking at RA_SKIP" << endl;
01287 #endif
01288 
01289 
01290     // Another case for a vertical break is frames with the RA_SKIP flag
01291     // Currently looking at all frames on top of all of our frames... maybe optimize better
01292     frameIt.toFirst();
01293     for ( ; frameIt.current(); ++frameIt )
01294     {
01295         Q_ASSERT( frameIt.current()->frameStack() );
01296         QValueList<KWFrame*> onTop = frameIt.current()->frameStack()->framesOnTop();
01297         for (QValueListIterator<KWFrame*> fIt = onTop.begin(); fIt != onTop.end(); ++fIt )
01298         {
01299             if ( (*fIt)->runAround() == KWFrame::RA_SKIP )
01300             {
01301                 KoRect rectOnTop = frameIt.current()->intersect( (*fIt)->runAroundRect() );
01302                 QPoint iTop, iBottom; // top and bottom in internal coordinates
01303                 if ( documentToInternal( rectOnTop.topLeft(), iTop ) &&
01304                      iTop.y() <= yp + hp &&
01305                      documentToInternal( rectOnTop.bottomLeft(), iBottom ) &&
01306                      checkVerticalBreak( yp, hp, parag, linesTogether,
01307                                          iTop.y(), iBottom.y() ) )
01308                 {
01309                     kdDebug(32002) << "KWTextFrameSet::formatVertically breaking around RA_SKIP frame yp="<<yp<<" hp=" << hp << endl;
01310                     // We don't "break;" here because there could be another such frame below the first one
01311                     // We assume that the frames on top are in order ( top to bottom ), btw.
01312                     // They should be, since updateFrames reorders before updating frames-on-top
01313                 }
01314             }
01315         }
01316     }
01317 
01318     // And the last case for a vertical break is RA_BOUNDINGRECT frames that
01319     // leave no space by their side for any text (e.g. most tables)
01320     int breakBegin = 0;
01321     int breakEnd = 0;
01322     int reqMinWidth = parag ? parag->string()->at( 0 ).width : 0;
01323     getMargins( yp, hp, reqMinWidth, 0L, 0L, 0L, 0L, &breakBegin, &breakEnd, parag );
01324     if ( breakEnd )
01325     {
01326         kdDebug(32002) << "KWTextFrameSet("<<name()<<")::formatVertically no-space case. breakBegin=" << breakBegin
01327                        << " breakEnd=" << breakEnd << " hp=" << hp << endl;
01328         Q_ASSERT( breakBegin <= breakEnd );
01329         if ( checkVerticalBreak( yp, hp, parag, linesTogether, breakBegin, breakEnd ) )
01330             ; //kdDebug(32002) << "checkVerticalBreak ok." << endl;
01331         else // shouldn't happen
01332             kdWarning(32002) << "checkVerticalBreak didn't find it" << endl;
01333     }
01334 
01335     // ## TODO loop around those three methods until we don't move anymore ?
01336 
01337     if ( parag )
01338     {
01339         if ( hp != oldHeight )
01340             parag->setHeight( hp );
01341         if ( yp != oldY ) {
01342             QRect r = parag->rect();
01343             r.moveBy( 0, yp - oldY );
01344             parag->setRect( r );
01345             parag->setMovedDown( true );
01346         }
01347     }
01348 #ifdef DEBUG_FORMATVERTICALLY
01349     kdDebug() << "KWTextFrameSet::formatVertically returning " << ( yp + hp ) - ( oldY + oldHeight ) << endl;
01350 #endif
01351     return ( yp + hp ) - ( oldY + oldHeight );
01352 }
01353 
01354 // adjustFlow is called e.g. to break the "top margin" of a paragraph.
01355 // There is no parag pointer in that case.
01356 int KWTextFrameSet::adjustFlow( int y, int w, int h )
01357 {
01358     QRect r( 0, y, w, h );
01359     return formatVertically( 0L, r );
01360 }
01361 
01362 void KWTextFrameSet::fixParagWidth( KWTextParag* parag )
01363 {
01364     // Fixing the parag rect for the formatting chars (CR and frame break).
01365     if ( parag && m_doc->viewFormattingChars() && parag->rect().width() < textDocument()->width() )
01366     {
01367         if ( parag->hardFrameBreakAfter() )
01368         {
01369             KoTextFormat * lastFormat = parag->at( parag->length() - 1 )->format();
01370             const QFontMetrics& refFontMetrics = lastFormat->refFontMetrics();
01371             // keep in sync with KWTextFrameSet::formatVertically
01372             QString str = i18n( "--- Frame Break ---" );
01373             int width = refFontMetrics.width( str );
01374             parag->setWidth( QMIN( parag->rect().width() + width, textDocument()->width() ) );
01375         }
01376         else // default KoTextFormatter implementation
01377             parag->fixParagWidth( true );
01378     }
01379 }
01380 
01381 KWTextFrameSet::~KWTextFrameSet()
01382 {
01383     textDocument()->takeFlow();
01384     //kdDebug(32001) << "KWTextFrameSet::~KWTextFrameSet" << endl;
01385     m_doc = 0L;
01386     delete m_textobj;
01387 }
01388 
01389 // This struct is used for sorting frames.
01390 // Since pages are one below the other, simply sorting on (y, x) does what we want.
01391 struct FrameStruct
01392 {
01393     KWFrame * frame;
01394     bool operator < ( const FrameStruct & t ) const {
01395         return compare(frame, t.frame) < 0;
01396     }
01397     bool operator <= ( const FrameStruct & t ) const {
01398         return compare(frame, t.frame) <= 0;
01399     }
01400     bool operator > ( const FrameStruct & t ) const {
01401         return compare(frame, t.frame) > 0;
01402     }
01403 
01404     /*
01405     the sorting of all frames in the same frameset is done as all sorting
01406     based on a simple frameOne > frameTwo question.
01407     Frame frameOne is greater then frameTwo if the center point lies more down then (the whole of)
01408     frame frameTwo. When they are equal, the X position is considered. */
01409     int compare (const KWFrame *frameOne, const KWFrame *frameTwo) const {
01410         // The first criteria is the page number though!
01411         int pageOne = frameOne->pageNumber();
01412         int pageTwo = frameTwo->pageNumber();
01413         if( (pageOne == -1) ^ (pageTwo == -1)) {
01414             if( pageOne == -1 )
01415                 return 5; // undefined is higher than defined.
01416             return -5;
01417         }
01418         if ( pageOne > pageTwo ) return 4; // frameOne > frameTwo
01419         if ( pageOne < pageTwo ) return -4; // frameOne < frameTwo
01420 
01421         double centerX = frameOne->left() + (frameOne->width() /2);
01422         // reverse the return values of the next two for RTL
01423         if ( centerX > frameTwo->right()) return 3; // frameOne > frameTwo
01424         if ( centerX < frameTwo->left()) return -3; // frameOne < frameTwo
01425 
01426         // check the Y position. Y is greater only when it is below the other frame.
01427         double centerY = frameOne->top() + (frameOne->height() /2);
01428         if ( centerY > frameTwo->bottom() ) return 2; //  frameOne > frameTwo
01429         if ( centerY < frameTwo->top() ) return -2; //  frameOne < frameTwo
01430 
01431         // the center of frameOne lies inside frameTwo. Lets check the topleft pos.
01432         if (frameOne->top() > frameTwo->top()) return 1;
01433         return -1;
01434     }
01435 };
01436 
01437 void KWTextFrameSet::updateFrames( int flags )
01438 {
01439     // Not visible ? Don't bother then.
01440     if ( !isVisible() ) {
01441         //kdDebug(32002) << "KWTextFrameSet::updateFrames " << name() << " not visible" << endl;
01442         m_textobj->setVisible(false);
01443         return;
01444     }
01445     m_textobj->setVisible(true);
01446 
01447     //kdDebug(32002) << "KWTextFrameSet::updateFrames " << name() << " frame-count=" << m_frames.count() << endl;
01448 
01449     // Sort frames of this frameset on (y coord, x coord)
01450     // Adjustment on 20-Jun-2002 which does not change the itent of this but moves the
01451     // sorting from top-left of frame to the whole frame area. (TZ)
01452 
01453     QValueList<FrameStruct> sortedFrames;
01454 
01455     int width = 0;
01456     QPtrListIterator<KWFrame> frameIter( frameIterator() );
01457     for ( ; frameIter.current(); ++frameIter )
01458     {
01459         // Calculate max width while we're at it
01460         //kdDebug(32002) << "KWTextFrameSet::updateFrames frame " << *frameIter.current() << " innerWidth=" << frameIter.current()->innerWidth() << "pt" << endl;
01461         width = QMAX( width, m_doc->ptToLayoutUnitPixX( frameIter.current()->innerWidth()));
01462         if ( flags & SortFrames )
01463         {
01464             FrameStruct str;
01465             str.frame = frameIter.current();
01466             sortedFrames.append( str );
01467         }
01468     }
01469     if ( width != textDocument()->width() )
01470     {
01471         //kdDebug(32002) << "KWTextFrameSet::updateFrames setWidth " << width << " LU pixels." << endl;
01472         //textDocument()->setMinimumWidth( -1, 0 );
01473         textDocument()->setWidth( width + 1 ); // QRect semantics problem (#32866)
01474     } //else kdDebug(32002) << "KWTextFrameSet::updateFrames width already " << width << " LU pixels." << endl;
01475 
01476     if ( flags & SortFrames )
01477     {
01478         qHeapSort( sortedFrames );
01479 
01480         // Re-fill the frames list with the frames in the right order
01481         m_frames.setAutoDelete( false );
01482         m_frames.clear();
01483 
01484         QValueList<FrameStruct>::Iterator it = sortedFrames.begin();
01485         for ( ; it != sortedFrames.end() ; ++it )
01486             m_frames.append( (*it).frame );
01487     }
01488 
01489     double availHeight = 0;
01490     double internalYpt = 0;
01491     double lastRealFrameHeight = 0;
01492     bool firstFrame = true;
01493 
01494     QPtrListIterator<KWFrame> frameIt( m_frames );
01495     for ( ; frameIt.current(); ++frameIt )
01496     {
01497         KWFrame* theFrame = frameIt.current();
01498 
01499         if ( !theFrame->isCopy() )
01500             internalYpt += lastRealFrameHeight;
01501 
01502         theFrame->setInternalY( internalYpt );
01503 
01504         // Update availHeight with the internal height of this frame - unless it's a copy
01505         if ( !theFrame->isCopy() || firstFrame )
01506         {
01507             lastRealFrameHeight = theFrame->innerHeight();
01508             availHeight += lastRealFrameHeight;
01509         }
01510         firstFrame = false;
01511     }
01512 
01513     m_textobj->setAvailableHeight( m_doc->ptToLayoutUnitPixY( availHeight ) );
01514     //kdDebug(32002) << this << " (" << name() << ") KWTextFrameSet::updateFrames availHeight=" << availHeight
01515     //               << " (LU: " << m_doc->ptToLayoutUnitPixY( availHeight ) << ")" << endl;
01516     m_frames.setAutoDelete( true );
01517 
01518     KWFrameSet::updateFrames( flags );
01519 }
01520 
01521 int KWTextFrameSet::availableHeight() const
01522 {
01523     return m_textobj->availableHeight();
01524 }
01525 
01526 KWFrame * KWTextFrameSet::internalToDocument( const KoPoint &relPoint, KoPoint &dPoint ) const
01527 {
01528 #ifdef DEBUG_ITD
01529     kdDebug() << name() << " ITD called for relPoint=" << relPoint.x() << "," << relPoint.y() << endl;
01530 #endif
01531     if ( !m_doc->layoutViewMode()->hasFrames() ) { // text viewmode
01532         dPoint = relPoint;
01533         return m_frames.getFirst();
01534     }
01535     // This does a binary search in the m_framesInPage array, with internalY as criteria
01536     // We only look at the first frame of each page. Refining is done later on.
01537     Q_ASSERT( !m_framesInPage.isEmpty() );
01538     int len = m_framesInPage.count();
01539     int n1 = 0;
01540     int n2 = len - 1;
01541     double internalY = 0.0;
01542     int mid = 0;
01543     bool found = FALSE;
01544     while ( n1 <= n2 ) {
01545         double res;
01546         mid = (n1 + n2)/2;
01547 #ifdef DEBUG_ITD
01548         kdDebug() << "ITD: begin. mid=" << mid << endl;
01549 #endif
01550         Q_ASSERT( m_framesInPage[mid] ); // We have no null items
01551         if ( m_framesInPage[mid]->isEmpty() )
01552             res = -1;
01553         else
01554         {
01555             KWFrame * theFrame = m_framesInPage[mid]->first();
01556             internalY = theFrame->internalY();
01557 #ifdef DEBUG_ITD
01558             kdDebug() << "ITD: relPoint.y=" << relPoint.y() << " internalY=" << internalY << endl;
01559 #endif
01560             res = relPoint.y() - internalY;
01561 #ifdef DEBUG_ITD
01562             kdDebug() << "ITD: res=" << res << endl;
01563 #endif
01564             // Anything between this internalY (top) and internalY+height (bottom) is fine
01565             // (Using the next page's first frame's internalY only works if there is a frame on the next page)
01566             if ( res >= 0 )
01567             {
01568                 double height = theFrame->innerHeight();
01569 #ifdef DEBUG_ITD
01570                 kdDebug() << "ITD: height=" << height << " -> the bottom is at " << internalY+height << endl;
01571 #endif
01572                 if ( relPoint.y() < internalY + height )
01573                 {
01574 #ifdef DEBUG_ITD
01575                     kdDebug() << "ITD: found a match " << mid << endl;
01576 #endif
01577                     found = true;
01578                     break;
01579                 }
01580             }
01581         }
01582         // res == 0 can't happen in theory, but in practice it happens when a frame has a height of 0
01583         // (e.g. newly imported table without correct row heights)
01584         if ( res < 0 )
01585             n2 = mid - 1;
01586         else // if ( res >= 0 )
01587             n1 = mid + 1;
01588 #ifdef DEBUG_ITD
01589         kdDebug() << "ITD: End of loop. n1=" << n1 << " n2=" << n2 << endl;
01590 #endif
01591     }
01592     if ( !found )
01593     {
01594         // Not found (n2 < n1)
01595         // We might have missed the frame because n2 has many frames
01596         // (and we only looked at the first one).
01597         mid = n2;
01598 #ifdef DEBUG_ITD
01599         kdDebug() << "ITD: Setting mid to n2=" << mid << endl;
01600 #endif
01601         if ( mid < 0 )
01602         {
01603 #ifdef DEBUG_ITD
01604             kdDebug(32002) << "KWTextFrameSet::internalToDocument " << relPoint.x() << "," << relPoint.y()
01605                            << " before any frame of " << (void*)this << endl;
01606 #endif
01607             dPoint = relPoint; // "bah", I said above :)
01608             return 0L;
01609         }
01610     }
01611     // search to first of equal items
01612     // This happens with copied frames, which have the same internalY
01613     int result = mid;
01614     while ( mid - 1 >= 0 )
01615     {
01616         mid--;
01617         if ( !m_framesInPage[mid]->isEmpty() )
01618         {
01619             KWFrame * theFrame = m_framesInPage[mid]->first();
01620 #ifdef DEBUG_ITD
01621             kdDebug() << "KWTextFrameSet::internalToDocument going back to page " << mid << " - frame: " << theFrame->internalY() << endl;
01622 #endif
01623             if ( theFrame->internalY() == internalY ) // same internalY as the frame we found before
01624                 result = mid;
01625             else
01626                 break;
01627         }
01628     }
01629 
01630     // Now iterate over the frames in page 'result' and find the right one
01631     QPtrListIterator<KWFrame> frameIt( *m_framesInPage[result] );
01632     for ( ; frameIt.current(); ++frameIt )
01633     {
01634         KWFrame *theFrame = frameIt.current();
01635         KoRect relRect( 0, theFrame->internalY(), theFrame->innerWidth(), theFrame->innerHeight() );
01636 #ifdef DEBUG_ITD
01637         kdDebug() << "KWTextFrameSet::internalToDocument frame's relative rect:" << relRect << endl;
01638 #endif
01639         if ( relRect.contains( relPoint ) ) // both relRect and relPoint are in "relative coordinates"
01640         {
01641             dPoint = internalToDocumentKnowingFrame( relPoint, theFrame );
01642             return theFrame;
01643         }
01644     }
01645 #ifdef DEBUG_ITD
01646     kdDebug(32002) << "KWTextFrameSet::internalToDocument " << relPoint.x() << "," << relPoint.y()
01647                    << " not in any frame of " << (void*)this << " (looked on page " << result << ")" << endl;
01648 #endif
01649     dPoint = relPoint; // bah again
01650     return 0L;
01651 }
01652 
01653 // same but with iPoint in LU
01654 KWFrame * KWTextFrameSet::internalToDocument( const QPoint &iPoint, KoPoint &dPoint ) const
01655 {
01656     KoPoint relPoint = m_doc->layoutUnitPtToPt( m_doc->pixelToPt( iPoint ) );
01657     return internalToDocument( relPoint, dPoint );
01658 }
01659 
01660 #ifndef NDEBUG
01661 void KWTextFrameSet::printDebug()
01662 {
01663     KWFrameSet::printDebug();
01664     if ( !isDeleted() )
01665     {
01666         kdDebug() << "KoTextDocument width = " << textDocument()->width() << " height = " << textDocument()->height() << endl;
01667     }
01668 
01669     QPtrListIterator<KoTextCustomItem> cit( textDocument()->allCustomItems() );
01670     for ( ; cit.current() ; ++cit )
01671     {
01672       KWAnchor *anc = dynamic_cast<KWAnchor *>( cit.current() );
01673       if (anc)
01674           kdDebug() << "Inline framesets: " << anc->frameSet()->name() << endl;
01675     }
01676 }
01677 #endif
01678 
01679 QDomElement KWTextFrameSet::saveInternal( QDomElement &parentElem, bool saveFrames, bool saveAnchorsFramesets )
01680 {
01681     if ( m_frames.isEmpty() ) // Deleted frameset -> don't save
01682         return QDomElement();
01683 
01684     QDomElement framesetElem = parentElem.ownerDocument().createElement( "FRAMESET" );
01685     parentElem.appendChild( framesetElem );
01686 
01687     if ( m_groupmanager ) {
01688         framesetElem.setAttribute( "grpMgr", m_groupmanager->name() );
01689 
01690         KWTableFrameSet::Cell *cell = (KWTableFrameSet::Cell *)this;
01691         framesetElem.setAttribute( "row", cell->firstRow() );
01692         framesetElem.setAttribute( "col", cell->firstColumn() );
01693         framesetElem.setAttribute( "rows", cell->rowSpan() );
01694         framesetElem.setAttribute( "cols", cell->columnSpan() );
01695     }
01696     if ( protectContent() )
01697         framesetElem.setAttribute( "protectContent", static_cast<int>(protectContent()));
01698 
01699     KWFrameSet::saveCommon( framesetElem, saveFrames );
01700 
01701     // Save paragraphs
01702     KWTextParag *start = static_cast<KWTextParag *>( textDocument()->firstParag() );
01703     while ( start ) {
01704         start->save( framesetElem, saveAnchorsFramesets );
01705         start = static_cast<KWTextParag *>( start->next() );
01706     }
01707 
01708     return framesetElem;
01709 }
01710 
01711 KWFrame* KWTextFrameSet::loadOasisTextFrame( const QDomElement& frameTag, const QDomElement &tag, KoOasisContext& context )
01712 {
01713     context.styleStack().save();
01714     context.fillStyleStack( frameTag, KoXmlNS::draw, "style-name", "graphic" ); // get the style for the graphics element
01715 
01716     KWFrame* frame = loadOasisFrame( frameTag, context );
01717 
01718     // Load minimum height - only available for text-box
01719     bool hasMinHeight = tag.hasAttributeNS( KoXmlNS::fo, "min-height" );
01720     if ( hasMinHeight ) {
01721         double height = KoUnit::parseValue( tag.attributeNS( KoXmlNS::fo, "min-height", QString::null ) );
01722         frame->setMinimumFrameHeight( height );
01723         if ( height > frame->height() || !tag.hasAttributeNS( KoXmlNS::fo, "height" ) )
01724             frame->setHeight( height );
01725     }
01726 
01727     // Load overflow behavior (OASIS 14.27.27, not in OO-1.1 DTD). This is here since it's only for text framesets.
01728     const QString overflowBehavior = context.styleStack().attributeNS( KoXmlNS::style, "overflow-behavior" );
01729     if ( frame->minimumFrameHeight() > 0 )
01730         frame->setFrameBehavior( KWFrame::AutoExtendFrame );
01731     else if ( overflowBehavior == "auto-create-new-frame" )
01732     {
01733         frame->setFrameBehavior( KWFrame::AutoCreateNewFrame );
01734         frame->setNewFrameBehavior( KWFrame::Reconnect ); // anything else doesn't make sense
01735     }
01736     else if ( overflowBehavior.isEmpty() || overflowBehavior == "clip" )
01737         frame->setFrameBehavior( KWFrame::Ignore );
01738     else
01739         kdWarning(32001) << "Unknown value for style:overflow-behavior: " << overflowBehavior << endl;
01740 
01741     context.styleStack().restore();
01742 
01743     return frame;
01744 }
01745 
01746 void KWTextFrameSet::loadOasisContent( const QDomElement &bodyElem, KoOasisContext& context )
01747 {
01748     return m_textobj->loadOasisContent( bodyElem, context, m_doc->styleCollection() );
01749 }
01750 
01751 KWFrame* KWTextFrameSet::loadOasis( const QDomElement& frameTag, const QDomElement &tag, KoOasisContext& context )
01752 {
01753     KWFrame* frame = loadOasisTextFrame( frameTag, tag, context );
01754     loadOasisContent( tag, context );
01755     return frame;
01756 }
01757 
01758 static void finishTOC( KoXmlWriter& writer )
01759 {
01760     writer.endElement(); // text:table-of-content
01761     writer.endElement(); // text:index-body
01762 }
01763 
01764 void KWTextFrameSet::saveOasisContent( KoXmlWriter& writer, KoSavingContext& context ) const
01765 {
01766     // TODO save protectContent
01767 
01768     QMap<const KoTextParag*, KoTextBookmarkList> bookmarksPerParagraph;
01769     if ( m_doc->bookmarkList() )
01770         bookmarksPerParagraph = m_doc->bookmarkList()->bookmarksPerParagraph();
01771 
01772     // Basically just call saveOasis on every paragraph.
01773     // But we do table-of-contents-handling (for kword) in addition,
01774     // as well as bookmarks.
01775     KoTextParag* parag = textDocument()->firstParag();
01776     bool inTOC = false;
01777     while ( parag ) {
01778         bool tocParag = parag->partOfTableOfContents();
01779         if ( tocParag != inTOC ) {
01780             if ( tocParag ) { // first TOC paragraph
01781                 writer.startElement( "text:table-of-content" );
01782                 writer.addAttribute( "text:name", "Table Of Contents" );
01783                 writer.addAttribute( "text:protected", "false" ); // true by default in OO, but we don't support that yet anyway
01784                 writer.startElement( "text:table-of-content-source" );
01785                 // TODO writer.addAttribute( "text:outline-level", ... );
01786                 // TODO for each level writer.startElement( "text:table-of-content-entry-template" );
01787                 // TODO writer.endElement(); // text:table-of-content-entry-template
01788                 writer.endElement(); // text:table-of-content-source
01789                 writer.startElement( "text:index-body" );
01790                 writer.startElement( "text:index-title" );
01791                 writer.addAttribute( "text:name", "Table Of Contents Heading" );
01792             } else {
01793                 finishTOC( writer );
01794             }
01795         }
01796 
01797 
01798         // I want Qt4's QMap/QHash::value()!
01799         KoSavingContext::BookmarkPositions bookmarkStarts, bookmarkEnds;
01800         QMap<const KoTextParag*, KoTextBookmarkList>::const_iterator bkit = bookmarksPerParagraph.find( parag );
01801         if ( bkit != bookmarksPerParagraph.end() ) {
01802             // Massage a bit the bookmarks data; KoTextParag wants it ordered by position, for speed.
01803             const KoTextBookmarkList& bookmarks = *bkit;
01804             for ( KoTextBookmarkList::const_iterator it = bookmarks.begin(); it != bookmarks.end(); ++it )
01805             {
01806                 const KoTextBookmark& bk = *it;
01807                 if ( bk.startParag() == parag )
01808                     bookmarkStarts.append( KoSavingContext::BookmarkPosition(
01809                                                bk.bookmarkName(), bk.bookmarkStartIndex(),
01810                                                bk.isSimple() ) );
01811                 if ( bk.endParag() == parag && !bk.isSimple() )
01812                     bookmarkEnds.append( KoSavingContext::BookmarkPosition( bk.bookmarkName(),
01813                                                                             bk.bookmarkEndIndex(), false ) );
01814             }
01815             qHeapSort( bookmarkStarts );
01816             qHeapSort( bookmarkEnds );
01817         }
01818         // should be done in all cases, even if both lists are empty
01819         context.setBookmarkPositions( bookmarkStarts, bookmarkEnds );
01820 
01821         // Save the whole parag, without the trailing space.
01822         parag->saveOasis( writer, context, 0, parag->lastCharPos() );
01823 
01824         if ( tocParag && !inTOC )
01825             writer.endElement(); // text:index-title
01826         inTOC = tocParag;
01827 
01828         parag = parag->next();
01829     }
01830     if ( inTOC )
01831         finishTOC( writer );
01832 }
01833 
01834 void KWTextFrameSet::saveOasis( KoXmlWriter& writer, KoSavingContext& context, bool saveFrames ) const
01835 {
01836     // Save first frame with the whole contents
01837     KWFrame* frame = m_frames.getFirst();
01838     QString lastFrameName = name();
01839     frame->startOasisFrame( writer, context.mainStyles(), lastFrameName );
01840 
01841     QString nextFrameName = name() + "-";
01842 
01843     writer.startElement( "draw:text-box" );
01844     if ( frame->frameBehavior() == KWFrame::AutoExtendFrame )
01845         writer.addAttributePt( "fo:min-height", frame->minimumFrameHeight() );
01846     if ( m_frames.count() > 1 && saveFrames )
01847         writer.addAttribute( "draw:chain-next-name", nextFrameName + "2" );
01848     saveOasisContent( writer, context );
01849     writer.endElement(); // draw:text-box
01850     writer.endElement(); // draw:frame
01851 
01852     // Save other frames using chaining
01853     if ( saveFrames ) // false when called from KWDocument::saveSelectedFrames
01854     {
01855         int frameNumber = 2;
01856         QPtrListIterator<KWFrame> frameIter( frameIterator() );
01857         ++frameIter; // skip first frame, already saved
01858         for ( ; frameIter.current(); ++frameIter, ++frameNumber )
01859         {
01860             const QString frameName = nextFrameName + QString::number( frameNumber );
01861             frameIter.current()->startOasisFrame( writer, context.mainStyles(), frameName, lastFrameName );
01862             lastFrameName = frameName; // this is used for copy-frames
01863             writer.startElement( "draw:text-box" );
01864             if ( frame->frameBehavior() == KWFrame::AutoExtendFrame )
01865                 writer.addAttributePt( "fo:min-height", frame->minimumFrameHeight() );
01866             if ( frameNumber < (int)m_frames.count() )
01867                 writer.addAttribute( "draw:chain-next-name", nextFrameName + QString::number( frameNumber+1 ) );
01868             // No contents. Well, OOo saves an empty paragraph, but I'd say that's wrong.
01869             writer.endElement();
01870             writer.endElement(); // draw:frame
01871         }
01872     }
01873 }
01874 
01875 void KWTextFrameSet::load( QDomElement &attributes, bool loadFrames )
01876 {
01877     KWFrameSet::load( attributes, loadFrames );
01878     if ( attributes.hasAttribute( "protectContent"))
01879         setProtectContent((bool)attributes.attribute( "protectContent" ).toInt());
01880 
01881     textDocument()->clear(false); // Get rid of dummy paragraph (and more if any)
01882     m_textobj->setLastFormattedParag( 0L ); // no more parags, avoid UMR in next setLastFormattedParag call
01883     KWTextParag *lastParagraph = 0L;
01884 
01885     // <PARAGRAPH>
01886     QDomElement paragraph = attributes.firstChild().toElement();
01887     for ( ; !paragraph.isNull() ; paragraph = paragraph.nextSibling().toElement() )
01888     {
01889         if ( paragraph.tagName() == "PARAGRAPH" )
01890         {
01891             KWTextParag *parag = new KWTextParag( textDocument(), lastParagraph );
01892             parag->load( paragraph );
01893             if ( !lastParagraph )        // First parag
01894                 textDocument()->setFirstParag( parag );
01895             lastParagraph = parag;
01896             m_doc->progressItemLoaded();
01897         }
01898     }
01899 
01900     if ( !lastParagraph )                // We created no paragraph
01901     {
01902         // Create an empty one, then. See KWTextDocument ctor.
01903         textDocument()->clear( true );
01904         static_cast<KWTextParag *>( textDocument()->firstParag() )->setStyle( m_doc->styleCollection()->findStyle( "Standard" ) );
01905     }
01906     else
01907         textDocument()->setLastParag( lastParagraph );
01908 
01909     m_textobj->setLastFormattedParag( textDocument()->firstParag() );
01910     //kdDebug(32001) << "KWTextFrameSet::load done" << endl;
01911 }
01912 
01913 void KWTextFrameSet::finalize()
01914 {
01915     KWFrameSet::finalize();
01916     m_textobj->formatMore( 0 ); // just to get the timer going
01917     // This is important in case of auto-resized frames or table cells,
01918     // which come from an import filter, which didn't give them the right size.
01919     // However it shouldn't start _now_ (so we use 0), because e.g. main frames
01920     // don't have the right size yet (KWFrameLayout not done yet).
01921 }
01922 
01923 void KWTextFrameSet::setVisible(bool visible)
01924 {
01925     setInlineFramesVisible( visible );
01926     KWFrameSet::setVisible( visible );
01927 }
01928 
01929 void KWTextFrameSet::setInlineFramesVisible(bool visible)
01930 {
01931     QPtrListIterator<KoTextCustomItem> cit( textDocument()->allCustomItems() );
01932     for ( ; cit.current() ; ++cit )
01933     {
01934       KWAnchor *anc = dynamic_cast<KWAnchor *>( cit.current() );
01935       if (anc)
01936             anc->frameSet()->setVisible( visible );
01937     }
01938 }
01939 
01940 void KWTextFrameSet::addTextFrameSets( QPtrList<KWTextFrameSet> & lst, bool onlyReadWrite )
01941 {
01942     if (!textObject()->protectContent() || !onlyReadWrite)
01943         lst.append(this);
01944 }
01945 
01946 void KWTextFrameSet::slotNewCommand( KCommand *cmd )
01947 {
01948     m_doc->addCommand( cmd );
01949 }
01950 
01951 void KWTextFrameSet::ensureFormatted( KoTextParag * parag, bool emitAfterFormatting )
01952 {
01953     if (!isVisible())
01954         return;
01955     m_textobj->ensureFormatted( parag, emitAfterFormatting );
01956 }
01957 
01958 bool KWTextFrameSet::slotAfterFormattingNeedMoreSpace( int bottom, KoTextParag *lastFormatted )
01959 {
01960     int availHeight = availableHeight();
01961 #ifdef DEBUG_FORMAT_MORE
01962     if(lastFormatted)
01963         kdDebug(32002) << "slotAfterFormatting We need more space in " << name()
01964                        << " bottom=" << bottom + lastFormatted->rect().height()
01965                        << " availHeight=" << availHeight << endl;
01966     else
01967         kdDebug(32002) << "slotAfterFormatting We need more space in " << name()
01968                        << " bottom2=" << bottom << " availHeight=" << availHeight << endl;
01969 #endif
01970     if ( m_frames.isEmpty() )
01971     {
01972         kdWarning(32002) << "slotAfterFormatting no more space, but no frame !" << endl;
01973         return true; // abort
01974     }
01975 
01976     KWFrame::FrameBehavior frmBehavior = m_frames.last()->frameBehavior();
01977     if ( frmBehavior == KWFrame::AutoExtendFrame && isProtectSize())
01978         frmBehavior = KWFrame::Ignore;
01979     if (  frmBehavior ==  KWFrame::AutoCreateNewFrame )
01980     {
01981         KWFrame *theFrame = settingsFrame( m_frames.last() );
01982         double minHeight = s_minFrameHeight + theFrame->paddingTop() + theFrame->paddingBottom() + 5;
01983         if ( availHeight < minHeight )
01984             frmBehavior = KWFrame::Ignore;
01985     }
01986 
01987     int difference = ( bottom + 2 ) - availHeight; // in layout unit pixels
01988 #ifdef DEBUG_FORMAT_MORE
01989     kdDebug(32002) << "AutoExtendFrame bottom=" << bottom << " availHeight=" << availHeight
01990                    << " => difference = " << difference << endl;
01991 #endif
01992     if( lastFormatted && bottom + lastFormatted->rect().height() > availHeight ) {
01993 #ifdef DEBUG_FORMAT_MORE
01994         kdDebug(32002) << " next will be off -> adding " << lastFormatted->rect().height() << endl;
01995 #endif
01996         difference += lastFormatted->rect().height();
01997     }
01998 
01999     switch ( frmBehavior ) {
02000     case KWFrame::AutoExtendFrame:
02001     {
02002         if(difference > 0) {
02003             // There's no point in resizing a copy, so go back to the last non-copy frame
02004             KWFrame *theFrame = settingsFrame( m_frames.last() );
02005             double wantedPosition = 0;
02006 
02007             // Footers and footnotes go up
02008             if ( theFrame->frameSet()->isAFooter() || theFrame->frameSet()->isFootNote() )
02009             {
02010                 // The Y position doesn't matter much, recalcFrames will reposition the frame
02011                 // But the point of this code is set the correct height for the frame.
02012                 double maxFooterSize = footerHeaderSizeMax( theFrame );
02013                 double diffPt = m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
02014                 wantedPosition = theFrame->top() - diffPt;
02015 #ifdef DEBUG_FORMAT_MORE
02016                 kdDebug() << "  diffPt=" << diffPt << " -> wantedPosition=" << wantedPosition << endl;
02017 #endif
02018                 if ( wantedPosition < 0 )
02019                 {
02020                     m_textobj->setLastFormattedParag( 0 );
02021                     return true; // abort
02022                 }
02023 
02024                 if ( wantedPosition != theFrame->top() &&
02025                      ( theFrame->frameSet()->isFootEndNote() ||
02026                        theFrame->bottom() - maxFooterSize <= wantedPosition ) ) // Apply maxFooterSize for footers only
02027                 {
02028                     theFrame->setTop( wantedPosition );
02029 #ifdef DEBUG_FORMAT_MORE
02030                     kdDebug() << "  ok: frame=" << *theFrame << " bottom=" << theFrame->bottom() << " height=" << theFrame->height() << endl;
02031 #endif
02032                     frameResized( theFrame, true );
02033                     // We only got room for the next paragraph, we still have to keep the formatting going...
02034                     return false; // keep going
02035                 }
02036                 kdDebug() << "slotAfterFormatting didn't manage to get more space for footer/footnote, aborting" << endl;
02037                 return true; // abort
02038             }
02039             // Other frames are resized by the bottom
02040 
02041             wantedPosition = m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) ) + theFrame->bottom();
02042             KWPage *page = m_doc->pageManager()->page( theFrame );
02043             double pageBottom;
02044             if(page)
02045                 pageBottom = page->offsetInDocument() + page->height() - page->bottomMargin();
02046             else
02047                 pageBottom = theFrame->bottom();
02048             double newPosition = QMIN( wantedPosition, pageBottom );
02049             kdDebug(32002) << "wantedPosition=" << wantedPosition << " pageBottom=" << pageBottom
02050                            << " -> newPosition=" << newPosition << endl;
02051 
02052             if ( theFrame->frameSet()->isAHeader() )
02053             {
02054                 double maxHeaderSize=footerHeaderSizeMax( theFrame );
02055                 newPosition = QMIN( newPosition, maxHeaderSize + theFrame->top() );
02056             }
02057 
02058             newPosition = QMAX( newPosition, theFrame->top() ); // avoid negative heights
02059             kdDebug(32002) << "newPosition=" << newPosition << endl;
02060 
02061             bool resized = false;
02062             if(theFrame->frameSet()->groupmanager()) {
02063                 KWTableFrameSet *table = theFrame->frameSet()->groupmanager();
02064 #ifdef DEBUG_FORMAT_MORE
02065                 kdDebug(32002) << "is table cell; just setting new minFrameHeight, to " << newPosition - theFrame->top() << endl;
02066 #endif
02067                 double newMinFrameHeight = newPosition - theFrame->top();
02068                 resized = QABS( newMinFrameHeight - theFrame->minimumFrameHeight() ) > 1E-10;
02069                 if ( resized ) {
02070                     theFrame->setMinimumFrameHeight( newMinFrameHeight );
02071                     KWTableFrameSet::Cell *cell = (KWTableFrameSet::Cell *)theFrame->frameSet();
02072                     table->recalcCols(cell->firstColumn(), cell->firstRow());
02073                     table->recalcRows(cell->firstColumn(), cell->firstRow());
02074 
02075                     if (!  table->anchorFrameset() )
02076                         ;// do nothing
02077                     else if ( table->anchorFrameset() && table->anchorFrameset()->isAHeader() ) //we must recalculate the header frame size
02078                     {
02079                       theFrame = table->anchorFrameset()->frameIterator().getLast();
02080                       theFrame->setBottom(newPosition);
02081                       frameResized( theFrame, false );
02082                     }
02083                     else if ( table->anchorFrameset()->isAFooter() || table->anchorFrameset()->isFootNote() ) //we must recalculate the footer frame size
02084                     {
02085                       theFrame = table->anchorFrameset()->frameIterator().getLast();
02086                       // The Y position doesn't matter much, recalcFrames will reposition the frame
02087                       // But the point of this code is set the correct height for the frame.
02088                       double maxFooterSize = footerHeaderSizeMax( theFrame );
02089                       double diffPt = m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
02090                       wantedPosition = theFrame->top() - diffPt;
02091                       if ( wantedPosition < 0 )
02092                       {
02093                         m_textobj->setLastFormattedParag( 0 );
02094                         return true; // abort
02095                       }
02096 
02097                       if ( wantedPosition != theFrame->top() &&
02098                            ( theFrame->frameSet()->isFootEndNote() ||
02099                            theFrame->bottom() - maxFooterSize <= wantedPosition ) ) // Apply maxFooterSize for footers only
02100                       {
02101                         theFrame->setTop( wantedPosition );
02102                         frameResized( theFrame, true );
02103                         // We only got room for the next paragraph, we still have to keep the formatting going...
02104                       }
02105                     }
02106 
02107                     m_doc->delayedRepaintAllViews();
02108                 }
02109                 return true; // abort formatting for now (not sure this is correct)
02110             } else {
02111                 resized = QABS( theFrame->bottom() - newPosition ) > 1E-10;
02112 #ifdef DEBUG_FORMAT_MORE
02113                 kdDebug() << "  bottom=" << theFrame->bottom() << " new position:" << newPosition << " wantedPosition=" << wantedPosition << "  resized=" << resized << endl;
02114 #endif
02115 
02116                 if ( resized )
02117                 {
02118 #ifdef DEBUG_FORMAT_MORE
02119                     kdDebug(32002) << "slotAfterFormatting changing bottom from " << theFrame->bottom() << " to " << newPosition << endl;
02120 #endif
02121                     theFrame->setBottom(newPosition);
02122                     frameResized( theFrame, false );
02123                 }
02124             }
02125 
02126             if(newPosition < wantedPosition &&
02127                (theFrame->newFrameBehavior() == KWFrame::Reconnect
02128                 && !theFrame->frameSet()->isEndNote())) // end notes are handled by KWFrameLayout
02129             {
02130                 wantedPosition = wantedPosition - newPosition + theFrame->top() + page->height();
02131 #ifdef DEBUG_FORMAT_MORE
02132                 kdDebug(32002) << "Not enough room in this page -> creating new one, with a reconnect frame" << endl;
02133                 kdDebug(32002) << "new wantedPosition=" << wantedPosition << endl;
02134 #endif
02135 
02136                 // fall through to AutoCreateNewFrame
02137             }
02138             else if(newPosition < wantedPosition && (theFrame->newFrameBehavior() == KWFrame::NoFollowup)) {
02139                 if ( theFrame->frameSet()->isEndNote() ) // we'll need a new page
02140                     m_doc->delayedRecalcFrames( theFrame->pageNumber() );
02141 
02142                 m_textobj->setLastFormattedParag( 0 );
02143                 return true; // abort
02144             } else {
02145                 if ( resized ) // we managed to resize a frame
02146                     return false; // keep going
02147                 return true; // abort
02148             }
02149         }
02150     }
02151 
02152     case KWFrame::AutoCreateNewFrame:
02153     {
02154         // We need a new frame in this frameset.
02155         return createNewPageAndNewFrame( lastFormatted, difference );
02156     }
02157 
02158     case KWFrame::Ignore:
02159 #ifdef DEBUG_FORMAT_MORE
02160         kdDebug(32002) << "slotAfterFormatting frame behaviour is Ignore" << endl;
02161 #endif
02162         m_textobj->setLastFormattedParag( 0 );
02163         return true; // abort
02164     }
02165     kdWarning() << "NEVERREACHED" << endl;
02166     // NEVERREACHED
02167     return true;
02168 }
02169 
02170 void KWTextFrameSet::slotAfterFormattingTooMuchSpace( int bottom )
02171 {
02172     int availHeight = availableHeight();
02173     // The + 2 here leaves 2 pixels below the last line. Without it we hit
02174     // the "break at end of frame" case in formatVertically (!!).
02175     int difference = availHeight - ( bottom + 2 );
02176 #ifdef DEBUG_FORMAT_MORE
02177     kdDebug(32002) << "slotAfterFormatting less text than space (AutoExtendFrame). Frameset " << name() << " availHeight=" << availHeight << " bottom=" << bottom << " ->difference=" << difference << endl;
02178 #endif
02179     // There's no point in resizing a copy, so go back to the last non-copy frame
02180     KWFrame *theFrame = settingsFrame( m_frames.last() );
02181 #ifdef DEBUG_FORMAT_MORE
02182     kdDebug(32002) << "   frame is " << *theFrame << " footer:" << ( theFrame->frameSet()->isAFooter() || theFrame->frameSet()->isFootEndNote() ) << endl;
02183 #endif
02184     if ( theFrame->frameSet()->isAFooter() || theFrame->frameSet()->isFootEndNote() )
02185     {
02186         double wantedPosition = theFrame->top() + m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
02187         Q_ASSERT( wantedPosition < theFrame->bottom() );
02188         if ( wantedPosition != theFrame->top() )
02189         {
02190 #ifdef DEBUG_FORMAT_MORE
02191             kdDebug() << "   top= " << theFrame->top() << " setTop " << wantedPosition << endl;
02192 #endif
02193             theFrame->setTop( wantedPosition );
02194 #ifdef DEBUG_FORMAT_MORE
02195             kdDebug() << "    -> the frame is now " << *theFrame << endl;
02196 #endif
02197             frameResized( theFrame, true );
02198         }
02199     }
02200     else // header or other frame: resize bottom
02201     {
02202         double wantedPosition = theFrame->bottom() - m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
02203 #ifdef DEBUG_FORMAT_MORE
02204         kdDebug() << "slotAfterFormatting wantedPosition=" << wantedPosition << " top+minheight=" << theFrame->top() + s_minFrameHeight << endl;
02205 #endif
02206         wantedPosition = QMAX( wantedPosition, theFrame->top() + s_minFrameHeight );
02207         if( theFrame->frameSet()->groupmanager() ) {
02208             if ( wantedPosition != theFrame->bottom()) {
02209                 KWTableFrameSet *table = theFrame->frameSet()->groupmanager();
02210                 // When a frame can be smaller we don't rescale it if it is a table, since
02211                 // we don't have the full picture of the change.
02212                 // We will set the minFrameHeight to the correct value and let the tables code
02213                 // do the rescaling based on all the frames in the row. (see KWTableFrameSet::recalcRows())
02214                 if(wantedPosition != theFrame->top() + theFrame->minimumFrameHeight()) {
02215                     theFrame->setMinimumFrameHeight(wantedPosition - theFrame->top());
02216 #ifdef DEBUG_FORMAT_MORE
02217                     kdDebug(32002) << "is table cell; only setting new minFrameHeight to " << theFrame->minimumFrameHeight() << ", recalcrows will do the rest" << endl;
02218 #endif
02219                     KWTableFrameSet::Cell *cell = (KWTableFrameSet::Cell *)theFrame->frameSet();
02220                     table->recalcCols(cell->firstColumn(), cell->firstRow());
02221                     table->recalcRows(cell->firstColumn(), cell->firstRow());
02222 
02223                     if (!  table->anchorFrameset() )
02224                         ;// do nothing
02225                     else if ( table->anchorFrameset() && table->anchorFrameset()->isAHeader()  )
02226                     {
02227                       theFrame = table->anchorFrameset()->frameIterator().getLast();
02228                       theFrame->setBottom(wantedPosition);
02229                       frameResized( theFrame, false );
02230                     }
02231                     else if ( table->anchorFrameset()->isAFooter() ||  table->anchorFrameset()->isFootEndNote() )
02232                     {
02233                       theFrame = table->anchorFrameset()->frameIterator().getLast();
02234                       double wantedPosition = theFrame->top() + m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
02235                       Q_ASSERT( wantedPosition < theFrame->bottom() );
02236                       if ( wantedPosition != theFrame->top() )
02237                       {
02238                         theFrame->setTop( wantedPosition );
02239                         frameResized( theFrame, true );
02240                       }
02241                     }
02242                     m_doc->delayedRepaintAllViews();
02243                 }
02244             }
02245         } else {
02246             // Also apply the frame's minimum height
02247             wantedPosition = QMAX( wantedPosition, theFrame->top() + theFrame->minimumFrameHeight() );
02248             if ( wantedPosition != theFrame->bottom()) {
02249 #ifdef DEBUG_FORMAT_MORE
02250                 kdDebug() << "    the frame was " << *theFrame << endl;
02251                 kdDebug() << "setBottom " << wantedPosition << endl;
02252 #endif
02253                 theFrame->setBottom( wantedPosition );
02254 #ifdef DEBUG_FORMAT_MORE
02255                 kdDebug() << "    -> the frame is now " << *theFrame << endl;
02256 #endif
02257                 frameResized( theFrame, true );
02258             }
02259         }
02260     }
02261 }
02262 
02263 void KWTextFrameSet::slotAfterFormatting( int bottom, KoTextParag *lastFormatted, bool* abort )
02264 {
02265     int availHeight = availableHeight();
02266     if ( ( bottom > availHeight ) ||   // this parag is already off page
02267          ( lastFormatted && bottom + lastFormatted->rect().height() > availHeight ) ) // or next parag will be off page
02268     {
02269         *abort = slotAfterFormattingNeedMoreSpace( bottom, lastFormatted );
02270     }
02271     // Handle the case where the last frame is empty, so we may want to
02272     // remove the last page.
02273     else if ( m_frames.count() > 1 && !lastFormatted && frameSetInfo() == KWFrameSet::FI_BODY
02274               && bottom < availHeight - m_doc->ptToLayoutUnitPixY( m_frames.last()->innerHeight() ) )
02275     {
02276 #ifdef DEBUG_FORMAT_MORE
02277         kdDebug(32002) << "slotAfterFormatting too much space (bottom=" << bottom << ", availHeight=" << availHeight << ") , trying to remove last frame" << endl;
02278 #endif
02279         // Remove the empty last frame, if it's an auto-created one (e.g. a
02280         // continuation on the next page). Not when the user just created it!
02281         if(m_frames.last()->frameBehavior() == KWFrame::AutoExtendFrame
02282            && m_frames.last()->minimumFrameHeight() < 1E-10 ) { // i.e. equal to 0
02283             deleteFrame(m_frames.last(), true);
02284             m_doc->frameChanged( 0L );
02285         }
02286         if ( m_doc->processingType() == KWDocument::WP ) {
02287             bool removed = m_doc->tryRemovingPages();
02288             // Do all the recalc in one go. Speeds up deleting many pages.
02289             if ( removed )
02290                 m_doc->afterRemovePages();
02291         }
02292     }
02293     // Handle the case where the last frame is in AutoExtendFrame mode
02294     // and there is less text than space
02295     else if ( !lastFormatted && bottom + 2 < availHeight &&
02296               (m_frames.last()->frameBehavior() == KWFrame::AutoExtendFrame&& !isProtectSize()) )
02297     {
02298         slotAfterFormattingTooMuchSpace( bottom );
02299         *abort = false;
02300     }
02301 
02302     if ( m_doc->processingType() == KWDocument::WP
02303          && this == m_doc->frameSet( 0 ) )
02304     {
02305         if ( m_lastTextDocHeight != textDocument()->height() )
02306         {
02307             m_lastTextDocHeight = textDocument()->height();
02308             emit mainTextHeightChanged();
02309         }
02310     }
02311 }
02312 
02313 // This is called when a text frame with behaviour AutoCreateNewFrame
02314 // has more text than available frame height, so we need to create a new page
02315 // so that a followup frame is created for this one
02316 bool KWTextFrameSet::createNewPageAndNewFrame( KoTextParag* lastFormatted, int /*difference*/ )
02317 {
02318     KWFrame* lastFrame = m_frames.last();
02319     // This is only going to help us if the new frame is reconnected. Otherwise bail out.
02320     if ( !lastFrame || lastFrame->newFrameBehavior() != KWFrame::Reconnect )  {
02321         kdDebug(32002) << name() << " : frame is AutoCreateNewFrame but not Reconnect !?!? Aborting." << endl;
02322         m_textobj->setLastFormattedParag( 0 );
02323         return true; // abort
02324     }
02325 
02326 //#ifdef DEBUG_FORMAT_MORE
02327     kdDebug(32002) << "createNewPageAndNewFrame creating new frame in frameset " << name() << endl;
02328 //#endif
02329     uint oldCount = m_frames.count();
02330     int lastPageNumber = m_doc->pageManager()->lastPageNumber();
02331     kdDebug(32002) << " last frame=" << lastFrame << " pagenum=" << lastFrame->pageNumber() << " lastPageNumber=" << lastPageNumber << "   m_frames count=" << oldCount << endl;
02332 
02333     // First create a new page for it if necessary
02334     if ( lastFrame->pageNumber() == lastPageNumber )
02335     {
02336         // Let's first check if it will give us more space than we
02337         // already have left in this page. Otherwise we'll loop infinitely.
02338 
02339         int heightWeWillGet = 0; // in LU
02340         if(isMainFrameset()) // is never added in the framesToCopyOnNewPage
02341             heightWeWillGet += m_doc->ptToLayoutUnitPixY( m_frames.last()->height() );
02342         else {
02343             QPtrList<KWFrame> framesToCopy = m_doc->framesToCopyOnNewPage( lastPageNumber );
02344             QPtrListIterator<KWFrame> frameIt( framesToCopy );
02345             for ( ; frameIt.current(); ++frameIt )
02346                 if (frameIt.current()->frameSet() == this &&
02347                     frameIt.current()->newFrameBehavior()==KWFrame::Reconnect)
02348                     heightWeWillGet += m_doc->ptToLayoutUnitPixY( frameIt.current()->height() );
02349         }
02350 
02351         // This logic doesn't applies to tables though, since they can be broken over multiple pages
02352         // TODO: lastFormatted->containsTable() or so (containsPageBreakableItem rather).
02353 
02354         // "difference" doesn't apply if we're pasting multiple paragraphs.
02355         // We want to compare the height of one paragraph, not all the missing height.
02356         KoTextParag* parag = lastFormatted ? lastFormatted : textDocument()->lastParag();
02357         // In fact the parag height isn't the right thing to test for - we should check
02358         // for the highest character that remains to be positioned.
02359         // Testcase: many big inline pictures in one paragraph.
02360         int paragHeight = parag->rect().height();
02361         kdDebug(32002) << "height we will get in the new page:" << heightWeWillGet << " parag " << parag << " height:" << paragHeight << endl;
02362         if ( heightWeWillGet < paragHeight && !m_groupmanager )
02363         {
02364             kdDebug(32002) << "not enough height on the new page, not worth it" << endl;
02365             m_textobj->setLastFormattedParag( 0 );
02366             return true; // abort
02367         }
02368 
02369         KWPage *page = m_doc->appendPage();
02370         if ( !m_doc->isLoading() )
02371             m_doc->afterInsertPage( page->pageNumber() );
02372         kdDebug(32002) << "now frames count=" << m_frames.count() << endl;
02373     }
02374 
02375     // Maybe creating the new page created the frame in this frameset, then we're done
02376     // Otherwise let's create it ourselves:
02377     if ( m_frames.count() == oldCount )
02378     {
02379         Q_ASSERT( !isMainFrameset() ); // ouch, should have gone to the appendPage case above...
02380         // Otherwise, create a new frame on next page
02381         kdDebug(32002) << "createNewPageAndNewFrame creating frame on page " << lastFrame->pageNumber()+1 << endl;
02382         KWFrame *frm = lastFrame->getCopy();
02383         frm->moveBy( 0, m_doc->pageManager()->page(frm)->height() );
02384         addFrame( frm );
02385     }
02386 
02387     updateFrames();
02388     Q_ASSERT(frame(0) && frame(0)->frameStack());
02389     frame(0)->frameStack()->update();
02392 
02393     // Reformat the last paragraph. If it's over the two pages, it will need
02394     // the new page (e.g. for inline frames that need internalToDocument to work)
02395     if ( lastFormatted )
02396         lastFormatted = lastFormatted->prev();
02397     else
02398         lastFormatted = textDocument()->lastParag();
02399 
02400     if ( lastFormatted )
02401     {
02402         m_textobj->setLastFormattedParag( lastFormatted );
02403         lastFormatted->invalidate( 0 );
02404         //This was a way to format the rest from here (recursively), but it didn't help much ensureCursorVisible()
02405         //So instead I fixed formatMore to return formatMore(2) itself.
02406         //m_textobj->formatMore( 2 );
02407         return false; // keep going
02408     }
02409     m_doc->delayedRepaintAllViews();
02410     return false; // all done
02411 }
02412 
02413 double KWTextFrameSet::footNoteSize( KWFrame *theFrame )
02414 {
02415     double tmp =0.0;
02416     int page = theFrame->pageNumber();
02417     QPtrListIterator<KWFrameSet> fit = m_doc->framesetsIterator();
02418     for ( ; fit.current() ; ++fit )
02419     {
02420         if((fit.current()->isFootNote() || fit.current()->isEndNote()) &&
02421            fit.current()->isVisible())
02422         {
02423             KWFrame * frm=fit.current()->frame( 0 );
02424             if(frm->pageNumber()==page )
02425                 tmp += frm->innerHeight()+m_doc->ptFootnoteBodySpacing();
02426         }
02427     }
02428     return tmp;
02429 }
02430 
02431 
02432 double KWTextFrameSet::footerHeaderSizeMax( KWFrame *theFrame )
02433 {
02434     KWPage *page = m_doc->pageManager()->page(theFrame);
02435     Q_ASSERT( page );
02436     if ( !page )
02437         return 0;
02438     double tmp = page->height() - page->bottomMargin() - page->topMargin() - 40;//default min 40 for page size
02439     bool header=theFrame->frameSet()->isAHeader();
02440     if( header ? m_doc->isHeaderVisible():m_doc->isFooterVisible() )
02441     {
02442         QPtrListIterator<KWFrameSet> fit = m_doc->framesetsIterator();
02443         for ( ; fit.current() ; ++fit )
02444         {
02445             bool state = header ? fit.current()->isAFooter():fit.current()->isAHeader();
02446             if(fit.current()->isVisible() && state)
02447             {
02448                 KWFrame * frm=fit.current()->frame( 0 );
02449                 if(frm->pageNumber()==page->pageNumber() )
02450                 {
02451                     return (tmp-frm->innerHeight()-footNoteSize( theFrame ));
02452                 }
02453             }
02454         }
02455     }
02456     if (theFrame->frameSet()->isHeaderOrFooter())
02457         return (tmp-footNoteSize( theFrame ));
02458 
02459     return tmp;
02460 }
02461 
02462 void KWTextFrameSet::frameResized( KWFrame *theFrame, bool invalidateLayout )
02463 {
02464     kdDebug(32002) << "KWTextFrameSet::frameResized " << theFrame << " " << *theFrame << " invalidateLayout=" << invalidateLayout << endl;
02465     if ( theFrame->height() < 0 )
02466         return; // safety!
02467 
02468     KWFrameSet * fs = theFrame->frameSet();
02469     Q_ASSERT( fs == this );
02470     fs->updateFrames(); // update e.g. available height
02471     Q_ASSERT(frame(0) && frame(0)->frameStack());
02472     frame(0)->frameStack()->update();
02473 
02474     theFrame->updateRulerHandles();
02475 
02476     // Do a full KWFrameLayout if this will have influence on other frames, i.e.:
02477     // * if we resized the last main text frame (the one before the first endnote)
02478     // * if we resized an endnote
02479     // Delay it though, to get the full height first.
02480     if ( fs->isMainFrameset() || fs->isEndNote() )
02481         m_doc->delayedRecalcFrames( theFrame->pageNumber() );
02482     // * if we resized a header, footer, or footnote
02483     else if ( fs->frameSetInfo() != KWFrameSet::FI_BODY )
02484         m_doc->recalcFrames( theFrame->pageNumber(), -1 ); // warning this can delete theFrame!
02485 
02486     // m_doc->frameChanged( theFrame );
02487     // Warning, can't call layout() (frameChanged calls it)
02488     // from here, since it calls formatMore() !
02489     if ( invalidateLayout )
02490         m_doc->invalidate(this);
02491 
02492     // Can't repaint directly, we might be in a paint event already
02493     m_doc->delayedRepaintAllViews();
02494 }
02495 
02496 bool KWTextFrameSet::isFrameEmpty( KWFrame * theFrame )
02497 {
02498     KoTextParag * lastParag = textDocument()->lastParag();
02499     // The problem is that if we format things here, and don't emit afterFormatting,
02500     // we won't resize autoresize frames properly etc. (e.g. endnotes)
02501     // Testcase for this problem: werner's footnote-1.doc
02502     //ensureFormatted( lastParag, false ); // maybe true here would do too? slow if maintextframeset though.
02503     if ( !lastParag->isValid() )
02504         return false; // we don't know yet
02505     int bottom = lastParag->rect().top() + lastParag->rect().height();
02506 
02507     if ( theFrame->frameSet() == this ) // safety check
02508     {
02509         kdDebug() << "KWTextFrameSet::isFrameEmpty text bottom=(LU) " << bottom << " theFrame=" << theFrame << " " << *theFrame << " its internalY(LU)=" << m_doc->ptToLayoutUnitPixY( theFrame->internalY() ) << endl;
02510         return bottom < m_doc->ptToLayoutUnitPixY( theFrame->internalY() );
02511     }
02512 
02513     kdWarning() << "KWTextFrameSet::isFrameEmpty called for frame " << theFrame << " which isn't a child of ours!" << endl;
02514     if ( theFrame->frameSet()!=0L && theFrame->frameSet()->name()!=0L)
02515         kdDebug() << "(this is " << name() << " and the frame belongs to " << theFrame->frameSet()->name() << ")" << endl;
02516     return false;
02517 }
02518 
02519 bool KWTextFrameSet::canRemovePage( int num )
02520 {
02521     kdDebug() << "KWTextFrameSet(" << name() << ")::canRemovePage " << num << endl;
02522 
02523     // No frame on that page ? ok for us then
02524     if ( num < m_firstPage || num >= (int)m_framesInPage.size() + m_firstPage ) {
02525         kdDebug() << "No frame on that page. Number of frames: " << frameCount() << endl;
02526         return true;
02527     }
02528 
02529     QPtrListIterator<KWFrame> frameIt( framesInPage( num ) );
02530     for ( ; frameIt.current(); ++frameIt )
02531     {
02532         KWFrame * theFrame = frameIt.current();
02533         kdDebug() << "canRemovePage: looking at " << theFrame << " pageNum=" << theFrame->pageNumber() << endl;
02534         Q_ASSERT( theFrame->pageNumber() == num );
02535         Q_ASSERT( theFrame->frameSet() == this );
02536         bool isEmpty = isFrameEmpty( theFrame );
02537         kdDebug() << "KWTextFrameSet(" << name() << ")::canRemovePage"
02538                   << " found a frame on page " << num << " empty:" << isEmpty << endl;
02539         // Ok, so we have a frame on that page -> we can't remove it unless it's a copied frame OR it's empty
02540         bool isCopy = theFrame->isCopy() && frameIt.current() != m_frames.first();
02541         if ( !isCopy && !isEmpty )
02542             return false;
02543     }
02544     return true;
02545 }
02546 
02547 void KWTextFrameSet::deleteFrame( unsigned int num, bool remove, bool recalc )
02548 {
02549     KWFrame *frm = m_frames.at( num );
02550     kdDebug() << "KWTextFrameSet(" << name() << ")::deleteFrame " << frm << " (" << num << ")" << endl;
02551     if ( frm )
02552         emit frameDeleted( frm );
02553     KWFrameSet::deleteFrame( num, remove, recalc );
02554 }
02555 
02556 void KWTextFrameSet::updateViewArea( QWidget * w, KWViewMode* viewMode, const QPoint & nPointBottom )
02557 {
02558     if (!isVisible(viewMode))
02559         return;
02560     int ah = availableHeight(); // make sure that it's not -1
02561 #ifdef DEBUG_VIEWAREA
02562     kdDebug(32002) << "KWTextFrameSet::updateViewArea " << (void*)w << " " << w->name()
02563                      << " nPointBottom=" << nPointBottom.x() << "," << nPointBottom.y()
02564                      << " availHeight=" << ah << " textDocument()->height()=" << textDocument()->height() << endl;
02565 #endif
02566 
02567     // Find last page that is visible
02568     int maxPage = m_doc->pageManager()->pageNumber(m_doc->unzoomItY( nPointBottom.y() ));
02569     int maxY = 0;
02570     if ( maxPage < m_firstPage || maxPage >= (int)m_framesInPage.size() + m_firstPage )
02571         maxY = ah;
02572     else
02573     {
02574         // Find frames on that page, and keep the max bottom, in internal coordinates
02575         QPtrListIterator<KWFrame> frameIt( framesInPage( maxPage ) );
02576         for ( ; frameIt.current(); ++frameIt )
02577         {
02578             maxY = QMAX( maxY, m_doc->ptToLayoutUnitPixY( frameIt.current()->internalY() + frameIt.current()->innerHeight() ) );
02579         }
02580     }
02581 #ifdef DEBUG_VIEWAREA
02582     kdDebug(32002) << "KWTextFrameSet (" << name() << ")::updateViewArea maxY now " << maxY << endl;
02583 #endif
02584     m_textobj->setViewArea( w, maxY );
02585     m_textobj->formatMore( 2 );
02586 }
02587 
02588 KCommand * KWTextFrameSet::setPageBreakingCommand( KoTextCursor * cursor, int pageBreaking )
02589 {
02590     if ( !textDocument()->hasSelection( KoTextDocument::Standard ) &&
02591          static_cast<KWTextParag *>(cursor->parag())->pageBreaking() == pageBreaking )
02592         return 0L; // No change needed.
02593 
02594     m_textobj->emitHideCursor();
02595 
02596     m_textobj->storeParagUndoRedoInfo( cursor );
02597 
02598     if ( !textDocument()->hasSelection( KoTextDocument::Standard ) ) {
02599         KWTextParag *parag = static_cast<KWTextParag *>( cursor->parag() );
02600         parag->setPageBreaking( pageBreaking );
02601         m_textobj->setLastFormattedParag( cursor->parag() );
02602     }
02603     else
02604     {
02605         KoTextParag *start = textDocument()->selectionStart( KoTextDocument::Standard );
02606         KoTextParag *end = textDocument()->selectionEnd( KoTextDocument::Standard );
02607         m_textobj->setLastFormattedParag( start );
02608         for ( ; start && start != end->next() ; start = start->next() )
02609             static_cast<KWTextParag *>(start)->setPageBreaking( pageBreaking );
02610     }
02611 
02612     m_textobj->formatMore( 2 );
02613     emit repaintChanged( this );
02614     KoTextObject::UndoRedoInfo & undoRedoInfo = m_textobj->undoRedoInfoStruct();
02615     undoRedoInfo.newParagLayout.pageBreaking = pageBreaking;
02616     KoTextParagCommand *cmd = new KoTextParagCommand(
02617         textDocument(), undoRedoInfo.id, undoRedoInfo.eid,
02618         undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout,
02619         KoParagLayout::PageBreaking );
02620     textDocument()->addCommand( cmd );
02621     undoRedoInfo.clear();
02622     m_textobj->emitShowCursor();
02623     m_textobj->emitUpdateUI( true );
02624     m_textobj->emitEnsureCursorVisible();
02625     // ## find a better name for the command
02626     return new KoTextCommand( m_textobj, /*cmd, */i18n("Change Paragraph Attribute") );
02627 }
02628 
02629 KCommand * KWTextFrameSet::pasteOasis( KoTextCursor * cursor, const QByteArray & data, bool removeSelected )
02630 {
02631     if (protectContent() )
02632         return 0;
02633 
02634     kdDebug(32001) << "KWTextFrameSet::pasteOasis data:" << data.size() << " bytes" << endl;
02635     KMacroCommand * macroCmd = new KMacroCommand( i18n("Paste") );
02636     if ( removeSelected && textDocument()->hasSelection( KoTextDocument::Standard ) )
02637         macroCmd->addCommand( m_textobj->removeSelectedTextCommand( cursor, KoTextDocument::Standard ) );
02638     m_textobj->emitHideCursor();
02639     m_textobj->setLastFormattedParag( cursor->parag()->prev() ?
02640                            cursor->parag()->prev() : cursor->parag() );
02641 
02642     KWOasisPasteCommand * cmd = new KWOasisPasteCommand( textDocument(), cursor->parag()->paragId(), cursor->index(), data );
02643     textDocument()->addCommand( cmd );
02644 
02645     macroCmd->addCommand( new KoTextCommand( m_textobj, /*cmd, */QString::null ) );
02646 
02647     *cursor = *( cmd->execute( cursor ) );
02648 
02649     // not enough when pasting many pages. We need the cursor's parag to be formatted.
02650     //m_textobj->formatMore( 2 );
02651     ensureFormatted( cursor->parag() );
02652 
02653     emit repaintChanged( this );
02654     m_textobj->emitEnsureCursorVisible();
02655     m_textobj->emitUpdateUI( true );
02656     m_textobj->emitShowCursor();
02657     m_textobj->selectionChangedNotify();
02658     return macroCmd;
02659 }
02660 
02661 void KWTextFrameSet::insertTOC( KoTextCursor * cursor )
02662 {
02663     m_textobj->emitHideCursor();
02664     KMacroCommand * macroCmd = new KMacroCommand( i18n("Insert Table of Contents") );
02665 
02666     // Remove old TOC
02667 
02668     KoTextCursor *cur= KWInsertTOCCommand::removeTOC( this, cursor, macroCmd );
02669 
02670     // Insert new TOC
02671 
02672     KoTextDocCommand * cmd = new KWInsertTOCCommand( this,cur ? cur->parag(): cursor->parag() );
02673     textDocument()->addCommand( cmd );
02674     macroCmd->addCommand( new KoTextCommand( m_textobj, QString::null ) );
02675     *cursor = *( cmd->execute( cursor ) );
02676 
02677     m_textobj->setLastFormattedParag( textDocument()->firstParag() );
02678     m_textobj->formatMore( 2 );
02679     emit repaintChanged( this );
02680     m_textobj->emitEnsureCursorVisible();
02681     m_textobj->emitUpdateUI( true );
02682     m_textobj->emitShowCursor();
02683 
02684     m_doc->addCommand( macroCmd );
02685 }
02686 
02687 KNamedCommand* KWTextFrameSet::insertFrameBreakCommand( KoTextCursor *cursor )
02688 {
02689     KMacroCommand* macroCmd = new KMacroCommand( QString::null );
02690     macroCmd->addCommand( m_textobj->insertParagraphCommand( cursor ) );
02691     KWTextParag *parag = static_cast<KWTextParag *>( cursor->parag() );
02692     if(parag->prev()) {
02693         parag=static_cast<KWTextParag *> (parag->prev());
02694         cursor->setParag( parag );
02695         cursor->setIndex( parag->length() - 1 );
02696     }
02697     macroCmd->addCommand( setPageBreakingCommand( cursor, parag->pageBreaking() | KoParagLayout::HardFrameBreakAfter ) );
02698     Q_ASSERT( parag->next() );
02699     if ( parag->next() ) {
02700         cursor->setParag( parag->next() );
02701         cursor->setIndex( 0 );
02702     }
02703     return macroCmd;
02704 }
02705 
02706 void KWTextFrameSet::insertFrameBreak( KoTextCursor *cursor )
02707 {
02708     clearUndoRedoInfo();
02709     m_textobj->emitHideCursor();
02710     KNamedCommand* cmd = insertFrameBreakCommand( cursor );
02711     cmd->setName( i18n( "Insert Break After Paragraph" ) );
02712     m_doc->addCommand( cmd );
02713 
02714     m_textobj->setLastFormattedParag( cursor->parag() );
02715     m_textobj->formatMore( 2 );
02716     emit repaintChanged( this );
02717     m_textobj->emitEnsureCursorVisible();
02718     m_textobj->emitUpdateUI( true );
02719     m_textobj->emitShowCursor();
02720 }
02721 
02722 QRect KWTextFrameSet::paragRect( KoTextParag * parag ) const
02723 {
02724     // ## Warning. Imagine a paragraph cut in two pieces (at the line-level),
02725     // between two columns. A single rect in internal coords, but two rects in
02726     // normal coords. QRect( topLeft, bottomRight ) is just plain wrong.
02727     // Currently this method is only used for "ensure visible" so that's fine, but
02728     // we shouldn't use it for more precise stuff.
02729     KoPoint p;
02730     (void)internalToDocument( parag->rect().topLeft(), p );
02731     QPoint topLeft = m_doc->zoomPoint( p );
02732     (void)internalToDocument( parag->rect().bottomRight(), p );
02733     QPoint bottomRight = m_doc->zoomPoint( p );
02734     return QRect( topLeft, bottomRight );
02735 }
02736 
02737 void KWTextFrameSet::findPosition( const KoPoint &dPoint, KoTextParag * & parag, int & index )
02738 {
02739     KoTextCursor cursor( textDocument() );
02740 
02741     QPoint iPoint;
02742     if ( documentToInternal( dPoint, iPoint ) )
02743     {
02744         cursor.place( iPoint, textDocument()->firstParag() );
02745         parag = cursor.parag();
02746         index = cursor.index();
02747     }
02748     else
02749     {
02750         // Not found, maybe under everything ?
02751         parag = textDocument()->lastParag();
02752         if ( parag )
02753             index = parag->length() - 1;
02754     }
02755 }
02756 
02757 bool KWTextFrameSet::minMaxInternalOnPage( int pageNum, int& topLU, int& bottomLU ) const
02758 {
02759     QPtrListIterator<KWFrame> frameIt( framesInPage( pageNum ) );
02760     if ( !frameIt.current() )
02761         return false;
02762 
02763     // Look at all frames in the page, and keep min and max "internalY" positions
02764     double topPt = frameIt.current()->internalY();
02765     double bottomPt = topPt + frameIt.current()->height();
02766 
02767     for ( ; frameIt.current(); ++frameIt )
02768     {
02769         double y = frameIt.current()->internalY();
02770         topPt = QMIN( topPt, y );
02771         bottomPt = QMAX( bottomPt, y + frameIt.current()->height() );
02772     }
02773     // Convert to layout units
02774     topLU = m_doc->ptToLayoutUnitPixY( topPt );
02775     bottomLU = m_doc->ptToLayoutUnitPixY( bottomPt );
02776     return true;
02777 }
02778 
02779 KoTextParag* KWTextFrameSet::paragAtLUPos( int yLU ) const
02780 {
02781     KoTextParag* parag = textDocument()->firstParag();
02782     for ( ; parag ; parag = parag->next() )
02783     {
02784         if ( parag->rect().bottom() >= yLU )
02785             return parag;
02786     }
02787     return 0L;
02788 }
02789 
02790 KCommand * KWTextFrameSet::deleteAnchoredFrame( KWAnchor * anchor )
02791 {
02792     kdDebug() << "KWTextFrameSet::deleteAnchoredFrame anchor->index=" << anchor->index() << endl;
02793     Q_ASSERT( anchor );
02794     KoTextCursor c( textDocument() );
02795     c.setParag( anchor->paragraph() );
02796     c.setIndex( anchor->index() );
02797 
02798     textDocument()->setSelectionStart( KoTextDocument::Temp, &c );
02799     c.setIndex( anchor->index() + 1 );
02800     textDocument()->setSelectionEnd( KoTextDocument::Temp, &c );
02801     KCommand *cmd = m_textobj->removeSelectedTextCommand( &c, KoTextDocument::Temp );
02802 
02803     m_doc->repaintAllViews();
02804     return cmd;
02805 }
02806 
02807 bool KWTextFrameSet::hasSelection() const
02808 {
02809     return m_textobj->hasSelection();
02810 }
02811 
02812 QString KWTextFrameSet::selectedText() const
02813 {
02814     return m_textobj->selectedText();
02815 }
02816 
02817 QString KWTextFrameSet::toPlainText() const
02818 {
02819     return m_textobj->textDocument()->plainText();
02820 }
02821 
02822 void KWTextFrameSet::highlightPortion( KoTextParag * parag, int index, int length, KWCanvas * canvas, bool repaint, KDialogBase* dialog )
02823 {
02824     Q_ASSERT( isVisible() );
02825     Q_ASSERT( m_textobj->isVisible() );
02826     //kdDebug() << "highlighting in " << name() << " parag=" << parag->paragId() << " index=" << index << " repaint=" << repaint << endl;
02827     m_textobj->highlightPortion( parag, index, length, repaint );
02828     if ( repaint ) {
02829         // Position the cursor
02830         canvas->editTextFrameSet( this, parag, index );
02831         // Ensure text is fully visible
02832         QRect expose = canvas->viewMode()->normalToView( paragRect( parag ) );
02833         canvas->ensureVisible( (expose.left()+expose.right()) / 2,  // point = center of the rect
02834                                (expose.top()+expose.bottom()) / 2,
02835                                (expose.right()-expose.left()) / 2,  // margin = half-width of the rect
02836                                (expose.bottom()-expose.top()) / 2);
02837         if ( dialog ) {
02838             //kdDebug() << k_funcinfo << " dialog=" << dialog << " avoiding rect=" << expose << endl;
02839             QRect globalRect( expose );
02840             globalRect.moveTopLeft( canvas->mapToGlobal( globalRect.topLeft() ) );
02841             KDialog::avoidArea( dialog, globalRect );
02842         }
02843     }
02844 }
02845 
02846 void KWTextFrameSet::removeHighlight( bool repaint )
02847 {
02848     m_textobj->removeHighlight( repaint );
02849 }
02850 
02851 void KWTextFrameSet::clearUndoRedoInfo()
02852 {
02853     m_textobj->clearUndoRedoInfo();
02854 }
02855 
02856 void KWTextFrameSet::applyStyleChange( KoStyleChangeDefMap changed )
02857 {
02858     m_textobj->applyStyleChange( changed );
02859 }
02860 
02861 // KoTextFormatInterface methods
02862 KoTextFormat *KWTextFrameSet::currentFormat() const
02863 {
02864     return m_textobj->currentFormat();
02865 }
02866 
02867 KCommand *KWTextFrameSet::setChangeCaseOfTextCommand(KoChangeCaseDia::TypeOfCase _type)
02868 {
02869     KoTextDocument *textdoc = m_textobj->textDocument();
02870     textdoc->selectAll( KoTextDocument::Standard );
02871     KoTextCursor *cursor = new KoTextCursor( textDocument() );
02872     KCommand* cmd = m_textobj->changeCaseOfText(cursor, _type);
02873     textdoc->removeSelection( KoTextDocument::Standard );
02874     delete cursor;
02875     return cmd;
02876 }
02877 
02878 
02879 KCommand *KWTextFrameSet::setFormatCommand( const KoTextFormat * newFormat, int flags, bool zoomFont )
02880 {
02881     m_textobj->textDocument()->selectAll( KoTextDocument::Temp );
02882     KCommand *cmd = m_textobj->setFormatCommand( 0L, 0L, newFormat, flags, zoomFont, KoTextDocument::Temp );
02883     m_textobj->textDocument()->removeSelection( KoTextDocument::Temp );
02884     return cmd;
02885 }
02886 
02887 const KoParagLayout * KWTextFrameSet::currentParagLayoutFormat() const
02888 {
02889     return m_textobj->currentParagLayoutFormat();
02890 }
02891 
02892 bool KWTextFrameSet::rtl() const
02893 {
02894     return m_textobj->rtl();
02895 }
02896 
02897 
02898 KCommand *KWTextFrameSet::setParagLayoutFormatCommand( KoParagLayout *newLayout,int flags, int marginIndex)
02899 {
02900     return m_textobj->setParagLayoutFormatCommand(newLayout, flags, marginIndex);
02901 }
02902 
02903 class KWFootNoteVarList : public QPtrList< KWFootNoteVariable >
02904 {
02905 protected:
02906     virtual int compareItems(QPtrCollection::Item a, QPtrCollection::Item b)
02907     {
02908         KWFootNoteVariable* vara = ((KWFootNoteVariable *)a);
02909         KWFootNoteVariable* varb = ((KWFootNoteVariable *)b);
02910         if ( vara->paragraph() == varb->paragraph() ) {
02911             // index() is a bit slow. But this is only called when there are
02912             // two footnotes in the same paragraph.
02913             int indexa = vara->index();
02914             int indexb = varb->index();
02915             return indexa < indexb ? -1 : indexa == indexb ? 0 : 1;
02916         }
02917         if ( vara->paragraph()->paragId() < varb->paragraph()->paragId() )
02918             return -1;
02919         return 1;
02920     }
02921 };
02922 
02923 void KWTextFrameSet::renumberFootNotes( bool repaint )
02924 {
02925     KWFootNoteVarList lst;
02926     QPtrListIterator<KoTextCustomItem> cit( textDocument()->allCustomItems() );
02927     for ( ; cit.current() ; ++cit )
02928     {
02929         KWFootNoteVariable *fnv = dynamic_cast<KWFootNoteVariable *>( cit.current() );
02930         if (fnv && !fnv->isDeleted() && (fnv->frameSet() && !fnv->frameSet()->isDeleted()))
02931             lst.append( fnv );
02932     }
02933     lst.sort();
02934     short int footNoteVarNumber = 0; // absolute order number [internal, not saved nor displayed]
02935     short int endNoteVarNumber = 0;
02936     short int footNoteNumDisplay = 1; // the number being displayed
02937     short int endNoteNumDisplay = 1;
02938     bool needRepaint = false;
02939     QPtrListIterator< KWFootNoteVariable > vit( lst );
02940 
02941     //create a list with all manual footnotes numbers
02942     QValueList<int> addedNums;
02943     for ( ; vit.current() ; ++vit )
02944     {
02945         KWFootNoteVariable* var = vit.current();
02946         if ( var->numberingType()==KWFootNoteVariable::Manual )
02947         {
02948             uint const num = var->text().toUInt();
02949             if ( num != 0 )
02950                 addedNums.append( num );
02951         }
02952     }
02953 
02954     for ( vit.toFirst() ; vit.current() ; )
02955     {
02956         KWFootNoteVariable* var = vit.current();
02957         bool endNote = var->noteType() == EndNote;
02958         short int & varNumber = endNote ? endNoteVarNumber : footNoteVarNumber;
02959         short int & numDisplay = endNote ? endNoteNumDisplay : footNoteNumDisplay;
02960         ++varNumber;
02961         bool changed = false;
02962         if ( varNumber != var->num() || var->numberingType()==KWFootNoteVariable::Manual )
02963         {
02964             changed = true;
02965             var->setNum( varNumber );
02966         }
02967         if ( var->numberingType()==KWFootNoteVariable::Auto )
02968         {
02969             if ( addedNums.contains( numDisplay ) != 0 ) // the automatic generated number should not be equal to a manual one
02970             {
02971                 numDisplay++;
02972                 continue; //try with the next number
02973             }
02974             if ( numDisplay != var->numDisplay() )
02975             {
02976                 changed = true;
02977                 var->setNumDisplay( numDisplay );
02978             }
02979             numDisplay++;
02980         }
02981         if ( changed )
02982         {
02983             if ( var->frameSet() ) //safety
02984             {
02985                 QString fsName = endNote ? i18n("Endnote %1") : i18n("Footnote %1");
02986                 if ( var->numberingType()== KWFootNoteVariable::Manual)
02987                     var->frameSet()->setName( m_doc->generateFramesetName(fsName));
02988                 else
02989                     var->frameSet()->setName( fsName.arg( var->text() ) );
02990                 var->frameSet()->setCounterText( var->text() );
02991             }
02992             var->resize();
02993             var->paragraph()->invalidate(0);
02994             var->paragraph()->setChanged( true );
02995             needRepaint = true;
02996         }
02997         ++vit;
02998     }
02999     if ( needRepaint && repaint )
03000         m_doc->slotRepaintChanged( this );
03001 }
03002 
03003 KoTextDocCommand *KWTextFrameSet::deleteTextCommand( KoTextDocument *textdoc, int id, int index, const QMemArray<KoTextStringChar> & str, const CustomItemsMap & customItemsMap, const QValueList<KoParagLayout> & oldParagLayouts )
03004 {
03005     return new KWTextDeleteCommand( textdoc, id, index, str, customItemsMap, oldParagLayouts );
03006 }
03007 
03008 QByteArray KWTextFrameSet::sortText(SortType type) const
03009 {
03010     const KoTextCursor c1 = textDocument()->selectionStartCursor(KoTextDocument::Standard );
03011     const KoTextCursor c2 = textDocument()->selectionEndCursor( KoTextDocument::Standard );
03012     if ( c1.parag() == c2.parag() )
03013         return QByteArray();
03014     else
03015     {
03016         // ( paragraph text -> paragraph ) map. Note that this sorts on the key automatically.
03017         QMap<QString, const KoTextParag*> sortMap;
03018         sortMap.insert( c1.parag()->toString(0), c1.parag() );
03019 
03020         const KoTextParag *p = c1.parag()->next();
03021         while ( p && p != c2.parag() ) {
03022             sortMap.insert( p->toString(0), p );
03023             p = p->next();
03024         }
03025         sortMap.insert( c2.parag()->toString(0), c2.parag());
03026 
03027         typedef QValueList<const KoTextParag *> ParagList;
03028         ParagList sortedParags = sortMap.values();
03029         if ( type == KW_SORTDECREASE )
03030         {
03031             // I could use an STL algorithm here, but only if Qt was compiled with STL support...
03032             ParagList newList;
03033             for ( ParagList::const_iterator it = sortedParags.begin(),
03034                                            end = sortedParags.end();
03035                   it != end ; ++it ) {
03036                 newList.prepend( *it );
03037             }
03038             sortedParags = newList;
03039         }
03040 
03041         KWOasisSaver oasisSaver( m_doc );
03042         oasisSaver.saveParagraphs( sortedParags );
03043         if ( !oasisSaver.finish() )
03044             return QByteArray();
03045         return oasisSaver.data();
03046     }
03047 }
03048 
03049 // This is used when loading (KWTextDocument::loadOasisFootnote)
03050 // and when inserting from the GUI (KWTextFrameSetEdit::insertFootNote),
03051 // so don't add any 'repaint' or 'recalc' code here
03052 KWFootNoteFrameSet * KWTextFrameSet::insertFootNote( NoteType noteType, KWFootNoteVariable::Numbering numType, const QString &manualString )
03053 {
03054      kdDebug() << "KWTextFrameSetEdit::insertFootNote " << endl;
03055      KWDocument * doc = m_doc;
03056      KWFootNoteVariable * var = new KWFootNoteVariable( textDocument(), doc->variableFormatCollection()->format( "NUMBER" ), doc->variableCollection(), doc );
03057      var->setNoteType( noteType );
03058      var->setNumberingType( numType );
03059      if ( numType == KWFootNoteVariable::Manual )
03060          var->setManualString( manualString );
03061 
03062      // Now create text frameset which will hold the variable's contents
03063      KWFootNoteFrameSet *fs = new KWFootNoteFrameSet( doc, i18n( "Footnotes" ) );
03064      fs->setFrameSetInfo( KWFrameSet::FI_FOOTNOTE );
03065 
03066      doc->addFrameSet( fs );
03067 
03068      // Bind the footnote variable and its text frameset
03069      var->setFrameSet( fs );
03070      fs->setFootNoteVariable( var );
03071 
03072      return fs;
03073 }
03074 
03075 KoVariable* KWTextFrameSet::variableUnderMouse( const KoPoint& dPoint )
03076 {
03077     QPoint iPoint;
03078     if ( documentToInternal( dPoint, iPoint ) )
03079         return textObject()->variableAtPoint( iPoint );
03080     return 0;
03081 }
03082 
03083 KoLinkVariable* KWTextFrameSet::linkVariableUnderMouse( const KoPoint& dPoint )
03084 {
03085     QPoint iPoint;
03086     if ( documentToInternal( dPoint, iPoint ) )
03087     {
03088         KoLinkVariable* linkVariable = dynamic_cast<KoLinkVariable *>( textObject()->variableAtPoint( iPoint ) );
03089         return linkVariable;
03090     }
03091     return 0;
03092 }
03093 
03095 
03096 KWTextFrameSetEdit::KWTextFrameSetEdit( KWTextFrameSet * fs, KWCanvas * canvas )
03097     : KoTextView( fs->textObject() ), KWFrameSetEdit( fs, canvas ), m_rtl( false )
03098 {
03099     setBackSpeller( fs->kWordDocument()->backSpeller() );
03100     //kdDebug(32001) << "KWTextFrameSetEdit::KWTextFrameSetEdit " << fs->name() << endl;
03101     KoTextView::setReadWrite( fs->kWordDocument()->isReadWrite() );
03102     KoTextObject* textobj = fs->textObject();
03103     connect( textobj, SIGNAL( selectionChanged(bool) ), canvas, SIGNAL( selectionChanged(bool) ) );
03104     connect( fs, SIGNAL( frameDeleted(KWFrame *) ), this, SLOT( slotFrameDeleted(KWFrame *) ) );
03105     connect( textView(), SIGNAL( cut() ), SLOT( cut() ) );
03106     connect( textView(), SIGNAL( copy() ), SLOT( copy() ) );
03107     connect( textView(), SIGNAL( paste() ), SLOT( paste() ) );
03108     updateUI( true, true );
03109 
03110     if( canvas->gui() && canvas->gui()->getHorzRuler())
03111     {
03112         if ( !textobj->protectContent() )
03113             canvas->gui()->getHorzRuler()->changeFlags(KoRuler::F_INDENTS | KoRuler::F_TABS);
03114         else
03115             canvas->gui()->getHorzRuler()->changeFlags(0);
03116     }
03117 
03118     setOverwriteMode( canvas->overwriteMode() );
03119 }
03120 
03121 KWTextFrameSetEdit::~KWTextFrameSetEdit()
03122 {
03123     //kdDebug(32001) << "KWTextFrameSetEdit::~KWTextFrameSetEdit" << endl;
03124     //m_canvas->gui()->getHorzRuler()->changeFlags(0);
03125 }
03126 
03127 KoTextViewIface* KWTextFrameSetEdit::dcopObject()
03128 {
03129     if ( !dcop )
03130         dcop = new KWordTextFrameSetEditIface( this );
03131 
03132     return dcop;
03133 }
03134 
03135 void KWTextFrameSetEdit::terminate(bool removeSelection)
03136 {
03137     disconnect( textView()->textObject(), SIGNAL( selectionChanged(bool) ), m_canvas, SIGNAL( selectionChanged(bool) ) );
03138     textView()->terminate(removeSelection);
03139 }
03140 
03141 void KWTextFrameSetEdit::slotFrameDeleted( KWFrame *frm )
03142 {
03143     if ( m_currentFrame == frm )
03144         m_currentFrame = 0L;
03145 }
03146 
03147 void KWTextFrameSetEdit::paste()
03148 {
03149     QMimeSource *data = QApplication::clipboard()->data();
03150     int provides = KWView::checkClipboard( data );
03151     pasteData( data, provides, false );
03152 }
03153 
03154 void KWTextFrameSetEdit::pasteData( QMimeSource* data, int provides, bool drop )
03155 {
03156     if ( provides & KWView::ProvidesOasis )
03157     {
03158         KCommand* cmd = pasteOasisCommand( data );
03159         if ( cmd )
03160             frameSet()->kWordDocument()->addCommand(cmd);
03161     }
03162     else if ( provides & KWView::ProvidesPlainText )
03163     {
03164         // Note: QClipboard::text() seems to do a better job than encodedData( "text/plain" )
03165         // In particular it handles charsets (in the mimetype).
03166         const QString text = QApplication::clipboard()->text();
03167         const bool removeSelected = !drop;
03168         if ( !text.isEmpty() )
03169             textObject()->pasteText( cursor(), text, currentFormat(), removeSelected );
03170     }
03171     else {
03172         kdWarning(32002) << "Unhandled case in KWTextFrameSetEdit::pasteData: provides=" << provides << endl;
03173     }
03174     // be sure that the footnote number didn't got erased
03175     KWFootNoteFrameSet *footNote = dynamic_cast<KWFootNoteFrameSet *>(textFrameSet());
03176     if ( footNote )
03177     {
03178         KoParagCounter *counter = footNote->textDocument()->firstParag()->counter();
03179         if ( !counter || ( counter->numbering() != KoParagCounter::NUM_FOOTNOTE ) )
03180             footNote->setCounterText( footNote->footNoteVariable()->text() );
03181             frameSet()->kWordDocument()->slotRepaintChanged( frameSet() );
03182     }
03183 }
03184 
03185 KCommand* KWTextFrameSetEdit::pasteOasisCommand( QMimeSource* data )
03186 {
03187     // Find which mimetype it was (could be oasis text, oasis presentation etc.)
03188     QCString returnedTypeMime = KoTextObject::providesOasis( data );
03189     if ( !returnedTypeMime.isEmpty() )
03190     {
03191         QByteArray arr = data->encodedData( returnedTypeMime );
03192         Q_ASSERT( !arr.isEmpty() );
03193         if ( arr.size() )
03194             return textFrameSet()->pasteOasis( cursor(), arr, true );
03195     }
03196     return 0;
03197 }
03198 
03199 void KWTextFrameSetEdit::cut()
03200 {
03201     if ( textDocument()->hasSelection( KoTextDocument::Standard ) ) {
03202         copy();
03203         textObject()->removeSelectedText( cursor() );
03204     }
03205 }
03206 
03207 void KWTextFrameSetEdit::copy()
03208 {
03209     if ( textDocument()->hasSelection( KoTextDocument::Standard ) ) {
03210         QDragObject *drag = newDrag( 0 );
03211         QApplication::clipboard()->setData( drag );
03212     }
03213 }
03214 
03215 bool KWTextFrameSetEdit::doIgnoreDoubleSpace(KoTextParag * parag,
03216         int index,QChar ch )
03217 {
03218     if( textFrameSet()->kWordDocument()->allowAutoFormat())
03219     {
03220         KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
03221         if(  autoFormat )
03222         {
03223             return autoFormat->doIgnoreDoubleSpace( parag, index,ch );
03224         }
03225     }
03226     return false;
03227 
03228 }
03229 
03230 
03231 void KWTextFrameSetEdit::doAutoFormat( KoTextCursor* cursor, KoTextParag *parag, int index, QChar ch )
03232 {
03233     if( textFrameSet()->kWordDocument()->allowAutoFormat() )
03234     {
03235         KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
03236         if( autoFormat )
03237             autoFormat->doAutoFormat( cursor, parag, index, ch, textObject());
03238     }
03239 }
03240 
03241 bool KWTextFrameSetEdit::doCompletion( KoTextCursor* cursor, KoTextParag *parag, int index )
03242 {
03243     if( textFrameSet()->kWordDocument()->allowAutoFormat() )
03244     {
03245         KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
03246         if( autoFormat )
03247             return autoFormat->doCompletion(  cursor, parag, index, textObject());
03248     }
03249     return false;
03250 }
03251 
03252 bool KWTextFrameSetEdit::doToolTipCompletion( KoTextCursor* cursor, KoTextParag *parag, int index, int keyPressed )
03253 {
03254     if( textFrameSet()->kWordDocument()->allowAutoFormat() )
03255     {
03256         KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
03257         if( autoFormat )
03258             return autoFormat->doToolTipCompletion(  cursor, parag, index, textObject(), keyPressed);
03259     }
03260     return false;
03261 }
03262 
03263 void KWTextFrameSetEdit::showToolTipBox(KoTextParag *parag, int index, QWidget *widget, const QPoint &pos)
03264 {
03265     if( textFrameSet()->kWordDocument()->allowAutoFormat() )
03266     {
03267         KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
03268         if( autoFormat )
03269             autoFormat->showToolTipBox(parag, index, widget, pos);
03270     }
03271 }
03272 
03273 void KWTextFrameSetEdit::removeToolTipCompletion()
03274 {
03275     if( textFrameSet()->kWordDocument()->allowAutoFormat() )
03276     {
03277         KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
03278         if( autoFormat )
03279             autoFormat->removeToolTipCompletion();
03280     }
03281 }
03282 
03283 void KWTextFrameSetEdit::textIncreaseIndent()
03284 {
03285     kdDebug(32001) << "Increasing list" << endl;
03286     m_canvas->gui()->getView()->textIncreaseIndent();
03287 }
03288 
03289 bool KWTextFrameSetEdit::textDecreaseIndent()
03290 {
03291     if (currentLeftMargin()>0)
03292     {
03293         kdDebug(32001) << "Decreasing list" << endl;
03294         m_canvas->gui()->getView()->textDecreaseIndent();
03295         return true;
03296     }
03297     else
03298         return false;
03299 }
03300 
03301 void KWTextFrameSetEdit::startDrag()
03302 {
03303     textView()->dragStarted();
03304     m_canvas->dragStarted();
03305     QDragObject *drag = newDrag( m_canvas->viewport() );
03306     if ( !frameSet()->kWordDocument()->isReadWrite() )
03307         drag->dragCopy();
03308     else {
03309         bool move = ( drag->drag() );
03310         if ( move )
03311         {
03312 #if 0
03313             if ( QDragObject::target() != m_canvas && QDragObject::target() != m_canvas->viewport() ) {
03314                 //This is when dropping text _out_ of KWord. Since we have Move and Copy
03315                 //options (Copy being accessed by pressing CTRL), both are possible.
03316                 //But is that intuitive enough ? Doesn't the user expect a Copy in all cases ?
03317                 //Losing the selected text when dropping out of kword seems quite unexpected to me.
03318                 //Undecided about this........
03319                 textObject()->removeSelectedText( cursor() );
03320             }
03321 #endif
03322         }
03323     }
03324 }
03325 
03326 QDragObject * KWTextFrameSetEdit::newDrag( QWidget * parent )
03327 {
03328     KWTextFrameSet* fs = textFrameSet();
03329     return fs->kWordDocument()->dragSelected( parent, fs );
03330 }
03331 
03332 void KWTextFrameSetEdit::ensureCursorVisible()
03333 {
03334     //kdDebug() << "KWTextFrameSetEdit::ensureCursorVisible paragId=" << cursor()->parag()->paragId() << " cursor->index()=" << cursor()->index() << endl;
03335     KoTextParag * parag = cursor()->parag();
03336     int idx = cursor()->index();
03337     textFrameSet()->ensureFormatted( parag );
03338     KoTextStringChar *chr = parag->at( idx );
03339     int cursorHeight = parag->lineHeightOfChar( idx );
03340     int x = parag->rect().x() + cursor()->x(); // this includes +charwidth for an RTL char
03341     //kdDebug() << "parag->rect().x()=" << parag->rect().x() << " x=" << cursor()->x() << endl;
03342     int y = 0; int dummy;
03343     parag->lineHeightOfChar( idx, &dummy, &y );
03344     y += parag->rect().y();
03345     //kdDebug() << "KWTextFrameSetEdit::ensureCursorVisible y=" << y << endl;
03346     // make sure one char is visible before, and one after
03347     KoTextStringChar *chrLeft = idx > 0 ? chr-1 : chr;
03348     // which char is on the left and which one is on the right depends on chr->rightToLeft
03349     int areaLeft = chr->rightToLeft ? chr->width : chrLeft->width;
03350     int areaRight = chr->rightToLeft ? chrLeft->width : chr->width;
03351     KoPoint pt;
03352     KoPoint hintDPoint;
03353     if ( m_currentFrame )
03354         hintDPoint = m_currentFrame->topLeft();
03355     KWFrame * theFrame = textFrameSet()->internalToDocumentWithHint( QPoint(x, y), pt, hintDPoint );
03356     //kdDebug() << "KWTextFrameSetEdit::ensureCursorVisible frame=" << theFrame << " m_currentFrame=" << m_currentFrame << endl;
03357     if ( theFrame && m_currentFrame != theFrame )
03358     {
03359         m_currentFrame = theFrame;
03360         m_canvas->gui()->getView()->updatePageInfo();
03361     }
03362     QPoint cursorPos = textFrameSet()->kWordDocument()->zoomPoint( pt );
03363     cursorPos = m_canvas->viewMode()->normalToView( cursorPos );
03364     areaLeft = textFrameSet()->kWordDocument()->layoutUnitToPixelX( areaLeft ) + 1;
03365     areaRight = textFrameSet()->kWordDocument()->layoutUnitToPixelX( areaRight ) + 1;
03366     cursorHeight = textFrameSet()->kWordDocument()->layoutUnitToPixelY( cursorHeight );
03367     //kdDebug() << "KWTextFrameSetEdit::ensureCursorVisible pt=" << pt << " cursorPos=" << cursorPos
03368     //          << " areaLeft=" << areaLeft << " areaRight=" << areaRight << " y=" << y << endl;
03369     m_canvas->ensureVisible( cursorPos.x() - areaLeft, cursorPos.y() + cursorHeight / 2, areaLeft + areaRight, cursorHeight / 2 + 2 );
03370 }
03371 
03372 bool KWTextFrameSetEdit::enterCustomItem( KoTextCustomItem* customItem, bool fromRight )
03373 {
03374     KWAnchor* anchor = dynamic_cast<KWAnchor*>( customItem );
03375     if ( anchor ) {
03376         KWFrameSet* frameSet = anchor->frameSet();
03377         if ( frameSet->type() == FT_FORMULA || frameSet->type() == FT_TEXT ) {
03378 
03379             // store the instance variable we need after "delete this"
03380             KWCanvas* canvas = m_canvas;
03381 
03382             // this will "delete this"!
03383             m_canvas->editFrameSet( frameSet );
03384 
03385             // We assume that `editFrameSet' succeeded.
03386             if ( fromRight ) {
03387                 KWFrameSetEdit* edit = canvas->currentFrameSetEdit();
03388                 if ( frameSet->type() == FT_FORMULA )
03389                     static_cast<KWFormulaFrameSetEdit*>( edit )->moveEnd();
03390                 else
03391                     static_cast<KWTextFrameSetEdit*>( edit )->moveCursor( MoveEnd );
03392             }
03393 
03394             if ( frameSet->type() == FT_FORMULA )
03395             {
03396                 // A FormulaFrameSetEdit looks a little different from
03397                 // a FormulaFrameSet. (Colors)
03398                 static_cast<KWFormulaFrameSet*>( frameSet )->setChanged();
03399                 canvas->repaintChanged( frameSet, true );
03400             }
03401             return true;
03402         }
03403     }
03404     return false;
03405 }
03406 
03407 void KWTextFrameSetEdit::keyPressEvent( QKeyEvent* e )
03408 {
03409     // Handle moving into inline frames (e.g. formula frames).
03410     if ( !( e->state() & ControlButton ) && !( e->state() & ShiftButton ) )
03411     {
03412         if (e->state() != Qt::NoButton)
03413                 removeToolTipCompletion();
03414         switch ( e->key() ) {
03415         case Key_Left: {
03416             KoTextCursor* cursor = textView()->cursor();
03417             KoTextParag* parag = cursor->parag();
03418             int index = cursor->index();
03419             if ( index > 0 ) {
03420                 KoTextStringChar* ch = parag->at( index-1 );
03421                 if ( ch->isCustom() ) {
03422                     KoTextCustomItem* customItem = ch->customItem();
03423                     if ( enterCustomItem( customItem, true ) ) {
03424                         // Don't do anything here, "this" is deleted!
03425                         return;
03426                     }
03427                 }
03428             }
03429             if ( index == 0 && !parag->prev() )
03430                 if ( exitLeft() )
03431                     return;
03432             break;
03433         }
03434         case Key_Right: {
03435             KoTextCursor* cursor = textView()->cursor();
03436             KoTextParag* parag = cursor->parag();
03437             int index = cursor->index();
03438             if ( index < parag->length() - 1 ) {
03439                 KoTextStringChar* ch = parag->at( index );
03440                 if ( ch->isCustom() ) {
03441                     KoTextCustomItem* customItem = ch->customItem();
03442                     if ( enterCustomItem( customItem, false ) ) {
03443                         // Don't do anything here, "this" is deleted!
03444                         return;
03445                     }
03446                 }
03447             } else if ( /*at end, covered by previous if, && */ !parag->next() )
03448                 if ( exitRight() )
03449                     return;
03450             break;
03451         }
03452         }
03453     }
03454     // Calculate position of tooltip for autocompletion
03455     QPoint pos = textFrameSet()->cursorPos( cursor(), m_canvas, m_currentFrame );
03456     textView()->handleKeyPressEvent( e, m_canvas, pos );
03457 }
03458 
03459 void KWTextFrameSetEdit::keyReleaseEvent( QKeyEvent* e )
03460 {
03461     textView()->handleKeyReleaseEvent( e );
03462 }
03463 
03464 void KWTextFrameSetEdit::imStartEvent( QIMEvent* e )
03465 {
03466     textView()->handleImStartEvent( e );
03467 }
03468 
03469 void KWTextFrameSetEdit::imComposeEvent( QIMEvent* e )
03470 {
03471     textView()->handleImComposeEvent( e );
03472 }
03473 
03474 void KWTextFrameSetEdit::imEndEvent( QIMEvent* e )
03475 {
03476     textView()->handleImEndEvent( e );
03477 }
03478 
03479 void KWTextFrameSetEdit::mousePressEvent( QMouseEvent *e, const QPoint &, const KoPoint & dPoint )
03480 {
03481     if ( dPoint.x() < 0 || dPoint.y() < 0 )
03482         return; // Ignore clicks completely outside of the page (e.g. in the gray area, or ruler)
03483 
03484     textFrameSet()->textObject()->clearUndoRedoInfo();
03485     if ( m_currentFrame )
03486         hideCursor(); // Need to do that with the old m_currentFrame
03487 
03488     QPoint iPoint;
03489     KWTextFrameSet::RelativePosition relPos;
03490     KWFrame * theFrame = textFrameSet()->documentToInternalMouseSelection( dPoint, iPoint, relPos, m_canvas->viewMode() );
03491     if ( theFrame && m_currentFrame != theFrame )
03492     {
03493         m_currentFrame = theFrame;
03494         m_canvas->gui()->getView()->updatePageInfo();
03495     }
03496 
03497     if ( m_currentFrame )
03498     {
03499         // Let KoTextView handle the mousepress event - but don't let it start
03500         // a drag if clicking on the left of the text (out of the frame itself)
03501         bool addParag = textView()->handleMousePressEvent( e, iPoint, relPos != KWTextFrameSet::LeftOfFrame, frameSet()->kWordDocument()->insertDirectCursor() );
03502 
03503 
03504         // Clicked on the left of the text -> select the whole paragraph
03505         if ( relPos == KWTextFrameSet::LeftOfFrame )
03506             textView()->selectParagUnderCursor( *textView()->cursor() );
03507         if ( addParag )
03508             frameSet()->kWordDocument()->setModified(true );
03509     }
03510     // else mightStartDrag = FALSE; necessary?
03511 
03512     if ( e->button() != LeftButton )
03513         return;
03514     KoVariable* var = variable();
03515     if ( var )
03516     {
03517         KWFootNoteVariable * footNoteVar = dynamic_cast<KWFootNoteVariable *>( var );
03518         if ( footNoteVar )
03519         {
03520             footNoteVar->frameSet()->startEditing( m_canvas );
03521             // --- and now we are deleted! ---
03522         }
03523     }
03524 }
03525 
03526 void KWTextFrameSetEdit::mouseMoveEvent( QMouseEvent * e, const QPoint & nPoint, const KoPoint & )
03527 {
03528     if ( textView()->maybeStartDrag( e ) )
03529         return;
03530     if ( nPoint.x() < 0 || nPoint.y() < 0 )
03531         return; // Ignore clicks completely outside of the page (e.g. in the gray area, or ruler)
03532 
03533     QPoint iPoint;
03534     KoPoint dPoint = frameSet()->kWordDocument()->unzoomPoint( nPoint );
03535     KWTextFrameSet::RelativePosition relPos;
03536     if ( nPoint.y() > 0 && textFrameSet()->documentToInternalMouseSelection( dPoint, iPoint, relPos , m_canvas->viewMode()) )
03537     {
03538         if ( relPos == KWTextFrameSet::LeftOfFrame )
03539             textView()->extendParagraphSelection( iPoint );
03540         else
03541             textView()->handleMouseMoveEvent( e, iPoint );
03542     }
03543 
03544 }
03545 
03546 bool KWTextFrameSetEdit::openLink( KoLinkVariable* variable )
03547 {
03548     KWTextFrameSet* fs = textFrameSet();
03549     KWDocument* doc = fs->kWordDocument();
03550     if ( doc->variableCollection()->variableSetting()->displayLink() ) {
03551 
03552         const QString url = variable->url();
03553         if( url.startsWith("bkm://") )
03554         {
03555             const KoTextBookmark* bookmark = doc->bookmarkByName(url.mid(6) );
03556             if ( bookmark )
03557             {
03558                 cursor()->setParag( bookmark->startParag() );
03559                 ensureCursorVisible();
03560                 return true;
03561             }
03562         }
03563         KoTextView::openLink( variable );
03564         return true;
03565     }
03566     return false;
03567 }
03568 
03569 void KWTextFrameSetEdit::openLink()
03570 {
03571     KoLinkVariable* v = linkVariable();
03572     if ( v )
03573         openLink( v );
03574 }
03575 
03576 void KWTextFrameSetEdit::mouseReleaseEvent( QMouseEvent *, const QPoint &, const KoPoint & )
03577 {
03578     textView()->handleMouseReleaseEvent();
03579 }
03580 
03581 void KWTextFrameSetEdit::mouseDoubleClickEvent( QMouseEvent *e, const QPoint &, const KoPoint & )
03582 {
03583     textView()->handleMouseDoubleClickEvent( e, QPoint() /* Currently unused */ );
03584 }
03585 
03586 void KWTextFrameSetEdit::dragEnterEvent( QDragEnterEvent * e )
03587 {
03588     int provides = KWView::checkClipboard( e );
03589     if ( !frameSet()->kWordDocument()->isReadWrite() || provides == 0 )
03590     {
03591         e->ignore();
03592         return;
03593     }
03594     e->acceptAction();
03595 }
03596 
03597 void KWTextFrameSetEdit::dragMoveEvent( QDragMoveEvent * e, const QPoint &nPoint, const KoPoint & )
03598 {
03599     int provides = KWView::checkClipboard( e );
03600     if ( !frameSet()->kWordDocument()->isReadWrite() || provides == 0 )
03601     {
03602         e->ignore();
03603         return;
03604     }
03605     // place cursor - unless dropping an image. well it's hard to know if the user
03606     // wants the dropped image to be inline or absolute positioned.
03607     if ( provides & ( KWView::ProvidesOasis | KWView::ProvidesPlainText | KWView::ProvidesFormula ) )
03608     {
03609         QPoint iPoint;
03610         KoPoint dPoint = frameSet()->kWordDocument()->unzoomPoint( nPoint );
03611         if ( textFrameSet()->documentToInternal( dPoint, iPoint ) )
03612         {
03613             textObject()->emitHideCursor();
03614             placeCursor( iPoint );
03615             textObject()->emitShowCursor();
03616         }
03617     }
03618     e->acceptAction();
03619 }
03620 
03621 void KWTextFrameSetEdit::dragLeaveEvent( QDragLeaveEvent * )
03622 {
03623 }
03624 
03625 void KWTextFrameSetEdit::dropEvent( QDropEvent * e, const QPoint & nPoint, const KoPoint &, KWView* view )
03626 {
03627     int provides = KWView::checkClipboard( e );
03628     if ( frameSet()->kWordDocument()->isReadWrite() && provides )
03629     {
03630         e->acceptAction();
03631         KoTextCursor dropCursor( textDocument() );
03632         QPoint dropPoint;
03633         KoPoint dPoint = frameSet()->kWordDocument()->unzoomPoint( nPoint );
03634         if ( !textFrameSet()->documentToInternal( dPoint, dropPoint ) )
03635             return; // Don't know where to paste
03636 
03637         dropCursor.place( dropPoint, textDocument()->firstParag() );
03638         kdDebug(32001) << "KWTextFrameSetEdit::dropEvent dropCursor at parag=" << dropCursor.parag()->paragId() << " index=" << dropCursor.index() << endl;
03639 
03640         if ( ( e->source() == m_canvas ||
03641                e->source() == m_canvas->viewport() ) &&
03642                e->action() == QDropEvent::Move &&
03643               // this is the indicator that the source and dest text objects are the same
03644              textDocument()->hasSelection( KoTextDocument::Standard ) ) {
03645 
03646             KCommand *cmd = textView()->prepareDropMove( dropCursor );
03647             if(cmd)
03648             {
03649                 KMacroCommand* macroCmd = new KMacroCommand( i18n( "Move Text" ) );
03650                 macroCmd->addCommand(cmd);
03651 
03652                 cmd = pasteOasisCommand( e );
03653                 if ( cmd )
03654                     macroCmd->addCommand(cmd);
03655                 //relayout textframeset after a dnd otherwise autoextend
03656                 //frameset is not re-layouted
03657                 textFrameSet()->layout();
03658                 frameSet()->kWordDocument()->addCommand( macroCmd );
03659             }
03660             return;
03661         }
03662         else
03663         {   // drop coming from outside -> forget about current selection
03664             textDocument()->removeSelection( KoTextDocument::Standard );
03665             textObject()->selectionChangedNotify();
03666         }
03667 
03668         // The cursor is already correctly positioned, all we need to do is to "paste" the dropped data.
03669         view->pasteData( e, true );
03670     }
03671 }
03672 
03673 void KWTextFrameSetEdit::focusInEvent()
03674 {
03675     textView()->focusInEvent();
03676 }
03677 
03678 void KWTextFrameSetEdit::focusOutEvent()
03679 {
03680     textView()->focusOutEvent();
03681 }
03682 
03683 void KWTextFrameSetEdit::selectAll()
03684 {
03685     textObject()->selectAll( true );
03686 }
03687 
03688 void KWTextFrameSetEdit::drawCursor( bool visible )
03689 {
03690 #ifdef DEBUG_CURSOR
03691     kdDebug() << "KWTextFrameSetEdit::drawCursor " << visible << endl;
03692 #endif
03693     KoTextView::drawCursor( visible );
03694     if ( !cursor()->parag() )
03695         return;
03696 
03697     if ( !cursor()->parag()->isValid() )
03698         textFrameSet()->ensureFormatted( cursor()->parag() );
03699 
03700     if ( !frameSet()->kWordDocument()->isReadWrite() )
03701         return;
03702     if ( m_canvas->viewMode()->hasFrames() && !m_currentFrame )
03703         return;
03704 
03705     QPainter p( m_canvas->viewport() );
03706     p.translate( -m_canvas->contentsX(), -m_canvas->contentsY() );
03707     p.setBrushOrigin( -m_canvas->contentsX(), -m_canvas->contentsY() );
03708 
03709     textFrameSet()->drawCursor( &p, cursor(), visible, m_canvas, m_currentFrame );
03710 }
03711 
03712 bool KWTextFrameSetEdit::pgUpKeyPressed()
03713 {
03714     QRect crect( m_canvas->contentsX(), m_canvas->contentsY(),
03715                  m_canvas->visibleWidth(), m_canvas->visibleHeight() );
03716     crect = m_canvas->viewMode()->viewToNormal( crect );
03717 
03718     // Go up of 90% of crect.height()
03719     int h = frameSet()->kWordDocument()->pixelToLayoutUnitY( (int)( (double)crect.height() * 0.9 ) );
03720     KoTextParag *s = textView()->cursor()->parag();
03721     KoTextParag* oldParag = s;
03722     int y = s->rect().y();
03723     while ( s ) {
03724         if ( y - s->rect().y() >= h )
03725             break;
03726         s = s->prev();
03727     }
03728 
03729     if ( !s )
03730         s = textDocument()->firstParag();
03731 
03732     textView()->cursor()->setParag( s );
03733     textView()->cursor()->setIndex( 0 );
03734     if ( s == oldParag )
03735     {
03736         m_canvas->viewportScroll( true );
03737         return false;
03738     }
03739     return true;
03740 }
03741 
03742 bool KWTextFrameSetEdit::pgDownKeyPressed()
03743 {
03744     QRect crect( m_canvas->contentsX(), m_canvas->contentsY(),
03745                  m_canvas->visibleWidth(), m_canvas->visibleHeight() );
03746     crect = m_canvas->viewMode()->viewToNormal( crect );
03747     // Go down of 90% of crect.height()
03748     int h = frameSet()->kWordDocument()->pixelToLayoutUnitY( (int)( (double)crect.height() * 0.9 ) );
03749 
03750     KoTextCursor *cursor = textView()->cursor();
03751     KoTextParag *s = cursor->parag();
03752     KoTextParag* oldParag = s;
03753     int y = s->rect().y();
03754     while ( s ) {
03755         if ( s->rect().y() - y >= h )
03756             break;
03757         s = s->next();
03758     }
03759 
03760     if ( !s ) {
03761         s = textDocument()->lastParag();
03762         cursor->setParag( s );
03763         cursor->setIndex( s->length() - 1 );
03764     } else {
03765         cursor->setParag( s );
03766         cursor->setIndex( 0 );
03767     }
03768     if ( s == oldParag )
03769     {
03770         m_canvas->viewportScroll( false );
03771         return false;
03772     }
03773     return true;
03774 }
03775 
03776 void KWTextFrameSetEdit::ctrlPgUpKeyPressed()
03777 {
03778     if ( m_currentFrame )
03779     {
03780         QPoint iPoint = textFrameSet()->moveToPage( m_currentFrame->pageNumber(), -1 );
03781         if ( !iPoint.isNull() )
03782             placeCursor( iPoint );
03783     }
03784 }
03785 
03786 void KWTextFrameSetEdit::ctrlPgDownKeyPressed()
03787 {
03788     if ( m_currentFrame )
03789     {
03790         QPoint iPoint = textFrameSet()->moveToPage( m_currentFrame->pageNumber(), +1 );
03791         if ( !iPoint.isNull() )
03792             placeCursor( iPoint );
03793     }
03794 }
03795 
03796 void KWTextFrameSetEdit::setCursor( KoTextParag* parag, int index )
03797 {
03798     cursor()->setParag( parag );
03799     cursor()->setIndex( index );
03800 }
03801 
03802 void KWTextFrameSetEdit::insertExpression(const QString &_c)
03803 {
03804     if(textObject()->hasSelection() )
03805         frameSet()->kWordDocument()->addCommand(textObject()->replaceSelectionCommand(
03806             cursor(), _c, i18n("Insert Expression")));
03807     else
03808        textObject()->insert( cursor(), currentFormat(), _c, i18n("Insert Expression") );
03809 }
03810 
03811 void KWTextFrameSetEdit::insertFloatingFrameSet( KWFrameSet * fs, const QString & commandName )
03812 {
03813     textObject()->clearUndoRedoInfo();
03814     CustomItemsMap customItemsMap;
03815     QString placeHolders;
03816     // TODO support for multiple floating items (like multiple-page tables)
03817     int frameNumber = 0;
03818     int index = 0;
03819     int insertFlags = KoTextObject::DoNotRemoveSelected;
03820     { // the loop will start here :)
03821         KWAnchor * anchor = fs->createAnchor( textFrameSet()->textDocument(), frameNumber );
03822         if ( frameNumber == 0 && anchor->ownLine() && cursor()->index() > 0 ) // enforce start of line - currently unused
03823         {
03824             kdDebug() << "ownline -> prepending \\n" << endl;
03825             placeHolders += QChar('\n');
03826             index++;
03827             insertFlags |= KoTextObject::CheckNewLine;
03828         }
03829         placeHolders += KoTextObject::customItemChar();
03830         customItemsMap.insert( index, anchor );
03831     }
03832     fs->setAnchored( textFrameSet() );
03833     textObject()->insert( cursor(), currentFormat(), placeHolders,
03834                           commandName, KoTextDocument::Standard, insertFlags,
03835                           customItemsMap );
03836 }
03837 
03838 void KWTextFrameSetEdit::insertLink(const QString &_linkName, const QString & hrefName)
03839 {
03840     KWDocument * doc = frameSet()->kWordDocument();
03841     KoVariable * var = new KoLinkVariable( textFrameSet()->textDocument(), _linkName, hrefName, doc->variableFormatCollection()->format( "STRING" ), doc->variableCollection() );
03842     insertVariable( var );
03843 }
03844 
03845 void KWTextFrameSetEdit::insertComment(const QString &_comment)
03846 {
03847     KWDocument * doc = frameSet()->kWordDocument();
03848     KoVariable * var = new KoNoteVariable( textFrameSet()->textDocument(), _comment, doc->variableFormatCollection()->format( "STRING" ), doc->variableCollection() );
03849     insertVariable( var );
03850 }
03851 
03852 
03853 void KWTextFrameSetEdit::insertCustomVariable( const QString &name)
03854 {
03855      KWDocument * doc = frameSet()->kWordDocument();
03856      KoVariable * var = new KoCustomVariable( textFrameSet()->textDocument(), name, doc->variableFormatCollection()->format( "STRING" ), doc->variableCollection());
03857      insertVariable( var );
03858 }
03859 
03860 void KWTextFrameSetEdit::insertFootNote( NoteType noteType, KWFootNoteVariable::Numbering numType, const QString &manualString )
03861 {
03862     KWFootNoteFrameSet *fs = textFrameSet()->insertFootNote( noteType, numType, manualString );
03863     KWFootNoteVariable * var = fs->footNoteVariable();
03864 
03865     // Place the frame on the correct page, but the exact coordinates
03866     // will be determined by recalcFrames (KWFrameLayout)
03867     int pageNum = m_currentFrame->pageNumber();
03868     fs->createInitialFrame( pageNum );
03869 
03870     insertVariable( var );
03871 
03872     // Re-number footnote variables
03873     textFrameSet()->renumberFootNotes();
03874 
03875     // Layout the footnote frame
03876     textFrameSet()->kWordDocument()->recalcFrames( pageNum, -1 ); // we know that for sure nothing changed before this page.
03877 
03878     //KoTextParag* parag = fs->textDocument()->firstParag();
03879     //parag->truncate(0); // why? we just created it, anyway...
03880 
03881     // And now edit the footnote frameset - all WPs do that it seems.
03882     fs->startEditing( m_canvas );
03883     // --- and now we are deleted! ---
03884 }
03885 
03886 void KWTextFrameSetEdit::insertVariable( int type, int subtype )
03887 {
03888     kdDebug() << "KWTextFrameSetEdit::insertVariable " << type << endl;
03889     KWDocument * doc = frameSet()->kWordDocument();
03890 
03891     KoVariable * var = 0L;
03892     bool refreshCustomMenu = false;
03893     if ( type == VT_CUSTOM )
03894     {
03895         KoCustomVarDialog dia( m_canvas );
03896         if ( dia.exec() == QDialog::Accepted )
03897         {
03898             KoCustomVariable *v = new KoCustomVariable( textFrameSet()->textDocument(), dia.name(), doc->variableFormatCollection()->format( "STRING" ),doc->variableCollection() );
03899             v->setValue( dia.value() );
03900             var = v;
03901             refreshCustomMenu = true;
03902         }
03903     }
03904     else if ( type == VT_MAILMERGE )
03905     {
03906         KWMailMergeVariableInsertDia dia( m_canvas, doc->mailMergeDataBase() );
03907         if ( dia.exec() == QDialog::Accepted )
03908         {
03909             var = new KWMailMergeVariable( textFrameSet()->textDocument(), dia.getName(), doc->variableFormatCollection()->format( "STRING" ),doc->variableCollection(),doc );
03910         }
03911     }
03912     else
03913         var = doc->variableCollection()->createVariable( type, subtype, doc->variableFormatCollection(), 0L, textFrameSet()->textDocument(), doc, 0);
03914     if ( var)
03915         insertVariable( var, 0L /*means currentFormat()*/, refreshCustomMenu);
03916 }
03917 
03918 void KWTextFrameSetEdit::insertVariable( KoVariable *var, KoTextFormat *format /*=0*/, bool refreshCustomMenu )
03919 {
03920     if ( var )
03921     {
03922         CustomItemsMap customItemsMap;
03923         customItemsMap.insert( 0, var );
03924         if (!format)
03925             format = currentFormat();
03926         kdDebug() << "KWTextFrameSetEdit::insertVariable inserting into paragraph" << endl;
03927 #ifdef DEBUG_FORMATS
03928         kdDebug() << "KWTextFrameSetEdit::insertVariable format=" << format << endl;
03929 #endif
03930         textObject()->insert( cursor(), format, KoTextObject::customItemChar(),
03931                               i18n("Insert Variable"),
03932                               KoTextDocument::Standard,
03933                               KoTextObject::DoNotRemoveSelected,
03934                               customItemsMap );
03935         frameSet()->kWordDocument()->slotRepaintChanged( frameSet() );
03936         if ( var->type()==VT_CUSTOM && refreshCustomMenu)
03937             frameSet()->kWordDocument()->refreshMenuCustomVariable();
03938     }
03939 }
03940 
03941 void KWTextFrameSetEdit::insertWPPage()
03942 {
03943     KWTextFrameSet* textfs = textFrameSet();
03944     textfs->clearUndoRedoInfo();
03945     KoTextObject* textobj = textObject();
03946     KWDocument * doc = frameSet()->kWordDocument();
03947     int pages = doc->pageCount();
03948     int columns = doc->numColumns();
03949     // There could be N columns. In that case we may need to add up to N framebreaks.
03950     int inserted = 0;
03951     KMacroCommand* macroCmd = new KMacroCommand( i18n("Insert Page") );
03952     do {
03953         macroCmd->addCommand( textfs->insertFrameBreakCommand( cursor() ) );
03954         textobj->setLastFormattedParag( cursor()->parag() );
03955         textobj->formatMore( 2 );
03956     } while ( pages == doc->pageCount() && ++inserted <= columns );
03957     if ( pages == doc->pageCount() )
03958         kdWarning(32002) << k_funcinfo << " didn't manage to insert a new page! inserted=" << inserted << " columns=" << columns << " pages=" << pages << endl;
03959 
03960     doc->addCommand( macroCmd );
03961 
03962     textfs->slotRepaintChanged();
03963     textobj->emitEnsureCursorVisible();
03964     textobj->emitUpdateUI( true );
03965     textobj->emitShowCursor();
03966 }
03967 
03968 KoBorder KWTextFrameSetEdit::border(KoBorder::BorderType type) {
03969     if(type == KoBorder::LeftBorder)
03970         return m_paragLayout.leftBorder;
03971     if(type == KoBorder::RightBorder)
03972         return m_paragLayout.rightBorder;
03973     if(type == KoBorder::TopBorder)
03974         return m_paragLayout.topBorder;
03975     return m_paragLayout.bottomBorder;
03976 }
03977 
03978 // Update the GUI toolbar button etc. to reflect the current cursor position.
03979 void KWTextFrameSetEdit::updateUI( bool updateFormat, bool force )
03980 {
03981     // Update UI - only for those items which have changed
03982     KoTextView::updateUI( updateFormat, force );
03983 
03984     // Paragraph settings
03985     KWTextParag * parag = static_cast<KWTextParag *>(cursor()->parag());
03986 
03987     if ( m_paragLayout.alignment != parag->resolveAlignment() || force ) {
03988         m_paragLayout.alignment = parag->resolveAlignment();
03989         m_canvas->gui()->getView()->showAlign( m_paragLayout.alignment );
03990     }
03991 
03992     // Counter
03993     if ( !m_paragLayout.counter )
03994         m_paragLayout.counter = new KoParagCounter; // we can afford to always have one here
03995     KoParagCounter::Style cstyle = m_paragLayout.counter->style();
03996     if ( parag->counter() )
03997         *m_paragLayout.counter = *parag->counter();
03998     else
03999     {
04000         m_paragLayout.counter->setNumbering( KoParagCounter::NUM_NONE );
04001         m_paragLayout.counter->setStyle( KoParagCounter::STYLE_NONE );
04002     }
04003     if ( m_paragLayout.counter->style() != cstyle || force )
04004         m_canvas->gui()->getView()->showCounter( * m_paragLayout.counter );
04005 
04006     if(m_paragLayout.leftBorder!=parag->leftBorder() ||
04007        m_paragLayout.rightBorder!=parag->rightBorder() ||
04008        m_paragLayout.topBorder!=parag->topBorder() ||
04009        m_paragLayout.bottomBorder!=parag->bottomBorder() || force )
04010     {
04011         m_paragLayout.leftBorder = parag->leftBorder();
04012         m_paragLayout.rightBorder = parag->rightBorder();
04013         m_paragLayout.topBorder = parag->topBorder();
04014         m_paragLayout.bottomBorder = parag->bottomBorder();
04015         m_canvas->gui()->getView()->updateBorderButtons( m_paragLayout.leftBorder, m_paragLayout.rightBorder, m_paragLayout.topBorder, m_paragLayout.bottomBorder );
04016     }
04017 
04018     if ( !parag->style() )
04019         kdWarning() << "Paragraph " << parag->paragId() << " has no style" << endl;
04020     else if ( m_paragLayout.style != parag->style() || force )
04021     {
04022         m_paragLayout.style = parag->style();
04023         m_canvas->gui()->getView()->showStyle( m_paragLayout.style->name() );
04024     }
04025 
04026     if( m_paragLayout.margins[QStyleSheetItem::MarginLeft] != parag->margin(QStyleSheetItem::MarginLeft)
04027         || m_paragLayout.margins[QStyleSheetItem::MarginFirstLine] != parag->margin(QStyleSheetItem::MarginFirstLine)
04028         || m_paragLayout.margins[QStyleSheetItem::MarginRight] != parag->margin(QStyleSheetItem::MarginRight)
04029         || parag->string()->isRightToLeft() != m_rtl
04030         || force )
04031     {
04032         m_paragLayout.margins[QStyleSheetItem::MarginFirstLine] = parag->margin(QStyleSheetItem::MarginFirstLine);
04033         m_paragLayout.margins[QStyleSheetItem::MarginLeft] = parag->margin(QStyleSheetItem::MarginLeft);
04034         m_paragLayout.margins[QStyleSheetItem::MarginRight] = parag->margin(QStyleSheetItem::MarginRight);
04035         if ( m_rtl != parag->string()->isRightToLeft() && parag->counter() )
04036         {
04037             parag->counter()->invalidate();
04038             parag->setChanged( true ); // repaint
04039         }
04040         m_rtl = parag->string()->isRightToLeft();
04041         m_canvas->gui()->getView()->showRulerIndent( m_paragLayout.margins[QStyleSheetItem::MarginLeft], m_paragLayout.margins[QStyleSheetItem::MarginFirstLine], m_paragLayout.margins[QStyleSheetItem::MarginRight], m_rtl );
04042     }
04043     if( m_paragLayout.tabList() != parag->tabList() || force)
04044     {
04045         m_paragLayout.setTabList( parag->tabList() );
04046         KoRuler * hr = m_canvas->gui()->getHorzRuler();
04047         if ( hr )
04048             hr->setTabList( parag->tabList() );
04049     }
04050     if( m_paragLayout.lineSpacingType != parag->paragLayout().lineSpacingType || force)
04051     {
04052       m_paragLayout.lineSpacingType = parag->paragLayout().lineSpacingType;
04053       m_canvas->gui()->getView()->showSpacing( m_paragLayout.lineSpacingType );
04054     }
04055     // There are more paragraph settings, but those that are not directly
04056     // visible in the UI don't need to be handled here.
04057     // For instance parag stuff, borders etc.
04058 }
04059 
04060 void KWTextFrameSetEdit::showFormat( KoTextFormat *format )
04061 {
04062     m_canvas->gui()->getView()->showFormat( *format );
04063 }
04064 
04065 QPoint KWTextFrameSet::cursorPos( KoTextCursor *cursor, KWCanvas* canvas, KWFrame* currentFrame )
04066 {
04067     KWViewMode *viewMode = canvas->viewMode();
04068 
04069     KoTextParag* parag = cursor->parag();
04070     const QPoint topLeft = parag->rect().topLeft();         // in QRT coords
04071     int lineY;
04072     parag->lineHeightOfChar( cursor->index(), 0, &lineY );
04073     // iPoint is the topright corner of the current character
04074     QPoint iPoint( topLeft.x() + cursor->x() + parag->at( cursor->index() )->width, topLeft.y() + lineY );
04075 
04076     KoPoint dPoint;
04077     QPoint vPoint;
04078     KoPoint hintDPoint = currentFrame ? currentFrame->innerRect().topLeft() : KoPoint();
04079     if ( internalToDocumentWithHint( iPoint, dPoint, hintDPoint ) )
04080     {
04081         vPoint = viewMode->normalToView( m_doc->zoomPoint( dPoint ) ); // from doc to view contents
04082         vPoint.rx() -= canvas->contentsX();
04083         vPoint.ry() -= canvas->contentsY();
04084     } // else ... ?
04085     return vPoint;
04086 }
04087 
04089 
04090 bool KWFootNoteFrameSet::isFootNote() const
04091 {
04092     if ( !m_footNoteVar ) {
04093         kdWarning() << k_funcinfo << " called too early? No footnote var." << endl;
04094         return false;
04095     }
04096     return ( m_footNoteVar->noteType() == FootNote );
04097 }
04098 
04099 bool KWFootNoteFrameSet::isEndNote() const
04100 {
04101     if ( !m_footNoteVar ) {
04102         kdWarning() << k_funcinfo << " called too early? No footnote var." << endl;
04103         return false;
04104     }
04105     return ( m_footNoteVar->noteType() == EndNote );
04106 }
04107 
04108 
04109 
04110 void KWFootNoteFrameSet::createInitialFrame( int pageNum )
04111 {
04112     KWFrame *frame = new KWFrame(this, 0, m_doc->pageManager()->topOfPage(pageNum) + 1, 20, 20 );
04113     frame->setFrameBehavior(KWFrame::AutoExtendFrame);
04114     frame->setNewFrameBehavior(KWFrame::NoFollowup);
04115     addFrame( frame );
04116 }
04117 
04118 void KWFootNoteFrameSet::startEditing( KWCanvas* canvas )
04119 {
04120     canvas->editFrameSet( this );
04121 
04122     // Ensure cursor is visible
04123     KWTextFrameSetEdit *textedit = dynamic_cast<KWTextFrameSetEdit *>(canvas->currentFrameSetEdit()->currentTextEdit());
04124     if ( textedit )
04125         textedit->ensureCursorVisible();
04126 }
04127 
04128 void KWFootNoteFrameSet::setFootNoteVariable( KWFootNoteVariable* var )
04129 {
04130      m_footNoteVar = var;
04131 }
04132 
04133 void KWFootNoteFrameSet::setCounterText( const QString& text )
04134 {
04135     KoTextParag* parag = textDocument()->firstParag();
04136     Q_ASSERT( parag );
04137     if ( parag ) {
04138         KoParagCounter counter;
04139         counter.setNumbering( KoParagCounter::NUM_FOOTNOTE );
04140         counter.setPrefix( text );
04141         counter.setSuffix( QString::null );
04142         parag->setCounter( counter );
04143     }
04144 }
04145 
04146 KWordFrameSetIface* KWFootNoteFrameSet::dcopObject()
04147 {
04148     if ( !m_dcop )
04149         m_dcop = new KWFootNoteFrameSetIface( this );
04150 
04151     return m_dcop;
04152 }
04153 
04154 #include "KWTextFrameSet.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys