lib

KoTextView.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001-2006 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "KoTextView.h"
00021 #include "KoTextParag.h"
00022 #include "KoParagCounter.h"
00023 #include "KoTextObject.h"
00024 #include "KoTextViewIface.h"
00025 #include "KoStyleCollection.h"
00026 #include "KoBgSpellCheck.h"
00027 #include "KoVariable.h"
00028 
00029 #include <klocale.h>
00030 #include <kstandarddirs.h>
00031 #include <kstdaccel.h>
00032 #include <kdebug.h>
00033 #include <kinstance.h>
00034 #include <kdatatool.h>
00035 #include <krun.h>
00036 #include <kmessagebox.h>
00037 #include <kcommand.h>
00038 #include <kbookmarkmanager.h>
00039 #include <kbookmark.h>
00040 #include <kurldrag.h>
00041 
00042 #include <qapplication.h>
00043 #include <qtimer.h>
00044 #include <qclipboard.h>
00045 
00046 class KoTextView::KoTextViewPrivate
00047 {
00048 public:
00049     KoTextViewPrivate()
00050     {
00051         m_currentUnicodeNumber = 0;
00052         m_backSpeller = 0;
00053     }
00054 
00055     void appendDigit( int digit ) { m_currentUnicodeNumber = 10 * m_currentUnicodeNumber + digit; }
00056     int currentUnicodeNumber() const { return m_currentUnicodeNumber; }
00057     void clearCurrentUnicodeNumber() { m_currentUnicodeNumber = 0; }
00058 
00059     KoBgSpellCheck* m_backSpeller;
00060 
00061 private:
00062     int m_currentUnicodeNumber; // For the alt+123 feature
00063 };
00064 
00065 KoTextView::KoTextView( KoTextObject *textobj )
00066 {
00067     d = new KoTextViewPrivate;
00068     m_bReadWrite = true;
00069     m_textobj = textobj;
00070     dcop=0;
00071     connect( m_textobj, SIGNAL( hideCursor() ), this, SLOT( hideCursor() ) );
00072     connect( m_textobj, SIGNAL( showCursor() ), this, SLOT( showCursor() ) );
00073     connect( m_textobj, SIGNAL( setCursor( KoTextCursor * ) ), this, SLOT( setCursor( KoTextCursor * ) ) );
00074     connect( m_textobj, SIGNAL( updateUI(bool, bool) ), this, SLOT( updateUI(bool, bool) ) );
00075     connect( m_textobj, SIGNAL( showCurrentFormat() ), this, SLOT( showCurrentFormat() ) );
00076     connect( m_textobj, SIGNAL( ensureCursorVisible() ), this, SLOT( ensureCursorVisible() ) );
00077 
00078     m_cursor = new KoTextCursor( m_textobj->textDocument() );
00079 
00080     m_cursorVisible = false;
00081 
00082     showCursor();
00083     blinkTimer = new QTimer( this );
00084     connect( blinkTimer, SIGNAL( timeout() ),
00085              this, SLOT( blinkCursor() ) );
00086     if ( QApplication::cursorFlashTime() > 0 )
00087         blinkTimer->start( QApplication::cursorFlashTime() / 2 );
00088 
00089     dragStartTimer = new QTimer( this );
00090     connect( dragStartTimer, SIGNAL( timeout() ),
00091              this, SLOT( startDrag() ) );
00092 
00093     m_textobj->formatMore( 2 );
00094 
00095     blinkCursorVisible = FALSE;
00096     inDoubleClick = FALSE;
00097     mightStartDrag = FALSE;
00098     possibleTripleClick = FALSE;
00099     afterTripleClick = FALSE;
00100     m_currentFormat = 0;
00101     m_variablePosition =-1;
00102     m_overwriteMode = false;
00103     //updateUI( true, true );
00104 }
00105 
00106 KoTextView::~KoTextView()
00107 {
00108     delete m_cursor;
00109     delete d;
00110     delete dcop;
00111     delete blinkTimer;
00112     delete dragStartTimer;
00113 }
00114 
00115 KoTextViewIface* KoTextView::dcopObject()
00116 {
00117     if ( !dcop )
00118         dcop = new KoTextViewIface( this );
00119 
00120     return dcop;
00121 }
00122 
00123 void KoTextView::terminate(bool removeselection)
00124 {
00125     textObject()->clearUndoRedoInfo();
00126     if ( removeselection && textDocument()->removeSelection( KoTextDocument::Standard ) )
00127         textObject()->selectionChangedNotify();
00128     hideCursor();
00129 }
00130 
00131 void KoTextView::deleteWordRight()
00132 {
00133     if ( textObject()->hasSelection() ) {
00134         textObject()->removeSelectedText( m_cursor );
00135         return;
00136     }
00137     textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00138 
00139     do {
00140         m_cursor->gotoRight();
00141     } while ( !m_cursor->atParagEnd()
00142               && !m_cursor->parag()->at( m_cursor->index() )->c.isSpace() );
00143     textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
00144     textObject()->removeSelectedText( m_cursor, KoTextDocument::Standard, i18n("Remove Word") );
00145 }
00146 
00147 void KoTextView::deleteWordLeft()
00148 {
00149     if ( textObject()->hasSelection() ) {
00150         textObject()->removeSelectedText( m_cursor );
00151         return;
00152     }
00153     textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00154 
00155     do {
00156         m_cursor->gotoLeft();
00157     } while ( !m_cursor->atParagStart()
00158               && !m_cursor->parag()->at( m_cursor->index()-1 )->c.isSpace() );
00159     textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
00160     textObject()->removeSelectedText( m_cursor, KoTextDocument::Standard, i18n("Remove Word") );
00161 }
00162 
00163 // Compare with QTextEdit::keyPressEvent
00164 void KoTextView::handleKeyPressEvent( QKeyEvent * e, QWidget *widget, const QPoint &pos)
00165 {
00166     textObject()->typingStarted();
00167 
00168     /* bool selChanged = FALSE;
00169     for ( int i = 1; i < textDocument()->numSelections(); ++i )
00170         selChanged = textDocument()->removeSelection( i ) || selChanged;
00171 
00172     if ( selChanged ) {
00173         // m_cursor->parag()->document()->nextDoubleBuffered = TRUE; ######## we need that only if we have nested items/documents
00174         textFrameSet()->selectionChangedNotify();
00175     }*/
00176 
00177     bool clearUndoRedoInfo = TRUE;
00178     if ( KShortcut( KKey( e ) ) == KStdAccel::deleteWordBack() )
00179     {
00180         if ( m_cursor->parag()->string()->isRightToLeft() )
00181             deleteWordRight();
00182         else
00183             deleteWordLeft();
00184         clearUndoRedoInfo = TRUE;
00185     } else if ( KShortcut( KKey( e ) ) == KStdAccel::deleteWordForward() )
00186     {
00187         if ( m_cursor->parag()->string()->isRightToLeft() )
00188             deleteWordLeft();
00189         else
00190             deleteWordRight();
00191         clearUndoRedoInfo = TRUE;
00192     }
00193     else
00194     switch ( e->key() ) {
00195     case Key_Left:
00196     case Key_Right: {
00197         if (!doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
00198         {
00199             // a bit hacky, but can't change this without introducing new enum values for move and keeping the
00200             // correct semantics and movement for BiDi and non BiDi text.
00201             CursorAction a;
00202             if ( m_cursor->parag()->string()->isRightToLeft() == (e->key() == Key_Right) )
00203                 a = e->state() & ControlButton ? MoveWordBackward : MoveBackward;
00204             else
00205                 a = e->state() & ControlButton ? MoveWordForward : MoveForward;
00206             moveCursor( a, e->state() & ShiftButton );
00207         }
00208         break;
00209     }
00210     case Key_Up:
00211         moveCursor( e->state() & ControlButton ? MoveParagUp : MoveUp, e->state() & ShiftButton );
00212         break;
00213     case Key_Down:
00214         moveCursor( e->state() & ControlButton ? MoveParagDown : MoveDown, e->state() & ShiftButton );
00215         break;
00216     case Key_Home:
00217         moveCursor( e->state() & ControlButton ? MoveHome : MoveLineStart, e->state() & ShiftButton );
00218         break;
00219     case Key_End:
00220         if (!doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
00221             moveCursor( e->state() & ControlButton ? MoveEnd : MoveLineEnd, e->state() & ShiftButton );
00222         break;
00223     case Key_Prior:
00224         moveCursor( e->state() & ControlButton ? MovePgUp : MoveViewportUp, e->state() & ShiftButton );
00225         break;
00226     case Key_Next:
00227         moveCursor( e->state() & ControlButton ? MovePgDown : MoveViewportDown, e->state() & ShiftButton );
00228         break;
00229     case Key_Return: case Key_Enter:
00230 
00231         if (!doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
00232             if ( (e->state() & (ShiftButton|ControlButton)) == 0 )
00233             {
00234                 if ( textObject()->hasSelection() )
00235                     textObject()->removeSelectedText( m_cursor );
00236                 clearUndoRedoInfo = FALSE;
00237                 textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionReturn );
00238                 Q_ASSERT( m_cursor->parag()->prev() );
00239                 if ( m_cursor->parag()->prev() )
00240                     doAutoFormat( m_cursor, m_cursor->parag()->prev(),
00241                                   m_cursor->parag()->prev()->length() - 1, '\n' );
00242             }
00243         clearUndoRedoInfo = true;
00244         break;
00245     case Key_Delete:
00246         if ( textObject()->hasSelection() ) {
00247             textObject()->removeSelectedText( m_cursor );
00248             break;
00249         }
00250 
00251         textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionDelete );
00252 
00253         clearUndoRedoInfo = FALSE;
00254         break;
00255     case Key_Backtab:
00256       if (e->state() & ShiftButton && m_cursor->parag() && m_cursor->atParagStart() && m_cursor->parag()->counter() && textDecreaseIndent())
00257     break;
00258       break;
00259     case Key_Backspace:
00260         if ( textObject()->hasSelection() ) {
00261             textObject()->removeSelectedText( m_cursor );
00262             break;
00263         }
00264     textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionBackspace );
00265 
00266         clearUndoRedoInfo = FALSE;
00267         break;
00268     case Key_F16: // Copy key on Sun keyboards
00269         emit copy();
00270         break;
00271     case Key_F18:  // Paste key on Sun keyboards
00272         emit paste();
00273         break;
00274     case Key_F20:  // Cut key on Sun keyboards
00275         emit cut();
00276         break;
00277     case Key_Direction_L: {
00278     if ( m_cursor->parag() && m_cursor->parag()->direction() != QChar::DirL )
00279         {
00280             KCommand* cmd = textObject()->setParagDirectionCommand( m_cursor, QChar::DirL );
00281             textObject()->emitNewCommand( cmd );
00282         }
00283         break;
00284     }
00285     case Key_Direction_R: {
00286     if ( m_cursor->parag() && m_cursor->parag()->direction() != QChar::DirR )
00287         {
00288             KCommand* cmd = textObject()->setParagDirectionCommand( m_cursor, QChar::DirR );
00289             textObject()->emitNewCommand( cmd );
00290         }
00291         break;
00292     }
00293     default: {
00294             //kdDebug(32500) << "KoTextView::keyPressEvent ascii=" << e->ascii() << " text=" << e->text()[0].unicode() << " state=" << e->state() << endl;
00295             if (e->key() == Qt::Key_Tab)
00296             {
00297                 if (doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
00298                         break;
00299         if ( m_cursor->parag() && m_cursor->atParagStart() && m_cursor->parag()->counter() )
00300         {
00301             textIncreaseIndent();
00302             break;
00303         }
00304             }
00305 
00306             if ( e->key() == Qt::Key_Space )
00307             {
00308                 if (doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
00309                         break;
00310             }
00311             if ( e->text().length() &&
00312                  ( !e->ascii() || e->ascii() >= 32 ) ||
00313                  ( e->text() == "\t" && !( e->state() & ControlButton ) ) ) {
00314                 clearUndoRedoInfo = FALSE;
00315                 QString text = e->text();
00316 
00317                 if ( d->m_backSpeller ) {
00318                     d->m_backSpeller->setIntraWordEditing( m_cursor->parag(), m_cursor->index() );
00319                 }
00320 
00321                 // Alt+123 feature
00322                 if ( ( e->state() & AltButton ) && text[0].isDigit() )
00323                 {
00324                     while ( text[0].isDigit() ) {
00325                         d->appendDigit( text[0].digitValue() );
00326                         text.remove( 0, 1 );
00327                     }
00328                 }
00329                 if ( !text.isEmpty() )
00330                 {
00331                     // Bidi support: need to reverse mirrored chars (e.g. parenthesis)
00332                     KoTextParag *p = m_cursor->parag();
00333                     if ( p && p->string() && p->string()->isRightToLeft() ) {
00334                         QChar *c = (QChar *)text.unicode();
00335                         int l = text.length();
00336                         while( l-- ) {
00337                             if ( c->mirrored() )
00338                                 *c = c->mirroredChar();
00339                             c++;
00340                         }
00341                     }
00342 
00343                     if( !doIgnoreDoubleSpace( p, m_cursor->index()-1, text[ text.length() - 1 ] ) )
00344                     {
00345                         // ###### BUG: with the event compression, typing "kde" and then " k", might not apply
00346                         // autocorrection like it does for "kde" followed by " " followed by "k". We need to insert
00347                         // one character at a time, or better, to tell doAutoFormat how many chars to consider...
00348                         insertText( text );
00349                         // Don't use 'p' past this point. If we replaced a selection, p could have been deleted (#48999)
00350                         doAutoFormat( m_cursor, m_cursor->parag(), m_cursor->index() - 1, text[ text.length() - 1 ] );
00351                     }
00352                     showToolTipBox(m_cursor->parag(), m_cursor->index()-1, widget,pos);
00353                 }
00354                  else
00355                      removeToolTipCompletion();
00356 
00357             }
00358             // We should use KAccel instead, to make this configurable !
00359             // Well, those are all alternate keys, for keys already configurable (KDE-wide)
00360             // and a kaccel makes it hard to
00361             else
00362         {
00363                 if ( e->state() & ControlButton ) {
00364                     switch ( e->key() )
00365                     {
00366                     case Key_F16: // Copy key on Sun keyboards
00367                         copy();
00368                         break;
00369                     case Key_A:
00370                         moveCursor( MoveLineStart, e->state() & ShiftButton );
00371                         break;
00372                     case Key_E:
00373                         moveCursor( MoveLineEnd, e->state() & ShiftButton );
00374                         break;
00375                     case Key_K:
00376                         textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionKill );
00377                         break;
00378                     case Key_Insert:
00379                         copy();
00380                         break;
00381                     case Key_Space:
00382                         insertNonbreakingSpace();
00383                         break;
00384                     default:
00385                         clearUndoRedoInfo = FALSE;
00386                         break;
00387                     }
00388                 }
00389                 else // e.g. just Key_Shift -> don't do anything (#129481)
00390                 {
00391                     clearUndoRedoInfo = FALSE;
00392                 }
00393         }
00394             break;
00395         }
00396     }
00397 
00398     if ( clearUndoRedoInfo ) {
00399         textObject()->clearUndoRedoInfo();
00400         if ( d->m_backSpeller )
00401             d->m_backSpeller->setIntraWordEditing( 0, 0 );
00402     }
00403 
00404     textObject()->typingDone();
00405 }
00406 
00407 void KoTextView::setOverwriteMode( bool overwriteMode )
00408 {
00409     m_overwriteMode = overwriteMode;
00410 }
00411 
00412 void KoTextView::insertText( const QString &text )
00413 {
00414     int insertFlags = KoTextObject::DefaultInsertFlags;
00415     if ( m_overwriteMode )
00416         insertFlags |= KoTextObject::OverwriteMode;
00417     textObject()->insert( m_cursor, m_currentFormat, text, i18n("Insert Text"), KoTextDocument::Standard, insertFlags );
00418 }
00419 
00420 void KoTextView::newParagraph()
00421 {
00422     textObject()->insert( m_cursor, m_currentFormat, "\n", i18n("Insert Text"), KoTextDocument::Standard, KoTextObject::CheckNewLine );
00423 }
00424 
00425 void KoTextView::handleKeyReleaseEvent( QKeyEvent * e )
00426 {
00427     if ( e->key() == Key_Alt && d->currentUnicodeNumber() >= 32 )
00428     {
00429         QString text = QChar( d->currentUnicodeNumber() );
00430         d->clearCurrentUnicodeNumber();
00431         insertText( text );
00432         doAutoFormat( m_cursor, m_cursor->parag(),
00433                       m_cursor->index() - 1, text[ text.length() - 1 ] );
00434     }
00435 }
00436 
00437 void KoTextView::handleImStartEvent( QIMEvent * )
00438 {
00439     // nothing to do
00440 }
00441 
00442 void KoTextView::handleImComposeEvent( QIMEvent * e )
00443 {
00444     // remove old preedit
00445     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
00446         textDocument()->removeSelection( KoTextDocument::Standard );
00447     if ( textDocument()->hasSelection( KoTextDocument::InputMethodPreedit ) )
00448         textDocument()->removeSelectedText( KoTextDocument::InputMethodPreedit, m_cursor );
00449 
00450     // insert preedit
00451     int preeditStartIdx = m_cursor->index();
00452     textDocument()->setSelectionStart( KoTextDocument::InputMethodPreedit, m_cursor );
00453     textObject()->insert( m_cursor, m_currentFormat, e->text(), i18n("Insert Text"),
00454                           KoTextDocument::Standard,
00455                           KoTextObject::DoNotRepaint/* DO NOT REPAINT CURSOR! */ );
00456     textDocument()->setSelectionEnd( KoTextDocument::InputMethodPreedit, m_cursor );
00457 
00458     // selection
00459     int preeditSelStart = preeditStartIdx + e->cursorPos();
00460     int preeditSelEnd   = preeditSelStart + e->selectionLength();
00461     m_cursor->setIndex( preeditSelStart );
00462     textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00463     m_cursor->setIndex( preeditSelEnd );
00464     textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
00465 
00466     // set cursor pos
00467     m_cursor->setIndex( preeditSelStart );
00468 
00469     textObject()->emitUpdateUI( true );
00470     textObject()->emitShowCursor();
00471     textObject()->selectionChangedNotify();
00472 }
00473 
00474 void KoTextView::handleImEndEvent( QIMEvent * e )
00475 {
00476     // remove old preedit
00477     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
00478         textDocument()->removeSelection( KoTextDocument::Standard  );
00479     if ( textDocument()->hasSelection( KoTextDocument::InputMethodPreedit ) )
00480         textDocument()->removeSelectedText( KoTextDocument::InputMethodPreedit, m_cursor );
00481 
00482     insertText( e->text() );
00483 
00484     textObject()->emitUpdateUI( true );
00485     textObject()->emitShowCursor();
00486     textObject()->selectionChangedNotify();
00487 }
00488 
00489 void KoTextView::completion()
00490 {
00491     (void) doCompletion(m_cursor, m_cursor->parag(),
00492                      m_cursor->index() - 1);
00493 }
00494 
00495 void KoTextView::moveCursor( CursorAction action, bool select )
00496 {
00497     hideCursor();
00498     bool cursorMoved = false;
00499     if ( select ) {
00500         if ( !textDocument()->hasSelection( KoTextDocument::Standard ) )
00501             textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00502         cursorMoved = moveCursor( action );
00503         if ( textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor ) ) {
00504             textObject()->selectionChangedNotify();
00505         }
00506     } else {
00507         bool redraw = textDocument()->removeSelection( KoTextDocument::Standard );
00508         cursorMoved = moveCursor( action );
00509         if ( redraw ) {
00510             textObject()->selectionChangedNotify();
00511         }
00512     }
00513 
00514     if ( cursorMoved ) // e.g. not when pressing Ctrl/PgDown after the last parag
00515     {
00516         ensureCursorVisible();
00517         // updateUI( true ); // done by moveCursor
00518     }
00519     showCursor();
00520 }
00521 
00522 bool KoTextView::moveCursor( CursorAction action )
00523 {
00524     bool cursorMoved = true;
00525     switch ( action ) {
00526         case MoveBackward:
00527             m_cursor->gotoPreviousLetter();
00528             break;
00529         case MoveWordBackward:
00530             m_cursor->gotoPreviousWord();
00531             break;
00532         case MoveForward:
00533             m_cursor->gotoNextLetter();
00534             break;
00535         case MoveWordForward:
00536             m_cursor->gotoNextWord();
00537             break;
00538         case MoveUp:
00539             m_cursor->gotoUp();
00540             break;
00541         case MoveDown:
00542             m_cursor->gotoDown();
00543             break;
00544         case MoveViewportUp:
00545             cursorMoved = pgUpKeyPressed();
00546             break;
00547         case MoveViewportDown:
00548             cursorMoved = pgDownKeyPressed();
00549             break;
00550         case MovePgUp:
00551             ctrlPgUpKeyPressed();
00552             break;
00553         case MovePgDown:
00554             ctrlPgDownKeyPressed();
00555             break;
00556         case MoveLineStart:
00557             m_cursor->gotoLineStart();
00558             break;
00559         case MoveHome:
00560             m_cursor->gotoHome();
00561             break;
00562         case MoveLineEnd:
00563             m_cursor->gotoLineEnd();
00564             break;
00565         case MoveEnd:
00566             textObject()->ensureFormatted( textDocument()->lastParag() );
00567             m_cursor->gotoEnd();
00568             break;
00569         case MoveParagUp: {
00570             KoTextParag * parag = m_cursor->parag()->prev();
00571             if ( m_cursor->index()==0 && parag )
00572             {
00573                 m_cursor->setParag( parag );
00574                 m_cursor->setIndex( 0 );
00575             }
00576             else m_cursor->setIndex( 0 );
00577         } break;
00578         case MoveParagDown: {
00579             KoTextParag * parag = m_cursor->parag()->next();
00580             if ( parag )
00581             {
00582                 m_cursor->setParag( parag );
00583                 m_cursor->setIndex( 0 );
00584             }
00585         } break;
00586     }
00587 
00588     updateUI( true );
00589     return cursorMoved;
00590 }
00591 
00592 KoTextCursor KoTextView::selectWordUnderCursor( const KoTextCursor& cursor, int selectionId )
00593 {
00594     KoTextCursor c1 = cursor;
00595     KoTextCursor c2 = cursor;
00596     if ( cursor.index() > 0 && !cursor.parag()->at( cursor.index()-1 )->c.isSpace() )
00597         c1.gotoWordLeft();
00598     if ( !cursor.parag()->at( cursor.index() )->c.isSpace() && !cursor.atParagEnd() )
00599         c2.gotoWordRight();
00600 
00601     // The above is almost correct, but gotoWordRight also skips the spaces/punctuations
00602     // until the next word. So the 'word under cursor' contained e.g. that trailing space.
00603     // To be on the safe side, we skip spaces/punctuations on both sides:
00604     KoTextString *s = cursor.parag()->string();
00605     bool beginFound = false;
00606     for ( int i = c1.index(); i< c2.index(); i++)
00607     {
00608         const QChar ch = s->at(i).c;
00609         // This list comes from KoTextCursor::gotoPreviousWord.
00610         // Can't use QChar::isPunct since "'" and "-" are not word separators
00611         const bool isWordDelimiter = ch.isSpace()
00612                                    || ch.category() == QChar::Punctuation_Open // e.g. '('
00613                                    || ch.category() == QChar::Punctuation_Close // e.g. ')'
00614                                    || ch.category() == QChar::Punctuation_Other // see http://www.fileformat.info/info/unicode/category/Po/list.htm
00615                                    ;
00616 
00617         if( !beginFound && !isWordDelimiter )
00618         {
00619             c1.setIndex(i);
00620             beginFound = true;
00621         }
00622         else if ( beginFound && isWordDelimiter )
00623         {
00624             c2.setIndex(i);
00625             break;
00626         }
00627     }
00628 
00629     textDocument()->setSelectionStart( selectionId, &c1 );
00630     textDocument()->setSelectionEnd( selectionId, &c2 );
00631     return c2;
00632 }
00633 
00634 KoTextCursor KoTextView::selectParagUnderCursor( const KoTextCursor& cursor, int selectionId, bool copyAndNotify )
00635 {
00636     KoTextCursor c1 = cursor;
00637     KoTextCursor c2 = cursor;
00638     c1.setIndex(0);
00639     c2.setIndex(c1.parag()->string()->length() - 1);
00640     textDocument()->setSelectionStart( selectionId, &c1 );
00641     textDocument()->setSelectionEnd( selectionId, &c2 );
00642     if ( copyAndNotify )
00643     {
00644         textObject()->selectionChangedNotify();
00645         // Copy the selection.
00646         QApplication::clipboard()->setSelectionMode( true );
00647         emit copy();
00648         QApplication::clipboard()->setSelectionMode( false );
00649     }
00650     return c2;
00651 }
00652 
00653 void KoTextView::extendParagraphSelection( const QPoint& iPoint )
00654 {
00655     hideCursor();
00656     KoTextCursor oldCursor = *m_cursor;
00657     placeCursor( iPoint );
00658 
00659     bool redraw = FALSE;
00660     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
00661     {
00662         redraw = textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
00663         if ( textDocument()->isSelectionSwapped( KoTextDocument::Standard ) )
00664             m_cursor->setIndex( 0 );
00665         else
00666             m_cursor->setIndex( m_cursor->parag()->string()->length() - 1 );
00667         textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
00668     }
00669     //else // it may be that the initial click was out of the frame
00670     //    textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00671 
00672     if ( redraw )
00673         textObject()->selectionChangedNotify( false );
00674 
00675     showCursor();
00676 }
00677 
00678 QString KoTextView::wordUnderCursor( const KoTextCursor& cursor )
00679 {
00680     selectWordUnderCursor( cursor, KoTextDocument::Temp );
00681     QString text = textObject()->selectedText( KoTextDocument::Temp );
00682     bool hasCustomItems = textObject()->selectionHasCustomItems( KoTextDocument::Temp );
00683     textDocument()->removeSelection( KoTextDocument::Temp );
00684     if( !hasCustomItems )
00685         return text;
00686     return QString::null;
00687 }
00688 
00689 bool KoTextView::handleMousePressEvent( QMouseEvent *e, const QPoint &iPoint, bool canStartDrag, bool insertDirectCursor )
00690 {
00691     bool addParag = false;
00692     mightStartDrag = FALSE;
00693     hideCursor();
00694 
00695     if (possibleTripleClick)
00696     {
00697         handleMouseTripleClickEvent( e, iPoint );
00698         return addParag;
00699     }
00700 
00701     KoTextCursor oldCursor = *m_cursor;
00702     addParag = placeCursor( iPoint, insertDirectCursor&& isReadWrite() );
00703     ensureCursorVisible();
00704 
00705     if ( e->button() != LeftButton )
00706     {
00707         showCursor();
00708         return addParag;
00709     }
00710 
00711     KoLinkVariable* lv = linkVariable();
00712     if ( lv && openLink( lv ) )
00713     {
00714         return addParag;
00715     }
00716 
00717     KoTextDocument * textdoc = textDocument();
00718     if ( canStartDrag && textdoc->inSelection( KoTextDocument::Standard, iPoint ) ) {
00719         mightStartDrag = TRUE;
00720         m_textobj->emitShowCursor();
00721         dragStartTimer->start( QApplication::startDragTime(), TRUE );
00722         dragStartPos = e->pos();
00723         return addParag;
00724     }
00725 
00726     bool redraw = FALSE;
00727     if ( textdoc->hasSelection( KoTextDocument::Standard ) ) {
00728         if ( !( e->state() & ShiftButton ) ) {
00729             redraw = textdoc->removeSelection( KoTextDocument::Standard );
00730             textdoc->setSelectionStart( KoTextDocument::Standard, m_cursor );
00731         } else {
00732             redraw = textdoc->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
00733         }
00734     } else {
00735         if ( !( e->state() & ShiftButton ) ) {
00736             textdoc->setSelectionStart( KoTextDocument::Standard, m_cursor );
00737         } else {
00738             textdoc->setSelectionStart( KoTextDocument::Standard, &oldCursor );
00739             redraw = textdoc->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
00740         }
00741     }
00742 
00743     //kdDebug(32500) << "KoTextView::mousePressEvent redraw=" << redraw << endl;
00744     if ( !redraw ) {
00745         showCursor();
00746     } else {
00747         textObject()->selectionChangedNotify();
00748     }
00749     return addParag;
00750 }
00751 
00752 void KoTextView::handleMouseMoveEvent( QMouseEvent*, const QPoint& iPoint )
00753 {
00754     hideCursor();
00755     KoTextCursor oldCursor = *m_cursor;
00756     placeCursor( iPoint );
00757 
00758     // Double click + mouse still down + moving the mouse selects full words.
00759     if ( inDoubleClick ) {
00760         KoTextCursor cl = *m_cursor;
00761         cl.gotoWordLeft();
00762         KoTextCursor cr = *m_cursor;
00763         cr.gotoWordRight();
00764 
00765         int diff = QABS( oldCursor.parag()->at( oldCursor.index() )->x - iPoint.x() );
00766         int ldiff = QABS( cl.parag()->at( cl.index() )->x - iPoint.x() );
00767         int rdiff = QABS( cr.parag()->at( cr.index() )->x - iPoint.x() );
00768 
00769         if ( m_cursor->parag()->lineStartOfChar( m_cursor->index() ) !=
00770              oldCursor.parag()->lineStartOfChar( oldCursor.index() ) )
00771             diff = 0xFFFFFF;
00772 
00773         if ( rdiff < diff && rdiff < ldiff )
00774             *m_cursor = cr;
00775         else if ( ldiff < diff && ldiff < rdiff )
00776             *m_cursor = cl;
00777         else
00778             *m_cursor = oldCursor;
00779     }
00780 
00781     bool redraw = FALSE;
00782     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
00783         redraw = textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
00784     else // it may be that the initial click was out of the frame
00785         textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00786 
00787     if ( redraw )
00788         textObject()->selectionChangedNotify( false );
00789 
00790     showCursor();
00791 }
00792 
00793 void KoTextView::handleMouseReleaseEvent()
00794 {
00795     if ( dragStartTimer->isActive() )
00796         dragStartTimer->stop();
00797     if ( mightStartDrag ) {
00798         textObject()->selectAll( FALSE );
00799         mightStartDrag = false;
00800     }
00801     else
00802     {
00803         if ( textDocument()->selectionStartCursor( KoTextDocument::Standard ) == textDocument()->selectionEndCursor( KoTextDocument::Standard ) )
00804         {
00805             textDocument()->removeSelection( KoTextDocument::Standard );
00806         }
00807 
00808         textObject()->selectionChangedNotify();
00809 
00810         // Copy the selection.
00811         QApplication::clipboard()->setSelectionMode( true );
00812         emit copy();
00813         QApplication::clipboard()->setSelectionMode( false );
00814     }
00815 
00816     inDoubleClick = FALSE;
00817     m_textobj->emitShowCursor();
00818 }
00819 
00820 void KoTextView::handleMouseDoubleClickEvent( QMouseEvent*ev, const QPoint& i )
00821 {
00822   //after a triple click it's not a double click but a simple click
00823   //but as triple click didn't exist it's necessary to do it.
00824     if(afterTripleClick)
00825     {
00826         handleMousePressEvent( ev, i );
00827         return;
00828     }
00829 
00830     inDoubleClick = TRUE;
00831     *m_cursor = selectWordUnderCursor( *m_cursor );
00832     textObject()->selectionChangedNotify();
00833     // Copy the selection.
00834     QApplication::clipboard()->setSelectionMode( true );
00835     emit copy();
00836     QApplication::clipboard()->setSelectionMode( false );
00837 
00838     possibleTripleClick=true;
00839 
00840     QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
00841 }
00842 
00843 void KoTextView::tripleClickTimeout()
00844 {
00845    possibleTripleClick=false;
00846 }
00847 
00848 void KoTextView::handleMouseTripleClickEvent( QMouseEvent*ev, const QPoint& /* Currently unused */ )
00849 {
00850     if ( ev->button() != LeftButton)
00851     {
00852         showCursor();
00853         return;
00854     }
00855     afterTripleClick= true;
00856     inDoubleClick = FALSE;
00857     *m_cursor = selectParagUnderCursor( *m_cursor );
00858     QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(afterTripleClickTimeout()));
00859 }
00860 
00861 void KoTextView::afterTripleClickTimeout()
00862 {
00863     afterTripleClick=false;
00864 }
00865 
00866 bool KoTextView::maybeStartDrag( QMouseEvent* e )
00867 {
00868     if ( mightStartDrag ) {
00869         dragStartTimer->stop();
00870         if ( ( e->pos() - dragStartPos ).manhattanLength() > QApplication::startDragDistance() )
00871             startDrag();
00872         return true;
00873     }
00874     return false;
00875 }
00876 
00877 bool KoTextView::insertParagraph(const QPoint &pos)
00878 {
00879     KoTextParag *last = textDocument()->lastParag();
00880     KoTextFormat *f = 0;
00881     KoParagStyle *style = last->style();
00882     KoParagCounter *counter = last->counter();
00883     int diff = (pos.y()- textDocument()->height());
00884     f = last->at( last->length()-1 )->format();
00885     int height =f->height();
00886     int nbParag = (diff / height);
00887     QFontMetrics fm = f->refFontMetrics();
00888     for (int i = 0; i < nbParag ;i++)
00889     {
00890         KoTextParag *s=textDocument()->createParag( textDocument(), last );
00891         s->setFormat( 0, 1, f, TRUE );
00892         if ( style )
00893             s->setStyle( style );
00894         s->setCounter( counter );
00895         last = s;
00896     }
00897     bool createParag = (nbParag > 0 );
00898     if ( createParag )
00899     {
00900         if ( pos.x() + f->width(' ') >= textDocument()->width())
00901         {
00902             //FIXME me bidi.
00903             //change parag alignment => right alignment
00904             last->setAlignment( Qt::AlignRight );
00905         }
00906         else
00907         {
00908             int nbSpace = pos.x()/f->width(' ');
00909             QString tmp;
00910             for (int i = 0; i< nbSpace; i++)
00911             {
00912                 tmp+=' ';
00913             }
00914             last->insert( 0, tmp );
00915         }
00916     }
00917     return createParag;
00918 
00919 }
00920 
00921 bool KoTextView::placeCursor( const QPoint &pos, bool insertDirectCursor )
00922 {
00923     bool addParag = false;
00924     if ( insertDirectCursor && (pos.y()>textDocument()->height()) )
00925         addParag = insertParagraph(pos);
00926     KoTextParag *s = 0L;
00927     if ( addParag )
00928         s = textDocument()->lastParag();
00929     else
00930         s = textDocument()->firstParag();
00931     m_cursor->place( pos, s, false, &m_variablePosition );
00932     if ( m_variablePosition != -1 )
00933         kdDebug() << k_funcinfo << " m_variablePosition set to " << m_variablePosition << endl;
00934     updateUI( true );
00935     return addParag;
00936 }
00937 
00938 void KoTextView::blinkCursor()
00939 {
00940     //kdDebug(32500) << "KoTextView::blinkCursor m_cursorVisible=" << m_cursorVisible
00941     //          << " blinkCursorVisible=" << blinkCursorVisible << endl;
00942     if ( !m_cursorVisible )
00943         return;
00944     bool cv = m_cursorVisible;
00945     blinkCursorVisible = !blinkCursorVisible;
00946     drawCursor( blinkCursorVisible );
00947     m_cursorVisible = cv;
00948 }
00949 
00950 void KoTextView::drawCursor( bool visible )
00951 {
00952     m_cursorVisible = visible;
00953     // The rest is up to the app ;)
00954 }
00955 
00956 void KoTextView::focusInEvent()
00957 {
00958     if ( QApplication::cursorFlashTime() > 0 )
00959         blinkTimer->start( QApplication::cursorFlashTime() / 2 );
00960     showCursor();
00961 }
00962 
00963 void KoTextView::focusOutEvent()
00964 {
00965     blinkTimer->stop();
00966     hideCursor();
00967 }
00968 
00969 /*void KoTextView::setFormat( KoTextFormat * newFormat, int flags, bool zoomFont)
00970 {
00971     textObject()->setFormat( m_cursor, m_currentFormat, newFormat, flags, zoomFont );
00972 }*/
00973 
00974 KCommand* KoTextView::setFormatCommand( const KoTextFormat * newFormat, int flags, bool zoomFont)
00975 {
00976     return textObject()->setFormatCommand( m_cursor, &m_currentFormat, newFormat, flags, zoomFont );
00977 }
00978 
00979 void KoTextView::dragStarted()
00980 {
00981     mightStartDrag = FALSE;
00982     inDoubleClick = FALSE;
00983 }
00984 
00985 void KoTextView::applyStyle( const KoParagStyle * style )
00986 {
00987     if ( style )
00988     {
00989         textObject()->applyStyle( m_cursor, style );
00990         showCurrentFormat();
00991     }
00992 }
00993 
00994 void KoTextView::updateUI( bool updateFormat, bool /*force*/ )
00995 {
00996     // Update UI - only for those items which have changed
00997 
00998     if ( updateFormat )
00999     {
01000         int i = cursor()->index();
01001         if ( i > 0 )
01002             --i;
01003 #ifdef DEBUG_FORMATS
01004         if ( currentFormat() )
01005             kdDebug(32500) << "KoTextView::updateUI old currentFormat=" << currentFormat()
01006                            << " " << currentFormat()->key()
01007                            << " parag format=" << cursor()->parag()->at( i )->format()->key() << endl;
01008         else
01009             kdDebug(32500) << "KoTextView::updateUI old currentFormat=0" << endl;
01010 #endif
01011         if ( !currentFormat() || currentFormat()->key() != cursor()->parag()->at( i )->format()->key() )
01012         {
01013             if ( currentFormat() )
01014                 currentFormat()->removeRef();
01015 #ifdef DEBUG_FORMATS
01016             kdDebug(32500) << "Setting currentFormat from format " << cursor()->parag()->at( i )->format()
01017                       << " ( character " << i << " in paragraph " << cursor()->parag()->paragId() << " )" << endl;
01018 #endif
01019             setCurrentFormat( textDocument()->formatCollection()->format( cursor()->parag()->at( i )->format() ) );
01020             if ( currentFormat()->isMisspelled() ) {
01021                 KoTextFormat fNoMisspelled( *currentFormat() );
01022                 fNoMisspelled.setMisspelled( false );
01023                 currentFormat()->removeRef();
01024                 setCurrentFormat( textDocument()->formatCollection()->format( &fNoMisspelled ) );
01025             }
01026             showCurrentFormat();
01027         }
01028     }
01029 }
01030 
01031 void KoTextView::showCurrentFormat()
01032 {
01033     //kdDebug(32500) << "KoTextView::showCurrentFormat currentFormat=" << currentFormat() << " " << currentFormat()->key() << endl;
01034     KoTextFormat format = *currentFormat();
01035     //format.setPointSize( textObject()->docFontSize( currentFormat() ) ); // "unzoom" the font size
01036     showFormat( &format );
01037 }
01038 
01039 KCommand * KoTextView::setCounterCommand( const KoParagCounter & counter )
01040 {
01041      return textObject()->setCounterCommand( m_cursor, counter );
01042 }
01043 KCommand * KoTextView::setAlignCommand( int align )
01044 {
01045      return textObject()->setAlignCommand( m_cursor, align );
01046 }
01047 KCommand * KoTextView::setLineSpacingCommand( double spacing, KoParagLayout::SpacingType _type)
01048 {
01049      return textObject()->setLineSpacingCommand( m_cursor, spacing, _type);
01050 }
01051 KCommand * KoTextView::setBordersCommand( const KoBorder& leftBorder, const KoBorder& rightBorder, const KoBorder& bottomBorder, const KoBorder& topBorder )
01052 {
01053     return textObject()->setBordersCommand( m_cursor, leftBorder, rightBorder, bottomBorder, topBorder );
01054 }
01055 KCommand * KoTextView::setJoinBordersCommand( bool join )
01056 {
01057     return textObject()->setJoinBordersCommand( m_cursor, join );
01058 }
01059 KCommand * KoTextView::setMarginCommand( QStyleSheetItem::Margin m, double margin )
01060 {
01061     return textObject()->setMarginCommand( m_cursor, m, margin );
01062 }
01063 KCommand * KoTextView::setTabListCommand( const KoTabulatorList & tabList )
01064 {
01065     return textObject()->setTabListCommand( m_cursor, tabList );
01066 }
01067 KCommand * KoTextView::setBackgroundColorCommand( const QColor & color )
01068 {
01069     return textObject()->setBackgroundColorCommand( m_cursor, color );
01070 }
01071 
01072 KoTextDocument * KoTextView::textDocument() const
01073 {
01074     return textObject()->textDocument();
01075 }
01076 
01077 KoVariable *KoTextView::variable()
01078 {
01079     if ( m_variablePosition < 0 )
01080         return 0;
01081     // Can't use m_cursor here, it could be before or after the variable, depending on which half of it was clicked
01082     return textObject()->variableAtPosition( m_cursor->parag(), m_variablePosition );
01083 }
01084 
01085 KoLinkVariable * KoTextView::linkVariable()
01086 {
01087     return dynamic_cast<KoLinkVariable *>(variable());
01088 }
01089 
01090 QPtrList<KAction> KoTextView::dataToolActionList(KInstance * instance, const QString& word, bool & _singleWord )
01091 {
01092     m_singleWord = false;
01093     m_wordUnderCursor = QString::null;
01094     QString text;
01095     if ( textObject()->hasSelection() )
01096     {
01097         text = textObject()->selectedText();
01098         if ( text.find(' ') == -1 && text.find('\t') == -1 && text.find(KoTextObject::customItemChar()) == -1 )
01099         {
01100             m_singleWord = true;
01101         }
01102         else
01103          {
01104             m_singleWord = false;
01105             //laurent : don't try to search thesaurus when we have a customItemChar.
01106             if( text.find(KoTextObject::customItemChar())!=-1)
01107                 text = QString::null;
01108         }
01109     }
01110     else // No selection -> use word under cursor
01111     {
01112         if ( !word.isEmpty() )
01113         {
01114             m_singleWord = true;
01115             m_wordUnderCursor = word;
01116             text = word;
01117         }
01118     }
01119 
01120     if ( text.isEmpty() || textObject()->protectContent()) // Nothing to apply a tool to
01121         return QPtrList<KAction>();
01122 
01123     // Any tool that works on plain text is relevant
01124     QValueList<KDataToolInfo> tools;
01125     tools +=KDataToolInfo::query( "QString", "text/plain", instance );
01126 
01127     // Add tools that work on a single word if that is the case
01128     if ( m_singleWord )
01129     {
01130         _singleWord = true;
01131         tools += KDataToolInfo::query( "QString", "application/x-singleword", instance );
01132     }
01133     // Maybe one day we'll have tools that use libkotext (or qt3's qrt), to act on formatted text
01134     tools += KDataToolInfo::query( "KoTextString", "application/x-qrichtext", instance );
01135 
01136     return KDataToolAction::dataToolActionList( tools, this, SLOT( slotToolActivated( const KDataToolInfo &, const QString & ) ) );
01137 }
01138 
01139 QString KoTextView::currentWordOrSelection() const
01140 {
01141     if ( textObject()->hasSelection() )
01142         return textObject()->selectedText();
01143     else
01144         return m_wordUnderCursor;
01145 }
01146 
01147 void KoTextView::slotToolActivated( const KDataToolInfo & info, const QString & command )
01148 {
01149     KDataTool* tool = info.createTool( );
01150     if ( !tool )
01151     {
01152         kdWarning() << "Could not create Tool !" << endl;
01153         return;
01154     }
01155 
01156     kdDebug(32500) << "KWTextFrameSetEdit::slotToolActivated command=" << command
01157               << " dataType=" << info.dataType() << endl;
01158 
01159     QString text;
01160     if ( textObject()->hasSelection() )
01161         text = textObject()->selectedText();
01162     else
01163         text = m_wordUnderCursor;
01164 
01165     // Preferred type is richtext
01166     QString mimetype = "application/x-qrichtext";
01167     QString datatype = "KoTextString";
01168     // If unsupported, try text/plain
01169     if ( !info.mimeTypes().contains( mimetype ) )
01170     {
01171         mimetype = "text/plain";
01172         datatype = "QString";
01173     }
01174     // If unsupported (and if we have a single word indeed), try application/x-singleword
01175     if ( !info.mimeTypes().contains( mimetype ) && m_singleWord )
01176         mimetype = "application/x-singleword";
01177 
01178     kdDebug(32500) << "Running tool with datatype=" << datatype << " mimetype=" << mimetype << endl;
01179 
01180     QString origText = text;
01181     if ( tool->run( command, &text, datatype, mimetype) )
01182     {
01183         kdDebug(32500) << "Tool ran. Text is now " << text << endl;
01184         if ( origText != text )
01185         {
01186             if ( !textObject()->hasSelection() )
01187             {
01188                 // Warning: ok for now, but wrong cursor if RMB doesn't place cursor anymore
01189                 selectWordUnderCursor( *m_cursor );
01190             }
01191             // replace selection with 'text'
01192             textObject()->emitNewCommand( textObject()->replaceSelectionCommand(
01193                 cursor(), text, i18n("Replace Word") ));
01194         }
01195     }
01196     delete tool;
01197 }
01198 
01199 bool KoTextView::openLink( KoLinkVariable* variable )
01200 {
01201     kdDebug() << k_funcinfo << variable->url() << endl;
01202     KURL url( variable->url() );
01203     if( url.isValid() )
01204     {
01205         (void) new KRun( url );
01206         return true;
01207     }
01208     else
01209     {
01210         KMessageBox::sorry( 0, i18n("%1 is not a valid link.").arg( variable->url() ) );
01211         return false;
01212     }
01213 }
01214 
01215 
01216 void KoTextView::insertSoftHyphen()
01217 {
01218     textObject()->insert( cursor(), currentFormat(), QChar(0xad) /* see QRichText */,
01219                           i18n("Insert Soft Hyphen") );
01220 }
01221 
01222 void KoTextView::insertLineBreak()
01223 {
01224     textObject()->insert( cursor(), currentFormat(), QChar('\n'),
01225                           i18n("Insert Line Break") );
01226 }
01227 
01228 void KoTextView::insertNonbreakingSpace()
01229 {
01230     textObject()->insert( cursor(), currentFormat(), QChar(0xa0) /* see QRichText */,
01231                           i18n("Insert Non-Breaking Space") );
01232 }
01233 
01234 void KoTextView::insertNonbreakingHyphen()
01235 {
01236     textObject()->insert( cursor(), currentFormat(), QChar(0x2013),
01237                           i18n("Insert Non-Breaking Hyphen") );
01238 }
01239 
01240 void KoTextView::insertSpecialChar(QChar _c, const QString& font)
01241 {
01242     KoTextFormat * newFormat = new KoTextFormat(*currentFormat());
01243     newFormat->setFamily( font );
01244     if ( textObject()->hasSelection() )
01245     {
01246         KoTextFormat * lastFormat = currentFormat();
01247 
01248         KCommand *cmd = textObject()->setFormatCommand( cursor(), &lastFormat, newFormat, KoTextFormat::Family );
01249 
01250         KMacroCommand* macroCmd = new KMacroCommand( i18n("Insert Special Char") );
01251         macroCmd->addCommand( cmd );
01252         macroCmd->addCommand( textObject()->replaceSelectionCommand(
01253                                   cursor(), _c, QString::null) );
01254         textObject()->emitNewCommand( macroCmd );
01255     }
01256     else
01257     {
01258         textObject()->insert( cursor(), newFormat, _c, i18n("Insert Special Char"));
01259         delete newFormat;
01260     }
01261 }
01262 
01263 const KoParagLayout * KoTextView::currentParagLayoutFormat() const
01264 {
01265     KoTextParag * parag = m_cursor->parag();
01266     return &(parag->paragLayout());
01267 }
01268 
01269 bool KoTextView::rtl() const
01270 {
01271     return m_cursor->parag()->string()->isRightToLeft();
01272 }
01273 
01274 KCommand* KoTextView::setParagLayoutFormatCommand( KoParagLayout *newLayout, int flags, int marginIndex )
01275 {
01276     return textObject()->setParagLayoutCommand( m_cursor, *newLayout, KoTextDocument::Standard,
01277                                                 flags, marginIndex, true /*createUndoRedo*/ );
01278 }
01279 
01280 // Heading1 -> Heading2 -> Heading3 -> normal -> 1 -> 1.1 -> 1.1.1
01281 void KoTextView::increaseNumberingLevel( const KoStyleCollection* styleCollection )
01282 {
01283     // TODO: do this for each paragraph in the selection
01284     KoParagStyle* style = 0;
01285     int level = 0;
01286     KoParagCounter* counter = m_cursor->parag()->counter();
01287     if ( counter )
01288         level = counter->depth() + 1;
01289     if ( m_cursor->parag()->style()->isOutline() )
01290     {
01291         QValueVector<KoParagStyle *> outlineStyles = styleCollection->outlineStyles();
01292         while ( level < 10 && !style ) {
01293             style = outlineStyles[ level ];
01294             ++level;
01295         }
01296         if ( !style ) // no lower-level heading exists, use standard style
01297             style = styleCollection->defaultStyle();
01298     }
01299     else // non-outline, just a numbered list
01300     {
01301         // Try to find a style with this depth, to know if the user wants display-levels etc.
01302         style = styleCollection->numberedStyleForLevel( level );
01303         if ( !style ) { // not found. Make the change though.
01304             KoParagCounter c;
01305             if (counter) {
01306                 c = *counter;
01307                 c.setDepth( level );
01308                 c.setDisplayLevels( c.displayLevels() + 1 );
01309             } else {
01310                 // Start a simple numbered list.
01311                 c.setNumbering(KoParagCounter::NUM_LIST);
01312                 c.setStyle(KoParagCounter::STYLE_NUM);
01313             }
01314             KCommand* command = textObject()->setCounterCommand( m_cursor, c );
01315             textObject()->emitNewCommand( command );
01316         }
01317     }
01318     if ( style ) // can't be 0
01319         textObject()->applyStyle( m_cursor, style );
01320 }
01321 
01322 // 1.1.1 -> 1.1 -> 1 -> normal -> Heading3 -> Heading2 -> Heading1
01323 void KoTextView::decreaseNumberingLevel( const KoStyleCollection* styleCollection )
01324 {
01325     // TODO: do this for each paragraph in the selection
01326     KoParagCounter* counter = m_cursor->parag()->counter();
01327     int level = 9;
01328     if ( counter )
01329         level = counter->depth() - 1;
01330     KoParagStyle* style = 0;
01331     if ( m_cursor->parag()->style()->isOutline() || !counter ) // heading or normal
01332     {
01333         if ( level == -1 ) // nothing higher than Heading1
01334             return;
01335         QValueVector<KoParagStyle *> outlineStyles = styleCollection->outlineStyles();
01336         while ( level >= 0 && !style ) {
01337             style = outlineStyles[ level ];
01338             --level;
01339         }
01340     }
01341     else // non-outline, numbered list
01342     {
01343         if ( level == -1 )
01344             style = styleCollection->defaultStyle();
01345         else
01346         {
01347             style = styleCollection->numberedStyleForLevel( level );
01348             if ( !style ) { // not found. Make the change though.
01349                 KoParagCounter c( *counter );
01350                 c.setDepth( level );
01351                 if ( c.displayLevels() > 1 ) {
01352                     c.setDisplayLevels( c.displayLevels() - 1 );
01353                 }
01354                 KCommand* command = textObject()->setCounterCommand( m_cursor, c );
01355                 textObject()->emitNewCommand( command );
01356             }
01357         }
01358     }
01359     if ( style )
01360         textObject()->applyStyle( m_cursor, style );
01361 }
01362 
01363 KCommand *KoTextView::setChangeCaseOfTextCommand(KoChangeCaseDia::TypeOfCase _type)
01364 {
01365     QString text;
01366     if ( textObject()->hasSelection() )
01367         text = textObject()->selectedText();
01368     if(!text.isEmpty())
01369         return textObject()->changeCaseOfText(cursor(), _type);
01370     else
01371         return 0L;
01372 }
01373 
01374 KCommand *KoTextView::prepareDropMove( KoTextCursor dropCursor )
01375 {
01376     Q_ASSERT( textDocument()->hasSelection( KoTextDocument::Standard ) );
01377     // Dropping into the selection itself ?
01378     KoTextCursor startSel = textDocument()->selectionStartCursor( KoTextDocument::Standard );
01379     KoTextCursor endSel = textDocument()->selectionEndCursor( KoTextDocument::Standard );
01380     bool inSelection = false;
01381     if ( startSel.parag() == endSel.parag() )
01382         inSelection = dropCursor.parag() == startSel.parag()
01383                       && dropCursor.index() >= startSel.index()
01384                       && dropCursor.index() <= endSel.index();
01385     else
01386     {
01387         // Looking at first line first:
01388         inSelection = dropCursor.parag() == startSel.parag() && dropCursor.index() >= startSel.index();
01389         if ( !inSelection )
01390         {
01391             // Look at all other paragraphs except last one
01392             KoTextParag *p = startSel.parag()->next();
01393             while ( !inSelection && p && p != endSel.parag() )
01394             {
01395                 inSelection = ( p == dropCursor.parag() );
01396                 p = p->next();
01397             }
01398             // Look at last paragraph
01399             if ( !inSelection )
01400                 inSelection = dropCursor.parag() == endSel.parag() && dropCursor.index() <= endSel.index();
01401         }
01402     }
01403     if ( inSelection || m_textobj->protectContent() )
01404     {
01405         textDocument()->removeSelection( KoTextDocument::Standard );
01406         textObject()->selectionChangedNotify();
01407         hideCursor();
01408         *cursor() = dropCursor;
01409         showCursor();
01410         ensureCursorVisible();
01411         return 0L;
01412     }
01413     if ( textObject()->protectContent() )
01414     {
01415         textDocument()->removeSelection( KoTextDocument::Standard );
01416         textObject()->selectionChangedNotify();
01417     }
01418     // Tricky. We don't want to do the placeCursor after removing the selection
01419     // (the user pointed at some text with the old selection in place).
01420     // However, something got deleted in our parag, dropCursor's index needs adjustment.
01421     if ( endSel.parag() == dropCursor.parag() )
01422     {
01423         // Does the selection starts before (other parag or same parag) ?
01424         if ( startSel.parag() != dropCursor.parag() || startSel.index() < dropCursor.index() )
01425         {
01426             // If other -> endSel.parag() will get deleted. The final position is in startSel.parag(),
01427             // where the selection started + how much after the end we are. Make a drawing :)
01428             // If same -> simply move back by how many chars we've deleted. Funny thing is, it's the same formula.
01429             int dropIndex = dropCursor.index();
01430             dropCursor.setParag( startSel.parag() );
01431             // If dropCursor - endSel < 0, selection ends after, we're dropping into selection (no-op)
01432             dropCursor.setIndex( dropIndex - QMIN( endSel.index(), dropIndex ) + startSel.index() );
01433         }
01434         kdDebug(32500) << "dropCursor: parag=" << dropCursor.parag()->paragId() << " index=" << dropCursor.index() << endl;
01435     }
01436     KCommand* cmd = textObject()->removeSelectedTextCommand( cursor(), KoTextDocument::Standard );
01437 
01438     hideCursor();
01439     *cursor() = dropCursor;
01440     showCursor();
01441 
01442     return cmd;
01443 }
01444 
01445 
01446 void KoTextView::copyTextOfComment()
01447 {
01448     KoNoteVariable *var = dynamic_cast<KoNoteVariable *>( variable() );
01449     if( var )
01450     {
01451         KURL::List lst;
01452         lst.append( var->note() );
01453         QApplication::clipboard()->setSelectionMode(true);
01454         QApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
01455         QApplication::clipboard()->setSelectionMode(false);
01456         QApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
01457     }
01458 }
01459 
01460 void KoTextView::removeComment()
01461 {
01462     KoNoteVariable *var = dynamic_cast<KoNoteVariable *>( variable() );
01463     if( var )
01464     {
01465         m_cursor->setIndex( m_variablePosition );
01466         textDocument()->setSelectionStart( KoTextDocument::Temp, m_cursor );
01467         m_cursor->setIndex( m_variablePosition + 1 );
01468         textDocument()->setSelectionEnd( KoTextDocument::Temp, m_cursor );
01469         textObject()->removeSelectedText( m_cursor,  KoTextDocument::Temp, i18n("Remove Comment") );
01470     }
01471 }
01472 
01473 KoParagStyle * KoTextView::createStyleFromSelection(const QString & name)
01474 {
01475     KoTextCursor cursor = *m_cursor;
01476     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
01477         cursor = textDocument()->selectionStartCursor( KoTextDocument::Standard );
01478     KoParagStyle * style = new KoParagStyle (name);
01479     KoParagLayout layout(cursor.parag()->paragLayout());
01480     layout.style = style;
01481     style->setFollowingStyle( style );
01482     style->format() = *(cursor.parag()->at(cursor.index())->format());
01483 
01484     style->paragLayout() = layout;
01485     // Select this new style - hmm only the parag layout, we don't want to erase any text-formatting
01486     cursor.parag()->setParagLayout( style->paragLayout() );
01487     return style;
01488 }
01489 
01490 void KoTextView::updateStyleFromSelection( KoParagStyle* style )
01491 {
01492     KoTextCursor cursor = *m_cursor;
01493     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
01494         cursor = textDocument()->selectionStartCursor( KoTextDocument::Standard );
01495 
01496     style->paragLayout() = cursor.parag()->paragLayout();
01497     style->paragLayout().style = style;
01498     style->format() = *(cursor.parag()->at(cursor.index())->format());
01499 }
01500 
01501 void KoTextView::addBookmarks(const QString &url)
01502 {
01503     QString filename = locateLocal( "data", QString::fromLatin1("konqueror/bookmarks.xml") );
01504     KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename,false );
01505     KBookmarkGroup group = bookManager->root();
01506     group.addBookmark( bookManager, url, KURL( url));
01507     bookManager->save();
01508     // delete bookManager;
01509 }
01510 
01511 void KoTextView::copyLink()
01512 {
01513     KoLinkVariable * var=linkVariable();
01514     if(var)
01515     {
01516         KURL::List lst;
01517         lst.append( var->url() );
01518         QApplication::clipboard()->setSelectionMode(true);
01519         QApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
01520         QApplication::clipboard()->setSelectionMode(false);
01521         QApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
01522     }
01523 }
01524 
01525 void KoTextView::removeLink()
01526 {
01527     KoLinkVariable * var=linkVariable();
01528     if(var)
01529     {
01530         KoTextCursor c1 = *m_cursor;
01531         KoTextCursor c2 = *m_cursor;
01532         c1.setIndex(var->index());
01533         c2.setIndex(var->index()+1);
01534         textDocument()->setSelectionStart( KoTextDocument::Temp, &c1 );
01535         textDocument()->setSelectionEnd( KoTextDocument::Temp, &c2 );
01536         KCommand *cmd=textObject()->replaceSelectionCommand( &c1, var->value(),
01537                                         i18n("Remove Link"), KoTextDocument::Temp );
01538         if ( cmd )
01539             textObject()->emitNewCommand( cmd );
01540     }
01541 }
01542 
01543 void KoTextView::setBackSpeller( KoBgSpellCheck* backSpeller )
01544 {
01545     d->m_backSpeller = backSpeller;
01546 }
01547 
01548 #include "KoTextView.moc"
01549 class KoBgSpellCheck;
KDE Home | KDE Accessibility Home | Description of Access Keys