00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "katetemplatehandler.h"
00023 #include "katedocument.h"
00024 #include "katesmartcursor.h"
00025 #include "kateview.h"
00026 #include "kateconfig.h"
00027 #include "katerenderer.h"
00028 #include "kateundomanager.h"
00029
00030 #include <ktexteditor/cursor.h>
00031 #include <ktexteditor/smartcursor.h>
00032 #include <ktexteditor/smartrange.h>
00033 #include <ktexteditor/range.h>
00034 #include <ktexteditor/attribute.h>
00035
00036 #include <QtCore/QQueue>
00037
00038 #include <kdebug.h>
00039
00040 using namespace KTextEditor;
00041
00042 #define ifDebug(x) x
00043
00045 bool customContains(SmartRange* range, const Cursor& cursor)
00046 {
00047 return range->start() <= cursor && range->end() >= cursor;
00048 }
00049
00050
00051
00052 KateTemplateHandler::KateTemplateHandler( KateDocument *doc, const Cursor& position,
00053 const QString &templateString, const QMap<QString, QString> &initialValues, KateUndoManager* undoManager )
00054 : QObject(doc)
00055 , m_doc(doc), m_wholeTemplateRange(0), m_finalCursorPosition(0)
00056 , m_lastCaretPosition(position), m_isMirroring(false), m_editWithUndo(false), m_jumping(false)
00057 {
00058 ifDebug(kDebug() << templateString << initialValues;)
00059
00060 connect(m_doc, SIGNAL(aboutToReload(KTextEditor::Document*)),
00061 this, SLOT(cleanupAndExit()));
00062
00063 connect(m_doc, SIGNAL(textInserted(KTextEditor::Document*, KTextEditor::Range)),
00064 this, SLOT(slotTemplateInserted(KTextEditor::Document*, KTextEditor::Range)));
00065
00067 m_doc->editStart();
00068 if ( m_doc->insertText(position, templateString) ) {
00069 Q_ASSERT(m_wholeTemplateRange);
00070
00071 if ( m_doc->activeKateView() ) {
00072
00073
00074
00075 m_doc->align(m_doc->activeKateView(), *m_wholeTemplateRange);
00076 }
00077
00078 m_doc->undoSafePoint();
00079 }
00080 m_doc->editEnd();
00081
00087 if ( !initialValues.isEmpty() && m_doc->activeView() ) {
00088
00089
00090 handleTemplateString(initialValues);
00091
00092 if ( !m_templateRanges.isEmpty() ) {
00093 foreach ( View* view, m_doc->views() ) {
00094 setupEventHandler(view);
00095 }
00096
00097 connect(m_doc, SIGNAL(viewCreated(KTextEditor::Document*, KTextEditor::View*)),
00098 this, SLOT(slotViewCreated(KTextEditor::Document*, KTextEditor::View*)));
00099 connect(m_doc, SIGNAL(textChanged(KTextEditor::Document*, KTextEditor::Range, KTextEditor::Range)),
00100 this, SLOT(slotTextChanged(KTextEditor::Document*, KTextEditor::Range)));
00101 connect(m_doc, SIGNAL(textInserted(KTextEditor::Document*, KTextEditor::Range)),
00102 this, SLOT(slotTextChanged(KTextEditor::Document*, KTextEditor::Range)));
00103 connect(m_doc, SIGNAL(textRemoved(KTextEditor::Document*, KTextEditor::Range)),
00104 this, SLOT(slotTextChanged(KTextEditor::Document*, KTextEditor::Range)));
00105
00106 setEditWithUndo(undoManager->isActive());
00107
00108 connect(undoManager, SIGNAL(isActiveChanged(bool)),
00109 this, SLOT(setEditWithUndo(bool)));
00110 } else {
00111
00112 jumpToFinalCursorPosition();
00113 cleanupAndExit();
00114 }
00115 } else {
00116
00117 cleanupAndExit();
00118 }
00119 }
00120
00121 KateTemplateHandler::~KateTemplateHandler()
00122 {
00123 }
00124
00125 void KateTemplateHandler::slotTemplateInserted(Document *document, const Range& range)
00126 {
00127 Q_ASSERT(document == m_doc);
00128 Q_UNUSED(document);
00129 ifDebug(kDebug() << "template range inserted" << range;)
00130
00131 m_wholeTemplateRange = m_doc->newSmartRange( range, 0, SmartRange::ExpandLeft | SmartRange::ExpandRight );
00132
00133 disconnect(m_doc, SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)),
00134 this, SLOT(slotTemplateInserted(KTextEditor::Document*,KTextEditor::Range)));
00135 }
00136
00138 void deleteSmartRange(SmartRange* range, KateDocument* doc) {
00139 doc->removeHighlightFromDocument(range);
00140 SmartCursor* start = &range->smartStart();
00141 SmartCursor* end = &range->smartEnd();
00142 doc->unbindSmartRange(range);
00143 delete start;
00144 delete end;
00145 }
00146
00147 void KateTemplateHandler::cleanupAndExit()
00148 {
00149 ifDebug(kDebug() << "cleaning up and exiting";)
00150 disconnect(m_doc, SIGNAL(viewCreated(KTextEditor::Document*,KTextEditor::View*)),
00151 this, SLOT(slotViewCreated(KTextEditor::Document*,KTextEditor::View*)));
00152 disconnect(m_doc, SIGNAL(textChanged(KTextEditor::Document*,KTextEditor::Range,KTextEditor::Range)),
00153 this, SLOT(slotTextChanged(KTextEditor::Document*,KTextEditor::Range)));
00154 disconnect(m_doc, SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)),
00155 this, SLOT(slotTextChanged(KTextEditor::Document*,KTextEditor::Range)));
00156 disconnect(m_doc, SIGNAL(textRemoved(KTextEditor::Document*,KTextEditor::Range)),
00157 this, SLOT(slotTextChanged(KTextEditor::Document*,KTextEditor::Range)));
00158
00159 if ( !m_templateRanges.isEmpty() ) {
00160 foreach ( SmartRange* range, m_templateRanges ) {
00161 deleteSmartRange(range, m_doc);
00162 }
00163 m_templateRanges.clear();
00164 }
00165 if ( m_wholeTemplateRange ) {
00166 deleteSmartRange(m_wholeTemplateRange, m_doc);
00167 }
00168 if ( m_finalCursorPosition ) {
00169 delete m_finalCursorPosition;
00170 }
00171 delete this;
00172 }
00173
00174 void KateTemplateHandler::jumpToFinalCursorPosition()
00175 {
00176 if ( m_doc->activeView() && (!m_wholeTemplateRange
00177 || customContains(m_wholeTemplateRange, m_doc->activeView()->cursorPosition())) )
00178 {
00179 m_doc->activeView()->setSelection(Range::invalid());
00180 m_doc->activeView()->setCursorPosition(*m_finalCursorPosition);
00181 }
00182 }
00183
00184 void KateTemplateHandler::setEditWithUndo(const bool &enabled)
00185 {
00186 m_editWithUndo = enabled;
00187 }
00188
00189 void KateTemplateHandler::slotViewCreated(Document* document, View* view)
00190 {
00191 Q_ASSERT(document == m_doc); Q_UNUSED(document)
00192 setupEventHandler(view);
00193 }
00194
00195 void KateTemplateHandler::setupEventHandler(View* view)
00196 {
00197 view->focusProxy()->installEventFilter(this);
00198 }
00199
00200 bool KateTemplateHandler::eventFilter(QObject* object, QEvent* event)
00201 {
00202
00203 if ( event->type() == QEvent::KeyPress ) {
00204 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
00205 if ( keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab ) {
00206 if ( !m_doc->activeKateView()->isCompletionActive() ) {
00207 return true;
00208 }
00209 }
00210 }
00211
00212 if ( event->type() == QEvent::ShortcutOverride ) {
00213 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
00214 if ( keyEvent->key() == Qt::Key_Return && keyEvent->modifiers() & Qt::AltModifier ) {
00215
00216 jumpToFinalCursorPosition();
00217 cleanupAndExit();
00218 return true;
00219 } else if ( keyEvent->key() == Qt::Key_Escape ) {
00220 if ( !m_doc->activeView() || !m_doc->activeView()->selection() ) {
00221
00222 jumpToFinalCursorPosition();
00223 cleanupAndExit();
00224 return true;
00225 }
00226 } else if ( keyEvent->key() == Qt::Key_Tab && !m_doc->activeKateView()->isCompletionActive() ) {
00227 if ( keyEvent->modifiers() & Qt::Key_Shift ) {
00228 jumpToPreviousRange();
00229 } else {
00230 jumpToNextRange();
00231 }
00232 return true;
00233 } else if ( keyEvent->key() == Qt::Key_Backtab && !m_doc->activeKateView()->isCompletionActive() ) {
00234 jumpToPreviousRange();
00235 return true;
00236 }
00237 }
00238 return QObject::eventFilter(object, event);
00239 }
00240
00241 void KateTemplateHandler::jumpToPreviousRange()
00242 {
00243 const Cursor & cursor = m_doc->activeView()->cursorPosition();
00244 if ( cursor == *m_finalCursorPosition ) {
00245
00246 setCurrentRange(m_templateRanges.last());
00247 return;
00248 }
00249 SmartRange* previousRange = 0;
00250 foreach ( SmartRange* range, m_templateRanges ) {
00251 if ( range->start() >= cursor ) {
00252 continue;
00253 }
00254 if ( !previousRange || range->start() > previousRange->start() ) {
00255 previousRange = range;
00256 }
00257 }
00258 if ( previousRange ) {
00259 setCurrentRange(previousRange);
00260 } else {
00261
00262 jumpToFinalCursorPosition();
00263 }
00264 }
00265
00266 void KateTemplateHandler::jumpToNextRange()
00267 {
00268 const Cursor & cursor = m_doc->activeView()->cursorPosition();
00269 if ( cursor == *m_finalCursorPosition ) {
00270
00271 setCurrentRange(m_templateRanges.first());
00272 return;
00273 }
00274 SmartRange* nextRange = 0;
00275 foreach ( SmartRange* range, m_templateRanges ) {
00276 if ( range->start() <= cursor ) {
00277 continue;
00278 }
00279 if ( !nextRange || range->start() < nextRange->start() ) {
00280 nextRange = range;
00281 }
00282 }
00283 if ( nextRange ) {
00284 setCurrentRange(nextRange);
00285 } else {
00286
00287 jumpToFinalCursorPosition();
00288 }
00289 }
00290
00291 void KateTemplateHandler::setCurrentRange(SmartRange* range)
00292 {
00293 if ( !range->childRanges().isEmpty() ) {
00294
00295 range = range->childRanges().first();
00296 }
00297
00298 if ( m_doc->activeView() ) {
00299 m_jumping = true;
00300 if ( m_uneditedRanges.contains(range) ) {
00301 m_doc->activeView()->setSelection(*range);
00302 }
00303 m_doc->activeView()->setCursorPosition(range->start());
00304 m_jumping = false;
00305 }
00306
00307 m_lastCaretPosition = range->start();
00308 }
00309
00313 Attribute::Ptr getAttribute(QColor color, int alpha = 230)
00314 {
00315 Attribute::Ptr attribute(new Attribute());
00316 color.setAlpha(alpha);
00317 attribute->setBackground(QBrush(color));
00318 return attribute;
00319 }
00320
00321 void KateTemplateHandler::handleTemplateString(const QMap< QString, QString >& initialValues)
00322 {
00323 QString templateString = m_doc->text(*m_wholeTemplateRange);
00324
00325 int line = m_wholeTemplateRange->start().line();
00326 int column = m_wholeTemplateRange->start().column();
00327
00328
00329 int startPos = -1;
00330
00331
00332
00333
00334
00335 QQueue<QString> keyQueue;
00336 QMultiMap<QString, Range> ranges;
00337
00338
00339 Cursor finalCursorPosition = Cursor::invalid();
00340
00341
00342
00343
00344
00345
00346 for ( int i = 0; i < templateString.size(); ++i ) {
00347 if ( templateString[i] == '\n' ) {
00348 ++line;
00349 column = 0;
00350 if ( startPos != -1 ) {
00351
00352 startPos = -1;
00353 }
00354 } else if ( (templateString[i] == '%' || templateString[i] == '$')
00355 && i + 1 < templateString.size() && templateString[i+1] == '{' ) {
00356
00357 startPos = i;
00358
00359 ++i;
00360 column += 2;
00361 } else if ( templateString[i] == '}' && startPos != -1 ) {
00362
00363 int escapeChars = 0;
00364 while ( startPos - escapeChars > 0 && templateString[startPos - escapeChars - 1] == '\\' ) {
00365 ++escapeChars;
00366 }
00367 if ( escapeChars > 0 ) {
00368
00369
00370 int toRemove = (escapeChars + 1) / 2;
00371 templateString.remove(startPos - escapeChars, toRemove);
00372 i -= toRemove;
00373 column -= toRemove;
00374 startPos -= toRemove;
00375 }
00376 if ( escapeChars % 2 == 0 ) {
00377
00378 const QString key = templateString.mid( startPos + 2, i - (startPos + 2) );
00379 if ( !initialValues.contains(key) ) {
00380 kWarning() << "unknown variable key:" << key;
00381 } else if ( key == "cursor" ) {
00382 finalCursorPosition = Cursor(line, column - key.length() - 2);
00383
00384 templateString.remove(startPos, i - startPos + 1);
00385
00386 i -= 3 + key.length();
00387 column -= 2 + key.length();
00388 startPos = -1;
00389 } else {
00390
00391 QChar c = templateString[startPos];
00392
00393 templateString.replace( startPos, i - startPos + 1, initialValues[key] );
00394
00395 i -= 3 + key.length() - initialValues[key].length();
00396
00397
00398 column -= 2 + key.length() - initialValues[key].length();
00399
00400
00401 if ( c == '$' || key == initialValues[key] ) {
00402 if ( !keyQueue.contains(key) ) {
00403 keyQueue.append(key);
00404 }
00405 ranges.insert( key,
00406 Range( line, column - initialValues[key].length(),
00407 line, column
00408 )
00409 );
00410 }
00411 }
00412 }
00413 startPos = -1;
00414 } else {
00415 ++column;
00416 }
00417 }
00418
00419 m_doc->editStart();
00420 if ( m_doc->replaceText(*m_wholeTemplateRange, templateString) ) {
00421 m_doc->undoSafePoint();
00422 }
00423 m_doc->editEnd();
00424
00425 Q_ASSERT(!m_wholeTemplateRange->isEmpty());
00426
00427 if ( m_doc->activeKateView() ) {
00432 m_doc->activeKateView()->updateView(true);
00433 }
00434
00435 if ( finalCursorPosition.isValid() ) {
00436 m_finalCursorPosition = m_doc->newSmartCursor(finalCursorPosition);
00437 } else {
00438 m_finalCursorPosition = m_doc->newSmartCursor(Cursor(line, column));
00439 }
00440
00441 if ( ranges.isEmpty() ) {
00442 return;
00443 }
00444
00445 KateRendererConfig *config = m_doc->activeKateView()->renderer()->config();
00446
00447 Attribute::Ptr editableAttribute = getAttribute(config->templateEditablePlaceholderColor());
00448 editableAttribute->setDynamicAttribute(
00449 Attribute::ActivateCaretIn, getAttribute(config->templateFocusedEditablePlaceholderColor(), 255)
00450 );
00451
00452 Attribute::Ptr mirroredAttribute = getAttribute(config->templateNotEditablePlaceholderColor());
00453
00454 m_wholeTemplateRange->setAttribute(getAttribute(config->templateBackgroundColor(), 200));
00455 m_doc->addHighlightToDocument(m_wholeTemplateRange, true);
00456
00457
00458
00459 foreach ( const QString& key, keyQueue ) {
00460 const QList<Range> &values = ranges.values(key);
00461
00462
00463 SmartRange* parent = m_doc->newSmartRange(values.first(), 0, SmartRange::ExpandLeft | SmartRange::ExpandRight);
00464 if ( values.size() > 1 ) {
00465
00466 for (int i = 0; i < values.size(); ++i ) {
00467 SmartRange* range = m_doc->newSmartRange(
00468 values[i], parent, SmartRange::ExpandLeft | SmartRange::ExpandRight
00469 );
00470
00471 if ( i == values.size() - 1 ) {
00472 range->setAttribute(editableAttribute);
00473 m_uneditedRanges.append(range);
00474 } else {
00475 range->setAttribute(mirroredAttribute);
00476 }
00477 }
00478 } else {
00479
00480 parent->setAttribute(editableAttribute);
00481 m_uneditedRanges.append(parent);
00482 }
00483
00484 m_doc->addHighlightToDocument(parent, true);
00485 m_templateRanges.append(parent);
00486 }
00487
00488 setCurrentRange(m_templateRanges.first());
00489 }
00490
00491 void KateTemplateHandler::slotTextChanged(Document* document, const Range& range)
00492 {
00493 Q_ASSERT(document == m_doc);
00494
00495 ifDebug(kDebug() << "text changed" << document << range;)
00496
00497 if ( m_isMirroring ) {
00498 ifDebug(kDebug() << "mirroring - ignoring edit";)
00499 return;
00500 }
00501 if ( (!m_editWithUndo && m_doc->isEditRunning()) || range.isEmpty() ) {
00502 ifDebug(kDebug(13020) << "slotTextChanged returning prematurely";)
00503 return;
00504 }
00505 if ( m_wholeTemplateRange->isEmpty() ) {
00506 ifDebug(kDebug() << "template range got deleted, exiting";)
00507 cleanupAndExit();
00508 return;
00509 }
00510 if ( range.end() == *m_finalCursorPosition ) {
00511 ifDebug(kDebug() << "editing at final cursor position, exiting.";)
00512 cleanupAndExit();
00513 return;
00514 }
00515 if ( !m_wholeTemplateRange->contains(range.start()) ) {
00516
00517 ifDebug(kDebug() << range << "not contained in" << *m_wholeTemplateRange;)
00518 return;
00519 }
00520
00521 ifDebug(kDebug() << "see if we have to mirror the edit";)
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531 SmartRange* baseRange = 0;
00532
00533 SmartRange* leftAdjacentRange = 0;
00534
00535 foreach ( SmartRange* parent, m_templateRanges ) {
00536 if ( customContains(parent, range.start()) )
00537 {
00538 if ( parent->childRanges().isEmpty() ) {
00539
00540 m_uneditedRanges.removeOne(parent);
00541 } else {
00542
00543 if ( baseRange ) {
00544
00545 ifDebug(kDebug() << "looking for adjacent mirror to" << *baseRange;)
00546 foreach ( SmartRange* child, parent->childRanges() ) {
00547 if ( child->start() == range.start() && child->end() >= range.end() ) {
00548 ifDebug(kDebug() << "found adjacent mirror - using as base" << *child;)
00549 leftAdjacentRange = baseRange;
00550 baseRange = child;
00551 break;
00552 }
00553 }
00554
00555 break;
00556 } else {
00557
00558 foreach ( SmartRange* child, parent->childRanges() ) {
00559 if ( customContains(child, range.start()) ) {
00560 baseRange = child;
00561 break;
00562 }
00563 }
00564 if ( baseRange && baseRange->end() != range.end() ) {
00565
00566 break;
00567 } else if ( baseRange && (baseRange->isEmpty() || range == *baseRange) ) {
00568
00569
00570 break;
00571 }
00572 }
00573 }
00574 } else if ( baseRange ) {
00575
00576 break;
00577 }
00578 }
00579 if ( baseRange ) {
00580 m_uneditedRanges.removeOne(baseRange);
00581 if ( leftAdjacentRange ) {
00582
00583 ifDebug(kDebug() << "removing edited range" << range << "from left adjacent range" << *leftAdjacentRange;)
00584 leftAdjacentRange->end() = range.start();
00585 ifDebug(kDebug() << "new range:" << *leftAdjacentRange;)
00586 }
00587 syncMirroredRanges(baseRange);
00588 if ( range.start() == baseRange->start() && m_doc->activeKateView() ) {
00589
00590
00591
00592 }
00593 } else {
00594 ifDebug(kDebug() << "no range found to mirror this edit";)
00595 }
00596 }
00597
00598 void KateTemplateHandler::syncMirroredRanges(SmartRange* range)
00599 {
00600 Q_ASSERT(m_templateRanges.contains(range->parentRange()));
00601
00602 m_isMirroring = true;
00603 m_doc->editStart();
00604
00605 const QString &newText = m_doc->text(*range);
00606 ifDebug(kDebug() << "mirroring" << newText << "from" << *range;)
00607
00608 foreach ( SmartRange* sibling, range->parentRange()->childRanges() ) {
00609 if ( sibling != range ) {
00610 m_doc->replaceText(*sibling, newText);
00611 }
00612 }
00613
00616 bool undoDontMerge = m_doc->undoDontMerge();
00617 m_doc->setUndoDontMerge(false);
00618 m_doc->setUndoAllowComplexMerge(true);
00619 m_doc->undoSafePoint();
00620 m_doc->editEnd();
00621 m_doc->setUndoDontMerge(undoDontMerge);
00622 m_isMirroring = false;
00623 }
00624
00625 #include "katetemplatehandler.moc"
00626
00627