00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "katetemplatehandler.h"
00020 #include "katetemplatehandler.moc"
00021 #include "katedocument.h"
00022 #include "katesmartcursor.h"
00023 #include "kateview.h"
00024 #include "kateconfig.h"
00025 #include "katerenderer.h"
00026
00027 #include <ktexteditor/cursor.h>
00028 #include <ktexteditor/smartcursor.h>
00029 #include <ktexteditor/smartrange.h>
00030 #include <ktexteditor/range.h>
00031 #include <ktexteditor/attribute.h>
00032
00033 #include <QtCore/QRegExp>
00034 #include <kdebug.h>
00035
00036 KateTemplateHandler::KateTemplateHandler(
00037 KateDocument *doc,
00038 const KTextEditor::Cursor& position,
00039 const QString &templateString,
00040 const QMap<QString, QString> &initialValues )
00041 : QObject( doc )
00042 , KateKeyInterceptorFunctor()
00043 , m_doc( doc )
00044 , m_currentTabStop( -1 )
00045 , m_currentRange( 0 )
00046 , m_initOk( false )
00047 , m_recursion( false )
00048 , m_templateRange(0)
00049 {
00050 connect( m_doc, SIGNAL( destroyed() ), this, SLOT( slotDocumentDestroyed() ) );
00051
00052 if ( !m_doc->setTabInterceptor( this ) )
00053 {
00054 deleteLater();
00055 return ;
00056 }
00057
00058
00059
00060
00061
00062
00063
00064 QList<KateTemplateHandlerPlaceHolderInfo> buildList;
00065 QRegExp rx( "([$%])\\{([^}\\s]+)\\}" );
00066 rx.setMinimal( true );
00067 int pos = 0;
00068 int opos = 0;
00069 QString insertString = templateString;
00070
00071 while ( pos >= 0 )
00072 {
00073 pos = rx.indexIn( insertString, pos );
00074
00075 if ( pos > -1 )
00076 {
00077 if ( ( pos - opos ) > 0 )
00078 {
00079 if ( insertString[ pos - 1 ] == '\\' )
00080 {
00081 insertString.remove( pos - 1, 1 );
00082 opos = pos;
00083 continue;
00084 }
00085 }
00086
00087 QString placeholder = rx.cap( 2 );
00088 QString value = initialValues[ placeholder ];
00089
00090
00091 if ( rx.cap( 1 ) != "%" || placeholder == value )
00092 buildList.append( KateTemplateHandlerPlaceHolderInfo( pos, value.length(), placeholder ) );
00093
00094 insertString.replace( pos, rx.matchedLength(), value );
00095 pos += value.length();
00096 opos = pos;
00097 }
00098 }
00099
00100 doc->editStart();
00101
00102 if ( !doc->insertText( position, insertString ) )
00103 {
00104 deleteLater();
00105 doc->editEnd();
00106 return ;
00107 }
00108
00109 if ( buildList.isEmpty() )
00110 {
00111 m_initOk = true;
00112 deleteLater();
00113 doc->editEnd();
00114 return ;
00115 }
00116
00117 doc->undoSafePoint();
00118 doc->editEnd();
00119 generateRangeTable( position, insertString, buildList );
00120
00121
00122
00123
00124
00125
00126
00127
00128 connect( doc, SIGNAL( textInserted(KTextEditor::Document*, const KTextEditor::Range& ) ), this, SLOT( slotTextInserted(KTextEditor::Document*, const KTextEditor::Range& ) ) );
00129 connect( doc, SIGNAL( aboutToRemoveText( const KTextEditor::Range& ) ), this, SLOT( slotAboutToRemoveText( const KTextEditor::Range& ) ) );
00130 connect( doc, SIGNAL( textRemoved() ), this, SLOT( slotTextRemoved() ) );
00131
00132 ( *this ) ( Qt::Key_Tab );
00133 }
00134
00135 KateTemplateHandler::~KateTemplateHandler()
00136 {
00137 if ( m_doc )
00138 {
00139 m_doc->removeTabInterceptor( this );
00140 }
00141 delete m_templateRange;
00142 #ifdef __GNUC__
00143 #warning delete placeholder infos here
00144 #endif
00145 }
00146
00147 void KateTemplateHandler::slotRangeDeleted(KTextEditor::SmartRange* range) {
00148 if (range==m_templateRange) m_templateRange=0;
00149 }
00150
00151 void KateTemplateHandler::slotDocumentDestroyed() {m_doc = 0;}
00152
00153 void KateTemplateHandler::generateRangeTable( const KTextEditor::Cursor& insertPosition, const QString& insertString, const QList<KateTemplateHandlerPlaceHolderInfo> &buildList )
00154 {
00155
00156 KateRendererConfig *config=m_doc->activeKateView()->renderer()->config();
00157 kDebug(13020)<<config->templateEditablePlaceholderColor()<<config->templateBackgroundColor()<<config->templateFocusedEditablePlaceholderColor()<<config->templateNotEditablePlaceholderColor();
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170 QColor color;
00171 color=config->templateEditablePlaceholderColor();
00172 color.setAlpha(0x88);
00173 KTextEditor::Attribute::Ptr attributeEditableElement(new KTextEditor::Attribute());
00174 attributeEditableElement->setBackground(QBrush(color));
00175 KTextEditor::Attribute::Ptr attributeEditableElementFocus(new KTextEditor::Attribute());
00176 color=config->templateFocusedEditablePlaceholderColor();
00177 color.setAlpha(0x88);
00178 attributeEditableElementFocus->setBackground(QBrush(color));
00179 attributeEditableElement->setDynamicAttribute(KTextEditor::Attribute::ActivateCaretIn,attributeEditableElementFocus);
00180
00181 KTextEditor::Attribute::Ptr attributeNotEditableElement(new KTextEditor::Attribute());
00182 color=config->templateNotEditablePlaceholderColor();
00183 color.setAlpha(0x88);
00184 attributeNotEditableElement->setBackground(QBrush(color));
00185
00186 KTextEditor::Attribute::Ptr attributeTemplateBackground(new KTextEditor::Attribute());
00187 color=config->templateBackgroundColor();
00188 color.setAlpha(0x88);
00189 attributeTemplateBackground->setBackground(QBrush(color));
00190
00191
00192 KTextEditor::SmartCursor *endC= m_doc->newSmartCursor(insertPosition);
00193 endC->advance(insertString.length());
00194 m_templateRange=m_doc->newSmartRange(KTextEditor::Range(insertPosition,*endC));
00195 connect(m_templateRange->primaryNotifier(),SIGNAL(rangeDeleted(KTextEditor::SmartRange*)),this,SLOT(slotRangeDeleted(KTextEditor::SmartRange*)));
00196 kDebug(13020)<<insertPosition.line()<<"/"<<insertPosition.column()<<"--"<<endC->line()<<"/"<<endC->column()<<"++++"<<m_templateRange;
00197 delete endC;
00198 m_templateRange->setAttribute(attributeTemplateBackground);
00199
00200 uint line = insertPosition.line();
00201 uint col = insertPosition.column();
00202 uint colInText = 0;
00203
00204 foreach (const KateTemplateHandlerPlaceHolderInfo& info, buildList)
00205 {
00206 bool firstOccurrence=false;
00207 KateTemplatePlaceHolder *ph = m_dict[ info.placeholder ];
00208
00209 if ( !ph )
00210 {
00211 firstOccurrence=true;
00212 ph = new KateTemplatePlaceHolder(( info.placeholder == "cursor" ),true,false);
00213
00214 m_dict.insert( info.placeholder, ph );
00215
00216 if ( !ph->isCursor ) m_tabOrder.append( ph );
00217 }
00218
00219
00220 while ( colInText < info.begin )
00221 {
00222 ++col;
00223
00224 if ( insertString.at( colInText ) == '\n' )
00225 {
00226 col = 0;
00227 line++;
00228 }
00229
00230 ++colInText;
00231 }
00232
00233 KTextEditor::SmartCursor *tmpC=m_doc->newSmartCursor(KTextEditor::Cursor(line,col));;
00234 tmpC->advance(info.len);
00235 KTextEditor::SmartRange *hlr=m_doc->newSmartRange(KTextEditor::Range(KTextEditor::Cursor(line,col),*tmpC),m_templateRange,KTextEditor::SmartRange::ExpandRight);
00236 hlr->setAttribute(firstOccurrence?attributeEditableElement:attributeNotEditableElement);
00237 hlr->setParentRange(m_templateRange);
00238 delete tmpC;
00239 ph->ranges.append(hlr);
00240
00241 colInText += info.len;
00242 col += info.len;
00243
00244
00245 }
00246
00247 KateTemplatePlaceHolder *cursor = m_dict[ "cursor" ];
00248
00249 if ( cursor ) m_tabOrder.append( cursor );
00250 m_doc->addHighlightToDocument(m_templateRange,true);
00251 }
00252
00253 void KateTemplateHandler::slotTextInserted(KTextEditor::Document*, const KTextEditor::Range& range)
00254 {
00255 if (m_doc->isEditRunning() && (!m_doc->isWithUndo())) return;
00256
00257 #ifdef __GNUC__
00258 #warning FIXME undo/redo detection
00259 #endif
00260 kDebug(13020)<<"KateTemplateHandler::slotTextInserted *****";
00261 if ( m_recursion ) return ;
00262 kDebug(13020)<<"KateTemplateHandler::slotTextInserted: no recurssion";
00263
00264 KTextEditor::Cursor cur=range.start();
00265
00266 kDebug(13020)<<cur.line()<<"/"<<cur.column()<<"---"<<m_currentRange->start().line()<<"/"<<m_currentRange->start().column()<<"+++"<<m_currentRange->end().line()<<"/"<<m_currentRange->end().column();
00267
00268 if ( ( !m_currentRange ) ||
00269 ( ( !m_currentRange->contains( cur ) ) && ( ! ( ( m_currentRange->start() == m_currentRange->end() ) && m_currentRange->end() == cur ) )
00270 ) ) locateRange( cur );
00271
00272 if ( !m_currentRange ) return ;
00273 kDebug(13020)<<"KateTemplateHandler::slotTextInserted: m_currentRange is not null";
00274
00275 KateTemplatePlaceHolder *ph = m_tabOrder.at( m_currentTabStop );
00276
00277 m_recursion = true;
00278 m_doc->editStart( );
00279
00280 QString sourceText = m_doc->text ( *m_currentRange );
00281 kDebug(13020)<<"KateTemplateHandler::slotTextInserted:"<<ph->isReplacableSpace<<"--->"<<sourceText<<"<---";
00282 if ( (sourceText.length()==0) || (ph->isReplacableSpace && (sourceText==" ")) ) {
00283 ph->isReplacableSpace = true;
00284 sourceText=QString(" ");
00285 KTextEditor::Cursor start = m_currentRange->start();
00286 m_doc->insertText( m_currentRange->start(), sourceText );
00287 m_currentRange->setRange(KTextEditor::Range(start,m_currentRange->end()));
00288 m_doc->activeView()->setSelection( *m_currentRange );
00289 kDebug()<<"inserted a replaceable space:"<<m_currentRange->start().line()<<"/"<<m_currentRange->start().column()<<"+++"<<m_currentRange->end().line()<<"/"<<m_currentRange->end().column();
00290 }
00291 else {
00292 if (ph->isReplacableSpace && sourceText.startsWith(' ')) {
00293 m_doc->removeText( KTextEditor::Range(m_currentRange->start(),1));
00294 sourceText=sourceText.right(sourceText.length()-1);
00295 }
00296 ph->isReplacableSpace = false;
00297 }
00298 ph->isInitialValue = false;
00299
00300 bool undoDontMerge = m_doc->undoDontMerge();
00301
00302
00303
00304 foreach ( KTextEditor::SmartRange* range, ph->ranges )
00305 {
00306 if ( range == m_currentRange ) continue;
00307 kDebug(13020)<<"KateTemplateHandler::slotTextInserted: updating a range:"<<range->start().line()<<"/"<<range->start().column()<<"+++"<<range->end().line()<<"/"<<range->end().column();
00308 KTextEditor::Cursor start = range->start();
00309 KTextEditor::Cursor end = range->end();
00310
00311
00312 m_doc->removeText( *range, false );
00313 kDebug(13020)<<"KateTemplateHandler::slotTextInserted: updating a range(2):"<<range->start().line()<<"/"<<range->start().column()<<"+++"<<range->end().line()<<"/"<<range->end().column();
00314 m_doc->insertText( start, sourceText );
00315 range->setRange(KTextEditor::Range(start,range->end()));
00316 kDebug(13020)<<"KateTemplateHandler::slotTextInserted: updating a range(3):"<<range->start().line()<<"/"<<range->start().column()<<"+++"<<range->end().line()<<"/"<<range->end().column();
00317 }
00318
00319 m_doc->setUndoDontMerge(false);
00320 m_doc->setUndoDontMergeComplex(true);
00321 m_doc->undoSafePoint();
00322 m_doc->editEnd();
00323 m_doc->setUndoDontMerge(undoDontMerge);
00324 m_recursion = false;
00325
00326 if ( ph->isCursor ) deleteLater();
00327 }
00328
00329 void KateTemplateHandler::locateRange( const KTextEditor::Cursor& cursor )
00330 {
00331
00332
00333
00334
00335
00336 for ( int i = 0;i < m_tabOrder.count();i++ )
00337 {
00338 KateTemplatePlaceHolder *ph = m_tabOrder.at( i );
00339
00340 foreach ( KTextEditor::SmartRange* range, ph->ranges)
00341 {
00342 kDebug(13020)<<"KateTemplateHandler::locateRange:"<<"CURSOR:"<<cursor.line()<<"|"<<cursor.column()<<" RANGE:"<<range->start().line()<<"/"<<range->start().column()<<"+++"<<range->end().line()<<"/"<<range->end().column();
00343 if ( range->contains( cursor ) )
00344 {
00345 m_currentTabStop = i;
00346 m_currentRange = range;
00347
00348 return ;
00349 }
00350 }
00351
00352 }
00353
00354 m_currentRange = 0;
00355
00356
00357
00358
00359 KateTemplatePlaceHolder *cur = m_dict[ "cursor" ];
00360 if (cur) {
00361 if (cur->isInitialValue) {
00362 m_doc->removeText(*(cur->ranges[0]));
00363 }
00364 }
00365 deleteLater();
00366 }
00367
00368
00369 bool KateTemplateHandler::operator() ( int key )
00370 {
00371 if ( key==Qt::Key_Tab )
00372 {
00373 m_currentTabStop++;
00374
00375 if ( m_currentTabStop >= ( int ) m_tabOrder.count() )
00376 m_currentTabStop = 0;
00377 }
00378 else
00379 {
00380 m_currentTabStop--;
00381
00382 if ( m_currentTabStop < 0 ) m_currentTabStop = m_tabOrder.count() - 1;
00383 }
00384
00385 m_currentRange = m_tabOrder.at( m_currentTabStop )->ranges[0];
00386
00387 KateTemplatePlaceHolder *ph=m_tabOrder.at( m_currentTabStop );
00388 if ( ph->isInitialValue || ph->isReplacableSpace)
00389 {
00390 m_doc->activeView()->setSelection( *m_currentRange );
00391 }
00392 else m_doc->activeView()->setSelection( KTextEditor::Range(m_currentRange->end(), m_currentRange->end()) );
00393
00394 KTextEditor::Cursor curpos=m_currentRange->end();
00395
00396 m_doc->activeView()->setCursorPosition( curpos );
00397 m_doc->activeKateView()->tagLine( m_currentRange->end() );
00398 return true;
00399 }
00400
00401 void KateTemplateHandler::slotAboutToRemoveText( const KTextEditor::Range& range )
00402 {
00403 if ( m_recursion ) return ;
00404
00405 kDebug(13020)<<"KateTemplateHandler::slotAboutToRemoveText (remove):"<<range.start().line()<<"/"<<range.start().column()<<"+++"<<range.end().line()<<"/"<<range.end().column();
00406
00407 if (range.start()==range.end()) return;
00408
00409 if (m_currentRange) {
00410 KTextEditor::Cursor cur=range.start();
00411 kDebug(13020)<<cur.line()<<"/"<<cur.column()<<"---"<<m_currentRange->start().line()<<"/"<<m_currentRange->start().column()<<"+++"<<m_currentRange->end().line()<<"/"<<m_currentRange->end().column();
00412 }
00413 if ( m_currentRange && ( !m_currentRange->contains( range.start() ) ) ) {
00414 kDebug(13020)<<"KateTemplateHandler::slotAboutToRemoveText: about to locate range";
00415 locateRange( range.start() );
00416 }
00417
00418 if ( m_currentRange != 0 )
00419 {
00420 if ( range.end() <= m_currentRange->end() ) return ;
00421 }
00422
00423 kDebug(13020)<<"KateTemplateHandler::slotAboutToRemoveText: disconnect & leave";
00424 if ( m_doc )
00425 {
00426 disconnect( m_doc, SIGNAL( textInserted(KTextEditor::Document*, const KTextEditor::Range& ) ), this, SLOT( slotTextInserted(KTextEditor::Document*, const KTextEditor::Range& ) ) );
00427 disconnect( m_doc, SIGNAL( aboutToRemoveText( const KTextEditor::Range& ) ), this, SLOT( slotAboutToRemoveText( const KTextEditor::Range& ) ) );
00428 disconnect( m_doc, SIGNAL( textRemoved() ), this, SLOT( slotTextRemoved() ) );
00429 }
00430
00431 deleteLater();
00432 }
00433
00434 void KateTemplateHandler::slotTextRemoved()
00435 {
00436 if ( m_recursion ) return ;
00437 if ( !m_currentRange ) return ;
00438
00439 slotTextInserted( m_doc,*m_currentRange);
00440 }
00441