/* This file is part of the KDE libraries Copyright (C) 2004 Joseph Wenninger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "katetemplatehandler.h" #include "katetemplatehandler.moc" #include "katedocument.h" #include "katesupercursor.h" #include "katearbitraryhighlight.h" #include "kateview.h" #include #include #include KateTemplateHandler::KateTemplateHandler( KateDocument *doc, uint line, uint column, const QString &templateString, const QMap &initialValues ) : QObject( doc ) , KateKeyInterceptorFunctor() , m_doc( doc ) , m_currentTabStop( -1 ) , m_currentRange( 0 ) , m_initOk( false ) , m_recursion( false ) { connect( m_doc, SIGNAL( destroyed() ), this, SLOT( slotDocumentDestroyed() ) ); m_ranges = new KateSuperRangeList( false, this ); //false/*,this*/); if ( !m_doc->setTabInterceptor( this ) ) { deleteLater(); return ; } KateArbitraryHighlight *kah = doc->arbitraryHL(); /*KateArbitraryHighlightRange *hlr=new KateArbitraryHighlightRange(doc,KateTextCursor(line,column), KateTextCursor(line,column+3)); hlr->setUnderline(true); hlr->setOverline(true); l->append(hlr);*/ QValueList buildList; QRegExp rx( "([$%])\\{([^}\\s]+)\\}" ); rx.setMinimal( true ); int pos = 0; int opos = 0; QString insertString = templateString; while ( pos >= 0 ) { pos = rx.search( insertString, pos ); if ( pos > -1 ) { if ( ( pos - opos ) > 0 ) { if ( insertString[ pos - 1 ] == '\\' ) { insertString.remove( pos - 1, 1 ); opos = pos; continue; } } QString placeholder = rx.cap( 2 ); QString value = initialValues[ placeholder ]; // don't add %{MACRO} to the tab navigation, unless there was not value if ( rx.cap( 1 ) != "%" || placeholder == value ) buildList.append( KateTemplateHandlerPlaceHolderInfo( pos, value.length(), placeholder ) ); insertString.replace( pos, rx.matchedLength(), value ); pos += value.length(); opos = pos; } } doc->editStart(); if ( !doc->insertText( line, column, insertString ) ) { deleteLater(); doc->editEnd(); return ; } if ( buildList.isEmpty() ) { m_initOk = true; deleteLater(); doc->editEnd(); return ; } doc->undoSafePoint(); doc->editEnd(); generateRangeTable( line, column, insertString, buildList ); kah->addHighlightToDocument( m_ranges ); for ( KateSuperRangeList::const_iterator it = m_ranges->begin();it != m_ranges->end();++it ) { m_doc->tagLines( ( *it ) ->start().line(), ( *it ) ->end().line() ); } /* connect(doc,SIGNAL(charactersInteractivelyInserted(int ,int ,const QString&)),this, SLOT(slotCharactersInteractivlyInserted(int,int,const QString&))); connect(doc,SIGNAL(charactersSemiInteractivelyInserted(int ,int ,const QString&)),this, SLOT(slotCharactersInteractivlyInserted(int,int,const QString&)));*/ connect( doc, SIGNAL( textInserted( int, int ) ), this, SLOT( slotTextInserted( int, int ) ) ); connect( doc, SIGNAL( aboutToRemoveText( const KateTextRange& ) ), this, SLOT( slotAboutToRemoveText( const KateTextRange& ) ) ); connect( doc, SIGNAL( textRemoved() ), this, SLOT( slotTextRemoved() ) ); ( *this ) ( Qt::Key_Tab ); } KateTemplateHandler::~KateTemplateHandler() { m_ranges->setAutoManage( true ); if ( m_doc ) { m_doc->removeTabInterceptor( this ); for ( KateSuperRangeList::const_iterator it = m_ranges->begin();it != m_ranges->end();++it ) { m_doc->tagLines( ( *it ) ->start().line(), ( *it ) ->end().line() ); } } m_ranges->clear(); } void KateTemplateHandler::slotDocumentDestroyed() {m_doc = 0;} void KateTemplateHandler::generateRangeTable( uint insertLine, uint insertCol, const QString& insertString, const QValueList &buildList ) { uint line = insertLine; uint col = insertCol; uint colInText = 0; for ( QValueList::const_iterator it = buildList.begin();it != buildList.end();++it ) { KateTemplatePlaceHolder *ph = m_dict[ ( *it ).placeholder ]; if ( !ph ) { ph = new KateTemplatePlaceHolder; ph->isInitialValue = true; ph->isCursor = ( ( *it ).placeholder == "cursor" ); m_dict.insert( ( *it ).placeholder, ph ); if ( !ph->isCursor ) m_tabOrder.append( ph ); ph->ranges.setAutoManage( false ); } // FIXME handle space/tab replacement correctly make it use of the indenter while ( colInText < ( *it ).begin ) { ++col; if ( insertString.at( colInText ) == '\n' ) { col = 0; line++; } ++colInText; } KateArbitraryHighlightRange *hlr = new KateArbitraryHighlightRange( m_doc, KateTextCursor( line, col ), KateTextCursor( line, ( *it ).len + col ) ); colInText += ( *it ).len; col += ( *it ).len; hlr->allowZeroLength(); hlr->setUnderline( true ); hlr->setOverline( true ); //hlr->setBehaviour(KateSuperRange::ExpandRight); ph->ranges.append( hlr ); m_ranges->append( hlr ); } KateTemplatePlaceHolder *cursor = m_dict[ "cursor" ]; if ( cursor ) m_tabOrder.append( cursor ); } void KateTemplateHandler::slotTextInserted( int line, int col ) { #ifdef __GNUC__ #warning FIXME undo/redo detection #endif if ( m_recursion ) return ; //if (m_editSessionNumber!=0) return; // assume that this is due an udno/redo operation right now KateTextCursor cur( line, col ); if ( ( !m_currentRange ) || ( ( !m_currentRange->includes( cur ) ) && ( ! ( ( m_currentRange->start() == m_currentRange->end() ) && m_currentRange->end() == cur ) ) ) ) locateRange( cur ); if ( !m_currentRange ) return ; KateTemplatePlaceHolder *ph = m_tabOrder.at( m_currentTabStop ); QString sourceText = m_doc->text ( m_currentRange->start().line(), m_currentRange->start().col(), m_currentRange->end().line(), m_currentRange->end().col(), false ); ph->isInitialValue = false; bool undoDontMerge = m_doc->m_undoDontMerge; Q_ASSERT( m_doc->editSessionNumber == 0 ); m_recursion = true; m_doc->editStart( /*false*/ ); for ( KateSuperRangeList::const_iterator it = ph->ranges.begin();it != ph->ranges.end();++it ) { if ( ( *it ) == m_currentRange ) continue; KateTextCursor start = ( *it ) ->start(); KateTextCursor end = ( *it ) ->end(); m_doc->removeText( start.line(), start.col(), end.line(), end.col(), false ); m_doc->insertText( start.line(), start.col(), sourceText ); } m_doc->m_undoDontMerge = false; m_doc->m_undoComplexMerge = true; m_doc->undoSafePoint(); m_doc->editEnd(); m_doc->m_undoDontMerge = undoDontMerge; m_recursion = false; if ( ph->isCursor ) deleteLater(); } void KateTemplateHandler::locateRange( const KateTextCursor& cursor ) { /* if (m_currentRange) { m_doc->tagLines(m_currentRange->start().line(),m_currentRange->end().line()); }*/ for ( uint i = 0;i < m_tabOrder.count();i++ ) { KateTemplatePlaceHolder *ph = m_tabOrder.at( i ); for ( KateSuperRangeList::const_iterator it = ph->ranges.begin();it != ph->ranges.end();++it ) { if ( ( *it ) ->includes( cursor ) ) { m_currentTabStop = i; m_currentRange = ( *it ); //m_doc->tagLines(m_currentRange->start().line(),m_currentRange->end().line()); return ; } } } m_currentRange = 0; /*while (m_ranges->count()>0) delete (m_ranges->take(0)); disconnect(m_ranges,0,0,0); delete m_ranges;*/ deleteLater(); } bool KateTemplateHandler::operator() ( KKey key ) { if ( key==Qt::Key_Tab ) { m_currentTabStop++; if ( m_currentTabStop >= ( int ) m_tabOrder.count() ) m_currentTabStop = 0; } else { m_currentTabStop--; if ( m_currentTabStop < 0 ) m_currentTabStop = m_tabOrder.count() - 1; } m_currentRange = m_tabOrder.at( m_currentTabStop ) ->ranges.at( 0 ); if ( m_tabOrder.at( m_currentTabStop ) ->isInitialValue ) { m_doc->activeView()->setSelection( m_currentRange->start(), m_currentRange->end() ); } else m_doc->activeView()->setSelection( m_currentRange->end(), m_currentRange->end() ); m_doc->activeView() ->setCursorPositionReal( m_currentRange->end().line(), m_currentRange->end().col() ); m_doc->activeView() ->tagLine( m_currentRange->end() ); return true; } void KateTemplateHandler::slotAboutToRemoveText( const KateTextRange &range ) { if ( m_recursion ) return ; if ( m_currentRange && ( !m_currentRange->includes( range.start() ) ) ) locateRange( range.start() ); if ( m_currentRange != 0 ) { if ( m_currentRange->end() <= range.end() ) return ; } if ( m_doc ) { disconnect( m_doc, SIGNAL( textInserted( int, int ) ), this, SLOT( slotTextInserted( int, int ) ) ); disconnect( m_doc, SIGNAL( aboutToRemoveText( const KateTextRange& ) ), this, SLOT( slotAboutToRemoveText( const KateTextRange& ) ) ); disconnect( m_doc, SIGNAL( textRemoved() ), this, SLOT( slotTextRemoved() ) ); } deleteLater(); } void KateTemplateHandler::slotTextRemoved() { if ( m_recursion ) return ; if ( !m_currentRange ) return ; slotTextInserted( m_currentRange->start().line(), m_currentRange->start().col() ); }