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