lib

KoTextView.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001-2006 David Faure <faure@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "KoTextView.h"
21#include "KoTextParag.h"
22#include "KoParagCounter.h"
23#include "KoTextObject.h"
24#include "KoTextViewIface.h"
25#include "KoStyleCollection.h"
26#include "KoBgSpellCheck.h"
27#include "KoVariable.h"
28
29#include <tdelocale.h>
30#include <kstandarddirs.h>
31#include <tdestdaccel.h>
32#include <kdebug.h>
33#include <tdeinstance.h>
34#include <kdatatool.h>
35#include <krun.h>
36#include <tdemessagebox.h>
37#include <kcommand.h>
38#include <kbookmarkmanager.h>
39#include <kbookmark.h>
40#include <kurldrag.h>
41
42#include <tqapplication.h>
43#include <tqtimer.h>
44#include <tqclipboard.h>
45
46class KoTextView::KoTextViewPrivate
47{
48public:
49 KoTextViewPrivate()
50 {
51 m_currentUnicodeNumber = 0;
52 m_backSpeller = 0;
53 }
54
55 void appendDigit( int digit ) { m_currentUnicodeNumber = 10 * m_currentUnicodeNumber + digit; }
56 int currentUnicodeNumber() const { return m_currentUnicodeNumber; }
57 void clearCurrentUnicodeNumber() { m_currentUnicodeNumber = 0; }
58
59 KoBgSpellCheck* m_backSpeller;
60
61private:
62 int m_currentUnicodeNumber; // For the alt+123 feature
63};
64
66{
67 d = new KoTextViewPrivate;
68 m_bReadWrite = true;
69 m_textobj = textobj;
70 dcop=0;
71 connect( m_textobj, TQ_SIGNAL( hideCursor() ), this, TQ_SLOT( hideCursor() ) );
72 connect( m_textobj, TQ_SIGNAL( showCursor() ), this, TQ_SLOT( showCursor() ) );
73 connect( m_textobj, TQ_SIGNAL( setCursor( KoTextCursor * ) ), this, TQ_SLOT( setCursor( KoTextCursor * ) ) );
74 connect( m_textobj, TQ_SIGNAL( updateUI(bool, bool) ), this, TQ_SLOT( updateUI(bool, bool) ) );
75 connect( m_textobj, TQ_SIGNAL( showCurrentFormat() ), this, TQ_SLOT( showCurrentFormat() ) );
76 connect( m_textobj, TQ_SIGNAL( ensureCursorVisible() ), this, TQ_SLOT( ensureCursorVisible() ) );
77
78 m_cursor = new KoTextCursor( m_textobj->textDocument() );
79
80 m_cursorVisible = false;
81
82 showCursor();
83 blinkTimer = new TQTimer( this );
84 connect( blinkTimer, TQ_SIGNAL( timeout() ),
85 this, TQ_SLOT( blinkCursor() ) );
86 if ( TQApplication::cursorFlashTime() > 0 )
87 blinkTimer->start( TQApplication::cursorFlashTime() / 2 );
88
89 dragStartTimer = new TQTimer( this );
90 connect( dragStartTimer, TQ_SIGNAL( timeout() ),
91 this, TQ_SLOT( startDrag() ) );
92
93 m_textobj->formatMore( 2 );
94
95 blinkCursorVisible = false;
96 inDoubleClick = false;
97 mightStartDrag = false;
98 possibleTripleClick = false;
99 afterTripleClick = false;
100 m_currentFormat = 0;
101 m_variablePosition =-1;
102 m_overwriteMode = false;
103 //updateUI( true, true );
104}
105
106KoTextView::~KoTextView()
107{
108 delete m_cursor;
109 delete d;
110 delete dcop;
111 delete blinkTimer;
112 delete dragStartTimer;
113}
114
115KoTextViewIface* KoTextView::dcopObject()
116{
117 if ( !dcop )
118 dcop = new KoTextViewIface( this );
119
120 return dcop;
121}
122
123void KoTextView::terminate(bool removeselection)
124{
125 textObject()->clearUndoRedoInfo();
126 if ( removeselection && textDocument()->removeSelection( KoTextDocument::Standard ) )
127 textObject()->selectionChangedNotify();
128 hideCursor();
129}
130
131void KoTextView::deleteWordRight()
132{
133 if ( textObject()->hasSelection() ) {
134 textObject()->removeSelectedText( m_cursor );
135 return;
136 }
137 textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
138
139 do {
140 m_cursor->gotoRight();
141 } while ( !m_cursor->atParagEnd()
142 && !m_cursor->parag()->at( m_cursor->index() )->c.isSpace() );
143 textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
144 textObject()->removeSelectedText( m_cursor, KoTextDocument::Standard, i18n("Remove Word") );
145}
146
147void KoTextView::deleteWordLeft()
148{
149 if ( textObject()->hasSelection() ) {
150 textObject()->removeSelectedText( m_cursor );
151 return;
152 }
153 textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
154
155 do {
156 m_cursor->gotoLeft();
157 } while ( !m_cursor->atParagStart()
158 && !m_cursor->parag()->at( m_cursor->index()-1 )->c.isSpace() );
159 textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
160 textObject()->removeSelectedText( m_cursor, KoTextDocument::Standard, i18n("Remove Word") );
161}
162
163// Compare with TQTextEdit::keyPressEvent
164void KoTextView::handleKeyPressEvent( TQKeyEvent * e, TQWidget *widget, const TQPoint &pos)
165{
166 textObject()->typingStarted();
167
168 /* bool selChanged = false;
169 for ( int i = 1; i < textDocument()->numSelections(); ++i )
170 selChanged = textDocument()->removeSelection( i ) || selChanged;
171
172 if ( selChanged ) {
173 // m_cursor->parag()->document()->nextDoubleBuffered = true; ######## we need that only if we have nested items/documents
174 textFrameSet()->selectionChangedNotify();
175 }*/
176
177 bool clearUndoRedoInfo = true;
178 if ( TDEShortcut( KKey( e ) ) == TDEStdAccel::deleteWordBack() )
179 {
180 if ( m_cursor->parag()->string()->isRightToLeft() )
181 deleteWordRight();
182 else
183 deleteWordLeft();
184 clearUndoRedoInfo = true;
185 } else if ( TDEShortcut( KKey( e ) ) == TDEStdAccel::deleteWordForward() )
186 {
187 if ( m_cursor->parag()->string()->isRightToLeft() )
188 deleteWordLeft();
189 else
190 deleteWordRight();
191 clearUndoRedoInfo = true;
192 }
193 else
194 switch ( e->key() ) {
195 case Key_Left:
196 case Key_Right: {
197 if (!doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
198 {
199 // a bit hacky, but can't change this without introducing new enum values for move and keeping the
200 // correct semantics and movement for BiDi and non BiDi text.
201 CursorAction a;
202 if ( m_cursor->parag()->string()->isRightToLeft() == (e->key() == Key_Right) )
203 a = e->state() & ControlButton ? MoveWordBackward : MoveBackward;
204 else
205 a = e->state() & ControlButton ? MoveWordForward : MoveForward;
206 moveCursor( a, e->state() & ShiftButton );
207 }
208 break;
209 }
210 case Key_Up:
211 moveCursor( e->state() & ControlButton ? MoveParagUp : MoveUp, e->state() & ShiftButton );
212 break;
213 case Key_Down:
214 moveCursor( e->state() & ControlButton ? MoveParagDown : MoveDown, e->state() & ShiftButton );
215 break;
216 case Key_Home:
217 moveCursor( e->state() & ControlButton ? MoveHome : MoveLineStart, e->state() & ShiftButton );
218 break;
219 case Key_End:
220 if (!doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
221 moveCursor( e->state() & ControlButton ? MoveEnd : MoveLineEnd, e->state() & ShiftButton );
222 break;
223 case Key_Prior:
224 moveCursor( e->state() & ControlButton ? MovePgUp : MoveViewportUp, e->state() & ShiftButton );
225 break;
226 case Key_Next:
227 moveCursor( e->state() & ControlButton ? MovePgDown : MoveViewportDown, e->state() & ShiftButton );
228 break;
229 case Key_Return: case Key_Enter:
230
231 if (!doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
232 if ( (e->state() & (ShiftButton|ControlButton)) == 0 )
233 {
234 if ( textObject()->hasSelection() )
235 textObject()->removeSelectedText( m_cursor );
236 clearUndoRedoInfo = false;
237 textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionReturn );
238 Q_ASSERT( m_cursor->parag()->prev() );
239 if ( m_cursor->parag()->prev() )
240 doAutoFormat( m_cursor, m_cursor->parag()->prev(),
241 m_cursor->parag()->prev()->length() - 1, '\n' );
242 }
243 clearUndoRedoInfo = true;
244 break;
245 case Key_Delete:
246 if ( textObject()->hasSelection() ) {
247 textObject()->removeSelectedText( m_cursor );
248 break;
249 }
250
251 textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionDelete );
252
253 clearUndoRedoInfo = false;
254 break;
255 case Key_Backtab:
256 if (e->state() & ShiftButton && m_cursor->parag() && m_cursor->atParagStart() && m_cursor->parag()->counter() && textDecreaseIndent())
257 break;
258 break;
259 case Key_Backspace:
260 if ( textObject()->hasSelection() ) {
261 textObject()->removeSelectedText( m_cursor );
262 break;
263 }
264 textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionBackspace );
265
266 clearUndoRedoInfo = false;
267 break;
268 case Key_F16: // Copy key on Sun keyboards
269 emit copy();
270 break;
271 case Key_F18: // Paste key on Sun keyboards
272 emit paste();
273 break;
274 case Key_F20: // Cut key on Sun keyboards
275 emit cut();
276 break;
277 case Key_Direction_L: {
278 if ( m_cursor->parag() && m_cursor->parag()->direction() != TQChar::DirL )
279 {
280 KCommand* cmd = textObject()->setParagDirectionCommand( m_cursor, TQChar::DirL );
281 textObject()->emitNewCommand( cmd );
282 }
283 break;
284 }
285 case Key_Direction_R: {
286 if ( m_cursor->parag() && m_cursor->parag()->direction() != TQChar::DirR )
287 {
288 KCommand* cmd = textObject()->setParagDirectionCommand( m_cursor, TQChar::DirR );
289 textObject()->emitNewCommand( cmd );
290 }
291 break;
292 }
293 default: {
294 //kdDebug(32500) << "KoTextView::keyPressEvent ascii=" << e->ascii() << " text=" << e->text()[0].unicode() << " state=" << e->state() << endl;
295 if (e->key() == TQt::Key_Tab)
296 {
297 if (doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
298 break;
299 if ( m_cursor->parag() && m_cursor->atParagStart() && m_cursor->parag()->counter() )
300 {
301 textIncreaseIndent();
302 break;
303 }
304 }
305
306 if ( e->key() == TQt::Key_Space )
307 {
308 if (doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
309 break;
310 }
311 if ( e->text().length() &&
312 ( !e->ascii() || e->ascii() >= 32 ) ||
313 ( e->text() == "\t" && !( e->state() & ControlButton ) ) ) {
314 clearUndoRedoInfo = false;
315 TQString text = e->text();
316
317 if ( d->m_backSpeller ) {
318 d->m_backSpeller->setIntraWordEditing( m_cursor->parag(), m_cursor->index() );
319 }
320
321 // Alt+123 feature
322 if ( ( e->state() & AltButton ) && text[0].isDigit() )
323 {
324 while ( text[0].isDigit() ) {
325 d->appendDigit( text[0].digitValue() );
326 text.remove( 0, 1 );
327 }
328 }
329 if ( !text.isEmpty() )
330 {
331 // Bidi support: need to reverse mirrored chars (e.g. parenthesis)
332 KoTextParag *p = m_cursor->parag();
333 if ( p && p->string() && p->string()->isRightToLeft() ) {
334 TQChar *c = (TQChar *)text.unicode();
335 int l = text.length();
336 while( l-- ) {
337 if ( c->mirrored() )
338 *c = c->mirroredChar();
339 c++;
340 }
341 }
342
343 if( !doIgnoreDoubleSpace( p, m_cursor->index()-1, text[ text.length() - 1 ] ) )
344 {
345 // ###### BUG: with the event compression, typing "kde" and then " k", might not apply
346 // autocorrection like it does for "kde" followed by " " followed by "k". We need to insert
347 // one character at a time, or better, to tell doAutoFormat how many chars to consider...
348 insertText( text );
349 // Don't use 'p' past this point. If we replaced a selection, p could have been deleted (#48999)
350 doAutoFormat( m_cursor, m_cursor->parag(), m_cursor->index() - 1, text[ text.length() - 1 ] );
351 }
352 showToolTipBox(m_cursor->parag(), m_cursor->index()-1, widget,pos);
353 }
354 else
355 removeToolTipCompletion();
356
357 }
358 // We should use TDEAccel instead, to make this configurable !
359 // Well, those are all alternate keys, for keys already configurable (KDE-wide)
360 // and a tdeaccel makes it hard to
361 else
362 {
363 if ( e->state() & ControlButton ) {
364 switch ( e->key() )
365 {
366 case Key_F16: // Copy key on Sun keyboards
367 copy();
368 break;
369 case Key_A:
370 moveCursor( MoveLineStart, e->state() & ShiftButton );
371 break;
372 case Key_E:
373 moveCursor( MoveLineEnd, e->state() & ShiftButton );
374 break;
375 case Key_K:
376 textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionKill );
377 break;
378 case Key_Insert:
379 copy();
380 break;
381 case Key_Space:
382 insertNonbreakingSpace();
383 break;
384 default:
385 clearUndoRedoInfo = false;
386 break;
387 }
388 }
389 else // e.g. just Key_Shift -> don't do anything (#129481)
390 {
391 clearUndoRedoInfo = false;
392 }
393 }
394 break;
395 }
396 }
397
398 if ( clearUndoRedoInfo ) {
399 textObject()->clearUndoRedoInfo();
400 if ( d->m_backSpeller )
401 d->m_backSpeller->setIntraWordEditing( 0, 0 );
402 }
403
404 textObject()->typingDone();
405}
406
407void KoTextView::setOverwriteMode( bool overwriteMode )
408{
409 m_overwriteMode = overwriteMode;
410}
411
412void KoTextView::insertText( const TQString &text )
413{
414 int insertFlags = KoTextObject::DefaultInsertFlags;
415 if ( m_overwriteMode )
416 insertFlags |= KoTextObject::OverwriteMode;
417 textObject()->insert( m_cursor, m_currentFormat, text, i18n("Insert Text"), KoTextDocument::Standard, insertFlags );
418}
419
420void KoTextView::newParagraph()
421{
422 textObject()->insert( m_cursor, m_currentFormat, "\n", i18n("Insert Text"), KoTextDocument::Standard, KoTextObject::CheckNewLine );
423}
424
425void KoTextView::handleKeyReleaseEvent( TQKeyEvent * e )
426{
427 if ( e->key() == Key_Alt && d->currentUnicodeNumber() >= 32 )
428 {
429 TQString text = TQChar( d->currentUnicodeNumber() );
430 d->clearCurrentUnicodeNumber();
431 insertText( text );
432 doAutoFormat( m_cursor, m_cursor->parag(),
433 m_cursor->index() - 1, text[ text.length() - 1 ] );
434 }
435}
436
437void KoTextView::handleImStartEvent( TQIMEvent * )
438{
439 // nothing to do
440}
441
442void KoTextView::handleImComposeEvent( TQIMEvent * e )
443{
444 // remove old preedit
445 if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
446 textDocument()->removeSelection( KoTextDocument::Standard );
447 if ( textDocument()->hasSelection( KoTextDocument::InputMethodPreedit ) )
448 textDocument()->removeSelectedText( KoTextDocument::InputMethodPreedit, m_cursor );
449
450 // insert preedit
451 int preeditStartIdx = m_cursor->index();
452 textDocument()->setSelectionStart( KoTextDocument::InputMethodPreedit, m_cursor );
453 textObject()->insert( m_cursor, m_currentFormat, e->text(), i18n("Insert Text"),
454 KoTextDocument::Standard,
455 KoTextObject::DoNotRepaint/* DO NOT REPAINT CURSOR! */ );
456 textDocument()->setSelectionEnd( KoTextDocument::InputMethodPreedit, m_cursor );
457
458 // selection
459 int preeditSelStart = preeditStartIdx + e->cursorPos();
460 int preeditSelEnd = preeditSelStart + e->selectionLength();
461 m_cursor->setIndex( preeditSelStart );
462 textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
463 m_cursor->setIndex( preeditSelEnd );
464 textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
465
466 // set cursor pos
467 m_cursor->setIndex( preeditSelStart );
468
469 textObject()->emitUpdateUI( true );
470 textObject()->emitShowCursor();
471 textObject()->selectionChangedNotify();
472}
473
474void KoTextView::handleImEndEvent( TQIMEvent * e )
475{
476 // remove old preedit
477 if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
478 textDocument()->removeSelection( KoTextDocument::Standard );
479 if ( textDocument()->hasSelection( KoTextDocument::InputMethodPreedit ) )
480 textDocument()->removeSelectedText( KoTextDocument::InputMethodPreedit, m_cursor );
481
482 insertText( e->text() );
483
484 textObject()->emitUpdateUI( true );
485 textObject()->emitShowCursor();
486 textObject()->selectionChangedNotify();
487}
488
489void KoTextView::completion()
490{
491 (void) doCompletion(m_cursor, m_cursor->parag(),
492 m_cursor->index() - 1);
493}
494
495void KoTextView::moveCursor( CursorAction action, bool select )
496{
497 hideCursor();
498 bool cursorMoved = false;
499 if ( select ) {
500 if ( !textDocument()->hasSelection( KoTextDocument::Standard ) )
501 textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
502 cursorMoved = moveCursor( action );
503 if ( textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor ) ) {
504 textObject()->selectionChangedNotify();
505 }
506 } else {
507 bool redraw = textDocument()->removeSelection( KoTextDocument::Standard );
508 cursorMoved = moveCursor( action );
509 if ( redraw ) {
510 textObject()->selectionChangedNotify();
511 }
512 }
513
514 if ( cursorMoved ) // e.g. not when pressing Ctrl/PgDown after the last parag
515 {
516 ensureCursorVisible();
517 // updateUI( true ); // done by moveCursor
518 }
519 showCursor();
520}
521
522bool KoTextView::moveCursor( CursorAction action )
523{
524 bool cursorMoved = true;
525 switch ( action ) {
526 case MoveBackward:
527 m_cursor->gotoPreviousLetter();
528 break;
529 case MoveWordBackward:
530 m_cursor->gotoPreviousWord();
531 break;
532 case MoveForward:
533 m_cursor->gotoNextLetter();
534 break;
535 case MoveWordForward:
536 m_cursor->gotoNextWord();
537 break;
538 case MoveUp:
539 m_cursor->gotoUp();
540 break;
541 case MoveDown:
542 m_cursor->gotoDown();
543 break;
544 case MoveViewportUp:
545 cursorMoved = pgUpKeyPressed();
546 break;
547 case MoveViewportDown:
548 cursorMoved = pgDownKeyPressed();
549 break;
550 case MovePgUp:
552 break;
553 case MovePgDown:
555 break;
556 case MoveLineStart:
557 m_cursor->gotoLineStart();
558 break;
559 case MoveHome:
560 m_cursor->gotoHome();
561 break;
562 case MoveLineEnd:
563 m_cursor->gotoLineEnd();
564 break;
565 case MoveEnd:
566 textObject()->ensureFormatted( textDocument()->lastParag() );
567 m_cursor->gotoEnd();
568 break;
569 case MoveParagUp: {
570 KoTextParag * parag = m_cursor->parag()->prev();
571 if ( m_cursor->index()==0 && parag )
572 {
573 m_cursor->setParag( parag );
574 m_cursor->setIndex( 0 );
575 }
576 else m_cursor->setIndex( 0 );
577 } break;
578 case MoveParagDown: {
579 KoTextParag * parag = m_cursor->parag()->next();
580 if ( parag )
581 {
582 m_cursor->setParag( parag );
583 m_cursor->setIndex( 0 );
584 }
585 } break;
586 }
587
588 updateUI( true );
589 return cursorMoved;
590}
591
592KoTextCursor KoTextView::selectWordUnderCursor( const KoTextCursor& cursor, int selectionId )
593{
594 KoTextCursor c1 = cursor;
595 KoTextCursor c2 = cursor;
596 if ( cursor.index() > 0 && !cursor.parag()->at( cursor.index()-1 )->c.isSpace() )
597 c1.gotoWordLeft();
598 if ( !cursor.parag()->at( cursor.index() )->c.isSpace() && !cursor.atParagEnd() )
599 c2.gotoWordRight();
600
601 // The above is almost correct, but gotoWordRight also skips the spaces/punctuations
602 // until the next word. So the 'word under cursor' contained e.g. that trailing space.
603 // To be on the safe side, we skip spaces/punctuations on both sides:
604 KoTextString *s = cursor.parag()->string();
605 bool beginFound = false;
606 for ( int i = c1.index(); i< c2.index(); i++)
607 {
608 const TQChar ch = s->at(i).c;
609 // This list comes from KoTextCursor::gotoPreviousWord.
610 // Can't use TQChar::isPunct since "'" and "-" are not word separators
611 const bool isWordDelimiter = ch.isSpace()
612 || ch.category() == TQChar::Punctuation_Open // e.g. '('
613 || ch.category() == TQChar::Punctuation_Close // e.g. ')'
614 || ch.category() == TQChar::Punctuation_Other // see http://www.fileformat.info/info/unicode/category/Po/list.htm
615 ;
616
617 if( !beginFound && !isWordDelimiter )
618 {
619 c1.setIndex(i);
620 beginFound = true;
621 }
622 else if ( beginFound && isWordDelimiter )
623 {
624 c2.setIndex(i);
625 break;
626 }
627 }
628
629 textDocument()->setSelectionStart( selectionId, &c1 );
630 textDocument()->setSelectionEnd( selectionId, &c2 );
631 return c2;
632}
633
634KoTextCursor KoTextView::selectParagUnderCursor( const KoTextCursor& cursor, int selectionId, bool copyAndNotify )
635{
636 KoTextCursor c1 = cursor;
637 KoTextCursor c2 = cursor;
638 c1.setIndex(0);
639 c2.setIndex(c1.parag()->string()->length() - 1);
640 textDocument()->setSelectionStart( selectionId, &c1 );
641 textDocument()->setSelectionEnd( selectionId, &c2 );
642 if ( copyAndNotify )
643 {
644 textObject()->selectionChangedNotify();
645 // Copy the selection.
646 TQApplication::clipboard()->setSelectionMode( true );
647 emit copy();
648 TQApplication::clipboard()->setSelectionMode( false );
649 }
650 return c2;
651}
652
653void KoTextView::extendParagraphSelection( const TQPoint& iPoint )
654{
655 hideCursor();
656 KoTextCursor oldCursor = *m_cursor;
657 placeCursor( iPoint );
658
659 bool redraw = false;
660 if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
661 {
662 redraw = textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
663 if ( textDocument()->isSelectionSwapped( KoTextDocument::Standard ) )
664 m_cursor->setIndex( 0 );
665 else
666 m_cursor->setIndex( m_cursor->parag()->string()->length() - 1 );
667 textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
668 }
669 //else // it may be that the initial click was out of the frame
670 // textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
671
672 if ( redraw )
673 textObject()->selectionChangedNotify( false );
674
675 showCursor();
676}
677
678TQString KoTextView::wordUnderCursor( const KoTextCursor& cursor )
679{
680 selectWordUnderCursor( cursor, KoTextDocument::Temp );
681 TQString text = textObject()->selectedText( KoTextDocument::Temp );
682 bool hasCustomItems = textObject()->selectionHasCustomItems( KoTextDocument::Temp );
683 textDocument()->removeSelection( KoTextDocument::Temp );
684 if( !hasCustomItems )
685 return text;
686 return TQString();
687}
688
689bool KoTextView::handleMousePressEvent( TQMouseEvent *e, const TQPoint &iPoint, bool canStartDrag, bool insertDirectCursor )
690{
691 bool addParag = false;
692 mightStartDrag = false;
693 hideCursor();
694
695 if (possibleTripleClick)
696 {
697 handleMouseTripleClickEvent( e, iPoint );
698 return addParag;
699 }
700
701 KoTextCursor oldCursor = *m_cursor;
702 addParag = placeCursor( iPoint, insertDirectCursor&& isReadWrite() );
703 ensureCursorVisible();
704
705 if ( e->button() != TQt::LeftButton )
706 {
707 showCursor();
708 return addParag;
709 }
710
711 KoLinkVariable* lv = linkVariable();
712 if ( lv && openLink( lv ) )
713 {
714 return addParag;
715 }
716
717 KoTextDocument * textdoc = textDocument();
718 if ( canStartDrag && textdoc->inSelection( KoTextDocument::Standard, iPoint ) ) {
719 mightStartDrag = true;
720 m_textobj->emitShowCursor();
721 dragStartTimer->start( TQApplication::startDragTime(), true );
722 dragStartPos = e->pos();
723 return addParag;
724 }
725
726 bool redraw = false;
727 if ( textdoc->hasSelection( KoTextDocument::Standard ) ) {
728 if ( !( e->state() & ShiftButton ) ) {
729 redraw = textdoc->removeSelection( KoTextDocument::Standard );
730 textdoc->setSelectionStart( KoTextDocument::Standard, m_cursor );
731 } else {
732 redraw = textdoc->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
733 }
734 } else {
735 if ( !( e->state() & ShiftButton ) ) {
736 textdoc->setSelectionStart( KoTextDocument::Standard, m_cursor );
737 } else {
738 textdoc->setSelectionStart( KoTextDocument::Standard, &oldCursor );
739 redraw = textdoc->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
740 }
741 }
742
743 //kdDebug(32500) << "KoTextView::mousePressEvent redraw=" << redraw << endl;
744 if ( !redraw ) {
745 showCursor();
746 } else {
747 textObject()->selectionChangedNotify();
748 }
749 return addParag;
750}
751
752void KoTextView::handleMouseMoveEvent( TQMouseEvent*, const TQPoint& iPoint )
753{
754 hideCursor();
755 KoTextCursor oldCursor = *m_cursor;
756 placeCursor( iPoint );
757
758 // Double click + mouse still down + moving the mouse selects full words.
759 if ( inDoubleClick ) {
760 KoTextCursor cl = *m_cursor;
761 cl.gotoWordLeft();
762 KoTextCursor cr = *m_cursor;
763 cr.gotoWordRight();
764
765 int diff = TQABS( oldCursor.parag()->at( oldCursor.index() )->x - iPoint.x() );
766 int ldiff = TQABS( cl.parag()->at( cl.index() )->x - iPoint.x() );
767 int rdiff = TQABS( cr.parag()->at( cr.index() )->x - iPoint.x() );
768
769 if ( m_cursor->parag()->lineStartOfChar( m_cursor->index() ) !=
770 oldCursor.parag()->lineStartOfChar( oldCursor.index() ) )
771 diff = 0xFFFFFF;
772
773 if ( rdiff < diff && rdiff < ldiff )
774 *m_cursor = cr;
775 else if ( ldiff < diff && ldiff < rdiff )
776 *m_cursor = cl;
777 else
778 *m_cursor = oldCursor;
779 }
780
781 bool redraw = false;
782 if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
783 redraw = textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
784 else // it may be that the initial click was out of the frame
785 textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
786
787 if ( redraw )
788 textObject()->selectionChangedNotify( false );
789
790 showCursor();
791}
792
793void KoTextView::handleMouseReleaseEvent()
794{
795 if ( dragStartTimer->isActive() )
796 dragStartTimer->stop();
797 if ( mightStartDrag ) {
798 textObject()->selectAll( false );
799 mightStartDrag = false;
800 }
801 else
802 {
803 if ( textDocument()->selectionStartCursor( KoTextDocument::Standard ) == textDocument()->selectionEndCursor( KoTextDocument::Standard ) )
804 {
805 textDocument()->removeSelection( KoTextDocument::Standard );
806 }
807
808 textObject()->selectionChangedNotify();
809
810 // Copy the selection.
811 TQApplication::clipboard()->setSelectionMode( true );
812 emit copy();
813 TQApplication::clipboard()->setSelectionMode( false );
814 }
815
816 inDoubleClick = false;
817 m_textobj->emitShowCursor();
818}
819
820void KoTextView::handleMouseDoubleClickEvent( TQMouseEvent*ev, const TQPoint& i )
821{
822 //after a triple click it's not a double click but a simple click
823 //but as triple click didn't exist it's necessary to do it.
824 if(afterTripleClick)
825 {
826 handleMousePressEvent( ev, i );
827 return;
828 }
829
830 inDoubleClick = true;
831 *m_cursor = selectWordUnderCursor( *m_cursor );
832 textObject()->selectionChangedNotify();
833 // Copy the selection.
834 TQApplication::clipboard()->setSelectionMode( true );
835 emit copy();
836 TQApplication::clipboard()->setSelectionMode( false );
837
838 possibleTripleClick=true;
839
840 TQTimer::singleShot(TQApplication::doubleClickInterval(),this,TQ_SLOT(tripleClickTimeout()));
841}
842
843void KoTextView::tripleClickTimeout()
844{
845 possibleTripleClick=false;
846}
847
848void KoTextView::handleMouseTripleClickEvent( TQMouseEvent*ev, const TQPoint& /* Currently unused */ )
849{
850 if ( ev->button() != TQt::LeftButton)
851 {
852 showCursor();
853 return;
854 }
855 afterTripleClick= true;
856 inDoubleClick = false;
857 *m_cursor = selectParagUnderCursor( *m_cursor );
858 TQTimer::singleShot(TQApplication::doubleClickInterval(),this,TQ_SLOT(afterTripleClickTimeout()));
859}
860
861void KoTextView::afterTripleClickTimeout()
862{
863 afterTripleClick=false;
864}
865
866bool KoTextView::maybeStartDrag( TQMouseEvent* e )
867{
868 if ( mightStartDrag ) {
869 dragStartTimer->stop();
870 if ( ( e->pos() - dragStartPos ).manhattanLength() > TQApplication::startDragDistance() )
871 startDrag();
872 return true;
873 }
874 return false;
875}
876
877bool KoTextView::insertParagraph(const TQPoint &pos)
878{
879 KoTextParag *last = textDocument()->lastParag();
880 KoTextFormat *f = 0;
881 KoParagStyle *style = last->style();
882 KoParagCounter *counter = last->counter();
883 int diff = (pos.y()- textDocument()->height());
884 f = last->at( last->length()-1 )->format();
885 int height =f->height();
886 int nbParag = (diff / height);
887 TQFontMetrics fm = f->refFontMetrics();
888 for (int i = 0; i < nbParag ;i++)
889 {
890 KoTextParag *s=textDocument()->createParag( textDocument(), last );
891 s->setFormat( 0, 1, f, true );
892 if ( style )
893 s->setStyle( style );
894 s->setCounter( counter );
895 last = s;
896 }
897 bool createParag = (nbParag > 0 );
898 if ( createParag )
899 {
900 if ( pos.x() + f->width(' ') >= textDocument()->width())
901 {
902 //FIXME me bidi.
903 //change parag alignment => right alignment
904 last->setAlignment( TQt::AlignRight );
905 }
906 else
907 {
908 int nbSpace = pos.x()/f->width(' ');
909 TQString tmp;
910 for (int i = 0; i< nbSpace; i++)
911 {
912 tmp+=' ';
913 }
914 last->insert( 0, tmp );
915 }
916 }
917 return createParag;
918
919}
920
921bool KoTextView::placeCursor( const TQPoint &pos, bool insertDirectCursor )
922{
923 bool addParag = false;
924 if ( insertDirectCursor && (pos.y()>textDocument()->height()) )
925 addParag = insertParagraph(pos);
926 KoTextParag *s = 0L;
927 if ( addParag )
928 s = textDocument()->lastParag();
929 else
930 s = textDocument()->firstParag();
931 m_cursor->place( pos, s, false, &m_variablePosition );
932 if ( m_variablePosition != -1 )
933 kdDebug() << k_funcinfo << " m_variablePosition set to " << m_variablePosition << endl;
934 updateUI( true );
935 return addParag;
936}
937
938void KoTextView::blinkCursor()
939{
940 //kdDebug(32500) << "KoTextView::blinkCursor m_cursorVisible=" << m_cursorVisible
941 // << " blinkCursorVisible=" << blinkCursorVisible << endl;
942 if ( !m_cursorVisible )
943 return;
944 bool cv = m_cursorVisible;
945 blinkCursorVisible = !blinkCursorVisible;
946 drawCursor( blinkCursorVisible );
947 m_cursorVisible = cv;
948}
949
950void KoTextView::drawCursor( bool visible )
951{
952 m_cursorVisible = visible;
953 // The rest is up to the app ;)
954}
955
956void KoTextView::focusInEvent()
957{
958 if ( TQApplication::cursorFlashTime() > 0 )
959 blinkTimer->start( TQApplication::cursorFlashTime() / 2 );
960 showCursor();
961}
962
963void KoTextView::focusOutEvent()
964{
965 blinkTimer->stop();
966 hideCursor();
967}
968
969/*void KoTextView::setFormat( KoTextFormat * newFormat, int flags, bool zoomFont)
970{
971 textObject()->setFormat( m_cursor, m_currentFormat, newFormat, flags, zoomFont );
972}*/
973
974KCommand* KoTextView::setFormatCommand( const KoTextFormat * newFormat, int flags, bool zoomFont)
975{
976 return textObject()->setFormatCommand( m_cursor, &m_currentFormat, newFormat, flags, zoomFont );
977}
978
979void KoTextView::dragStarted()
980{
981 mightStartDrag = false;
982 inDoubleClick = false;
983}
984
985void KoTextView::applyStyle( const KoParagStyle * style )
986{
987 if ( style )
988 {
989 textObject()->applyStyle( m_cursor, style );
990 showCurrentFormat();
991 }
992}
993
994void KoTextView::updateUI( bool updateFormat, bool /*force*/ )
995{
996 // Update UI - only for those items which have changed
997
998 if ( updateFormat )
999 {
1000 int i = cursor()->index();
1001 if ( i > 0 )
1002 --i;
1003#ifdef DEBUG_FORMATS
1004 if ( currentFormat() )
1005 kdDebug(32500) << "KoTextView::updateUI old currentFormat=" << currentFormat()
1006 << " " << currentFormat()->key()
1007 << " parag format=" << cursor()->parag()->at( i )->format()->key() << endl;
1008 else
1009 kdDebug(32500) << "KoTextView::updateUI old currentFormat=0" << endl;
1010#endif
1011 if ( !currentFormat() || currentFormat()->key() != cursor()->parag()->at( i )->format()->key() )
1012 {
1013 if ( currentFormat() )
1014 currentFormat()->removeRef();
1015#ifdef DEBUG_FORMATS
1016 kdDebug(32500) << "Setting currentFormat from format " << cursor()->parag()->at( i )->format()
1017 << " ( character " << i << " in paragraph " << cursor()->parag()->paragId() << " )" << endl;
1018#endif
1019 setCurrentFormat( textDocument()->formatCollection()->format( cursor()->parag()->at( i )->format() ) );
1020 if ( currentFormat()->isMisspelled() ) {
1021 KoTextFormat fNoMisspelled( *currentFormat() );
1022 fNoMisspelled.setMisspelled( false );
1023 currentFormat()->removeRef();
1024 setCurrentFormat( textDocument()->formatCollection()->format( &fNoMisspelled ) );
1025 }
1026 showCurrentFormat();
1027 }
1028 }
1029}
1030
1031void KoTextView::showCurrentFormat()
1032{
1033 //kdDebug(32500) << "KoTextView::showCurrentFormat currentFormat=" << currentFormat() << " " << currentFormat()->key() << endl;
1034 KoTextFormat format = *currentFormat();
1035 //format.setPointSize( textObject()->docFontSize( currentFormat() ) ); // "unzoom" the font size
1036 showFormat( &format );
1037}
1038
1039KCommand * KoTextView::setCounterCommand( const KoParagCounter & counter )
1040{
1041 return textObject()->setCounterCommand( m_cursor, counter );
1042}
1043KCommand * KoTextView::setAlignCommand( int align )
1044{
1045 return textObject()->setAlignCommand( m_cursor, align );
1046}
1047KCommand * KoTextView::setLineSpacingCommand( double spacing, KoParagLayout::SpacingType _type)
1048{
1049 return textObject()->setLineSpacingCommand( m_cursor, spacing, _type);
1050}
1051KCommand * KoTextView::setBordersCommand( const KoBorder& leftBorder, const KoBorder& rightBorder, const KoBorder& bottomBorder, const KoBorder& topBorder )
1052{
1053 return textObject()->setBordersCommand( m_cursor, leftBorder, rightBorder, bottomBorder, topBorder );
1054}
1055KCommand * KoTextView::setJoinBordersCommand( bool join )
1056{
1057 return textObject()->setJoinBordersCommand( m_cursor, join );
1058}
1059KCommand * KoTextView::setMarginCommand( TQStyleSheetItem::Margin m, double margin )
1060{
1061 return textObject()->setMarginCommand( m_cursor, m, margin );
1062}
1063KCommand * KoTextView::setTabListCommand( const KoTabulatorList & tabList )
1064{
1065 return textObject()->setTabListCommand( m_cursor, tabList );
1066}
1067KCommand * KoTextView::setBackgroundColorCommand( const TQColor & color )
1068{
1069 return textObject()->setBackgroundColorCommand( m_cursor, color );
1070}
1071
1072KoTextDocument * KoTextView::textDocument() const
1073{
1074 return textObject()->textDocument();
1075}
1076
1077KoVariable *KoTextView::variable()
1078{
1079 if ( m_variablePosition < 0 )
1080 return 0;
1081 // Can't use m_cursor here, it could be before or after the variable, depending on which half of it was clicked
1082 return textObject()->variableAtPosition( m_cursor->parag(), m_variablePosition );
1083}
1084
1085KoLinkVariable * KoTextView::linkVariable()
1086{
1087 return dynamic_cast<KoLinkVariable *>(variable());
1088}
1089
1090TQPtrList<TDEAction> KoTextView::dataToolActionList(TDEInstance * instance, const TQString& word, bool & _singleWord )
1091{
1092 m_singleWord = false;
1093 m_wordUnderCursor = TQString();
1094 TQString text;
1095 if ( textObject()->hasSelection() )
1096 {
1097 text = textObject()->selectedText();
1098 if ( text.find(' ') == -1 && text.find('\t') == -1 && text.find(KoTextObject::customItemChar()) == -1 )
1099 {
1100 m_singleWord = true;
1101 }
1102 else
1103 {
1104 m_singleWord = false;
1105 //laurent : don't try to search thesaurus when we have a customItemChar.
1106 if( text.find(KoTextObject::customItemChar())!=-1)
1107 text = TQString();
1108 }
1109 }
1110 else // No selection -> use word under cursor
1111 {
1112 if ( !word.isEmpty() )
1113 {
1114 m_singleWord = true;
1115 m_wordUnderCursor = word;
1116 text = word;
1117 }
1118 }
1119
1120 if ( text.isEmpty() || textObject()->protectContent()) // Nothing to apply a tool to
1121 return TQPtrList<TDEAction>();
1122
1123 // Any tool that works on plain text is relevant
1124 TQValueList<KDataToolInfo> tools;
1125 tools +=KDataToolInfo::query( "TQString", "text/plain", instance );
1126
1127 // Add tools that work on a single word if that is the case
1128 if ( m_singleWord )
1129 {
1130 _singleWord = true;
1131 tools += KDataToolInfo::query( "TQString", "application/x-singleword", instance );
1132 }
1133 // Maybe one day we'll have tools that use libkotext (or qt3's qrt), to act on formatted text
1134 tools += KDataToolInfo::query( "KoTextString", "application/x-qrichtext", instance );
1135
1136 return KDataToolAction::dataToolActionList( tools, this, TQ_SLOT( slotToolActivated( const KDataToolInfo &, const TQString & ) ) );
1137}
1138
1139TQString KoTextView::currentWordOrSelection() const
1140{
1141 if ( textObject()->hasSelection() )
1142 return textObject()->selectedText();
1143 else
1144 return m_wordUnderCursor;
1145}
1146
1147void KoTextView::slotToolActivated( const KDataToolInfo & info, const TQString & command )
1148{
1149 KDataTool* tool = info.createTool( );
1150 if ( !tool )
1151 {
1152 kdWarning() << "Could not create Tool !" << endl;
1153 return;
1154 }
1155
1156 kdDebug(32500) << "KWTextFrameSetEdit::slotToolActivated command=" << command
1157 << " dataType=" << info.dataType() << endl;
1158
1159 TQString text;
1160 if ( textObject()->hasSelection() )
1161 text = textObject()->selectedText();
1162 else
1163 text = m_wordUnderCursor;
1164
1165 // Preferred type is richtext
1166 TQString mimetype = "application/x-qrichtext";
1167 TQString datatype = "KoTextString";
1168 // If unsupported, try text/plain
1169 if ( !info.mimeTypes().contains( mimetype ) )
1170 {
1171 mimetype = "text/plain";
1172 datatype = "TQString";
1173 }
1174 // If unsupported (and if we have a single word indeed), try application/x-singleword
1175 if ( !info.mimeTypes().contains( mimetype ) && m_singleWord )
1176 mimetype = "application/x-singleword";
1177
1178 kdDebug(32500) << "Running tool with datatype=" << datatype << " mimetype=" << mimetype << endl;
1179
1180 TQString origText = text;
1181 if ( tool->run( command, &text, datatype, mimetype) )
1182 {
1183 kdDebug(32500) << "Tool ran. Text is now " << text << endl;
1184 if ( origText != text )
1185 {
1186 if ( !textObject()->hasSelection() )
1187 {
1188 // Warning: ok for now, but wrong cursor if RMB doesn't place cursor anymore
1189 selectWordUnderCursor( *m_cursor );
1190 }
1191 // replace selection with 'text'
1192 textObject()->emitNewCommand( textObject()->replaceSelectionCommand(
1193 cursor(), text, i18n("Replace Word") ));
1194 }
1195 }
1196 delete tool;
1197}
1198
1199bool KoTextView::openLink( KoLinkVariable* variable )
1200{
1201 kdDebug() << k_funcinfo << variable->url() << endl;
1202 KURL url( variable->url() );
1203 if( url.isValid() )
1204 {
1205 (void) new KRun( url );
1206 return true;
1207 }
1208 else
1209 {
1210 KMessageBox::sorry( 0, i18n("%1 is not a valid link.").arg( variable->url() ) );
1211 return false;
1212 }
1213}
1214
1215
1216void KoTextView::insertSoftHyphen()
1217{
1218 textObject()->insert( cursor(), currentFormat(), TQChar(0xad) /* see TQRichText */,
1219 i18n("Insert Soft Hyphen") );
1220}
1221
1222void KoTextView::insertLineBreak()
1223{
1224 textObject()->insert( cursor(), currentFormat(), TQChar('\n'),
1225 i18n("Insert Line Break") );
1226}
1227
1228void KoTextView::insertNonbreakingSpace()
1229{
1230 textObject()->insert( cursor(), currentFormat(), TQChar(0xa0) /* see TQRichText */,
1231 i18n("Insert Non-Breaking Space") );
1232}
1233
1234void KoTextView::insertNonbreakingHyphen()
1235{
1236 textObject()->insert( cursor(), currentFormat(), TQChar(0x2013),
1237 i18n("Insert Non-Breaking Hyphen") );
1238}
1239
1240void KoTextView::insertSpecialChar(TQChar _c, const TQString& font)
1241{
1242 KoTextFormat * newFormat = new KoTextFormat(*currentFormat());
1243 newFormat->setFamily( font );
1244 if ( textObject()->hasSelection() )
1245 {
1246 KoTextFormat * lastFormat = currentFormat();
1247
1248 KCommand *cmd = textObject()->setFormatCommand( cursor(), &lastFormat, newFormat, KoTextFormat::Family );
1249
1250 KMacroCommand* macroCmd = new KMacroCommand( i18n("Insert Special Char") );
1251 macroCmd->addCommand( cmd );
1252 macroCmd->addCommand( textObject()->replaceSelectionCommand(
1253 cursor(), _c, TQString()) );
1254 textObject()->emitNewCommand( macroCmd );
1255 }
1256 else
1257 {
1258 textObject()->insert( cursor(), newFormat, _c, i18n("Insert Special Char"));
1259 delete newFormat;
1260 }
1261}
1262
1264{
1265 KoTextParag * parag = m_cursor->parag();
1266 return &(parag->paragLayout());
1267}
1268
1269bool KoTextView::rtl() const
1270{
1271 return m_cursor->parag()->string()->isRightToLeft();
1272}
1273
1274KCommand* KoTextView::setParagLayoutFormatCommand( KoParagLayout *newLayout, int flags, int marginIndex )
1275{
1276 return textObject()->setParagLayoutCommand( m_cursor, *newLayout, KoTextDocument::Standard,
1277 flags, marginIndex, true /*createUndoRedo*/ );
1278}
1279
1280// Heading1 -> Heading2 -> Heading3 -> normal -> 1 -> 1.1 -> 1.1.1
1281void KoTextView::increaseNumberingLevel( const KoStyleCollection* styleCollection )
1282{
1283 // TODO: do this for each paragraph in the selection
1284 KoParagStyle* style = 0;
1285 int level = 0;
1286 KoParagCounter* counter = m_cursor->parag()->counter();
1287 if ( counter )
1288 level = counter->depth() + 1;
1289 if ( m_cursor->parag()->style()->isOutline() )
1290 {
1291 TQValueVector<KoParagStyle *> outlineStyles = styleCollection->outlineStyles();
1292 while ( level < 10 && !style ) {
1293 style = outlineStyles[ level ];
1294 ++level;
1295 }
1296 if ( !style ) // no lower-level heading exists, use standard style
1297 style = styleCollection->defaultStyle();
1298 }
1299 else // non-outline, just a numbered list
1300 {
1301 // Try to find a style with this depth, to know if the user wants display-levels etc.
1302 style = styleCollection->numberedStyleForLevel( level );
1303 if ( !style ) { // not found. Make the change though.
1304 KoParagCounter c;
1305 if (counter) {
1306 c = *counter;
1307 c.setDepth( level );
1308 c.setDisplayLevels( c.displayLevels() + 1 );
1309 } else {
1310 // Start a simple numbered list.
1311 c.setNumbering(KoParagCounter::NUM_LIST);
1312 c.setStyle(KoParagCounter::STYLE_NUM);
1313 }
1314 KCommand* command = textObject()->setCounterCommand( m_cursor, c );
1315 textObject()->emitNewCommand( command );
1316 }
1317 }
1318 if ( style ) // can't be 0
1319 textObject()->applyStyle( m_cursor, style );
1320}
1321
1322// 1.1.1 -> 1.1 -> 1 -> normal -> Heading3 -> Heading2 -> Heading1
1323void KoTextView::decreaseNumberingLevel( const KoStyleCollection* styleCollection )
1324{
1325 // TODO: do this for each paragraph in the selection
1326 KoParagCounter* counter = m_cursor->parag()->counter();
1327 int level = 9;
1328 if ( counter )
1329 level = counter->depth() - 1;
1330 KoParagStyle* style = 0;
1331 if ( m_cursor->parag()->style()->isOutline() || !counter ) // heading or normal
1332 {
1333 if ( level == -1 ) // nothing higher than Heading1
1334 return;
1335 TQValueVector<KoParagStyle *> outlineStyles = styleCollection->outlineStyles();
1336 while ( level >= 0 && !style ) {
1337 style = outlineStyles[ level ];
1338 --level;
1339 }
1340 }
1341 else // non-outline, numbered list
1342 {
1343 if ( level == -1 )
1344 style = styleCollection->defaultStyle();
1345 else
1346 {
1347 style = styleCollection->numberedStyleForLevel( level );
1348 if ( !style ) { // not found. Make the change though.
1349 KoParagCounter c( *counter );
1350 c.setDepth( level );
1351 if ( c.displayLevels() > 1 ) {
1352 c.setDisplayLevels( c.displayLevels() - 1 );
1353 }
1354 KCommand* command = textObject()->setCounterCommand( m_cursor, c );
1355 textObject()->emitNewCommand( command );
1356 }
1357 }
1358 }
1359 if ( style )
1360 textObject()->applyStyle( m_cursor, style );
1361}
1362
1363KCommand *KoTextView::setChangeCaseOfTextCommand(KoChangeCaseDia::TypeOfCase _type)
1364{
1365 TQString text;
1366 if ( textObject()->hasSelection() )
1367 text = textObject()->selectedText();
1368 if(!text.isEmpty())
1369 return textObject()->changeCaseOfText(cursor(), _type);
1370 else
1371 return 0L;
1372}
1373
1374KCommand *KoTextView::prepareDropMove( KoTextCursor dropCursor )
1375{
1376 Q_ASSERT( textDocument()->hasSelection( KoTextDocument::Standard ) );
1377 // Dropping into the selection itself ?
1378 KoTextCursor startSel = textDocument()->selectionStartCursor( KoTextDocument::Standard );
1379 KoTextCursor endSel = textDocument()->selectionEndCursor( KoTextDocument::Standard );
1380 bool inSelection = false;
1381 if ( startSel.parag() == endSel.parag() )
1382 inSelection = dropCursor.parag() == startSel.parag()
1383 && dropCursor.index() >= startSel.index()
1384 && dropCursor.index() <= endSel.index();
1385 else
1386 {
1387 // Looking at first line first:
1388 inSelection = dropCursor.parag() == startSel.parag() && dropCursor.index() >= startSel.index();
1389 if ( !inSelection )
1390 {
1391 // Look at all other paragraphs except last one
1392 KoTextParag *p = startSel.parag()->next();
1393 while ( !inSelection && p && p != endSel.parag() )
1394 {
1395 inSelection = ( p == dropCursor.parag() );
1396 p = p->next();
1397 }
1398 // Look at last paragraph
1399 if ( !inSelection )
1400 inSelection = dropCursor.parag() == endSel.parag() && dropCursor.index() <= endSel.index();
1401 }
1402 }
1403 if ( inSelection || m_textobj->protectContent() )
1404 {
1405 textDocument()->removeSelection( KoTextDocument::Standard );
1406 textObject()->selectionChangedNotify();
1407 hideCursor();
1408 *cursor() = dropCursor;
1409 showCursor();
1410 ensureCursorVisible();
1411 return 0L;
1412 }
1413 if ( textObject()->protectContent() )
1414 {
1415 textDocument()->removeSelection( KoTextDocument::Standard );
1416 textObject()->selectionChangedNotify();
1417 }
1418 // Tricky. We don't want to do the placeCursor after removing the selection
1419 // (the user pointed at some text with the old selection in place).
1420 // However, something got deleted in our parag, dropCursor's index needs adjustment.
1421 if ( endSel.parag() == dropCursor.parag() )
1422 {
1423 // Does the selection starts before (other parag or same parag) ?
1424 if ( startSel.parag() != dropCursor.parag() || startSel.index() < dropCursor.index() )
1425 {
1426 // If other -> endSel.parag() will get deleted. The final position is in startSel.parag(),
1427 // where the selection started + how much after the end we are. Make a drawing :)
1428 // If same -> simply move back by how many chars we've deleted. Funny thing is, it's the same formula.
1429 int dropIndex = dropCursor.index();
1430 dropCursor.setParag( startSel.parag() );
1431 // If dropCursor - endSel < 0, selection ends after, we're dropping into selection (no-op)
1432 dropCursor.setIndex( dropIndex - TQMIN( endSel.index(), dropIndex ) + startSel.index() );
1433 }
1434 kdDebug(32500) << "dropCursor: parag=" << dropCursor.parag()->paragId() << " index=" << dropCursor.index() << endl;
1435 }
1436 KCommand* cmd = textObject()->removeSelectedTextCommand( cursor(), KoTextDocument::Standard );
1437
1438 hideCursor();
1439 *cursor() = dropCursor;
1440 showCursor();
1441
1442 return cmd;
1443}
1444
1445
1446void KoTextView::copyTextOfComment()
1447{
1448 KoNoteVariable *var = dynamic_cast<KoNoteVariable *>( variable() );
1449 if( var )
1450 {
1451 KURL::List lst;
1452 lst.append( var->note() );
1453 TQApplication::clipboard()->setSelectionMode(true);
1454 TQApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
1455 TQApplication::clipboard()->setSelectionMode(false);
1456 TQApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
1457 }
1458}
1459
1460void KoTextView::removeComment()
1461{
1462 KoNoteVariable *var = dynamic_cast<KoNoteVariable *>( variable() );
1463 if( var )
1464 {
1465 m_cursor->setIndex( m_variablePosition );
1466 textDocument()->setSelectionStart( KoTextDocument::Temp, m_cursor );
1467 m_cursor->setIndex( m_variablePosition + 1 );
1468 textDocument()->setSelectionEnd( KoTextDocument::Temp, m_cursor );
1469 textObject()->removeSelectedText( m_cursor, KoTextDocument::Temp, i18n("Remove Comment") );
1470 }
1471}
1472
1473KoParagStyle * KoTextView::createStyleFromSelection(const TQString & name)
1474{
1475 KoTextCursor cursor = *m_cursor;
1476 if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
1477 cursor = textDocument()->selectionStartCursor( KoTextDocument::Standard );
1478 KoParagStyle * style = new KoParagStyle (name);
1479 KoParagLayout layout(cursor.parag()->paragLayout());
1480 layout.style = style;
1481 style->setFollowingStyle( style );
1482 style->format() = *(cursor.parag()->at(cursor.index())->format());
1483
1484 style->paragLayout() = layout;
1485 // Select this new style - hmm only the parag layout, we don't want to erase any text-formatting
1486 cursor.parag()->setParagLayout( style->paragLayout() );
1487 return style;
1488}
1489
1490void KoTextView::updateStyleFromSelection( KoParagStyle* style )
1491{
1492 KoTextCursor cursor = *m_cursor;
1493 if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
1494 cursor = textDocument()->selectionStartCursor( KoTextDocument::Standard );
1495
1496 style->paragLayout() = cursor.parag()->paragLayout();
1497 style->paragLayout().style = style;
1498 style->format() = *(cursor.parag()->at(cursor.index())->format());
1499}
1500
1501void KoTextView::addBookmarks(const TQString &url)
1502{
1503 TQString filename = locateLocal( "data", TQString::fromLatin1("konqueror/bookmarks.xml") );
1504 KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename,false );
1505 KBookmarkGroup group = bookManager->root();
1506 group.addBookmark( bookManager, url, KURL( url));
1507 bookManager->save();
1508 // delete bookManager;
1509}
1510
1511void KoTextView::copyLink()
1512{
1513 KoLinkVariable * var=linkVariable();
1514 if(var)
1515 {
1516 KURL::List lst;
1517 lst.append( var->url() );
1518 TQApplication::clipboard()->setSelectionMode(true);
1519 TQApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
1520 TQApplication::clipboard()->setSelectionMode(false);
1521 TQApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
1522 }
1523}
1524
1525void KoTextView::removeLink()
1526{
1527 KoLinkVariable * var=linkVariable();
1528 if(var)
1529 {
1530 KoTextCursor c1 = *m_cursor;
1531 KoTextCursor c2 = *m_cursor;
1532 c1.setIndex(var->index());
1533 c2.setIndex(var->index()+1);
1534 textDocument()->setSelectionStart( KoTextDocument::Temp, &c1 );
1535 textDocument()->setSelectionEnd( KoTextDocument::Temp, &c2 );
1536 KCommand *cmd=textObject()->replaceSelectionCommand( &c1, var->value(),
1537 i18n("Remove Link"), KoTextDocument::Temp );
1538 if ( cmd )
1539 textObject()->emitNewCommand( cmd );
1540 }
1541}
1542
1543void KoTextView::setBackSpeller( KoBgSpellCheck* backSpeller )
1544{
1545 d->m_backSpeller = backSpeller;
1546}
1547
1548#include "KoTextView.moc"
1549class KoBgSpellCheck;
void addCommand(KCommand *command)
static void sorry(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, int options=Notify)
This class represents a border - for anything, like paragraph, or frame.
Definition KoBorder.h:34
This is the structure associated with a paragraph (KoTextParag), to define the bullet or numbering of...
unsigned int depth() const
The level of the numbering.
int displayLevels() const
Number of levels whose numbers are displayed at the current level.
This class holds the paragraph-specific formatting information It's separated from KoTextParag so tha...
SpacingType
Line spacing values Assuming h is the height of the biggest char in the line, and v is the linespacin...
A paragraph style is a combination of a character style and paragraph-layout attributes,...
TODO rename to KoParagStyleCollection - or should char styles be part of it too?
TQValueVector< KoParagStyle * > outlineStyles() const
KoParagStyle * defaultStyle() const
KoParagStyle * numberedStyleForLevel(int level) const
int index() const
The index in paragraph(), where this anchor is Slightly slow (does a linear search in the paragraph).
Each character (KoTextStringChar) points to a KoTextFormat that defines the formatting of that charac...
const TQFontMetrics & refFontMetrics() const
int width(const TQChar &c) const
Return the width of one char (from a string, not necessarily from a paragraph) in LU pixels.
The KoTextObject is the high-level object that contains a KoTextDocument (the list of paragraphs),...
void insert(KoTextCursor *cursor, KoTextFormat *currentFormat, const TQString &text, const TQString &commandName, KoTextDocument::SelectionId selectionId=KoTextDocument::Standard, int insertFlags=DefaultInsertFlags, CustomItemsMap customItemsMap=CustomItemsMap())
The main "insert" method, including undo/redo creation/update.
@ OverwriteMode
< if true, the text to be inserted is checked for '\n' (as a paragraph delimiter)
@ DoNotRepaint
usually we repaint in insert(), this allows to turn it off
void removeSelectedText(KoTextCursor *cursor, KoTextDocument::SelectionId selectionId=KoTextDocument::Standard, const TQString &cmdName=TQString(), bool createUndoRedo=true)
Remove the text currently selected, including undo/redo creation/update.
TQString selectedText(KoTextDocument::SelectionId selectionId=KoTextDocument::Standard) const
returns the selected text [without formatting] if hasSelection()
virtual void ctrlPgUpKeyPressed()
Reimplement this to handle CTRL+PageUp.
Definition KoTextView.h:245
virtual KoTextFormat * currentFormat() const
Interface for accessing the current format.
Definition KoTextView.h:84
virtual void showFormat(KoTextFormat *format)=0
Show the settings of this format in the GUI.
bool isReadWrite() const
Return true if the view is allowed to modify the text object.
Definition KoTextView.h:80
virtual void updateUI(bool updateFormat, bool force=false)
Show the current settings (those for the paragraph and character under the cursor),...
virtual KCommand * setParagLayoutFormatCommand(KoParagLayout *newLayout, int flags, int marginIndex=-1)
Interface for changing the paragraph layout.
KoTextView(KoTextObject *textobj)
Constructor.
virtual void ctrlPgDownKeyPressed()
Reimplement this to handle CTRL+PageDown.
Definition KoTextView.h:247
virtual bool openLink(KoLinkVariable *linkVariable)
Called when clicking on a link.
void insertText(const TQString &text)
This is a slot so that it's accessible from DCOP.
TQPtrList< TDEAction > dataToolActionList(TDEInstance *instance, const TQString &word, bool &_singleWord)
Return the list of actions from data-tools.
virtual bool pgDownKeyPressed()=0
Reimplement this to handle PageDown.
virtual bool pgUpKeyPressed()=0
Reimplement this to handle PageUp.
void terminate(bool removeselection=true)
Call this before deleting.
virtual const KoParagLayout * currentParagLayoutFormat() const
Use this format for displaying the properties (Align/counter/...) of the object.
virtual KCommand * setFormatCommand(const KoTextFormat *newFormat, int flags, bool zoomFont=false)
Implement the KoTextFormatInterface.
virtual void doAutoFormat(KoTextCursor *, KoTextParag *, int, TQChar)
Called when a character (ch) has been inserted into parag, at the given index.
Definition KoTextView.h:212
KCommand * prepareDropMove(KoTextCursor dropCursor)
A "drop move" will move the selected text to the given cursor position.
virtual void startDrag()=0
Start a drag.
virtual void drawCursor(bool b)
Draws the cursor (or hides it if b is false).
A KoVariable is a custom item, i.e.
Definition KoVariable.h:364