kword

KWInsertTOCCommand.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <KoParagCounter.h>
00021 
00022 #include "KWInsertTOCCommand.h"
00023 #include "KWDocument.h"
00024 #include "KWTextFrameSet.h"
00025 #include "KWTextDocument.h"
00026 #include "KWTextParag.h"
00027 
00028 #include <klocale.h>
00029 #include <kdebug.h>
00030 
00031 KWInsertTOCCommand::KWInsertTOCCommand( KWTextFrameSet * fs, KoTextParag *parag )
00032     : KoTextDocCommand( fs->textDocument() ), m_paragId( parag->paragId() )
00033 {
00034 }
00035 
00036 KoTextCursor * KWInsertTOCCommand::execute( KoTextCursor *c )
00037 {
00038     KWTextDocument * textdoc = static_cast<KWTextDocument *>(doc);
00039     KWTextFrameSet * fs = textdoc->textFrameSet();
00040 
00041     fs->kWordDocument()->setTocPresent(true);
00042 
00043     KoTextParag *insertionParag = textdoc->paragAt( m_paragId );
00044     if ( !insertionParag ) {
00045         qWarning( "KWInsertTOCCommand:: can't locate parag at %d, last parag: %d", m_paragId, textdoc->lastParag()->paragId() );
00046         return c;
00047     }
00048     KWTextParag *body = static_cast<KWTextParag *>( insertionParag );
00049     // Create new very first paragraph
00050     KWTextParag *parag = static_cast<KWTextParag *>( textdoc->createParag( textdoc, body->prev() /*prev*/, body /*next*/, true ) );
00051     parag->append( i18n( "Table of Contents" ) );
00052     KoParagStyle * style = findOrCreateTOCStyle( fs, -1 ); // "Contents Title"
00053     parag->setParagLayout( style->paragLayout() );
00054     parag->setFormat( 0, parag->string()->length(), textdoc->formatCollection()->format( &style->format() ) );
00055     parag->setPartOfTableOfContents( true );
00056 
00057     // Insert table and THEN set page numbers
00058     // Otherwise the page numbers are incorrect
00059 
00060     KWTextParag *p = static_cast<KWTextParag *>(textdoc->firstParag()/*parag->next()*/);
00061     //ASSERT( p == body );
00062     KWTextParag *prevTOCParag = parag;
00063     QMap<KWTextParag *, KWTextParag *> paragMap;     // Associate a paragraph form the TOC with the real one from the body
00064     while ( p ) {
00065         // Include this paragraph in the TOC depending on the isOutline flag of the style
00066         if ( p->style() && p->style()->isOutline() )
00067         {
00068             parag = static_cast<KWTextParag *>(textdoc->createParag( textdoc, prevTOCParag /*prev*/, body /*next*/, true ));
00069             parag->setPartOfTableOfContents( true );
00070             QString txt = p->toString(); // includes the heading number, if any
00071             parag->append( txt );
00072             prevTOCParag = parag;
00073 
00074             paragMap.insert( parag, p );
00075         }
00076         p = static_cast<KWTextParag *>(p->next());
00077     }
00078     // Set a hard frame break after the last TOC parag
00079     kdDebug() << "KWInsertTOCCommand::execute setPageBreaking on " << prevTOCParag << " " << prevTOCParag->paragId() << endl;
00080     prevTOCParag->setPageBreaking( prevTOCParag->pageBreaking() | KWParagLayout::HardFrameBreakAfter );
00081 
00082     // Format paragraphs, to take this page break into account and update page numbers
00083     fs->layout();
00084     fs->updateFrames();
00085 
00086     //kdDebug() << "KWInsertTOCCommand::execute layouting done, setting page numbers" << endl;
00087 
00088     // Now add the page numbers, and apply the style
00089     QMap<KWTextParag *, KWTextParag *>::Iterator mapIt = paragMap.begin();
00090     for ( ; mapIt != paragMap.end() ; ++mapIt )
00091     {
00092         KWTextParag * parag = mapIt.key(); // Parag in the TOC
00093         KWTextParag * p = mapIt.data();    // Parag in the body
00094 
00095         // Find page number for paragraph
00096         KoPoint pt;
00097         KWFrame * frame = fs->internalToDocument( QPoint(0, p->rect().top()), pt );
00098         if ( frame ) // let's be safe
00099         {
00100             parag->append( "\t" );
00101             parag->append( QString::number( frame->pageNumber() ) );
00102         }
00103 
00104         // Apply style
00105         int depth = p->counter() ? p->counter()->depth() : 0;
00106         KoParagStyle * tocStyle = findOrCreateTOCStyle( fs, depth );
00107         parag->setParagLayout( tocStyle->paragLayout() );
00108         parag->setFormat( 0, parag->string()->length(), & tocStyle->format() );
00109     }
00110     // The setParagLayout ruined it, so here it is again :)
00111     prevTOCParag->setPageBreaking( prevTOCParag->pageBreaking() | KWParagLayout::HardFrameBreakAfter );
00112     return c;
00113 }
00114 
00115 KoTextCursor *KWInsertTOCCommand::unexecute( KoTextCursor *c )
00116 {
00117     KWTextDocument * textdoc = static_cast<KWTextDocument *>(doc);
00118     KWTextFrameSet * fs = textdoc->textFrameSet();
00119 
00120     removeTOC( fs, c, 0L );
00121     fs->kWordDocument()->setTocPresent(false);
00122     return c;
00123 }
00124 
00125 KoTextCursor * KWInsertTOCCommand::removeTOC( KWTextFrameSet *fs, KoTextCursor *cursor, KMacroCommand * /*macroCmd*/ )
00126 {
00127     KoTextDocument * textdoc = fs->textDocument();
00128     // Remove existing table of contents, based on the style
00129     KoTextCursor start( textdoc );
00130     KoTextCursor end( textdoc );
00131     // We start from the end, to avoid the parag shifting problem
00132     KoTextParag *p = textdoc->lastParag();
00133     KoTextCursor *posOfTable=0L;
00134     KoTextParag *posOfToc=0L;
00135 
00136     while ( p )
00137     {
00138         KWTextParag * parag = static_cast<KWTextParag *>(p);
00139         if ( parag->partOfTableOfContents() )
00140         {
00141             kdDebug() << "KWContents::createContents Deleting paragraph " << p << " " << p->paragId() << endl;
00142             // This paragraph is part of the TOC -> remove
00143 
00144             /* This method aims to provide an "undo" that restores the previous version of the TOC.
00145                Problem is, it screws up the parag style (due to removeSelectedText calling join),
00146                and the first parag of the body ends up with the Contents Title style.
00147             start.setParag( p );
00148             start.setIndex( 0 );
00149             textdoc->setSelectionStart( KoTextDocument::Temp, &start );
00150             ASSERT( p->next() );
00151             end.setParag( p->next() );
00152             end.setIndex( 0 );
00153             textdoc->setSelectionEnd( KoTextDocument::Temp, &end );
00154             KCommand * cmd = fs->removeSelectedTextCommand( cursor, KoTextDocument::Temp );
00155             if ( macroCmd )
00156                 macroCmd->addCommand( cmd );
00157             */
00158 
00159             // So instead, we do things by hand, and without undo....
00160 
00161             KoTextParag *prev = p->prev();
00162             KoTextParag *next = p->next();
00163             // Move cursor out
00164             if ( cursor->parag() == p )
00165                 cursor->setParag( next ? next : prev );
00166             delete p;
00167             kdDebug() << "KWInsertTOCCommand::removeTOC " << p << " deleted" << endl;
00168             p = next;
00169             posOfToc = p;
00170             kdDebug() << "KWInsertTOCCommand::removeTOC prev=" << prev << " p=" << p << endl;
00171             // Fix parag chain
00172             if ( prev )
00173             {
00174                 prev->setNext( p );
00175                 if ( p )
00176                     p->setParagId( prev->paragId() + 1 );
00177                 else
00178                     posOfToc = prev;
00179             }
00180             else
00181             {
00182                 textdoc->setFirstParag( p );
00183                 if ( p )
00184                 {
00185                     p->setParagId( 0 );
00186                 } else // completely empty document !
00187                 {
00188                     textdoc->clear( true ); // recreate empty parag.
00189                     cursor->setParag( textdoc->firstParag() );
00190                     posOfToc = textdoc->firstParag();
00191                     break;
00192                 }
00193             }
00194             p->setPrev( prev );
00195         }
00196         p = p->prev();
00197     }
00198     textdoc->invalidate();
00199     if(posOfToc)
00200     {
00201          posOfTable=new KoTextCursor( textdoc );
00202          posOfTable->setParag(posOfToc  );
00203          posOfTable->setIndex( 0 );//start of parag
00204     }
00205     // ### TODO propagate parag ID changes.
00206     return posOfTable;
00207 }
00208 
00209 KoParagStyle * KWInsertTOCCommand::findOrCreateTOCStyle( KWTextFrameSet *fs, int depth )
00210 {
00211     // Determine style name.
00212     QString name;
00213     QString displayName;
00214     if ( depth >= 0 ) {
00215         // Don't add i18n to this one, those are internal names and must have no spaces.
00216         name = QString( "Contents_Head_%1" ).arg( depth+1 );
00217         displayName = i18n( "Contents Head %1" ).arg( depth+1 );
00218     } else {
00219         name = "Contents_Title";
00220         displayName = i18n( "Contents Title" );
00221     }
00222     KoParagStyle * style = fs->kWordDocument()->styleCollection()->findStyle( name );
00223     if ( !style )
00224     {
00225         style = new KoParagStyle( name );
00226         style->setDisplayName( displayName );
00227         style->format().setBold( ( ( depth==-1) || ( depth==0 ) ) ? true : false );
00228         style->format().setPointSize( depth==-1 ? 20 : 12 );
00229         if ( depth == -1 )
00230         {
00231             style->paragLayout().topBorder = KoBorder( Qt::black, KoBorder::SOLID, 1 );
00232             style->paragLayout().bottomBorder = KoBorder( Qt::black, KoBorder::SOLID, 1 );
00233             // Old kword had only top and bottom. But borders are drawn differently now
00234             // (not the whole line anymore), so we need the 4 borders.
00235             style->paragLayout().leftBorder = KoBorder( Qt::black, KoBorder::SOLID, 1 );
00236             style->paragLayout().rightBorder = KoBorder( Qt::black, KoBorder::SOLID, 1 );
00237             style->paragLayout().alignment = Qt::AlignHCenter;
00238         }
00239         else
00240         {
00241             KoTabulatorList tabList;
00242             KoTabulator tab;
00243 
00244             tab.ptPos = KoUnit::fromUserValue( floor( KoUnit::toMM( fs->frame( 0 )->width() ) ), KoUnit::unit("mm") );
00245 
00246             tab.type = T_RIGHT;
00247             tab.filling = TF_DOTS;
00248             tab.ptWidth = 0.5;
00249             tabList.append( tab );
00250             style->paragLayout().setTabList( tabList );
00251             style->paragLayout().margins[QStyleSheetItem::MarginLeft] = KoUnit::fromUserValue( (depth*4.5), KoUnit::unit("mm") );
00252         }
00253         style = fs->kWordDocument()->styleCollection()->addStyle( style );     // register the new style
00254         fs->kWordDocument()->updateAllStyleLists();                 // show it in the UI
00255     }
00256     return style;
00257 }
KDE Home | KDE Accessibility Home | Description of Access Keys