lib Library API Documentation

kotextview.cc

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