kword

KWTextFrameSet.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998, 1999, 2000 Reginald Stadlbauer <reggie@kde.org>
3 Copyright (C) 2001-2006 David Faure <faure@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "KWTextFrameSet.h"
22#include "KWTableFrameSet.h"
23#include "KWDocument.h"
24#include "KWView.h"
25#include "KWViewMode.h"
26#include "KWCanvas.h"
27#include "KWAnchor.h"
28#include "KWCommand.h"
29#include "KWFormulaFrameSet.h"
30#include "KWBgSpellCheck.h"
31#include "KWordTextFrameSetIface.h"
32#include "KWordTextFrameSetEditIface.h"
33#include "KWordFootNoteFrameSetIface.h"
34#include "KWordFrameSetIface.h"
35#include "KWLoadingInfo.h"
36#include "KWInsertTOCCommand.h"
37#include "KWMailMergeDataBase.h"
38#include "KoTextBookmark.h"
39#include "KWVariable.h"
40#include "KWOasisSaver.h"
41#include "KWFrameList.h"
42#include "KWPageManager.h"
43#include "KWPage.h"
44
45#include <KoParagCounter.h>
46#include <KoCustomVariablesDia.h>
47#include <KoAutoFormat.h>
48#include <KoTextObject.h>
49#include <KoTextCommand.h>
50#include <KoTextFormatter.h>
51#include <KoChangeCaseDia.h>
52#include <KoXmlNS.h>
53#include <KoXmlWriter.h>
54#include <KoOasisContext.h>
55#include <KoStore.h>
56
57#include <tdelocale.h>
58#include <tdeaction.h>
59#include <tdemessagebox.h>
60#include <kdebug.h>
61
62#include <tqclipboard.h>
63#include <tqdragobject.h>
64#include <tqcursor.h>
65#include <tqfile.h>
66#include <tqprogressdialog.h>
67#include <tqregexp.h>
68
69#include <assert.h>
70#include <tqapplication.h>
71
72//#define DEBUG_MARGINS
73//#define DEBUG_FORMATVERTICALLY
74//#define DEBUG_FORMATS
75//#define DEBUG_FORMAT_MORE
76//#define DEBUG_VIEWAREA
77//#define DEBUG_CURSOR
78
79//#define DEBUG_DTI
80//#define DEBUG_ITD
81
87class KWTextFormatter : public KoTextFormatter
88{
89public:
90 KWTextFormatter( KWTextFrameSet *textfs ) : m_textfs( textfs ) {}
91 virtual ~KWTextFormatter() {}
92
93 virtual int formatVertically( KoTextDocument*, KoTextParag* parag )
94 {
95 return m_textfs->formatVertically( parag, parag->rect() );
96 }
97 virtual void postFormat( KoTextParag* parag )
98 {
99 m_textfs->fixParagWidth( static_cast<KWTextParag *>( parag ) );
100 }
101private:
102 KWTextFrameSet *m_textfs;
103};
104
105KWTextFrameSet::KWTextFrameSet( KWDocument *_doc, const TQString & name )
106 : KWFrameSet( _doc )
107{
108 //kdDebug() << "KWTextFrameSet::KWTextFrameSet " << this << endl;
109 if ( name.isEmpty() )
110 m_name = _doc->generateFramesetName( i18n( "Text Frameset %1" ) );
111 else
112 m_name = name;
113
114 TQObject::setName( m_name.utf8() ); // store name in the TQObject, for DCOP users
115 init();
116}
117
118KWTextFrameSet::KWTextFrameSet( KWDocument* doc, const TQDomElement& tag, KoOasisContext& /*context*/ )
119 : KWFrameSet( doc )
120{
121 m_name = tag.attributeNS( KoXmlNS::draw, "name", TQString() );
122 if ( doc->frameSetByName( m_name ) ) // already exists!
123 m_name = doc->generateFramesetName( m_name + " %1" );
124 init();
125 // Note that we don't call loadOasis here. The caller wants to do it,
126 // to get the frame it returns.
127}
128
129// protected constructor for testing purposes; does not do an init.
130KWTextFrameSet::KWTextFrameSet( const TQString &name ) : KWFrameSet(0) {
131 m_name = name;
132
133 TQObject::setName( m_name.utf8() ); // store name in the TQObject, for DCOP users
134 m_currentViewMode = 0L;
135 m_currentDrawnFrame = 0L;
136 m_lastTextDocHeight = 0;
137 m_textobj = 0;
138}
139
140void KWTextFrameSet::init()
141{
142 m_currentViewMode = 0L;
143 m_currentDrawnFrame = 0L;
144 m_lastTextDocHeight = 0;
145 // Create the text document to set in the text object
146 KWTextDocument* textdoc = new KWTextDocument( this,
147 new KoTextFormatCollection( m_doc->defaultFont(), TQColor(),
148 m_doc->globalLanguage(),
149 m_doc->globalHyphenation() ),
150 new KWTextFormatter( this ) );
151 textdoc->setFlow( this );
152 textdoc->setPageBreakEnabled( true ); // get verticalBreak to be called
153 if ( m_doc->tabStopValue() != -1 )
154 textdoc->setTabStops( m_doc->ptToLayoutUnitPixX( m_doc->tabStopValue() ));
155
156 m_textobj = new KoTextObject( textdoc, m_doc->styleCollection()->findStyle( "Standard" ),
157 this, (m_name+"-textobj").utf8() );
158 m_doc->backSpeller()->registerNewTextObject( m_textobj );
159 connect( m_textobj, TQ_SIGNAL( availableHeightNeeded() ),
160 TQ_SLOT( slotAvailableHeightNeeded() ) );
161 connect( m_textobj, TQ_SIGNAL( afterFormatting( int, KoTextParag*, bool* ) ),
162 TQ_SLOT( slotAfterFormatting( int, KoTextParag*, bool* ) ) );
163 //connect( m_textobj, TQ_SIGNAL( formattingFirstParag() ),
164 // TQ_SLOT( slotFormattingFirstParag() ) );
165 //connect( m_textobj, TQ_SIGNAL( chapterParagraphFormatted( KoTextParag * ) ),
166 // TQ_SLOT( slotChapterParagraphFormatted( KoTextParag * ) ) );
167 connect( m_textobj, TQ_SIGNAL( newCommand( KCommand * ) ),
168 TQ_SLOT( slotNewCommand( KCommand * ) ) );
169 connect( m_textobj, TQ_SIGNAL( repaintChanged( KoTextObject* ) ),
170 TQ_SLOT( slotRepaintChanged() ) );
171 connect( m_textobj, TQ_SIGNAL( paragraphDeleted( KoTextParag*) ),
172 TQ_SLOT( slotParagraphDeleted(KoTextParag*) ));
173
174 connect( m_textobj, TQ_SIGNAL( paragraphCreated( KoTextParag*) ),
175 TQ_SLOT( slotParagraphCreated(KoTextParag*) ));
176 connect( m_textobj, TQ_SIGNAL( paragraphModified( KoTextParag*, int, int, int) ),
177 TQ_SLOT( slotParagraphModified(KoTextParag*, int, int, int) ));
178}
179
180void KWTextFrameSet::slotParagraphModified(KoTextParag* _parag, int /*KoTextParag::ParagModifyType*/ _type, int start, int length)
181{
182 kWordDocument()->paragraphModified(_parag, _type, start, length);
183}
184
185void KWTextFrameSet::slotParagraphCreated(KoTextParag* /*_parag*/)
186{
187 //todo
188}
189
190void KWTextFrameSet::slotParagraphDeleted(KoTextParag*_parag)
191{
192 kWordDocument()->paragraphDeleted( _parag, this);
193}
194
195KWordFrameSetIface* KWTextFrameSet::dcopObject()
196{
197 if ( !m_dcop )
198 m_dcop = new KWordTextFrameSetIface( this );
199
200 return m_dcop;
201}
202
204{
205 return new KWTextFrameSetEdit( this, canvas );
206}
207
208KoTextDocument * KWTextFrameSet::textDocument() const
209{
210 return m_textobj->textDocument();
211}
212
213KWTextDocument * KWTextFrameSet::kwTextDocument() const
214{
215 return static_cast<KWTextDocument *>(m_textobj->textDocument());
216}
217
218void KWTextFrameSet::slotAvailableHeightNeeded()
219{
220 Q_ASSERT( isVisible() );
221 kdDebug() << "KWTextFrameSet::slotAvailableHeightNeeded " << name() << endl;
222 updateFrames( 0 ); // only do the available-height determination
223}
224
225KWFrame * KWTextFrameSet::documentToInternal( const KoPoint &dPoint, TQPoint &iPoint ) const
226{
227#ifdef DEBUG_DTI
228 kdDebug() << "KWTextFrameSet::documentToInternal dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
229#endif
230 if ( !m_doc->layoutViewMode()->hasFrames() ) { // text viewmode
231 iPoint = TQPoint( m_doc->ptToLayoutUnitPixX( dPoint.x() ),
232 m_doc->ptToLayoutUnitPixY( dPoint.y() ) );
233 return m_frames.getFirst();
234 }
235 // Find the frame that contains dPoint. To go fast, we look them up by page number.
236 int pageNum = m_doc->pageManager()->pageNumber(dPoint);
237 TQPtrListIterator<KWFrame> frameIt( framesInPage( pageNum ) );
238 for ( ; frameIt.current(); ++frameIt )
239 {
240 KWFrame *theFrame = frameIt.current();
241 if ( theFrame->contains( dPoint ) )
242 {
243 iPoint.setX( m_doc->ptToLayoutUnitPixX( dPoint.x() - theFrame->innerRect().x() ) );
244 iPoint.setY( m_doc->ptToLayoutUnitPixY( dPoint.y() - theFrame->innerRect().y() + theFrame->internalY() ) );
245#ifdef DEBUG_DTI
246 kdDebug() << "documentToInternal: returning " << iPoint.x() << "," << iPoint.y()
247 << " internalY=" << theFrame->internalY() << " because frame=" << theFrame
248 << " contains dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
249#endif
250 return theFrame;
251 }
252#ifdef DEBUG_DTI
253 //else
254 // kdDebug() << "DTI: " << frameRect
255 // << " doesn't contain nPoint:" << nPoint.x() << "," << nPoint.y() << endl;
256#endif
257 }
258 // Not found. This means the mouse isn't over any frame, in the page pageNum.
259 iPoint = m_doc->ptToLayoutUnitPix( dPoint ); // bah
260 return 0;
261}
262
263KWFrame * KWTextFrameSet::documentToInternalMouseSelection( const KoPoint &dPoint, TQPoint &iPoint, RelativePosition& relPos, KWViewMode *viewMode ) const
264{
265#ifdef DEBUG_DTI
266 kdDebug() << "KWTextFrameSet::documentToInternalMouseSelection dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
267#endif
268 if ( !viewMode->hasFrames() ) { // text viewmode
269 relPos = InsideFrame;
270 iPoint = TQPoint( m_doc->ptToLayoutUnitPixX( dPoint.x() ),
271 m_doc->ptToLayoutUnitPixY( dPoint.y() ) );
272 return m_frames.getFirst();
273 }
274
275 // Find the frame that contains dPoint. To go fast, we look them up by page number.
276 int pageNum = m_doc->pageManager()->pageNumber(dPoint);
277 TQPtrListIterator<KWFrame> frameIt( framesInPage( pageNum ) );
278 for ( ; frameIt.current(); ++frameIt )
279 {
280 KWFrame *theFrame = frameIt.current();
281 if ( theFrame->contains( dPoint ) )
282 {
283 iPoint.setX( m_doc->ptToLayoutUnitPixX( dPoint.x() - theFrame->innerRect().x() ) );
284 iPoint.setY( m_doc->ptToLayoutUnitPixY( dPoint.y() - theFrame->innerRect().y() + theFrame->internalY() ) );
285#ifdef DEBUG_DTI
286 kdDebug() << "documentToInternal: returning InsideFrame " << iPoint.x() << "," << iPoint.y()
287 << " internalY=" << theFrame->internalY() << " because frame=" << theFrame
288 << " contains dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
289#endif
290 relPos = InsideFrame;
291 return theFrame;
292 }
293 }
294 frameIt.toFirst();
295 for ( ; frameIt.current(); ++frameIt )
296 {
297 KWFrame *theFrame = frameIt.current();
298 KoRect openLeftRect( theFrame->innerRect() );
299 openLeftRect.setLeft( theFrame->paddingLeft() );
300#ifdef DEBUG_DTI
301 kdDebug() << "documentToInternal: openLeftRect=" << openLeftRect << endl;
302#endif
303 if ( openLeftRect.contains( dPoint ) )
304 {
305 // We are at the left of this frame (and not in any other frame of this frameset)
306 iPoint.setX( m_doc->ptToLayoutUnitPixX(theFrame->innerRect().left() ));
307 iPoint.setY( m_doc->ptToLayoutUnitPixY( dPoint.y() - theFrame->top() + theFrame->internalY() ) );
308#ifdef DEBUG_DTI
309 kdDebug() << "documentToInternal: returning LeftOfFrame " << iPoint.x() << "," << iPoint.y()
310 << " internalY=" << theFrame->internalY() << " because openLeftRect=" << openLeftRect
311 << " contains dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
312#endif
313 relPos = LeftOfFrame;
314 return theFrame;
315 }
316 KoRect openTopRect( KoPoint( 0, 0 ), theFrame->innerRect().bottomRight() );
317#ifdef DEBUG_DTI
318 kdDebug() << "documentToInternal: openTopRect=" << openTopRect << endl;
319#endif
320 if ( openTopRect.contains( dPoint ) )
321 {
322 // We are at the top of this frame (...)
323 iPoint.setX( m_doc->ptToLayoutUnitPixX( dPoint.x() - theFrame->innerRect().left() ) );
324 iPoint.setY( m_doc->ptToLayoutUnitPixY( theFrame->internalY() ) );
325#ifdef DEBUG_DTI
326 kdDebug() << "documentToInternal: returning " << iPoint.x() << "," << iPoint.y()
327 << " internalY=" << theFrame->internalY() << " because openTopRect=" << openTopRect
328 << " contains dPoint:" << dPoint.x() << "," << dPoint.y() << endl;
329#endif
330 relPos = TopOfFrame;
331 return theFrame;
332 }
333 }
334 // Not found. This means we are under (or at the right of), the frames in pageNum.
335 // In that case, go for the first frame in the next page.
336 if ( pageNum + 1 >= (int)m_framesInPage.size() + m_firstPage )
337 {
338 // Under last frame of last page!
339 KWFrame *theFrame = m_frames.getLast();
340 iPoint.setX( m_doc->ptToLayoutUnitPixX( theFrame->innerWidth() ) );
341 iPoint.setY( m_doc->ptToLayoutUnitPixY( theFrame->innerHeight() ) );
342#ifdef DEBUG_DTI
343 kdDebug() << "documentToInternal: returning AtEnd " << iPoint.x() << "," << iPoint.y()
344 << " because we are under all frames of the last page" << endl;
345#endif
346 relPos = AtEnd;
347 return theFrame;
348 }
349 else
350 {
351 TQPtrListIterator<KWFrame> frameIt( framesInPage( pageNum + 1 ) );
352 if ( frameIt.current() )
353 {
354 // There is a frame on the next page
355 KWFrame *theFrame = frameIt.current();
356 KoRect openTopRect( theFrame->innerRect() );
357 openTopRect.setTop( 0 );
358 if ( openTopRect.contains( dPoint ) ) // We are at the top of this frame
359 iPoint.setX( m_doc->ptToLayoutUnitPixX( dPoint.x() - theFrame->left() ) );
360 else
361 iPoint.setX( 0 ); // We are, hmm, on the left or right of the frames
362 iPoint.setY( m_doc->ptToLayoutUnitPixY( theFrame->internalY() ) );
363#ifdef DEBUG_DTI
364 kdDebug() << "documentToInternal: returning TopOfFrame " << iPoint.x() << "," << iPoint.y()
365 << " because we are under all frames of page " << pageNum << endl;
366#endif
367 relPos = TopOfFrame;
368 return theFrame;
369 } // else there is a gap (no frames on that page, but on some other further down)
370 // This case isn't handled (and should be VERY rare I think)
371 }
372
373 iPoint = m_doc->ptToLayoutUnitPix( dPoint ); // bah
374#ifdef DEBUG_DTI
375 kdDebug() << "documentToInternal: returning not found for " << iPoint.x() << "," << iPoint.y() << endl;
376#endif
377 return 0;
378}
379
380KWFrame * KWTextFrameSet::internalToDocumentWithHint( const TQPoint &iPoint, KoPoint &dPoint, const KoPoint &hintDPoint ) const
381{
382#ifdef DEBUG_ITD
383 kdDebug() << "KWTextFrameSet::internalToDocumentWithHint hintDPoint: " << hintDPoint.x() << "," << hintDPoint.y() << endl;
384#endif
385 if ( !m_doc->layoutViewMode()->hasFrames() ) { // text viewmode
386 dPoint = m_doc->layoutUnitPtToPt( m_doc->pixelToPt( iPoint ) );
387 return m_frames.getFirst();
388 }
389 KWFrame *lastFrame = 0L;
390 TQPtrListIterator<KWFrame> frameIt( frameIterator() );
391 for ( ; frameIt.current(); ++frameIt )
392 {
393 KWFrame *theFrame = frameIt.current();
394 TQRect r( 0, m_doc->ptToLayoutUnitPixY( theFrame->internalY() ), m_doc->ptToLayoutUnitPixX( theFrame->innerWidth() )+1, m_doc->ptToLayoutUnitPixY( theFrame->innerHeight() )+1 );
395#ifdef DEBUG_ITD
396 kdDebug() << "ITD: r=" << r << " iPoint=" << iPoint.x() << "," << iPoint.y() << endl;
397#endif
398 // r is the frame in qrt coords
399 if ( r.contains( iPoint ) ) // both r and p are in layout units (aka internal)
400 {
401 dPoint = internalToDocumentKnowingFrame( iPoint, theFrame );
402#ifdef DEBUG_ITD
403 kdDebug() << "copy: " << theFrame->isCopy() << " hintDPoint.y()=" << hintDPoint.y() << " dPoint.y()=" << dPoint.y() << endl;
404#endif
405 // First test: No "hintDPoint" specified, go for the first match
406 // Second test: hintDPoint specified, check if we are far enough
407 if ( hintDPoint.isNull() || hintDPoint.y() <= dPoint.y() )
408 return theFrame;
409 // Remember that this frame matched, in case we find no further frame that matches
410 lastFrame = theFrame;
411 }
412 else if ( lastFrame )
413 {
414 return lastFrame;
415 }
416 }
417
418 // This happens when the parag is on a not-yet-created page (formatMore will notice afterwards)
419 // So it doesn't matter much what happens here, we'll redo it anyway.
420#ifdef DEBUG_ITD
421 kdDebug(32002) << "KWTextFrameSet::internalToDocumentWithHint " << iPoint.x() << "," << iPoint.y()
422 << " not in any frame of " << (void*)this << endl;
423#endif
424 dPoint = m_doc->layoutUnitPtToPt( m_doc->pixelToPt( iPoint ) ); // bah
425 return 0L;
426}
427
428// relPoint is in relative coordinates (pt)
429KoPoint KWTextFrameSet::internalToDocumentKnowingFrame( const KoPoint &relPoint, KWFrame* theFrame ) const
430{
431 // It's ok to have theFrame == 0 in the text viewmode, but not in other modes
432 if ( m_doc->layoutViewMode()->hasFrames() )
433 Q_ASSERT( theFrame );
434 if ( theFrame )
435 return KoPoint( relPoint.x() + theFrame->innerRect().x(),
436 relPoint.y() - theFrame->internalY() + theFrame->innerRect().y() );
437 else
438 return relPoint;
439}
440
441KoPoint KWTextFrameSet::internalToDocumentKnowingFrame( const TQPoint &iPoint, KWFrame* theFrame ) const
442{
443 // Convert LU to relative coordinates (pt), then call the real internalToDocumentKnowingFrame().
444 return internalToDocumentKnowingFrame( m_doc->layoutUnitPtToPt( m_doc->pixelToPt( iPoint ) ), theFrame );
445}
446
447TQPoint KWTextFrameSet::moveToPage( int currentPgNum, short int direction ) const
448{
449 if ( !isVisible() || m_frames.isEmpty() )
450 return TQPoint();
451 //kdDebug() << "KWTextFrameSet::moveToPage currentPgNum=" << currentPgNum << " direction=" << direction << endl;
452 int num = currentPgNum + direction;
453 int pages = m_doc->pageCount();
454 for ( ; num >= 0 && num < pages ; num += direction )
455 {
456 //kdDebug() << "KWTextFrameSet::moveToPage num=" << num << " pages=" << pages << endl;
457 // Find the first frame on page num
458 if ( num < m_firstPage || num >= (int)m_framesInPage.size() + m_firstPage )
459 continue; // No frame on that page
460
461 //kdDebug() << "KWTextFrameSet::moveToPage ok for first frame in page " << num << endl;
462 TQPtrListIterator<KWFrame> frameIt( framesInPage( num ) );
463 return TQPoint( 0, m_doc->ptToLayoutUnitPixY( frameIt.current()->internalY() ) + 2 ); // found, ok.
464 }
465 // Not found. Go to top of first frame or bottom of last frame, depending on direction
466 if ( direction < 0 )
467 return TQPoint( 0, m_doc->ptToLayoutUnitPixY( m_frames.getFirst()->internalY() ) + 2 );
468 else
469 {
470 KWFrame * theFrame = m_frames.getLast();
471 return TQPoint( 0, m_doc->ptToLayoutUnitPixY( theFrame->internalY() + theFrame->innerHeight() ) );
472 }
473}
474
475void KWTextFrameSet::drawContents( TQPainter *p, const TQRect & crect, const TQColorGroup &cg,
476 bool onlyChanged, bool resetChanged,
477 KWFrameSetEdit *edit, KWViewMode *viewMode,
479{
480 m_currentViewMode = viewMode;
481 KWFrameSet::drawContents( p, crect, cg, onlyChanged, resetChanged, edit, viewMode, fvm );
482
483 // Main textframeset: draw the footnote line if there are footnotes
484 if ( isMainFrameset() && viewMode->hasFrames() )
485 {
486 // We stored the info "there's a footnote in this page" in the frame[s]
487 // of the maintextframeset for that page. Usually one, but could be more
488 // if there are columns. However we want to draw the line only once, so we
489 // do it here and not in drawFrame (we'd have problems with cliprect anyway).
490 if ( m_doc->footNoteSeparatorLineWidth() ==0.0)
491 return;
492
493 int pages = m_doc->pageCount();
494 KWPage *page = m_doc->pageManager()->page(m_doc->pageManager()->startPage());
495 double left = page->leftMargin();
496 double pageWidth = page->width() - page->rightMargin() - left;
497 double width = pageWidth * m_doc->footNoteSeparatorLineLength() / 100.0;
498 int numColumns = m_doc->numColumns();
499 for ( int pageNum = 0; pageNum < pages; pageNum++ )
500 {
501 //if ( viewMode->isPageVisible( pageNum ) )
502 {
503 uint frameNum = pageNum * numColumns /*+ col 0 here*/;
504 if ( frameNum < frameCount() ) // not true on the "endnotes-only" page
505 {
506 KWFrame* frame = this->frame( frameNum ); // ## or use framesInPage ?
507 //kdDebug() << " Footnote line: page " << pageNum << " found frame " << frameNum << " drawFootNoteLine=" << frame->drawFootNoteLine() << endl;
508 if ( frame->drawFootNoteLine() )
509 {
510 double y = frame->bottomLeft().y() + m_doc->headerFooterInfo().ptFootNoteBodySpacing / 2;
511 KoRect rect( left, y, width, 0 ); // this rect is flat
512 switch( m_doc->footNoteSeparatorLinePosition())
513 {
514 case SLP_LEFT:
515 break;
516 case SLP_CENTERED:
517 rect = KoRect( pageWidth/2.0+left-width/2.0, y,width,0);
518 break;
519 case SLP_RIGHT:
520 rect = KoRect( pageWidth+left-width, y,width,0);
521 break;
522 }
523
524 TQRect flatRect = viewMode->normalToView( m_doc->zoomRect( rect ) );
525 //kdDebug() << " KWTextFrameSet::drawContents rect=" << rect << " zoomed:" << flatRect << endl;
526 flatRect.setBottom( flatRect.top() + 1 ); // #!@!@!& TQRect....
527 if ( flatRect.intersects( crect ) ) {
528 p->save();
529 TQPen pen( KoTextFormat::defaultTextColor( p ), // always in default fg color (and black when printing)
530 KoBorder::zoomWidthY( m_doc->footNoteSeparatorLineWidth(), m_doc, 1 ) ); // penwidth = zoomIt( 2 pt )
531 switch( m_doc->footNoteSeparatorLineType())
532 {
533 case SLT_SOLID:
534 pen.setStyle( TQt::SolidLine );
535 break;
536 case SLT_DASH:
537 pen.setStyle( TQt::DashLine );
538 break;
539 case SLT_DOT:
540 pen.setStyle( TQt::DotLine );
541 break;
542 case SLT_DASH_DOT:
543 pen.setStyle( TQt::DashDotLine );
544 break;
545 case SLT_DASH_DOT_DOT:
546 pen.setStyle( TQt::DashDotDotLine );
547 break;
548 }
549 p->setPen( pen );
550 p->drawLine( flatRect.left(), flatRect.top(), flatRect.right(), flatRect.top() );
551 //kdDebug() << " drawLine( " << flatRect.left() << ", " << flatRect.top() << ", " << flatRect.right() << ", " << flatRect.top() << endl;
552 p->restore();
553 }
554 }
555 }
556 }
557 }
558 }
559}
560
561void KWTextFrameSet::drawFrame( KWFrame *theFrame, TQPainter *painter, const TQRect &fcrect, const TQRect &crect,
562 const TQPoint& translationOffset,
563 KWFrame *settingsFrame, const TQColorGroup &cg, bool onlyChanged, bool resetChanged,
564 KWFrameSetEdit *edit, KWViewMode *viewMode, bool drawUnderlyingFrames )
565{
566 // Detect if text frame needs transparency painting, to save time if it's using SolidPattern
567 // In theory this code should be in kwFrameSet, but currently only text frames obey m_backgroundColor.
568 if ( theFrame )
569 {
570 drawUnderlyingFrames &= theFrame->isTransparent();
571 }
572 KWFrameSet::drawFrame( theFrame, painter, fcrect, crect, translationOffset, settingsFrame, cg, onlyChanged, resetChanged, edit, viewMode, drawUnderlyingFrames );
573}
574
575void KWTextFrameSet::drawFrameContents( KWFrame *theFrame, TQPainter *painter, const TQRect &r,
576 const TQColorGroup &cg, bool onlyChanged, bool resetChanged,
577 KWFrameSetEdit *edit, KWViewMode *viewMode )
578{
579 Q_ASSERT( r.isValid() );
580 // In this method the painter is translated to the frame's coordinate system
581 // (in the first frame (0,0) will be its topleft, in the second frame it will be (0,internalY) etc.
582
583 //kdDebug(32001) << "KWTextFrameSet::drawFrameContents " << name() << "(frame " << frameFromPtr( theFrame ) << ") crect(r)=" << r << " onlyChanged=" << onlyChanged << endl;
584 m_currentDrawnFrame = theFrame;
585 if ( theFrame ) // 0L in the text viewmode
586 {
587 // Update variables for each frame (e.g. for page-number)
588 // If more than KWPgNumVariable need this functionality, create an intermediary base class
589 TQPtrListIterator<KoTextCustomItem> cit( textDocument()->allCustomItems() );
590 for ( ; cit.current() ; ++cit )
591 {
592 KWPgNumVariable * var = dynamic_cast<KWPgNumVariable *>( cit.current() );
593 if ( var && !var->isDeleted() )
594 {
595 TQSize oldSize( var->width, var->height );
596 const int pageNumberOffset = kWordDocument()->variableCollection()->variableSetting()->startingPageNumber() - 1;
597 switch ( var->subType() )
598 {
599 case KWPgNumVariable::VST_PGNUM_CURRENT:
600 //kdDebug() << "KWTextFrameSet::drawFrame updating pgnum variable to " << theFrame->pageNumber()
601 // << " and invalidating parag " << var->paragraph() << endl;
602 var->setPgNum( theFrame->pageNumber() + pageNumberOffset );
603 break;
604 case KWPgNumVariable::VST_CURRENT_SECTION:
605 var->setSectionTitle( kWordDocument()->sectionTitle( theFrame->pageNumber() ) );
606 break;
607 case KWPgNumVariable::VST_PGNUM_PREVIOUS:
608 var->setPgNum( TQMAX(theFrame->pageNumber()-1,0) + pageNumberOffset );
609 break;
610 case KWPgNumVariable::VST_PGNUM_NEXT:
611 var->setPgNum( theFrame->pageNumber() + 1 + pageNumberOffset );
612 break;
613 }
614
615 var->resize();
616 TQSize newSize( var->width, var->height );
617 if ( oldSize != newSize )
618 var->paragraph()->invalidate( 0 ); // size has changed -> need reformatting !
619 var->paragraph()->setChanged( true );
620 }
621 }
622 }
623
624 KoTextCursor * cursor = edit ? (dynamic_cast<KWTextFrameSetEdit *>(edit) ? static_cast<KWTextFrameSetEdit *>(edit)->cursor() : 0) : 0;
625 uint drawingFlags = 0;
626 if ( viewMode->drawSelections() )
627 drawingFlags |= KoTextDocument::DrawSelections;
628 if ( !viewMode->drawFrameBackground() )
629 drawingFlags |= KoTextDocument::TransparentBackground;
630 if ( m_doc->backgroundSpellCheckEnabled() )
631 drawingFlags |= KoTextDocument::DrawMisspelledLine;
632 if ( m_doc->viewFormattingChars() )
633 drawingFlags |= KoTextDocument::DrawFormattingChars;
634
635 //kdDebug(32001) << "KWTextFrameSet::drawFrame calling drawWYSIWYG. cg base color:" << cg.brush( TQColorGroup::Base) << endl;
636 KoTextParag * lastFormatted = textDocument()->drawWYSIWYG(
637 painter, r.x(), r.y(), r.width(), r.height(),
638 cg, kWordDocument(),
639 onlyChanged, false, cursor, resetChanged, drawingFlags );
640
641 // The last paragraph of this frame might have a bit in the next frame too.
642 // In that case, and if we're only drawing changed paragraphs, (and resetting changed),
643 // we have to set changed to true again, to draw the bottom of the parag in the next frame.
644 if ( onlyChanged && resetChanged )
645 {
646 // Finding the "last parag of the frame" is a bit tricky.
647 // It's usually the one before lastFormatted, except if it's actually lastParag :} [see KoTextDocument::draw]
648 KoTextParag * lastDrawn = lastFormatted->prev();
649 if ( lastFormatted == textDocument()->lastParag() && ( !lastDrawn || m_doc->layoutUnitToPixelY( lastDrawn->rect().bottom() ) < r.bottom() ) )
650 lastDrawn = lastFormatted;
651
652 //kdDebug(32002) << "KWTextFrameSet::drawFrame drawn. onlyChanged=" << onlyChanged << " resetChanged=" << resetChanged << " lastDrawn=" << lastDrawn->paragId() << " lastDrawn's bottom:" << lastDrawn->rect().bottom() << " r.bottom=" << r.bottom() << endl;
653 if ( lastDrawn && m_doc->layoutUnitToPixelY( lastDrawn->rect().bottom() ) > r.bottom() )
654 {
655 //kdDebug(32002) << "KWTextFrameSet::drawFrame setting lastDrawn " << lastDrawn->paragId() << " to changed" << endl;
656 lastDrawn->setChanged( true );
657 }
658 }
659
660 // NOTE: TQTextView sets m_lastFormatted to lastFormatted here
661 // But when scrolling up, this causes to reformat a lot of stuff for nothing.
662 // And updateViewArea takes care of formatting things before we even arrive here.
663
664 // Blank area under the very last paragraph - TQRT draws it up to textdoc->height,
665 // we have to draw it from there up to the bottom of the last frame.
666 if ( ( !lastFormatted || lastFormatted == textDocument()->lastParag() )
667 && viewMode->drawFrameBackground() )
668 {
669 // This is drawing code, so we convert everything to pixels
670 int docHeight = textDocument()->lastParag()->pixelRect(m_doc).bottom() + 1;
671 //TQRect frameRect = m_doc->zoomRect( (theFrame->innerRect()) );
672
673 TQSize availSize = viewMode->availableSizeForText( this );
674 TQRect blank( 0, docHeight, availSize.width(), availSize.height() /*+ frameRect.height() ?? */ - docHeight );
675 //kdDebug(32002) << this << " Blank area: " << blank << endl;
676 painter->fillRect( blank, cg.brush( TQColorGroup::Base ) );
677 // for debugging :)
678 //painter->setPen( TQPen(TQt::blue, 1, DashLine) ); painter->drawRect( blank );
679 }
680 m_currentDrawnFrame = 0L;
681}
682
683void KWTextFrameSet::drawCursor( TQPainter *p, KoTextCursor *cursor, bool cursorVisible, KWCanvas *canvas, KWFrame *theFrame )
684{
685 // This redraws the paragraph where the cursor is - with a small clip region around the cursor
686 m_currentViewMode = canvas->viewMode();
687 bool hasFrames = m_currentViewMode->hasFrames();
688 m_currentDrawnFrame = theFrame;
689
690 TQRect normalFrameRect;
691 if (hasFrames)
692 normalFrameRect = m_doc->zoomRect( theFrame->innerRect() );
693 else
694 normalFrameRect = TQRect( TQPoint(0, 0), m_currentViewMode->contentsSize() );
695
696 KoTextParag* parag = cursor->parag();
697 TQPoint topLeft = parag->rect().topLeft(); // in TQRT coords
698 int lineY;
699 // Cursor height, in pixels
700 int cursorHeight = m_doc->layoutUnitToPixelY( topLeft.y(), parag->lineHeightOfChar( cursor->index(), 0, &lineY ) );
701 TQPoint iPoint( topLeft.x() + cursor->x(),
702 topLeft.y() + lineY );
703
704#ifdef DEBUG_CURSOR
705 kdDebug() << "KWTextFrameSet::drawCursor topLeft=" << topLeft.x() << "," << topLeft.y()
706 << " x=" << cursor->x() << " y=" << lineY << endl;
707 kdDebug() << "KWTextFrameSet::drawCursor iPoint=" << iPoint.x() << "," << iPoint.y()
708 << " cursorHeight=" << cursorHeight << endl;
709#endif
710 KoPoint dPoint;
711 KoPoint hintDPoint = theFrame ? theFrame->innerRect().topLeft() : KoPoint();
712 if ( internalToDocumentWithHint( iPoint, dPoint, hintDPoint ) )
713 {
714#ifdef DEBUG_CURSOR
715 kdDebug() << " dPoint(doc, pts)=" << dPoint.x() << "," << dPoint.y() << endl;
716 TQPoint debugPt = m_doc->zoomPoint( dPoint );
717 kdDebug() << " zoomed dPoint(doc, pixels)=" << debugPt.x() << "," << debugPt.y() << endl;
718#endif
719 TQPoint vPoint = m_currentViewMode->normalToView( m_doc->zoomPoint( dPoint ) ); // from doc to view contents
720#ifdef DEBUG_CURSOR
721 kdDebug() << " vPoint(view, pixels)=" << vPoint.x() << "," << vPoint.y() << endl;
722#endif
723 // from now on, iPoint will be in pixels
724 iPoint = m_doc->layoutUnitToPixel( iPoint );
725 //int xadj = parag->at( cursor->index() )->pixelxadj;
726#ifdef DEBUG_CURSOR
727 //kdDebug() << " iPoint in pixels : " << iPoint.x() << "," << iPoint.y() << " will add xadj=" << xadj << endl;
728#endif
729 //iPoint.rx() += xadj;
730 //vPoint.rx() += xadj;
731 // very small clipping around the cursor
732 TQRect clip( vPoint.x() - 5, vPoint.y(), 10, cursorHeight );
733
734#ifdef DEBUG_CURSOR
735 kdDebug() << " clip(view, before intersect)=" << clip << endl;
736#endif
737
738 TQRect viewFrameRect = m_currentViewMode->normalToView( normalFrameRect );
739 clip &= viewFrameRect; // clip to frame
740#ifdef DEBUG_CURSOR
741 kdDebug() << "KWTextFrameSet::drawCursor normalFrameRect=" << normalFrameRect
742 << " clip(view, after intersect)=" << clip << endl;
743#endif
744
745 TQRegion reg;
746 if ( hasFrames ) {
747 reg = frameClipRegion( p, theFrame, clip, m_currentViewMode );
748 if ( !isFloating() ) // problem with multiparent inline frames
749 reg &= p->xForm( viewFrameRect );
750 }
751
752 if ( !hasFrames || !reg.isEmpty() )
753 {
754#ifdef DEBUG_CURSOR
755 // for debug only!
756 //p->fillRect( clip, TQBrush( TQt::red, TQBrush::Dense3Pattern ) );
757#endif
758
759 p->save();
760 TQColorGroup cg = TQApplication::palette().active();
761
762 if ( hasFrames )
763 {
764 p->setClipRegion( reg );
765 // translate to qrt coords - after setting the clip region !
766 // see internalToDocumentWithHint
767 int translationX = viewFrameRect.left();
768 int translationY = viewFrameRect.top() - m_doc->zoomItY( theFrame->internalY() );
769#ifdef DEBUG_CURSOR
770 kdDebug() << " translating Y by viewFrameRect.top()-internalY in pixelY= " << viewFrameRect.top() << "-" << m_doc->zoomItY( theFrame->internalY() ) << "=" << viewFrameRect.top() - m_doc->zoomItY( theFrame->internalY() ) << endl;
771#endif
772 p->translate( translationX, translationY );
773 p->setBrushOrigin( p->brushOrigin().x() + translationX, p->brushOrigin().y() + translationY );
774
775 // The settings come from this frame
776 KWFrame * settings = settingsFrame( theFrame );
777
778 TQBrush bgBrush( settings->backgroundColor() );
779 bgBrush.setColor( KWDocument::resolveBgColor( bgBrush.color(), p ) );
780 cg.setBrush( TQColorGroup::Base, bgBrush );
781 // color of cursor, the inverse of the frame background
782 TQColor background = bgBrush.color();
783 cg.setColor(TQColorGroup::Text, TQColor( 255 - background.red(),
784 255 - background.green(), 255 - background.blue()) );
785 }
786 else if(dynamic_cast<KWViewModeText *>(m_currentViewMode) != 0)
787 p->translate( KWViewModeText::OFFSET, 0 );
788
789 TQPixmap *pix = 0;
790 uint drawingFlags = KoTextDocument::DrawSelections;
791 if ( m_doc->backgroundSpellCheckEnabled() )
792 drawingFlags |= KoTextDocument::DrawMisspelledLine;
793 if ( m_doc->viewFormattingChars() )
794 drawingFlags |= KoTextDocument::DrawFormattingChars;
795
796 // To force the drawing to happen:
797 bool wasChanged = parag->hasChanged();
798 int oldLineChanged = parag->lineChanged();
799 int line; // line number
800 parag->lineStartOfChar( cursor->index(), 0, &line );
801 parag->setChanged( false ); // not all changed, only from a given line
802 parag->setLineChanged( line );
803
804 textDocument()->drawParagWYSIWYG(
805 p, parag,
806 TQMAX(0, iPoint.x() - 5), // negative values create problems
807 iPoint.y(), clip.width(), clip.height(),
808 pix, cg, m_doc, // TODO view's zoom handler
809 cursorVisible, cursor, false /*resetChanged*/, drawingFlags );
810
811 if ( wasChanged ) // Maybe we have more changes to draw, than those in the small cliprect
812 parag->setLineChanged( oldLineChanged ); // -1 = all
813 else
814 parag->setChanged( false );
815
816 p->restore();
817
818 //XIM Position
819 TQPoint ximPoint = vPoint;
820 canvas->setXimPosition( ximPoint.x(), ximPoint.y(),
821 0, cursorHeight - parag->lineSpacing( line ) );
822 }
823 }
824 m_currentDrawnFrame = 0L;
825}
826
828{
829 invalidate();
830 // Get the thing going though, repainting doesn't call formatMore
831 m_textobj->formatMore( 2 );
832}
833
835{
836 //kdDebug() << "KWTextFrameSet::invalidate " << name() << endl;
837 m_textobj->setLastFormattedParag( textDocument()->firstParag() );
838 textDocument()->invalidate(); // lazy layout, real update follows upon next repaint
839}
840
841void KWTextFrameSet::slotRepaintChanged()
842{
843 emit repaintChanged( this );
844}
845
846int KWTextFrameSet::paragraphs()
847{
848 int paragraphs = 0;
849 KoTextParag * parag = textDocument()->firstParag();
850 for ( ; parag ; parag = parag->next() )
851 paragraphs++;
852 return paragraphs;
853}
854
855int KWTextFrameSet::paragraphsSelected()
856{
857 int paragraphs = 0;
858 KoTextParag *parag = textDocument()->firstParag();
859 for ( ; parag ; parag = parag->next() ) {
860 if ( parag->hasSelection( KoTextDocument::Standard ) )
861 paragraphs++;
862 }
863 return paragraphs;
864}
865
866
867bool KWTextFrameSet::statistics( TQProgressDialog *progress, ulong & charsWithSpace, ulong & charsWithoutSpace, ulong & words,
868 ulong & sentences, ulong & syllables, ulong & lines, bool selected )
869{
870 return m_textobj->statistics( progress, charsWithSpace, charsWithoutSpace, words, sentences, syllables, lines, selected );
871}
872
873// Only interested in the body textframeset, not in header/footer
874#define kdDebugBody(area) if ( frameSetInfo() == FI_BODY ) kdDebug(area)
875
876TQValueList<KWFrame*> KWTextFrameSet::framesFromTo( int y1, int y2 ) const
877{
878 TQValueList<KWFrame*> framesList;
879 KoPoint pt;
880 KWFrame * firstFrame = internalToDocument( TQPoint(0, y1), pt );
881 if ( !firstFrame )
882 return framesList;
883 framesList.append( firstFrame );
884 uint frameIndex = const_cast<KWTextFrameSet *>(this)->m_frames.findRef( firstFrame );
885 while ( ++frameIndex < m_frames.count() ) {
886 KWFrame* f = frame( frameIndex );
887 if ( f->internalY() > y2 ) // too far down, we're done
888 break;
889 framesList.append( f );
890 }
891 return framesList;
892}
893
894// Helper for adjust*. There are 3 ways to use this method.
895// 1) marginLeft and marginRight set -> determination of left and right margins for adjustMargins
896// 2) marginRight set -> determination of right margin for adjustRMargin
897// 3) breakBegin, breakEnd set -> check whether we should jump over some frames
898// [when there is not enough space besides them]
899// reqMinWidth is the width that the formatter wants to use (current char/item)
900// validHeight is set to the height where marginLeft/marginRight applies (TODO)
901void KWTextFrameSet::getMargins( int yp, int h, int reqMinWidth,
902 int* marginLeft, int* marginRight, int* pageWidth,
903 int* validHeight,
904 int* breakBegin, int* breakEnd, KoTextParag* parag )
905{
906 // paragLeftMargin will be used as the minimum width needed for the parag,
907 // to "see" the parag.
908 // So we only apply the first line margin if it increases that width, i.e. if > 0.
909 // Otherwise only the first line might be visible, in a narrow passage.
910 int paragLeftMargin = parag ? parag->leftMargin() : 0;
911 if ( parag && !parag->string()->isRightToLeft() && parag->firstLineMargin() > 0 )
912 paragLeftMargin += parag->firstLineMargin();
913#ifdef DEBUG_MARGINS
914 kdDebugBody(32002) << " KWTextFrameSet " << this << "(" << name() << ") getMargins yp=" << yp
915 << " h=" << h << " called by "
916 << (marginLeft && marginRight ? "adjustMargins" : "formatVertically")
917 << " paragLeftMargin=" << paragLeftMargin
918 << endl;
919 // Both or none...
920 if (breakBegin) assert(breakEnd);
921 if (breakEnd) assert(breakBegin);
922 // Idem
923 if ( marginLeft ) { assert( marginRight ); assert( pageWidth ); }
924#endif
925
926 // List of text frames holding the paragraph (yp,yp+h)
927 // Usually there is only one, but you can have a paragraph
928 // starting in one frame/column and ending in another one.
929 TQValueList<KWFrame*> textFrames = framesFromTo( yp, yp + h );
930 if (textFrames.isEmpty())
931 {
932#ifdef DEBUG_MARGINS
933 kdDebug(32002) << " getMargins: internalToDocument returned no text frames for y1=" << yp << " y2=" << yp + h << " ->aborting with 0 margins" << endl;
934#endif
935 // frame == 0 happens when the parag is under the last frame.
936 // On an auto-resizable frame, we know the frame will grow so we can go ahead
937 // and use its width.
938 if ( !m_frames.isEmpty() && m_frames.last()->frameBehavior() == KWFrame::AutoExtendFrame )
939 {
940 textFrames.append( m_frames.last() );
941 }
942 else
943 {
944 // On auto-create-new-frame, this means the parag is on a not-yet-created page
945 // (formatMore will notice afterwards)
946 // Abort then, no need to return precise values
947 // We also abort in the third case (Ignore)
948 if ( validHeight )
949 *validHeight = 0;
950 return;
951 }
952 }
953 else
954 {
955
956#ifdef DEBUG_MARGINS
957 kdDebugBody(32002) << " getMargins: internalToDocument returned " << textFrames.count() << " frames holding this paragraph" << endl;
958#endif
959 }
960 if ( validHeight )
961 *validHeight = h; // TODO
962
963 // Everything from there is in layout units
964 // Note: it is very important that this method works in internal coordinates.
965 // Otherwise, parags broken at the line-level (e.g. between two columns) are seen
966 // as still in one piece, and we miss the frames in the 2nd column.
967 int from = 0;
968 // TODO support for variable width... maybe it's enough to take the max here
969 int to = m_doc->ptToLayoutUnitPixX( textFrames.first()->innerWidth() );
970 if ( pageWidth )
971 *pageWidth = to;
972 bool init = false;
973
974#ifdef DEBUG_MARGINS
975 kdDebugBody(32002) << " getMargins: looking for frames between " << yp << " and " << yp+h << " (internal coords)" << endl;
976#endif
977 if ( m_doc->layoutViewMode()->shouldAdjustMargins() )
978 {
979 // Principle: for every frame on top at this height, we'll move from and to
980 // towards each other. The text flows between 'from' and 'to'
981 for ( TQValueList<KWFrame*>::const_iterator txtit = textFrames.begin(), txtend = textFrames.end() ; txtit != txtend ; ++txtit ) {
982 KWFrame* theFrame = *txtit;
983 Q_ASSERT( theFrame->frameStack() );
984 TQValueList<KWFrame*> onTop = theFrame->frameStack()->framesOnTop();
985 for (TQValueListIterator<KWFrame*> fIt = onTop.begin(); from < to && fIt != onTop.end(); ++fIt )
986 {
987 if ( (*fIt)->runAround() == KWFrame::RA_BOUNDINGRECT )
988 {
989 KoRect rectOnTop = theFrame->intersect( (*fIt)->runAroundRect() );
990#ifdef DEBUG_MARGINS
991 kdDebugBody(32002) << " getMargins found frame on top " << (*fIt)->frameSet()->name() << " with rect-on-top at (normal coords) " << rectOnTop << endl;
992#endif
993 TQPoint iTop, iBottom; // top and bottom of intersection in internal coordinates
994
995 if ( documentToInternal( rectOnTop.topLeft(), iTop ) &&
996 iTop.y() <= yp + h && // optimization
997 documentToInternal( rectOnTop.bottomRight(), iBottom ) )
998 {
999#ifdef DEBUG_MARGINS
1000 kdDebugBody(32002) << " in internal coords: " << TQRect(iTop,iBottom) << endl;
1001#endif
1002 // Look for intersection between yp -- yp+h and iTop -- iBottom
1003 if ( TQMAX( yp, iTop.y() ) <= TQMIN( yp+h, iBottom.y() ) )
1004 {
1005#ifdef DEBUG_MARGINS
1006 kdDebugBody(32002) << " getMargins iTop=" << iTop.x() << "," << iTop.y()
1007 << " iBottom=" << iBottom.x() << "," << iBottom.y() << endl;
1008#endif
1009 int availLeft = TQMAX( 0, iTop.x() - from );
1010 int availRight = TQMAX( 0, to - iBottom.x() );
1011#ifdef DEBUG_MARGINS
1012 kdDebugBody(32002) << " getMargins availLeft=" << availLeft
1013 << " availRight=" << availRight << endl;
1014#endif
1015 bool chooseLeft = false;
1016 switch ( (*fIt)->runAroundSide() ) {
1017 case KWFrame::RA_LEFT:
1018 chooseLeft = true;
1019 break;
1020 case KWFrame::RA_RIGHT:
1021 break; // chooseLeft remains false
1022 case KWFrame::RA_BIGGEST:
1023 chooseLeft = ( availLeft > availRight ); // choose the max
1024 };
1025
1026 if ( chooseLeft )
1027 // flow text at the left of the frame
1028 to = TQMIN( to, from + availLeft - 1 ); // can only go left -> TQMIN
1029 else
1030 // flow text at the right of the frame
1031 from = TQMAX( from, to - availRight + 1 ); // can only go right -> TQMAX
1032
1033#ifdef DEBUG_MARGINS
1034 kdDebugBody(32002) << " getMargins from=" << from << " to=" << to << endl;
1035#endif
1036 // If the available space is too small, give up on it
1037 if ( to - from < reqMinWidth + paragLeftMargin )
1038 {
1039#ifdef DEBUG_MARGINS
1040 kdDebugBody(32002) << " smaller than minimum=" << m_doc->ptToLayoutUnitPixX( 15 ) + paragLeftMargin << endl;
1041#endif
1042 from = to;
1043 }
1044
1045 if ( breakEnd && from == to ) // no-space case
1046 {
1047 if ( !init ) // first time
1048 {
1049 init = true;
1050 *breakBegin = iTop.y();
1051 *breakEnd = iBottom.y();
1052 }
1053 else
1054 {
1055 *breakBegin = TQMIN( *breakBegin, iTop.y() );
1056 *breakEnd = TQMAX( *breakEnd, iBottom.y() );
1057 }
1058#ifdef DEBUG_MARGINS
1059 kdDebugBody(32002) << " getMargins iBottom.y=" << iBottom.y()
1060 << " breakBegin=" << *breakBegin
1061 << " breakEnd=" << *breakEnd << endl;
1062#endif
1063 }
1064 } // else no intersection
1065 }// else we got a 0L, or the iTop.y()<=yp+h test didn't work - wrong debug output
1066 // kdDebugBody(32002) << " gerMargins: normalToInternal returned 0L" << endl;
1067 }
1068 }
1069 }
1070 }
1071 if ( marginLeft /*&& marginRight && pageWidth -- implicit*/ )
1072 {
1073#ifdef DEBUG_MARGINS
1074 kdDebugBody(32002) << " getMargins done. from=" << from << " to=" << to << endl;
1075#endif
1076 if ( from == to ) {
1077 from = 0;
1078 to = *pageWidth;
1079 }
1080
1081 if ( marginLeft )
1082 *marginLeft += from;
1083 if ( marginRight )
1084 {
1085#ifdef DEBUG_MARGINS
1086 kdDebug(32002) << " getMargins " << name()
1087 << " page width=" << *pageWidth
1088 << " to=" << to << endl;
1089#endif
1090 *marginRight += *pageWidth - to;
1091 }
1092 }
1093}
1094
1095void KWTextFrameSet::adjustMargins( int yp, int h, int reqMinWidth, int& leftMargin, int& rightMargin, int& pageWidth, KoTextParag* parag )
1096{
1097#ifdef DEBUG_MARGINS
1098 kdDebugBody(32002) << "KWTextFrameSet::adjustMargins called for paragraph " << (parag?parag->paragId():-1) << endl;
1099#endif
1100 int validHeight; // currently ignored (TODO)
1101 getMargins( yp, h, reqMinWidth, &leftMargin, &rightMargin, &pageWidth, &validHeight, 0L, 0L, parag );
1102#ifdef DEBUG_MARGINS
1103 kdDebugBody(32002) << "KWTextFrameSet::adjustMargins(yp=" << yp << " h=" << h << " reqMinWidth=" << reqMinWidth << " returning"
1104 << " leftMargin=" << leftMargin << " rightMargin=" << rightMargin
1105 << " valid from " << yp << " to " << yp+validHeight << endl;
1106#endif
1107}
1108
1109// helper for formatVertically
1110bool KWTextFrameSet::checkVerticalBreak( int & yp, int & hp, KoTextParag * parag, bool linesTogether, int breakBegin, int breakEnd )
1111{
1112 // We need the "+1" here because when skipping a frame on top, we want to be _under_
1113 // its bottom. Without the +1, we hit the frame again on the next adjustLMargin call.
1114
1115 // Check for intersection between the parag (yp -- yp+hp) and the break area (breakBegin -- breakEnd)
1116 if ( TQMAX( yp, breakBegin ) <= TQMIN( yp+hp, breakEnd ) )
1117 {
1118 if ( !parag || linesTogether ) // Paragraph-level breaking
1119 {
1120#ifdef DEBUG_FORMATVERTICALLY
1121 kdDebug(32002) << "checkVerticalBreak ADJUSTING yp=" << yp << " hp=" << hp
1122 << " breakEnd+2 [new value for yp]=" << breakEnd+2 << endl;
1123#endif
1124 yp = breakEnd + 1;
1125 return true;
1126 }
1127 else // Line-level breaking
1128 {
1129 TQMap<int, KoTextParagLineStart*>& lineStarts = parag->lineStartList();
1130#ifdef DEBUG_FORMATVERTICALLY
1131 kdDebug(32002) << "checkVerticalBreak parag " << parag->paragId()
1132 << ". lineStarts has " << lineStarts.count()
1133 << " items" << endl;
1134#endif
1135 int dy = 0;
1136 int line = 0;
1137 TQMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
1138 for ( ; it != lineStarts.end() ; ++it, ++line )
1139 {
1140 KoTextParagLineStart * ls = it.data();
1141 Q_ASSERT( ls );
1142 int y = parag->rect().y() + ls->y;
1143#ifdef DEBUG_FORMATVERTICALLY
1144 kdDebug(32002) << "checkVerticalBreak parag " << parag->paragId()
1145 << " line " << line << " ls->y=" << ls->y
1146 << " ls->h=" << ls->h << " y=" << y
1147 << " breakBegin=" << breakBegin
1148 << " breakEnd=" << breakEnd << endl;
1149#endif
1150 if ( !dy )
1151 {
1152 if ( TQMAX( y, breakBegin ) <= TQMIN( y + ls->h, breakEnd ) )
1153 {
1154 if ( line == 0 ) // First line ? It's like a paragraph breaking then
1155 {
1156#ifdef DEBUG_FORMATVERTICALLY
1157 kdDebug(32002) << "checkVerticalBreak parag " << parag->paragId()
1158 << " BREAKING first line -> parag break" << endl;
1159#endif
1160 yp = breakEnd + 1;
1161 return true;
1162 }
1163 dy = breakEnd + 1 - y;
1164 ls->y = breakEnd + 1 - parag->rect().y();
1165#ifdef DEBUG_FORMATVERTICALLY
1166 kdDebug(32002) << "checkVerticalBreak parag " << parag->paragId()
1167 << " BREAKING at line " << line << " dy=" << dy << " Setting ls->y to " << ls->y << ", y=" << breakEnd << endl;
1168#endif
1169 }
1170 }
1171 else
1172 {
1173 ls->y += dy;
1174#ifdef DEBUG_FORMATVERTICALLY
1175 if ( dy )
1176 kdDebug(32002) << " moving down to position ls->y=" << ls->y << endl;
1177#endif
1178 }
1179 }
1180 parag->setMovedDown( true );
1181 parag->setHeight( hp + dy );
1182#ifdef DEBUG_FORMATVERTICALLY
1183 kdDebug(32002) << "Paragraph height set to " << hp+dy << endl;
1184#endif
1185 hp += dy;
1186 return true;
1187 } // End of line-level breaking
1188 }
1189 return false;
1190}
1191
1192int KWTextFrameSet::formatVertically( KoTextParag * _parag, const TQRect& paragRect )
1193{
1194 // WARNING: in this whole method parag can be 0. See adjustFlow()
1195 KWTextParag *parag = static_cast<KWTextParag *>( _parag );
1196 if ( !m_doc->layoutViewMode()->shouldFormatVertically() )
1197 {
1198 return 0;
1199 }
1200
1201#ifdef DEBUG_FORMATVERTICALLY
1202 kdDebugBody(32002) << "KWTextFrameSet::formatVertically called for paragraph " << (parag?parag->paragId():-1) << endl;
1203#endif
1204
1205 int yp = paragRect.y();
1206 int hp = paragRect.height();
1207 int oldHeight = hp;
1208 int oldY = yp;
1209
1210 // This is called by KoTextFormatter to apply "vertical breaks".
1211 // End of frames/pages lead to those "vertical breaks".
1212 // What we do, is adjust the Y accordingly,
1213 // to implement page-break at the paragraph level and at the line level.
1214 // It's cumulative (the space of one break will be included in the further
1215 // paragraph's y position), which makes it easy to implement.
1216 // But don't forget that formatVertically is called twice for every parag, since the formatting
1217 // is re-done after moving down.
1218
1219 bool linesTogether = parag ? parag->linesTogether() : true;
1220 bool hardFrameBreak = parag ? parag->hardFrameBreakBefore() : false;
1221 if ( !hardFrameBreak && parag && parag->prev() )
1222 hardFrameBreak = static_cast<KWTextParag *>(parag->prev())->hardFrameBreakAfter();
1223
1224#ifdef DEBUG_FORMATVERTICALLY
1225 kdDebugBody(32002) << "KWTextFrameSet::formatVertically parag=" << parag
1226 << " linesTogether=" << linesTogether << " hardFrameBreak=" << hardFrameBreak
1227 << " yp=" << yp
1228 << " hp=" << hp << endl;
1229#endif
1230
1231 int totalHeight = 0;
1232 TQPtrListIterator<KWFrame> frameIt( frameIterator() );
1233 for ( ; frameIt.current(); ++frameIt )
1234 {
1235 int frameHeight = kWordDocument()->ptToLayoutUnitPixY( frameIt.current()->innerHeight() );
1236 int bottom = totalHeight + frameHeight;
1237 // Only skip bottom of frame if there is a next one or if there'll be another one created.
1238 // ( Not for header/footer, for instance. )
1239 bool check = frameIt.atLast() && frameIt.current()->frameBehavior() == KWFrame::AutoCreateNewFrame;
1240 if ( !check )
1241 {
1242 // ## TODO optimize this [maybe we should simply start from the end in the main loop?]
1243 // Or cache the attribute ( e.g. "frame->hasCopy()" ).
1244 TQPtrListIterator<KWFrame> nextFrame( frameIt );
1245 while ( !check && !nextFrame.atLast() )
1246 {
1247 ++nextFrame;
1248 if ( !nextFrame.current()->isCopy() )
1249 check = true; // Found a frame after us that isn't a copy => we have somewhere for our overflow
1250 }
1251 }
1252
1253 if ( check )
1254 {
1255 if ( hardFrameBreak && yp > totalHeight && yp < bottom && !parag->wasMovedDown() )
1256 {
1257 // The paragraph wants a frame break before it, and is in the current frame
1258 // The last check is for whether we did the frame break already
1259 // (formatVertically is called twice for each paragraph, if a break was done)
1260 yp = bottom /*+ 2*/;
1261#ifdef DEBUG_FORMATVERTICALLY
1262 kdDebug(32002) << "KWTextFrameSet::formatVertically -> HARD FRAME BREAK" << endl;
1263 kdDebug(32002) << "KWTextFrameSet::formatVertically yp now " << yp << endl;
1264#endif
1265 break;
1266 }
1267
1268#ifdef DEBUG_FORMATVERTICALLY
1269 kdDebug(32002) << " formatVertically: frameHeight=" << frameHeight << " bottom=" << bottom << endl;
1270#endif
1271 // don't move down parags that have only one line and are bigger than the page (e.g. floating tables)
1272 if ( hp < frameHeight || ( parag && parag->lineStartList().count() > 1 ) )
1273 {
1274 // breakBegin==breakEnd==bottom, since the next frame's top is the same as bottom, in TQRT coords.
1275 (void) checkVerticalBreak( yp, hp, parag, linesTogether, bottom, bottom );
1276 // Some people write a single paragraph over 3 frames! So we have to keep looking, that's why we ignore the return value
1277 }
1278
1279 }
1280 if ( yp+hp < bottom )
1281 break; // we've been past the parag, so stop here
1282 totalHeight = bottom;
1283 }
1284
1285#ifdef DEBUG_FORMATVERTICALLY
1286 kdDebug(32002) << " formatVertically: now looking at RA_SKIP" << endl;
1287#endif
1288
1289
1290 // Another case for a vertical break is frames with the RA_SKIP flag
1291 // Currently looking at all frames on top of all of our frames... maybe optimize better
1292 frameIt.toFirst();
1293 for ( ; frameIt.current(); ++frameIt )
1294 {
1295 Q_ASSERT( frameIt.current()->frameStack() );
1296 TQValueList<KWFrame*> onTop = frameIt.current()->frameStack()->framesOnTop();
1297 for (TQValueListIterator<KWFrame*> fIt = onTop.begin(); fIt != onTop.end(); ++fIt )
1298 {
1299 if ( (*fIt)->runAround() == KWFrame::RA_SKIP )
1300 {
1301 KoRect rectOnTop = frameIt.current()->intersect( (*fIt)->runAroundRect() );
1302 TQPoint iTop, iBottom; // top and bottom in internal coordinates
1303 if ( documentToInternal( rectOnTop.topLeft(), iTop ) &&
1304 iTop.y() <= yp + hp &&
1305 documentToInternal( rectOnTop.bottomLeft(), iBottom ) &&
1306 checkVerticalBreak( yp, hp, parag, linesTogether,
1307 iTop.y(), iBottom.y() ) )
1308 {
1309 kdDebug(32002) << "KWTextFrameSet::formatVertically breaking around RA_SKIP frame yp="<<yp<<" hp=" << hp << endl;
1310 // We don't "break;" here because there could be another such frame below the first one
1311 // We assume that the frames on top are in order ( top to bottom ), btw.
1312 // They should be, since updateFrames reorders before updating frames-on-top
1313 }
1314 }
1315 }
1316 }
1317
1318 // And the last case for a vertical break is RA_BOUNDINGRECT frames that
1319 // leave no space by their side for any text (e.g. most tables)
1320 int breakBegin = 0;
1321 int breakEnd = 0;
1322 int reqMinWidth = parag ? parag->string()->at( 0 ).width : 0;
1323 getMargins( yp, hp, reqMinWidth, 0L, 0L, 0L, 0L, &breakBegin, &breakEnd, parag );
1324 if ( breakEnd )
1325 {
1326 kdDebug(32002) << "KWTextFrameSet("<<name()<<")::formatVertically no-space case. breakBegin=" << breakBegin
1327 << " breakEnd=" << breakEnd << " hp=" << hp << endl;
1328 Q_ASSERT( breakBegin <= breakEnd );
1329 if ( checkVerticalBreak( yp, hp, parag, linesTogether, breakBegin, breakEnd ) )
1330 ; //kdDebug(32002) << "checkVerticalBreak ok." << endl;
1331 else // shouldn't happen
1332 kdWarning(32002) << "checkVerticalBreak didn't find it" << endl;
1333 }
1334
1335 // ## TODO loop around those three methods until we don't move anymore ?
1336
1337 if ( parag )
1338 {
1339 if ( hp != oldHeight )
1340 parag->setHeight( hp );
1341 if ( yp != oldY ) {
1342 TQRect r = parag->rect();
1343 r.moveBy( 0, yp - oldY );
1344 parag->setRect( r );
1345 parag->setMovedDown( true );
1346 }
1347 }
1348#ifdef DEBUG_FORMATVERTICALLY
1349 kdDebug() << "KWTextFrameSet::formatVertically returning " << ( yp + hp ) - ( oldY + oldHeight ) << endl;
1350#endif
1351 return ( yp + hp ) - ( oldY + oldHeight );
1352}
1353
1354// adjustFlow is called e.g. to break the "top margin" of a paragraph.
1355// There is no parag pointer in that case.
1356int KWTextFrameSet::adjustFlow( int y, int w, int h )
1357{
1358 TQRect r( 0, y, w, h );
1359 return formatVertically( 0L, r );
1360}
1361
1363{
1364 // Fixing the parag rect for the formatting chars (CR and frame break).
1365 if ( parag && m_doc->viewFormattingChars() && parag->rect().width() < textDocument()->width() )
1366 {
1367 if ( parag->hardFrameBreakAfter() )
1368 {
1369 KoTextFormat * lastFormat = parag->at( parag->length() - 1 )->format();
1370 const TQFontMetrics& refFontMetrics = lastFormat->refFontMetrics();
1371 // keep in sync with KWTextFrameSet::formatVertically
1372 TQString str = i18n( "--- Frame Break ---" );
1373 int width = refFontMetrics.width( str );
1374 parag->setWidth( TQMIN( parag->rect().width() + width, textDocument()->width() ) );
1375 }
1376 else // default KoTextFormatter implementation
1377 parag->fixParagWidth( true );
1378 }
1379}
1380
1382{
1383 textDocument()->takeFlow();
1384 //kdDebug(32001) << "KWTextFrameSet::~KWTextFrameSet" << endl;
1385 m_doc = 0L;
1386 delete m_textobj;
1387}
1388
1389// This struct is used for sorting frames.
1390// Since pages are one below the other, simply sorting on (y, x) does what we want.
1391struct FrameStruct
1392{
1393 KWFrame * frame;
1394 bool operator < ( const FrameStruct & t ) const {
1395 return compare(frame, t.frame) < 0;
1396 }
1397 bool operator <= ( const FrameStruct & t ) const {
1398 return compare(frame, t.frame) <= 0;
1399 }
1400 bool operator > ( const FrameStruct & t ) const {
1401 return compare(frame, t.frame) > 0;
1402 }
1403
1404 /*
1405 the sorting of all frames in the same frameset is done as all sorting
1406 based on a simple frameOne > frameTwo question.
1407 Frame frameOne is greater then frameTwo if the center point lies more down then (the whole of)
1408 frame frameTwo. When they are equal, the X position is considered. */
1409 int compare (const KWFrame *frameOne, const KWFrame *frameTwo) const {
1410 // The first criteria is the page number though!
1411 int pageOne = frameOne->pageNumber();
1412 int pageTwo = frameTwo->pageNumber();
1413 if( (pageOne == -1) ^ (pageTwo == -1)) {
1414 if( pageOne == -1 )
1415 return 5; // undefined is higher than defined.
1416 return -5;
1417 }
1418 if ( pageOne > pageTwo ) return 4; // frameOne > frameTwo
1419 if ( pageOne < pageTwo ) return -4; // frameOne < frameTwo
1420
1421 double centerX = frameOne->left() + (frameOne->width() /2);
1422 // reverse the return values of the next two for RTL
1423 if ( centerX > frameTwo->right()) return 3; // frameOne > frameTwo
1424 if ( centerX < frameTwo->left()) return -3; // frameOne < frameTwo
1425
1426 // check the Y position. Y is greater only when it is below the other frame.
1427 double centerY = frameOne->top() + (frameOne->height() /2);
1428 if ( centerY > frameTwo->bottom() ) return 2; // frameOne > frameTwo
1429 if ( centerY < frameTwo->top() ) return -2; // frameOne < frameTwo
1430
1431 // the center of frameOne lies inside frameTwo. Lets check the topleft pos.
1432 if (frameOne->top() > frameTwo->top()) return 1;
1433 return -1;
1434 }
1435};
1436
1438{
1439 // Not visible ? Don't bother then.
1440 if ( !isVisible() ) {
1441 //kdDebug(32002) << "KWTextFrameSet::updateFrames " << name() << " not visible" << endl;
1442 m_textobj->setVisible(false);
1443 return;
1444 }
1445 m_textobj->setVisible(true);
1446
1447 //kdDebug(32002) << "KWTextFrameSet::updateFrames " << name() << " frame-count=" << m_frames.count() << endl;
1448
1449 // Sort frames of this frameset on (y coord, x coord)
1450 // Adjustment on 20-Jun-2002 which does not change the itent of this but moves the
1451 // sorting from top-left of frame to the whole frame area. (TZ)
1452
1453 TQValueList<FrameStruct> sortedFrames;
1454
1455 int width = 0;
1456 TQPtrListIterator<KWFrame> frameIter( frameIterator() );
1457 for ( ; frameIter.current(); ++frameIter )
1458 {
1459 // Calculate max width while we're at it
1460 //kdDebug(32002) << "KWTextFrameSet::updateFrames frame " << *frameIter.current() << " innerWidth=" << frameIter.current()->innerWidth() << "pt" << endl;
1461 width = TQMAX( width, m_doc->ptToLayoutUnitPixX( frameIter.current()->innerWidth()));
1462 if ( flags & SortFrames )
1463 {
1464 FrameStruct str;
1465 str.frame = frameIter.current();
1466 sortedFrames.append( str );
1467 }
1468 }
1469 if ( width != textDocument()->width() )
1470 {
1471 //kdDebug(32002) << "KWTextFrameSet::updateFrames setWidth " << width << " LU pixels." << endl;
1472 //textDocument()->setMinimumWidth( -1, 0 );
1473 textDocument()->setWidth( width + 1 ); // TQRect semantics problem (#32866)
1474 } //else kdDebug(32002) << "KWTextFrameSet::updateFrames width already " << width << " LU pixels." << endl;
1475
1476 if ( flags & SortFrames )
1477 {
1478 qHeapSort( sortedFrames );
1479
1480 // Re-fill the frames list with the frames in the right order
1481 m_frames.setAutoDelete( false );
1482 m_frames.clear();
1483
1484 TQValueList<FrameStruct>::Iterator it = sortedFrames.begin();
1485 for ( ; it != sortedFrames.end() ; ++it )
1486 m_frames.append( (*it).frame );
1487 }
1488
1489 double availHeight = 0;
1490 double internalYpt = 0;
1491 double lastRealFrameHeight = 0;
1492 bool firstFrame = true;
1493
1494 TQPtrListIterator<KWFrame> frameIt( m_frames );
1495 for ( ; frameIt.current(); ++frameIt )
1496 {
1497 KWFrame* theFrame = frameIt.current();
1498
1499 if ( !theFrame->isCopy() )
1500 internalYpt += lastRealFrameHeight;
1501
1502 theFrame->setInternalY( internalYpt );
1503
1504 // Update availHeight with the internal height of this frame - unless it's a copy
1505 if ( !theFrame->isCopy() || firstFrame )
1506 {
1507 lastRealFrameHeight = theFrame->innerHeight();
1508 availHeight += lastRealFrameHeight;
1509 }
1510 firstFrame = false;
1511 }
1512
1513 m_textobj->setAvailableHeight( m_doc->ptToLayoutUnitPixY( availHeight ) );
1514 //kdDebug(32002) << this << " (" << name() << ") KWTextFrameSet::updateFrames availHeight=" << availHeight
1515 // << " (LU: " << m_doc->ptToLayoutUnitPixY( availHeight ) << ")" << endl;
1516 m_frames.setAutoDelete( true );
1517
1518 KWFrameSet::updateFrames( flags );
1519}
1520
1522{
1523 return m_textobj->availableHeight();
1524}
1525
1526KWFrame * KWTextFrameSet::internalToDocument( const KoPoint &relPoint, KoPoint &dPoint ) const
1527{
1528#ifdef DEBUG_ITD
1529 kdDebug() << name() << " ITD called for relPoint=" << relPoint.x() << "," << relPoint.y() << endl;
1530#endif
1531 if ( !m_doc->layoutViewMode()->hasFrames() ) { // text viewmode
1532 dPoint = relPoint;
1533 return m_frames.getFirst();
1534 }
1535 // This does a binary search in the m_framesInPage array, with internalY as criteria
1536 // We only look at the first frame of each page. Refining is done later on.
1537 Q_ASSERT( !m_framesInPage.isEmpty() );
1538 int len = m_framesInPage.count();
1539 int n1 = 0;
1540 int n2 = len - 1;
1541 double internalY = 0.0;
1542 int mid = 0;
1543 bool found = false;
1544 while ( n1 <= n2 ) {
1545 double res;
1546 mid = (n1 + n2)/2;
1547#ifdef DEBUG_ITD
1548 kdDebug() << "ITD: begin. mid=" << mid << endl;
1549#endif
1550 Q_ASSERT( m_framesInPage[mid] ); // We have no null items
1551 if ( m_framesInPage[mid]->isEmpty() )
1552 res = -1;
1553 else
1554 {
1555 KWFrame * theFrame = m_framesInPage[mid]->first();
1556 internalY = theFrame->internalY();
1557#ifdef DEBUG_ITD
1558 kdDebug() << "ITD: relPoint.y=" << relPoint.y() << " internalY=" << internalY << endl;
1559#endif
1560 res = relPoint.y() - internalY;
1561#ifdef DEBUG_ITD
1562 kdDebug() << "ITD: res=" << res << endl;
1563#endif
1564 // Anything between this internalY (top) and internalY+height (bottom) is fine
1565 // (Using the next page's first frame's internalY only works if there is a frame on the next page)
1566 if ( res >= 0 )
1567 {
1568 double height = theFrame->innerHeight();
1569#ifdef DEBUG_ITD
1570 kdDebug() << "ITD: height=" << height << " -> the bottom is at " << internalY+height << endl;
1571#endif
1572 if ( relPoint.y() < internalY + height )
1573 {
1574#ifdef DEBUG_ITD
1575 kdDebug() << "ITD: found a match " << mid << endl;
1576#endif
1577 found = true;
1578 break;
1579 }
1580 }
1581 }
1582 // res == 0 can't happen in theory, but in practice it happens when a frame has a height of 0
1583 // (e.g. newly imported table without correct row heights)
1584 if ( res < 0 )
1585 n2 = mid - 1;
1586 else // if ( res >= 0 )
1587 n1 = mid + 1;
1588#ifdef DEBUG_ITD
1589 kdDebug() << "ITD: End of loop. n1=" << n1 << " n2=" << n2 << endl;
1590#endif
1591 }
1592 if ( !found )
1593 {
1594 // Not found (n2 < n1)
1595 // We might have missed the frame because n2 has many frames
1596 // (and we only looked at the first one).
1597 mid = n2;
1598#ifdef DEBUG_ITD
1599 kdDebug() << "ITD: Setting mid to n2=" << mid << endl;
1600#endif
1601 if ( mid < 0 )
1602 {
1603#ifdef DEBUG_ITD
1604 kdDebug(32002) << "KWTextFrameSet::internalToDocument " << relPoint.x() << "," << relPoint.y()
1605 << " before any frame of " << (void*)this << endl;
1606#endif
1607 dPoint = relPoint; // "bah", I said above :)
1608 return 0L;
1609 }
1610 }
1611 // search to first of equal items
1612 // This happens with copied frames, which have the same internalY
1613 int result = mid;
1614 while ( mid - 1 >= 0 )
1615 {
1616 mid--;
1617 if ( !m_framesInPage[mid]->isEmpty() )
1618 {
1619 KWFrame * theFrame = m_framesInPage[mid]->first();
1620#ifdef DEBUG_ITD
1621 kdDebug() << "KWTextFrameSet::internalToDocument going back to page " << mid << " - frame: " << theFrame->internalY() << endl;
1622#endif
1623 if ( theFrame->internalY() == internalY ) // same internalY as the frame we found before
1624 result = mid;
1625 else
1626 break;
1627 }
1628 }
1629
1630 // Now iterate over the frames in page 'result' and find the right one
1631 TQPtrListIterator<KWFrame> frameIt( *m_framesInPage[result] );
1632 for ( ; frameIt.current(); ++frameIt )
1633 {
1634 KWFrame *theFrame = frameIt.current();
1635 KoRect relRect( 0, theFrame->internalY(), theFrame->innerWidth(), theFrame->innerHeight() );
1636#ifdef DEBUG_ITD
1637 kdDebug() << "KWTextFrameSet::internalToDocument frame's relative rect:" << relRect << endl;
1638#endif
1639 if ( relRect.contains( relPoint ) ) // both relRect and relPoint are in "relative coordinates"
1640 {
1641 dPoint = internalToDocumentKnowingFrame( relPoint, theFrame );
1642 return theFrame;
1643 }
1644 }
1645#ifdef DEBUG_ITD
1646 kdDebug(32002) << "KWTextFrameSet::internalToDocument " << relPoint.x() << "," << relPoint.y()
1647 << " not in any frame of " << (void*)this << " (looked on page " << result << ")" << endl;
1648#endif
1649 dPoint = relPoint; // bah again
1650 return 0L;
1651}
1652
1653// same but with iPoint in LU
1654KWFrame * KWTextFrameSet::internalToDocument( const TQPoint &iPoint, KoPoint &dPoint ) const
1655{
1656 KoPoint relPoint = m_doc->layoutUnitPtToPt( m_doc->pixelToPt( iPoint ) );
1657 return internalToDocument( relPoint, dPoint );
1658}
1659
1660#ifndef NDEBUG
1661void KWTextFrameSet::printDebug()
1662{
1663 KWFrameSet::printDebug();
1664 if ( !isDeleted() )
1665 {
1666 kdDebug() << "KoTextDocument width = " << textDocument()->width() << " height = " << textDocument()->height() << endl;
1667 }
1668
1669 TQPtrListIterator<KoTextCustomItem> cit( textDocument()->allCustomItems() );
1670 for ( ; cit.current() ; ++cit )
1671 {
1672 KWAnchor *anc = dynamic_cast<KWAnchor *>( cit.current() );
1673 if (anc)
1674 kdDebug() << "Inline framesets: " << anc->frameSet()->name() << endl;
1675 }
1676}
1677#endif
1678
1679TQDomElement KWTextFrameSet::saveInternal( TQDomElement &parentElem, bool saveFrames, bool saveAnchorsFramesets )
1680{
1681 if ( m_frames.isEmpty() ) // Deleted frameset -> don't save
1682 return TQDomElement();
1683
1684 TQDomElement framesetElem = parentElem.ownerDocument().createElement( "FRAMESET" );
1685 parentElem.appendChild( framesetElem );
1686
1687 if ( m_groupmanager ) {
1688 framesetElem.setAttribute( "grpMgr", m_groupmanager->name() );
1689
1691 framesetElem.setAttribute( "row", cell->firstRow() );
1692 framesetElem.setAttribute( "col", cell->firstColumn() );
1693 framesetElem.setAttribute( "rows", cell->rowSpan() );
1694 framesetElem.setAttribute( "cols", cell->columnSpan() );
1695 }
1696 if ( protectContent() )
1697 framesetElem.setAttribute( "protectContent", static_cast<int>(protectContent()));
1698
1699 KWFrameSet::saveCommon( framesetElem, saveFrames );
1700
1701 // Save paragraphs
1702 KWTextParag *start = static_cast<KWTextParag *>( textDocument()->firstParag() );
1703 while ( start ) {
1704 start->save( framesetElem, saveAnchorsFramesets );
1705 start = static_cast<KWTextParag *>( start->next() );
1706 }
1707
1708 return framesetElem;
1709}
1710
1711KWFrame* KWTextFrameSet::loadOasisTextFrame( const TQDomElement& frameTag, const TQDomElement &tag, KoOasisContext& context )
1712{
1713 context.styleStack().save();
1714 context.fillStyleStack( frameTag, KoXmlNS::draw, "style-name", "graphic" ); // get the style for the graphics element
1715
1716 KWFrame* frame = loadOasisFrame( frameTag, context );
1717
1718 // Load minimum height - only available for text-box
1719 bool hasMinHeight = tag.hasAttributeNS( KoXmlNS::fo, "min-height" );
1720 if ( hasMinHeight ) {
1721 double height = KoUnit::parseValue( tag.attributeNS( KoXmlNS::fo, "min-height", TQString() ) );
1722 frame->setMinimumFrameHeight( height );
1723 if ( height > frame->height() || !tag.hasAttributeNS( KoXmlNS::fo, "height" ) )
1724 frame->setHeight( height );
1725 }
1726
1727 // Load overflow behavior (OASIS 14.27.27, not in OO-1.1 DTD). This is here since it's only for text framesets.
1728 const TQString overflowBehavior = context.styleStack().attributeNS( KoXmlNS::style, "overflow-behavior" );
1729 if ( frame->minimumFrameHeight() > 0 )
1730 frame->setFrameBehavior( KWFrame::AutoExtendFrame );
1731 else if ( overflowBehavior == "auto-create-new-frame" )
1732 {
1733 frame->setFrameBehavior( KWFrame::AutoCreateNewFrame );
1734 frame->setNewFrameBehavior( KWFrame::Reconnect ); // anything else doesn't make sense
1735 }
1736 else if ( overflowBehavior.isEmpty() || overflowBehavior == "clip" )
1737 frame->setFrameBehavior( KWFrame::Ignore );
1738 else
1739 kdWarning(32001) << "Unknown value for style:overflow-behavior: " << overflowBehavior << endl;
1740
1741 context.styleStack().restore();
1742
1743 return frame;
1744}
1745
1746void KWTextFrameSet::loadOasisContent( const TQDomElement &bodyElem, KoOasisContext& context )
1747{
1748 return m_textobj->loadOasisContent( bodyElem, context, m_doc->styleCollection() );
1749}
1750
1751KWFrame* KWTextFrameSet::loadOasis( const TQDomElement& frameTag, const TQDomElement &tag, KoOasisContext& context )
1752{
1753 KWFrame* frame = loadOasisTextFrame( frameTag, tag, context );
1754 loadOasisContent( tag, context );
1755 return frame;
1756}
1757
1758static void finishTOC( KoXmlWriter& writer )
1759{
1760 writer.endElement(); // text:table-of-content
1761 writer.endElement(); // text:index-body
1762}
1763
1764void KWTextFrameSet::saveOasisContent( KoXmlWriter& writer, KoSavingContext& context ) const
1765{
1766 // TODO save protectContent
1767
1768 TQMap<const KoTextParag*, KoTextBookmarkList> bookmarksPerParagraph;
1769 if ( m_doc->bookmarkList() )
1770 bookmarksPerParagraph = m_doc->bookmarkList()->bookmarksPerParagraph();
1771
1772 // Basically just call saveOasis on every paragraph.
1773 // But we do table-of-contents-handling (for kword) in addition,
1774 // as well as bookmarks.
1775 KoTextParag* parag = textDocument()->firstParag();
1776 bool inTOC = false;
1777 while ( parag ) {
1778 bool tocParag = parag->partOfTableOfContents();
1779 if ( tocParag != inTOC ) {
1780 if ( tocParag ) { // first TOC paragraph
1781 writer.startElement( "text:table-of-content" );
1782 writer.addAttribute( "text:name", "Table Of Contents" );
1783 writer.addAttribute( "text:protected", "false" ); // true by default in OO, but we don't support that yet anyway
1784 writer.startElement( "text:table-of-content-source" );
1785 // TODO writer.addAttribute( "text:outline-level", ... );
1786 // TODO for each level writer.startElement( "text:table-of-content-entry-template" );
1787 // TODO writer.endElement(); // text:table-of-content-entry-template
1788 writer.endElement(); // text:table-of-content-source
1789 writer.startElement( "text:index-body" );
1790 writer.startElement( "text:index-title" );
1791 writer.addAttribute( "text:name", "Table Of Contents Heading" );
1792 } else {
1793 finishTOC( writer );
1794 }
1795 }
1796
1797
1798 KoSavingContext::BookmarkPositions bookmarkStarts, bookmarkEnds;
1799 TQMap<const KoTextParag*, KoTextBookmarkList>::const_iterator bkit = bookmarksPerParagraph.find( parag );
1800 if ( bkit != bookmarksPerParagraph.end() ) {
1801 // Massage a bit the bookmarks data; KoTextParag wants it ordered by position, for speed.
1802 const KoTextBookmarkList& bookmarks = *bkit;
1803 for ( KoTextBookmarkList::const_iterator it = bookmarks.begin(); it != bookmarks.end(); ++it )
1804 {
1805 const KoTextBookmark& bk = *it;
1806 if ( bk.startParag() == parag )
1807 bookmarkStarts.append( KoSavingContext::BookmarkPosition(
1808 bk.bookmarkName(), bk.bookmarkStartIndex(),
1809 bk.isSimple() ) );
1810 if ( bk.endParag() == parag && !bk.isSimple() )
1811 bookmarkEnds.append( KoSavingContext::BookmarkPosition( bk.bookmarkName(),
1812 bk.bookmarkEndIndex(), false ) );
1813 }
1814 qHeapSort( bookmarkStarts );
1815 qHeapSort( bookmarkEnds );
1816 }
1817 // should be done in all cases, even if both lists are empty
1818 context.setBookmarkPositions( bookmarkStarts, bookmarkEnds );
1819
1820 // Save the whole parag, without the trailing space.
1821 parag->saveOasis( writer, context, 0, parag->lastCharPos() );
1822
1823 if ( tocParag && !inTOC )
1824 writer.endElement(); // text:index-title
1825 inTOC = tocParag;
1826
1827 parag = parag->next();
1828 }
1829 if ( inTOC )
1830 finishTOC( writer );
1831}
1832
1833void KWTextFrameSet::saveOasis( KoXmlWriter& writer, KoSavingContext& context, bool saveFrames ) const
1834{
1835 // Save first frame with the whole contents
1836 KWFrame* frame = m_frames.getFirst();
1837 TQString lastFrameName = name();
1838 frame->startOasisFrame( writer, context.mainStyles(), lastFrameName );
1839
1840 TQString nextFrameName = name() + "-";
1841
1842 writer.startElement( "draw:text-box" );
1843 if ( frame->frameBehavior() == KWFrame::AutoExtendFrame )
1844 writer.addAttributePt( "fo:min-height", frame->minimumFrameHeight() );
1845 if ( m_frames.count() > 1 && saveFrames )
1846 writer.addAttribute( "draw:chain-next-name", nextFrameName + "2" );
1847 saveOasisContent( writer, context );
1848 writer.endElement(); // draw:text-box
1849 writer.endElement(); // draw:frame
1850
1851 // Save other frames using chaining
1852 if ( saveFrames ) // false when called from KWDocument::saveSelectedFrames
1853 {
1854 int frameNumber = 2;
1855 TQPtrListIterator<KWFrame> frameIter( frameIterator() );
1856 ++frameIter; // skip first frame, already saved
1857 for ( ; frameIter.current(); ++frameIter, ++frameNumber )
1858 {
1859 const TQString frameName = nextFrameName + TQString::number( frameNumber );
1860 frameIter.current()->startOasisFrame( writer, context.mainStyles(), frameName, lastFrameName );
1861 lastFrameName = frameName; // this is used for copy-frames
1862 writer.startElement( "draw:text-box" );
1863 if ( frame->frameBehavior() == KWFrame::AutoExtendFrame )
1864 writer.addAttributePt( "fo:min-height", frame->minimumFrameHeight() );
1865 if ( frameNumber < (int)m_frames.count() )
1866 writer.addAttribute( "draw:chain-next-name", nextFrameName + TQString::number( frameNumber+1 ) );
1867 // No contents. Well, OOo saves an empty paragraph, but I'd say that's wrong.
1868 writer.endElement();
1869 writer.endElement(); // draw:frame
1870 }
1871 }
1872}
1873
1874void KWTextFrameSet::load( TQDomElement &attributes, bool loadFrames )
1875{
1876 KWFrameSet::load( attributes, loadFrames );
1877 if ( attributes.hasAttribute( "protectContent"))
1878 setProtectContent((bool)attributes.attribute( "protectContent" ).toInt());
1879
1880 textDocument()->clear(false); // Get rid of dummy paragraph (and more if any)
1881 m_textobj->setLastFormattedParag( 0L ); // no more parags, avoid UMR in next setLastFormattedParag call
1882 KWTextParag *lastParagraph = 0L;
1883
1884 // <PARAGRAPH>
1885 TQDomElement paragraph = attributes.firstChild().toElement();
1886 for ( ; !paragraph.isNull() ; paragraph = paragraph.nextSibling().toElement() )
1887 {
1888 if ( paragraph.tagName() == "PARAGRAPH" )
1889 {
1890 KWTextParag *parag = new KWTextParag( textDocument(), lastParagraph );
1891 parag->load( paragraph );
1892 if ( !lastParagraph ) // First parag
1893 textDocument()->setFirstParag( parag );
1894 lastParagraph = parag;
1895 m_doc->progressItemLoaded();
1896 }
1897 }
1898
1899 if ( !lastParagraph ) // We created no paragraph
1900 {
1901 // Create an empty one, then. See KWTextDocument ctor.
1902 textDocument()->clear( true );
1903 static_cast<KWTextParag *>( textDocument()->firstParag() )->setStyle( m_doc->styleCollection()->findStyle( "Standard" ) );
1904 }
1905 else
1906 textDocument()->setLastParag( lastParagraph );
1907
1908 m_textobj->setLastFormattedParag( textDocument()->firstParag() );
1909 //kdDebug(32001) << "KWTextFrameSet::load done" << endl;
1910}
1911
1913{
1915 m_textobj->formatMore( 0 ); // just to get the timer going
1916 // This is important in case of auto-resized frames or table cells,
1917 // which come from an import filter, which didn't give them the right size.
1918 // However it shouldn't start _now_ (so we use 0), because e.g. main frames
1919 // don't have the right size yet (KWFrameLayout not done yet).
1920}
1921
1923{
1924 setInlineFramesVisible( visible );
1925 KWFrameSet::setVisible( visible );
1926}
1927
1929{
1930 TQPtrListIterator<KoTextCustomItem> cit( textDocument()->allCustomItems() );
1931 for ( ; cit.current() ; ++cit )
1932 {
1933 KWAnchor *anc = dynamic_cast<KWAnchor *>( cit.current() );
1934 if (anc)
1935 anc->frameSet()->setVisible( visible );
1936 }
1937}
1938
1939void KWTextFrameSet::addTextFrameSets( TQPtrList<KWTextFrameSet> & lst, bool onlyReadWrite )
1940{
1941 if (!textObject()->protectContent() || !onlyReadWrite)
1942 lst.append(this);
1943}
1944
1945void KWTextFrameSet::slotNewCommand( KCommand *cmd )
1946{
1947 m_doc->addCommand( cmd );
1948}
1949
1950void KWTextFrameSet::ensureFormatted( KoTextParag * parag, bool emitAfterFormatting )
1951{
1952 if (!isVisible())
1953 return;
1954 m_textobj->ensureFormatted( parag, emitAfterFormatting );
1955}
1956
1957bool KWTextFrameSet::slotAfterFormattingNeedMoreSpace( int bottom, KoTextParag *lastFormatted )
1958{
1959 int availHeight = availableHeight();
1960#ifdef DEBUG_FORMAT_MORE
1961 if(lastFormatted)
1962 kdDebug(32002) << "slotAfterFormatting We need more space in " << name()
1963 << " bottom=" << bottom + lastFormatted->rect().height()
1964 << " availHeight=" << availHeight << endl;
1965 else
1966 kdDebug(32002) << "slotAfterFormatting We need more space in " << name()
1967 << " bottom2=" << bottom << " availHeight=" << availHeight << endl;
1968#endif
1969 if ( m_frames.isEmpty() )
1970 {
1971 kdWarning(32002) << "slotAfterFormatting no more space, but no frame !" << endl;
1972 return true; // abort
1973 }
1974
1975 KWFrame::FrameBehavior frmBehavior = m_frames.last()->frameBehavior();
1976 if ( frmBehavior == KWFrame::AutoExtendFrame && isProtectSize())
1977 frmBehavior = KWFrame::Ignore;
1978 if ( frmBehavior == KWFrame::AutoCreateNewFrame )
1979 {
1980 KWFrame *theFrame = settingsFrame( m_frames.last() );
1981 double minHeight = s_minFrameHeight + theFrame->paddingTop() + theFrame->paddingBottom() + 5;
1982 if ( availHeight < minHeight )
1983 frmBehavior = KWFrame::Ignore;
1984 }
1985
1986 int difference = ( bottom + 2 ) - availHeight; // in layout unit pixels
1987#ifdef DEBUG_FORMAT_MORE
1988 kdDebug(32002) << "AutoExtendFrame bottom=" << bottom << " availHeight=" << availHeight
1989 << " => difference = " << difference << endl;
1990#endif
1991 if( lastFormatted && bottom + lastFormatted->rect().height() > availHeight ) {
1992#ifdef DEBUG_FORMAT_MORE
1993 kdDebug(32002) << " next will be off -> adding " << lastFormatted->rect().height() << endl;
1994#endif
1995 difference += lastFormatted->rect().height();
1996 }
1997
1998 switch ( frmBehavior ) {
1999 case KWFrame::AutoExtendFrame:
2000 {
2001 if(difference > 0) {
2002 // There's no point in resizing a copy, so go back to the last non-copy frame
2003 KWFrame *theFrame = settingsFrame( m_frames.last() );
2004 double wantedPosition = 0;
2005
2006 // Footers and footnotes go up
2007 if ( theFrame->frameSet()->isAFooter() || theFrame->frameSet()->isFootNote() )
2008 {
2009 // The Y position doesn't matter much, recalcFrames will reposition the frame
2010 // But the point of this code is set the correct height for the frame.
2011 double maxFooterSize = footerHeaderSizeMax( theFrame );
2012 double diffPt = m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
2013 wantedPosition = theFrame->top() - diffPt;
2014#ifdef DEBUG_FORMAT_MORE
2015 kdDebug() << " diffPt=" << diffPt << " -> wantedPosition=" << wantedPosition << endl;
2016#endif
2017 if ( wantedPosition < 0 )
2018 {
2019 m_textobj->setLastFormattedParag( 0 );
2020 return true; // abort
2021 }
2022
2023 if ( wantedPosition != theFrame->top() &&
2024 ( theFrame->frameSet()->isFootEndNote() ||
2025 theFrame->bottom() - maxFooterSize <= wantedPosition ) ) // Apply maxFooterSize for footers only
2026 {
2027 theFrame->setTop( wantedPosition );
2028#ifdef DEBUG_FORMAT_MORE
2029 kdDebug() << " ok: frame=" << *theFrame << " bottom=" << theFrame->bottom() << " height=" << theFrame->height() << endl;
2030#endif
2031 frameResized( theFrame, true );
2032 // We only got room for the next paragraph, we still have to keep the formatting going...
2033 return false; // keep going
2034 }
2035 kdDebug() << "slotAfterFormatting didn't manage to get more space for footer/footnote, aborting" << endl;
2036 return true; // abort
2037 }
2038 // Other frames are resized by the bottom
2039
2040 wantedPosition = m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) ) + theFrame->bottom();
2041 KWPage *page = m_doc->pageManager()->page( theFrame );
2042 double pageBottom;
2043 if(page)
2044 pageBottom = page->offsetInDocument() + page->height() - page->bottomMargin();
2045 else
2046 pageBottom = theFrame->bottom();
2047 double newPosition = TQMIN( wantedPosition, pageBottom );
2048 kdDebug(32002) << "wantedPosition=" << wantedPosition << " pageBottom=" << pageBottom
2049 << " -> newPosition=" << newPosition << endl;
2050
2051 if ( theFrame->frameSet()->isAHeader() )
2052 {
2053 double maxHeaderSize=footerHeaderSizeMax( theFrame );
2054 newPosition = TQMIN( newPosition, maxHeaderSize + theFrame->top() );
2055 }
2056
2057 newPosition = TQMAX( newPosition, theFrame->top() ); // avoid negative heights
2058 kdDebug(32002) << "newPosition=" << newPosition << endl;
2059
2060 bool resized = false;
2061 if(theFrame->frameSet()->groupmanager()) {
2062 KWTableFrameSet *table = theFrame->frameSet()->groupmanager();
2063#ifdef DEBUG_FORMAT_MORE
2064 kdDebug(32002) << "is table cell; just setting new minFrameHeight, to " << newPosition - theFrame->top() << endl;
2065#endif
2066 double newMinFrameHeight = newPosition - theFrame->top();
2067 resized = TQABS( newMinFrameHeight - theFrame->minimumFrameHeight() ) > 1E-10;
2068 if ( resized ) {
2069 theFrame->setMinimumFrameHeight( newMinFrameHeight );
2071 table->recalcCols(cell->firstColumn(), cell->firstRow());
2072 table->recalcRows(cell->firstColumn(), cell->firstRow());
2073
2074 if (! table->anchorFrameset() )
2075 ;// do nothing
2076 else if ( table->anchorFrameset() && table->anchorFrameset()->isAHeader() ) //we must recalculate the header frame size
2077 {
2078 theFrame = table->anchorFrameset()->frameIterator().getLast();
2079 theFrame->setBottom(newPosition);
2080 frameResized( theFrame, false );
2081 }
2082 else if ( table->anchorFrameset()->isAFooter() || table->anchorFrameset()->isFootNote() ) //we must recalculate the footer frame size
2083 {
2084 theFrame = table->anchorFrameset()->frameIterator().getLast();
2085 // The Y position doesn't matter much, recalcFrames will reposition the frame
2086 // But the point of this code is set the correct height for the frame.
2087 double maxFooterSize = footerHeaderSizeMax( theFrame );
2088 double diffPt = m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
2089 wantedPosition = theFrame->top() - diffPt;
2090 if ( wantedPosition < 0 )
2091 {
2092 m_textobj->setLastFormattedParag( 0 );
2093 return true; // abort
2094 }
2095
2096 if ( wantedPosition != theFrame->top() &&
2097 ( theFrame->frameSet()->isFootEndNote() ||
2098 theFrame->bottom() - maxFooterSize <= wantedPosition ) ) // Apply maxFooterSize for footers only
2099 {
2100 theFrame->setTop( wantedPosition );
2101 frameResized( theFrame, true );
2102 // We only got room for the next paragraph, we still have to keep the formatting going...
2103 }
2104 }
2105
2106 m_doc->delayedRepaintAllViews();
2107 }
2108 return true; // abort formatting for now (not sure this is correct)
2109 } else {
2110 resized = TQABS( theFrame->bottom() - newPosition ) > 1E-10;
2111#ifdef DEBUG_FORMAT_MORE
2112 kdDebug() << " bottom=" << theFrame->bottom() << " new position:" << newPosition << " wantedPosition=" << wantedPosition << " resized=" << resized << endl;
2113#endif
2114
2115 if ( resized )
2116 {
2117#ifdef DEBUG_FORMAT_MORE
2118 kdDebug(32002) << "slotAfterFormatting changing bottom from " << theFrame->bottom() << " to " << newPosition << endl;
2119#endif
2120 theFrame->setBottom(newPosition);
2121 frameResized( theFrame, false );
2122 }
2123 }
2124
2125 if(newPosition < wantedPosition &&
2126 (theFrame->newFrameBehavior() == KWFrame::Reconnect
2127 && !theFrame->frameSet()->isEndNote())) // end notes are handled by KWFrameLayout
2128 {
2129 wantedPosition = wantedPosition - newPosition + theFrame->top() + page->height();
2130#ifdef DEBUG_FORMAT_MORE
2131 kdDebug(32002) << "Not enough room in this page -> creating new one, with a reconnect frame" << endl;
2132 kdDebug(32002) << "new wantedPosition=" << wantedPosition << endl;
2133#endif
2134
2135 // fall through to AutoCreateNewFrame
2136 }
2137 else if(newPosition < wantedPosition && (theFrame->newFrameBehavior() == KWFrame::NoFollowup)) {
2138 if ( theFrame->frameSet()->isEndNote() ) // we'll need a new page
2139 m_doc->delayedRecalcFrames( theFrame->pageNumber() );
2140
2141 m_textobj->setLastFormattedParag( 0 );
2142 return true; // abort
2143 } else {
2144 if ( resized ) // we managed to resize a frame
2145 return false; // keep going
2146 return true; // abort
2147 }
2148 }
2149 }
2150
2151 case KWFrame::AutoCreateNewFrame:
2152 {
2153 // We need a new frame in this frameset.
2154 return createNewPageAndNewFrame( lastFormatted, difference );
2155 }
2156
2157 case KWFrame::Ignore:
2158#ifdef DEBUG_FORMAT_MORE
2159 kdDebug(32002) << "slotAfterFormatting frame behaviour is Ignore" << endl;
2160#endif
2161 m_textobj->setLastFormattedParag( 0 );
2162 return true; // abort
2163 }
2164 kdWarning() << "NEVERREACHED" << endl;
2165 // NEVERREACHED
2166 return true;
2167}
2168
2169void KWTextFrameSet::slotAfterFormattingTooMuchSpace( int bottom )
2170{
2171 int availHeight = availableHeight();
2172 // The + 2 here leaves 2 pixels below the last line. Without it we hit
2173 // the "break at end of frame" case in formatVertically (!!).
2174 int difference = availHeight - ( bottom + 2 );
2175#ifdef DEBUG_FORMAT_MORE
2176 kdDebug(32002) << "slotAfterFormatting less text than space (AutoExtendFrame). Frameset " << name() << " availHeight=" << availHeight << " bottom=" << bottom << " ->difference=" << difference << endl;
2177#endif
2178 // There's no point in resizing a copy, so go back to the last non-copy frame
2179 KWFrame *theFrame = settingsFrame( m_frames.last() );
2180#ifdef DEBUG_FORMAT_MORE
2181 kdDebug(32002) << " frame is " << *theFrame << " footer:" << ( theFrame->frameSet()->isAFooter() || theFrame->frameSet()->isFootEndNote() ) << endl;
2182#endif
2183 if ( theFrame->frameSet()->isAFooter() || theFrame->frameSet()->isFootEndNote() )
2184 {
2185 double wantedPosition = theFrame->top() + m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
2186 Q_ASSERT( wantedPosition < theFrame->bottom() );
2187 if ( wantedPosition != theFrame->top() )
2188 {
2189#ifdef DEBUG_FORMAT_MORE
2190 kdDebug() << " top= " << theFrame->top() << " setTop " << wantedPosition << endl;
2191#endif
2192 theFrame->setTop( wantedPosition );
2193#ifdef DEBUG_FORMAT_MORE
2194 kdDebug() << " -> the frame is now " << *theFrame << endl;
2195#endif
2196 frameResized( theFrame, true );
2197 }
2198 }
2199 else // header or other frame: resize bottom
2200 {
2201 double wantedPosition = theFrame->bottom() - m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
2202#ifdef DEBUG_FORMAT_MORE
2203 kdDebug() << "slotAfterFormatting wantedPosition=" << wantedPosition << " top+minheight=" << theFrame->top() + s_minFrameHeight << endl;
2204#endif
2205 wantedPosition = TQMAX( wantedPosition, theFrame->top() + s_minFrameHeight );
2206 if( theFrame->frameSet()->groupmanager() ) {
2207 if ( wantedPosition != theFrame->bottom()) {
2208 KWTableFrameSet *table = theFrame->frameSet()->groupmanager();
2209 // When a frame can be smaller we don't rescale it if it is a table, since
2210 // we don't have the full picture of the change.
2211 // We will set the minFrameHeight to the correct value and let the tables code
2212 // do the rescaling based on all the frames in the row. (see KWTableFrameSet::recalcRows())
2213 if(wantedPosition != theFrame->top() + theFrame->minimumFrameHeight()) {
2214 theFrame->setMinimumFrameHeight(wantedPosition - theFrame->top());
2215#ifdef DEBUG_FORMAT_MORE
2216 kdDebug(32002) << "is table cell; only setting new minFrameHeight to " << theFrame->minimumFrameHeight() << ", recalcrows will do the rest" << endl;
2217#endif
2219 table->recalcCols(cell->firstColumn(), cell->firstRow());
2220 table->recalcRows(cell->firstColumn(), cell->firstRow());
2221
2222 if (! table->anchorFrameset() )
2223 ;// do nothing
2224 else if ( table->anchorFrameset() && table->anchorFrameset()->isAHeader() )
2225 {
2226 theFrame = table->anchorFrameset()->frameIterator().getLast();
2227 theFrame->setBottom(wantedPosition);
2228 frameResized( theFrame, false );
2229 }
2230 else if ( table->anchorFrameset()->isAFooter() || table->anchorFrameset()->isFootEndNote() )
2231 {
2232 theFrame = table->anchorFrameset()->frameIterator().getLast();
2233 double wantedPosition = theFrame->top() + m_doc->layoutUnitPtToPt( m_doc->pixelYToPt( difference ) );
2234 Q_ASSERT( wantedPosition < theFrame->bottom() );
2235 if ( wantedPosition != theFrame->top() )
2236 {
2237 theFrame->setTop( wantedPosition );
2238 frameResized( theFrame, true );
2239 }
2240 }
2241 m_doc->delayedRepaintAllViews();
2242 }
2243 }
2244 } else {
2245 // Also apply the frame's minimum height
2246 wantedPosition = TQMAX( wantedPosition, theFrame->top() + theFrame->minimumFrameHeight() );
2247 if ( wantedPosition != theFrame->bottom()) {
2248#ifdef DEBUG_FORMAT_MORE
2249 kdDebug() << " the frame was " << *theFrame << endl;
2250 kdDebug() << "setBottom " << wantedPosition << endl;
2251#endif
2252 theFrame->setBottom( wantedPosition );
2253#ifdef DEBUG_FORMAT_MORE
2254 kdDebug() << " -> the frame is now " << *theFrame << endl;
2255#endif
2256 frameResized( theFrame, true );
2257 }
2258 }
2259 }
2260}
2261
2262void KWTextFrameSet::slotAfterFormatting( int bottom, KoTextParag *lastFormatted, bool* abort )
2263{
2264 int availHeight = availableHeight();
2265 if ( ( bottom > availHeight ) || // this parag is already off page
2266 ( lastFormatted && bottom + lastFormatted->rect().height() > availHeight ) ) // or next parag will be off page
2267 {
2268 *abort = slotAfterFormattingNeedMoreSpace( bottom, lastFormatted );
2269 }
2270 // Handle the case where the last frame is empty, so we may want to
2271 // remove the last page.
2272 else if ( m_frames.count() > 1 && !lastFormatted && frameSetInfo() == KWFrameSet::FI_BODY
2273 && bottom < availHeight - m_doc->ptToLayoutUnitPixY( m_frames.last()->innerHeight() ) )
2274 {
2275#ifdef DEBUG_FORMAT_MORE
2276 kdDebug(32002) << "slotAfterFormatting too much space (bottom=" << bottom << ", availHeight=" << availHeight << ") , trying to remove last frame" << endl;
2277#endif
2278 // Remove the empty last frame, if it's an auto-created one (e.g. a
2279 // continuation on the next page). Not when the user just created it!
2280 if(m_frames.last()->frameBehavior() == KWFrame::AutoExtendFrame
2281 && m_frames.last()->minimumFrameHeight() < 1E-10 ) { // i.e. equal to 0
2282 deleteFrame(m_frames.last(), true);
2283 m_doc->frameChanged( 0L );
2284 }
2285 if ( m_doc->processingType() == KWDocument::WP ) {
2286 bool removed = m_doc->tryRemovingPages();
2287 // Do all the recalc in one go. Speeds up deleting many pages.
2288 if ( removed )
2289 m_doc->afterRemovePages();
2290 }
2291 }
2292 // Handle the case where the last frame is in AutoExtendFrame mode
2293 // and there is less text than space
2294 else if ( !lastFormatted && bottom + 2 < availHeight &&
2295 (m_frames.last()->frameBehavior() == KWFrame::AutoExtendFrame&& !isProtectSize()) )
2296 {
2297 slotAfterFormattingTooMuchSpace( bottom );
2298 *abort = false;
2299 }
2300
2301 if ( m_doc->processingType() == KWDocument::WP
2302 && this == m_doc->frameSet( 0 ) )
2303 {
2304 if ( m_lastTextDocHeight != textDocument()->height() )
2305 {
2306 m_lastTextDocHeight = textDocument()->height();
2307 emit mainTextHeightChanged();
2308 }
2309 }
2310}
2311
2312// This is called when a text frame with behaviour AutoCreateNewFrame
2313// has more text than available frame height, so we need to create a new page
2314// so that a followup frame is created for this one
2315bool KWTextFrameSet::createNewPageAndNewFrame( KoTextParag* lastFormatted, int /*difference*/ )
2316{
2317 KWFrame* lastFrame = m_frames.last();
2318 // This is only going to help us if the new frame is reconnected. Otherwise bail out.
2319 if ( !lastFrame || lastFrame->newFrameBehavior() != KWFrame::Reconnect ) {
2320 kdDebug(32002) << name() << " : frame is AutoCreateNewFrame but not Reconnect !?!? Aborting." << endl;
2321 m_textobj->setLastFormattedParag( 0 );
2322 return true; // abort
2323 }
2324
2325//#ifdef DEBUG_FORMAT_MORE
2326 kdDebug(32002) << "createNewPageAndNewFrame creating new frame in frameset " << name() << endl;
2327//#endif
2328 uint oldCount = m_frames.count();
2329 int lastPageNumber = m_doc->pageManager()->lastPageNumber();
2330 kdDebug(32002) << " last frame=" << lastFrame << " pagenum=" << lastFrame->pageNumber() << " lastPageNumber=" << lastPageNumber << " m_frames count=" << oldCount << endl;
2331
2332 // First create a new page for it if necessary
2333 if ( lastFrame->pageNumber() == lastPageNumber )
2334 {
2335 // Let's first check if it will give us more space than we
2336 // already have left in this page. Otherwise we'll loop infinitely.
2337
2338 int heightWeWillGet = 0; // in LU
2339 if(isMainFrameset()) // is never added in the framesToCopyOnNewPage
2340 heightWeWillGet += m_doc->ptToLayoutUnitPixY( m_frames.last()->height() );
2341 else {
2342 TQPtrList<KWFrame> framesToCopy = m_doc->framesToCopyOnNewPage( lastPageNumber );
2343 TQPtrListIterator<KWFrame> frameIt( framesToCopy );
2344 for ( ; frameIt.current(); ++frameIt )
2345 if (frameIt.current()->frameSet() == this &&
2346 frameIt.current()->newFrameBehavior()==KWFrame::Reconnect)
2347 heightWeWillGet += m_doc->ptToLayoutUnitPixY( frameIt.current()->height() );
2348 }
2349
2350 // This logic doesn't applies to tables though, since they can be broken over multiple pages
2351 // TODO: lastFormatted->containsTable() or so (containsPageBreakableItem rather).
2352
2353 // "difference" doesn't apply if we're pasting multiple paragraphs.
2354 // We want to compare the height of one paragraph, not all the missing height.
2355 KoTextParag* parag = lastFormatted ? lastFormatted : textDocument()->lastParag();
2356 // In fact the parag height isn't the right thing to test for - we should check
2357 // for the highest character that remains to be positioned.
2358 // Testcase: many big inline pictures in one paragraph.
2359 int paragHeight = parag->rect().height();
2360 kdDebug(32002) << "height we will get in the new page:" << heightWeWillGet << " parag " << parag << " height:" << paragHeight << endl;
2361 if ( heightWeWillGet < paragHeight && !m_groupmanager )
2362 {
2363 kdDebug(32002) << "not enough height on the new page, not worth it" << endl;
2364 m_textobj->setLastFormattedParag( 0 );
2365 return true; // abort
2366 }
2367
2368 KWPage *page = m_doc->appendPage();
2369 if ( !m_doc->isLoading() )
2370 m_doc->afterInsertPage( page->pageNumber() );
2371 kdDebug(32002) << "now frames count=" << m_frames.count() << endl;
2372 }
2373
2374 // Maybe creating the new page created the frame in this frameset, then we're done
2375 // Otherwise let's create it ourselves:
2376 if ( m_frames.count() == oldCount )
2377 {
2378 Q_ASSERT( !isMainFrameset() ); // ouch, should have gone to the appendPage case above...
2379 // Otherwise, create a new frame on next page
2380 kdDebug(32002) << "createNewPageAndNewFrame creating frame on page " << lastFrame->pageNumber()+1 << endl;
2381 KWFrame *frm = lastFrame->getCopy();
2382 frm->moveBy( 0, m_doc->pageManager()->page(frm)->height() );
2383 addFrame( frm );
2384 }
2385
2386 updateFrames();
2387 Q_ASSERT(frame(0) && frame(0)->frameStack());
2388 frame(0)->frameStack()->update();
2391
2392 // Reformat the last paragraph. If it's over the two pages, it will need
2393 // the new page (e.g. for inline frames that need internalToDocument to work)
2394 if ( lastFormatted )
2395 lastFormatted = lastFormatted->prev();
2396 else
2397 lastFormatted = textDocument()->lastParag();
2398
2399 if ( lastFormatted )
2400 {
2401 m_textobj->setLastFormattedParag( lastFormatted );
2402 lastFormatted->invalidate( 0 );
2403 //This was a way to format the rest from here (recursively), but it didn't help much ensureCursorVisible()
2404 //So instead I fixed formatMore to return formatMore(2) itself.
2405 //m_textobj->formatMore( 2 );
2406 return false; // keep going
2407 }
2408 m_doc->delayedRepaintAllViews();
2409 return false; // all done
2410}
2411
2412double KWTextFrameSet::footNoteSize( KWFrame *theFrame )
2413{
2414 double tmp =0.0;
2415 int page = theFrame->pageNumber();
2416 TQPtrListIterator<KWFrameSet> fit = m_doc->framesetsIterator();
2417 for ( ; fit.current() ; ++fit )
2418 {
2419 if((fit.current()->isFootNote() || fit.current()->isEndNote()) &&
2420 fit.current()->isVisible())
2421 {
2422 KWFrame * frm=fit.current()->frame( 0 );
2423 if(frm->pageNumber()==page )
2424 tmp += frm->innerHeight()+m_doc->ptFootnoteBodySpacing();
2425 }
2426 }
2427 return tmp;
2428}
2429
2430
2431double KWTextFrameSet::footerHeaderSizeMax( KWFrame *theFrame )
2432{
2433 KWPage *page = m_doc->pageManager()->page(theFrame);
2434 Q_ASSERT( page );
2435 if ( !page )
2436 return 0;
2437 double tmp = page->height() - page->bottomMargin() - page->topMargin() - 40;//default min 40 for page size
2438 bool header=theFrame->frameSet()->isAHeader();
2439 if( header ? m_doc->isHeaderVisible():m_doc->isFooterVisible() )
2440 {
2441 TQPtrListIterator<KWFrameSet> fit = m_doc->framesetsIterator();
2442 for ( ; fit.current() ; ++fit )
2443 {
2444 bool state = header ? fit.current()->isAFooter():fit.current()->isAHeader();
2445 if(fit.current()->isVisible() && state)
2446 {
2447 KWFrame * frm=fit.current()->frame( 0 );
2448 if(frm->pageNumber()==page->pageNumber() )
2449 {
2450 return (tmp-frm->innerHeight()-footNoteSize( theFrame ));
2451 }
2452 }
2453 }
2454 }
2455 if (theFrame->frameSet()->isHeaderOrFooter())
2456 return (tmp-footNoteSize( theFrame ));
2457
2458 return tmp;
2459}
2460
2461void KWTextFrameSet::frameResized( KWFrame *theFrame, bool invalidateLayout )
2462{
2463 kdDebug(32002) << "KWTextFrameSet::frameResized " << theFrame << " " << *theFrame << " invalidateLayout=" << invalidateLayout << endl;
2464 if ( theFrame->height() < 0 )
2465 return; // safety!
2466
2467 KWFrameSet * fs = theFrame->frameSet();
2468 Q_ASSERT( fs == this );
2469 fs->updateFrames(); // update e.g. available height
2470 Q_ASSERT(frame(0) && frame(0)->frameStack());
2471 frame(0)->frameStack()->update();
2472
2473 theFrame->updateRulerHandles();
2474
2475 // Do a full KWFrameLayout if this will have influence on other frames, i.e.:
2476 // * if we resized the last main text frame (the one before the first endnote)
2477 // * if we resized an endnote
2478 // Delay it though, to get the full height first.
2479 if ( fs->isMainFrameset() || fs->isEndNote() )
2480 m_doc->delayedRecalcFrames( theFrame->pageNumber() );
2481 // * if we resized a header, footer, or footnote
2482 else if ( fs->frameSetInfo() != KWFrameSet::FI_BODY )
2483 m_doc->recalcFrames( theFrame->pageNumber(), -1 ); // warning this can delete theFrame!
2484
2485 // m_doc->frameChanged( theFrame );
2486 // Warning, can't call layout() (frameChanged calls it)
2487 // from here, since it calls formatMore() !
2488 if ( invalidateLayout )
2489 m_doc->invalidate(this);
2490
2491 // Can't repaint directly, we might be in a paint event already
2492 m_doc->delayedRepaintAllViews();
2493}
2494
2496{
2497 KoTextParag * lastParag = textDocument()->lastParag();
2498 // The problem is that if we format things here, and don't emit afterFormatting,
2499 // we won't resize autoresize frames properly etc. (e.g. endnotes)
2500 // Testcase for this problem: werner's footnote-1.doc
2501 //ensureFormatted( lastParag, false ); // maybe true here would do too? slow if maintextframeset though.
2502 if ( !lastParag->isValid() )
2503 return false; // we don't know yet
2504 int bottom = lastParag->rect().top() + lastParag->rect().height();
2505
2506 if ( theFrame->frameSet() == this ) // safety check
2507 {
2508 kdDebug() << "KWTextFrameSet::isFrameEmpty text bottom=(LU) " << bottom << " theFrame=" << theFrame << " " << *theFrame << " its internalY(LU)=" << m_doc->ptToLayoutUnitPixY( theFrame->internalY() ) << endl;
2509 return bottom < m_doc->ptToLayoutUnitPixY( theFrame->internalY() );
2510 }
2511
2512 kdWarning() << "KWTextFrameSet::isFrameEmpty called for frame " << theFrame << " which isn't a child of ours!" << endl;
2513 if ( theFrame->frameSet()!=0L && theFrame->frameSet()->name()!=0L)
2514 kdDebug() << "(this is " << name() << " and the frame belongs to " << theFrame->frameSet()->name() << ")" << endl;
2515 return false;
2516}
2517
2519{
2520 kdDebug() << "KWTextFrameSet(" << name() << ")::canRemovePage " << num << endl;
2521
2522 // No frame on that page ? ok for us then
2523 if ( num < m_firstPage || num >= (int)m_framesInPage.size() + m_firstPage ) {
2524 kdDebug() << "No frame on that page. Number of frames: " << frameCount() << endl;
2525 return true;
2526 }
2527
2528 TQPtrListIterator<KWFrame> frameIt( framesInPage( num ) );
2529 for ( ; frameIt.current(); ++frameIt )
2530 {
2531 KWFrame * theFrame = frameIt.current();
2532 kdDebug() << "canRemovePage: looking at " << theFrame << " pageNum=" << theFrame->pageNumber() << endl;
2533 Q_ASSERT( theFrame->pageNumber() == num );
2534 Q_ASSERT( theFrame->frameSet() == this );
2535 bool isEmpty = isFrameEmpty( theFrame );
2536 kdDebug() << "KWTextFrameSet(" << name() << ")::canRemovePage"
2537 << " found a frame on page " << num << " empty:" << isEmpty << endl;
2538 // Ok, so we have a frame on that page -> we can't remove it unless it's a copied frame OR it's empty
2539 bool isCopy = theFrame->isCopy() && frameIt.current() != m_frames.first();
2540 if ( !isCopy && !isEmpty )
2541 return false;
2542 }
2543 return true;
2544}
2545
2546void KWTextFrameSet::deleteFrame( unsigned int num, bool remove, bool recalc )
2547{
2548 KWFrame *frm = m_frames.at( num );
2549 kdDebug() << "KWTextFrameSet(" << name() << ")::deleteFrame " << frm << " (" << num << ")" << endl;
2550 if ( frm )
2551 emit frameDeleted( frm );
2552 KWFrameSet::deleteFrame( num, remove, recalc );
2553}
2554
2555void KWTextFrameSet::updateViewArea( TQWidget * w, KWViewMode* viewMode, const TQPoint & nPointBottom )
2556{
2557 if (!isVisible(viewMode))
2558 return;
2559 int ah = availableHeight(); // make sure that it's not -1
2560#ifdef DEBUG_VIEWAREA
2561 kdDebug(32002) << "KWTextFrameSet::updateViewArea " << (void*)w << " " << w->name()
2562 << " nPointBottom=" << nPointBottom.x() << "," << nPointBottom.y()
2563 << " availHeight=" << ah << " textDocument()->height()=" << textDocument()->height() << endl;
2564#endif
2565
2566 // Find last page that is visible
2567 int maxPage = m_doc->pageManager()->pageNumber(m_doc->unzoomItY( nPointBottom.y() ));
2568 int maxY = 0;
2569 if ( maxPage < m_firstPage || maxPage >= (int)m_framesInPage.size() + m_firstPage )
2570 maxY = ah;
2571 else
2572 {
2573 // Find frames on that page, and keep the max bottom, in internal coordinates
2574 TQPtrListIterator<KWFrame> frameIt( framesInPage( maxPage ) );
2575 for ( ; frameIt.current(); ++frameIt )
2576 {
2577 maxY = TQMAX( maxY, m_doc->ptToLayoutUnitPixY( frameIt.current()->internalY() + frameIt.current()->innerHeight() ) );
2578 }
2579 }
2580#ifdef DEBUG_VIEWAREA
2581 kdDebug(32002) << "KWTextFrameSet (" << name() << ")::updateViewArea maxY now " << maxY << endl;
2582#endif
2583 m_textobj->setViewArea( w, maxY );
2584 m_textobj->formatMore( 2 );
2585}
2586
2587KCommand * KWTextFrameSet::setPageBreakingCommand( KoTextCursor * cursor, int pageBreaking )
2588{
2589 if ( !textDocument()->hasSelection( KoTextDocument::Standard ) &&
2590 static_cast<KWTextParag *>(cursor->parag())->pageBreaking() == pageBreaking )
2591 return 0L; // No change needed.
2592
2593 m_textobj->emitHideCursor();
2594
2595 m_textobj->storeParagUndoRedoInfo( cursor );
2596
2597 if ( !textDocument()->hasSelection( KoTextDocument::Standard ) ) {
2598 KWTextParag *parag = static_cast<KWTextParag *>( cursor->parag() );
2599 parag->setPageBreaking( pageBreaking );
2600 m_textobj->setLastFormattedParag( cursor->parag() );
2601 }
2602 else
2603 {
2604 KoTextParag *start = textDocument()->selectionStart( KoTextDocument::Standard );
2605 KoTextParag *end = textDocument()->selectionEnd( KoTextDocument::Standard );
2606 m_textobj->setLastFormattedParag( start );
2607 for ( ; start && start != end->next() ; start = start->next() )
2608 static_cast<KWTextParag *>(start)->setPageBreaking( pageBreaking );
2609 }
2610
2611 m_textobj->formatMore( 2 );
2612 emit repaintChanged( this );
2613 KoTextObject::UndoRedoInfo & undoRedoInfo = m_textobj->undoRedoInfoStruct();
2614 undoRedoInfo.newParagLayout.pageBreaking = pageBreaking;
2615 KoTextParagCommand *cmd = new KoTextParagCommand(
2616 textDocument(), undoRedoInfo.id, undoRedoInfo.eid,
2617 undoRedoInfo.oldParagLayouts, undoRedoInfo.newParagLayout,
2618 KoParagLayout::PageBreaking );
2619 textDocument()->addCommand( cmd );
2620 undoRedoInfo.clear();
2621 m_textobj->emitShowCursor();
2622 m_textobj->emitUpdateUI( true );
2623 m_textobj->emitEnsureCursorVisible();
2624 // ## find a better name for the command
2625 return new KoTextCommand( m_textobj, /*cmd, */i18n("Change Paragraph Attribute") );
2626}
2627
2628KCommand * KWTextFrameSet::pasteOasis( KoTextCursor * cursor, const TQByteArray & data, bool removeSelected )
2629{
2630 if (protectContent() )
2631 return 0;
2632
2633 kdDebug(32001) << "KWTextFrameSet::pasteOasis data:" << data.size() << " bytes" << endl;
2634 KMacroCommand * macroCmd = new KMacroCommand( i18n("Paste") );
2635 if ( removeSelected && textDocument()->hasSelection( KoTextDocument::Standard ) )
2636 macroCmd->addCommand( m_textobj->removeSelectedTextCommand( cursor, KoTextDocument::Standard ) );
2637 m_textobj->emitHideCursor();
2638 m_textobj->setLastFormattedParag( cursor->parag()->prev() ?
2639 cursor->parag()->prev() : cursor->parag() );
2640
2641 KWOasisPasteCommand * cmd = new KWOasisPasteCommand( textDocument(), cursor->parag()->paragId(), cursor->index(), data );
2642 textDocument()->addCommand( cmd );
2643
2644 macroCmd->addCommand( new KoTextCommand( m_textobj, /*cmd, */TQString() ) );
2645
2646 *cursor = *( cmd->execute( cursor ) );
2647
2648 // not enough when pasting many pages. We need the cursor's parag to be formatted.
2649 //m_textobj->formatMore( 2 );
2650 ensureFormatted( cursor->parag() );
2651
2652 emit repaintChanged( this );
2653 m_textobj->emitEnsureCursorVisible();
2654 m_textobj->emitUpdateUI( true );
2655 m_textobj->emitShowCursor();
2656 m_textobj->selectionChangedNotify();
2657 return macroCmd;
2658}
2659
2660void KWTextFrameSet::insertTOC( KoTextCursor * cursor )
2661{
2662 m_textobj->emitHideCursor();
2663 KMacroCommand * macroCmd = new KMacroCommand( i18n("Insert Table of Contents") );
2664
2665 // Remove old TOC
2666
2667 KoTextCursor *cur= KWInsertTOCCommand::removeTOC( this, cursor, macroCmd );
2668
2669 // Insert new TOC
2670
2671 KoTextDocCommand * cmd = new KWInsertTOCCommand( this,cur ? cur->parag(): cursor->parag() );
2672 textDocument()->addCommand( cmd );
2673 macroCmd->addCommand( new KoTextCommand( m_textobj, TQString() ) );
2674 *cursor = *( cmd->execute( cursor ) );
2675
2676 m_textobj->setLastFormattedParag( textDocument()->firstParag() );
2677 m_textobj->formatMore( 2 );
2678 emit repaintChanged( this );
2679 m_textobj->emitEnsureCursorVisible();
2680 m_textobj->emitUpdateUI( true );
2681 m_textobj->emitShowCursor();
2682
2683 m_doc->addCommand( macroCmd );
2684}
2685
2686KNamedCommand* KWTextFrameSet::insertFrameBreakCommand( KoTextCursor *cursor )
2687{
2688 KMacroCommand* macroCmd = new KMacroCommand( TQString() );
2689 macroCmd->addCommand( m_textobj->insertParagraphCommand( cursor ) );
2690 KWTextParag *parag = static_cast<KWTextParag *>( cursor->parag() );
2691 if(parag->prev()) {
2692 parag=static_cast<KWTextParag *> (parag->prev());
2693 cursor->setParag( parag );
2694 cursor->setIndex( parag->length() - 1 );
2695 }
2696 macroCmd->addCommand( setPageBreakingCommand( cursor, parag->pageBreaking() | KoParagLayout::HardFrameBreakAfter ) );
2697 Q_ASSERT( parag->next() );
2698 if ( parag->next() ) {
2699 cursor->setParag( parag->next() );
2700 cursor->setIndex( 0 );
2701 }
2702 return macroCmd;
2703}
2704
2705void KWTextFrameSet::insertFrameBreak( KoTextCursor *cursor )
2706{
2707 clearUndoRedoInfo();
2708 m_textobj->emitHideCursor();
2709 KNamedCommand* cmd = insertFrameBreakCommand( cursor );
2710 cmd->setName( i18n( "Insert Break After Paragraph" ) );
2711 m_doc->addCommand( cmd );
2712
2713 m_textobj->setLastFormattedParag( cursor->parag() );
2714 m_textobj->formatMore( 2 );
2715 emit repaintChanged( this );
2716 m_textobj->emitEnsureCursorVisible();
2717 m_textobj->emitUpdateUI( true );
2718 m_textobj->emitShowCursor();
2719}
2720
2721TQRect KWTextFrameSet::paragRect( KoTextParag * parag ) const
2722{
2723 // ## Warning. Imagine a paragraph cut in two pieces (at the line-level),
2724 // between two columns. A single rect in internal coords, but two rects in
2725 // normal coords. TQRect( topLeft, bottomRight ) is just plain wrong.
2726 // Currently this method is only used for "ensure visible" so that's fine, but
2727 // we shouldn't use it for more precise stuff.
2728 KoPoint p;
2729 (void)internalToDocument( parag->rect().topLeft(), p );
2730 TQPoint topLeft = m_doc->zoomPoint( p );
2731 (void)internalToDocument( parag->rect().bottomRight(), p );
2732 TQPoint bottomRight = m_doc->zoomPoint( p );
2733 return TQRect( topLeft, bottomRight );
2734}
2735
2736void KWTextFrameSet::findPosition( const KoPoint &dPoint, KoTextParag * & parag, int & index )
2737{
2738 KoTextCursor cursor( textDocument() );
2739
2740 TQPoint iPoint;
2741 if ( documentToInternal( dPoint, iPoint ) )
2742 {
2743 cursor.place( iPoint, textDocument()->firstParag() );
2744 parag = cursor.parag();
2745 index = cursor.index();
2746 }
2747 else
2748 {
2749 // Not found, maybe under everything ?
2750 parag = textDocument()->lastParag();
2751 if ( parag )
2752 index = parag->length() - 1;
2753 }
2754}
2755
2756bool KWTextFrameSet::minMaxInternalOnPage( int pageNum, int& topLU, int& bottomLU ) const
2757{
2758 TQPtrListIterator<KWFrame> frameIt( framesInPage( pageNum ) );
2759 if ( !frameIt.current() )
2760 return false;
2761
2762 // Look at all frames in the page, and keep min and max "internalY" positions
2763 double topPt = frameIt.current()->internalY();
2764 double bottomPt = topPt + frameIt.current()->height();
2765
2766 for ( ; frameIt.current(); ++frameIt )
2767 {
2768 double y = frameIt.current()->internalY();
2769 topPt = TQMIN( topPt, y );
2770 bottomPt = TQMAX( bottomPt, y + frameIt.current()->height() );
2771 }
2772 // Convert to layout units
2773 topLU = m_doc->ptToLayoutUnitPixY( topPt );
2774 bottomLU = m_doc->ptToLayoutUnitPixY( bottomPt );
2775 return true;
2776}
2777
2778KoTextParag* KWTextFrameSet::paragAtLUPos( int yLU ) const
2779{
2780 KoTextParag* parag = textDocument()->firstParag();
2781 for ( ; parag ; parag = parag->next() )
2782 {
2783 if ( parag->rect().bottom() >= yLU )
2784 return parag;
2785 }
2786 return 0L;
2787}
2788
2789KCommand * KWTextFrameSet::deleteAnchoredFrame( KWAnchor * anchor )
2790{
2791 kdDebug() << "KWTextFrameSet::deleteAnchoredFrame anchor->index=" << anchor->index() << endl;
2792 Q_ASSERT( anchor );
2793 KoTextCursor c( textDocument() );
2794 c.setParag( anchor->paragraph() );
2795 c.setIndex( anchor->index() );
2796
2797 textDocument()->setSelectionStart( KoTextDocument::Temp, &c );
2798 c.setIndex( anchor->index() + 1 );
2799 textDocument()->setSelectionEnd( KoTextDocument::Temp, &c );
2800 KCommand *cmd = m_textobj->removeSelectedTextCommand( &c, KoTextDocument::Temp );
2801
2802 m_doc->repaintAllViews();
2803 return cmd;
2804}
2805
2807{
2808 return m_textobj->hasSelection();
2809}
2810
2812{
2813 return m_textobj->selectedText();
2814}
2815
2816TQString KWTextFrameSet::toPlainText() const
2817{
2818 return m_textobj->textDocument()->plainText();
2819}
2820
2821void KWTextFrameSet::highlightPortion( KoTextParag * parag, int index, int length, KWCanvas * canvas, bool repaint, KDialogBase* dialog )
2822{
2823 Q_ASSERT( isVisible() );
2824 Q_ASSERT( m_textobj->isVisible() );
2825 //kdDebug() << "highlighting in " << name() << " parag=" << parag->paragId() << " index=" << index << " repaint=" << repaint << endl;
2826 m_textobj->highlightPortion( parag, index, length, repaint );
2827 if ( repaint ) {
2828 // Position the cursor
2829 canvas->editTextFrameSet( this, parag, index );
2830 // Ensure text is fully visible
2831 TQRect expose = canvas->viewMode()->normalToView( paragRect( parag ) );
2832 canvas->ensureVisible( (expose.left()+expose.right()) / 2, // point = center of the rect
2833 (expose.top()+expose.bottom()) / 2,
2834 (expose.right()-expose.left()) / 2, // margin = half-width of the rect
2835 (expose.bottom()-expose.top()) / 2);
2836 if ( dialog ) {
2837 //kdDebug() << k_funcinfo << " dialog=" << dialog << " avoiding rect=" << expose << endl;
2838 TQRect globalRect( expose );
2839 globalRect.moveTopLeft( canvas->mapToGlobal( globalRect.topLeft() ) );
2840 KDialog::avoidArea( dialog, globalRect );
2841 }
2842 }
2843}
2844
2845void KWTextFrameSet::removeHighlight( bool repaint )
2846{
2847 m_textobj->removeHighlight( repaint );
2848}
2849
2850void KWTextFrameSet::clearUndoRedoInfo()
2851{
2852 m_textobj->clearUndoRedoInfo();
2853}
2854
2855void KWTextFrameSet::applyStyleChange( KoStyleChangeDefMap changed )
2856{
2857 m_textobj->applyStyleChange( changed );
2858}
2859
2860// KoTextFormatInterface methods
2862{
2863 return m_textobj->currentFormat();
2864}
2865
2866KCommand *KWTextFrameSet::setChangeCaseOfTextCommand(KoChangeCaseDia::TypeOfCase _type)
2867{
2868 KoTextDocument *textdoc = m_textobj->textDocument();
2869 textdoc->selectAll( KoTextDocument::Standard );
2870 KoTextCursor *cursor = new KoTextCursor( textDocument() );
2871 KCommand* cmd = m_textobj->changeCaseOfText(cursor, _type);
2872 textdoc->removeSelection( KoTextDocument::Standard );
2873 delete cursor;
2874 return cmd;
2875}
2876
2877
2878KCommand *KWTextFrameSet::setFormatCommand( const KoTextFormat * newFormat, int flags, bool zoomFont )
2879{
2880 m_textobj->textDocument()->selectAll( KoTextDocument::Temp );
2881 KCommand *cmd = m_textobj->setFormatCommand( 0L, 0L, newFormat, flags, zoomFont, KoTextDocument::Temp );
2882 m_textobj->textDocument()->removeSelection( KoTextDocument::Temp );
2883 return cmd;
2884}
2885
2886const KoParagLayout * KWTextFrameSet::currentParagLayoutFormat() const
2887{
2888 return m_textobj->currentParagLayoutFormat();
2889}
2890
2891bool KWTextFrameSet::rtl() const
2892{
2893 return m_textobj->rtl();
2894}
2895
2896
2897KCommand *KWTextFrameSet::setParagLayoutFormatCommand( KoParagLayout *newLayout,int flags, int marginIndex)
2898{
2899 return m_textobj->setParagLayoutFormatCommand(newLayout, flags, marginIndex);
2900}
2901
2902class KWFootNoteVarList : public TQPtrList< KWFootNoteVariable >
2903{
2904protected:
2905 virtual int compareItems(TQPtrCollection::Item a, TQPtrCollection::Item b)
2906 {
2909 if ( vara->paragraph() == varb->paragraph() ) {
2910 // index() is a bit slow. But this is only called when there are
2911 // two footnotes in the same paragraph.
2912 int indexa = vara->index();
2913 int indexb = varb->index();
2914 return indexa < indexb ? -1 : indexa == indexb ? 0 : 1;
2915 }
2916 if ( vara->paragraph()->paragId() < varb->paragraph()->paragId() )
2917 return -1;
2918 return 1;
2919 }
2920};
2921
2923{
2924 KWFootNoteVarList lst;
2925 TQPtrListIterator<KoTextCustomItem> cit( textDocument()->allCustomItems() );
2926 for ( ; cit.current() ; ++cit )
2927 {
2928 KWFootNoteVariable *fnv = dynamic_cast<KWFootNoteVariable *>( cit.current() );
2929 if (fnv && !fnv->isDeleted() && (fnv->frameSet() && !fnv->frameSet()->isDeleted()))
2930 lst.append( fnv );
2931 }
2932 lst.sort();
2933 short int footNoteVarNumber = 0; // absolute order number [internal, not saved nor displayed]
2934 short int endNoteVarNumber = 0;
2935 short int footNoteNumDisplay = 1; // the number being displayed
2936 short int endNoteNumDisplay = 1;
2937 bool needRepaint = false;
2938 TQPtrListIterator< KWFootNoteVariable > vit( lst );
2939
2940 //create a list with all manual footnotes numbers
2941 TQValueList<int> addedNums;
2942 for ( ; vit.current() ; ++vit )
2943 {
2944 KWFootNoteVariable* var = vit.current();
2945 if ( var->numberingType()==KWFootNoteVariable::Manual )
2946 {
2947 uint const num = var->text().toUInt();
2948 if ( num != 0 )
2949 addedNums.append( num );
2950 }
2951 }
2952
2953 for ( vit.toFirst() ; vit.current() ; )
2954 {
2955 KWFootNoteVariable* var = vit.current();
2956 bool endNote = var->noteType() == EndNote;
2957 short int & varNumber = endNote ? endNoteVarNumber : footNoteVarNumber;
2958 short int & numDisplay = endNote ? endNoteNumDisplay : footNoteNumDisplay;
2959 ++varNumber;
2960 bool changed = false;
2961 if ( varNumber != var->num() || var->numberingType()==KWFootNoteVariable::Manual )
2962 {
2963 changed = true;
2964 var->setNum( varNumber );
2965 }
2966 if ( var->numberingType()==KWFootNoteVariable::Auto )
2967 {
2968 if ( addedNums.contains( numDisplay ) != 0 ) // the automatic generated number should not be equal to a manual one
2969 {
2970 numDisplay++;
2971 continue; //try with the next number
2972 }
2973 if ( numDisplay != var->numDisplay() )
2974 {
2975 changed = true;
2976 var->setNumDisplay( numDisplay );
2977 }
2978 numDisplay++;
2979 }
2980 if ( changed )
2981 {
2982 if ( var->frameSet() ) //safety
2983 {
2984 TQString fsName = endNote ? i18n("Endnote %1") : i18n("Footnote %1");
2985 if ( var->numberingType()== KWFootNoteVariable::Manual)
2986 var->frameSet()->setName( m_doc->generateFramesetName(fsName));
2987 else
2988 var->frameSet()->setName( fsName.arg( var->text() ) );
2989 var->frameSet()->setCounterText( var->text() );
2990 }
2991 var->resize();
2992 var->paragraph()->invalidate(0);
2993 var->paragraph()->setChanged( true );
2994 needRepaint = true;
2995 }
2996 ++vit;
2997 }
2998 if ( needRepaint && repaint )
2999 m_doc->slotRepaintChanged( this );
3000}
3001
3002KoTextDocCommand *KWTextFrameSet::deleteTextCommand( KoTextDocument *textdoc, int id, int index, const TQMemArray<KoTextStringChar> & str, const CustomItemsMap & customItemsMap, const TQValueList<KoParagLayout> & oldParagLayouts )
3003{
3004 return new KWTextDeleteCommand( textdoc, id, index, str, customItemsMap, oldParagLayouts );
3005}
3006
3007TQByteArray KWTextFrameSet::sortText(SortType type) const
3008{
3009 const KoTextCursor c1 = textDocument()->selectionStartCursor(KoTextDocument::Standard );
3010 const KoTextCursor c2 = textDocument()->selectionEndCursor( KoTextDocument::Standard );
3011 if ( c1.parag() == c2.parag() )
3012 return TQByteArray();
3013 else
3014 {
3015 // ( paragraph text -> paragraph ) map. Note that this sorts on the key automatically.
3016 TQMap<TQString, const KoTextParag*> sortMap;
3017 sortMap.insert( c1.parag()->toString(0), c1.parag() );
3018
3019 const KoTextParag *p = c1.parag()->next();
3020 while ( p && p != c2.parag() ) {
3021 sortMap.insert( p->toString(0), p );
3022 p = p->next();
3023 }
3024 sortMap.insert( c2.parag()->toString(0), c2.parag());
3025
3026 typedef TQValueList<const KoTextParag *> ParagList;
3027 ParagList sortedParags = sortMap.values();
3028 if ( type == KW_SORTDECREASE )
3029 {
3030 // I could use an STL algorithm here, but only if TQt was compiled with STL support...
3031 ParagList newList;
3032 for ( ParagList::const_iterator it = sortedParags.begin(),
3033 end = sortedParags.end();
3034 it != end ; ++it ) {
3035 newList.prepend( *it );
3036 }
3037 sortedParags = newList;
3038 }
3039
3040 KWOasisSaver oasisSaver( m_doc );
3041 oasisSaver.saveParagraphs( sortedParags );
3042 if ( !oasisSaver.finish() )
3043 return TQByteArray();
3044 return oasisSaver.data();
3045 }
3046}
3047
3048// This is used when loading (KWTextDocument::loadOasisFootnote)
3049// and when inserting from the GUI (KWTextFrameSetEdit::insertFootNote),
3050// so don't add any 'repaint' or 'recalc' code here
3051KWFootNoteFrameSet * KWTextFrameSet::insertFootNote( NoteType noteType, KWFootNoteVariable::Numbering numType, const TQString &manualString )
3052{
3053 kdDebug() << "KWTextFrameSetEdit::insertFootNote " << endl;
3054 KWDocument * doc = m_doc;
3055 KWFootNoteVariable * var = new KWFootNoteVariable( textDocument(), doc->variableFormatCollection()->format( "NUMBER" ), doc->variableCollection(), doc );
3056 var->setNoteType( noteType );
3057 var->setNumberingType( numType );
3058 if ( numType == KWFootNoteVariable::Manual )
3059 var->setManualString( manualString );
3060
3061 // Now create text frameset which will hold the variable's contents
3062 KWFootNoteFrameSet *fs = new KWFootNoteFrameSet( doc, i18n( "Footnotes" ) );
3063 fs->setFrameSetInfo( KWFrameSet::FI_FOOTNOTE );
3064
3065 doc->addFrameSet( fs );
3066
3067 // Bind the footnote variable and its text frameset
3068 var->setFrameSet( fs );
3069 fs->setFootNoteVariable( var );
3070
3071 return fs;
3072}
3073
3074KoVariable* KWTextFrameSet::variableUnderMouse( const KoPoint& dPoint )
3075{
3076 TQPoint iPoint;
3077 if ( documentToInternal( dPoint, iPoint ) )
3078 return textObject()->variableAtPoint( iPoint );
3079 return 0;
3080}
3081
3082KoLinkVariable* KWTextFrameSet::linkVariableUnderMouse( const KoPoint& dPoint )
3083{
3084 TQPoint iPoint;
3085 if ( documentToInternal( dPoint, iPoint ) )
3086 {
3087 KoLinkVariable* linkVariable = dynamic_cast<KoLinkVariable *>( textObject()->variableAtPoint( iPoint ) );
3088 return linkVariable;
3089 }
3090 return 0;
3091}
3092
3094
3095KWTextFrameSetEdit::KWTextFrameSetEdit( KWTextFrameSet * fs, KWCanvas * canvas )
3096 : KoTextView( fs->textObject() ), KWFrameSetEdit( fs, canvas ), m_rtl( false )
3097{
3098 setBackSpeller( fs->kWordDocument()->backSpeller() );
3099 //kdDebug(32001) << "KWTextFrameSetEdit::KWTextFrameSetEdit " << fs->name() << endl;
3100 KoTextView::setReadWrite( fs->kWordDocument()->isReadWrite() );
3101 KoTextObject* textobj = fs->textObject();
3102 connect( textobj, TQ_SIGNAL( selectionChanged(bool) ), canvas, TQ_SIGNAL( selectionChanged(bool) ) );
3103 connect( fs, TQ_SIGNAL( frameDeleted(KWFrame *) ), this, TQ_SLOT( slotFrameDeleted(KWFrame *) ) );
3104 connect( textView(), TQ_SIGNAL( cut() ), TQ_SLOT( cut() ) );
3105 connect( textView(), TQ_SIGNAL( copy() ), TQ_SLOT( copy() ) );
3106 connect( textView(), TQ_SIGNAL( paste() ), TQ_SLOT( paste() ) );
3107 updateUI( true, true );
3108
3109 if( canvas->gui() && canvas->gui()->getHorzRuler())
3110 {
3111 if ( !textobj->protectContent() )
3112 canvas->gui()->getHorzRuler()->changeFlags(KoRuler::F_INDENTS | KoRuler::F_TABS);
3113 else
3114 canvas->gui()->getHorzRuler()->changeFlags(0);
3115 }
3116
3117 setOverwriteMode( canvas->overwriteMode() );
3118}
3119
3120KWTextFrameSetEdit::~KWTextFrameSetEdit()
3121{
3122 //kdDebug(32001) << "KWTextFrameSetEdit::~KWTextFrameSetEdit" << endl;
3123 //m_canvas->gui()->getHorzRuler()->changeFlags(0);
3124}
3125
3126KoTextViewIface* KWTextFrameSetEdit::dcopObject()
3127{
3128 if ( !dcop )
3129 dcop = new KWordTextFrameSetEditIface( this );
3130
3131 return dcop;
3132}
3133
3134void KWTextFrameSetEdit::terminate(bool removeSelection)
3135{
3136 disconnect( textView()->textObject(), TQ_SIGNAL( selectionChanged(bool) ), m_canvas, TQ_SIGNAL( selectionChanged(bool) ) );
3137 textView()->terminate(removeSelection);
3138}
3139
3140void KWTextFrameSetEdit::slotFrameDeleted( KWFrame *frm )
3141{
3142 if ( m_currentFrame == frm )
3143 m_currentFrame = 0L;
3144}
3145
3146void KWTextFrameSetEdit::paste()
3147{
3148 TQMimeSource *data = TQApplication::clipboard()->data();
3149 int provides = KWView::checkClipboard( data );
3150 pasteData( data, provides, false );
3151}
3152
3153void KWTextFrameSetEdit::pasteData( TQMimeSource* data, int provides, bool drop )
3154{
3155 if ( provides & KWView::ProvidesOasis )
3156 {
3157 KCommand* cmd = pasteOasisCommand( data );
3158 if ( cmd )
3159 frameSet()->kWordDocument()->addCommand(cmd);
3160 }
3161 else if ( provides & KWView::ProvidesPlainText )
3162 {
3163 // Note: TQClipboard::text() seems to do a better job than encodedData( "text/plain" )
3164 // In particular it handles charsets (in the mimetype).
3165 const TQString text = TQApplication::clipboard()->text();
3166 const bool removeSelected = !drop;
3167 if ( !text.isEmpty() )
3168 textObject()->pasteText( cursor(), text, currentFormat(), removeSelected );
3169 }
3170 else {
3171 kdWarning(32002) << "Unhandled case in KWTextFrameSetEdit::pasteData: provides=" << provides << endl;
3172 }
3173 // be sure that the footnote number didn't got erased
3174 KWFootNoteFrameSet *footNote = dynamic_cast<KWFootNoteFrameSet *>(textFrameSet());
3175 if ( footNote )
3176 {
3177 KoParagCounter *counter = footNote->textDocument()->firstParag()->counter();
3178 if ( !counter || ( counter->numbering() != KoParagCounter::NUM_FOOTNOTE ) )
3179 footNote->setCounterText( footNote->footNoteVariable()->text() );
3180 frameSet()->kWordDocument()->slotRepaintChanged( frameSet() );
3181 }
3182}
3183
3184KCommand* KWTextFrameSetEdit::pasteOasisCommand( TQMimeSource* data )
3185{
3186 // Find which mimetype it was (could be oasis text, oasis presentation etc.)
3187 TQCString returnedTypeMime = KoTextObject::providesOasis( data );
3188 if ( !returnedTypeMime.isEmpty() )
3189 {
3190 TQByteArray arr = data->encodedData( returnedTypeMime );
3191 Q_ASSERT( !arr.isEmpty() );
3192 if ( arr.size() )
3193 return textFrameSet()->pasteOasis( cursor(), arr, true );
3194 }
3195 return 0;
3196}
3197
3198void KWTextFrameSetEdit::cut()
3199{
3200 if ( textDocument()->hasSelection( KoTextDocument::Standard ) ) {
3201 copy();
3202 textObject()->removeSelectedText( cursor() );
3203 }
3204}
3205
3206void KWTextFrameSetEdit::copy()
3207{
3208 if ( textDocument()->hasSelection( KoTextDocument::Standard ) ) {
3209 TQDragObject *drag = newDrag( 0 );
3210 TQApplication::clipboard()->setData( drag );
3211 }
3212}
3213
3214bool KWTextFrameSetEdit::doIgnoreDoubleSpace(KoTextParag * parag,
3215 int index,TQChar ch )
3216{
3217 if( textFrameSet()->kWordDocument()->allowAutoFormat())
3218 {
3219 KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
3220 if( autoFormat )
3221 {
3222 return autoFormat->doIgnoreDoubleSpace( parag, index,ch );
3223 }
3224 }
3225 return false;
3226
3227}
3228
3229
3230void KWTextFrameSetEdit::doAutoFormat( KoTextCursor* cursor, KoTextParag *parag, int index, TQChar ch )
3231{
3232 if( textFrameSet()->kWordDocument()->allowAutoFormat() )
3233 {
3234 KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
3235 if( autoFormat )
3236 autoFormat->doAutoFormat( cursor, parag, index, ch, textObject());
3237 }
3238}
3239
3240bool KWTextFrameSetEdit::doCompletion( KoTextCursor* cursor, KoTextParag *parag, int index )
3241{
3242 if( textFrameSet()->kWordDocument()->allowAutoFormat() )
3243 {
3244 KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
3245 if( autoFormat )
3246 return autoFormat->doCompletion( cursor, parag, index, textObject());
3247 }
3248 return false;
3249}
3250
3251bool KWTextFrameSetEdit::doToolTipCompletion( KoTextCursor* cursor, KoTextParag *parag, int index, int keyPressed )
3252{
3253 if( textFrameSet()->kWordDocument()->allowAutoFormat() )
3254 {
3255 KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
3256 if( autoFormat )
3257 return autoFormat->doToolTipCompletion( cursor, parag, index, textObject(), keyPressed);
3258 }
3259 return false;
3260}
3261
3262void KWTextFrameSetEdit::showToolTipBox(KoTextParag *parag, int index, TQWidget *widget, const TQPoint &pos)
3263{
3264 if( textFrameSet()->kWordDocument()->allowAutoFormat() )
3265 {
3266 KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
3267 if( autoFormat )
3268 autoFormat->showToolTipBox(parag, index, widget, pos);
3269 }
3270}
3271
3272void KWTextFrameSetEdit::removeToolTipCompletion()
3273{
3274 if( textFrameSet()->kWordDocument()->allowAutoFormat() )
3275 {
3276 KoAutoFormat * autoFormat = textFrameSet()->kWordDocument()->autoFormat();
3277 if( autoFormat )
3278 autoFormat->removeToolTipCompletion();
3279 }
3280}
3281
3282void KWTextFrameSetEdit::textIncreaseIndent()
3283{
3284 kdDebug(32001) << "Increasing list" << endl;
3285 m_canvas->gui()->getView()->textIncreaseIndent();
3286}
3287
3288bool KWTextFrameSetEdit::textDecreaseIndent()
3289{
3290 if (currentLeftMargin()>0)
3291 {
3292 kdDebug(32001) << "Decreasing list" << endl;
3293 m_canvas->gui()->getView()->textDecreaseIndent();
3294 return true;
3295 }
3296 else
3297 return false;
3298}
3299
3300void KWTextFrameSetEdit::startDrag()
3301{
3302 textView()->dragStarted();
3303 m_canvas->dragStarted();
3304 TQDragObject *drag = newDrag( m_canvas->viewport() );
3305 if ( !frameSet()->kWordDocument()->isReadWrite() )
3306 drag->dragCopy();
3307 else {
3308 bool move = ( drag->drag() );
3309 if ( move )
3310 {
3311#if 0
3312 if ( TQDragObject::target() != m_canvas && TQDragObject::target() != m_canvas->viewport() ) {
3313 //This is when dropping text _out_ of KWord. Since we have Move and Copy
3314 //options (Copy being accessed by pressing CTRL), both are possible.
3315 //But is that intuitive enough ? Doesn't the user expect a Copy in all cases ?
3316 //Losing the selected text when dropping out of kword seems quite unexpected to me.
3317 //Undecided about this........
3318 textObject()->removeSelectedText( cursor() );
3319 }
3320#endif
3321 }
3322 }
3323}
3324
3325TQDragObject * KWTextFrameSetEdit::newDrag( TQWidget * parent )
3326{
3327 KWTextFrameSet* fs = textFrameSet();
3328 return fs->kWordDocument()->dragSelected( parent, fs );
3329}
3330
3331void KWTextFrameSetEdit::ensureCursorVisible()
3332{
3333 //kdDebug() << "KWTextFrameSetEdit::ensureCursorVisible paragId=" << cursor()->parag()->paragId() << " cursor->index()=" << cursor()->index() << endl;
3334 KoTextParag * parag = cursor()->parag();
3335 int idx = cursor()->index();
3336 textFrameSet()->ensureFormatted( parag );
3337 KoTextStringChar *chr = parag->at( idx );
3338 int cursorHeight = parag->lineHeightOfChar( idx );
3339 int x = parag->rect().x() + cursor()->x(); // this includes +charwidth for an RTL char
3340 //kdDebug() << "parag->rect().x()=" << parag->rect().x() << " x=" << cursor()->x() << endl;
3341 int y = 0; int dummy;
3342 parag->lineHeightOfChar( idx, &dummy, &y );
3343 y += parag->rect().y();
3344 //kdDebug() << "KWTextFrameSetEdit::ensureCursorVisible y=" << y << endl;
3345 // make sure one char is visible before, and one after
3346 KoTextStringChar *chrLeft = idx > 0 ? chr-1 : chr;
3347 // which char is on the left and which one is on the right depends on chr->rightToLeft
3348 int areaLeft = chr->rightToLeft ? chr->width : chrLeft->width;
3349 int areaRight = chr->rightToLeft ? chrLeft->width : chr->width;
3350 KoPoint pt;
3351 KoPoint hintDPoint;
3352 if ( m_currentFrame )
3353 hintDPoint = m_currentFrame->topLeft();
3354 KWFrame * theFrame = textFrameSet()->internalToDocumentWithHint( TQPoint(x, y), pt, hintDPoint );
3355 //kdDebug() << "KWTextFrameSetEdit::ensureCursorVisible frame=" << theFrame << " m_currentFrame=" << m_currentFrame << endl;
3356 if ( theFrame && m_currentFrame != theFrame )
3357 {
3358 m_currentFrame = theFrame;
3359 m_canvas->gui()->getView()->updatePageInfo();
3360 }
3361 TQPoint cursorPos = textFrameSet()->kWordDocument()->zoomPoint( pt );
3362 cursorPos = m_canvas->viewMode()->normalToView( cursorPos );
3363 areaLeft = textFrameSet()->kWordDocument()->layoutUnitToPixelX( areaLeft ) + 1;
3364 areaRight = textFrameSet()->kWordDocument()->layoutUnitToPixelX( areaRight ) + 1;
3365 cursorHeight = textFrameSet()->kWordDocument()->layoutUnitToPixelY( cursorHeight );
3366 //kdDebug() << "KWTextFrameSetEdit::ensureCursorVisible pt=" << pt << " cursorPos=" << cursorPos
3367 // << " areaLeft=" << areaLeft << " areaRight=" << areaRight << " y=" << y << endl;
3368 m_canvas->ensureVisible( cursorPos.x() - areaLeft, cursorPos.y() + cursorHeight / 2, areaLeft + areaRight, cursorHeight / 2 + 2 );
3369}
3370
3371bool KWTextFrameSetEdit::enterCustomItem( KoTextCustomItem* customItem, bool fromRight )
3372{
3373 KWAnchor* anchor = dynamic_cast<KWAnchor*>( customItem );
3374 if ( anchor ) {
3375 KWFrameSet* frameSet = anchor->frameSet();
3376 if ( frameSet->type() == FT_FORMULA || frameSet->type() == FT_TEXT ) {
3377
3378 // store the instance variable we need after "delete this"
3379 KWCanvas* canvas = m_canvas;
3380
3381 // this will "delete this"!
3382 m_canvas->editFrameSet( frameSet );
3383
3384 // We assume that `editFrameSet' succeeded.
3385 if ( fromRight ) {
3386 KWFrameSetEdit* edit = canvas->currentFrameSetEdit();
3387 if ( frameSet->type() == FT_FORMULA )
3388 static_cast<KWFormulaFrameSetEdit*>( edit )->moveEnd();
3389 else
3390 static_cast<KWTextFrameSetEdit*>( edit )->moveCursor( MoveEnd );
3391 }
3392
3393 if ( frameSet->type() == FT_FORMULA )
3394 {
3395 // A FormulaFrameSetEdit looks a little different from
3396 // a FormulaFrameSet. (Colors)
3397 static_cast<KWFormulaFrameSet*>( frameSet )->setChanged();
3398 canvas->repaintChanged( frameSet, true );
3399 }
3400 return true;
3401 }
3402 }
3403 return false;
3404}
3405
3406void KWTextFrameSetEdit::keyPressEvent( TQKeyEvent* e )
3407{
3408 // Handle moving into inline frames (e.g. formula frames).
3409 if ( !( e->state() & ControlButton ) && !( e->state() & ShiftButton ) )
3410 {
3411 if (e->state() != TQt::NoButton)
3412 removeToolTipCompletion();
3413 switch ( e->key() ) {
3414 case Key_Left: {
3415 KoTextCursor* cursor = textView()->cursor();
3416 KoTextParag* parag = cursor->parag();
3417 int index = cursor->index();
3418 if ( index > 0 ) {
3419 KoTextStringChar* ch = parag->at( index-1 );
3420 if ( ch->isCustom() ) {
3421 KoTextCustomItem* customItem = ch->customItem();
3422 if ( enterCustomItem( customItem, true ) ) {
3423 // Don't do anything here, "this" is deleted!
3424 return;
3425 }
3426 }
3427 }
3428 if ( index == 0 && !parag->prev() )
3429 if ( exitLeft() )
3430 return;
3431 break;
3432 }
3433 case Key_Right: {
3434 KoTextCursor* cursor = textView()->cursor();
3435 KoTextParag* parag = cursor->parag();
3436 int index = cursor->index();
3437 if ( index < parag->length() - 1 ) {
3438 KoTextStringChar* ch = parag->at( index );
3439 if ( ch->isCustom() ) {
3440 KoTextCustomItem* customItem = ch->customItem();
3441 if ( enterCustomItem( customItem, false ) ) {
3442 // Don't do anything here, "this" is deleted!
3443 return;
3444 }
3445 }
3446 } else if ( /*at end, covered by previous if, && */ !parag->next() )
3447 if ( exitRight() )
3448 return;
3449 break;
3450 }
3451 }
3452 }
3453 // Calculate position of tooltip for autocompletion
3454 TQPoint pos = textFrameSet()->cursorPos( cursor(), m_canvas, m_currentFrame );
3455 textView()->handleKeyPressEvent( e, m_canvas, pos );
3456}
3457
3458void KWTextFrameSetEdit::keyReleaseEvent( TQKeyEvent* e )
3459{
3460 textView()->handleKeyReleaseEvent( e );
3461}
3462
3463void KWTextFrameSetEdit::imStartEvent( TQIMEvent* e )
3464{
3465 textView()->handleImStartEvent( e );
3466}
3467
3468void KWTextFrameSetEdit::imComposeEvent( TQIMEvent* e )
3469{
3470 textView()->handleImComposeEvent( e );
3471}
3472
3473void KWTextFrameSetEdit::imEndEvent( TQIMEvent* e )
3474{
3475 textView()->handleImEndEvent( e );
3476}
3477
3478void KWTextFrameSetEdit::mousePressEvent( TQMouseEvent *e, const TQPoint &, const KoPoint & dPoint )
3479{
3480 if ( dPoint.x() < 0 || dPoint.y() < 0 )
3481 return; // Ignore clicks completely outside of the page (e.g. in the gray area, or ruler)
3482
3483 textFrameSet()->textObject()->clearUndoRedoInfo();
3484 if ( m_currentFrame )
3485 hideCursor(); // Need to do that with the old m_currentFrame
3486
3487 TQPoint iPoint;
3489 KWFrame * theFrame = textFrameSet()->documentToInternalMouseSelection( dPoint, iPoint, relPos, m_canvas->viewMode() );
3490 if ( theFrame && m_currentFrame != theFrame )
3491 {
3492 m_currentFrame = theFrame;
3493 m_canvas->gui()->getView()->updatePageInfo();
3494 }
3495
3496 if ( m_currentFrame )
3497 {
3498 // Let KoTextView handle the mousepress event - but don't let it start
3499 // a drag if clicking on the left of the text (out of the frame itself)
3500 bool addParag = textView()->handleMousePressEvent( e, iPoint, relPos != KWTextFrameSet::LeftOfFrame, frameSet()->kWordDocument()->insertDirectCursor() );
3501
3502
3503 // Clicked on the left of the text -> select the whole paragraph
3504 if ( relPos == KWTextFrameSet::LeftOfFrame )
3505 textView()->selectParagUnderCursor( *textView()->cursor() );
3506 if ( addParag )
3507 frameSet()->kWordDocument()->setModified(true );
3508 }
3509 // else mightStartDrag = false; necessary?
3510
3511 if ( e->button() != TQt::LeftButton )
3512 return;
3513 KoVariable* var = variable();
3514 if ( var )
3515 {
3516 KWFootNoteVariable * footNoteVar = dynamic_cast<KWFootNoteVariable *>( var );
3517 if ( footNoteVar )
3518 {
3519 footNoteVar->frameSet()->startEditing( m_canvas );
3520 // --- and now we are deleted! ---
3521 }
3522 }
3523}
3524
3525void KWTextFrameSetEdit::mouseMoveEvent( TQMouseEvent * e, const TQPoint & nPoint, const KoPoint & )
3526{
3527 if ( textView()->maybeStartDrag( e ) )
3528 return;
3529 if ( nPoint.x() < 0 || nPoint.y() < 0 )
3530 return; // Ignore clicks completely outside of the page (e.g. in the gray area, or ruler)
3531
3532 TQPoint iPoint;
3533 KoPoint dPoint = frameSet()->kWordDocument()->unzoomPoint( nPoint );
3535 if ( nPoint.y() > 0 && textFrameSet()->documentToInternalMouseSelection( dPoint, iPoint, relPos , m_canvas->viewMode()) )
3536 {
3537 if ( relPos == KWTextFrameSet::LeftOfFrame )
3538 textView()->extendParagraphSelection( iPoint );
3539 else
3540 textView()->handleMouseMoveEvent( e, iPoint );
3541 }
3542
3543}
3544
3545bool KWTextFrameSetEdit::openLink( KoLinkVariable* variable )
3546{
3547 KWTextFrameSet* fs = textFrameSet();
3548 KWDocument* doc = fs->kWordDocument();
3549 if ( doc->variableCollection()->variableSetting()->displayLink() ) {
3550
3551 const TQString url = variable->url();
3552 if( url.startsWith("bkm://") )
3553 {
3554 const KoTextBookmark* bookmark = doc->bookmarkByName(url.mid(6) );
3555 if ( bookmark )
3556 {
3557 cursor()->setParag( bookmark->startParag() );
3558 ensureCursorVisible();
3559 return true;
3560 }
3561 }
3562 KoTextView::openLink( variable );
3563 return true;
3564 }
3565 return false;
3566}
3567
3569{
3570 KoLinkVariable* v = linkVariable();
3571 if ( v )
3572 openLink( v );
3573}
3574
3575void KWTextFrameSetEdit::mouseReleaseEvent( TQMouseEvent *, const TQPoint &, const KoPoint & )
3576{
3577 textView()->handleMouseReleaseEvent();
3578}
3579
3580void KWTextFrameSetEdit::mouseDoubleClickEvent( TQMouseEvent *e, const TQPoint &, const KoPoint & )
3581{
3582 textView()->handleMouseDoubleClickEvent( e, TQPoint() /* Currently unused */ );
3583}
3584
3585void KWTextFrameSetEdit::dragEnterEvent( TQDragEnterEvent * e )
3586{
3587 int provides = KWView::checkClipboard( e );
3588 if ( !frameSet()->kWordDocument()->isReadWrite() || provides == 0 )
3589 {
3590 e->ignore();
3591 return;
3592 }
3593 e->acceptAction();
3594}
3595
3596void KWTextFrameSetEdit::dragMoveEvent( TQDragMoveEvent * e, const TQPoint &nPoint, const KoPoint & )
3597{
3598 int provides = KWView::checkClipboard( e );
3599 if ( !frameSet()->kWordDocument()->isReadWrite() || provides == 0 )
3600 {
3601 e->ignore();
3602 return;
3603 }
3604 // place cursor - unless dropping an image. well it's hard to know if the user
3605 // wants the dropped image to be inline or absolute positioned.
3606 if ( provides & ( KWView::ProvidesOasis | KWView::ProvidesPlainText | KWView::ProvidesFormula ) )
3607 {
3608 TQPoint iPoint;
3609 KoPoint dPoint = frameSet()->kWordDocument()->unzoomPoint( nPoint );
3610 if ( textFrameSet()->documentToInternal( dPoint, iPoint ) )
3611 {
3612 textObject()->emitHideCursor();
3613 placeCursor( iPoint );
3614 textObject()->emitShowCursor();
3615 }
3616 }
3617 e->acceptAction();
3618}
3619
3620void KWTextFrameSetEdit::dragLeaveEvent( TQDragLeaveEvent * )
3621{
3622}
3623
3624void KWTextFrameSetEdit::dropEvent( TQDropEvent * e, const TQPoint & nPoint, const KoPoint &, KWView* view )
3625{
3626 int provides = KWView::checkClipboard( e );
3627 if ( frameSet()->kWordDocument()->isReadWrite() && provides )
3628 {
3629 e->acceptAction();
3630 KoTextCursor dropCursor( textDocument() );
3631 TQPoint dropPoint;
3632 KoPoint dPoint = frameSet()->kWordDocument()->unzoomPoint( nPoint );
3633 if ( !textFrameSet()->documentToInternal( dPoint, dropPoint ) )
3634 return; // Don't know where to paste
3635
3636 dropCursor.place( dropPoint, textDocument()->firstParag() );
3637 kdDebug(32001) << "KWTextFrameSetEdit::dropEvent dropCursor at parag=" << dropCursor.parag()->paragId() << " index=" << dropCursor.index() << endl;
3638
3639 if ( ( e->source() == m_canvas ||
3640 e->source() == m_canvas->viewport() ) &&
3641 e->action() == TQDropEvent::Move &&
3642 // this is the indicator that the source and dest text objects are the same
3643 textDocument()->hasSelection( KoTextDocument::Standard ) ) {
3644
3645 KCommand *cmd = textView()->prepareDropMove( dropCursor );
3646 if(cmd)
3647 {
3648 KMacroCommand* macroCmd = new KMacroCommand( i18n( "Move Text" ) );
3649 macroCmd->addCommand(cmd);
3650
3651 cmd = pasteOasisCommand( e );
3652 if ( cmd )
3653 macroCmd->addCommand(cmd);
3654 //relayout textframeset after a dnd otherwise autoextend
3655 //frameset is not re-layouted
3656 textFrameSet()->layout();
3657 frameSet()->kWordDocument()->addCommand( macroCmd );
3658 }
3659 return;
3660 }
3661 else
3662 { // drop coming from outside -> forget about current selection
3663 textDocument()->removeSelection( KoTextDocument::Standard );
3664 textObject()->selectionChangedNotify();
3665 }
3666
3667 // The cursor is already correctly positioned, all we need to do is to "paste" the dropped data.
3668 view->pasteData( e, true );
3669 }
3670}
3671
3672void KWTextFrameSetEdit::focusInEvent()
3673{
3674 textView()->focusInEvent();
3675}
3676
3677void KWTextFrameSetEdit::focusOutEvent()
3678{
3679 textView()->focusOutEvent();
3680}
3681
3682void KWTextFrameSetEdit::selectAll()
3683{
3684 textObject()->selectAll( true );
3685}
3686
3687void KWTextFrameSetEdit::drawCursor( bool visible )
3688{
3689#ifdef DEBUG_CURSOR
3690 kdDebug() << "KWTextFrameSetEdit::drawCursor " << visible << endl;
3691#endif
3692 KoTextView::drawCursor( visible );
3693 if ( !cursor()->parag() )
3694 return;
3695
3696 if ( !cursor()->parag()->isValid() )
3697 textFrameSet()->ensureFormatted( cursor()->parag() );
3698
3699 if ( !frameSet()->kWordDocument()->isReadWrite() )
3700 return;
3701 if ( m_canvas->viewMode()->hasFrames() && !m_currentFrame )
3702 return;
3703
3704 TQPainter p( m_canvas->viewport() );
3705 p.translate( -m_canvas->contentsX(), -m_canvas->contentsY() );
3706 p.setBrushOrigin( -m_canvas->contentsX(), -m_canvas->contentsY() );
3707
3708 textFrameSet()->drawCursor( &p, cursor(), visible, m_canvas, m_currentFrame );
3709}
3710
3711bool KWTextFrameSetEdit::pgUpKeyPressed()
3712{
3713 TQRect crect( m_canvas->contentsX(), m_canvas->contentsY(),
3714 m_canvas->visibleWidth(), m_canvas->visibleHeight() );
3715 crect = m_canvas->viewMode()->viewToNormal( crect );
3716
3717 // Go up of 90% of crect.height()
3718 int h = frameSet()->kWordDocument()->pixelToLayoutUnitY( (int)( (double)crect.height() * 0.9 ) );
3719 KoTextParag *s = textView()->cursor()->parag();
3720 KoTextParag* oldParag = s;
3721 int y = s->rect().y();
3722 while ( s ) {
3723 if ( y - s->rect().y() >= h )
3724 break;
3725 s = s->prev();
3726 }
3727
3728 if ( !s )
3729 s = textDocument()->firstParag();
3730
3731 textView()->cursor()->setParag( s );
3732 textView()->cursor()->setIndex( 0 );
3733 if ( s == oldParag )
3734 {
3735 m_canvas->viewportScroll( true );
3736 return false;
3737 }
3738 return true;
3739}
3740
3741bool KWTextFrameSetEdit::pgDownKeyPressed()
3742{
3743 TQRect crect( m_canvas->contentsX(), m_canvas->contentsY(),
3744 m_canvas->visibleWidth(), m_canvas->visibleHeight() );
3745 crect = m_canvas->viewMode()->viewToNormal( crect );
3746 // Go down of 90% of crect.height()
3747 int h = frameSet()->kWordDocument()->pixelToLayoutUnitY( (int)( (double)crect.height() * 0.9 ) );
3748
3749 KoTextCursor *cursor = textView()->cursor();
3750 KoTextParag *s = cursor->parag();
3751 KoTextParag* oldParag = s;
3752 int y = s->rect().y();
3753 while ( s ) {
3754 if ( s->rect().y() - y >= h )
3755 break;
3756 s = s->next();
3757 }
3758
3759 if ( !s ) {
3760 s = textDocument()->lastParag();
3761 cursor->setParag( s );
3762 cursor->setIndex( s->length() - 1 );
3763 } else {
3764 cursor->setParag( s );
3765 cursor->setIndex( 0 );
3766 }
3767 if ( s == oldParag )
3768 {
3769 m_canvas->viewportScroll( false );
3770 return false;
3771 }
3772 return true;
3773}
3774
3775void KWTextFrameSetEdit::ctrlPgUpKeyPressed()
3776{
3777 if ( m_currentFrame )
3778 {
3779 TQPoint iPoint = textFrameSet()->moveToPage( m_currentFrame->pageNumber(), -1 );
3780 if ( !iPoint.isNull() )
3781 placeCursor( iPoint );
3782 }
3783}
3784
3785void KWTextFrameSetEdit::ctrlPgDownKeyPressed()
3786{
3787 if ( m_currentFrame )
3788 {
3789 TQPoint iPoint = textFrameSet()->moveToPage( m_currentFrame->pageNumber(), +1 );
3790 if ( !iPoint.isNull() )
3791 placeCursor( iPoint );
3792 }
3793}
3794
3795void KWTextFrameSetEdit::setCursor( KoTextParag* parag, int index )
3796{
3797 cursor()->setParag( parag );
3798 cursor()->setIndex( index );
3799}
3800
3801void KWTextFrameSetEdit::insertExpression(const TQString &_c)
3802{
3803 if(textObject()->hasSelection() )
3804 frameSet()->kWordDocument()->addCommand(textObject()->replaceSelectionCommand(
3805 cursor(), _c, i18n("Insert Expression")));
3806 else
3807 textObject()->insert( cursor(), currentFormat(), _c, i18n("Insert Expression") );
3808}
3809
3810void KWTextFrameSetEdit::insertFloatingFrameSet( KWFrameSet * fs, const TQString & commandName )
3811{
3812 textObject()->clearUndoRedoInfo();
3813 CustomItemsMap customItemsMap;
3814 TQString placeHolders;
3815 // TODO support for multiple floating items (like multiple-page tables)
3816 int frameNumber = 0;
3817 int index = 0;
3818 int insertFlags = KoTextObject::DoNotRemoveSelected;
3819 { // the loop will start here :)
3820 KWAnchor * anchor = fs->createAnchor( textFrameSet()->textDocument(), frameNumber );
3821 if ( frameNumber == 0 && anchor->ownLine() && cursor()->index() > 0 ) // enforce start of line - currently unused
3822 {
3823 kdDebug() << "ownline -> prepending \\n" << endl;
3824 placeHolders += TQChar('\n');
3825 index++;
3826 insertFlags |= KoTextObject::CheckNewLine;
3827 }
3828 placeHolders += KoTextObject::customItemChar();
3829 customItemsMap.insert( index, anchor );
3830 }
3831 fs->setAnchored( textFrameSet() );
3832 textObject()->insert( cursor(), currentFormat(), placeHolders,
3833 commandName, KoTextDocument::Standard, insertFlags,
3834 customItemsMap );
3835}
3836
3837void KWTextFrameSetEdit::insertLink(const TQString &_linkName, const TQString & hrefName)
3838{
3839 KWDocument * doc = frameSet()->kWordDocument();
3840 KoVariable * var = new KoLinkVariable( textFrameSet()->textDocument(), _linkName, hrefName, doc->variableFormatCollection()->format( "STRING" ), doc->variableCollection() );
3841 insertVariable( var );
3842}
3843
3844void KWTextFrameSetEdit::insertComment(const TQString &_comment)
3845{
3846 KWDocument * doc = frameSet()->kWordDocument();
3847 KoVariable * var = new KoNoteVariable( textFrameSet()->textDocument(), _comment, doc->variableFormatCollection()->format( "STRING" ), doc->variableCollection() );
3848 insertVariable( var );
3849}
3850
3851
3852void KWTextFrameSetEdit::insertCustomVariable( const TQString &name)
3853{
3854 KWDocument * doc = frameSet()->kWordDocument();
3855 KoVariable * var = new KoCustomVariable( textFrameSet()->textDocument(), name, doc->variableFormatCollection()->format( "STRING" ), doc->variableCollection());
3856 insertVariable( var );
3857}
3858
3859void KWTextFrameSetEdit::insertFootNote( NoteType noteType, KWFootNoteVariable::Numbering numType, const TQString &manualString )
3860{
3861 KWFootNoteFrameSet *fs = textFrameSet()->insertFootNote( noteType, numType, manualString );
3862 KWFootNoteVariable * var = fs->footNoteVariable();
3863
3864 // Place the frame on the correct page, but the exact coordinates
3865 // will be determined by recalcFrames (KWFrameLayout)
3866 int pageNum = m_currentFrame->pageNumber();
3867 fs->createInitialFrame( pageNum );
3868
3869 insertVariable( var );
3870
3871 // Re-number footnote variables
3872 textFrameSet()->renumberFootNotes();
3873
3874 // Layout the footnote frame
3875 textFrameSet()->kWordDocument()->recalcFrames( pageNum, -1 ); // we know that for sure nothing changed before this page.
3876
3877 //KoTextParag* parag = fs->textDocument()->firstParag();
3878 //parag->truncate(0); // why? we just created it, anyway...
3879
3880 // And now edit the footnote frameset - all WPs do that it seems.
3881 fs->startEditing( m_canvas );
3882 // --- and now we are deleted! ---
3883}
3884
3885void KWTextFrameSetEdit::insertVariable( int type, int subtype )
3886{
3887 kdDebug() << "KWTextFrameSetEdit::insertVariable " << type << endl;
3888 KWDocument * doc = frameSet()->kWordDocument();
3889
3890 KoVariable * var = 0L;
3891 bool refreshCustomMenu = false;
3892 if ( type == VT_CUSTOM )
3893 {
3894 KoCustomVarDialog dia( m_canvas );
3895 if ( dia.exec() == TQDialog::Accepted )
3896 {
3897 KoCustomVariable *v = new KoCustomVariable( textFrameSet()->textDocument(), dia.name(), doc->variableFormatCollection()->format( "STRING" ),doc->variableCollection() );
3898 v->setValue( dia.value() );
3899 var = v;
3900 refreshCustomMenu = true;
3901 }
3902 }
3903 else if ( type == VT_MAILMERGE )
3904 {
3905 KWMailMergeVariableInsertDia dia( m_canvas, doc->mailMergeDataBase() );
3906 if ( dia.exec() == TQDialog::Accepted )
3907 {
3908 var = new KWMailMergeVariable( textFrameSet()->textDocument(), dia.getName(), doc->variableFormatCollection()->format( "STRING" ),doc->variableCollection(),doc );
3909 }
3910 }
3911 else
3912 var = doc->variableCollection()->createVariable( type, subtype, doc->variableFormatCollection(), 0L, textFrameSet()->textDocument(), doc, 0);
3913 if ( var)
3914 insertVariable( var, 0L /*means currentFormat()*/, refreshCustomMenu);
3915}
3916
3917void KWTextFrameSetEdit::insertVariable( KoVariable *var, KoTextFormat *format /*=0*/, bool refreshCustomMenu )
3918{
3919 if ( var )
3920 {
3921 CustomItemsMap customItemsMap;
3922 customItemsMap.insert( 0, var );
3923 if (!format)
3924 format = currentFormat();
3925 kdDebug() << "KWTextFrameSetEdit::insertVariable inserting into paragraph" << endl;
3926#ifdef DEBUG_FORMATS
3927 kdDebug() << "KWTextFrameSetEdit::insertVariable format=" << format << endl;
3928#endif
3929 textObject()->insert( cursor(), format, KoTextObject::customItemChar(),
3930 i18n("Insert Variable"),
3931 KoTextDocument::Standard,
3932 KoTextObject::DoNotRemoveSelected,
3933 customItemsMap );
3934 frameSet()->kWordDocument()->slotRepaintChanged( frameSet() );
3935 if ( var->type()==VT_CUSTOM && refreshCustomMenu)
3936 frameSet()->kWordDocument()->refreshMenuCustomVariable();
3937 }
3938}
3939
3940void KWTextFrameSetEdit::insertWPPage()
3941{
3942 KWTextFrameSet* textfs = textFrameSet();
3943 textfs->clearUndoRedoInfo();
3944 KoTextObject* textobj = textObject();
3945 KWDocument * doc = frameSet()->kWordDocument();
3946 int pages = doc->pageCount();
3947 int columns = doc->numColumns();
3948 // There could be N columns. In that case we may need to add up to N framebreaks.
3949 int inserted = 0;
3950 KMacroCommand* macroCmd = new KMacroCommand( i18n("Insert Page") );
3951 do {
3952 macroCmd->addCommand( textfs->insertFrameBreakCommand( cursor() ) );
3953 textobj->setLastFormattedParag( cursor()->parag() );
3954 textobj->formatMore( 2 );
3955 } while ( pages == doc->pageCount() && ++inserted <= columns );
3956 if ( pages == doc->pageCount() )
3957 kdWarning(32002) << k_funcinfo << " didn't manage to insert a new page! inserted=" << inserted << " columns=" << columns << " pages=" << pages << endl;
3958
3959 doc->addCommand( macroCmd );
3960
3961 textfs->slotRepaintChanged();
3962 textobj->emitEnsureCursorVisible();
3963 textobj->emitUpdateUI( true );
3964 textobj->emitShowCursor();
3965}
3966
3967KoBorder KWTextFrameSetEdit::border(KoBorder::BorderType type) {
3968 if(type == KoBorder::LeftBorder)
3969 return m_paragLayout.leftBorder;
3970 if(type == KoBorder::RightBorder)
3971 return m_paragLayout.rightBorder;
3972 if(type == KoBorder::TopBorder)
3973 return m_paragLayout.topBorder;
3974 return m_paragLayout.bottomBorder;
3975}
3976
3977// Update the GUI toolbar button etc. to reflect the current cursor position.
3978void KWTextFrameSetEdit::updateUI( bool updateFormat, bool force )
3979{
3980 // Update UI - only for those items which have changed
3981 KoTextView::updateUI( updateFormat, force );
3982
3983 // Paragraph settings
3984 KWTextParag * parag = static_cast<KWTextParag *>(cursor()->parag());
3985
3986 if ( m_paragLayout.alignment != parag->resolveAlignment() || force ) {
3987 m_paragLayout.alignment = parag->resolveAlignment();
3988 m_canvas->gui()->getView()->showAlign( m_paragLayout.alignment );
3989 }
3990
3991 // Counter
3992 if ( !m_paragLayout.counter )
3993 m_paragLayout.counter = new KoParagCounter; // we can afford to always have one here
3994 KoParagCounter::Style cstyle = m_paragLayout.counter->style();
3995 if ( parag->counter() )
3996 *m_paragLayout.counter = *parag->counter();
3997 else
3998 {
3999 m_paragLayout.counter->setNumbering( KoParagCounter::NUM_NONE );
4000 m_paragLayout.counter->setStyle( KoParagCounter::STYLE_NONE );
4001 }
4002 if ( m_paragLayout.counter->style() != cstyle || force )
4003 m_canvas->gui()->getView()->showCounter( * m_paragLayout.counter );
4004
4005 if(m_paragLayout.leftBorder!=parag->leftBorder() ||
4006 m_paragLayout.rightBorder!=parag->rightBorder() ||
4007 m_paragLayout.topBorder!=parag->topBorder() ||
4008 m_paragLayout.bottomBorder!=parag->bottomBorder() || force )
4009 {
4010 m_paragLayout.leftBorder = parag->leftBorder();
4011 m_paragLayout.rightBorder = parag->rightBorder();
4012 m_paragLayout.topBorder = parag->topBorder();
4013 m_paragLayout.bottomBorder = parag->bottomBorder();
4014 m_canvas->gui()->getView()->updateBorderButtons( m_paragLayout.leftBorder, m_paragLayout.rightBorder, m_paragLayout.topBorder, m_paragLayout.bottomBorder );
4015 }
4016
4017 if ( !parag->style() )
4018 kdWarning() << "Paragraph " << parag->paragId() << " has no style" << endl;
4019 else if ( m_paragLayout.style != parag->style() || force )
4020 {
4021 m_paragLayout.style = parag->style();
4022 m_canvas->gui()->getView()->showStyle( m_paragLayout.style->name() );
4023 }
4024
4025 if( m_paragLayout.margins[TQStyleSheetItem::MarginLeft] != parag->margin(TQStyleSheetItem::MarginLeft)
4026 || m_paragLayout.margins[TQStyleSheetItem::MarginFirstLine] != parag->margin(TQStyleSheetItem::MarginFirstLine)
4027 || m_paragLayout.margins[TQStyleSheetItem::MarginRight] != parag->margin(TQStyleSheetItem::MarginRight)
4028 || parag->string()->isRightToLeft() != m_rtl
4029 || force )
4030 {
4031 m_paragLayout.margins[TQStyleSheetItem::MarginFirstLine] = parag->margin(TQStyleSheetItem::MarginFirstLine);
4032 m_paragLayout.margins[TQStyleSheetItem::MarginLeft] = parag->margin(TQStyleSheetItem::MarginLeft);
4033 m_paragLayout.margins[TQStyleSheetItem::MarginRight] = parag->margin(TQStyleSheetItem::MarginRight);
4034 if ( m_rtl != parag->string()->isRightToLeft() && parag->counter() )
4035 {
4036 parag->counter()->invalidate();
4037 parag->setChanged( true ); // repaint
4038 }
4039 m_rtl = parag->string()->isRightToLeft();
4040 m_canvas->gui()->getView()->showRulerIndent( m_paragLayout.margins[TQStyleSheetItem::MarginLeft], m_paragLayout.margins[TQStyleSheetItem::MarginFirstLine], m_paragLayout.margins[TQStyleSheetItem::MarginRight], m_rtl );
4041 }
4042 if( m_paragLayout.tabList() != parag->tabList() || force)
4043 {
4044 m_paragLayout.setTabList( parag->tabList() );
4045 KoRuler * hr = m_canvas->gui()->getHorzRuler();
4046 if ( hr )
4047 hr->setTabList( parag->tabList() );
4048 }
4049 if( m_paragLayout.lineSpacingType != parag->paragLayout().lineSpacingType || force)
4050 {
4051 m_paragLayout.lineSpacingType = parag->paragLayout().lineSpacingType;
4052 m_canvas->gui()->getView()->showSpacing( m_paragLayout.lineSpacingType );
4053 }
4054 // There are more paragraph settings, but those that are not directly
4055 // visible in the UI don't need to be handled here.
4056 // For instance parag stuff, borders etc.
4057}
4058
4059void KWTextFrameSetEdit::showFormat( KoTextFormat *format )
4060{
4061 m_canvas->gui()->getView()->showFormat( *format );
4062}
4063
4064TQPoint KWTextFrameSet::cursorPos( KoTextCursor *cursor, KWCanvas* canvas, KWFrame* currentFrame )
4065{
4066 KWViewMode *viewMode = canvas->viewMode();
4067
4068 KoTextParag* parag = cursor->parag();
4069 const TQPoint topLeft = parag->rect().topLeft(); // in TQRT coords
4070 int lineY;
4071 parag->lineHeightOfChar( cursor->index(), 0, &lineY );
4072 // iPoint is the topright corner of the current character
4073 TQPoint iPoint( topLeft.x() + cursor->x() + parag->at( cursor->index() )->width, topLeft.y() + lineY );
4074
4075 KoPoint dPoint;
4076 TQPoint vPoint;
4077 KoPoint hintDPoint = currentFrame ? currentFrame->innerRect().topLeft() : KoPoint();
4078 if ( internalToDocumentWithHint( iPoint, dPoint, hintDPoint ) )
4079 {
4080 vPoint = viewMode->normalToView( m_doc->zoomPoint( dPoint ) ); // from doc to view contents
4081 vPoint.rx() -= canvas->contentsX();
4082 vPoint.ry() -= canvas->contentsY();
4083 } // else ... ?
4084 return vPoint;
4085}
4086
4088
4089bool KWFootNoteFrameSet::isFootNote() const
4090{
4091 if ( !m_footNoteVar ) {
4092 kdWarning() << k_funcinfo << " called too early? No footnote var." << endl;
4093 return false;
4094 }
4095 return ( m_footNoteVar->noteType() == FootNote );
4096}
4097
4098bool KWFootNoteFrameSet::isEndNote() const
4099{
4100 if ( !m_footNoteVar ) {
4101 kdWarning() << k_funcinfo << " called too early? No footnote var." << endl;
4102 return false;
4103 }
4104 return ( m_footNoteVar->noteType() == EndNote );
4105}
4106
4107
4108
4109void KWFootNoteFrameSet::createInitialFrame( int pageNum )
4110{
4111 KWFrame *frame = new KWFrame(this, 0, m_doc->pageManager()->topOfPage(pageNum) + 1, 20, 20 );
4112 frame->setFrameBehavior(KWFrame::AutoExtendFrame);
4113 frame->setNewFrameBehavior(KWFrame::NoFollowup);
4114 addFrame( frame );
4115}
4116
4117void KWFootNoteFrameSet::startEditing( KWCanvas* canvas )
4118{
4119 canvas->editFrameSet( this );
4120
4121 // Ensure cursor is visible
4122 KWTextFrameSetEdit *textedit = dynamic_cast<KWTextFrameSetEdit *>(canvas->currentFrameSetEdit()->currentTextEdit());
4123 if ( textedit )
4124 textedit->ensureCursorVisible();
4125}
4126
4127void KWFootNoteFrameSet::setFootNoteVariable( KWFootNoteVariable* var )
4128{
4129 m_footNoteVar = var;
4130}
4131
4132void KWFootNoteFrameSet::setCounterText( const TQString& text )
4133{
4134 KoTextParag* parag = textDocument()->firstParag();
4135 Q_ASSERT( parag );
4136 if ( parag ) {
4137 KoParagCounter counter;
4138 counter.setNumbering( KoParagCounter::NUM_FOOTNOTE );
4139 counter.setPrefix( text );
4140 counter.setSuffix( TQString() );
4141 parag->setCounter( counter );
4142 }
4143}
4144
4145KWordFrameSetIface* KWFootNoteFrameSet::dcopObject()
4146{
4147 if ( !m_dcop )
4148 m_dcop = new KWFootNoteFrameSetIface( this );
4149
4150 return m_dcop;
4151}
4152
4153#include "KWTextFrameSet.moc"
An anchor is a special character, or 'custom item'.
Definition KWAnchor.h:35
KWFrameSet * frameSet() const
The frameset that will provide the content to display in this anchor object.
Definition KWAnchor.h:50
Class: KWCanvas This class is responsible for the rendering of the frames to the screen as well as th...
Definition KWCanvas.h:61
void editTextFrameSet(KWFrameSet *fs, KoTextParag *parag, int index)
Starting editing fs if we're not yet doing it.
void repaintChanged(KWFrameSet *fs, bool resetChanged)
Only repaint the frameset fs.
Definition KWCanvas.cpp:186
The variable showing the footnote number in superscript, in the text.
Definition KWVariable.h:106
KWFootNoteFrameSet * frameSet() const
The frameset that contains the text for this footnote.
Definition KWVariable.h:126
void update()
Update the z-ordering around the frame we host the framelist for.
TQValueList< KWFrame * > framesOnTop() const
Fetches the frames on top of the frame this frameList is for.
This object is created to edit a particular frameset in a particular view The view's canvas creates i...
bool exitLeft()
Called if the cursor tries to leave the frameset at its beginning.
bool exitRight()
Called if the cursor tries to leave the frameset at its end.
KWFrame * m_currentFrame
The Frameset-Edit implementation is responsible for updating that one (to the frame where the current...
virtual KWFrameSetEdit * currentTextEdit()
Return the current most-low-level text edit object.
Class: KWFrameSet Base type, a frameset holds content as well as frames to show that content.
Definition KWFrameSet.h:62
void repaintChanged(KWFrameSet *frameset)
Emitted when something has changed in this frameset, so that all views repaint it.
virtual void setVisible(bool v)
set the visibility of the frameset.
virtual void deleteFrame(unsigned int num, bool remove=true, bool recalc=true)
Delete a frame from the set of frames this frameSet has.
static KWFrame * settingsFrame(const KWFrame *frame)
Returns the original frame that the param frame is a copy off, or the frame itself.
virtual FrameSetType type() const
The type of frameset.
Definition KWFrameSet.h:76
@ SortFrames
kwtextframeset only
Definition KWFrameSet.h:297
int m_firstPage
always equal to m_framesInPage[0].first()->pageNumber() :)
Definition KWFrameSet.h:491
const TQPtrList< KWFrame > & frameIterator() const
Iterator over the child frames.
Definition KWFrameSet.h:159
virtual void finalize()
Called once the frameset has been completely loaded or constructed.
virtual void drawContents(TQPainter *painter, const TQRect &crect, const TQColorGroup &cg, bool onlyChanged, bool resetChanged, KWFrameSetEdit *edit, KWViewMode *viewMode, KWFrameViewManager *frameViewManager)
Paint this frameset.
void setAnchored(KWTextFrameSet *textfs, KoTextParag *parag, int index, bool placeHolderExists=false, bool repaint=true)
Make this frameset anchored, with the anchor at paragId,index in the text frameset textfs.
bool isDeleted() const
True if the frameset was deleted (but not destroyed, since it's in the undo/redo)
Definition KWFrameSet.h:166
TQPtrVector< TQPtrList< KWFrame > > m_framesInPage
This array provides a direct access to the frames on page N.
Definition KWFrameSet.h:489
virtual void drawFrame(KWFrame *frame, TQPainter *painter, const TQRect &fcrect, const TQRect &crect, const TQPoint &translationOffset, KWFrame *settingsFrame, const TQColorGroup &cg, bool onlyChanged, bool resetChanged, KWFrameSetEdit *edit, KWViewMode *viewMode, bool drawUnderlyingFrames)
Draw a particular frame of this frameset.
virtual KWAnchor * createAnchor(KoTextDocument *txt, int frameNum)
Create an anchor for the floating frame identified by frameNum.
unsigned int frameCount() const
Get number of child frames.
Definition KWFrameSet.h:163
virtual void load(TQDomElement &framesetElem, bool loadFrames=true)
load from XML - when loading
bool isFloating() const
Return true if this frameset is floating (inline), false if it's fixed.
Definition KWFrameSet.h:372
virtual void updateFrames(int flags=0xff)
Called when our frames change, or when another frameset's frames change.
void saveCommon(TQDomElement &parentElem, bool saveFrames)
save the common attributes for the frameset
KWTableFrameSet * groupmanager() const
returns the groupManager this frameset belongs to.
Definition KWFrameSet.h:408
KWTextFrameSet * anchorFrameset() const
Return the frameset in which our anchor is - assuming isFloating()
Definition KWFrameSet.h:374
KWFrame * frame(unsigned int _num) const
get a frame by number
TQString name() const
get/set frameset name. For tables in particular, this must be unique
Definition KWFrameSet.h:428
TQRegion frameClipRegion(TQPainter *painter, KWFrame *frame, const TQRect &crect, KWViewMode *viewMode)
Determine the clipping rectangle for drawing the contents of frame with painter in the rectangle deli...
const TQPtrList< KWFrame > & framesInPage(int pageNum) const
Return list of frames in page pageNum.
Info frameSetInfo() const
Returns the type of TextFrameSet this is.
Definition KWFrameSet.h:95
bool isVisible(KWViewMode *viewMode=0L) const
Returns true if the frameset is visible.
Class that knows about all the (visible) frames in the document and will act as a manager between the...
This class represents a single frame.
Definition KWFrame.h:68
double minimumFrameHeight(void) const
Definition KWFrame.h:284
KWFrameSet * frameSet() const
Data stucture methods.
Definition KWFrame.h:143
double paddingTop() const
get top padding
Definition KWFrame.h:240
double paddingBottom() const
get bottom padding
Definition KWFrame.h:242
void setMinimumFrameHeight(double h)
The property minimum frame height is used to make the automatic frame shrinking code stop.
Definition KWFrame.h:279
FrameBehavior
what should happen when the frame is full
Definition KWFrame.h:116
void save(TQDomElement &frameElem)
create XML to describe yourself
Definition KWFrame.cpp:222
void setInternalY(double y)
The "internal Y" is the offset (in pt) of the real frame showed in this one ("real" means "the last t...
Definition KWFrame.h:223
double paddingLeft() const
get left padding
Definition KWFrame.h:236
bool isCopy() const
Drawing property: if isCopy, this frame is a copy of the previous frame in the frameset.
Definition KWFrame.h:138
int pageNumber() const
The page on which this frame is.
Definition KWFrame.cpp:116
void startOasisFrame(KoXmlWriter &xmlWriter, KoGenStyles &mainStyles, const TQString &name, const TQString &lastFrameName=TQString()) const
Write out a draw:frame element and its style.
Definition KWFrame.cpp:544
KWFrame * getCopy()
returns a copy of self
Definition KWFrame.cpp:135
Mail Merge variable.
Definition KWVariable.h:91
Command created when pasting oasis-formatted text.
Definition KWCommand.h:59
This is used to save a bunch of paragraphs into a oasis store in memory, as some features do (e....
TQByteArray data() const
Return the entire ZIP store as a byte array.
bool finish()
You MUST call this after saving the paragraphs and before calling data() Return true on success.
void saveParagraphs(const TQValueList< const KoTextParag * > &paragraphs)
Save a list of paragraphs.
This class represents a printed page of the document.
Definition KWPage.h:30
double height() const
return the height of this page (in pt)
Definition KWPage.cpp:44
double leftMargin() const
return the width of the margin at left (in pt);
Definition KWPage.cpp:97
int pageNumber() const
returns the number of this page as it will be shown to the user.
Definition KWPage.h:102
double topMargin() const
return the height of the margin at top (in pt);
Definition KWPage.cpp:87
double bottomMargin() const
return the height of the margin at bottom (in pt);
Definition KWPage.cpp:92
double width() const
return the width of this page (in pt)
Definition KWPage.cpp:38
double rightMargin() const
return the width of the margin at left (in pt);
Definition KWPage.cpp:108
double offsetInDocument() const
All Pages are in a document-wide coordinate system that simply puts every page under the next page an...
Definition KWPage.cpp:128
"current page number" and "number of pages" variables
Definition KWVariable.h:75
This class implements tables by acting as the manager for the frame(set)s which make up the table cel...
void recalcCols(uint column, uint row)
resize and position all cells
This is our KoTextDocument reimplementation, to create KWTextParag instead of KoTextParags,...
Object that is created to edit a Text frame set (KWTextFrameSet).
KoBorder border(KoBorder::BorderType type)
Return the requested border of the paragraph that the cursor currently is in.
virtual void terminate(bool removeselection=true)
Called before destruction, when terminating edition - use to e.g.
void openLink()
Called by KWView when using the action.
Class: KWTextFrameSet Contains text (KoTextObject) and frames to display that text.
KWFrame * internalToDocumentWithHint(const TQPoint &iPoint, KoPoint &dPoint, const KoPoint &hintDPoint) const
Same as internalToDocument, but goes directly to the normal coords (zoomed) since this method is used...
TQPoint moveToPage(int currentPgNum, short int direction) const
Implementation of Ctrl+PageUp/PageDown Returns a point in layout units (for placing the cursor)
virtual KWFrameSetEdit * createFrameSetEdit(KWCanvas *canvas)
Create a framesetedit object to edit this frameset in canvas.
virtual FrameSetType type() const
The type of frameset.
bool hasSelection() const
return true if some text is selected
TQByteArray sortText(SortType type) const
Sort selected paragraphs Return a complete OASIS store, ready for "pasting".
void frameDeleted(KWFrame *frame)
Tell the Edit object that this frame got deleted.
KoTextObject * textObject() const
Return the contained text object.
virtual void updateFrames(int flags=0xff)
reshuffle frames so text is always displayed from top-left down and then right.
KoPoint internalToDocumentKnowingFrame(const KoPoint &relPoint, KWFrame *theFrame) const
A variant of internalToDocument, when the frame is already known.
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...
KWTextFrameSet(KWDocument *_doc, const TQString &name)
Cnstructor.
virtual int adjustFlow(int y, int w, int h)
reimplemented from KoTextParag, adjusts y and returns the shift.
void ensureFormatted(KoTextParag *parag, bool emitAfterFormatting=true)
Make sure this paragraph is formatted If formatting happens, the afterFormatting signal will only be ...
virtual KoTextFormat * currentFormat() const
Let KoTextFormatInterface access the current format.
KWFrame * loadOasisTextFrame(const TQDomElement &frameTag, const TQDomElement &tag, KoOasisContext &context)
Load a frame and add it to this frameset - called by KWOasisLoader.
virtual bool canRemovePage(int num)
Return true if page num can be removed, as far as this frameset is concerned.
void setInlineFramesVisible(bool)
Show or hide all inline frames that are inside this frameset.
KWFrame * loadOasis(const TQDomElement &frame, const TQDomElement &bodyElem, KoOasisContext &context)
Load a complete textbox (frame and text)
virtual void layout()
Invalidate all paragraphs and start re-formatting.
bool isFrameEmpty(KWFrame *frame)
Return true if the last frame is empty.
virtual void load(TQDomElement &attributes, bool loadFrames=true)
load from XML - when loading
RelativePosition
used by documentToInternalMouseSelection()
void mainTextHeightChanged()
Tell the text viewmode that the height of the text might have changed.
virtual int availableHeight() const
Return the available height in pixels (sum of all frames' height, with zoom applied) Used to know if ...
void loadOasisContent(const TQDomElement &bodyElem, KoOasisContext &context)
Load the contents of a frame (i.e. the text)
virtual KCommand * setParagLayoutFormatCommand(KoParagLayout *newLayout, int flags, int marginIndex=-1)
Let KoTextFormatInterface set a modified current parag layout.
virtual void saveOasis(KoXmlWriter &writer, KoSavingContext &context, bool saveFrames) const
Save a complete textbox (frame and text)
virtual const KoParagLayout * currentParagLayoutFormat() const
Let KoTextFormatInterface access the current parag layout.
virtual void invalidate()
Invalidate all paragraphs (causes a re-flow of the text upon next redraw)
virtual void deleteFrame(unsigned int num, bool remove=true, bool recalc=true)
Delete a frame from the set of frames this frameSet has.
KWFrame * documentToInternal(const KoPoint &dPoint, TQPoint &iPoint) const
Convert the dPoint in the normal coordinate system (and in pt) into a point (iPoint) in the internal ...
KoTextParag * paragAtLUPos(int yLU) const
Find the parag at the given Y position (in LU)
virtual void adjustMargins(int yp, int h, int reqMinWidth, int &leftMargin, int &rightMargin, int &pageWidth, KoTextParag *parag)
reimplemented from KoTextFlow, implements flowing around frames etc.
int formatVertically(KoTextParag *parag, const TQRect &rect)
Called by KWTextFormatter.
virtual void drawFrameContents(KWFrame *frame, TQPainter *painter, const TQRect &fcrect, const TQColorGroup &cg, bool onlyChanged, bool resetChanged, KWFrameSetEdit *edit, KWViewMode *viewMode)
Implement this one instead of drawFrame to benefit from double-buffering AND transparency handling (p...
void saveOasisContent(KoXmlWriter &writer, KoSavingContext &context) const
Save the contents of a frame (i.e. the text)
virtual KCommand * setFormatCommand(const KoTextFormat *newFormat, int flags, bool zoomFont=false)
Let KoTextFormatInterface set the modified format.
virtual void drawFrame(KWFrame *frame, TQPainter *painter, const TQRect &fcrect, const TQRect &crect, const TQPoint &translationOffset, KWFrame *settingsFrame, const TQColorGroup &cg, bool onlyChanged, bool resetChanged, KWFrameSetEdit *edit, KWViewMode *viewMode, bool drawUnderlyingFrames)
Draw a particular frame of this frameset.
void fixParagWidth(KWTextParag *parag)
Called by KWTextFormatter::postFormat()
bool minMaxInternalOnPage(int pageNum, int &topLU, int &bottomLU) const
Return the min and max LU coordinates for the text in the given page, if the frameset has any frames ...
KWFootNoteFrameSet * insertFootNote(NoteType noteType, KWFootNoteVariable::Numbering numType, const TQString &manualString)
Insert a footnote (var and frameset).
virtual void drawContents(TQPainter *painter, const TQRect &crect, const TQColorGroup &cg, bool onlyChanged, bool resetChanged, KWFrameSetEdit *edit, KWViewMode *viewMode, KWFrameViewManager *frameViewManager)
Paint this frameset.
virtual void finalize()
Called once the frameset has been completely loaded or constructed.
virtual void setVisible(bool v)
set the visibility of the frameset.
~KWTextFrameSet()
Destructor.
void updateViewArea(TQWidget *w, KWViewMode *viewMode, const TQPoint &nPointBottom)
Views notify the KWTextFrameSet of which area of the text they're looking at, so that formatMore() en...
TQString selectedText() const
returns the selected text [without formatting] if hasSelection()
void renumberFootNotes(bool repaint=true)
(Assuming this==main frameset), recalc the foot note numbers
void highlightPortion(KoTextParag *parag, int index, int length, KWCanvas *canvas, bool repaint=true, KDialogBase *dialog=0)
Highlighting support (for search/replace, spellchecking etc.)
virtual bool statistics(TQProgressDialog *progress, ulong &charsWithSpace, ulong &charsWithoutSpace, ulong &words, ulong &sentences, ulong &syllables, ulong &lines, bool selected)
Calculate statistics for this frameset.
void applyStyleChange(KoStyleChangeDefMap changed)
Update the paragraph that use the given style, after this style was changed.
This class extends KoTextParag for KWord-specific formatting stuff, custom items, loading and saving.
Definition KWTextParag.h:41
void load(TQDomElement &attributes)
Load (from KWord-1.3 XML) the paragraph.
void save(TQDomElement &parentElem, bool saveAnchorsFramesets=false)
Save (to KWord-1.3 XML) the whole paragraph.
void setPageBreaking(int pb)
The type of page-breaking behaviour.
The 'edit only one text frameset' view mode.
Definition KWViewMode.h:253
static const int OFFSET
hardcoded distance from the left side this viewmode starts its rendering
Definition KWViewMode.h:290
Abstract base class for KWCanvas's view modes.
Definition KWViewMode.h:48
virtual bool hasFrames()
Does this viewmode know anything about frames?
Definition KWViewMode.h:119
virtual TQSize availableSizeForText(KWTextFrameSet *textfs)
Size (in pixels) of the total area available for text in a given textframeset This is used by KWTextF...
virtual TQPoint normalToView(const TQPoint &nPoint)=0
Normal coord -> view coord.
virtual bool drawSelections()
Should selected text be drawn as such?
Definition KWViewMode.h:99
virtual TQSize contentsSize()=0
Size of the contents area, in pixels.
virtual TQPoint viewToNormal(const TQPoint &vPoint)=0
View coord -> normal coord.
virtual bool drawFrameBackground()
Should frame background be drawn? Usually yes, but not for embedded docs with transparent=true.
Definition KWViewMode.h:102