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 KTextEditor::Cursor curE=range.end();
00266
00267 kDebug(13020)<<cur.line()<<"/"<<cur.column()<<"---"<<m_currentRange->start().line()<<"/"<<m_currentRange->start().column()<<"+++"<<m_currentRange->end().line()<<"/"<<m_currentRange->end().column();
00268
00269 kDebug(13020)<<m_doc->text(range);
00270
00271 if ( ( !m_currentRange ) ||
00272 ( ( !m_currentRange->contains( cur ) ) && ( ! ( ( m_currentRange->start() == m_currentRange->end() ) && ( (m_currentRange->end() == cur) ||
00273 (m_currentRange->start()==curE) ) ) )
00274 ) ) locateRange( cur,curE );
00275
00276 if ( !m_currentRange ) return ;
00277
00278
00279 bool expandedLeft=false;
00280
00281 if (m_currentRange->start()==curE) {
00282 expandedLeft=true;
00283 m_currentRange->setRange(KTextEditor::Range(cur,m_currentRange->end()));
00284 }
00285
00286 kDebug(13020)<<"KateTemplateHandler::slotTextInserted: m_currentRange is not null";
00287
00288 KateTemplatePlaceHolder *ph = m_tabOrder.at( m_currentTabStop );
00289
00290 m_recursion = true;
00291 m_doc->editStart( );
00292
00293 QString sourceText = m_doc->text ( *m_currentRange );
00294 kDebug(13020)<<"KateTemplateHandler::slotTextInserted:"<<ph->isReplacableSpace<<"--->"<<sourceText<<"<---";
00295 if ( (sourceText.length()==0) || (ph->isReplacableSpace && (sourceText==" ")) ) {
00296 ph->isReplacableSpace = true;
00297 sourceText=QString(" ");
00298 KTextEditor::Cursor start = m_currentRange->start();
00299 m_doc->insertText( m_currentRange->start(), sourceText );
00300 m_currentRange->setRange(KTextEditor::Range(start,m_currentRange->end()));
00301 m_doc->activeView()->setSelection( *m_currentRange );
00302 kDebug()<<"inserted a replaceable space:"<<m_currentRange->start().line()<<"/"<<m_currentRange->start().column()<<"+++"<<m_currentRange->end().line()<<"/"<<m_currentRange->end().column();
00303 }
00304 else {
00305 if (ph->isReplacableSpace && sourceText.startsWith(' ')) {
00306 m_doc->removeText( KTextEditor::Range(m_currentRange->start(),1));
00307 sourceText=sourceText.right(sourceText.length()-1);
00308 } else if (ph->isReplacableSpace && expandedLeft) {
00309 m_doc->removeText( KTextEditor::Range(KTextEditor::Cursor(m_currentRange->end().line(),m_currentRange->end().column()-1),1) );
00310 sourceText=sourceText.left(sourceText.length()-1);
00311 }
00312 ph->isReplacableSpace = false;
00313 }
00314 ph->isInitialValue = false;
00315
00316 bool undoDontMerge = m_doc->undoDontMerge();
00317
00318
00319
00320 foreach ( KTextEditor::SmartRange* range, ph->ranges )
00321 {
00322 if ( range == m_currentRange ) continue;
00323 kDebug(13020)<<"KateTemplateHandler::slotTextInserted: updating a range:"<<range->start().line()<<"/"<<range->start().column()<<"+++"<<range->end().line()<<"/"<<range->end().column();
00324 KTextEditor::Cursor start = range->start();
00325 KTextEditor::Cursor end = range->end();
00326
00327
00328 m_doc->removeText( *range, false );
00329 kDebug(13020)<<"KateTemplateHandler::slotTextInserted: updating a range(2):"<<range->start().line()<<"/"<<range->start().column()<<"+++"<<range->end().line()<<"/"<<range->end().column();
00330 m_doc->insertText( start, sourceText );
00331 range->setRange(KTextEditor::Range(start,range->end()));
00332 kDebug(13020)<<"KateTemplateHandler::slotTextInserted: updating a range(3):"<<range->start().line()<<"/"<<range->start().column()<<"+++"<<range->end().line()<<"/"<<range->end().column();
00333 }
00334
00335 m_doc->setUndoDontMerge(false);
00336 m_doc->setUndoDontMergeComplex(true);
00337 m_doc->undoSafePoint();
00338 m_doc->editEnd();
00339 m_doc->setUndoDontMerge(undoDontMerge);
00340 m_recursion = false;
00341
00342 if ( ph->isCursor ) deleteLater();
00343 }
00344
00345 void KateTemplateHandler::locateRange( const KTextEditor::Cursor& cursor, const KTextEditor::Cursor& cursor2 )
00346 {
00347
00348
00349
00350
00351
00352 for ( int i = 0;i < m_tabOrder.count();i++ )
00353 {
00354 KateTemplatePlaceHolder *ph = m_tabOrder.at( i );
00355
00356 foreach ( KTextEditor::SmartRange* range, ph->ranges)
00357 {
00358 kDebug(13020)<<"KateTemplateHandler::locateRange:"<<"CURSOR:"<<cursor.line()<<"|"<<cursor.column()<<" RANGE:"<<range->start().line()<<"/"<<range->start().column()<<"+++"<<range->end().line()<<"/"<<range->end().column();
00359 if ( range->contains( cursor ) )
00360 {
00361 m_currentTabStop = i;
00362 m_currentRange = range;
00363
00364 return ;
00365 }
00366 }
00367
00368 }
00369
00370 for ( int i = 0;i < m_tabOrder.count();i++ )
00371 {
00372 KateTemplatePlaceHolder *ph = m_tabOrder.at( i );
00373
00374 foreach ( KTextEditor::SmartRange* range, ph->ranges)
00375 {
00376 kDebug(13020)<<"KateTemplateHandler::locateRange:"<<"CURSOR:"<<cursor.line()<<"|"<<cursor.column()<<" RANGE:"<<range->start().line()<<"/"<<range->start().column()<<"+++"<<range->end().line()<<"/"<<range->end().column();
00377 if ( range->contains( cursor2 ) )
00378 {
00379 m_currentTabStop = i;
00380 m_currentRange = range;
00381
00382 return ;
00383 }
00384 }
00385
00386 }
00387
00388 m_currentRange = 0;
00389
00390
00391
00392
00393 KateTemplatePlaceHolder *cur = m_dict[ "cursor" ];
00394 if (cur) {
00395 if (cur->isInitialValue) {
00396 m_doc->removeText(*(cur->ranges[0]));
00397 }
00398 }
00399 deleteLater();
00400 }
00401
00402
00403 bool KateTemplateHandler::operator() ( int key )
00404 {
00405 if ( key==Qt::Key_Tab )
00406 {
00407 m_currentTabStop++;
00408
00409 if ( m_currentTabStop >= ( int ) m_tabOrder.count() )
00410 m_currentTabStop = 0;
00411 }
00412 else
00413 {
00414 m_currentTabStop--;
00415
00416 if ( m_currentTabStop < 0 ) m_currentTabStop = m_tabOrder.count() - 1;
00417 }
00418
00419 m_currentRange = m_tabOrder.at( m_currentTabStop )->ranges[0];
00420
00421 KateTemplatePlaceHolder *ph=m_tabOrder.at( m_currentTabStop );
00422 if ( ph->isInitialValue || ph->isReplacableSpace)
00423 {
00424 m_doc->activeView()->setSelection( *m_currentRange );
00425 }
00426 else m_doc->activeView()->setSelection( KTextEditor::Range(m_currentRange->end(), m_currentRange->end()) );
00427
00428 KTextEditor::Cursor curpos=m_currentRange->end();
00429
00430 m_doc->activeView()->setCursorPosition( curpos );
00431 m_doc->activeKateView()->tagLine( m_currentRange->end() );
00432 return true;
00433 }
00434
00435 void KateTemplateHandler::slotAboutToRemoveText( const KTextEditor::Range& range )
00436 {
00437 if ( m_recursion ) return ;
00438
00439 kDebug(13020)<<"KateTemplateHandler::slotAboutToRemoveText (remove):"<<range.start().line()<<"/"<<range.start().column()<<"+++"<<range.end().line()<<"/"<<range.end().column();
00440
00441 if (range.start()==range.end()) return;
00442
00443 if (m_currentRange) {
00444 KTextEditor::Cursor cur=range.start();
00445 kDebug(13020)<<cur.line()<<"/"<<cur.column()<<"---"<<m_currentRange->start().line()<<"/"<<m_currentRange->start().column()<<"+++"<<m_currentRange->end().line()<<"/"<<m_currentRange->end().column();
00446 }
00447 if ( m_currentRange && ( !m_currentRange->contains( range.start() ) ) ) {
00448 kDebug(13020)<<"KateTemplateHandler::slotAboutToRemoveText: about to locate range";
00449 locateRange( range.start(), KTextEditor::Cursor(-1,-1) );
00450 }
00451
00452 if ( m_currentRange != 0 )
00453 {
00454 if ( range.end() <= m_currentRange->end() ) return ;
00455 }
00456
00457 kDebug(13020)<<"KateTemplateHandler::slotAboutToRemoveText: disconnect & leave";
00458 if ( m_doc )
00459 {
00460 disconnect( m_doc, SIGNAL( textInserted(KTextEditor::Document*, const KTextEditor::Range& ) ), this, SLOT( slotTextInserted(KTextEditor::Document*, const KTextEditor::Range& ) ) );
00461 disconnect( m_doc, SIGNAL( aboutToRemoveText( const KTextEditor::Range& ) ), this, SLOT( slotAboutToRemoveText( const KTextEditor::Range& ) ) );
00462 disconnect( m_doc, SIGNAL( textRemoved() ), this, SLOT( slotTextRemoved() ) );
00463 }
00464
00465 deleteLater();
00466 }
00467
00468 void KateTemplateHandler::slotTextRemoved()
00469 {
00470 if ( m_recursion ) return ;
00471 if ( !m_currentRange ) return ;
00472
00473 slotTextInserted( m_doc,*m_currentRange);
00474 }
00475