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           }
00385         }
00386             break;
00387         }
00388     }
00389 
00390     if ( clearUndoRedoInfo ) {
00391         textObject()->clearUndoRedoInfo();
00392         if ( d->m_backSpeller )
00393             d->m_backSpeller->setIntraWordEditing( 0, 0 );
00394     }
00395 
00396     textObject()->typingDone();
00397 }
00398 
00399 void KoTextView::setOverwriteMode( bool overwriteMode )
00400 {
00401     m_overwriteMode = overwriteMode;
00402 }
00403 
00404 void KoTextView::insertText( const QString &text )
00405 {
00406     int insertFlags = KoTextObject::DefaultInsertFlags;
00407     if ( m_overwriteMode )
00408         insertFlags |= KoTextObject::OverwriteMode;
00409     textObject()->insert( m_cursor, m_currentFormat, text, i18n("Insert Text"), KoTextDocument::Standard, insertFlags );
00410 }
00411 
00412 void KoTextView::newParagraph()
00413 {
00414     textObject()->insert( m_cursor, m_currentFormat, "\n", i18n("Insert Text"), KoTextDocument::Standard, KoTextObject::CheckNewLine );
00415 }
00416 
00417 void KoTextView::handleKeyReleaseEvent( QKeyEvent * e )
00418 {
00419     if ( e->key() == Key_Alt && d->currentUnicodeNumber() >= 32 )
00420     {
00421         QString text = QChar( d->currentUnicodeNumber() );
00422         d->clearCurrentUnicodeNumber();
00423         insertText( text );
00424         doAutoFormat( m_cursor, m_cursor->parag(),
00425                       m_cursor->index() - 1, text[ text.length() - 1 ] );
00426     }
00427 }
00428 
00429 void KoTextView::handleImStartEvent( QIMEvent * )
00430 {
00431     // nothing to do
00432 }
00433 
00434 void KoTextView::handleImComposeEvent( QIMEvent * e )
00435 {
00436     // remove old preedit
00437     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
00438         textDocument()->removeSelection( KoTextDocument::Standard );
00439     if ( textDocument()->hasSelection( KoTextDocument::InputMethodPreedit ) )
00440         textDocument()->removeSelectedText( KoTextDocument::InputMethodPreedit, m_cursor );
00441 
00442     // insert preedit
00443     int preeditStartIdx = m_cursor->index();
00444     textDocument()->setSelectionStart( KoTextDocument::InputMethodPreedit, m_cursor );
00445     textObject()->insert( m_cursor, m_currentFormat, e->text(), i18n("Insert Text"),
00446                           KoTextDocument::Standard,
00447                           KoTextObject::DoNotRepaint/* DO NOT REPAINT CURSOR! */ );
00448     textDocument()->setSelectionEnd( KoTextDocument::InputMethodPreedit, m_cursor );
00449 
00450     // selection
00451     int preeditSelStart = preeditStartIdx + e->cursorPos();
00452     int preeditSelEnd   = preeditSelStart + e->selectionLength();
00453     m_cursor->setIndex( preeditSelStart );
00454     textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00455     m_cursor->setIndex( preeditSelEnd );
00456     textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
00457 
00458     // set cursor pos
00459     m_cursor->setIndex( preeditSelStart );
00460 
00461     textObject()->emitUpdateUI( true );
00462     textObject()->emitShowCursor();
00463     textObject()->selectionChangedNotify();
00464 }
00465 
00466 void KoTextView::handleImEndEvent( QIMEvent * e )
00467 {
00468     // remove old preedit
00469     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
00470         textDocument()->removeSelection( KoTextDocument::Standard  );
00471     if ( textDocument()->hasSelection( KoTextDocument::InputMethodPreedit ) )
00472         textDocument()->removeSelectedText( KoTextDocument::InputMethodPreedit, m_cursor );
00473 
00474     insertText( e->text() );
00475 
00476     textObject()->emitUpdateUI( true );
00477     textObject()->emitShowCursor();
00478     textObject()->selectionChangedNotify();
00479 }
00480 
00481 void KoTextView::completion()
00482 {
00483     (void) doCompletion(m_cursor, m_cursor->parag(),
00484                      m_cursor->index() - 1);
00485 }
00486 
00487 void KoTextView::moveCursor( CursorAction action, bool select )
00488 {
00489     hideCursor();
00490     bool cursorMoved = false;
00491     if ( select ) {
00492         if ( !textDocument()->hasSelection( KoTextDocument::Standard ) )
00493             textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00494         cursorMoved = moveCursor( action );
00495         if ( textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor ) ) {
00496             textObject()->selectionChangedNotify();
00497         }
00498     } else {
00499         bool redraw = textDocument()->removeSelection( KoTextDocument::Standard );
00500         cursorMoved = moveCursor( action );
00501         if ( redraw ) {
00502             textObject()->selectionChangedNotify();
00503         }
00504     }
00505 
00506     if ( cursorMoved ) // e.g. not when pressing Ctrl/PgDown after the last parag
00507     {
00508         ensureCursorVisible();
00509         // updateUI( true ); // done by moveCursor
00510     }
00511     showCursor();
00512 }
00513 
00514 bool KoTextView::moveCursor( CursorAction action )
00515 {
00516     bool cursorMoved = true;
00517     switch ( action ) {
00518         case MoveBackward:
00519             m_cursor->gotoPreviousLetter();
00520             break;
00521         case MoveWordBackward:
00522             m_cursor->gotoPreviousWord();
00523             break;
00524         case MoveForward:
00525             m_cursor->gotoNextLetter();
00526             break;
00527         case MoveWordForward:
00528             m_cursor->gotoNextWord();
00529             break;
00530         case MoveUp:
00531             m_cursor->gotoUp();
00532             break;
00533         case MoveDown:
00534             m_cursor->gotoDown();
00535             break;
00536         case MoveViewportUp:
00537             cursorMoved = pgUpKeyPressed();
00538             break;
00539         case MoveViewportDown:
00540             cursorMoved = pgDownKeyPressed();
00541             break;
00542         case MovePgUp:
00543             ctrlPgUpKeyPressed();
00544             break;
00545         case MovePgDown:
00546             ctrlPgDownKeyPressed();
00547             break;
00548         case MoveLineStart:
00549             m_cursor->gotoLineStart();
00550             break;
00551         case MoveHome:
00552             m_cursor->gotoHome();
00553             break;
00554         case MoveLineEnd:
00555             m_cursor->gotoLineEnd();
00556             break;
00557         case MoveEnd:
00558             textObject()->ensureFormatted( textDocument()->lastParag() );
00559             m_cursor->gotoEnd();
00560             break;
00561         case MoveParagUp: {
00562             KoTextParag * parag = m_cursor->parag()->prev();
00563             if ( m_cursor->index()==0 && parag )
00564             {
00565                 m_cursor->setParag( parag );
00566                 m_cursor->setIndex( 0 );
00567             }
00568             else m_cursor->setIndex( 0 );
00569         } break;
00570         case MoveParagDown: {
00571             KoTextParag * parag = m_cursor->parag()->next();
00572             if ( parag )
00573             {
00574                 m_cursor->setParag( parag );
00575                 m_cursor->setIndex( 0 );
00576             }
00577         } break;
00578     }
00579 
00580     updateUI( true );
00581     return cursorMoved;
00582 }
00583 
00584 KoTextCursor KoTextView::selectWordUnderCursor( const KoTextCursor& cursor, int selectionId )
00585 {
00586     KoTextCursor c1 = cursor;
00587     KoTextCursor c2 = cursor;
00588     if ( cursor.index() > 0 && !cursor.parag()->at( cursor.index()-1 )->c.isSpace() )
00589         c1.gotoWordLeft();
00590     if ( !cursor.parag()->at( cursor.index() )->c.isSpace() && !cursor.atParagEnd() )
00591         c2.gotoWordRight();
00592 
00593     // The above is almost correct, but gotoWordRight also skips the spaces/punctuations
00594     // until the next word. So the 'word under cursor' contained e.g. that trailing space.
00595     // To be on the safe side, we skip spaces/punctuations on both sides:
00596     KoTextString *s = cursor.parag()->string();
00597     bool beginFound = false;
00598     for ( int i = c1.index(); i< c2.index(); i++)
00599     {
00600         const QChar ch = s->at(i).c;
00601         // This list comes from KoTextCursor::gotoPreviousWord.
00602         // Can't use QChar::isPunct since "'" and "-" are not word separators
00603         const bool isWordDelimiter = ch.isSpace()
00604                                    || ch.category() == QChar::Punctuation_Open // e.g. '('
00605                                    || ch.category() == QChar::Punctuation_Close // e.g. ')'
00606                                    || ch.category() == QChar::Punctuation_Other // see http://www.fileformat.info/info/unicode/category/Po/list.htm
00607                                    ;
00608 
00609         if( !beginFound && !isWordDelimiter )
00610         {
00611             c1.setIndex(i);
00612             beginFound = true;
00613         }
00614         else if ( beginFound && isWordDelimiter )
00615         {
00616             c2.setIndex(i);
00617             break;
00618         }
00619     }
00620 
00621     textDocument()->setSelectionStart( selectionId, &c1 );
00622     textDocument()->setSelectionEnd( selectionId, &c2 );
00623     return c2;
00624 }
00625 
00626 KoTextCursor KoTextView::selectParagUnderCursor( const KoTextCursor& cursor, int selectionId, bool copyAndNotify )
00627 {
00628     KoTextCursor c1 = cursor;
00629     KoTextCursor c2 = cursor;
00630     c1.setIndex(0);
00631     c2.setIndex(c1.parag()->string()->length() - 1);
00632     textDocument()->setSelectionStart( selectionId, &c1 );
00633     textDocument()->setSelectionEnd( selectionId, &c2 );
00634     if ( copyAndNotify )
00635     {
00636         textObject()->selectionChangedNotify();
00637         // Copy the selection.
00638         QApplication::clipboard()->setSelectionMode( true );
00639         emit copy();
00640         QApplication::clipboard()->setSelectionMode( false );
00641     }
00642     return c2;
00643 }
00644 
00645 void KoTextView::extendParagraphSelection( const QPoint& iPoint )
00646 {
00647     hideCursor();
00648     KoTextCursor oldCursor = *m_cursor;
00649     placeCursor( iPoint );
00650 
00651     bool redraw = FALSE;
00652     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
00653     {
00654         redraw = textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
00655         if ( textDocument()->isSelectionSwapped( KoTextDocument::Standard ) )
00656             m_cursor->setIndex( 0 );
00657         else
00658             m_cursor->setIndex( m_cursor->parag()->string()->length() - 1 );
00659         textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
00660     }
00661     //else // it may be that the initial click was out of the frame
00662     //    textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00663 
00664     if ( redraw )
00665         textObject()->selectionChangedNotify( false );
00666 
00667     showCursor();
00668 }
00669 
00670 QString KoTextView::wordUnderCursor( const KoTextCursor& cursor )
00671 {
00672     selectWordUnderCursor( cursor, KoTextDocument::Temp );
00673     QString text = textObject()->selectedText( KoTextDocument::Temp );
00674     bool hasCustomItems = textObject()->selectionHasCustomItems( KoTextDocument::Temp );
00675     textDocument()->removeSelection( KoTextDocument::Temp );
00676     if( !hasCustomItems )
00677         return text;
00678     return QString::null;
00679 }
00680 
00681 bool KoTextView::handleMousePressEvent( QMouseEvent *e, const QPoint &iPoint, bool canStartDrag, bool insertDirectCursor )
00682 {
00683     bool addParag = false;
00684     mightStartDrag = FALSE;
00685     hideCursor();
00686 
00687     if (possibleTripleClick)
00688     {
00689         handleMouseTripleClickEvent( e, iPoint );
00690         return addParag;
00691     }
00692 
00693     KoTextCursor oldCursor = *m_cursor;
00694     addParag = placeCursor( iPoint, insertDirectCursor&& isReadWrite() );
00695     ensureCursorVisible();
00696 
00697     if ( e->button() != LeftButton )
00698     {
00699         showCursor();
00700         return addParag;
00701     }
00702 
00703     KoLinkVariable* lv = linkVariable();
00704     if ( lv && openLink( lv ) )
00705     {
00706         return addParag;
00707     }
00708 
00709     KoTextDocument * textdoc = textDocument();
00710     if ( canStartDrag && textdoc->inSelection( KoTextDocument::Standard, iPoint ) ) {
00711         mightStartDrag = TRUE;
00712         m_textobj->emitShowCursor();
00713         dragStartTimer->start( QApplication::startDragTime(), TRUE );
00714         dragStartPos = e->pos();
00715         return addParag;
00716     }
00717 
00718     bool redraw = FALSE;
00719     if ( textdoc->hasSelection( KoTextDocument::Standard ) ) {
00720         if ( !( e->state() & ShiftButton ) ) {
00721             redraw = textdoc->removeSelection( KoTextDocument::Standard );
00722             textdoc->setSelectionStart( KoTextDocument::Standard, m_cursor );
00723         } else {
00724             redraw = textdoc->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
00725         }
00726     } else {
00727         if ( !( e->state() & ShiftButton ) ) {
00728             textdoc->setSelectionStart( KoTextDocument::Standard, m_cursor );
00729         } else {
00730             textdoc->setSelectionStart( KoTextDocument::Standard, &oldCursor );
00731             redraw = textdoc->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
00732         }
00733     }
00734 
00735     //kdDebug(32500) << "KoTextView::mousePressEvent redraw=" << redraw << endl;
00736     if ( !redraw ) {
00737         showCursor();
00738     } else {
00739         textObject()->selectionChangedNotify();
00740     }
00741     return addParag;
00742 }
00743 
00744 void KoTextView::handleMouseMoveEvent( QMouseEvent*, const QPoint& iPoint )
00745 {
00746     hideCursor();
00747     KoTextCursor oldCursor = *m_cursor;
00748     placeCursor( iPoint );
00749 
00750     // Double click + mouse still down + moving the mouse selects full words.
00751     if ( inDoubleClick ) {
00752         KoTextCursor cl = *m_cursor;
00753         cl.gotoWordLeft();
00754         KoTextCursor cr = *m_cursor;
00755         cr.gotoWordRight();
00756 
00757         int diff = QABS( oldCursor.parag()->at( oldCursor.index() )->x - iPoint.x() );
00758         int ldiff = QABS( cl.parag()->at( cl.index() )->x - iPoint.x() );
00759         int rdiff = QABS( cr.parag()->at( cr.index() )->x - iPoint.x() );
00760 
00761         if ( m_cursor->parag()->lineStartOfChar( m_cursor->index() ) !=
00762              oldCursor.parag()->lineStartOfChar( oldCursor.index() ) )
00763             diff = 0xFFFFFF;
00764 
00765         if ( rdiff < diff && rdiff < ldiff )
00766             *m_cursor = cr;
00767         else if ( ldiff < diff && ldiff < rdiff )
00768             *m_cursor = cl;
00769         else
00770             *m_cursor = oldCursor;
00771     }
00772 
00773     bool redraw = FALSE;
00774     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
00775         redraw = textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
00776     else // it may be that the initial click was out of the frame
00777         textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
00778 
00779     if ( redraw )
00780         textObject()->selectionChangedNotify( false );
00781 
00782     showCursor();
00783 }
00784 
00785 void KoTextView::handleMouseReleaseEvent()
00786 {
00787     if ( dragStartTimer->isActive() )
00788         dragStartTimer->stop();
00789     if ( mightStartDrag ) {
00790         textObject()->selectAll( FALSE );
00791         mightStartDrag = false;
00792     }
00793     else
00794     {
00795         if ( textDocument()->selectionStartCursor( KoTextDocument::Standard ) == textDocument()->selectionEndCursor( KoTextDocument::Standard ) )
00796         {
00797             textDocument()->removeSelection( KoTextDocument::Standard );
00798         }
00799 
00800         textObject()->selectionChangedNotify();
00801 
00802         // Copy the selection.
00803         QApplication::clipboard()->setSelectionMode( true );
00804         emit copy();
00805         QApplication::clipboard()->setSelectionMode( false );
00806     }
00807 
00808     inDoubleClick = FALSE;
00809     m_textobj->emitShowCursor();
00810 }
00811 
00812 void KoTextView::handleMouseDoubleClickEvent( QMouseEvent*ev, const QPoint& i )
00813 {
00814   //after a triple click it's not a double click but a simple click
00815   //but as triple click didn't exist it's necessary to do it.
00816     if(afterTripleClick)
00817     {
00818         handleMousePressEvent( ev, i );
00819         return;
00820     }
00821 
00822     inDoubleClick = TRUE;
00823     *m_cursor = selectWordUnderCursor( *m_cursor );
00824     textObject()->selectionChangedNotify();
00825     // Copy the selection.
00826     QApplication::clipboard()->setSelectionMode( true );
00827     emit copy();
00828     QApplication::clipboard()->setSelectionMode( false );
00829 
00830     possibleTripleClick=true;
00831 
00832     QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout()));
00833 }
00834 
00835 void KoTextView::tripleClickTimeout()
00836 {
00837    possibleTripleClick=false;
00838 }
00839 
00840 void KoTextView::handleMouseTripleClickEvent( QMouseEvent*ev, const QPoint& /* Currently unused */ )
00841 {
00842     if ( ev->button() != LeftButton)
00843     {
00844         showCursor();
00845         return;
00846     }
00847     afterTripleClick= true;
00848     inDoubleClick = FALSE;
00849     *m_cursor = selectParagUnderCursor( *m_cursor );
00850     QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(afterTripleClickTimeout()));
00851 }
00852 
00853 void KoTextView::afterTripleClickTimeout()
00854 {
00855     afterTripleClick=false;
00856 }
00857 
00858 bool KoTextView::maybeStartDrag( QMouseEvent* e )
00859 {
00860     if ( mightStartDrag ) {
00861         dragStartTimer->stop();
00862         if ( ( e->pos() - dragStartPos ).manhattanLength() > QApplication::startDragDistance() )
00863             startDrag();
00864         return true;
00865     }
00866     return false;
00867 }
00868 
00869 bool KoTextView::insertParagraph(const QPoint &pos)
00870 {
00871     KoTextParag *last = textDocument()->lastParag();
00872     KoTextFormat *f = 0;
00873     KoParagStyle *style = last->style();
00874     KoParagCounter *counter = last->counter();
00875     int diff = (pos.y()- textDocument()->height());
00876     f = last->at( last->length()-1 )->format();
00877     int height =f->height();
00878     int nbParag = (diff / height);
00879     QFontMetrics fm = f->refFontMetrics();
00880     for (int i = 0; i < nbParag ;i++)
00881     {
00882         KoTextParag *s=textDocument()->createParag( textDocument(), last );
00883         if ( f )
00884         s->setFormat( 0, 1, f, TRUE );
00885         if ( style )
00886             s->setStyle( style );
00887         s->setCounter( counter );
00888         last = s;
00889     }
00890     bool createParag = (nbParag > 0 );
00891     if ( createParag )
00892     {
00893         if ( pos.x() + f->width(' ') >= textDocument()->width())
00894         {
00895             //FIXME me bidi.
00896             //change parag alignment => right alignment
00897             last->setAlignment( Qt::AlignRight );
00898         }
00899         else
00900         {
00901             int nbSpace = pos.x()/f->width(' ');
00902             QString tmp;
00903             for (int i = 0; i< nbSpace; i++)
00904             {
00905                 tmp+=' ';
00906             }
00907             last->insert( 0, tmp );
00908         }
00909     }
00910     return createParag;
00911 
00912 }
00913 
00914 bool KoTextView::placeCursor( const QPoint &pos, bool insertDirectCursor )
00915 {
00916     bool addParag = false;
00917     if ( insertDirectCursor && (pos.y()>textDocument()->height()) )
00918         addParag = insertParagraph(pos);
00919     KoTextParag *s = 0L;
00920     if ( addParag )
00921         s = textDocument()->lastParag();
00922     else
00923         s = textDocument()->firstParag();
00924     m_cursor->place( pos, s, false, &m_variablePosition );
00925     if ( m_variablePosition != -1 )
00926         kdDebug() << k_funcinfo << " m_variablePosition set to " << m_variablePosition << endl;
00927     updateUI( true );
00928     return addParag;
00929 }
00930 
00931 void KoTextView::blinkCursor()
00932 {
00933     //kdDebug(32500) << "KoTextView::blinkCursor m_cursorVisible=" << m_cursorVisible
00934     //          << " blinkCursorVisible=" << blinkCursorVisible << endl;
00935     if ( !m_cursorVisible )
00936         return;
00937     bool cv = m_cursorVisible;
00938     blinkCursorVisible = !blinkCursorVisible;
00939     drawCursor( blinkCursorVisible );
00940     m_cursorVisible = cv;
00941 }
00942 
00943 void KoTextView::drawCursor( bool visible )
00944 {
00945     m_cursorVisible = visible;
00946     // The rest is up to the app ;)
00947 }
00948 
00949 void KoTextView::focusInEvent()
00950 {
00951     if ( QApplication::cursorFlashTime() > 0 )
00952         blinkTimer->start( QApplication::cursorFlashTime() / 2 );
00953     showCursor();
00954 }
00955 
00956 void KoTextView::focusOutEvent()
00957 {
00958     blinkTimer->stop();
00959     hideCursor();
00960 }
00961 
00962 /*void KoTextView::setFormat( KoTextFormat * newFormat, int flags, bool zoomFont)
00963 {
00964     textObject()->setFormat( m_cursor, m_currentFormat, newFormat, flags, zoomFont );
00965 }*/
00966 
00967 KCommand* KoTextView::setFormatCommand( const KoTextFormat * newFormat, int flags, bool zoomFont)
00968 {
00969     return textObject()->setFormatCommand( m_cursor, &m_currentFormat, newFormat, flags, zoomFont );
00970 }
00971 
00972 void KoTextView::dragStarted()
00973 {
00974     mightStartDrag = FALSE;
00975     inDoubleClick = FALSE;
00976 }
00977 
00978 void KoTextView::applyStyle( const KoParagStyle * style )
00979 {
00980     if ( style )
00981     {
00982         textObject()->applyStyle( m_cursor, style );
00983         showCurrentFormat();
00984     }
00985 }
00986 
00987 void KoTextView::updateUI( bool updateFormat, bool /*force*/ )
00988 {
00989     // Update UI - only for those items which have changed
00990 
00991     if ( updateFormat )
00992     {
00993         int i = cursor()->index();
00994         if ( i > 0 )
00995             --i;
00996 #ifdef DEBUG_FORMATS
00997         if ( currentFormat() )
00998             kdDebug(32500) << "KoTextView::updateUI old currentFormat=" << currentFormat()
00999                            << " " << currentFormat()->key()
01000                            << " parag format=" << cursor()->parag()->at( i )->format()->key() << endl;
01001         else
01002             kdDebug(32500) << "KoTextView::updateUI old currentFormat=0" << endl;
01003 #endif
01004         if ( !currentFormat() || currentFormat()->key() != cursor()->parag()->at( i )->format()->key() )
01005         {
01006             if ( currentFormat() )
01007                 currentFormat()->removeRef();
01008 #ifdef DEBUG_FORMATS
01009             kdDebug(32500) << "Setting currentFormat from format " << cursor()->parag()->at( i )->format()
01010                       << " ( character " << i << " in paragraph " << cursor()->parag()->paragId() << " )" << endl;
01011 #endif
01012             setCurrentFormat( textDocument()->formatCollection()->format( cursor()->parag()->at( i )->format() ) );
01013             if ( currentFormat()->isMisspelled() ) {
01014                 KoTextFormat fNoMisspelled( *currentFormat() );
01015                 fNoMisspelled.setMisspelled( false );
01016                 currentFormat()->removeRef();
01017                 setCurrentFormat( textDocument()->formatCollection()->format( &fNoMisspelled ) );
01018             }
01019             showCurrentFormat();
01020         }
01021     }
01022 }
01023 
01024 void KoTextView::showCurrentFormat()
01025 {
01026     //kdDebug(32500) << "KoTextView::showCurrentFormat currentFormat=" << currentFormat() << " " << currentFormat()->key() << endl;
01027     KoTextFormat format = *currentFormat();
01028     //format.setPointSize( textObject()->docFontSize( currentFormat() ) ); // "unzoom" the font size
01029     showFormat( &format );
01030 }
01031 
01032 KCommand * KoTextView::setCounterCommand( const KoParagCounter & counter )
01033 {
01034      return textObject()->setCounterCommand( m_cursor, counter );
01035 }
01036 KCommand * KoTextView::setAlignCommand( int align )
01037 {
01038      return textObject()->setAlignCommand( m_cursor, align );
01039 }
01040 KCommand * KoTextView::setLineSpacingCommand( double spacing, KoParagLayout::SpacingType _type)
01041 {
01042      return textObject()->setLineSpacingCommand( m_cursor, spacing, _type);
01043 }
01044 KCommand * KoTextView::setBordersCommand( const KoBorder& leftBorder, const KoBorder& rightBorder, const KoBorder& bottomBorder, const KoBorder& topBorder )
01045 {
01046     return textObject()->setBordersCommand( m_cursor, leftBorder, rightBorder, bottomBorder, topBorder );
01047 }
01048 KCommand * KoTextView::setJoinBordersCommand( bool join )
01049 {
01050     return textObject()->setJoinBordersCommand( m_cursor, join );
01051 }
01052 KCommand * KoTextView::setMarginCommand( QStyleSheetItem::Margin m, double margin )
01053 {
01054     return textObject()->setMarginCommand( m_cursor, m, margin );
01055 }
01056 KCommand * KoTextView::setTabListCommand( const KoTabulatorList & tabList )
01057 {
01058     return textObject()->setTabListCommand( m_cursor, tabList );
01059 }
01060 KCommand * KoTextView::setBackgroundColorCommand( const QColor & color )
01061 {
01062     return textObject()->setBackgroundColorCommand( m_cursor, color );
01063 }
01064 
01065 KoTextDocument * KoTextView::textDocument() const
01066 {
01067     return textObject()->textDocument();
01068 }
01069 
01070 KoVariable *KoTextView::variable()
01071 {
01072     if ( m_variablePosition < 0 )
01073         return 0;
01074     // Can't use m_cursor here, it could be before or after the variable, depending on which half of it was clicked
01075     return textObject()->variableAtPosition( m_cursor->parag(), m_variablePosition );
01076 }
01077 
01078 KoLinkVariable * KoTextView::linkVariable()
01079 {
01080     return dynamic_cast<KoLinkVariable *>(variable());
01081 }
01082 
01083 QPtrList<KAction> KoTextView::dataToolActionList(KInstance * instance, const QString& word, bool & _singleWord )
01084 {
01085     m_singleWord = false;
01086     m_wordUnderCursor = QString::null;
01087     QString text;
01088     if ( textObject()->hasSelection() )
01089     {
01090         text = textObject()->selectedText();
01091         if ( text.find(' ') == -1 && text.find('\t') == -1 && text.find(KoTextObject::customItemChar()) == -1 )
01092         {
01093             m_singleWord = true;
01094         }
01095         else
01096          {
01097             m_singleWord = false;
01098             //laurent : don't try to search thesaurus when we have a customItemChar.
01099             if( text.find(KoTextObject::customItemChar())!=-1)
01100                 text = QString::null;
01101         }
01102     }
01103     else // No selection -> use word under cursor
01104     {
01105         if ( !word.isEmpty() )
01106         {
01107             m_singleWord = true;
01108             m_wordUnderCursor = word;
01109             text = word;
01110         }
01111     }
01112 
01113     if ( text.isEmpty() || textObject()->protectContent()) // Nothing to apply a tool to
01114         return QPtrList<KAction>();
01115 
01116     // Any tool that works on plain text is relevant
01117     QValueList<KDataToolInfo> tools;
01118     tools +=KDataToolInfo::query( "QString", "text/plain", instance );
01119 
01120     // Add tools that work on a single word if that is the case
01121     if ( m_singleWord )
01122     {
01123         _singleWord = true;
01124         tools += KDataToolInfo::query( "QString", "application/x-singleword", instance );
01125     }
01126     // Maybe one day we'll have tools that use libkotext (or qt3's qrt), to act on formatted text
01127     tools += KDataToolInfo::query( "KoTextString", "application/x-qrichtext", instance );
01128 
01129     return KDataToolAction::dataToolActionList( tools, this, SLOT( slotToolActivated( const KDataToolInfo &, const QString & ) ) );
01130 }
01131 
01132 QString KoTextView::currentWordOrSelection() const
01133 {
01134     if ( textObject()->hasSelection() )
01135         return textObject()->selectedText();
01136     else
01137         return m_wordUnderCursor;
01138 }
01139 
01140 void KoTextView::slotToolActivated( const KDataToolInfo & info, const QString & command )
01141 {
01142     KDataTool* tool = info.createTool( );
01143     if ( !tool )
01144     {
01145         kdWarning() << "Could not create Tool !" << endl;
01146         return;
01147     }
01148 
01149     kdDebug(32500) << "KWTextFrameSetEdit::slotToolActivated command=" << command
01150               << " dataType=" << info.dataType() << endl;
01151 
01152     QString text;
01153     if ( textObject()->hasSelection() )
01154         text = textObject()->selectedText();
01155     else
01156         text = m_wordUnderCursor;
01157 
01158     // Preferred type is richtext
01159     QString mimetype = "application/x-qrichtext";
01160     QString datatype = "KoTextString";
01161     // If unsupported, try text/plain
01162     if ( !info.mimeTypes().contains( mimetype ) )
01163     {
01164         mimetype = "text/plain";
01165         datatype = "QString";
01166     }
01167     // If unsupported (and if we have a single word indeed), try application/x-singleword
01168     if ( !info.mimeTypes().contains( mimetype ) && m_singleWord )
01169         mimetype = "application/x-singleword";
01170 
01171     kdDebug(32500) << "Running tool with datatype=" << datatype << " mimetype=" << mimetype << endl;
01172 
01173     QString origText = text;
01174     if ( tool->run( command, &text, datatype, mimetype) )
01175     {
01176         kdDebug(32500) << "Tool ran. Text is now " << text << endl;
01177         if ( origText != text )
01178         {
01179             if ( !textObject()->hasSelection() )
01180             {
01181                 // Warning: ok for now, but wrong cursor if RMB doesn't place cursor anymore
01182                 selectWordUnderCursor( *m_cursor );
01183             }
01184             // replace selection with 'text'
01185             textObject()->emitNewCommand( textObject()->replaceSelectionCommand(
01186                 cursor(), text, i18n("Replace Word") ));
01187         }
01188     }
01189     delete tool;
01190 }
01191 
01192 bool KoTextView::openLink( KoLinkVariable* variable )
01193 {
01194     kdDebug() << k_funcinfo << variable->url() << endl;
01195     KURL url( variable->url() );
01196     if( url.isValid() )
01197     {
01198         (void) new KRun( url );
01199         return true;
01200     }
01201     else
01202     {
01203         KMessageBox::sorry( 0, i18n("%1 is not a valid link.").arg( variable->url() ) );
01204         return false;
01205     }
01206 }
01207 
01208 
01209 void KoTextView::insertSoftHyphen()
01210 {
01211     textObject()->insert( cursor(), currentFormat(), QChar(0xad) /* see QRichText */,
01212                           i18n("Insert Soft Hyphen") );
01213 }
01214 
01215 void KoTextView::insertLineBreak()
01216 {
01217     textObject()->insert( cursor(), currentFormat(), QChar('\n'),
01218                           i18n("Insert Line Break") );
01219 }
01220 
01221 void KoTextView::insertNonbreakingSpace()
01222 {
01223     textObject()->insert( cursor(), currentFormat(), QChar(0xa0) /* see QRichText */,
01224                           i18n("Insert Non-Breaking Space") );
01225 }
01226 
01227 void KoTextView::insertNonbreakingHyphen()
01228 {
01229     textObject()->insert( cursor(), currentFormat(), QChar(0x2013),
01230                           i18n("Insert Non-Breaking Hyphen") );
01231 }
01232 
01233 void KoTextView::insertSpecialChar(QChar _c, const QString& font)
01234 {
01235     KoTextFormat * newFormat = new KoTextFormat(*currentFormat());
01236     newFormat->setFamily( font );
01237     if ( textObject()->hasSelection() )
01238     {
01239         KoTextFormat * lastFormat = currentFormat();
01240 
01241         KCommand *cmd = textObject()->setFormatCommand( cursor(), &lastFormat, newFormat, KoTextFormat::Family );
01242 
01243         KMacroCommand* macroCmd = new KMacroCommand( i18n("Insert Special Char") );
01244         macroCmd->addCommand( cmd );
01245         macroCmd->addCommand( textObject()->replaceSelectionCommand(
01246                                   cursor(), _c, QString::null) );
01247         textObject()->emitNewCommand( macroCmd );
01248     }
01249     else
01250     {
01251         textObject()->insert( cursor(), newFormat, _c, i18n("Insert Special Char"));
01252         delete newFormat;
01253     }
01254 }
01255 
01256 const KoParagLayout * KoTextView::currentParagLayoutFormat() const
01257 {
01258     KoTextParag * parag = m_cursor->parag();
01259     return &(parag->paragLayout());
01260 }
01261 
01262 bool KoTextView::rtl() const
01263 {
01264     return m_cursor->parag()->string()->isRightToLeft();
01265 }
01266 
01267 KCommand* KoTextView::setParagLayoutFormatCommand( KoParagLayout *newLayout, int flags, int marginIndex )
01268 {
01269     return textObject()->setParagLayoutCommand( m_cursor, *newLayout, KoTextDocument::Standard,
01270                                                 flags, marginIndex, true /*createUndoRedo*/ );
01271 }
01272 
01273 // Heading1 -> Heading2 -> Heading3 -> normal -> 1 -> 1.1 -> 1.1.1
01274 void KoTextView::increaseNumberingLevel( const KoStyleCollection* styleCollection )
01275 {
01276     // TODO: do this for each paragraph in the selection
01277     KoParagStyle* style = 0;
01278     int level = 0;
01279     KoParagCounter* counter = m_cursor->parag()->counter();
01280     if ( counter )
01281         level = counter->depth() + 1;
01282     if ( m_cursor->parag()->style()->isOutline() )
01283     {
01284         QValueVector<KoParagStyle *> outlineStyles = styleCollection->outlineStyles();
01285         while ( level < 10 && !style ) {
01286             style = outlineStyles[ level ];
01287             ++level;
01288         }
01289         if ( !style ) // no lower-level heading exists, use standard style
01290             style = styleCollection->defaultStyle();
01291     }
01292     else // non-outline, just a numbered list
01293     {
01294         // Try to find a style with this depth, to know if the user wants display-levels etc.
01295         style = styleCollection->numberedStyleForLevel( level );
01296         if ( !style ) { // not found. Make the change though.
01297             KoParagCounter c;
01298             if (counter) {
01299                 c = *counter;
01300                 c.setDepth( level );
01301                 c.setDisplayLevels( c.displayLevels() + 1 );
01302             } else {
01303                 // Start a simple numbered list.
01304                 c.setNumbering(KoParagCounter::NUM_LIST);
01305                 c.setStyle(KoParagCounter::STYLE_NUM);
01306             }
01307             KCommand* command = textObject()->setCounterCommand( m_cursor, c );
01308             textObject()->emitNewCommand( command );
01309         }
01310     }
01311     if ( style ) // can't be 0
01312         textObject()->applyStyle( m_cursor, style );
01313 }
01314 
01315 // 1.1.1 -> 1.1 -> 1 -> normal -> Heading3 -> Heading2 -> Heading1
01316 void KoTextView::decreaseNumberingLevel( const KoStyleCollection* styleCollection )
01317 {
01318     // TODO: do this for each paragraph in the selection
01319     KoParagCounter* counter = m_cursor->parag()->counter();
01320     int level = 9;
01321     if ( counter )
01322         level = counter->depth() - 1;
01323     KoParagStyle* style = 0;
01324     if ( m_cursor->parag()->style()->isOutline() || !counter ) // heading or normal
01325     {
01326         if ( level == -1 ) // nothing higher than Heading1
01327             return;
01328         QValueVector<KoParagStyle *> outlineStyles = styleCollection->outlineStyles();
01329         while ( level >= 0 && !style ) {
01330             style = outlineStyles[ level ];
01331             --level;
01332         }
01333     }
01334     else // non-outline, numbered list
01335     {
01336         if ( level == -1 )
01337             style = styleCollection->defaultStyle();
01338         else
01339         {
01340             style = styleCollection->numberedStyleForLevel( level );
01341             if ( !style ) { // not found. Make the change though.
01342                 KoParagCounter c( *counter );
01343                 c.setDepth( level );
01344                 if ( c.displayLevels() > 1 ) {
01345                     c.setDisplayLevels( c.displayLevels() - 1 );
01346                 }
01347                 KCommand* command = textObject()->setCounterCommand( m_cursor, c );
01348                 textObject()->emitNewCommand( command );
01349             }
01350         }
01351     }
01352     if ( style )
01353         textObject()->applyStyle( m_cursor, style );
01354 }
01355 
01356 KCommand *KoTextView::setChangeCaseOfTextCommand(KoChangeCaseDia::TypeOfCase _type)
01357 {
01358     QString text;
01359     if ( textObject()->hasSelection() )
01360         text = textObject()->selectedText();
01361     if(!text.isEmpty())
01362         return textObject()->changeCaseOfText(cursor(), _type);
01363     else
01364         return 0L;
01365 }
01366 
01367 KCommand *KoTextView::prepareDropMove( KoTextCursor dropCursor )
01368 {
01369     Q_ASSERT( textDocument()->hasSelection( KoTextDocument::Standard ) );
01370     // Dropping into the selection itself ?
01371     KoTextCursor startSel = textDocument()->selectionStartCursor( KoTextDocument::Standard );
01372     KoTextCursor endSel = textDocument()->selectionEndCursor( KoTextDocument::Standard );
01373     bool inSelection = false;
01374     if ( startSel.parag() == endSel.parag() )
01375         inSelection = dropCursor.parag() == startSel.parag()
01376                       && dropCursor.index() >= startSel.index()
01377                       && dropCursor.index() <= endSel.index();
01378     else
01379     {
01380         // Looking at first line first:
01381         inSelection = dropCursor.parag() == startSel.parag() && dropCursor.index() >= startSel.index();
01382         if ( !inSelection )
01383         {
01384             // Look at all other paragraphs except last one
01385             KoTextParag *p = startSel.parag()->next();
01386             while ( !inSelection && p && p != endSel.parag() )
01387             {
01388                 inSelection = ( p == dropCursor.parag() );
01389                 p = p->next();
01390             }
01391             // Look at last paragraph
01392             if ( !inSelection )
01393                 inSelection = dropCursor.parag() == endSel.parag() && dropCursor.index() <= endSel.index();
01394         }
01395     }
01396     if ( inSelection || m_textobj->protectContent() )
01397     {
01398         textDocument()->removeSelection( KoTextDocument::Standard );
01399         textObject()->selectionChangedNotify();
01400         hideCursor();
01401         *cursor() = dropCursor;
01402         showCursor();
01403         ensureCursorVisible();
01404         return 0L;
01405     }
01406     if ( textObject()->protectContent() )
01407     {
01408         textDocument()->removeSelection( KoTextDocument::Standard );
01409         textObject()->selectionChangedNotify();
01410     }
01411     // Tricky. We don't want to do the placeCursor after removing the selection
01412     // (the user pointed at some text with the old selection in place).
01413     // However, something got deleted in our parag, dropCursor's index needs adjustment.
01414     if ( endSel.parag() == dropCursor.parag() )
01415     {
01416         // Does the selection starts before (other parag or same parag) ?
01417         if ( startSel.parag() != dropCursor.parag() || startSel.index() < dropCursor.index() )
01418         {
01419             // If other -> endSel.parag() will get deleted. The final position is in startSel.parag(),
01420             // where the selection started + how much after the end we are. Make a drawing :)
01421             // If same -> simply move back by how many chars we've deleted. Funny thing is, it's the same formula.
01422             int dropIndex = dropCursor.index();
01423             dropCursor.setParag( startSel.parag() );
01424             // If dropCursor - endSel < 0, selection ends after, we're dropping into selection (no-op)
01425             dropCursor.setIndex( dropIndex - QMIN( endSel.index(), dropIndex ) + startSel.index() );
01426         }
01427         kdDebug(32500) << "dropCursor: parag=" << dropCursor.parag()->paragId() << " index=" << dropCursor.index() << endl;
01428     }
01429     KCommand* cmd = textObject()->removeSelectedTextCommand( cursor(), KoTextDocument::Standard );
01430 
01431     hideCursor();
01432     *cursor() = dropCursor;
01433     showCursor();
01434 
01435     return cmd;
01436 }
01437 
01438 
01439 void KoTextView::copyTextOfComment()
01440 {
01441     KoNoteVariable *var = dynamic_cast<KoNoteVariable *>( variable() );
01442     if( var )
01443     {
01444         KURL::List lst;
01445         lst.append( var->note() );
01446         QApplication::clipboard()->setSelectionMode(true);
01447         QApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
01448         QApplication::clipboard()->setSelectionMode(false);
01449         QApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
01450     }
01451 }
01452 
01453 void KoTextView::removeComment()
01454 {
01455     KoNoteVariable *var = dynamic_cast<KoNoteVariable *>( variable() );
01456     if( var )
01457     {
01458         m_cursor->setIndex( m_variablePosition );
01459         textDocument()->setSelectionStart( KoTextDocument::Temp, m_cursor );
01460         m_cursor->setIndex( m_variablePosition + 1 );
01461         textDocument()->setSelectionEnd( KoTextDocument::Temp, m_cursor );
01462         textObject()->removeSelectedText( m_cursor,  KoTextDocument::Temp, i18n("Remove Comment") );
01463     }
01464 }
01465 
01466 KoParagStyle * KoTextView::createStyleFromSelection(const QString & name)
01467 {
01468     KoTextCursor cursor = *m_cursor;
01469     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
01470         cursor = textDocument()->selectionStartCursor( KoTextDocument::Standard );
01471     KoParagStyle * style = new KoParagStyle (name);
01472     KoParagLayout layout(cursor.parag()->paragLayout());
01473     layout.style = style;
01474     style->setFollowingStyle( style );
01475     style->format() = *(cursor.parag()->at(cursor.index())->format());
01476 
01477     style->paragLayout() = layout;
01478     // Select this new style - hmm only the parag layout, we don't want to erase any text-formatting
01479     cursor.parag()->setParagLayout( style->paragLayout() );
01480     return style;
01481 }
01482 
01483 void KoTextView::updateStyleFromSelection( KoParagStyle* style )
01484 {
01485     KoTextCursor cursor = *m_cursor;
01486     if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
01487         cursor = textDocument()->selectionStartCursor( KoTextDocument::Standard );
01488 
01489     style->paragLayout() = cursor.parag()->paragLayout();
01490     style->paragLayout().style = style;
01491     style->format() = *(cursor.parag()->at(cursor.index())->format());
01492 }
01493 
01494 void KoTextView::addBookmarks(const QString &url)
01495 {
01496     QString filename = locateLocal( "data", QString::fromLatin1("konqueror/bookmarks.xml") );
01497     KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename,false );
01498     KBookmarkGroup group = bookManager->root();
01499     group.addBookmark( bookManager, url, KURL( url));
01500     bookManager->save();
01501     // delete bookManager;
01502 }
01503 
01504 void KoTextView::copyLink()
01505 {
01506     KoLinkVariable * var=linkVariable();
01507     if(var)
01508     {
01509         KURL::List lst;
01510         lst.append( var->url() );
01511         QApplication::clipboard()->setSelectionMode(true);
01512         QApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
01513         QApplication::clipboard()->setSelectionMode(false);
01514         QApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
01515     }
01516 }
01517 
01518 void KoTextView::removeLink()
01519 {
01520     KoLinkVariable * var=linkVariable();
01521     if(var)
01522     {
01523         KoTextCursor c1 = *m_cursor;
01524         KoTextCursor c2 = *m_cursor;
01525         c1.setIndex(var->index());
01526         c2.setIndex(var->index()+1);
01527         textDocument()->setSelectionStart( KoTextDocument::Temp, &c1 );
01528         textDocument()->setSelectionEnd( KoTextDocument::Temp, &c2 );
01529         KCommand *cmd=textObject()->replaceSelectionCommand( &c1, var->value(),
01530                                         i18n("Remove Link"), KoTextDocument::Temp );
01531         if ( cmd )
01532             textObject()->emitNewCommand( cmd );
01533     }
01534 }
01535 
01536 void KoTextView::setBackSpeller( KoBgSpellCheck* backSpeller )
01537 {
01538     d->m_backSpeller = backSpeller;
01539 }
01540 
01541 #include "KoTextView.moc"
01542 class KoBgSpellCheck;
KDE Home | KDE Accessibility Home | Description of Access Keys