kword

KWInsertTOCCommand.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include <KoParagCounter.h>
21
22#include "KWInsertTOCCommand.h"
23#include "KWDocument.h"
24#include "KWTextFrameSet.h"
25#include "KWTextDocument.h"
26#include "KWTextParag.h"
27
28#include <tdelocale.h>
29#include <kdebug.h>
30
31KWInsertTOCCommand::KWInsertTOCCommand( KWTextFrameSet * fs, KoTextParag *parag )
32 : KoTextDocCommand( fs->textDocument() ), m_paragId( parag->paragId() )
33{
34}
35
36KoTextCursor * KWInsertTOCCommand::execute( KoTextCursor *c )
37{
38 KWTextDocument * textdoc = static_cast<KWTextDocument *>(doc);
39 KWTextFrameSet * fs = textdoc->textFrameSet();
40
41 fs->kWordDocument()->setTocPresent(true);
42
43 KoTextParag *insertionParag = textdoc->paragAt( m_paragId );
44 if ( !insertionParag ) {
45 tqWarning( "KWInsertTOCCommand:: can't locate parag at %d, last parag: %d", m_paragId, textdoc->lastParag()->paragId() );
46 return c;
47 }
48 KWTextParag *body = static_cast<KWTextParag *>( insertionParag );
49 // Create new very first paragraph
50 KWTextParag *parag = static_cast<KWTextParag *>( textdoc->createParag( textdoc, body->prev() /*prev*/, body /*next*/, true ) );
51 parag->append( i18n( "Table of Contents" ) );
52 KoParagStyle * style = findOrCreateTOCStyle( fs, -1 ); // "Contents Title"
53 parag->setParagLayout( style->paragLayout() );
54 parag->setFormat( 0, parag->string()->length(), textdoc->formatCollection()->format( &style->format() ) );
55 parag->setPartOfTableOfContents( true );
56
57 // Insert table and THEN set page numbers
58 // Otherwise the page numbers are incorrect
59
60 KWTextParag *p = static_cast<KWTextParag *>(textdoc->firstParag()/*parag->next()*/);
61 //ASSERT( p == body );
62 KWTextParag *prevTOCParag = parag;
63 TQMap<KWTextParag *, KWTextParag *> paragMap; // Associate a paragraph form the TOC with the real one from the body
64 while ( p ) {
65 // Include this paragraph in the TOC depending on the isOutline flag of the style
66 if ( p->style() && p->style()->isOutline() )
67 {
68 parag = static_cast<KWTextParag *>(textdoc->createParag( textdoc, prevTOCParag /*prev*/, body /*next*/, true ));
69 parag->setPartOfTableOfContents( true );
70 TQString txt = p->toString(); // includes the heading number, if any
71 parag->append( txt );
72 prevTOCParag = parag;
73
74 paragMap.insert( parag, p );
75 }
76 p = static_cast<KWTextParag *>(p->next());
77 }
78 // Set a hard frame break after the last TOC parag
79 kdDebug() << "KWInsertTOCCommand::execute setPageBreaking on " << prevTOCParag << " " << prevTOCParag->paragId() << endl;
80 prevTOCParag->setPageBreaking( prevTOCParag->pageBreaking() | KWParagLayout::HardFrameBreakAfter );
81
82 // Format paragraphs, to take this page break into account and update page numbers
83 fs->layout();
84 fs->updateFrames();
85
86 //kdDebug() << "KWInsertTOCCommand::execute layouting done, setting page numbers" << endl;
87
88 // Now add the page numbers, and apply the style
89 TQMap<KWTextParag *, KWTextParag *>::Iterator mapIt = paragMap.begin();
90 for ( ; mapIt != paragMap.end() ; ++mapIt )
91 {
92 KWTextParag * parag = mapIt.key(); // Parag in the TOC
93 KWTextParag * p = mapIt.data(); // Parag in the body
94
95 // Find page number for paragraph
96 KoPoint pt;
97 KWFrame * frame = fs->internalToDocument( TQPoint(0, p->rect().top()), pt );
98 if ( frame ) // let's be safe
99 {
100 parag->append( "\t" );
101 parag->append( TQString::number( frame->pageNumber() ) );
102 }
103
104 // Apply style
105 int depth = p->counter() ? p->counter()->depth() : 0;
106 KoParagStyle * tocStyle = findOrCreateTOCStyle( fs, depth );
107 parag->setParagLayout( tocStyle->paragLayout() );
108 parag->setFormat( 0, parag->string()->length(), & tocStyle->format() );
109 }
110 // The setParagLayout ruined it, so here it is again :)
111 prevTOCParag->setPageBreaking( prevTOCParag->pageBreaking() | KWParagLayout::HardFrameBreakAfter );
112 return c;
113}
114
115KoTextCursor *KWInsertTOCCommand::unexecute( KoTextCursor *c )
116{
117 KWTextDocument * textdoc = static_cast<KWTextDocument *>(doc);
118 KWTextFrameSet * fs = textdoc->textFrameSet();
119
120 removeTOC( fs, c, 0L );
121 fs->kWordDocument()->setTocPresent(false);
122 return c;
123}
124
125KoTextCursor * KWInsertTOCCommand::removeTOC( KWTextFrameSet *fs, KoTextCursor *cursor, KMacroCommand * /*macroCmd*/ )
126{
127 KoTextDocument * textdoc = fs->textDocument();
128 // Remove existing table of contents, based on the style
129 KoTextCursor start( textdoc );
130 KoTextCursor end( textdoc );
131 // We start from the end, to avoid the parag shifting problem
132 KoTextParag *p = textdoc->lastParag();
133 KoTextCursor *posOfTable=0L;
134 KoTextParag *posOfToc=0L;
135
136 while ( p )
137 {
138 KWTextParag * parag = static_cast<KWTextParag *>(p);
139 if ( parag->partOfTableOfContents() )
140 {
141 kdDebug() << "KWContents::createContents Deleting paragraph " << p << " " << p->paragId() << endl;
142 // This paragraph is part of the TOC -> remove
143
144 /* This method aims to provide an "undo" that restores the previous version of the TOC.
145 Problem is, it screws up the parag style (due to removeSelectedText calling join),
146 and the first parag of the body ends up with the Contents Title style.
147 start.setParag( p );
148 start.setIndex( 0 );
149 textdoc->setSelectionStart( KoTextDocument::Temp, &start );
150 ASSERT( p->next() );
151 end.setParag( p->next() );
152 end.setIndex( 0 );
153 textdoc->setSelectionEnd( KoTextDocument::Temp, &end );
154 KCommand * cmd = fs->removeSelectedTextCommand( cursor, KoTextDocument::Temp );
155 if ( macroCmd )
156 macroCmd->addCommand( cmd );
157 */
158
159 // So instead, we do things by hand, and without undo....
160
161 KoTextParag *prev = p->prev();
162 KoTextParag *next = p->next();
163 // Move cursor out
164 if ( cursor->parag() == p )
165 cursor->setParag( next ? next : prev );
166 delete p;
167 kdDebug() << "KWInsertTOCCommand::removeTOC " << p << " deleted" << endl;
168 p = next;
169 posOfToc = p;
170 kdDebug() << "KWInsertTOCCommand::removeTOC prev=" << prev << " p=" << p << endl;
171 // Fix parag chain
172 if ( prev )
173 {
174 prev->setNext( p );
175 if ( p )
176 p->setParagId( prev->paragId() + 1 );
177 else
178 posOfToc = prev;
179 }
180 else
181 {
182 textdoc->setFirstParag( p );
183 if ( p )
184 {
185 p->setParagId( 0 );
186 } else // completely empty document !
187 {
188 textdoc->clear( true ); // recreate empty parag.
189 cursor->setParag( textdoc->firstParag() );
190 posOfToc = textdoc->firstParag();
191 break;
192 }
193 }
194 if(! p) { // see bug #131122
195 p = prev;
196 continue;
197 }
198 p->setPrev( prev );
199 }
200 p = p->prev();
201 }
202 textdoc->invalidate();
203 if(posOfToc)
204 {
205 posOfTable=new KoTextCursor( textdoc );
206 posOfTable->setParag(posOfToc );
207 posOfTable->setIndex( 0 );//start of parag
208 }
209 // ### TODO propagate parag ID changes.
210 return posOfTable;
211}
212
213KoParagStyle * KWInsertTOCCommand::findOrCreateTOCStyle( KWTextFrameSet *fs, int depth )
214{
215 // Determine style name.
216 TQString name;
217 TQString displayName;
218 if ( depth >= 0 ) {
219 // Don't add i18n to this one, those are internal names and must have no spaces.
220 name = TQString( "Contents_Head_%1" ).arg( depth+1 );
221 displayName = i18n( "Contents Head %1" ).arg( depth+1 );
222 } else {
223 name = "Contents_Title";
224 displayName = i18n( "Contents Title" );
225 }
226 KoParagStyle * style = fs->kWordDocument()->styleCollection()->findStyle( name );
227 if ( !style )
228 {
229 style = new KoParagStyle( name );
230 style->setDisplayName( displayName );
231 style->format().setBold( ( ( depth==-1) || ( depth==0 ) ) ? true : false );
232 style->format().setPointSize( depth==-1 ? 20 : 12 );
233 if ( depth == -1 )
234 {
235 style->paragLayout().topBorder = KoBorder( TQt::black, KoBorder::SOLID, 1 );
236 style->paragLayout().bottomBorder = KoBorder( TQt::black, KoBorder::SOLID, 1 );
237 // Old kword had only top and bottom. But borders are drawn differently now
238 // (not the whole line anymore), so we need the 4 borders.
239 style->paragLayout().leftBorder = KoBorder( TQt::black, KoBorder::SOLID, 1 );
240 style->paragLayout().rightBorder = KoBorder( TQt::black, KoBorder::SOLID, 1 );
241 style->paragLayout().alignment = TQt::AlignHCenter;
242 }
243 else
244 {
245 KoTabulatorList tabList;
246 KoTabulator tab;
247
248 tab.ptPos = KoUnit::fromUserValue( floor( KoUnit::toMM( fs->frame( 0 )->width() ) ), KoUnit::unit("mm") );
249
250 tab.type = T_RIGHT;
251 tab.filling = TF_DOTS;
252 tab.ptWidth = 0.5;
253 tabList.append( tab );
254 style->paragLayout().setTabList( tabList );
255 style->paragLayout().margins[TQStyleSheetItem::MarginLeft] = KoUnit::fromUserValue( (depth*4.5), KoUnit::unit("mm") );
256 }
257 style = fs->kWordDocument()->styleCollection()->addStyle( style ); // register the new style
258 fs->kWordDocument()->updateAllStyleLists(); // show it in the UI
259 }
260 return style;
261}
KWFrame * frame(unsigned int _num) const
get a frame by number
int pageNumber() const
The page on which this frame is.
Definition KWFrame.cpp:116
KWTextFrameSet * textFrameSet() const
Return the text frameset in which this document is.
Class: KWTextFrameSet Contains text (KoTextObject) and frames to display that text.
virtual void updateFrames(int flags=0xff)
reshuffle frames so text is always displayed from top-left down and then right.
KWFrame * internalToDocument(const KoPoint &relPoint, KoPoint &dPoint) const
Convert the in the internal qtextdoc coordinates (in layout units) into a point in the document coord...
virtual void layout()
Invalidate all paragraphs and start re-formatting.
void setPageBreaking(int pb)
The type of page-breaking behaviour.