• Skip to content
  • Skip to link menu
KDE 4.5 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

katetemplatehandler.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries and the Kate part.
00002  *
00003  *  Copyright (C) 2004 Joseph Wenninger <jowenn@kde.org>
00004  *  Copyright (C) 2009 Milian Wolff <mail@milianw.de>
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Library General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Library General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU Library General Public License
00017  *  along with this library; see the file COPYING.LIB.  If not, write to
00018  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  *  Boston, MA 02110-1301, USA.
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       // indent the inserted template properly, this makes it possible
00073       // to share snippets e.g. via GHNS without caring about
00074       // what indent-style to use.
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     // only do complex stuff when required
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       // when no interesting ranges got added, we can terminate directly
00112       jumpToFinalCursorPosition();
00113       cleanupAndExit();
00114     }
00115   } else {
00116     // simple templates just need to be (which gets done in handleTemplateString())
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   // prevent indenting by eating the keypress event for TAB
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   // actually offer shortcuts for navigation
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       // terminate
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         // terminate
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     // wrap and jump to last range
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     // wrap and jump to final cursor
00262     jumpToFinalCursorPosition();
00263   }
00264 }
00265 
00266 void KateTemplateHandler::jumpToNextRange()
00267 {
00268   const Cursor & cursor = m_doc->activeView()->cursorPosition();
00269   if ( cursor == *m_finalCursorPosition ) {
00270     // wrap and jump to first range
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     // wrap and jump to final cursor
00287     jumpToFinalCursorPosition();
00288   }
00289 }
00290 
00291 void KateTemplateHandler::setCurrentRange(SmartRange* range)
00292 {
00293   if ( !range->childRanges().isEmpty() ) {
00294     // jump to first mirrored range
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   // not equal -1 when we found a start position
00329   int startPos = -1;
00330 
00331   // each found variable gets it's range(s) added to the list.
00332   // the key is the varname, e.g. the same as in initialValues
00333   // to be able to iterate over them in a FIFO matter, also store
00334   // the keys in a queue.
00335   QQueue<QString> keyQueue;
00336   QMultiMap<QString, Range> ranges;
00337 
00338   // valid, if we find an occurrence of ${cursor}
00339   Cursor finalCursorPosition = Cursor::invalid();
00340 
00341   // parse string for ${VAR} or %{VAR}
00342   // VAR must not contain $ or %
00343   // VAR must not contain newlines
00344   // VAR must be set as key in initialValues
00345   // expression must not be escaped
00346   for ( int i = 0; i < templateString.size(); ++i ) {
00347     if ( templateString[i] == '\n' ) {
00348       ++line;
00349       column = 0;
00350       if ( startPos != -1 ) {
00351         // don't allow variables to span multiple lines
00352         startPos = -1;
00353       }
00354     } else if ( (templateString[i] == '%' || templateString[i] == '$')
00355                 && i + 1 < templateString.size() && templateString[i+1] == '{' ) {
00356       // don't check for startPos == -1 here, overwrite blindly since nested variables are not supported
00357       startPos = i;
00358       // skip '{'
00359       ++i;
00360       column += 2;
00361     } else if ( templateString[i] == '}' && startPos != -1 ) {
00362       // check whether this var is escaped
00363       int escapeChars = 0;
00364       while ( startPos - escapeChars > 0 && templateString[startPos - escapeChars - 1] == '\\' ) {
00365         ++escapeChars;
00366       }
00367       if ( escapeChars > 0 ) {
00368         // remove half of the escape chars (i.e. \\ => \) and make sure the
00369         // odd rest is removed as well (i.e. the one that escapes this var)
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         // get key, i.e. contents between ${..}
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           // don't insert anything, just remove the placeholder
00384           templateString.remove(startPos, i - startPos + 1);
00385           // correct iterator pos, 3 == $ + { + }
00386           i -= 3 + key.length();
00387           column -= 2 + key.length();
00388           startPos = -1;
00389         } else {
00390           // whether the variable starts with % or $
00391           QChar c = templateString[startPos];
00392           // replace variable with initial value
00393           templateString.replace( startPos, i - startPos + 1, initialValues[key] );
00394           // correct iterator pos, 3 == % + { + }
00395           i -= 3 + key.length() - initialValues[key].length();
00396           // correct column to point at end of range, taking replacement width diff into account
00397           // 2 == % + {
00398           column -= 2 + key.length() - initialValues[key].length();
00399           // always add ${...} to the editable ranges
00400           // only add %{...} to the editable ranges when it's value equals the key
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   // create smart ranges for each found variable
00458   // if the variable exists more than once, create "mirrored" ranges
00459   foreach ( const QString& key, keyQueue ) {
00460     const QList<Range> &values = ranges.values(key);
00461     // used as parent for mirrored variables,
00462     // and as only item for not-mirrored variables
00463     SmartRange* parent = m_doc->newSmartRange(values.first(), 0, SmartRange::ExpandLeft | SmartRange::ExpandRight);
00464     if ( values.size() > 1 ) {
00465       // add all "real" ranges as children
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         // the last item will be our real first range (see multimap docs)
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       // just a single range
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     // outside our template or one of our own changes
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   // Check if @p range is mirrored.
00524   // If we have two adjacent mirrored ranges, think ${first}${second},
00525   // the second will be mirrored, as that's what the user will see "activated",
00526   // since Kate uses contains() that does a comparison "< end()".
00527   // We use "<= end()" though so we can handle contents that were added at the end of a range.
00528   // TODO: make it possible to select either, the left or right rannge (LOWPRIO)
00529 
00530   // The found child range to act as base for mirroring.
00531   SmartRange* baseRange = 0;
00532   // The left-adjacent range that gets some special treatment (if it exists).
00533   SmartRange* leftAdjacentRange = 0;
00534 
00535   foreach ( SmartRange* parent, m_templateRanges ) {
00536     if ( customContains(parent, range.start()) )
00537     {
00538       if ( parent->childRanges().isEmpty() ) {
00539         // simple, not-mirrored range got changed
00540         m_uneditedRanges.removeOne(parent);
00541       } else {
00542         // handle mirrored ranges
00543         if ( baseRange ) {
00544           // look for adjacent range (we find the right-handed one here)
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           // adjacent mirror handled, we can finish
00555           break;
00556         } else {
00557           // find mirrored range that got edited
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             // finish, don't look for adjacent mirror as we are not at the end of this range
00566             break;
00567           } else if ( baseRange && (baseRange->isEmpty() || range == *baseRange) ) {
00568             // always add to empty ranges first. else ${foo}${bar} will make foo unusable when
00569             // it gets selected and an edit takes place.
00570             break;
00571           } // else don't finish, look for baseRange or adjacent mirror first
00572         }
00573       }
00574     } else if ( baseRange ) {
00575       // no adjacent mirrored range found, we can finish
00576       break;
00577     }
00578   }
00579   if ( baseRange ) {
00580     m_uneditedRanges.removeOne(baseRange);
00581     if ( leftAdjacentRange ) {
00582       // revert that something got added to the adjacent range
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       // TODO: update the attribute, since kate doesn't do that automatically
00590       // TODO: fix in kate itself
00591       // TODO: attribute->changed == undefined reference...
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 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  •     Sodep
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.9-20090814
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal