F:/KPlato/koffice/libs/kotext/KoTextView.cpp

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

Généré le Wed Nov 22 23:41:10 2006 pour KPlato par  doxygen 1.5.1-p1