You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1550 lines
53 KiB
1550 lines
53 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2001-2006 David Faure <faure@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "KoTextView.h"
|
|
#include "KoTextParag.h"
|
|
#include "KoParagCounter.h"
|
|
#include "KoTextObject.h"
|
|
#include "KoTextViewIface.h"
|
|
#include "KoStyleCollection.h"
|
|
#include "KoBgSpellCheck.h"
|
|
#include "KoVariable.h"
|
|
|
|
#include <klocale.h>
|
|
#include <kstandarddirs.h>
|
|
#include <tdestdaccel.h>
|
|
#include <kdebug.h>
|
|
#include <kinstance.h>
|
|
#include <kdatatool.h>
|
|
#include <krun.h>
|
|
#include <kmessagebox.h>
|
|
#include <kcommand.h>
|
|
#include <kbookmarkmanager.h>
|
|
#include <kbookmark.h>
|
|
#include <kurldrag.h>
|
|
|
|
#include <tqapplication.h>
|
|
#include <tqtimer.h>
|
|
#include <tqclipboard.h>
|
|
|
|
class KoTextView::KoTextViewPrivate
|
|
{
|
|
public:
|
|
KoTextViewPrivate()
|
|
{
|
|
m_currentUnicodeNumber = 0;
|
|
m_backSpeller = 0;
|
|
}
|
|
|
|
void appendDigit( int digit ) { m_currentUnicodeNumber = 10 * m_currentUnicodeNumber + digit; }
|
|
int currentUnicodeNumber() const { return m_currentUnicodeNumber; }
|
|
void clearCurrentUnicodeNumber() { m_currentUnicodeNumber = 0; }
|
|
|
|
KoBgSpellCheck* m_backSpeller;
|
|
|
|
private:
|
|
int m_currentUnicodeNumber; // For the alt+123 feature
|
|
};
|
|
|
|
KoTextView::KoTextView( KoTextObject *textobj )
|
|
{
|
|
d = new KoTextViewPrivate;
|
|
m_bReadWrite = true;
|
|
m_textobj = textobj;
|
|
dcop=0;
|
|
connect( m_textobj, TQT_SIGNAL( hideCursor() ), this, TQT_SLOT( hideCursor() ) );
|
|
connect( m_textobj, TQT_SIGNAL( showCursor() ), this, TQT_SLOT( showCursor() ) );
|
|
connect( m_textobj, TQT_SIGNAL( setCursor( KoTextCursor * ) ), this, TQT_SLOT( setCursor( KoTextCursor * ) ) );
|
|
connect( m_textobj, TQT_SIGNAL( updateUI(bool, bool) ), this, TQT_SLOT( updateUI(bool, bool) ) );
|
|
connect( m_textobj, TQT_SIGNAL( showCurrentFormat() ), this, TQT_SLOT( showCurrentFormat() ) );
|
|
connect( m_textobj, TQT_SIGNAL( ensureCursorVisible() ), this, TQT_SLOT( ensureCursorVisible() ) );
|
|
|
|
m_cursor = new KoTextCursor( m_textobj->textDocument() );
|
|
|
|
m_cursorVisible = false;
|
|
|
|
showCursor();
|
|
blinkTimer = new TQTimer( this );
|
|
connect( blinkTimer, TQT_SIGNAL( timeout() ),
|
|
this, TQT_SLOT( blinkCursor() ) );
|
|
if ( TQApplication::cursorFlashTime() > 0 )
|
|
blinkTimer->start( TQApplication::cursorFlashTime() / 2 );
|
|
|
|
dragStartTimer = new TQTimer( this );
|
|
connect( dragStartTimer, TQT_SIGNAL( timeout() ),
|
|
this, TQT_SLOT( startDrag() ) );
|
|
|
|
m_textobj->formatMore( 2 );
|
|
|
|
blinkCursorVisible = FALSE;
|
|
inDoubleClick = FALSE;
|
|
mightStartDrag = FALSE;
|
|
possibleTripleClick = FALSE;
|
|
afterTripleClick = FALSE;
|
|
m_currentFormat = 0;
|
|
m_variablePosition =-1;
|
|
m_overwriteMode = false;
|
|
//updateUI( true, true );
|
|
}
|
|
|
|
KoTextView::~KoTextView()
|
|
{
|
|
delete m_cursor;
|
|
delete d;
|
|
delete dcop;
|
|
delete blinkTimer;
|
|
delete dragStartTimer;
|
|
}
|
|
|
|
KoTextViewIface* KoTextView::dcopObject()
|
|
{
|
|
if ( !dcop )
|
|
dcop = new KoTextViewIface( this );
|
|
|
|
return dcop;
|
|
}
|
|
|
|
void KoTextView::terminate(bool removeselection)
|
|
{
|
|
textObject()->clearUndoRedoInfo();
|
|
if ( removeselection && textDocument()->removeSelection( KoTextDocument::Standard ) )
|
|
textObject()->selectionChangedNotify();
|
|
hideCursor();
|
|
}
|
|
|
|
void KoTextView::deleteWordRight()
|
|
{
|
|
if ( textObject()->hasSelection() ) {
|
|
textObject()->removeSelectedText( m_cursor );
|
|
return;
|
|
}
|
|
textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
|
|
|
|
do {
|
|
m_cursor->gotoRight();
|
|
} while ( !m_cursor->atParagEnd()
|
|
&& !m_cursor->parag()->at( m_cursor->index() )->c.isSpace() );
|
|
textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
|
|
textObject()->removeSelectedText( m_cursor, KoTextDocument::Standard, i18n("Remove Word") );
|
|
}
|
|
|
|
void KoTextView::deleteWordLeft()
|
|
{
|
|
if ( textObject()->hasSelection() ) {
|
|
textObject()->removeSelectedText( m_cursor );
|
|
return;
|
|
}
|
|
textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
|
|
|
|
do {
|
|
m_cursor->gotoLeft();
|
|
} while ( !m_cursor->atParagStart()
|
|
&& !m_cursor->parag()->at( m_cursor->index()-1 )->c.isSpace() );
|
|
textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
|
|
textObject()->removeSelectedText( m_cursor, KoTextDocument::Standard, i18n("Remove Word") );
|
|
}
|
|
|
|
// Compare with TQTextEdit::keyPressEvent
|
|
void KoTextView::handleKeyPressEvent( TQKeyEvent * e, TQWidget *widget, const TQPoint &pos)
|
|
{
|
|
textObject()->typingStarted();
|
|
|
|
/* bool selChanged = FALSE;
|
|
for ( int i = 1; i < textDocument()->numSelections(); ++i )
|
|
selChanged = textDocument()->removeSelection( i ) || selChanged;
|
|
|
|
if ( selChanged ) {
|
|
// m_cursor->parag()->document()->nextDoubleBuffered = TRUE; ######## we need that only if we have nested items/documents
|
|
textFrameSet()->selectionChangedNotify();
|
|
}*/
|
|
|
|
bool clearUndoRedoInfo = TRUE;
|
|
if ( TDEShortcut( KKey( e ) ) == TDEStdAccel::deleteWordBack() )
|
|
{
|
|
if ( m_cursor->parag()->string()->isRightToLeft() )
|
|
deleteWordRight();
|
|
else
|
|
deleteWordLeft();
|
|
clearUndoRedoInfo = TRUE;
|
|
} else if ( TDEShortcut( KKey( e ) ) == TDEStdAccel::deleteWordForward() )
|
|
{
|
|
if ( m_cursor->parag()->string()->isRightToLeft() )
|
|
deleteWordLeft();
|
|
else
|
|
deleteWordRight();
|
|
clearUndoRedoInfo = TRUE;
|
|
}
|
|
else
|
|
switch ( e->key() ) {
|
|
case Key_Left:
|
|
case Key_Right: {
|
|
if (!doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
|
|
{
|
|
// a bit hacky, but can't change this without introducing new enum values for move and keeping the
|
|
// correct semantics and movement for BiDi and non BiDi text.
|
|
CursorAction a;
|
|
if ( m_cursor->parag()->string()->isRightToLeft() == (e->key() == Key_Right) )
|
|
a = e->state() & ControlButton ? MoveWordBackward : MoveBackward;
|
|
else
|
|
a = e->state() & ControlButton ? MoveWordForward : MoveForward;
|
|
moveCursor( a, e->state() & ShiftButton );
|
|
}
|
|
break;
|
|
}
|
|
case Key_Up:
|
|
moveCursor( e->state() & ControlButton ? MoveParagUp : MoveUp, e->state() & ShiftButton );
|
|
break;
|
|
case Key_Down:
|
|
moveCursor( e->state() & ControlButton ? MoveParagDown : MoveDown, e->state() & ShiftButton );
|
|
break;
|
|
case Key_Home:
|
|
moveCursor( e->state() & ControlButton ? MoveHome : MoveLineStart, e->state() & ShiftButton );
|
|
break;
|
|
case Key_End:
|
|
if (!doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
|
|
moveCursor( e->state() & ControlButton ? MoveEnd : MoveLineEnd, e->state() & ShiftButton );
|
|
break;
|
|
case Key_Prior:
|
|
moveCursor( e->state() & ControlButton ? MovePgUp : MoveViewportUp, e->state() & ShiftButton );
|
|
break;
|
|
case Key_Next:
|
|
moveCursor( e->state() & ControlButton ? MovePgDown : MoveViewportDown, e->state() & ShiftButton );
|
|
break;
|
|
case Key_Return: case Key_Enter:
|
|
|
|
if (!doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
|
|
if ( (e->state() & (ShiftButton|ControlButton)) == 0 )
|
|
{
|
|
if ( textObject()->hasSelection() )
|
|
textObject()->removeSelectedText( m_cursor );
|
|
clearUndoRedoInfo = FALSE;
|
|
textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionReturn );
|
|
Q_ASSERT( m_cursor->parag()->prev() );
|
|
if ( m_cursor->parag()->prev() )
|
|
doAutoFormat( m_cursor, m_cursor->parag()->prev(),
|
|
m_cursor->parag()->prev()->length() - 1, '\n' );
|
|
}
|
|
clearUndoRedoInfo = true;
|
|
break;
|
|
case Key_Delete:
|
|
if ( textObject()->hasSelection() ) {
|
|
textObject()->removeSelectedText( m_cursor );
|
|
break;
|
|
}
|
|
|
|
textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionDelete );
|
|
|
|
clearUndoRedoInfo = FALSE;
|
|
break;
|
|
case Key_Backtab:
|
|
if (e->state() & ShiftButton && m_cursor->parag() && m_cursor->atParagStart() && m_cursor->parag()->counter() && textDecreaseIndent())
|
|
break;
|
|
break;
|
|
case Key_Backspace:
|
|
if ( textObject()->hasSelection() ) {
|
|
textObject()->removeSelectedText( m_cursor );
|
|
break;
|
|
}
|
|
textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionBackspace );
|
|
|
|
clearUndoRedoInfo = FALSE;
|
|
break;
|
|
case Key_F16: // Copy key on Sun keyboards
|
|
emit copy();
|
|
break;
|
|
case Key_F18: // Paste key on Sun keyboards
|
|
emit paste();
|
|
break;
|
|
case Key_F20: // Cut key on Sun keyboards
|
|
emit cut();
|
|
break;
|
|
case Key_Direction_L: {
|
|
if ( m_cursor->parag() && m_cursor->parag()->direction() != TQChar::DirL )
|
|
{
|
|
KCommand* cmd = textObject()->setParagDirectionCommand( m_cursor, TQChar::DirL );
|
|
textObject()->emitNewCommand( cmd );
|
|
}
|
|
break;
|
|
}
|
|
case Key_Direction_R: {
|
|
if ( m_cursor->parag() && m_cursor->parag()->direction() != TQChar::DirR )
|
|
{
|
|
KCommand* cmd = textObject()->setParagDirectionCommand( m_cursor, TQChar::DirR );
|
|
textObject()->emitNewCommand( cmd );
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
//kdDebug(32500) << "KoTextView::keyPressEvent ascii=" << e->ascii() << " text=" << e->text()[0].unicode() << " state=" << e->state() << endl;
|
|
if (e->key() == TQt::Key_Tab)
|
|
{
|
|
if (doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
|
|
break;
|
|
if ( m_cursor->parag() && m_cursor->atParagStart() && m_cursor->parag()->counter() )
|
|
{
|
|
textIncreaseIndent();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( e->key() == TQt::Key_Space )
|
|
{
|
|
if (doToolTipCompletion(m_cursor, m_cursor->parag(), m_cursor->index() - 1, e->key()) )
|
|
break;
|
|
}
|
|
if ( e->text().length() &&
|
|
( !e->ascii() || e->ascii() >= 32 ) ||
|
|
( e->text() == "\t" && !( e->state() & ControlButton ) ) ) {
|
|
clearUndoRedoInfo = FALSE;
|
|
TQString text = e->text();
|
|
|
|
if ( d->m_backSpeller ) {
|
|
d->m_backSpeller->setIntraWordEditing( m_cursor->parag(), m_cursor->index() );
|
|
}
|
|
|
|
// Alt+123 feature
|
|
if ( ( e->state() & AltButton ) && text[0].isDigit() )
|
|
{
|
|
while ( text[0].isDigit() ) {
|
|
d->appendDigit( text[0].digitValue() );
|
|
text.remove( 0, 1 );
|
|
}
|
|
}
|
|
if ( !text.isEmpty() )
|
|
{
|
|
// Bidi support: need to reverse mirrored chars (e.g. parenthesis)
|
|
KoTextParag *p = m_cursor->parag();
|
|
if ( p && p->string() && p->string()->isRightToLeft() ) {
|
|
TQChar *c = (TQChar *)text.unicode();
|
|
int l = text.length();
|
|
while( l-- ) {
|
|
if ( c->mirrored() )
|
|
*c = c->mirroredChar();
|
|
c++;
|
|
}
|
|
}
|
|
|
|
if( !doIgnoreDoubleSpace( p, m_cursor->index()-1, text[ text.length() - 1 ] ) )
|
|
{
|
|
// ###### BUG: with the event compression, typing "kde" and then " k", might not apply
|
|
// autocorrection like it does for "kde" followed by " " followed by "k". We need to insert
|
|
// one character at a time, or better, to tell doAutoFormat how many chars to consider...
|
|
insertText( text );
|
|
// Don't use 'p' past this point. If we replaced a selection, p could have been deleted (#48999)
|
|
doAutoFormat( m_cursor, m_cursor->parag(), m_cursor->index() - 1, text[ text.length() - 1 ] );
|
|
}
|
|
showToolTipBox(m_cursor->parag(), m_cursor->index()-1, widget,pos);
|
|
}
|
|
else
|
|
removeToolTipCompletion();
|
|
|
|
}
|
|
// We should use TDEAccel instead, to make this configurable !
|
|
// Well, those are all alternate keys, for keys already configurable (KDE-wide)
|
|
// and a tdeaccel makes it hard to
|
|
else
|
|
{
|
|
if ( e->state() & ControlButton ) {
|
|
switch ( e->key() )
|
|
{
|
|
case Key_F16: // Copy key on Sun keyboards
|
|
copy();
|
|
break;
|
|
case Key_A:
|
|
moveCursor( MoveLineStart, e->state() & ShiftButton );
|
|
break;
|
|
case Key_E:
|
|
moveCursor( MoveLineEnd, e->state() & ShiftButton );
|
|
break;
|
|
case Key_K:
|
|
textObject()->doKeyboardAction( m_cursor, m_currentFormat, KoTextObject::ActionKill );
|
|
break;
|
|
case Key_Insert:
|
|
copy();
|
|
break;
|
|
case Key_Space:
|
|
insertNonbreakingSpace();
|
|
break;
|
|
default:
|
|
clearUndoRedoInfo = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else // e.g. just Key_Shift -> don't do anything (#129481)
|
|
{
|
|
clearUndoRedoInfo = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( clearUndoRedoInfo ) {
|
|
textObject()->clearUndoRedoInfo();
|
|
if ( d->m_backSpeller )
|
|
d->m_backSpeller->setIntraWordEditing( 0, 0 );
|
|
}
|
|
|
|
textObject()->typingDone();
|
|
}
|
|
|
|
void KoTextView::setOverwriteMode( bool overwriteMode )
|
|
{
|
|
m_overwriteMode = overwriteMode;
|
|
}
|
|
|
|
void KoTextView::insertText( const TQString &text )
|
|
{
|
|
int insertFlags = KoTextObject::DefaultInsertFlags;
|
|
if ( m_overwriteMode )
|
|
insertFlags |= KoTextObject::OverwriteMode;
|
|
textObject()->insert( m_cursor, m_currentFormat, text, i18n("Insert Text"), KoTextDocument::Standard, insertFlags );
|
|
}
|
|
|
|
void KoTextView::newParagraph()
|
|
{
|
|
textObject()->insert( m_cursor, m_currentFormat, "\n", i18n("Insert Text"), KoTextDocument::Standard, KoTextObject::CheckNewLine );
|
|
}
|
|
|
|
void KoTextView::handleKeyReleaseEvent( TQKeyEvent * e )
|
|
{
|
|
if ( e->key() == Key_Alt && d->currentUnicodeNumber() >= 32 )
|
|
{
|
|
TQString text = TQChar( d->currentUnicodeNumber() );
|
|
d->clearCurrentUnicodeNumber();
|
|
insertText( text );
|
|
doAutoFormat( m_cursor, m_cursor->parag(),
|
|
m_cursor->index() - 1, text[ text.length() - 1 ] );
|
|
}
|
|
}
|
|
|
|
void KoTextView::handleImStartEvent( TQIMEvent * )
|
|
{
|
|
// nothing to do
|
|
}
|
|
|
|
void KoTextView::handleImComposeEvent( TQIMEvent * e )
|
|
{
|
|
// remove old preedit
|
|
if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
|
|
textDocument()->removeSelection( KoTextDocument::Standard );
|
|
if ( textDocument()->hasSelection( KoTextDocument::InputMethodPreedit ) )
|
|
textDocument()->removeSelectedText( KoTextDocument::InputMethodPreedit, m_cursor );
|
|
|
|
// insert preedit
|
|
int preeditStartIdx = m_cursor->index();
|
|
textDocument()->setSelectionStart( KoTextDocument::InputMethodPreedit, m_cursor );
|
|
textObject()->insert( m_cursor, m_currentFormat, e->text(), i18n("Insert Text"),
|
|
KoTextDocument::Standard,
|
|
KoTextObject::DoNotRepaint/* DO NOT REPAINT CURSOR! */ );
|
|
textDocument()->setSelectionEnd( KoTextDocument::InputMethodPreedit, m_cursor );
|
|
|
|
// selection
|
|
int preeditSelStart = preeditStartIdx + e->cursorPos();
|
|
int preeditSelEnd = preeditSelStart + e->selectionLength();
|
|
m_cursor->setIndex( preeditSelStart );
|
|
textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
|
|
m_cursor->setIndex( preeditSelEnd );
|
|
textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
|
|
|
|
// set cursor pos
|
|
m_cursor->setIndex( preeditSelStart );
|
|
|
|
textObject()->emitUpdateUI( true );
|
|
textObject()->emitShowCursor();
|
|
textObject()->selectionChangedNotify();
|
|
}
|
|
|
|
void KoTextView::handleImEndEvent( TQIMEvent * e )
|
|
{
|
|
// remove old preedit
|
|
if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
|
|
textDocument()->removeSelection( KoTextDocument::Standard );
|
|
if ( textDocument()->hasSelection( KoTextDocument::InputMethodPreedit ) )
|
|
textDocument()->removeSelectedText( KoTextDocument::InputMethodPreedit, m_cursor );
|
|
|
|
insertText( e->text() );
|
|
|
|
textObject()->emitUpdateUI( true );
|
|
textObject()->emitShowCursor();
|
|
textObject()->selectionChangedNotify();
|
|
}
|
|
|
|
void KoTextView::completion()
|
|
{
|
|
(void) doCompletion(m_cursor, m_cursor->parag(),
|
|
m_cursor->index() - 1);
|
|
}
|
|
|
|
void KoTextView::moveCursor( CursorAction action, bool select )
|
|
{
|
|
hideCursor();
|
|
bool cursorMoved = false;
|
|
if ( select ) {
|
|
if ( !textDocument()->hasSelection( KoTextDocument::Standard ) )
|
|
textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
|
|
cursorMoved = moveCursor( action );
|
|
if ( textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor ) ) {
|
|
textObject()->selectionChangedNotify();
|
|
}
|
|
} else {
|
|
bool redraw = textDocument()->removeSelection( KoTextDocument::Standard );
|
|
cursorMoved = moveCursor( action );
|
|
if ( redraw ) {
|
|
textObject()->selectionChangedNotify();
|
|
}
|
|
}
|
|
|
|
if ( cursorMoved ) // e.g. not when pressing Ctrl/PgDown after the last parag
|
|
{
|
|
ensureCursorVisible();
|
|
// updateUI( true ); // done by moveCursor
|
|
}
|
|
showCursor();
|
|
}
|
|
|
|
bool KoTextView::moveCursor( CursorAction action )
|
|
{
|
|
bool cursorMoved = true;
|
|
switch ( action ) {
|
|
case MoveBackward:
|
|
m_cursor->gotoPreviousLetter();
|
|
break;
|
|
case MoveWordBackward:
|
|
m_cursor->gotoPreviousWord();
|
|
break;
|
|
case MoveForward:
|
|
m_cursor->gotoNextLetter();
|
|
break;
|
|
case MoveWordForward:
|
|
m_cursor->gotoNextWord();
|
|
break;
|
|
case MoveUp:
|
|
m_cursor->gotoUp();
|
|
break;
|
|
case MoveDown:
|
|
m_cursor->gotoDown();
|
|
break;
|
|
case MoveViewportUp:
|
|
cursorMoved = pgUpKeyPressed();
|
|
break;
|
|
case MoveViewportDown:
|
|
cursorMoved = pgDownKeyPressed();
|
|
break;
|
|
case MovePgUp:
|
|
ctrlPgUpKeyPressed();
|
|
break;
|
|
case MovePgDown:
|
|
ctrlPgDownKeyPressed();
|
|
break;
|
|
case MoveLineStart:
|
|
m_cursor->gotoLineStart();
|
|
break;
|
|
case MoveHome:
|
|
m_cursor->gotoHome();
|
|
break;
|
|
case MoveLineEnd:
|
|
m_cursor->gotoLineEnd();
|
|
break;
|
|
case MoveEnd:
|
|
textObject()->ensureFormatted( textDocument()->lastParag() );
|
|
m_cursor->gotoEnd();
|
|
break;
|
|
case MoveParagUp: {
|
|
KoTextParag * parag = m_cursor->parag()->prev();
|
|
if ( m_cursor->index()==0 && parag )
|
|
{
|
|
m_cursor->setParag( parag );
|
|
m_cursor->setIndex( 0 );
|
|
}
|
|
else m_cursor->setIndex( 0 );
|
|
} break;
|
|
case MoveParagDown: {
|
|
KoTextParag * parag = m_cursor->parag()->next();
|
|
if ( parag )
|
|
{
|
|
m_cursor->setParag( parag );
|
|
m_cursor->setIndex( 0 );
|
|
}
|
|
} break;
|
|
}
|
|
|
|
updateUI( true );
|
|
return cursorMoved;
|
|
}
|
|
|
|
KoTextCursor KoTextView::selectWordUnderCursor( const KoTextCursor& cursor, int selectionId )
|
|
{
|
|
KoTextCursor c1 = cursor;
|
|
KoTextCursor c2 = cursor;
|
|
if ( cursor.index() > 0 && !cursor.parag()->at( cursor.index()-1 )->c.isSpace() )
|
|
c1.gotoWordLeft();
|
|
if ( !cursor.parag()->at( cursor.index() )->c.isSpace() && !cursor.atParagEnd() )
|
|
c2.gotoWordRight();
|
|
|
|
// The above is almost correct, but gotoWordRight also skips the spaces/punctuations
|
|
// until the next word. So the 'word under cursor' contained e.g. that trailing space.
|
|
// To be on the safe side, we skip spaces/punctuations on both sides:
|
|
KoTextString *s = cursor.parag()->string();
|
|
bool beginFound = false;
|
|
for ( int i = c1.index(); i< c2.index(); i++)
|
|
{
|
|
const TQChar ch = s->at(i).c;
|
|
// This list comes from KoTextCursor::gotoPreviousWord.
|
|
// Can't use TQChar::isPunct since "'" and "-" are not word separators
|
|
const bool isWordDelimiter = ch.isSpace()
|
|
|| ch.category() == TQChar::Punctuation_Open // e.g. '('
|
|
|| ch.category() == TQChar::Punctuation_Close // e.g. ')'
|
|
|| ch.category() == TQChar::Punctuation_Other // see http://www.fileformat.info/info/unicode/category/Po/list.htm
|
|
;
|
|
|
|
if( !beginFound && !isWordDelimiter )
|
|
{
|
|
c1.setIndex(i);
|
|
beginFound = true;
|
|
}
|
|
else if ( beginFound && isWordDelimiter )
|
|
{
|
|
c2.setIndex(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
textDocument()->setSelectionStart( selectionId, &c1 );
|
|
textDocument()->setSelectionEnd( selectionId, &c2 );
|
|
return c2;
|
|
}
|
|
|
|
KoTextCursor KoTextView::selectParagUnderCursor( const KoTextCursor& cursor, int selectionId, bool copyAndNotify )
|
|
{
|
|
KoTextCursor c1 = cursor;
|
|
KoTextCursor c2 = cursor;
|
|
c1.setIndex(0);
|
|
c2.setIndex(c1.parag()->string()->length() - 1);
|
|
textDocument()->setSelectionStart( selectionId, &c1 );
|
|
textDocument()->setSelectionEnd( selectionId, &c2 );
|
|
if ( copyAndNotify )
|
|
{
|
|
textObject()->selectionChangedNotify();
|
|
// Copy the selection.
|
|
TQApplication::clipboard()->setSelectionMode( true );
|
|
emit copy();
|
|
TQApplication::clipboard()->setSelectionMode( false );
|
|
}
|
|
return c2;
|
|
}
|
|
|
|
void KoTextView::extendParagraphSelection( const TQPoint& iPoint )
|
|
{
|
|
hideCursor();
|
|
KoTextCursor oldCursor = *m_cursor;
|
|
placeCursor( iPoint );
|
|
|
|
bool redraw = FALSE;
|
|
if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
|
|
{
|
|
redraw = textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
|
|
if ( textDocument()->isSelectionSwapped( KoTextDocument::Standard ) )
|
|
m_cursor->setIndex( 0 );
|
|
else
|
|
m_cursor->setIndex( m_cursor->parag()->string()->length() - 1 );
|
|
textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor );
|
|
}
|
|
//else // it may be that the initial click was out of the frame
|
|
// textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
|
|
|
|
if ( redraw )
|
|
textObject()->selectionChangedNotify( false );
|
|
|
|
showCursor();
|
|
}
|
|
|
|
TQString KoTextView::wordUnderCursor( const KoTextCursor& cursor )
|
|
{
|
|
selectWordUnderCursor( cursor, KoTextDocument::Temp );
|
|
TQString text = textObject()->selectedText( KoTextDocument::Temp );
|
|
bool hasCustomItems = textObject()->selectionHasCustomItems( KoTextDocument::Temp );
|
|
textDocument()->removeSelection( KoTextDocument::Temp );
|
|
if( !hasCustomItems )
|
|
return text;
|
|
return TQString();
|
|
}
|
|
|
|
bool KoTextView::handleMousePressEvent( TQMouseEvent *e, const TQPoint &iPoint, bool canStartDrag, bool insertDirectCursor )
|
|
{
|
|
bool addParag = false;
|
|
mightStartDrag = FALSE;
|
|
hideCursor();
|
|
|
|
if (possibleTripleClick)
|
|
{
|
|
handleMouseTripleClickEvent( e, iPoint );
|
|
return addParag;
|
|
}
|
|
|
|
KoTextCursor oldCursor = *m_cursor;
|
|
addParag = placeCursor( iPoint, insertDirectCursor&& isReadWrite() );
|
|
ensureCursorVisible();
|
|
|
|
if ( e->button() != Qt::LeftButton )
|
|
{
|
|
showCursor();
|
|
return addParag;
|
|
}
|
|
|
|
KoLinkVariable* lv = linkVariable();
|
|
if ( lv && openLink( lv ) )
|
|
{
|
|
return addParag;
|
|
}
|
|
|
|
KoTextDocument * textdoc = textDocument();
|
|
if ( canStartDrag && textdoc->inSelection( KoTextDocument::Standard, iPoint ) ) {
|
|
mightStartDrag = TRUE;
|
|
m_textobj->emitShowCursor();
|
|
dragStartTimer->start( TQApplication::startDragTime(), TRUE );
|
|
dragStartPos = e->pos();
|
|
return addParag;
|
|
}
|
|
|
|
bool redraw = FALSE;
|
|
if ( textdoc->hasSelection( KoTextDocument::Standard ) ) {
|
|
if ( !( e->state() & ShiftButton ) ) {
|
|
redraw = textdoc->removeSelection( KoTextDocument::Standard );
|
|
textdoc->setSelectionStart( KoTextDocument::Standard, m_cursor );
|
|
} else {
|
|
redraw = textdoc->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
|
|
}
|
|
} else {
|
|
if ( !( e->state() & ShiftButton ) ) {
|
|
textdoc->setSelectionStart( KoTextDocument::Standard, m_cursor );
|
|
} else {
|
|
textdoc->setSelectionStart( KoTextDocument::Standard, &oldCursor );
|
|
redraw = textdoc->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
|
|
}
|
|
}
|
|
|
|
//kdDebug(32500) << "KoTextView::mousePressEvent redraw=" << redraw << endl;
|
|
if ( !redraw ) {
|
|
showCursor();
|
|
} else {
|
|
textObject()->selectionChangedNotify();
|
|
}
|
|
return addParag;
|
|
}
|
|
|
|
void KoTextView::handleMouseMoveEvent( TQMouseEvent*, const TQPoint& iPoint )
|
|
{
|
|
hideCursor();
|
|
KoTextCursor oldCursor = *m_cursor;
|
|
placeCursor( iPoint );
|
|
|
|
// Double click + mouse still down + moving the mouse selects full words.
|
|
if ( inDoubleClick ) {
|
|
KoTextCursor cl = *m_cursor;
|
|
cl.gotoWordLeft();
|
|
KoTextCursor cr = *m_cursor;
|
|
cr.gotoWordRight();
|
|
|
|
int diff = TQABS( oldCursor.parag()->at( oldCursor.index() )->x - iPoint.x() );
|
|
int ldiff = TQABS( cl.parag()->at( cl.index() )->x - iPoint.x() );
|
|
int rdiff = TQABS( cr.parag()->at( cr.index() )->x - iPoint.x() );
|
|
|
|
if ( m_cursor->parag()->lineStartOfChar( m_cursor->index() ) !=
|
|
oldCursor.parag()->lineStartOfChar( oldCursor.index() ) )
|
|
diff = 0xFFFFFF;
|
|
|
|
if ( rdiff < diff && rdiff < ldiff )
|
|
*m_cursor = cr;
|
|
else if ( ldiff < diff && ldiff < rdiff )
|
|
*m_cursor = cl;
|
|
else
|
|
*m_cursor = oldCursor;
|
|
}
|
|
|
|
bool redraw = FALSE;
|
|
if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
|
|
redraw = textDocument()->setSelectionEnd( KoTextDocument::Standard, m_cursor ) || redraw;
|
|
else // it may be that the initial click was out of the frame
|
|
textDocument()->setSelectionStart( KoTextDocument::Standard, m_cursor );
|
|
|
|
if ( redraw )
|
|
textObject()->selectionChangedNotify( false );
|
|
|
|
showCursor();
|
|
}
|
|
|
|
void KoTextView::handleMouseReleaseEvent()
|
|
{
|
|
if ( dragStartTimer->isActive() )
|
|
dragStartTimer->stop();
|
|
if ( mightStartDrag ) {
|
|
textObject()->selectAll( FALSE );
|
|
mightStartDrag = false;
|
|
}
|
|
else
|
|
{
|
|
if ( textDocument()->selectionStartCursor( KoTextDocument::Standard ) == textDocument()->selectionEndCursor( KoTextDocument::Standard ) )
|
|
{
|
|
textDocument()->removeSelection( KoTextDocument::Standard );
|
|
}
|
|
|
|
textObject()->selectionChangedNotify();
|
|
|
|
// Copy the selection.
|
|
TQApplication::clipboard()->setSelectionMode( true );
|
|
emit copy();
|
|
TQApplication::clipboard()->setSelectionMode( false );
|
|
}
|
|
|
|
inDoubleClick = FALSE;
|
|
m_textobj->emitShowCursor();
|
|
}
|
|
|
|
void KoTextView::handleMouseDoubleClickEvent( TQMouseEvent*ev, const TQPoint& i )
|
|
{
|
|
//after a triple click it's not a double click but a simple click
|
|
//but as triple click didn't exist it's necessary to do it.
|
|
if(afterTripleClick)
|
|
{
|
|
handleMousePressEvent( ev, i );
|
|
return;
|
|
}
|
|
|
|
inDoubleClick = TRUE;
|
|
*m_cursor = selectWordUnderCursor( *m_cursor );
|
|
textObject()->selectionChangedNotify();
|
|
// Copy the selection.
|
|
TQApplication::clipboard()->setSelectionMode( true );
|
|
emit copy();
|
|
TQApplication::clipboard()->setSelectionMode( false );
|
|
|
|
possibleTripleClick=true;
|
|
|
|
TQTimer::singleShot(TQApplication::doubleClickInterval(),this,TQT_SLOT(tripleClickTimeout()));
|
|
}
|
|
|
|
void KoTextView::tripleClickTimeout()
|
|
{
|
|
possibleTripleClick=false;
|
|
}
|
|
|
|
void KoTextView::handleMouseTripleClickEvent( TQMouseEvent*ev, const TQPoint& /* Currently unused */ )
|
|
{
|
|
if ( ev->button() != Qt::LeftButton)
|
|
{
|
|
showCursor();
|
|
return;
|
|
}
|
|
afterTripleClick= true;
|
|
inDoubleClick = FALSE;
|
|
*m_cursor = selectParagUnderCursor( *m_cursor );
|
|
TQTimer::singleShot(TQApplication::doubleClickInterval(),this,TQT_SLOT(afterTripleClickTimeout()));
|
|
}
|
|
|
|
void KoTextView::afterTripleClickTimeout()
|
|
{
|
|
afterTripleClick=false;
|
|
}
|
|
|
|
bool KoTextView::maybeStartDrag( TQMouseEvent* e )
|
|
{
|
|
if ( mightStartDrag ) {
|
|
dragStartTimer->stop();
|
|
if ( ( e->pos() - dragStartPos ).manhattanLength() > TQApplication::startDragDistance() )
|
|
startDrag();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool KoTextView::insertParagraph(const TQPoint &pos)
|
|
{
|
|
KoTextParag *last = textDocument()->lastParag();
|
|
KoTextFormat *f = 0;
|
|
KoParagStyle *style = last->style();
|
|
KoParagCounter *counter = last->counter();
|
|
int diff = (pos.y()- textDocument()->height());
|
|
f = last->at( last->length()-1 )->format();
|
|
int height =f->height();
|
|
int nbParag = (diff / height);
|
|
TQFontMetrics fm = f->refFontMetrics();
|
|
for (int i = 0; i < nbParag ;i++)
|
|
{
|
|
KoTextParag *s=textDocument()->createParag( textDocument(), last );
|
|
s->setFormat( 0, 1, f, TRUE );
|
|
if ( style )
|
|
s->setStyle( style );
|
|
s->setCounter( counter );
|
|
last = s;
|
|
}
|
|
bool createParag = (nbParag > 0 );
|
|
if ( createParag )
|
|
{
|
|
if ( pos.x() + f->width(' ') >= textDocument()->width())
|
|
{
|
|
//FIXME me bidi.
|
|
//change parag alignment => right alignment
|
|
last->setAlignment( TQt::AlignRight );
|
|
}
|
|
else
|
|
{
|
|
int nbSpace = pos.x()/f->width(' ');
|
|
TQString tmp;
|
|
for (int i = 0; i< nbSpace; i++)
|
|
{
|
|
tmp+=' ';
|
|
}
|
|
last->insert( 0, tmp );
|
|
}
|
|
}
|
|
return createParag;
|
|
|
|
}
|
|
|
|
bool KoTextView::placeCursor( const TQPoint &pos, bool insertDirectCursor )
|
|
{
|
|
bool addParag = false;
|
|
if ( insertDirectCursor && (pos.y()>textDocument()->height()) )
|
|
addParag = insertParagraph(pos);
|
|
KoTextParag *s = 0L;
|
|
if ( addParag )
|
|
s = textDocument()->lastParag();
|
|
else
|
|
s = textDocument()->firstParag();
|
|
m_cursor->place( pos, s, false, &m_variablePosition );
|
|
if ( m_variablePosition != -1 )
|
|
kdDebug() << k_funcinfo << " m_variablePosition set to " << m_variablePosition << endl;
|
|
updateUI( true );
|
|
return addParag;
|
|
}
|
|
|
|
void KoTextView::blinkCursor()
|
|
{
|
|
//kdDebug(32500) << "KoTextView::blinkCursor m_cursorVisible=" << m_cursorVisible
|
|
// << " blinkCursorVisible=" << blinkCursorVisible << endl;
|
|
if ( !m_cursorVisible )
|
|
return;
|
|
bool cv = m_cursorVisible;
|
|
blinkCursorVisible = !blinkCursorVisible;
|
|
drawCursor( blinkCursorVisible );
|
|
m_cursorVisible = cv;
|
|
}
|
|
|
|
void KoTextView::drawCursor( bool visible )
|
|
{
|
|
m_cursorVisible = visible;
|
|
// The rest is up to the app ;)
|
|
}
|
|
|
|
void KoTextView::focusInEvent()
|
|
{
|
|
if ( TQApplication::cursorFlashTime() > 0 )
|
|
blinkTimer->start( TQApplication::cursorFlashTime() / 2 );
|
|
showCursor();
|
|
}
|
|
|
|
void KoTextView::focusOutEvent()
|
|
{
|
|
blinkTimer->stop();
|
|
hideCursor();
|
|
}
|
|
|
|
/*void KoTextView::setFormat( KoTextFormat * newFormat, int flags, bool zoomFont)
|
|
{
|
|
textObject()->setFormat( m_cursor, m_currentFormat, newFormat, flags, zoomFont );
|
|
}*/
|
|
|
|
KCommand* KoTextView::setFormatCommand( const KoTextFormat * newFormat, int flags, bool zoomFont)
|
|
{
|
|
return textObject()->setFormatCommand( m_cursor, &m_currentFormat, newFormat, flags, zoomFont );
|
|
}
|
|
|
|
void KoTextView::dragStarted()
|
|
{
|
|
mightStartDrag = FALSE;
|
|
inDoubleClick = FALSE;
|
|
}
|
|
|
|
void KoTextView::applyStyle( const KoParagStyle * style )
|
|
{
|
|
if ( style )
|
|
{
|
|
textObject()->applyStyle( m_cursor, style );
|
|
showCurrentFormat();
|
|
}
|
|
}
|
|
|
|
void KoTextView::updateUI( bool updateFormat, bool /*force*/ )
|
|
{
|
|
// Update UI - only for those items which have changed
|
|
|
|
if ( updateFormat )
|
|
{
|
|
int i = cursor()->index();
|
|
if ( i > 0 )
|
|
--i;
|
|
#ifdef DEBUG_FORMATS
|
|
if ( currentFormat() )
|
|
kdDebug(32500) << "KoTextView::updateUI old currentFormat=" << currentFormat()
|
|
<< " " << currentFormat()->key()
|
|
<< " parag format=" << cursor()->parag()->at( i )->format()->key() << endl;
|
|
else
|
|
kdDebug(32500) << "KoTextView::updateUI old currentFormat=0" << endl;
|
|
#endif
|
|
if ( !currentFormat() || currentFormat()->key() != cursor()->parag()->at( i )->format()->key() )
|
|
{
|
|
if ( currentFormat() )
|
|
currentFormat()->removeRef();
|
|
#ifdef DEBUG_FORMATS
|
|
kdDebug(32500) << "Setting currentFormat from format " << cursor()->parag()->at( i )->format()
|
|
<< " ( character " << i << " in paragraph " << cursor()->parag()->paragId() << " )" << endl;
|
|
#endif
|
|
setCurrentFormat( textDocument()->formatCollection()->format( cursor()->parag()->at( i )->format() ) );
|
|
if ( currentFormat()->isMisspelled() ) {
|
|
KoTextFormat fNoMisspelled( *currentFormat() );
|
|
fNoMisspelled.setMisspelled( false );
|
|
currentFormat()->removeRef();
|
|
setCurrentFormat( textDocument()->formatCollection()->format( &fNoMisspelled ) );
|
|
}
|
|
showCurrentFormat();
|
|
}
|
|
}
|
|
}
|
|
|
|
void KoTextView::showCurrentFormat()
|
|
{
|
|
//kdDebug(32500) << "KoTextView::showCurrentFormat currentFormat=" << currentFormat() << " " << currentFormat()->key() << endl;
|
|
KoTextFormat format = *currentFormat();
|
|
//format.setPointSize( textObject()->docFontSize( currentFormat() ) ); // "unzoom" the font size
|
|
showFormat( &format );
|
|
}
|
|
|
|
KCommand * KoTextView::setCounterCommand( const KoParagCounter & counter )
|
|
{
|
|
return textObject()->setCounterCommand( m_cursor, counter );
|
|
}
|
|
KCommand * KoTextView::setAlignCommand( int align )
|
|
{
|
|
return textObject()->setAlignCommand( m_cursor, align );
|
|
}
|
|
KCommand * KoTextView::setLineSpacingCommand( double spacing, KoParagLayout::SpacingType _type)
|
|
{
|
|
return textObject()->setLineSpacingCommand( m_cursor, spacing, _type);
|
|
}
|
|
KCommand * KoTextView::setBordersCommand( const KoBorder& leftBorder, const KoBorder& rightBorder, const KoBorder& bottomBorder, const KoBorder& topBorder )
|
|
{
|
|
return textObject()->setBordersCommand( m_cursor, leftBorder, rightBorder, bottomBorder, topBorder );
|
|
}
|
|
KCommand * KoTextView::setJoinBordersCommand( bool join )
|
|
{
|
|
return textObject()->setJoinBordersCommand( m_cursor, join );
|
|
}
|
|
KCommand * KoTextView::setMarginCommand( TQStyleSheetItem::Margin m, double margin )
|
|
{
|
|
return textObject()->setMarginCommand( m_cursor, m, margin );
|
|
}
|
|
KCommand * KoTextView::setTabListCommand( const KoTabulatorList & tabList )
|
|
{
|
|
return textObject()->setTabListCommand( m_cursor, tabList );
|
|
}
|
|
KCommand * KoTextView::setBackgroundColorCommand( const TQColor & color )
|
|
{
|
|
return textObject()->setBackgroundColorCommand( m_cursor, color );
|
|
}
|
|
|
|
KoTextDocument * KoTextView::textDocument() const
|
|
{
|
|
return textObject()->textDocument();
|
|
}
|
|
|
|
KoVariable *KoTextView::variable()
|
|
{
|
|
if ( m_variablePosition < 0 )
|
|
return 0;
|
|
// Can't use m_cursor here, it could be before or after the variable, depending on which half of it was clicked
|
|
return textObject()->variableAtPosition( m_cursor->parag(), m_variablePosition );
|
|
}
|
|
|
|
KoLinkVariable * KoTextView::linkVariable()
|
|
{
|
|
return dynamic_cast<KoLinkVariable *>(variable());
|
|
}
|
|
|
|
TQPtrList<TDEAction> KoTextView::dataToolActionList(TDEInstance * instance, const TQString& word, bool & _singleWord )
|
|
{
|
|
m_singleWord = false;
|
|
m_wordUnderCursor = TQString();
|
|
TQString text;
|
|
if ( textObject()->hasSelection() )
|
|
{
|
|
text = textObject()->selectedText();
|
|
if ( text.find(' ') == -1 && text.find('\t') == -1 && text.find(KoTextObject::customItemChar()) == -1 )
|
|
{
|
|
m_singleWord = true;
|
|
}
|
|
else
|
|
{
|
|
m_singleWord = false;
|
|
//laurent : don't try to search thesaurus when we have a customItemChar.
|
|
if( text.find(KoTextObject::customItemChar())!=-1)
|
|
text = TQString();
|
|
}
|
|
}
|
|
else // No selection -> use word under cursor
|
|
{
|
|
if ( !word.isEmpty() )
|
|
{
|
|
m_singleWord = true;
|
|
m_wordUnderCursor = word;
|
|
text = word;
|
|
}
|
|
}
|
|
|
|
if ( text.isEmpty() || textObject()->protectContent()) // Nothing to apply a tool to
|
|
return TQPtrList<TDEAction>();
|
|
|
|
// Any tool that works on plain text is relevant
|
|
TQValueList<KDataToolInfo> tools;
|
|
tools +=KDataToolInfo::query( TQSTRING_OBJECT_NAME_STRING, "text/plain", instance );
|
|
|
|
// Add tools that work on a single word if that is the case
|
|
if ( m_singleWord )
|
|
{
|
|
_singleWord = true;
|
|
tools += KDataToolInfo::query( TQSTRING_OBJECT_NAME_STRING, "application/x-singleword", instance );
|
|
}
|
|
// Maybe one day we'll have tools that use libkotext (or qt3's qrt), to act on formatted text
|
|
tools += KDataToolInfo::query( "KoTextString", "application/x-qrichtext", instance );
|
|
|
|
return KDataToolAction::dataToolActionList( tools, this, TQT_SLOT( slotToolActivated( const KDataToolInfo &, const TQString & ) ) );
|
|
}
|
|
|
|
TQString KoTextView::currentWordOrSelection() const
|
|
{
|
|
if ( textObject()->hasSelection() )
|
|
return textObject()->selectedText();
|
|
else
|
|
return m_wordUnderCursor;
|
|
}
|
|
|
|
void KoTextView::slotToolActivated( const KDataToolInfo & info, const TQString & command )
|
|
{
|
|
KDataTool* tool = info.createTool( );
|
|
if ( !tool )
|
|
{
|
|
kdWarning() << "Could not create Tool !" << endl;
|
|
return;
|
|
}
|
|
|
|
kdDebug(32500) << "KWTextFrameSetEdit::slotToolActivated command=" << command
|
|
<< " dataType=" << info.dataType() << endl;
|
|
|
|
TQString text;
|
|
if ( textObject()->hasSelection() )
|
|
text = textObject()->selectedText();
|
|
else
|
|
text = m_wordUnderCursor;
|
|
|
|
// Preferred type is richtext
|
|
TQString mimetype = "application/x-qrichtext";
|
|
TQString datatype = "KoTextString";
|
|
// If unsupported, try text/plain
|
|
if ( !info.mimeTypes().contains( mimetype ) )
|
|
{
|
|
mimetype = "text/plain";
|
|
datatype = TQSTRING_OBJECT_NAME_STRING;
|
|
}
|
|
// If unsupported (and if we have a single word indeed), try application/x-singleword
|
|
if ( !info.mimeTypes().contains( mimetype ) && m_singleWord )
|
|
mimetype = "application/x-singleword";
|
|
|
|
kdDebug(32500) << "Running tool with datatype=" << datatype << " mimetype=" << mimetype << endl;
|
|
|
|
TQString origText = text;
|
|
if ( tool->run( command, &text, datatype, mimetype) )
|
|
{
|
|
kdDebug(32500) << "Tool ran. Text is now " << text << endl;
|
|
if ( origText != text )
|
|
{
|
|
if ( !textObject()->hasSelection() )
|
|
{
|
|
// Warning: ok for now, but wrong cursor if RMB doesn't place cursor anymore
|
|
selectWordUnderCursor( *m_cursor );
|
|
}
|
|
// replace selection with 'text'
|
|
textObject()->emitNewCommand( textObject()->replaceSelectionCommand(
|
|
cursor(), text, i18n("Replace Word") ));
|
|
}
|
|
}
|
|
delete tool;
|
|
}
|
|
|
|
bool KoTextView::openLink( KoLinkVariable* variable )
|
|
{
|
|
kdDebug() << k_funcinfo << variable->url() << endl;
|
|
KURL url( variable->url() );
|
|
if( url.isValid() )
|
|
{
|
|
(void) new KRun( url );
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
KMessageBox::sorry( 0, i18n("%1 is not a valid link.").arg( variable->url() ) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
void KoTextView::insertSoftHyphen()
|
|
{
|
|
textObject()->insert( cursor(), currentFormat(), TQChar(0xad) /* see TQRichText */,
|
|
i18n("Insert Soft Hyphen") );
|
|
}
|
|
|
|
void KoTextView::insertLineBreak()
|
|
{
|
|
textObject()->insert( cursor(), currentFormat(), TQChar('\n'),
|
|
i18n("Insert Line Break") );
|
|
}
|
|
|
|
void KoTextView::insertNonbreakingSpace()
|
|
{
|
|
textObject()->insert( cursor(), currentFormat(), TQChar(0xa0) /* see TQRichText */,
|
|
i18n("Insert Non-Breaking Space") );
|
|
}
|
|
|
|
void KoTextView::insertNonbreakingHyphen()
|
|
{
|
|
textObject()->insert( cursor(), currentFormat(), TQChar(0x2013),
|
|
i18n("Insert Non-Breaking Hyphen") );
|
|
}
|
|
|
|
void KoTextView::insertSpecialChar(TQChar _c, const TQString& font)
|
|
{
|
|
KoTextFormat * newFormat = new KoTextFormat(*currentFormat());
|
|
newFormat->setFamily( font );
|
|
if ( textObject()->hasSelection() )
|
|
{
|
|
KoTextFormat * lastFormat = currentFormat();
|
|
|
|
KCommand *cmd = textObject()->setFormatCommand( cursor(), &lastFormat, newFormat, KoTextFormat::Family );
|
|
|
|
KMacroCommand* macroCmd = new KMacroCommand( i18n("Insert Special Char") );
|
|
macroCmd->addCommand( cmd );
|
|
macroCmd->addCommand( textObject()->replaceSelectionCommand(
|
|
cursor(), _c, TQString()) );
|
|
textObject()->emitNewCommand( macroCmd );
|
|
}
|
|
else
|
|
{
|
|
textObject()->insert( cursor(), newFormat, _c, i18n("Insert Special Char"));
|
|
delete newFormat;
|
|
}
|
|
}
|
|
|
|
const KoParagLayout * KoTextView::currentParagLayoutFormat() const
|
|
{
|
|
KoTextParag * parag = m_cursor->parag();
|
|
return &(parag->paragLayout());
|
|
}
|
|
|
|
bool KoTextView::rtl() const
|
|
{
|
|
return m_cursor->parag()->string()->isRightToLeft();
|
|
}
|
|
|
|
KCommand* KoTextView::setParagLayoutFormatCommand( KoParagLayout *newLayout, int flags, int marginIndex )
|
|
{
|
|
return textObject()->setParagLayoutCommand( m_cursor, *newLayout, KoTextDocument::Standard,
|
|
flags, marginIndex, true /*createUndoRedo*/ );
|
|
}
|
|
|
|
// Heading1 -> Heading2 -> Heading3 -> normal -> 1 -> 1.1 -> 1.1.1
|
|
void KoTextView::increaseNumberingLevel( const KoStyleCollection* styleCollection )
|
|
{
|
|
// TODO: do this for each paragraph in the selection
|
|
KoParagStyle* style = 0;
|
|
int level = 0;
|
|
KoParagCounter* counter = m_cursor->parag()->counter();
|
|
if ( counter )
|
|
level = counter->depth() + 1;
|
|
if ( m_cursor->parag()->style()->isOutline() )
|
|
{
|
|
TQValueVector<KoParagStyle *> outlineStyles = styleCollection->outlineStyles();
|
|
while ( level < 10 && !style ) {
|
|
style = outlineStyles[ level ];
|
|
++level;
|
|
}
|
|
if ( !style ) // no lower-level heading exists, use standard style
|
|
style = styleCollection->defaultStyle();
|
|
}
|
|
else // non-outline, just a numbered list
|
|
{
|
|
// Try to find a style with this depth, to know if the user wants display-levels etc.
|
|
style = styleCollection->numberedStyleForLevel( level );
|
|
if ( !style ) { // not found. Make the change though.
|
|
KoParagCounter c;
|
|
if (counter) {
|
|
c = *counter;
|
|
c.setDepth( level );
|
|
c.setDisplayLevels( c.displayLevels() + 1 );
|
|
} else {
|
|
// Start a simple numbered list.
|
|
c.setNumbering(KoParagCounter::NUM_LIST);
|
|
c.setStyle(KoParagCounter::STYLE_NUM);
|
|
}
|
|
KCommand* command = textObject()->setCounterCommand( m_cursor, c );
|
|
textObject()->emitNewCommand( command );
|
|
}
|
|
}
|
|
if ( style ) // can't be 0
|
|
textObject()->applyStyle( m_cursor, style );
|
|
}
|
|
|
|
// 1.1.1 -> 1.1 -> 1 -> normal -> Heading3 -> Heading2 -> Heading1
|
|
void KoTextView::decreaseNumberingLevel( const KoStyleCollection* styleCollection )
|
|
{
|
|
// TODO: do this for each paragraph in the selection
|
|
KoParagCounter* counter = m_cursor->parag()->counter();
|
|
int level = 9;
|
|
if ( counter )
|
|
level = counter->depth() - 1;
|
|
KoParagStyle* style = 0;
|
|
if ( m_cursor->parag()->style()->isOutline() || !counter ) // heading or normal
|
|
{
|
|
if ( level == -1 ) // nothing higher than Heading1
|
|
return;
|
|
TQValueVector<KoParagStyle *> outlineStyles = styleCollection->outlineStyles();
|
|
while ( level >= 0 && !style ) {
|
|
style = outlineStyles[ level ];
|
|
--level;
|
|
}
|
|
}
|
|
else // non-outline, numbered list
|
|
{
|
|
if ( level == -1 )
|
|
style = styleCollection->defaultStyle();
|
|
else
|
|
{
|
|
style = styleCollection->numberedStyleForLevel( level );
|
|
if ( !style ) { // not found. Make the change though.
|
|
KoParagCounter c( *counter );
|
|
c.setDepth( level );
|
|
if ( c.displayLevels() > 1 ) {
|
|
c.setDisplayLevels( c.displayLevels() - 1 );
|
|
}
|
|
KCommand* command = textObject()->setCounterCommand( m_cursor, c );
|
|
textObject()->emitNewCommand( command );
|
|
}
|
|
}
|
|
}
|
|
if ( style )
|
|
textObject()->applyStyle( m_cursor, style );
|
|
}
|
|
|
|
KCommand *KoTextView::setChangeCaseOfTextCommand(KoChangeCaseDia::TypeOfCase _type)
|
|
{
|
|
TQString text;
|
|
if ( textObject()->hasSelection() )
|
|
text = textObject()->selectedText();
|
|
if(!text.isEmpty())
|
|
return textObject()->changeCaseOfText(cursor(), _type);
|
|
else
|
|
return 0L;
|
|
}
|
|
|
|
KCommand *KoTextView::prepareDropMove( KoTextCursor dropCursor )
|
|
{
|
|
Q_ASSERT( textDocument()->hasSelection( KoTextDocument::Standard ) );
|
|
// Dropping into the selection itself ?
|
|
KoTextCursor startSel = textDocument()->selectionStartCursor( KoTextDocument::Standard );
|
|
KoTextCursor endSel = textDocument()->selectionEndCursor( KoTextDocument::Standard );
|
|
bool inSelection = false;
|
|
if ( startSel.parag() == endSel.parag() )
|
|
inSelection = dropCursor.parag() == startSel.parag()
|
|
&& dropCursor.index() >= startSel.index()
|
|
&& dropCursor.index() <= endSel.index();
|
|
else
|
|
{
|
|
// Looking at first line first:
|
|
inSelection = dropCursor.parag() == startSel.parag() && dropCursor.index() >= startSel.index();
|
|
if ( !inSelection )
|
|
{
|
|
// Look at all other paragraphs except last one
|
|
KoTextParag *p = startSel.parag()->next();
|
|
while ( !inSelection && p && p != endSel.parag() )
|
|
{
|
|
inSelection = ( p == dropCursor.parag() );
|
|
p = p->next();
|
|
}
|
|
// Look at last paragraph
|
|
if ( !inSelection )
|
|
inSelection = dropCursor.parag() == endSel.parag() && dropCursor.index() <= endSel.index();
|
|
}
|
|
}
|
|
if ( inSelection || m_textobj->protectContent() )
|
|
{
|
|
textDocument()->removeSelection( KoTextDocument::Standard );
|
|
textObject()->selectionChangedNotify();
|
|
hideCursor();
|
|
*cursor() = dropCursor;
|
|
showCursor();
|
|
ensureCursorVisible();
|
|
return 0L;
|
|
}
|
|
if ( textObject()->protectContent() )
|
|
{
|
|
textDocument()->removeSelection( KoTextDocument::Standard );
|
|
textObject()->selectionChangedNotify();
|
|
}
|
|
// Tricky. We don't want to do the placeCursor after removing the selection
|
|
// (the user pointed at some text with the old selection in place).
|
|
// However, something got deleted in our parag, dropCursor's index needs adjustment.
|
|
if ( endSel.parag() == dropCursor.parag() )
|
|
{
|
|
// Does the selection starts before (other parag or same parag) ?
|
|
if ( startSel.parag() != dropCursor.parag() || startSel.index() < dropCursor.index() )
|
|
{
|
|
// If other -> endSel.parag() will get deleted. The final position is in startSel.parag(),
|
|
// where the selection started + how much after the end we are. Make a drawing :)
|
|
// If same -> simply move back by how many chars we've deleted. Funny thing is, it's the same formula.
|
|
int dropIndex = dropCursor.index();
|
|
dropCursor.setParag( startSel.parag() );
|
|
// If dropCursor - endSel < 0, selection ends after, we're dropping into selection (no-op)
|
|
dropCursor.setIndex( dropIndex - TQMIN( endSel.index(), dropIndex ) + startSel.index() );
|
|
}
|
|
kdDebug(32500) << "dropCursor: parag=" << dropCursor.parag()->paragId() << " index=" << dropCursor.index() << endl;
|
|
}
|
|
KCommand* cmd = textObject()->removeSelectedTextCommand( cursor(), KoTextDocument::Standard );
|
|
|
|
hideCursor();
|
|
*cursor() = dropCursor;
|
|
showCursor();
|
|
|
|
return cmd;
|
|
}
|
|
|
|
|
|
void KoTextView::copyTextOfComment()
|
|
{
|
|
KoNoteVariable *var = dynamic_cast<KoNoteVariable *>( variable() );
|
|
if( var )
|
|
{
|
|
KURL::List lst;
|
|
lst.append( var->note() );
|
|
TQApplication::clipboard()->setSelectionMode(true);
|
|
TQApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
|
|
TQApplication::clipboard()->setSelectionMode(false);
|
|
TQApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
|
|
}
|
|
}
|
|
|
|
void KoTextView::removeComment()
|
|
{
|
|
KoNoteVariable *var = dynamic_cast<KoNoteVariable *>( variable() );
|
|
if( var )
|
|
{
|
|
m_cursor->setIndex( m_variablePosition );
|
|
textDocument()->setSelectionStart( KoTextDocument::Temp, m_cursor );
|
|
m_cursor->setIndex( m_variablePosition + 1 );
|
|
textDocument()->setSelectionEnd( KoTextDocument::Temp, m_cursor );
|
|
textObject()->removeSelectedText( m_cursor, KoTextDocument::Temp, i18n("Remove Comment") );
|
|
}
|
|
}
|
|
|
|
KoParagStyle * KoTextView::createStyleFromSelection(const TQString & name)
|
|
{
|
|
KoTextCursor cursor = *m_cursor;
|
|
if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
|
|
cursor = textDocument()->selectionStartCursor( KoTextDocument::Standard );
|
|
KoParagStyle * style = new KoParagStyle (name);
|
|
KoParagLayout layout(cursor.parag()->paragLayout());
|
|
layout.style = style;
|
|
style->setFollowingStyle( style );
|
|
style->format() = *(cursor.parag()->at(cursor.index())->format());
|
|
|
|
style->paragLayout() = layout;
|
|
// Select this new style - hmm only the parag layout, we don't want to erase any text-formatting
|
|
cursor.parag()->setParagLayout( style->paragLayout() );
|
|
return style;
|
|
}
|
|
|
|
void KoTextView::updateStyleFromSelection( KoParagStyle* style )
|
|
{
|
|
KoTextCursor cursor = *m_cursor;
|
|
if ( textDocument()->hasSelection( KoTextDocument::Standard ) )
|
|
cursor = textDocument()->selectionStartCursor( KoTextDocument::Standard );
|
|
|
|
style->paragLayout() = cursor.parag()->paragLayout();
|
|
style->paragLayout().style = style;
|
|
style->format() = *(cursor.parag()->at(cursor.index())->format());
|
|
}
|
|
|
|
void KoTextView::addBookmarks(const TQString &url)
|
|
{
|
|
TQString filename = locateLocal( "data", TQString::fromLatin1("konqueror/bookmarks.xml") );
|
|
KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename,false );
|
|
KBookmarkGroup group = bookManager->root();
|
|
group.addBookmark( bookManager, url, KURL( url));
|
|
bookManager->save();
|
|
// delete bookManager;
|
|
}
|
|
|
|
void KoTextView::copyLink()
|
|
{
|
|
KoLinkVariable * var=linkVariable();
|
|
if(var)
|
|
{
|
|
KURL::List lst;
|
|
lst.append( var->url() );
|
|
TQApplication::clipboard()->setSelectionMode(true);
|
|
TQApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
|
|
TQApplication::clipboard()->setSelectionMode(false);
|
|
TQApplication::clipboard()->setData( new KURLDrag(lst, 0, 0) );
|
|
}
|
|
}
|
|
|
|
void KoTextView::removeLink()
|
|
{
|
|
KoLinkVariable * var=linkVariable();
|
|
if(var)
|
|
{
|
|
KoTextCursor c1 = *m_cursor;
|
|
KoTextCursor c2 = *m_cursor;
|
|
c1.setIndex(var->index());
|
|
c2.setIndex(var->index()+1);
|
|
textDocument()->setSelectionStart( KoTextDocument::Temp, &c1 );
|
|
textDocument()->setSelectionEnd( KoTextDocument::Temp, &c2 );
|
|
KCommand *cmd=textObject()->replaceSelectionCommand( &c1, var->value(),
|
|
i18n("Remove Link"), KoTextDocument::Temp );
|
|
if ( cmd )
|
|
textObject()->emitNewCommand( cmd );
|
|
}
|
|
}
|
|
|
|
void KoTextView::setBackSpeller( KoBgSpellCheck* backSpeller )
|
|
{
|
|
d->m_backSpeller = backSpeller;
|
|
}
|
|
|
|
#include "KoTextView.moc"
|
|
class KoBgSpellCheck;
|