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.
1669 lines
45 KiB
1669 lines
45 KiB
/* ****************************************************************************
|
|
This file is part of KBabel
|
|
|
|
Copyright (C) 1999-2000 by Matthias Kiefer
|
|
<matthias.kiefer@gmx.de>
|
|
2001-2004 by Stanislav Visnovsky
|
|
<visnovsky@kde.org>
|
|
|
|
Alt+123 feature idea taken from KOffice by David Faure <david@mandrakesoft.com>.
|
|
Word wrap support by Jarno Elonen <elonen@iki.fi>, 2003
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
In addition, as a special exception, the copyright holders give
|
|
permission to link the code of this program with any edition of
|
|
the TQt library by Trolltech AS, Norway (or with modified versions
|
|
of TQt that use the same license as TQt), and distribute linked
|
|
combinations including the two. You must obey the GNU General
|
|
Public License in all respects for all of the code used other than
|
|
TQt. If you modify this file, you may extend this exception to
|
|
your version of the file, but you are not obligated to do so. If
|
|
you do not wish to do so, delete this exception statement from
|
|
your version.
|
|
|
|
**************************************************************************** */
|
|
|
|
|
|
#include "mymultilineedit.h"
|
|
#include "editcmd.h"
|
|
#include "resources.h"
|
|
|
|
#include <tqpixmap.h>
|
|
#include <tqpainter.h>
|
|
#include <tqvaluelist.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqregexp.h>
|
|
#include <tqclipboard.h>
|
|
#include <tqapplication.h>
|
|
#include <tqdragobject.h>
|
|
//#include <private/tqrichtext_p.h>
|
|
#include <tqpopupmenu.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <tdeglobal.h>
|
|
#include <tdeglobalsettings.h>
|
|
#include <tdemessagebox.h>
|
|
#include <tdestdaccel.h>
|
|
|
|
#include "kbhighlighting.h"
|
|
|
|
using namespace KBabel;
|
|
|
|
MyMultiLineEdit::MyMultiLineEdit(int ID, TQWidget* parent,const char* name)
|
|
:KTextEdit(parent,name), emitUndo(true),
|
|
_firstChangedLine(0),
|
|
_lastChangedLine(0),
|
|
_lastParagraph(0),
|
|
_lastParagraphOffset(0),
|
|
_lastSelectionStart(-1),
|
|
_lastSelectionEnd(-1),
|
|
_dontUpdate(false), _myID (ID),
|
|
_menu(0), _overwrite(false)
|
|
{
|
|
setUndoRedoEnabled(false); // we handle this ourselves
|
|
setWordWrap( WidgetWidth );
|
|
viewport()->setAcceptDrops( false ); // we need our parent to get drops
|
|
|
|
connect(this, TQ_SIGNAL(selectionChanged()), this, TQ_SLOT( onSelectionChanged() ) );
|
|
}
|
|
|
|
void MyMultiLineEdit::onSelectionChanged()
|
|
{
|
|
kdDebug(KBABEL) << "MyMultiLineEdit::onSelectionChanged" << endl;
|
|
int parFrom, parTo, indexFrom, indexTo;
|
|
if ( hasSelectedText() ) {
|
|
getSelection( &parFrom, &indexFrom, &parTo, &indexTo );
|
|
kdDebug(KBABEL) << "parFrom=" << parFrom << "indexFrom=" << indexFrom << "parTo=" << parTo << "indexTo=" << indexTo << endl;
|
|
_lastSelectionStart = beginOfMarkedText();
|
|
}
|
|
else
|
|
{
|
|
_lastSelectionStart = -1; // no selection
|
|
_lastSelectionEnd = -1;
|
|
}
|
|
|
|
//kdDebug(KBABEL) << "_lastSelectionStart=" << _lastSelectionStart << endl;
|
|
}
|
|
|
|
void MyMultiLineEdit::processCommand(EditCommand* cmd, bool undo)
|
|
{
|
|
if(cmd->terminator()!=0)
|
|
return;
|
|
|
|
DelTextCmd* delcmd = (DelTextCmd*) cmd;
|
|
bool ins = true;
|
|
if (delcmd->type() == EditCommand::Delete )
|
|
ins = undo;
|
|
else if (delcmd->type() == EditCommand::Insert )
|
|
ins = !undo;
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
// avoid duplicate update of catalog
|
|
bool oldEmitUndo = emitUndo;
|
|
emitUndo = false;
|
|
|
|
TQPalette _visibleHighlight( palette() );
|
|
TQPalette _invisibleHighlight( palette() );
|
|
TQColorGroup newcg( colorGroup() );
|
|
newcg.setColor( TQColorGroup::HighlightedText, newcg.text() );
|
|
newcg.setColor( TQColorGroup::Highlight, newcg.base() );
|
|
if( hasFocus() ) _invisibleHighlight.setActive( newcg );
|
|
else _invisibleHighlight.setInactive( newcg );
|
|
setPalette( _invisibleHighlight );
|
|
|
|
if(delcmd->offset <= (int)_lastParagraphOffset)
|
|
{
|
|
_lastParagraph=0;
|
|
_lastParagraphOffset=0;
|
|
}
|
|
|
|
if ( ins )
|
|
{
|
|
int row, col;
|
|
|
|
offset2Pos( delcmd->offset, row, col );
|
|
setCursorPosition( row, col );
|
|
|
|
_firstChangedLine=row;
|
|
if(delcmd->str.find("\n")>0 )_lastChangedLine=row+delcmd->str.contains("\n");
|
|
else _lastChangedLine=row;
|
|
|
|
KTextEdit::insert( delcmd->str );
|
|
|
|
offset2Pos( delcmd->offset+delcmd->str.length(), row, col );
|
|
setCursorPosition( row, col);
|
|
}
|
|
else
|
|
{ // del
|
|
|
|
int row, col, rowEnd, colEnd;
|
|
|
|
offset2Pos( delcmd->offset, row, col );
|
|
offset2Pos( delcmd->offset + delcmd->str.length(), rowEnd, colEnd );
|
|
|
|
setSelection( row, col, rowEnd, colEnd, 0 );
|
|
_firstChangedLine=_lastChangedLine=row;
|
|
KTextEdit::removeSelectedText();
|
|
}
|
|
|
|
|
|
setPalette( _visibleHighlight );
|
|
|
|
emitUndo = oldEmitUndo;
|
|
|
|
emitCursorPosition();
|
|
}
|
|
|
|
int MyMultiLineEdit::beginOfLastMarkedText()
|
|
{
|
|
if ( _lastSelectionStart != -1 )
|
|
return _lastSelectionStart;
|
|
else
|
|
return currentIndex();
|
|
}
|
|
|
|
int MyMultiLineEdit::endOfLastMarkedText()
|
|
{
|
|
if ( _lastSelectionEnd != -1 )
|
|
return _lastSelectionEnd;
|
|
else
|
|
return currentIndex();
|
|
}
|
|
|
|
int MyMultiLineEdit::beginOfMarkedText()
|
|
{
|
|
int beginX=0;
|
|
int beginY=0;
|
|
int endX=0;
|
|
int endY=0;
|
|
|
|
int pos=-1;
|
|
|
|
getSelection(&beginY,&beginX,&endY,&endX);
|
|
if( hasSelectedText() )
|
|
{
|
|
pos = pos2Offset(beginY,beginX);
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
void MyMultiLineEdit::emitCursorPosition()
|
|
{
|
|
int line=0;
|
|
int col=0;
|
|
getCursorPosition(&line,&col);
|
|
|
|
emit cursorPositionChanged(line, col);
|
|
}
|
|
|
|
void MyMultiLineEdit::wheelEvent(TQWheelEvent *e)
|
|
{
|
|
e->ignore();
|
|
}
|
|
|
|
void MyMultiLineEdit::focusInEvent(TQFocusEvent *e)
|
|
{
|
|
KTextEdit::focusInEvent(e);
|
|
emitCursorPosition();
|
|
}
|
|
|
|
void MyMultiLineEdit::contentsContextMenuEvent( TQContextMenuEvent * e)
|
|
{
|
|
e->accept();
|
|
if( _menu ) _menu->exec( e->globalPos() );
|
|
}
|
|
|
|
TQPopupMenu * MyMultiLineEdit::createPopupMenu()
|
|
{
|
|
return _menu;
|
|
}
|
|
|
|
TQPopupMenu * MyMultiLineEdit::createPopupMenu(const TQPoint &)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void MyMultiLineEdit::setContextMenu( TQPopupMenu * menu )
|
|
{
|
|
_menu = menu;
|
|
}
|
|
|
|
void MyMultiLineEdit::doKeyboardAction( KeyboardAction action )
|
|
{
|
|
int row,col;
|
|
getCursorPosition(&row, &col);
|
|
|
|
switch( action ) {
|
|
case ActionDelete:
|
|
_firstChangedLine=_lastChangedLine=row;
|
|
my_del(); break;
|
|
|
|
case ActionBackspace:
|
|
_firstChangedLine=_lastChangedLine=row;
|
|
my_backspace(); break;
|
|
|
|
case ActionReturn:
|
|
if( emitUndo)
|
|
emit signalUndoCmd( new InsTextCmd(currentIndex(), "\n", _myID) );
|
|
break;
|
|
|
|
case ActionKill:
|
|
_firstChangedLine=_lastChangedLine=row;
|
|
if(emitUndo)
|
|
{
|
|
int x,y;
|
|
getCursorPosition( &x, &y );
|
|
TQString s = text(x);
|
|
if( y < (int)s.length()-1 ) // not the end of paragraph
|
|
{
|
|
TQString delText = s.mid( y, s.length()-y-1);
|
|
emit signalUndoCmd( new DelTextCmd(currentIndex(), delText, _myID ) );
|
|
} else
|
|
if( x < paragraphs()-1 ) // not the end of text
|
|
emit signalUndoCmd( new DelTextCmd(currentIndex(), "\n", _myID ) );
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
KTextEdit::doKeyboardAction( action );
|
|
|
|
emitCursorPosition();
|
|
}
|
|
|
|
void MyMultiLineEdit::setText(const TQString& s)
|
|
{
|
|
_lastParagraph=0;
|
|
_lastParagraphOffset=0;
|
|
// workaround, since insert does not interpret markup
|
|
setTextFormat( TQt::PlainText );
|
|
_firstChangedLine=_lastChangedLine=0;
|
|
KTextEdit::setText(s);
|
|
setTextFormat( TQt::AutoText );
|
|
// now the number of lines is known, let's do highlight
|
|
_lastChangedLine=paragraphs();
|
|
emit textChanged();
|
|
emitCursorPosition();
|
|
}
|
|
|
|
void MyMultiLineEdit::insertAt( const TQString & s, int line, int col, bool mark )
|
|
{
|
|
// it will invoke insert, don't need to send InsTextCmd
|
|
KTextEdit::insertAt(s,line,col);
|
|
|
|
// code from TQMultiLineEdit
|
|
if( mark )
|
|
setSelection( line, col, line, col + s.length(), 0 );
|
|
// end of copied code
|
|
|
|
emitCursorPosition();
|
|
}
|
|
|
|
void MyMultiLineEdit::insert( const TQString & text, bool indent, bool checkNewLine, bool removeSelected )
|
|
{
|
|
int row,col;
|
|
|
|
bool noSelectionRemoved = true;
|
|
setUpdatesEnabled(false);
|
|
if( removeSelected && hasSelectedText() )
|
|
{
|
|
int endRow,endCol;
|
|
getSelection(&row,&col,&endRow,&endCol);
|
|
|
|
if( row < (int)_lastParagraph )
|
|
{
|
|
_lastParagraph=0;
|
|
_lastParagraphOffset=0;
|
|
}
|
|
|
|
_firstChangedLine=_lastChangedLine=row;
|
|
removeSelectedText();
|
|
noSelectionRemoved = false;
|
|
}
|
|
|
|
getCursorPosition(&row,&col);
|
|
_firstChangedLine=row;
|
|
_lastChangedLine=row;
|
|
|
|
if( emitUndo)
|
|
{
|
|
emit signalUndoCmd( new BeginCommand(-1,UndefPart));
|
|
// reimplemented overwrite
|
|
if( _overwrite && noSelectionRemoved)
|
|
{
|
|
doKeyboardAction( ActionDelete );
|
|
}
|
|
|
|
emit signalUndoCmd( new InsTextCmd(currentIndex(), text, _myID) );
|
|
emit signalUndoCmd( new EndCommand(-1,UndefPart));
|
|
}
|
|
|
|
int n=text.find("\n");
|
|
if( n > 0 ) _lastChangedLine+=n;
|
|
|
|
// setup palettes
|
|
|
|
TQPalette _visibleHighlight( palette() );
|
|
TQPalette _invisibleHighlight( palette() );
|
|
TQColorGroup newcg( colorGroup() );
|
|
newcg.setColor( TQColorGroup::HighlightedText, newcg.text() );
|
|
newcg.setColor( TQColorGroup::Highlight, newcg.base() );
|
|
if( hasFocus() ) _invisibleHighlight.setActive( newcg );
|
|
else _invisibleHighlight.setInactive( newcg );
|
|
setPalette( _invisibleHighlight );
|
|
KTextEdit::insert(text, indent, checkNewLine, removeSelected);
|
|
setPalette( _visibleHighlight );
|
|
|
|
setUpdatesEnabled(true);
|
|
|
|
emitCursorPosition();
|
|
}
|
|
|
|
void MyMultiLineEdit::removeLine ( int line )
|
|
{
|
|
kdDebug(KBABEL) << "removeLine invoked" << endl;
|
|
|
|
KTextEdit::removeParagraph(line);
|
|
emitCursorPosition();
|
|
}
|
|
|
|
void MyMultiLineEdit::clear()
|
|
{
|
|
_lastParagraph=0;
|
|
_lastParagraphOffset=0;
|
|
|
|
_dontUpdate=true;
|
|
|
|
TQString s = text();
|
|
if( !s.isEmpty() && emitUndo ) {
|
|
emit signalUndoCmd( new BeginCommand(-1,UndefPart) );
|
|
emit signalUndoCmd( new DelTextCmd(0,s,_myID) );
|
|
emit signalUndoCmd( new EndCommand(-1,UndefPart) );
|
|
}
|
|
|
|
KTextEdit::clear();
|
|
|
|
_dontUpdate=false;
|
|
|
|
_firstChangedLine=_lastChangedLine=0;
|
|
emitCursorPosition();
|
|
}
|
|
|
|
|
|
void MyMultiLineEdit::my_backspace()
|
|
{
|
|
|
|
int cursorY, cursorX;
|
|
getCursorPosition( &cursorY, &cursorX );
|
|
|
|
if( hasSelectedText())
|
|
{
|
|
Q_ASSERT( "backspace: This should never happen, why is not invoked removeSelectedText()?");
|
|
}
|
|
else if(! (cursorY==0 && cursorX==0) )
|
|
{
|
|
if(emitUndo)
|
|
{
|
|
int offset = currentIndex();
|
|
|
|
TQString s= text(cursorY);
|
|
if(cursorX != 0)
|
|
{
|
|
TQString delTxt(s[cursorX-1]);
|
|
emit signalUndoCmd(new DelTextCmd(offset-1,delTxt,_myID));
|
|
}
|
|
else if( cursorY > 0 || cursorX > 0 ) // not at the beginning
|
|
{
|
|
emit signalUndoCmd(new DelTextCmd(offset-1,"\n",_myID));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MyMultiLineEdit::my_del()
|
|
{
|
|
|
|
int cursorY, cursorX;
|
|
getCursorPosition( &cursorY, &cursorX );
|
|
|
|
if( hasSelectedText())
|
|
{
|
|
Q_ASSERT( "del: This should never happen, why is not invoked removeSelectedText()?");
|
|
}
|
|
else if(! (cursorY==paragraphs()-1 && cursorX==paragraphLength( cursorY )) )
|
|
{
|
|
if(emitUndo)
|
|
{
|
|
int offset = pos2Offset(cursorY, cursorX);
|
|
|
|
TQString s=text(cursorY);
|
|
if(cursorX != (int)s.length()-1)
|
|
{
|
|
TQString delTxt(s[cursorX]);
|
|
emit signalUndoCmd(new DelTextCmd(offset,delTxt,_myID));
|
|
}
|
|
else if( cursorY < (int)paragraphs()-1 || ( (cursorY == (int)paragraphs()-1) && (cursorX < (int)text( paragraphs()-1 ).length()-1 ) ) )// !atEnd() )
|
|
{
|
|
emit signalUndoCmd(new DelTextCmd(offset,"\n",_myID));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MyMultiLineEdit::removeSelectedText(int selNum)
|
|
{
|
|
if( selNum != 0 )
|
|
{
|
|
_lastParagraph=0;
|
|
_lastParagraphOffset=0;
|
|
|
|
KTextEdit::removeSelectedText(selNum);
|
|
}
|
|
else
|
|
{
|
|
int paraFrom, idxFrom, paraTo, idxTo;
|
|
KTextEdit::getSelection( ¶From, &idxFrom, ¶To, &idxTo );
|
|
|
|
if( paraFrom < (int)_lastParagraph )
|
|
{
|
|
_lastParagraph=0;
|
|
_lastParagraphOffset=0;
|
|
}
|
|
|
|
int offset = pos2Offset( paraFrom, idxFrom );
|
|
emit signalUndoCmd(new DelTextCmd( offset, selectedText(), _myID ) );
|
|
KTextEdit::removeSelectedText(selNum);
|
|
}
|
|
|
|
emitCursorPosition();
|
|
}
|
|
|
|
void MyMultiLineEdit::paste()
|
|
{
|
|
KTextEdit::paste();
|
|
emitCursorPosition();
|
|
}
|
|
|
|
int MyMultiLineEdit::currentIndex()
|
|
{
|
|
int para; // paragraph of current position
|
|
int index; // index in the current paragraph
|
|
|
|
KTextEdit::getCursorPosition(¶,&index);
|
|
|
|
return pos2Offset( para, index );
|
|
}
|
|
|
|
|
|
void MyMultiLineEdit::offset2Pos(int offset, int ¶graph, int &index) const
|
|
{
|
|
if (offset <= 0)
|
|
{
|
|
paragraph = 0;
|
|
index = 0;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
int charsLeft = offset;
|
|
int i;
|
|
|
|
for( i = 0; i < paragraphs(); ++i )
|
|
{
|
|
if (paragraphLength( i ) < charsLeft)
|
|
charsLeft -= paragraphLength( i );
|
|
else
|
|
{
|
|
paragraph = i;
|
|
index = charsLeft;
|
|
return;
|
|
}
|
|
--charsLeft;
|
|
}
|
|
|
|
paragraph = i-1;
|
|
index = charsLeft;
|
|
return;
|
|
}
|
|
}
|
|
|
|
int MyMultiLineEdit::pos2Offset(uint paragraph, uint index)
|
|
{
|
|
paragraph = TQMAX( TQMIN( (int)paragraph, paragraphs() - 1), 0 ); // Sanity check
|
|
index = TQMAX( TQMIN( (int)index, paragraphLength( paragraph )), 0 ); // Sanity check
|
|
|
|
{
|
|
uint lastI;
|
|
lastI = paragraphLength( paragraph );
|
|
uint i = 0;
|
|
uint tmp = 0;
|
|
|
|
if( paragraph>=_lastParagraph )
|
|
{
|
|
tmp = _lastParagraphOffset;
|
|
i = _lastParagraph;
|
|
}
|
|
|
|
for( ;i < paragraph ; i++ )
|
|
{
|
|
tmp += paragraphLength( i ) + 1;
|
|
}
|
|
|
|
_lastParagraphOffset=tmp;
|
|
_lastParagraph=paragraph;
|
|
|
|
tmp += TQMIN( lastI, index );
|
|
|
|
return tmp;
|
|
}
|
|
}
|
|
|
|
void MyMultiLineEdit::setReadOnly(bool on)
|
|
{
|
|
// I want this backgroundmode, also when readonly==true
|
|
if(on)
|
|
{
|
|
setBackgroundMode(PaletteBase);
|
|
}
|
|
|
|
TQTextEdit::setReadOnly(on);
|
|
}
|
|
|
|
void MyMultiLineEdit::setOverwriteMode( bool b )
|
|
{
|
|
_overwrite = b;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
MsgMultiLineEdit::MsgMultiLineEdit(int ID, KSpell* spell, TQWidget* parent,const char* name)
|
|
:MyMultiLineEdit(ID, parent,name),
|
|
_quotes(false),
|
|
_cleverEditing(false),
|
|
_highlightBg(false),
|
|
_spacePoints(false),
|
|
_bgColor(colorGroup().base().dark(110)),
|
|
_textColor(TDEGlobalSettings::textColor()),
|
|
_errorColor(TQt::red),
|
|
_currentColor(TDEGlobalSettings::textColor()),
|
|
_whitespace(0),
|
|
_hlSyntax(true),
|
|
_quoteColor(TQt::darkGreen),
|
|
_unquoteColor(TQt::red),
|
|
_cformatColor(TQt::blue),
|
|
_accelColor(TQt::darkMagenta),
|
|
_showDiff(false),
|
|
_diffUnderlineAdd(true),
|
|
_diffStrikeOutDel(true),
|
|
_diffAddColor(TQt::darkGreen),
|
|
_diffDelColor(TQt::darkRed),
|
|
_currentUnicodeNumber(0),
|
|
highlighter(0),
|
|
_tagStartPara(0), _tagStartIndex(0), _tagEndPara(0), _tagEndIndex(0)
|
|
{
|
|
diffPos.setAutoDelete(true);
|
|
diffPos.clear();
|
|
|
|
_whitespace = new TQPixmap(2,2,-1,TQPixmap::BestOptim);
|
|
_whitespace->fill(_textColor);
|
|
_errorWhitespace = new TQPixmap(2,2,-1,TQPixmap::BestOptim);
|
|
_errorWhitespace->fill(_errorColor);
|
|
|
|
_whitespaceNB = new TQPixmap(3,3,-1,TQPixmap::BestOptim);
|
|
_whitespaceNB->fill();
|
|
_errorWhitespaceNB = new TQPixmap(3,3,-1,TQPixmap::BestOptim);
|
|
_errorWhitespaceNB->fill();
|
|
|
|
TQPainter p(_whitespaceNB);
|
|
p.setPen( _textColor );
|
|
p.drawEllipse(_whitespaceNB->rect());
|
|
|
|
TQPainter q(_errorWhitespaceNB);
|
|
q.setPen( _errorColor );
|
|
q.drawEllipse(_errorWhitespaceNB->rect());
|
|
|
|
// this will setup bitBlt pixmaps
|
|
setFont( font() );
|
|
highlighter = new KBabelHighlighter( this, spell );
|
|
connect( this, TQ_SIGNAL( signalSyntaxHighlightingChanged( bool ) ), highlighter, TQ_SLOT( setSyntaxHighlighting( bool ) ) );
|
|
|
|
connect( this, TQ_SIGNAL( selectionChanged() ), this, TQ_SLOT( paintSpacePoints() ) );
|
|
connect( this, TQ_SIGNAL( cursorPositionChanged( int, int ) ), this, TQ_SLOT( paintSpacePoints(int, int) ) );
|
|
connect( this, TQ_SIGNAL( textChanged() ), this, TQ_SLOT( emittedTextChanged() ) );
|
|
}
|
|
|
|
MsgMultiLineEdit::~MsgMultiLineEdit ()
|
|
{
|
|
if(highlighter)
|
|
delete highlighter;
|
|
}
|
|
|
|
void MsgMultiLineEdit::setText(const TQString& s)
|
|
{
|
|
TQString str = s;
|
|
|
|
if(_showDiff)
|
|
{
|
|
diffPos.clear();
|
|
int lines = s.contains('\n');
|
|
diffPos.resize(lines+1);
|
|
|
|
TQStringList lineList = TQStringList::split('\n',s,true);
|
|
|
|
int lineCounter=-1;
|
|
bool haveAdd=false;
|
|
bool haveDel=false;
|
|
bool multiline=false;
|
|
TQStringList::Iterator it;
|
|
for(it = lineList.begin(); it != lineList.end(); ++it)
|
|
{
|
|
lineCounter++;
|
|
|
|
int lastPos=0;
|
|
bool atEnd=false;
|
|
|
|
while(!atEnd)
|
|
{
|
|
int addPos=-1;
|
|
int delPos=-1;
|
|
|
|
if(haveAdd && multiline)
|
|
{
|
|
addPos=0;
|
|
}
|
|
else
|
|
{
|
|
addPos = (*it).find("<KBABELADD>",lastPos);
|
|
}
|
|
|
|
if(haveDel && multiline)
|
|
{
|
|
delPos=0;
|
|
}
|
|
else
|
|
{
|
|
delPos = (*it).find("<KBABELDEL>",lastPos);
|
|
}
|
|
|
|
if(delPos >= 0 && addPos >= 0)
|
|
{
|
|
if(delPos <= addPos)
|
|
{
|
|
haveDel=true;
|
|
haveAdd=false;
|
|
}
|
|
else
|
|
{
|
|
haveDel=false;
|
|
haveAdd=true;
|
|
}
|
|
}
|
|
else if(delPos >= 0)
|
|
{
|
|
haveDel=true;
|
|
haveAdd=false;
|
|
}
|
|
else if(addPos >= 0)
|
|
{
|
|
haveDel=false;
|
|
haveAdd=true;
|
|
}
|
|
else
|
|
{
|
|
atEnd=true;
|
|
haveAdd=false;
|
|
haveDel=false;
|
|
}
|
|
|
|
DiffInfo di;
|
|
di.begin=-1;
|
|
|
|
if(haveAdd)
|
|
{
|
|
if(!multiline)
|
|
{
|
|
(*it).remove(addPos,11);
|
|
}
|
|
|
|
int endPos = (*it).find("</KBABELADD>",addPos);
|
|
if(endPos < 0)
|
|
{
|
|
endPos = (*it).length();
|
|
atEnd=true;
|
|
multiline=true;
|
|
}
|
|
else
|
|
{
|
|
(*it).remove(endPos,12);
|
|
haveAdd=false;
|
|
multiline=false;
|
|
}
|
|
|
|
lastPos=endPos;
|
|
|
|
di.begin=addPos;
|
|
di.end=endPos-1;
|
|
di.add=true;
|
|
}
|
|
else if(haveDel)
|
|
{
|
|
if(!multiline)
|
|
{
|
|
(*it).remove(delPos,11);
|
|
}
|
|
|
|
int endPos = (*it).find("</KBABELDEL>",delPos);
|
|
if(endPos < 0)
|
|
{
|
|
endPos = (*it).length();
|
|
atEnd=true;
|
|
multiline=true;
|
|
}
|
|
else
|
|
{
|
|
(*it).remove(endPos,12);
|
|
haveDel=false;
|
|
multiline=false;
|
|
}
|
|
|
|
lastPos=endPos;
|
|
|
|
di.begin=delPos;
|
|
di.end=endPos-1;
|
|
di.add=false;
|
|
}
|
|
|
|
if(di.begin >= 0)
|
|
{
|
|
TQValueList<DiffInfo> *list = diffPos[lineCounter];
|
|
if(!list)
|
|
{
|
|
list = new TQValueList<DiffInfo>;
|
|
diffPos.insert(lineCounter,list);
|
|
}
|
|
|
|
list->append(di);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
TQRegExp reg("</?KBABELADD>");
|
|
str.replace(reg,"");
|
|
reg.setPattern("</?KBABELDEL>");
|
|
str.replace(reg,"");
|
|
}
|
|
|
|
MyMultiLineEdit::setText(str);
|
|
paintSpacePoints();
|
|
}
|
|
|
|
void MsgMultiLineEdit::setQuotes(bool on)
|
|
{
|
|
_quotes=on;
|
|
update();
|
|
}
|
|
|
|
void MsgMultiLineEdit::setCleverEditing(bool on)
|
|
{
|
|
_cleverEditing=on;
|
|
}
|
|
|
|
|
|
void MsgMultiLineEdit::setHighlightBg(bool on)
|
|
{
|
|
_highlightBg=on;
|
|
update();
|
|
}
|
|
|
|
|
|
void MsgMultiLineEdit::setBgColor(const TQColor& color)
|
|
{
|
|
_bgColor=color;
|
|
|
|
if(_highlightBg)
|
|
update();
|
|
}
|
|
|
|
void MsgMultiLineEdit::setSpacePoints(bool on)
|
|
{
|
|
_spacePoints=on;
|
|
|
|
update();
|
|
}
|
|
|
|
void MsgMultiLineEdit::setHighlightSyntax(bool on)
|
|
{
|
|
_hlSyntax=on;
|
|
|
|
emit signalSyntaxHighlightingChanged (on);
|
|
|
|
update();
|
|
}
|
|
|
|
void MsgMultiLineEdit::setHighlightColors(const TQColor& quoteColor, const TQColor& unquoteColor
|
|
, const TQColor& cformatColor, const TQColor& accelColor, const TQColor& tagColor)
|
|
{
|
|
_quoteColor=quoteColor;
|
|
_unquoteColor=unquoteColor;
|
|
_cformatColor=cformatColor;
|
|
_accelColor=accelColor;
|
|
_tagColor=tagColor;
|
|
|
|
highlighter->setHighlightColor( KBabelHighlighter::Tag, tagColor );
|
|
highlighter->setHighlightColor( KBabelHighlighter::Entity, accelColor );
|
|
highlighter->setHighlightColor( KBabelHighlighter::CFormat, cformatColor );
|
|
highlighter->setHighlightColor( KBabelHighlighter::Masked, quoteColor );
|
|
|
|
update();
|
|
}
|
|
|
|
|
|
void MsgMultiLineEdit::setFont(const TQFont& font)
|
|
{
|
|
KTextEdit::setFont(font);
|
|
|
|
// we don't need to calculate a special offset for non-breaking space, since
|
|
// they are very similar in size
|
|
TQFontMetrics fm(font);
|
|
_wsOffsetX = TQMAX(fm.width(' ')/2-2,1);
|
|
_wsOffsetY = TQMAX(fm.height()/2-1,0);
|
|
|
|
repaint();
|
|
}
|
|
|
|
void MsgMultiLineEdit::setDiffDisplayMode(bool addUnderline, bool delStrikeOut)
|
|
{
|
|
_diffUnderlineAdd = addUnderline;
|
|
_diffStrikeOutDel = delStrikeOut;
|
|
|
|
if(_showDiff)
|
|
update();
|
|
}
|
|
|
|
void MsgMultiLineEdit::setDiffColors(const TQColor& addColor
|
|
, const TQColor& delColor)
|
|
{
|
|
_diffAddColor = addColor;
|
|
_diffDelColor = delColor;
|
|
|
|
if(_showDiff)
|
|
update();
|
|
}
|
|
|
|
void MsgMultiLineEdit::setTextColor(const TQColor &color )
|
|
{
|
|
TQPalette p( palette() );
|
|
TQColorGroup newcg( colorGroup() );
|
|
newcg.setColor( TQColorGroup::Text, color );
|
|
if( hasFocus() ) p.setActive( newcg );
|
|
else p.setInactive( newcg );
|
|
setPalette( p );
|
|
_textColor = color;
|
|
highlighter->setHighlightColor( KBabelHighlighter::Normal, color );
|
|
}
|
|
|
|
void MsgMultiLineEdit::setErrorColor(const TQColor &color )
|
|
{
|
|
_errorColor = color;
|
|
highlighter->setHighlightColor( KBabelHighlighter::Error, color );
|
|
}
|
|
|
|
void MsgMultiLineEdit::setCurrentColor(const TextColor color)
|
|
{
|
|
if( color == NormalColor ) {
|
|
_currentColor = _textColor;
|
|
highlighter->setHasErrors( false );
|
|
} else {
|
|
_currentColor = _errorColor;
|
|
highlighter->setHasErrors( true );
|
|
}
|
|
|
|
/*
|
|
setUpdatesEnabled(false);
|
|
// need to block signals (especially textChanged() to avoid recursion with KBabelView::autoCheck
|
|
blockSignals(true);
|
|
selectAll();
|
|
setColor( _currentColor );
|
|
removeSelection();
|
|
setColor(_currentColor);
|
|
blockSignals(false);
|
|
setUpdatesEnabled(true);
|
|
*/
|
|
forceUpdate();
|
|
}
|
|
|
|
void MsgMultiLineEdit::setSpellChecker(KSpell* spell)
|
|
{
|
|
highlighter->setSpellChecker(spell);
|
|
}
|
|
|
|
void MsgMultiLineEdit::paintSpacePoints(int, int )
|
|
{
|
|
paintSpacePoints();
|
|
}
|
|
|
|
void MsgMultiLineEdit::paintSpacePoints()
|
|
{
|
|
TQRect r;
|
|
TQPainter painter(viewport() );
|
|
const TQFontMetrics& fm = fontMetrics();
|
|
|
|
int paranum = paragraphAt(TQPoint(contentsX(), contentsY()));
|
|
|
|
if( _spacePoints )
|
|
{
|
|
int curpara = paranum;
|
|
painter.setPen( _currentColor );
|
|
TQPixmap* ws, *wsnb;
|
|
|
|
if( _currentColor== _errorColor )
|
|
{
|
|
ws = _errorWhitespace;
|
|
wsnb = _errorWhitespaceNB;
|
|
}
|
|
else
|
|
{
|
|
ws = _whitespace;
|
|
wsnb = _whitespaceNB;
|
|
}
|
|
|
|
while( curpara < paragraphs())
|
|
{
|
|
if ( paragraphRect( curpara ).top() > contentsY()+visibleHeight()) break;
|
|
|
|
const TQString& s = text(curpara);
|
|
int i = s.find( " " );
|
|
while( (i >= 0) && (i < (int)s.length()-1) ) // -1 because text will end by EOLN
|
|
{
|
|
TQPixmap* pm = ( s.at(i).unicode() == 0x00A0U ) ? wsnb : ws;
|
|
TQRect r = mapToView( curpara, i );
|
|
r.moveBy( r.width()/2, (r.height() - fm.descent())/2 );
|
|
r.moveBy( -pm->rect().width()/2, -pm->rect().height()/2-1 );
|
|
bitBlt(viewport(), r.topLeft(), pm, pm->rect(), TQt::CopyROP);
|
|
i = s.find( " ", i+1 );
|
|
}
|
|
++curpara;
|
|
}
|
|
}
|
|
|
|
if( _quotes )
|
|
{
|
|
TQFontMetrics fm( font());
|
|
TQRect qs = fm.boundingRect("\"");
|
|
|
|
for( int curpara = paranum; curpara < paragraphs() ; curpara++ )
|
|
{
|
|
r = paragraphRect(curpara);
|
|
if( r.y() > contentsY()+visibleHeight() ) break;
|
|
|
|
painter.drawText( TQPoint( 0, mapToView( curpara, 0 ).top()) +
|
|
TQPoint(0, qs.height() + 4), "\""); // 4 = hardcoded margin in QT
|
|
painter.drawText( mapToView( curpara, TQMAX( 0,
|
|
((int)text( curpara ).length())-1)).topRight() +
|
|
TQPoint(0, qs.height() + 4), "\""); // 4 = hardcoded margin in QT
|
|
}
|
|
}
|
|
|
|
if( _showDiff && (!_diffUnderlineAdd || !_diffStrikeOutDel) )
|
|
{
|
|
if( paragraphs() == (int)diffPos.size() ) // sanity check
|
|
{
|
|
painter.setRasterOp( TQt::AndROP );
|
|
for( int curpara = paranum; curpara < paragraphs() ; curpara++ )
|
|
{
|
|
r = paragraphRect(curpara);
|
|
if( r.y() > contentsY()+visibleHeight() ) break;
|
|
|
|
TQValueList<DiffInfo> *list = diffPos[curpara];
|
|
if(list)
|
|
{
|
|
TQValueList<DiffInfo>::ConstIterator it;
|
|
for(it = list->begin(); it != list->end(); ++it)
|
|
{
|
|
TQRect beg = mapToView( curpara, (*it).begin );
|
|
TQRect end = mapToView( curpara, (*it).end );
|
|
|
|
TQColor* c = 0;
|
|
if( (*it).add && !_diffUnderlineAdd)
|
|
c = &_diffAddColor;
|
|
else if(!(*it).add && !_diffStrikeOutDel)
|
|
c = &_diffDelColor;
|
|
|
|
if ( c != 0 )
|
|
{
|
|
// Single or multiple lines?
|
|
if ( beg.top() == end.top())
|
|
{
|
|
painter.fillRect( TQRect( beg.topLeft(),
|
|
TQPoint( end.right(), end.bottom())), *c );
|
|
}
|
|
else
|
|
{
|
|
painter.fillRect( TQRect(
|
|
beg.topLeft(),
|
|
TQPoint( r.right(), beg.bottom())), *c );
|
|
if( end.top()-beg.bottom() > 2 ) {
|
|
// there is a line, not only thin space
|
|
painter.fillRect( TQRect(
|
|
TQPoint( r.left(), beg.bottom()),
|
|
TQPoint( r.right(), end.top())), *c );
|
|
}
|
|
painter.fillRect( TQRect(
|
|
TQPoint( r.left(), end.top()),
|
|
TQPoint( end.right(), end.bottom())), *c );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( _showDiff && (_diffUnderlineAdd || _diffStrikeOutDel) )
|
|
{
|
|
if( paragraphs() == (int)diffPos.size() ) // sanity check
|
|
{
|
|
for( int curpara = paranum; curpara < paragraphs() ; curpara++ )
|
|
{
|
|
r = paragraphRect(curpara);
|
|
if( r.y() > contentsY()+visibleHeight() ) break;
|
|
|
|
TQValueList<DiffInfo> *list = diffPos[curpara];
|
|
if(list)
|
|
{
|
|
TQPen addPen(_diffAddColor,2);
|
|
TQPen delPen(_diffDelColor,2);
|
|
TQValueList<DiffInfo>::ConstIterator it;
|
|
for(it = list->begin(); it != list->end(); ++it)
|
|
{
|
|
TQRect beg = mapToView( curpara, (*it).begin );
|
|
TQRect end = mapToView( curpara, (*it).end );
|
|
|
|
TQPen* p = 0;
|
|
int dy = 0;
|
|
if( (*it).add && _diffUnderlineAdd)
|
|
p = &addPen;
|
|
else if(!(*it).add && _diffStrikeOutDel)
|
|
{
|
|
p = &delPen;
|
|
dy = fm.ascent()/2-1;
|
|
}
|
|
|
|
if ( p != 0 )
|
|
{
|
|
painter.setPen( *p );
|
|
|
|
// Single or multiple lines?
|
|
if ( beg.top() == end.top())
|
|
painter.drawLine(
|
|
beg.topLeft() + TQPoint(0, fm.ascent()-dy),
|
|
end.topRight()+ TQPoint(0, fm.ascent()-dy));
|
|
else
|
|
{
|
|
int y = beg.top() + fm.ascent();
|
|
painter.drawLine(
|
|
TQPoint(beg.left(), y),
|
|
TQPoint(r.right(), y));
|
|
y += fm.lineSpacing();
|
|
while (y < end.top() + fm.ascent())
|
|
{
|
|
painter.drawLine(
|
|
TQPoint(r.left(), y),
|
|
TQPoint(r.right(), y));
|
|
y += fm.lineSpacing();
|
|
}
|
|
painter.drawLine(
|
|
TQPoint(r.left(), end.top() + fm.ascent()),
|
|
TQPoint(end.right(), end.top() + fm.ascent()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MsgMultiLineEdit::repaint()
|
|
{
|
|
highlight();
|
|
MyMultiLineEdit::repaint();
|
|
}
|
|
|
|
void MsgMultiLineEdit::forceUpdate()
|
|
{
|
|
_firstChangedLine=0;
|
|
_lastChangedLine=paragraphs()-1;
|
|
highlighter->highlight();
|
|
MyMultiLineEdit::repaint();
|
|
}
|
|
|
|
void MsgMultiLineEdit::ensureCursorVisible()
|
|
{
|
|
if( isUpdatesEnabled() )
|
|
MyMultiLineEdit::ensureCursorVisible();
|
|
}
|
|
|
|
void MsgMultiLineEdit::highlight()
|
|
{
|
|
/* if( _dontUpdate ) return;
|
|
|
|
TQColor bg;
|
|
if( _highlightBg ) bg = _bgColor;
|
|
else bg = colorGroup().base();
|
|
|
|
for( int i = 0 ; i < paragraphs() ; i++ )
|
|
setParagraphBackgroundColor( i, bg );
|
|
|
|
|
|
if(_hlSyntax)
|
|
{
|
|
blockSignals(true); // block signals to avoid recursion
|
|
setUpdatesEnabled(false);
|
|
int cursorParagraph, cursorIndex;
|
|
|
|
getCursorPosition( &cursorParagraph, &cursorIndex );
|
|
|
|
// setup new colors
|
|
|
|
uint i;
|
|
|
|
TQRegExp markup("(\\\\)|(\")|(\\\\[abfnrtv'\"\?\\\\])|(\\\\\\d+)|(\\\\x[\\dabcdef]+)"
|
|
"|(%[\\ddioxXucsfeEgGphln]+)|(&[^\\s])|(&[\\w-]+;)");
|
|
|
|
for( i = TQMAX(_firstChangedLine,0) ; i < TQMIN(_lastChangedLine+1,(uint)paragraphs()) ; i++ ) {
|
|
|
|
TQString line=text(i);
|
|
|
|
//remove old highlighting
|
|
setSelection(i,0,i,line.length());
|
|
setColor( _currentColor );
|
|
removeSelection();
|
|
|
|
TQColor colorToUse;
|
|
|
|
int index=0;
|
|
index=markup.search( line, index );
|
|
while(index>=0)
|
|
{
|
|
switch( line[index].latin1() )
|
|
{
|
|
case '\\':
|
|
if( markup.matchedLength() == 1 ) colorToUse=_unquoteColor;
|
|
else colorToUse=_quoteColor;
|
|
break;
|
|
case '\"':
|
|
colorToUse=_unquoteColor;
|
|
break;
|
|
case '%':
|
|
colorToUse=_cformatColor;
|
|
break;
|
|
case '&':
|
|
colorToUse=_accelColor;
|
|
break;
|
|
}
|
|
|
|
setSelection( i, index, i, index+markup.matchedLength(), 0);
|
|
setColor( colorToUse );
|
|
removeSelection();
|
|
index=markup.search( line, index+markup.matchedLength() );
|
|
}
|
|
}
|
|
|
|
// Color XML and HTML tags
|
|
|
|
int tagindex=0;
|
|
int taglength=0;
|
|
int lineindex=0;
|
|
uint index=0;
|
|
int startPara, endPara, startIndex, endIndex;
|
|
TQString t= text();
|
|
|
|
if(_lastParagraph <= _firstChangedLine)
|
|
{
|
|
index=_lastParagraph;
|
|
lineindex=_lastParagraphOffset;
|
|
}
|
|
|
|
for( ; index<_firstChangedLine ; index++)
|
|
lineindex+=paragraphLength(index)+1;
|
|
|
|
TQRegExp re("<.*>");
|
|
re.setMinimal(true);
|
|
|
|
if( _firstChangedLine >0 )
|
|
{
|
|
TQColor c;
|
|
TQFont f;
|
|
TQt::VerticalAlignment v;
|
|
getFormat(_firstChangedLine-1, paragraphLength(_firstChangedLine-1)-1, &f, &c, &v);
|
|
TQString l = text(_firstChangedLine-1);
|
|
if( c==_tagColor && !l.endsWith(">") ) // hope _tagColor will be different than other colors
|
|
{
|
|
TQRegExp endtag("[^<]*>");
|
|
tagindex=endtag.search(t, lineindex);
|
|
taglength=endtag.matchedLength();
|
|
} else {
|
|
tagindex=re.search(t, lineindex);
|
|
taglength=re.matchedLength();
|
|
}
|
|
} else {
|
|
tagindex=re.search( t, lineindex );
|
|
taglength=re.matchedLength();
|
|
}
|
|
|
|
while( tagindex >= 0 && (int)index<paragraphs())
|
|
{
|
|
while( tagindex>=lineindex && index<_lastChangedLine+2)
|
|
lineindex+=paragraphLength(index++)+1;
|
|
if(index==_lastChangedLine+2) break;
|
|
lineindex-=paragraphLength(index-1);
|
|
lineindex--;
|
|
index--;
|
|
|
|
startPara=index;
|
|
startIndex=tagindex-lineindex;
|
|
|
|
tagindex+=taglength;
|
|
|
|
while( tagindex>=lineindex && (int)index<paragraphs())
|
|
lineindex+=paragraphLength(index++)+1;
|
|
lineindex-=paragraphLength(index-1);
|
|
lineindex--;
|
|
index--;
|
|
|
|
endPara=index;
|
|
endIndex=tagindex-lineindex;
|
|
|
|
setSelection( startPara, startIndex, endPara, endIndex, 0 );
|
|
setColor( _tagColor );
|
|
removeSelection();
|
|
|
|
if(index>_lastChangedLine) break;
|
|
tagindex=re.search( t, tagindex );
|
|
taglength=re.matchedLength();
|
|
}
|
|
|
|
setCursorPosition( cursorParagraph, cursorIndex );
|
|
setColor( _textColor );
|
|
setUpdatesEnabled(true);
|
|
blockSignals(false); // block signals to avoid recursion
|
|
updateContents();
|
|
}
|
|
ensureCursorVisible();
|
|
*/
|
|
}
|
|
|
|
void MsgMultiLineEdit::drawContents( TQPainter *painter, int clipx, int clipy, int clipw, int cliph )
|
|
{
|
|
MyMultiLineEdit::drawContents( painter, clipx, clipy, clipw, cliph );
|
|
paintSpacePoints();
|
|
}
|
|
|
|
void MsgMultiLineEdit::paintEvent( TQPaintEvent *event )
|
|
{
|
|
MyMultiLineEdit::paintEvent( event );
|
|
paintSpacePoints();
|
|
}
|
|
|
|
TQRect MsgMultiLineEdit::mapToView( int para, int index )
|
|
{
|
|
if( para < 0 || para > paragraphs() ||
|
|
index < 0 || index > paragraphLength(para) )
|
|
return TQRect(); //invalid rectangle
|
|
|
|
const TQFontMetrics& fm = fontMetrics();
|
|
const TQString& paratext = text(para);
|
|
|
|
// Find index of the first character on the same line as parameter
|
|
// 'index' using binary search. Very fast, even for long texts.
|
|
int linestart = 0;
|
|
int indexline = lineOfChar( para, index );
|
|
if ( indexline > 0 )
|
|
{
|
|
int min = 0, max = index;
|
|
int i = (min + max)/2;
|
|
int iline = lineOfChar( para, i );
|
|
while ( iline != indexline-1 ||
|
|
lineOfChar( para, i+1 ) != indexline )
|
|
{
|
|
Q_ASSERT( min != max && min != i && max != i );
|
|
if ( iline < indexline )
|
|
min = i;
|
|
else
|
|
max = i;
|
|
i = (min + max)/2;
|
|
iline = lineOfChar( para, i );
|
|
}
|
|
linestart = i+1;
|
|
}
|
|
Q_ASSERT( linestart >= 0 );
|
|
|
|
int linewidth;
|
|
|
|
// if the tag is not valid, easy
|
|
if( (_tagStartPara == _tagEndPara) && (_tagStartIndex == _tagEndIndex) ) {
|
|
linewidth = fm.width( paratext.mid( linestart, index-linestart ));
|
|
} else {
|
|
int tso = pos2Offset( _tagStartPara, _tagStartIndex );
|
|
int teo = pos2Offset( _tagEndPara, _tagEndIndex );
|
|
int off = pos2Offset( para, index );
|
|
|
|
if( off < tso ) {
|
|
// it is surely before the tag
|
|
linewidth = fm.width( paratext.mid( linestart, index-linestart ));
|
|
} else if( off >= teo ) {
|
|
// it is surely after the tag
|
|
|
|
// is it on the same line as the end of the tag?
|
|
if( _tagEndPara < para || lineOfChar( _tagEndPara, _tagEndIndex ) < indexline ) {
|
|
// no tag on the line, no bold
|
|
linewidth = fm.width( paratext.mid( linestart, index-linestart ));
|
|
} else {
|
|
TQFont f( font() );
|
|
f.setBold( true );
|
|
TQFontMetrics bfm( f );
|
|
// is tag single displayed line?
|
|
if( _tagStartPara == _tagEndPara
|
|
&& lineOfChar( _tagStartPara, _tagStartIndex ) == lineOfChar( _tagEndPara, _tagEndIndex ) )
|
|
{
|
|
// yes, count the non-bold before the tag start
|
|
linewidth = fm.width( paratext.mid( linestart, _tagStartIndex-linestart ) )
|
|
+ bfm.width( paratext.mid( _tagStartIndex, _tagEndIndex-_tagStartIndex ) );
|
|
}
|
|
else
|
|
{
|
|
// count the part of the tag itself
|
|
linewidth = bfm.width( paratext.mid( linestart, _tagEndIndex-linestart ) );
|
|
}
|
|
|
|
// add the rest from tag to the index
|
|
linewidth += fm.width( paratext.mid( _tagEndIndex, index-_tagEndIndex ) );
|
|
}
|
|
}
|
|
else {
|
|
// in tag
|
|
TQFont f( font() );
|
|
f.setBold( true );
|
|
TQFontMetrics bfm( f );
|
|
// is it the first line of the tag?
|
|
if( para == _tagStartPara && indexline == lineOfChar( _tagStartPara, _tagStartIndex ) ) {
|
|
// start of the line is normal
|
|
linewidth = fm.width( paratext.mid( linestart, _tagStartIndex-linestart ) )
|
|
+ bfm.width( paratext.mid( _tagStartIndex, index-_tagStartIndex ) );
|
|
} else {
|
|
// whole is bold
|
|
linewidth = bfm.width( paratext.mid( linestart, index-linestart ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// FIXME as soon as it's possible to ask real margins from TQTextEdit:
|
|
const int left_margin = 4;
|
|
// const int top_margin = 4;
|
|
|
|
TQPainter painter( viewport());
|
|
const TQRect& linerect = paragraphRect(para);
|
|
return TQRect(
|
|
contentsToViewport( TQPoint(
|
|
left_margin + linerect.left() + linewidth ,
|
|
/*top_margin + */linerect.top() + indexline * fm.lineSpacing() + fm.leading())),
|
|
TQSize(
|
|
fm.charWidth( paratext, index ),
|
|
fm.lineSpacing()
|
|
));
|
|
}
|
|
|
|
void MsgMultiLineEdit::keyPressEvent(TQKeyEvent *e)
|
|
{
|
|
if(!_cleverEditing || isReadOnly())
|
|
{
|
|
MyMultiLineEdit::keyPressEvent(e);
|
|
return;
|
|
}
|
|
|
|
KKey key( e );
|
|
|
|
if(e->key() == Key_Return || e->key() == Key_Enter)
|
|
{
|
|
emit signalUndoCmd(new BeginCommand(-1,UndefPart));
|
|
|
|
int row, col;
|
|
getCursorPosition(&row,&col);
|
|
TQString str=text(row);
|
|
|
|
if(e->state() & ShiftButton)
|
|
{
|
|
if(col > 0 && !str.isEmpty())
|
|
{
|
|
if(str.at(col-1) == '\\' && !isMasked(&str,col-1))
|
|
{
|
|
insert("n",false);
|
|
}
|
|
else
|
|
{
|
|
insert("\\n",false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
insert("\\n",false);
|
|
}
|
|
}
|
|
else if(!(e->state() & ControlButton))
|
|
{
|
|
if(col > 0 && !str.isEmpty() && !str.at(col-1).isSpace())
|
|
{
|
|
if(str.at(col-1)=='\\' && !isMasked(&str,col-1))
|
|
{
|
|
insert("\\",false);
|
|
}
|
|
|
|
// if there is not a new line at the end
|
|
if(col < 2 || str.mid(col-2,2)!="\\n")
|
|
{
|
|
insert(" ",false);
|
|
}
|
|
}
|
|
else if(str.isEmpty())
|
|
{
|
|
insert("\\n",false);
|
|
}
|
|
}
|
|
|
|
if( !str.isEmpty())
|
|
{
|
|
// construct new event without modifiers
|
|
MyMultiLineEdit::keyPressEvent( new TQKeyEvent(e->type(), e->key(), e->ascii(), 0,
|
|
e->text(), e->isAutoRepeat(), e->count() ) );
|
|
e->accept();
|
|
}
|
|
|
|
emit signalUndoCmd(new EndCommand(-1,UndefPart));
|
|
return;
|
|
}
|
|
else if(e->key() == Key_Tab)
|
|
{
|
|
insert("\\t",false);
|
|
emit textChanged();
|
|
e->accept();
|
|
return;
|
|
}
|
|
else if((e->key() == Key_Delete && !(e->state() & ControlButton))
|
|
|| ((e->state() & ControlButton) && e->key() == Key_D) )
|
|
{
|
|
emit signalUndoCmd(new BeginCommand(-1,UndefPart));
|
|
|
|
if(!hasSelectedText())
|
|
{
|
|
int row, col;
|
|
getCursorPosition(&row,&col);
|
|
TQString str=text(row);
|
|
|
|
if(!str.isEmpty() && col < (int)str.length() && str.at(col) == '\\'
|
|
&& !isMasked(&str,col))
|
|
{
|
|
TQString spclChars="abfnrtv'\"?\\";
|
|
if(col < (int)str.length()-1
|
|
&& spclChars.contains(str.at(col+1)))
|
|
{
|
|
del();
|
|
}
|
|
}
|
|
}
|
|
|
|
del();
|
|
|
|
emit signalUndoCmd(new EndCommand(-1,UndefPart));
|
|
emit textChanged();
|
|
e->accept();
|
|
return;
|
|
}
|
|
else if(e->key() == Key_BackSpace
|
|
|| ((e->state() & ControlButton) && e->key() == Key_H) )
|
|
{
|
|
emit signalUndoCmd(new BeginCommand(-1,UndefPart));
|
|
|
|
if(!hasSelectedText())
|
|
{
|
|
int row, col;
|
|
getCursorPosition(&row,&col);
|
|
TQString str=text(row);
|
|
|
|
TQString spclChars="abfnrtv'\"?\\";
|
|
if(!str.isEmpty() && col > 0 && spclChars.contains(str.at(col-1)))
|
|
{
|
|
if(col > 1 && str.at(col-2)=='\\' && !isMasked(&str,col-2))
|
|
{
|
|
MyMultiLineEdit::keyPressEvent(e);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
MyMultiLineEdit::keyPressEvent(e);
|
|
|
|
emit signalUndoCmd(new EndCommand(-1,UndefPart));
|
|
|
|
e->accept();
|
|
return;
|
|
}
|
|
else if(e->text() == "\"")
|
|
{
|
|
emit signalUndoCmd(new BeginCommand(-1,UndefPart));
|
|
|
|
int row, col;
|
|
getCursorPosition(&row,&col);
|
|
TQString str=text(row);
|
|
|
|
if(col == 0 || str.at(col-1) != '\\' || isMasked(&str,col-1) )
|
|
{
|
|
insert("\\\"",false);
|
|
}
|
|
else
|
|
{
|
|
insert("\"",false);
|
|
}
|
|
|
|
e->accept();
|
|
|
|
emit signalUndoCmd(new EndCommand(-1,UndefPart));
|
|
return;
|
|
}
|
|
else if(e->key() == Key_Space && ( e->state() & AltButton ) )
|
|
{
|
|
insert( TQChar( 0x00a0U ) );
|
|
e->accept();
|
|
return;
|
|
}
|
|
// ALT+123 feature
|
|
else if(( e->state() & AltButton ) && e->text()[0].isDigit() )
|
|
{
|
|
TQString text=e->text();
|
|
while ( text[0].isDigit() ) {
|
|
_currentUnicodeNumber = 10*_currentUnicodeNumber+(text[0].digitValue());
|
|
text.remove( 0, 1 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MyMultiLineEdit::keyPressEvent(e);
|
|
}
|
|
}
|
|
|
|
void MsgMultiLineEdit::keyReleaseEvent(TQKeyEvent* e)
|
|
{
|
|
if ( e->key() == Key_Alt && _currentUnicodeNumber >= 32 )
|
|
{
|
|
TQString text = TQChar( _currentUnicodeNumber );
|
|
_currentUnicodeNumber=0;
|
|
insert( text );
|
|
}
|
|
}
|
|
|
|
void MsgMultiLineEdit::setDiffMode(bool on)
|
|
{
|
|
_showDiff=on;
|
|
|
|
if(!on)
|
|
{
|
|
diffPos.clear();
|
|
}
|
|
}
|
|
|
|
bool MsgMultiLineEdit::isMasked(TQString *str, uint col)
|
|
{
|
|
if(col == 0 || !str)
|
|
return false;
|
|
|
|
uint counter=0;
|
|
int pos=col;
|
|
|
|
while(pos >= 0 && str->at(pos) == '\\')
|
|
{
|
|
counter++;
|
|
pos--;
|
|
}
|
|
|
|
return !(bool)(counter%2);
|
|
}
|
|
|
|
void MsgMultiLineEdit::emittedTextChanged()
|
|
{
|
|
highlight();
|
|
paintSpacePoints();
|
|
}
|
|
|
|
void MsgMultiLineEdit::selectTag(int start, int length)
|
|
{
|
|
setUpdatesEnabled(false);
|
|
setSelection( _tagStartPara, _tagStartIndex, _tagEndPara, _tagEndIndex);
|
|
setBold( false );
|
|
|
|
offset2Pos(start, _tagStartPara, _tagStartIndex);
|
|
offset2Pos(start+length, _tagEndPara, _tagEndIndex);
|
|
|
|
setSelection( _tagStartPara, _tagStartIndex, _tagEndPara, _tagEndIndex);
|
|
setBold( true );
|
|
setUpdatesEnabled(true);
|
|
}
|
|
|
|
#include "mymultilineedit.moc"
|