/* **************************************************************************** This file is part of KBabel Copyright (C) 1999-2000 by Matthias Kiefer 2001-2004 by Stanislav Visnovsky Alt+123 feature idea taken from KOffice by David Faure . Word wrap support by Jarno Elonen , 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 #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #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("",lastPos); } if(haveDel && multiline) { delPos=0; } else { delPos = (*it).find("",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("",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("",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 *list = diffPos[lineCounter]; if(!list) { list = new TQValueList; diffPos.insert(lineCounter,list); } list->append(di); } } } TQRegExp reg(""); str.replace(reg,""); reg.setPattern(""); 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 *list = diffPos[curpara]; if(list) { TQValueList::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 *list = diffPos[curpara]; if(list) { TQPen addPen(_diffAddColor,2); TQPen delPen(_diffDelColor,2); TQValueList::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=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_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"