Kate
katedocument.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org> 00003 Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> 00004 Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de> 00005 Copyright (C) 2006 Hamish Rodda <rodda@kde.org> 00006 Copyright (C) 2007 Mirko Stocker <me@misto.ch> 00007 Copyright (C) 2009-2010 Michel Ludwig <michel.ludwig@kdemail.net> 00008 00009 This library is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU Library General Public 00011 License version 2 as published by the Free Software Foundation. 00012 00013 This library is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 Library General Public License for more details. 00017 00018 You should have received a copy of the GNU Library General Public License 00019 along with this library; see the file COPYING.LIB. If not, write to 00020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 Boston, MA 02111-13020, USA. 00022 */ 00023 00024 //BEGIN includes 00025 #include "katedocument.h" 00026 #include "katedocument.moc" 00027 #include "kateglobal.h" 00028 #include "katedialogs.h" 00029 #include "katehighlight.h" 00030 #include "kateview.h" 00031 #include "kateautoindent.h" 00032 #include "katetextline.h" 00033 #include "katedocumenthelpers.h" 00034 #include "kateprinter.h" 00035 #include "katerenderer.h" 00036 #include "kateregexp.h" 00037 #include "kateplaintextsearch.h" 00038 #include "kateregexpsearch.h" 00039 #include "kateconfig.h" 00040 #include "katemodemanager.h" 00041 #include "kateschema.h" 00042 #include "katetemplatehandler.h" 00043 #include "katebuffer.h" 00044 #include "kateundomanager.h" 00045 #include "katepartpluginmanager.h" 00046 #include "katevireplacemode.h" 00047 #include "spellcheck/prefixstore.h" 00048 #include "spellcheck/ontheflycheck.h" 00049 #include "spellcheck/spellcheck.h" 00050 #include "katescriptmanager.h" 00051 #include "kateswapfile.h" 00052 00053 #include <ktexteditor/attribute.h> 00054 #include <ktexteditor/plugin.h> 00055 #include <ktexteditor/loadsavefiltercheckplugin.h> 00056 00057 #include <kio/job.h> 00058 #include <kio/jobuidelegate.h> 00059 #include <kio/netaccess.h> 00060 #include <kfileitem.h> 00061 00062 #include <klocale.h> 00063 #include <kglobal.h> 00064 #include <kmenu.h> 00065 #include <kconfig.h> 00066 #include <kfiledialog.h> 00067 #include <kmessagebox.h> 00068 #include <kstandardaction.h> 00069 #include <kxmlguifactory.h> 00070 #include <kdebug.h> 00071 #include <kglobalsettings.h> 00072 #include <kdirwatch.h> 00073 #include <kencodingfiledialog.h> 00074 #include <kcodecs.h> 00075 #include <kstandarddirs.h> 00076 #include <kstringhandler.h> 00077 00078 #include <kservicetypetrader.h> 00079 00080 #include <QtDBus/QtDBus> 00081 #include <QtGui/QApplication> 00082 #include <QtCore/QTimer> 00083 #include <QtCore/QFile> 00084 #include <QtGui/QClipboard> 00085 #include <QtCore/QTextStream> 00086 #include <QtCore/QTextCodec> 00087 #include <QtCore/QMap> 00088 //END includes 00089 00090 00091 static int dummy = 0; 00092 00093 class KateTemplateScript; 00094 00095 class KateDocument::LoadSaveFilterCheckPlugins 00096 { 00097 public: 00098 LoadSaveFilterCheckPlugins() { 00099 KService::List traderList = KServiceTypeTrader::self()->query("KTextEditor/LoadSaveFilterCheckPlugin"); 00100 00101 foreach(const KService::Ptr &ptr, traderList) 00102 { 00103 QString libname; 00104 libname=ptr->library(); 00105 libname=libname.right(libname.length()-12); //ktexteditor_ == 12 00106 m_plugins[libname]=0;//new KatePythonEncodingCheck(); 00107 m_plugins2Service[libname] = ptr; 00108 } 00109 00110 } 00111 ~LoadSaveFilterCheckPlugins() { 00112 if ( m_plugins.count()==0) return; 00113 QHashIterator<QString,KTextEditor::LoadSaveFilterCheckPlugin*>i(m_plugins); 00114 while (i.hasNext()) 00115 i.next(); 00116 delete i.value(); 00117 m_plugins.clear(); 00118 } 00119 bool preSavePostDialogFilterCheck(const QString& pluginName,KateDocument *document,QWidget *parentWidget) { 00120 KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName); 00121 if (!plug) { 00122 if (KMessageBox::warningContinueCancel (parentWidget 00123 , i18n ("The filter/check plugin '%1' could not be found, still continue saving of %2", pluginName,document->url().pathOrUrl()) 00124 , i18n ("Saving problems") 00125 , KGuiItem(i18n("Save Nevertheless")) 00126 , KStandardGuiItem::cancel()) != KMessageBox::Continue) 00127 return false; 00128 else 00129 return true; 00130 } 00131 return plug->preSavePostDialogFilterCheck(document); 00132 } 00133 void postLoadFilter(const QString& pluginName,KateDocument *document) { 00134 KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName); 00135 if (!plug) return; 00136 plug->postLoadFilter(document); 00137 } 00138 bool postSaveFilterCheck(const QString& pluginName,KateDocument *document,bool saveas) { 00139 KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName); 00140 if (!plug) return false; 00141 return plug->postSaveFilterCheck(document,saveas); 00142 } 00143 private: 00144 KTextEditor::LoadSaveFilterCheckPlugin *getPlugin(const QString & pluginName) 00145 { 00146 if (!m_plugins.contains(pluginName)) return 0; 00147 if (!m_plugins.value(pluginName, 0)) { 00148 m_plugins[pluginName]=m_plugins2Service.value(pluginName)->createInstance<KTextEditor::LoadSaveFilterCheckPlugin>(); 00149 } 00150 return m_plugins.value(pluginName); 00151 } 00152 QHash <QString,KTextEditor::LoadSaveFilterCheckPlugin*> m_plugins; 00153 QHash <QString, KService::Ptr> m_plugins2Service; 00154 }; 00155 00156 //BEGIN d'tor, c'tor 00157 // 00158 // KateDocument Constructor 00159 // 00160 KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView, 00161 bool bReadOnly, QWidget *parentWidget, 00162 QObject *parent) 00163 : KTextEditor::Document (parent), 00164 m_bSingleViewMode(bSingleViewMode), 00165 m_bBrowserView(bBrowserView), 00166 m_bReadOnly(bReadOnly), 00167 m_activeView(0), 00168 editSessionNumber(0), 00169 editIsRunning(false), 00170 m_undoManager(new KateUndoManager(this)), 00171 m_editableMarks(markType01), 00172 m_annotationModel(0), 00173 m_saveAs(false), 00174 m_isasking(0), 00175 m_blockRemoveTrailingSpaces(false), 00176 m_buffer(new KateBuffer(this)), 00177 m_indenter(new KateAutoIndent(this)), 00178 hlSetByUser(false), 00179 m_bomSetByUser(false), 00180 m_modOnHd(false), 00181 m_modOnHdReason(OnDiskUnmodified), 00182 m_docName("need init"), 00183 m_docNameNumber(0), 00184 m_fileTypeSetByUser(false), 00185 m_reloading(false), 00186 m_config(new KateDocumentConfig(this)), 00187 m_fileChangedDialogsActivated(false), 00188 m_savingToUrl(false), 00189 m_onTheFlyChecker(0) 00190 { 00191 setComponentData ( KateGlobal::self()->componentData () ); 00192 00193 QString pathName ("/Kate/Document/%1"); 00194 pathName = pathName.arg (++dummy); 00195 00196 // my dbus object 00197 QDBusConnection::sessionBus().registerObject (pathName, this); 00198 00199 // register doc at factory 00200 KateGlobal::self()->registerDocument(this); 00201 00202 // normal hl 00203 m_buffer->setHighlight (0); 00204 00205 // swap file 00206 m_swapfile = new Kate::SwapFile(this); 00207 00208 new KateBrowserExtension( this ); // deleted by QObject memory management 00209 00210 // important, fill in the config into the indenter we use... 00211 m_indenter->updateConfig (); 00212 00213 // some nice signals from the buffer 00214 connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int))); 00215 connect(m_buffer, SIGNAL(respellCheckBlock(int, int)), this , SLOT(respellCheckBlock(int, int))); 00216 connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated())); 00217 00218 // if the user changes the highlight with the dialog, notify the doc 00219 connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged())); 00220 00221 // signals for mod on hd 00222 connect( KateGlobal::self()->dirWatch(), SIGNAL(dirty (const QString &)), 00223 this, SLOT(slotModOnHdDirty (const QString &)) ); 00224 00225 connect( KateGlobal::self()->dirWatch(), SIGNAL(created (const QString &)), 00226 this, SLOT(slotModOnHdCreated (const QString &)) ); 00227 00228 connect( KateGlobal::self()->dirWatch(), SIGNAL(deleted (const QString &)), 00229 this, SLOT(slotModOnHdDeleted (const QString &)) ); 00230 00231 connect (this,SIGNAL(completed()),this,SLOT(slotCompleted())); 00232 connect (this,SIGNAL(canceled(const QString&)),this,SLOT(slotCanceled())); 00233 // update doc name 00234 setDocName (QString()); 00235 00236 // if single view mode, like in the konqui embedding, create a default view ;) 00237 // be lazy, only create it now, if any parentWidget is given, otherwise widget() 00238 // will create it on demand... 00239 if ( m_bSingleViewMode && parentWidget ) 00240 { 00241 KTextEditor::View *view = (KTextEditor::View*)createView( parentWidget ); 00242 insertChildClient( view ); 00243 view->show(); 00244 setWidget( view ); 00245 } 00246 00247 connect(m_undoManager, SIGNAL(undoChanged()), this, SIGNAL(undoChanged())); 00248 connect(m_undoManager, SIGNAL(undoStart(KTextEditor::Document*)), this, SIGNAL(exclusiveEditStart(KTextEditor::Document*))); 00249 connect(m_undoManager, SIGNAL(undoEnd(KTextEditor::Document*)), this, SIGNAL(exclusiveEditEnd(KTextEditor::Document*))); 00250 connect(m_undoManager, SIGNAL(redoStart(KTextEditor::Document*)), this, SIGNAL(exclusiveEditStart(KTextEditor::Document*))); 00251 connect(m_undoManager, SIGNAL(redoEnd(KTextEditor::Document*)), this, SIGNAL(exclusiveEditEnd(KTextEditor::Document*))); 00252 00253 connect(this,SIGNAL(sigQueryClose(bool *, bool*)),this,SLOT(slotQueryClose_save(bool *, bool*))); 00254 00255 onTheFlySpellCheckingEnabled(config()->onTheFlySpellCheck()); 00256 00257 // register document in plugins 00258 KatePartPluginManager::self()->addDocument(this); 00259 } 00260 00261 // 00262 // KateDocument Destructor 00263 // 00264 KateDocument::~KateDocument() 00265 { 00269 emit aboutToDeleteMovingInterfaceContent (this); 00270 00271 // kill it early, it has ranges! 00272 delete m_onTheFlyChecker; 00273 m_onTheFlyChecker = NULL; 00274 00275 clearDictionaryRanges(); 00276 00277 // Tell the world that we're about to close (== destruct) 00278 // Apps must receive this in a direct signal-slot connection, and prevent 00279 // any further use of interfaces once they return. 00280 emit aboutToClose(this); 00281 00282 // remove file from dirwatch 00283 deactivateDirWatch (); 00284 00285 // thanks for offering, KPart, but we're already self-destructing 00286 setAutoDeleteWidget(false); 00287 setAutoDeletePart(false); 00288 00289 // clean up remaining views 00290 while (!m_views.isEmpty()) { 00291 delete m_views.takeFirst(); 00292 } 00293 00294 // de-register from plugin 00295 KatePartPluginManager::self()->removeDocument(this); 00296 00297 // cu marks 00298 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) 00299 delete i.value(); 00300 m_marks.clear(); 00301 00302 delete m_config; 00303 KateGlobal::self()->deregisterDocument (this); 00304 } 00305 //END 00306 00307 // on-demand view creation 00308 QWidget *KateDocument::widget() 00309 { 00310 // no singleViewMode -> no widget()... 00311 if (!singleViewMode()) 00312 return 0; 00313 00314 // does a widget exist already? use it! 00315 if (KTextEditor::Document::widget()) 00316 return KTextEditor::Document::widget(); 00317 00318 // create and return one... 00319 KTextEditor::View *view = (KTextEditor::View*)createView(0); 00320 insertChildClient( view ); 00321 setWidget( view ); 00322 return view; 00323 } 00324 00325 //BEGIN KTextEditor::Document stuff 00326 00327 KTextEditor::View *KateDocument::createView( QWidget *parent ) 00328 { 00329 KateView* newView = new KateView( this, parent); 00330 if ( m_fileChangedDialogsActivated ) 00331 connect( newView, SIGNAL(focusIn( KTextEditor::View * )), this, SLOT(slotModifiedOnDisk()) ); 00332 00333 emit viewCreated (this, newView); 00334 00335 return newView; 00336 } 00337 00338 const QList<KTextEditor::View*> &KateDocument::views () const 00339 { 00340 return m_textEditViews; 00341 } 00342 00343 KTextEditor::Editor *KateDocument::editor () 00344 { 00345 return KateGlobal::self(); 00346 } 00347 00348 KTextEditor::Range KateDocument::rangeOnLine(KTextEditor::Range range, int line) const 00349 { 00350 int col1 = const_cast<KateDocument*>(this)->toVirtualColumn(range.start()); 00351 int col2 = const_cast<KateDocument*>(this)->toVirtualColumn(range.end()); 00352 00353 Kate::TextLine tl = const_cast<KateDocument*>(this)->kateTextLine(line); 00354 col1 = tl->fromVirtualColumn(col1, config()->tabWidth()); 00355 col2 = tl->fromVirtualColumn(col2, config()->tabWidth()); 00356 00357 return KTextEditor::Range(line, col1, line, col2); 00358 } 00359 00360 //BEGIN KTextEditor::EditInterface stuff 00361 00362 QString KateDocument::text() const 00363 { 00364 return m_buffer->text (); 00365 } 00366 00367 QString KateDocument::text( const KTextEditor::Range& range, bool blockwise ) const 00368 { 00369 if (!range.isValid()) { 00370 kWarning() << k_funcinfo << "Text requested for invalid range" << range; 00371 return QString(); 00372 } 00373 00374 QString s; 00375 00376 if (range.start().line() == range.end().line()) 00377 { 00378 if (range.start().column() > range.end().column()) 00379 return QString (); 00380 00381 Kate::TextLine textLine = m_buffer->plainLine(range.start().line()); 00382 00383 if ( !textLine ) 00384 return QString (); 00385 00386 return textLine->string(range.start().column(), range.end().column()-range.start().column()); 00387 } 00388 else 00389 { 00390 00391 for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i) 00392 { 00393 Kate::TextLine textLine = m_buffer->plainLine(i); 00394 00395 if ( !blockwise ) 00396 { 00397 if (i == range.start().line()) 00398 s.append (textLine->string(range.start().column(), textLine->length()-range.start().column())); 00399 else if (i == range.end().line()) 00400 s.append (textLine->string(0, range.end().column())); 00401 else 00402 s.append (textLine->string()); 00403 } 00404 else 00405 { 00406 KTextEditor::Range subRange = rangeOnLine(range, i); 00407 s.append(textLine->string(subRange.start().column(), subRange.columnWidth())); 00408 } 00409 00410 if ( i < range.end().line() ) 00411 s.append(QChar::fromAscii('\n')); 00412 } 00413 } 00414 00415 return s; 00416 } 00417 00418 QChar KateDocument::character( const KTextEditor::Cursor & position ) const 00419 { 00420 Kate::TextLine textLine = m_buffer->plainLine(position.line()); 00421 00422 if ( !textLine ) 00423 return QChar(); 00424 00425 if (position.column() >= 0 && position.column() < textLine->string().length()) 00426 return textLine->string().at(position.column()); 00427 00428 return QChar(); 00429 } 00430 00431 QStringList KateDocument::textLines( const KTextEditor::Range & range, bool blockwise ) const 00432 { 00433 QStringList ret; 00434 00435 if (!range.isValid()) { 00436 kWarning() << k_funcinfo << "Text requested for invalid range" << range; 00437 return ret; 00438 } 00439 00440 if ( blockwise && (range.start().column() > range.end().column()) ) 00441 return ret; 00442 00443 if (range.start().line() == range.end().line()) 00444 { 00445 Q_ASSERT(range.start() <= range.end()); 00446 00447 Kate::TextLine textLine = m_buffer->plainLine(range.start().line()); 00448 00449 if ( !textLine ) 00450 return ret; 00451 00452 ret << textLine->string(range.start().column(), range.end().column() - range.start().column()); 00453 } 00454 else 00455 { 00456 for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i) 00457 { 00458 Kate::TextLine textLine = m_buffer->plainLine(i); 00459 00460 if ( !blockwise ) 00461 { 00462 if (i == range.start().line()) 00463 ret << textLine->string(range.start().column(), textLine->length() - range.start().column()); 00464 else if (i == range.end().line()) 00465 ret << textLine->string(0, range.end().column()); 00466 else 00467 ret << textLine->string(); 00468 } 00469 else 00470 { 00471 KTextEditor::Range subRange = rangeOnLine(range, i); 00472 ret << textLine->string(subRange.start().column(), subRange.columnWidth()); 00473 } 00474 } 00475 } 00476 00477 return ret; 00478 } 00479 00480 QString KateDocument::line( int line ) const 00481 { 00482 Kate::TextLine l = m_buffer->plainLine(line); 00483 00484 if (!l) 00485 return QString(); 00486 00487 return l->string(); 00488 } 00489 00490 bool KateDocument::setText(const QString &s) 00491 { 00492 if (!isReadWrite()) 00493 return false; 00494 00495 QList<KTextEditor::Mark> msave; 00496 00497 foreach (KTextEditor::Mark* mark, m_marks) 00498 msave.append(*mark); 00499 00500 editStart (); 00501 00502 // delete the text 00503 clear(); 00504 00505 // insert the new text 00506 insertText (KTextEditor::Cursor(), s); 00507 00508 editEnd (); 00509 00510 foreach (const KTextEditor::Mark& mark, msave) 00511 setMark (mark.line, mark.type); 00512 00513 return true; 00514 } 00515 00516 bool KateDocument::setText( const QStringList & text ) 00517 { 00518 if (!isReadWrite()) 00519 return false; 00520 00521 QList<KTextEditor::Mark> msave; 00522 00523 foreach (KTextEditor::Mark* mark, m_marks) 00524 msave.append(*mark); 00525 00526 editStart (); 00527 00528 // delete the text 00529 clear(); 00530 00531 // insert the new text 00532 insertText (KTextEditor::Cursor::start(), text); 00533 00534 editEnd (); 00535 00536 foreach (const KTextEditor::Mark& mark, msave) 00537 setMark (mark.line, mark.type); 00538 00539 return true; 00540 } 00541 00542 bool KateDocument::clear() 00543 { 00544 if (!isReadWrite()) 00545 return false; 00546 00547 foreach (KateView *view, m_views) { 00548 view->clear(); 00549 view->tagAll(); 00550 view->update(); 00551 } 00552 00553 clearMarks (); 00554 00555 emit aboutToInvalidateMovingInterfaceContent(this); 00556 m_buffer->invalidateRanges(); 00557 00558 emit aboutToRemoveText(documentRange()); 00559 00560 return editRemoveLines(0, lastLine()); 00561 } 00562 00563 bool KateDocument::insertText( const KTextEditor::Cursor& position, const QString& text, bool block ) 00564 { 00565 if (!isReadWrite()) 00566 return false; 00567 00568 if (text.isEmpty()) 00569 return true; 00570 00571 editStart(); 00572 00573 int currentLine = position.line(); 00574 int currentLineStart = 0; 00575 int totalLength = text.length(); 00576 int insertColumn = position.column(); 00577 00578 if (position.line() > lines()) 00579 { 00580 int line = lines(); 00581 while (line != position.line() + totalLength + 1) 00582 { 00583 editInsertLine(line,QString()); 00584 line++; 00585 } 00586 } 00587 00588 bool replacetabs = ( config()->replaceTabsDyn() ); 00589 int tabWidth = config()->tabWidth(); 00590 00591 static const QChar newLineChar('\n'); 00592 static const QChar tabChar('\t'); 00593 static const QChar spaceChar(' '); 00594 00595 int insertColumnExpanded = insertColumn; 00596 Kate::TextLine l = kateTextLine( currentLine ); 00597 if (l) 00598 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth ); 00599 00600 int pos = 0; 00601 for (; pos < totalLength; pos++) 00602 { 00603 const QChar& ch = text.at(pos); 00604 00605 if (ch == newLineChar) 00606 { 00607 // Only perform the text insert if there is text to insert 00608 if (currentLineStart < pos) 00609 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart)); 00610 00611 if ( !block ) 00612 { 00613 editWrapLine(currentLine, insertColumn + pos - currentLineStart); 00614 insertColumn = 0; 00615 } 00616 else 00617 { 00618 if ( currentLine == lastLine() ) 00619 editWrapLine(currentLine , insertColumn + pos - currentLineStart); 00620 insertColumn = position.column(); // tab expansion might change this 00621 } 00622 00623 currentLine++; 00624 currentLineStart = pos + 1; 00625 l = kateTextLine( currentLine ); 00626 if (l) 00627 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth ); 00628 } 00629 else 00630 { 00631 if ( replacetabs && ch == tabChar ) 00632 { 00633 int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth ); 00634 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar)); 00635 00636 insertColumn += pos - currentLineStart + spacesRequired; 00637 currentLineStart = pos + 1; 00638 l = kateTextLine( currentLine ); 00639 if (l) 00640 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth ); 00641 } 00642 } 00643 } 00644 00645 // Only perform the text insert if there is text to insert 00646 if (currentLineStart < pos) 00647 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart)); 00648 00649 editEnd(); 00650 return true; 00651 } 00652 00653 bool KateDocument::insertText( const KTextEditor::Cursor & position, const QStringList & textLines, bool block ) 00654 { 00655 if (!isReadWrite()) 00656 return false; 00657 00658 // just reuse normal function 00659 return insertText (position, textLines.join ("\n"), block); 00660 } 00661 00662 bool KateDocument::removeText ( const KTextEditor::Range &_range, bool block ) 00663 { 00664 KTextEditor::Range range = _range; 00665 00666 if (!isReadWrite()) 00667 return false; 00668 00669 // Should now be impossible to trigger with the new Range class 00670 Q_ASSERT( range.start().line() <= range.end().line() ); 00671 00672 if ( range.start().line() > lastLine() ) 00673 return false; 00674 00675 if (!block) 00676 emit aboutToRemoveText(range); 00677 00678 editStart(); 00679 00680 if ( !block ) 00681 { 00682 if ( range.end().line() > lastLine() ) 00683 { 00684 range.end().setPosition(lastLine()+1, 0); 00685 } 00686 00687 if (range.onSingleLine()) 00688 { 00689 editRemoveText(range.start().line(), range.start().column(), range.columnWidth()); 00690 } 00691 else 00692 { 00693 int from = range.start().line(); 00694 int to = range.end().line(); 00695 00696 // remove last line 00697 if (to <= lastLine()) 00698 editRemoveText(to, 0, range.end().column()); 00699 00700 // editRemoveLines() will be called on first line (to remove bookmark) 00701 if (range.start().column() == 0 && from > 0) 00702 --from; 00703 00704 // remove middle lines 00705 editRemoveLines(from+1, to-1); 00706 00707 // remove first line if not already removed by editRemoveLines() 00708 if (range.start().column() > 0 || range.start().line() == 0) { 00709 editRemoveText(from, range.start().column(), m_buffer->plainLine(from)->length() - range.start().column()); 00710 editUnWrapLine(from); 00711 } 00712 } 00713 } // if ( ! block ) 00714 else 00715 { 00716 int startLine = qMax(0, range.start().line()); 00717 int vc1 = toVirtualColumn(range.start()); 00718 int vc2 = toVirtualColumn(range.end()); 00719 for (int line = qMin(range.end().line(), lastLine()); line >= startLine; --line) { 00720 Kate::TextLine tl = const_cast<KateDocument*>(this)->kateTextLine(line); 00721 int col1 = tl->fromVirtualColumn(vc1, config()->tabWidth()); 00722 int col2 = tl->fromVirtualColumn(vc2, config()->tabWidth()); 00723 editRemoveText(line, qMin(col1, col2), qAbs(col2 - col1)); 00724 } 00725 } 00726 00727 editEnd (); 00728 return true; 00729 } 00730 00731 bool KateDocument::insertLine( int l, const QString &str ) 00732 { 00733 if (!isReadWrite()) 00734 return false; 00735 00736 if (l < 0 || l > lines()) 00737 return false; 00738 00739 return editInsertLine (l, str); 00740 } 00741 00742 bool KateDocument::insertLines( int line, const QStringList & text ) 00743 { 00744 if (!isReadWrite()) 00745 return false; 00746 00747 if (line < 0 || line > lines()) 00748 return false; 00749 00750 bool success = true; 00751 foreach (const QString &string, text) 00752 success &= editInsertLine (line++, string); 00753 00754 return success; 00755 } 00756 00757 bool KateDocument::removeLine( int line ) 00758 { 00759 if (!isReadWrite()) 00760 return false; 00761 00762 if (line < 0 || line > lastLine()) 00763 return false; 00764 00765 return editRemoveLine (line); 00766 } 00767 00768 int KateDocument::totalCharacters() const 00769 { 00770 int l = 0; 00771 00772 for (int i = 0; i < m_buffer->count(); ++i) 00773 { 00774 Kate::TextLine line = m_buffer->plainLine(i); 00775 00776 if (line) 00777 l += line->length(); 00778 } 00779 00780 return l; 00781 } 00782 00783 int KateDocument::lines() const 00784 { 00785 return m_buffer->count(); 00786 } 00787 00788 int KateDocument::numVisLines() const 00789 { 00790 return m_buffer->countVisible (); 00791 } 00792 00793 int KateDocument::lineLength ( int line ) const 00794 { 00795 if (line < 0 || line > lastLine()) 00796 return -1; 00797 00798 Kate::TextLine l = m_buffer->plainLine(line); 00799 00800 if (!l) 00801 return -1; 00802 00803 return l->length(); 00804 } 00805 //END 00806 00807 //BEGIN KTextEditor::EditInterface internal stuff 00808 // 00809 // Starts an edit session with (or without) undo, update of view disabled during session 00810 // 00811 void KateDocument::editStart () 00812 { 00813 editSessionNumber++; 00814 00815 if (editSessionNumber > 1) 00816 return; 00817 00818 editIsRunning = true; 00819 00820 m_undoManager->editStart(); 00821 00822 foreach(KateView *view,m_views) 00823 view->editStart (); 00824 00825 m_buffer->editStart (); 00826 } 00827 00828 // 00829 // End edit session and update Views 00830 // 00831 void KateDocument::editEnd () 00832 { 00833 if (editSessionNumber == 0) { 00834 Q_ASSERT(0); 00835 return; 00836 } 00837 00838 // wrap the new/changed text, if something really changed! 00839 if (m_buffer->editChanged() && (editSessionNumber == 1)) 00840 if (m_undoManager->isActive() && config()->wordWrap()) 00841 wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd()); 00842 00843 editSessionNumber--; 00844 00845 if (editSessionNumber > 0) 00846 return; 00847 00848 // end buffer edit, will trigger hl update 00849 // this will cause some possible adjustment of tagline start/end 00850 m_buffer->editEnd (); 00851 00852 m_undoManager->editEnd(); 00853 00854 // edit end for all views !!!!!!!!! 00855 foreach(KateView *view, m_views) 00856 view->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom()); 00857 00858 if (m_buffer->editChanged()) { 00859 setModified(true); 00860 emit textChanged (this); 00861 } 00862 00863 editIsRunning = false; 00864 } 00865 00866 void KateDocument::pushEditState () 00867 { 00868 editStateStack.push(editSessionNumber); 00869 } 00870 00871 void KateDocument::popEditState () 00872 { 00873 if (editStateStack.isEmpty()) return; 00874 00875 int count = editStateStack.pop() - editSessionNumber; 00876 while (count < 0) { ++count; editEnd(); } 00877 while (count > 0) { --count; editStart(); } 00878 } 00879 00880 void KateDocument::inputMethodStart() 00881 { 00882 m_undoManager->inputMethodStart(); 00883 } 00884 00885 void KateDocument::inputMethodEnd() 00886 { 00887 m_undoManager->inputMethodEnd(); 00888 } 00889 00890 bool KateDocument::wrapText(int startLine, int endLine) 00891 { 00892 if (startLine < 0 || endLine < 0) 00893 return false; 00894 00895 if (!isReadWrite()) 00896 return false; 00897 00898 int col = config()->wordWrapAt(); 00899 00900 if (col == 0) 00901 return false; 00902 00903 editStart (); 00904 00905 for (int line = startLine; (line <= endLine) && (line < lines()); line++) 00906 { 00907 Kate::TextLine l = kateTextLine(line); 00908 00909 if (!l) 00910 return false; 00911 00912 kDebug (13020) << "try wrap line: " << line; 00913 00914 if (l->virtualLength(m_buffer->tabWidth()) > col) 00915 { 00916 Kate::TextLine nextl = kateTextLine(line+1); 00917 00918 kDebug (13020) << "do wrap line: " << line; 00919 00920 int eolPosition = l->length()-1; 00921 00922 // take tabs into account here, too 00923 int x = 0; 00924 const QString & t = l->string(); 00925 int z2 = 0; 00926 for ( ; z2 < l->length(); z2++) 00927 { 00928 static const QChar tabChar('\t'); 00929 if (t.at(z2) == tabChar) 00930 x += m_buffer->tabWidth() - (x % m_buffer->tabWidth()); 00931 else 00932 x++; 00933 00934 if (x > col) 00935 break; 00936 } 00937 00938 int searchStart = qMin (z2, l->length()-1); 00939 00940 // If where we are wrapping is an end of line and is a space we don't 00941 // want to wrap there 00942 if (searchStart == eolPosition && t.at(searchStart).isSpace()) 00943 searchStart--; 00944 00945 // Scan backwards looking for a place to break the line 00946 // We are not interested in breaking at the first char 00947 // of the line (if it is a space), but we are at the second 00948 // anders: if we can't find a space, try breaking on a word 00949 // boundary, using KateHighlight::canBreakAt(). 00950 // This could be a priority (setting) in the hl/filetype/document 00951 int z = 0; 00952 int nw = 0; // alternative position, a non word character 00953 for (z=searchStart; z > 0; z--) 00954 { 00955 if (t.at(z).isSpace()) break; 00956 if ( ! nw && highlight()->canBreakAt( t.at(z) , l->attribute(z) ) ) 00957 nw = z; 00958 } 00959 00960 bool removeTrailingSpace = false; 00961 if (z > 0) 00962 { 00963 // So why don't we just remove the trailing space right away? 00964 // Well, the (view's) cursor may be directly in front of that space 00965 // (user typing text before the last word on the line), and if that 00966 // happens, the cursor would be moved to the next line, which is not 00967 // what we want (bug #106261) 00968 z++; 00969 removeTrailingSpace = true; 00970 } 00971 else 00972 { 00973 // There was no space to break at so break at a nonword character if 00974 // found, or at the wrapcolumn ( that needs be configurable ) 00975 // Don't try and add any white space for the break 00976 if ( nw && nw < col ) nw++; // break on the right side of the character 00977 z = nw ? nw : col; 00978 } 00979 00980 if (nextl && !nextl->isAutoWrapped()) 00981 { 00982 editWrapLine (line, z, true); 00983 editMarkLineAutoWrapped (line+1, true); 00984 00985 endLine++; 00986 } 00987 else 00988 { 00989 if (nextl && (nextl->length() > 0) && !nextl->at(0).isSpace() && ((l->length() < 1) || !l->at(l->length()-1).isSpace())) 00990 editInsertText (line+1, 0, QString (" ")); 00991 00992 bool newLineAdded = false; 00993 editWrapLine (line, z, false, &newLineAdded); 00994 00995 editMarkLineAutoWrapped (line+1, true); 00996 00997 endLine++; 00998 } 00999 01000 if (removeTrailingSpace) { 01001 // cu space 01002 editRemoveText (line, z - 1, 1); 01003 } 01004 } 01005 } 01006 01007 editEnd (); 01008 01009 return true; 01010 } 01011 01012 bool KateDocument::editInsertText ( int line, int col, const QString &s ) 01013 { 01014 if (line < 0 || col < 0) 01015 return false; 01016 01017 if (!isReadWrite()) 01018 return false; 01019 01020 Kate::TextLine l = kateTextLine(line); 01021 01022 if (!l) 01023 return false; 01024 01025 // nothing to do, do nothing! 01026 if (s.isEmpty()) 01027 return true; 01028 01029 editStart (); 01030 01031 QString s2 = s; 01032 int col2 = col; 01033 if (col2 > l->length()) { 01034 s2 = QString(col2 - l->length(), QLatin1Char(' ')) + s; 01035 col2 = l->length(); 01036 } 01037 01038 m_undoManager->slotTextInserted(line, col2, s2); 01039 01040 // insert text into line 01041 m_buffer->insertText (KTextEditor::Cursor (line, col2), s2); 01042 01043 emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col2, line, col2 + s2.length())); 01044 01045 editEnd(); 01046 01047 return true; 01048 } 01049 01050 bool KateDocument::editRemoveText ( int line, int col, int len ) 01051 { 01052 if (line < 0 || col < 0 || len < 0) 01053 return false; 01054 01055 if (!isReadWrite()) 01056 return false; 01057 01058 Kate::TextLine l = kateTextLine(line); 01059 01060 if (!l) 01061 return false; 01062 01063 // nothing to do, do nothing! 01064 if (len == 0) 01065 return true; 01066 01067 // wrong column 01068 if (col >= l->text().size()) 01069 return false; 01070 01071 // don't try to remove what's not there 01072 len = qMin(len, l->text().size() - col); 01073 01074 editStart (); 01075 01076 QString oldText = l->string().mid(col, len); 01077 01078 m_undoManager->slotTextRemoved(line, col, oldText); 01079 01080 // remove text from line 01081 m_buffer->removeText (KTextEditor::Range (KTextEditor::Cursor (line, col), KTextEditor::Cursor (line, col+len))); 01082 01083 removeTrailingSpace( line ); 01084 01085 emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len)); 01086 emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len), oldText); 01087 01088 editEnd (); 01089 01090 return true; 01091 } 01092 01093 bool KateDocument::editMarkLineAutoWrapped ( int line, bool autowrapped ) 01094 { 01095 if (line < 0) 01096 return false; 01097 01098 if (!isReadWrite()) 01099 return false; 01100 01101 Kate::TextLine l = kateTextLine(line); 01102 01103 if (!l) 01104 return false; 01105 01106 editStart (); 01107 01108 m_undoManager->slotMarkLineAutoWrapped(line, autowrapped); 01109 01110 l->setAutoWrapped (autowrapped); 01111 01112 editEnd (); 01113 01114 return true; 01115 } 01116 01117 bool KateDocument::editWrapLine ( int line, int col, bool newLine, bool *newLineAdded) 01118 { 01119 if (line < 0 || col < 0) 01120 return false; 01121 01122 if (!isReadWrite()) 01123 return false; 01124 01125 Kate::TextLine l = kateTextLine(line); 01126 01127 if (!l) 01128 return false; 01129 01130 editStart (); 01131 01132 Kate::TextLine nextLine = kateTextLine(line+1); 01133 01134 int pos = l->length() - col; 01135 01136 if (pos < 0) 01137 pos = 0; 01138 01139 m_undoManager->slotLineWrapped(line, col, pos, (!nextLine || newLine)); 01140 01141 if (!nextLine || newLine) 01142 { 01143 m_buffer->wrapLine (KTextEditor::Cursor (line, col)); 01144 01145 QList<KTextEditor::Mark*> list; 01146 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) 01147 { 01148 if( i.value()->line >= line ) 01149 { 01150 if ((col == 0) || (i.value()->line > line)) 01151 list.append( i.value() ); 01152 } 01153 } 01154 01155 for( int i=0; i < list.size(); ++i ) 01156 m_marks.take( list.at(i)->line ); 01157 01158 for( int i=0; i < list.size(); ++i ) 01159 { 01160 list.at(i)->line++; 01161 m_marks.insert( list.at(i)->line, list.at(i) ); 01162 } 01163 01164 if( !list.isEmpty() ) 01165 emit marksChanged( this ); 01166 01167 // yes, we added a new line ! 01168 if (newLineAdded) 01169 (*newLineAdded) = true; 01170 } 01171 else 01172 { 01173 m_buffer->wrapLine (KTextEditor::Cursor (line, col)); 01174 m_buffer->unwrapLine (line + 2); 01175 01176 // no, no new line added ! 01177 if (newLineAdded) 01178 (*newLineAdded) = false; 01179 } 01180 01181 emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line+1, pos)); 01182 01183 editEnd (); 01184 01185 return true; 01186 } 01187 01188 bool KateDocument::editUnWrapLine ( int line, bool removeLine, int length ) 01189 { 01190 if (line < 0 || length < 0) 01191 return false; 01192 01193 if (!isReadWrite()) 01194 return false; 01195 01196 Kate::TextLine l = kateTextLine(line); 01197 Kate::TextLine nextLine = kateTextLine(line+1); 01198 01199 if (!l || !nextLine) 01200 return false; 01201 01202 editStart (); 01203 01204 int col = l->length (); 01205 01206 m_undoManager->slotLineUnWrapped(line, col, length, removeLine); 01207 01208 if (removeLine) 01209 { 01210 m_buffer->unwrapLine (line+1); 01211 } 01212 else 01213 { 01214 m_buffer->insertText (KTextEditor::Cursor (line, col), nextLine->string().left((nextLine->length() < length) ? nextLine->length() : length)); 01215 m_buffer->removeText (KTextEditor::Range (KTextEditor::Cursor (line + 1, 0), KTextEditor::Cursor (line + 1, (nextLine->length() < length) ? nextLine->length() : length))); 01216 } 01217 01218 QList<KTextEditor::Mark*> list; 01219 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) 01220 { 01221 if( i.value()->line >= line+1 ) 01222 list.append( i.value() ); 01223 01224 if ( i.value()->line == line+1 ) 01225 { 01226 KTextEditor::Mark* mark = m_marks.take( line ); 01227 01228 if (mark) 01229 { 01230 i.value()->type |= mark->type; 01231 } 01232 } 01233 } 01234 01235 for( int i=0; i < list.size(); ++i ) 01236 m_marks.take( list.at(i)->line ); 01237 01238 for( int i=0; i < list.size(); ++i ) 01239 { 01240 list.at(i)->line--; 01241 m_marks.insert( list.at(i)->line, list.at(i) ); 01242 } 01243 01244 if( !list.isEmpty() ) 01245 emit marksChanged( this ); 01246 01247 emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line+1, 0)); 01248 emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line+1, 0), "\n"); 01249 01250 editEnd (); 01251 01252 return true; 01253 } 01254 01255 bool KateDocument::editInsertLine ( int line, const QString &s ) 01256 { 01257 if (line < 0) 01258 return false; 01259 01260 if (!isReadWrite()) 01261 return false; 01262 01263 if ( line > lines() ) 01264 return false; 01265 01266 editStart (); 01267 01268 m_undoManager->slotLineInserted(line, s); 01269 01270 removeTrailingSpace( line ); // old line 01271 01272 // wrap line 01273 if (line > 0) { 01274 Kate::TextLine previousLine = m_buffer->line (line-1); 01275 m_buffer->wrapLine (KTextEditor::Cursor (line-1, previousLine->text().size())); 01276 } else { 01277 m_buffer->wrapLine (KTextEditor::Cursor (0, 0)); 01278 } 01279 01280 // insert text 01281 m_buffer->insertText (KTextEditor::Cursor (line, 0), s); 01282 01283 removeTrailingSpace( line ); // new line 01284 01285 Kate::TextLine tl = m_buffer->line (line); 01286 01287 QList<KTextEditor::Mark*> list; 01288 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) 01289 { 01290 if( i.value()->line >= line ) 01291 list.append( i.value() ); 01292 } 01293 01294 for( int i=0; i < list.size(); ++i ) 01295 m_marks.take( list.at(i)->line ); 01296 01297 for( int i=0; i < list.size(); ++i ) 01298 { 01299 list.at(i)->line++; 01300 m_marks.insert( list.at(i)->line, list.at(i) ); 01301 } 01302 01303 if( !list.isEmpty() ) 01304 emit marksChanged( this ); 01305 01306 KTextEditor::Range rangeInserted(line, 0, line, tl->length()); 01307 01308 if (line) { 01309 Kate::TextLine prevLine = plainKateTextLine(line - 1); 01310 rangeInserted.start().setPosition(line - 1, prevLine->length()); 01311 } else { 01312 rangeInserted.end().setPosition(line + 1, 0); 01313 } 01314 01315 emit KTextEditor::Document::textInserted(this, rangeInserted); 01316 01317 editEnd (); 01318 01319 return true; 01320 } 01321 01322 bool KateDocument::editRemoveLine ( int line ) 01323 { 01324 return editRemoveLines(line, line); 01325 } 01326 01327 bool KateDocument::editRemoveLines ( int from, int to ) 01328 { 01329 if (to < from || from < 0 || to > lastLine()) 01330 return false; 01331 01332 if (!isReadWrite()) 01333 return false; 01334 01335 if (lines() == 1) 01336 return editRemoveText(0, 0, kateTextLine(0)->length()); 01337 01338 editStart(); 01339 QStringList oldText; 01340 01341 for (int line = to; line >= from; line--) { 01342 KateLineInfo info; 01343 lineInfo(&info, line); 01344 if (info.startsInVisibleBlock) 01345 foldingTree()->toggleRegionVisibility(line); 01346 } 01347 01348 for (int line = to; line >= from; line--) { 01349 Kate::TextLine tl = m_buffer->line (line); 01350 oldText << this->line(line); 01351 m_undoManager->slotLineRemoved(line, this->line(line)); 01352 01353 m_buffer->removeText (KTextEditor::Range (KTextEditor::Cursor (line, 0), KTextEditor::Cursor (line, tl->text().size()))); 01354 if (line + 1 <= lastLine()) 01355 m_buffer->unwrapLine (line+1); 01356 else if (line != 0) 01357 m_buffer->unwrapLine (line); 01358 } 01359 01360 QList<int> rmark; 01361 QList<int> list; 01362 01363 foreach (KTextEditor::Mark* mark, m_marks) { 01364 int line = mark->line; 01365 if (line > to) 01366 list << line; 01367 else if (line >= from) 01368 rmark << line; 01369 } 01370 01371 foreach (int line, rmark) 01372 delete m_marks.take(line); 01373 01374 foreach (int line, list) 01375 { 01376 KTextEditor::Mark* mark = m_marks.take(line); 01377 mark->line -= to - from + 1; 01378 m_marks.insert(mark->line, mark); 01379 } 01380 01381 if (!list.isEmpty()) 01382 emit marksChanged(this); 01383 01384 KTextEditor::Range rangeRemoved(from, 0, to + 1, 0); 01385 01386 if (to == lastLine() + to - from + 1) { 01387 rangeRemoved.end().setPosition(to, oldText.first().length()); 01388 if (from > 0) { 01389 Kate::TextLine prevLine = plainKateTextLine(from - 1); 01390 rangeRemoved.start().setPosition(from - 1, prevLine->length()); 01391 } 01392 } 01393 01394 emit KTextEditor::Document::textRemoved(this, rangeRemoved); 01395 emit KTextEditor::Document::textRemoved(this, rangeRemoved, oldText.join("\n")); 01396 01397 editEnd(); 01398 01399 return true; 01400 } 01401 //END 01402 01403 //BEGIN KTextEditor::UndoInterface stuff 01404 01405 uint KateDocument::undoCount () const 01406 { 01407 return m_undoManager->undoCount (); 01408 } 01409 01410 uint KateDocument::redoCount () const 01411 { 01412 return m_undoManager->redoCount (); 01413 } 01414 01415 void KateDocument::undo() 01416 { 01417 m_undoManager->undo(); 01418 } 01419 01420 void KateDocument::redo() 01421 { 01422 m_undoManager->redo(); 01423 } 01424 //END 01425 01426 //BEGIN KTextEditor::SearchInterface stuff 01427 QVector<KTextEditor::Range> KateDocument::searchText( 01428 const KTextEditor::Range & range, 01429 const QString & pattern, 01430 const KTextEditor::Search::SearchOptions options) 01431 { 01432 // TODO 01433 // * support BlockInputRange 01434 // * support DotMatchesNewline 01435 01436 const bool escapeSequences = options.testFlag(KTextEditor::Search::EscapeSequences); 01437 const bool regexMode = options.testFlag(KTextEditor::Search::Regex); 01438 const bool backwards = options.testFlag(KTextEditor::Search::Backwards); 01439 const bool wholeWords = options.testFlag(KTextEditor::Search::WholeWords); 01440 const Qt::CaseSensitivity caseSensitivity = options.testFlag(KTextEditor::Search::CaseInsensitive) ? Qt::CaseInsensitive : Qt::CaseSensitive; 01441 01442 if (regexMode) 01443 { 01444 // regexp search 01445 // escape sequences are supported by definition 01446 KateRegExpSearch searcher(this, caseSensitivity); 01447 return searcher.search(pattern, range, backwards); 01448 } 01449 01450 if (escapeSequences) 01451 { 01452 // escaped search 01453 KatePlainTextSearch searcher(this, caseSensitivity, wholeWords); 01454 KTextEditor::Range match = searcher.search(KateRegExpSearch::escapePlaintext(pattern), range, backwards); 01455 01456 QVector<KTextEditor::Range> result; 01457 result.append(match); 01458 return result; 01459 } 01460 01461 // plaintext search 01462 KatePlainTextSearch searcher(this, caseSensitivity, wholeWords); 01463 KTextEditor::Range match = searcher.search(pattern, range, backwards); 01464 01465 QVector<KTextEditor::Range> result; 01466 result.append(match); 01467 return result; 01468 } 01469 01470 01471 01472 KTextEditor::Search::SearchOptions KateDocument::supportedSearchOptions() const 01473 { 01474 KTextEditor::Search::SearchOptions supported(KTextEditor::Search::Default); 01475 supported |= KTextEditor::Search::Regex; 01476 supported |= KTextEditor::Search::CaseInsensitive; 01477 supported |= KTextEditor::Search::Backwards; 01478 // supported |= KTextEditor::Search::BlockInputRange; 01479 supported |= KTextEditor::Search::EscapeSequences; 01480 supported |= KTextEditor::Search::WholeWords; 01481 // supported |= KTextEditor::Search::DotMatchesNewline; 01482 return supported; 01483 } 01484 //END 01485 01486 QWidget * KateDocument::dialogParent() 01487 { 01488 QWidget *w=widget(); 01489 01490 if(!w) 01491 { 01492 w=activeView(); 01493 01494 if(!w) 01495 w=QApplication::activeWindow(); 01496 } 01497 01498 return w; 01499 } 01500 01501 //BEGIN KTextEditor::HighlightingInterface stuff 01502 bool KateDocument::setMode (const QString &name) 01503 { 01504 updateFileType (name); 01505 return true; 01506 } 01507 01508 QString KateDocument::mode () const 01509 { 01510 return m_fileType; 01511 } 01512 01513 QStringList KateDocument::modes () const 01514 { 01515 QStringList m; 01516 01517 const QList<KateFileType *> &modeList = KateGlobal::self()->modeManager()->list(); 01518 foreach(KateFileType* type, modeList) 01519 m << type->name; 01520 01521 return m; 01522 } 01523 01524 bool KateDocument::setHighlightingMode (const QString &name) 01525 { 01526 m_buffer->setHighlight (KateHlManager::self()->nameFind(name)); 01527 return true; 01528 } 01529 01530 QString KateDocument::highlightingMode () const 01531 { 01532 return highlight()->name (); 01533 } 01534 01535 QStringList KateDocument::highlightingModes () const 01536 { 01537 QStringList hls; 01538 01539 for (int i = 0; i < KateHlManager::self()->highlights(); ++i) 01540 hls << KateHlManager::self()->hlName (i); 01541 01542 return hls; 01543 } 01544 01545 QString KateDocument::highlightingModeSection( int index ) const 01546 { 01547 return KateHlManager::self()->hlSection( index ); 01548 } 01549 01550 QString KateDocument::modeSection( int index ) const 01551 { 01552 return KateGlobal::self()->modeManager()->list().at( index )->section; 01553 } 01554 01555 void KateDocument::bufferHlChanged () 01556 { 01557 // update all views 01558 makeAttribs(false); 01559 01560 // deactivate indenter if necessary 01561 m_indenter->checkRequiredStyle(); 01562 01563 emit highlightingModeChanged(this); 01564 } 01565 01566 01567 void KateDocument::setDontChangeHlOnSave() 01568 { 01569 hlSetByUser = true; 01570 } 01571 01572 void KateDocument::bomSetByUser() 01573 { 01574 m_bomSetByUser=true; 01575 } 01576 //END 01577 01578 //BEGIN KTextEditor::SessionConfigInterface and KTextEditor::ParameterizedSessionConfigInterface stuff 01579 void KateDocument::readSessionConfig(const KConfigGroup &kconfig) 01580 { 01581 readParameterizedSessionConfig(kconfig, SkipNone); 01582 } 01583 01584 void KateDocument::readParameterizedSessionConfig(const KConfigGroup &kconfig, 01585 unsigned long configParameters) 01586 { 01587 if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipEncoding)) { 01588 // get the encoding 01589 QString tmpenc=kconfig.readEntry("Encoding"); 01590 if (!tmpenc.isEmpty() && (tmpenc != encoding())) 01591 setEncoding(tmpenc); 01592 } 01593 01594 if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipUrl)) { 01595 // restore the url 01596 KUrl url (kconfig.readEntry("URL")); 01597 01598 // open the file if url valid 01599 if (!url.isEmpty() && url.isValid()) 01600 openUrl (url); 01601 else completed(); //perhaps this should be emitted at the end of this function 01602 } 01603 else { 01604 completed(); //perhaps this should be emitted at the end of this function 01605 } 01606 01607 if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipMode)) { 01608 // restore the filetype 01609 updateFileType (kconfig.readEntry("Mode", "Normal")); 01610 } 01611 01612 if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipHighlighting)) { 01613 // restore the hl stuff 01614 m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig.readEntry("Highlighting"))); 01615 } 01616 01617 // read only mode 01618 // todo: what does m_bReadOnly mean? 01619 setReadWrite(kconfig.readEntry("ReadWrite", true)); 01620 01621 // indent mode 01622 config()->setIndentationMode( kconfig.readEntry("Indentation Mode", config()->indentationMode() ) ); 01623 01624 // Restore Bookmarks 01625 const QList<int> marks = kconfig.readEntry("Bookmarks", QList<int>()); 01626 for( int i = 0; i < marks.count(); i++ ) 01627 addMark( marks.at(i), KateDocument::markType01 ); 01628 } 01629 01630 void KateDocument::writeSessionConfig(KConfigGroup &kconfig) 01631 { 01632 writeParameterizedSessionConfig(kconfig, SkipNone); 01633 } 01634 01635 void KateDocument::writeParameterizedSessionConfig(KConfigGroup &kconfig, 01636 unsigned long configParameters) 01637 { 01638 if ( this->url().isLocalFile() ) { 01639 const QString path = this->url().toLocalFile(); 01640 if ( KGlobal::dirs()->relativeLocation( "tmp", path ) != path ) { 01641 return; // inside tmp resource, do not save 01642 } 01643 } 01644 01645 if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipUrl)) { 01646 // save url 01647 kconfig.writeEntry("URL", this->url().prettyUrl() ); 01648 } 01649 01650 if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipEncoding)) { 01651 // save encoding 01652 kconfig.writeEntry("Encoding",encoding()); 01653 } 01654 01655 if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipMode)) { 01656 // save file type 01657 kconfig.writeEntry("Mode", m_fileType); 01658 } 01659 01660 if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipHighlighting)) { 01661 // save hl 01662 kconfig.writeEntry("Highlighting", highlight()->name()); 01663 } 01664 01665 // read only mode 01666 kconfig.writeEntry("ReadWrite", isReadWrite()); 01667 01668 // indent mode 01669 kconfig.writeEntry("Indentation Mode", config()->indentationMode() ); 01670 01671 // Save Bookmarks 01672 QList<int> marks; 01673 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) 01674 if (i.value()->type & KTextEditor::MarkInterface::markType01) 01675 marks << i.value()->line; 01676 01677 kconfig.writeEntry( "Bookmarks", marks ); 01678 } 01679 01680 //END KTextEditor::SessionConfigInterface and KTextEditor::ParameterizedSessionConfigInterface stuff 01681 01682 uint KateDocument::mark( int line ) 01683 { 01684 KTextEditor::Mark* m = m_marks.value(line); 01685 if( !m ) 01686 return 0; 01687 01688 return m->type; 01689 } 01690 01691 void KateDocument::setMark( int line, uint markType ) 01692 { 01693 clearMark( line ); 01694 addMark( line, markType ); 01695 } 01696 01697 void KateDocument::clearMark( int line ) 01698 { 01699 if( line > lastLine() ) 01700 return; 01701 01702 if( !m_marks.value(line) ) 01703 return; 01704 01705 KTextEditor::Mark* mark = m_marks.take( line ); 01706 emit markChanged( this, *mark, MarkRemoved ); 01707 emit marksChanged( this ); 01708 delete mark; 01709 tagLines( line, line ); 01710 repaintViews(true); 01711 } 01712 01713 void KateDocument::addMark( int line, uint markType ) 01714 { 01715 KTextEditor::Mark *mark; 01716 01717 if( line > lastLine()) 01718 return; 01719 01720 if( markType == 0 ) 01721 return; 01722 01723 if( (mark = m_marks.value(line)) ) { 01724 // Remove bits already set 01725 markType &= ~mark->type; 01726 01727 if( markType == 0 ) 01728 return; 01729 01730 // Add bits 01731 mark->type |= markType; 01732 } else { 01733 mark = new KTextEditor::Mark; 01734 mark->line = line; 01735 mark->type = markType; 01736 m_marks.insert( line, mark ); 01737 } 01738 01739 // Emit with a mark having only the types added. 01740 KTextEditor::Mark temp; 01741 temp.line = line; 01742 temp.type = markType; 01743 emit markChanged( this, temp, MarkAdded ); 01744 01745 emit marksChanged( this ); 01746 tagLines( line, line ); 01747 repaintViews(true); 01748 } 01749 01750 void KateDocument::removeMark( int line, uint markType ) 01751 { 01752 if( line > lastLine() ) 01753 return; 01754 01755 KTextEditor::Mark* mark = m_marks.value(line); 01756 01757 if( !mark ) 01758 return; 01759 01760 // Remove bits not set 01761 markType &= mark->type; 01762 01763 if( markType == 0 ) 01764 return; 01765 01766 // Subtract bits 01767 mark->type &= ~markType; 01768 01769 // Emit with a mark having only the types removed. 01770 KTextEditor::Mark temp; 01771 temp.line = line; 01772 temp.type = markType; 01773 emit markChanged( this, temp, MarkRemoved ); 01774 01775 if( mark->type == 0 ) 01776 m_marks.remove( line ); 01777 01778 emit marksChanged( this ); 01779 tagLines( line, line ); 01780 repaintViews(true); 01781 } 01782 01783 const QHash<int, KTextEditor::Mark*> &KateDocument::marks() 01784 { 01785 return m_marks; 01786 } 01787 01788 void KateDocument::requestMarkTooltip( int line, QPoint position ) 01789 { 01790 KTextEditor::Mark* mark = m_marks.value(line); 01791 if(!mark) 01792 return; 01793 01794 bool handled = false; 01795 emit markToolTipRequested( this, *mark, position, handled ); 01796 } 01797 01798 bool KateDocument::handleMarkClick( int line ) 01799 { 01800 KTextEditor::Mark* mark = m_marks.value(line); 01801 if(!mark) 01802 return false; 01803 01804 bool handled = false; 01805 emit markClicked( this, *mark, handled ); 01806 01807 return handled; 01808 } 01809 01810 bool KateDocument::handleMarkContextMenu( int line, QPoint position ) 01811 { 01812 KTextEditor::Mark* mark = m_marks.value(line); 01813 if(!mark) 01814 return false; 01815 01816 bool handled = false; 01817 01818 emit markContextMenuRequested( this, *mark, position, handled ); 01819 01820 return handled; 01821 } 01822 01823 void KateDocument::clearMarks() 01824 { 01825 while (!m_marks.isEmpty()) 01826 { 01827 QHash<int, KTextEditor::Mark*>::iterator it = m_marks.begin(); 01828 KTextEditor::Mark mark = *it.value(); 01829 delete it.value(); 01830 m_marks.erase (it); 01831 01832 emit markChanged( this, mark, MarkRemoved ); 01833 tagLines( mark.line, mark.line ); 01834 } 01835 01836 m_marks.clear(); 01837 01838 emit marksChanged( this ); 01839 repaintViews(true); 01840 } 01841 01842 void KateDocument::setMarkPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap ) 01843 { 01844 m_markPixmaps.insert( type, pixmap ); 01845 } 01846 01847 void KateDocument::setMarkDescription( MarkInterface::MarkTypes type, const QString& description ) 01848 { 01849 m_markDescriptions.insert( type, description ); 01850 } 01851 01852 QPixmap KateDocument::markPixmap( MarkInterface::MarkTypes type ) const 01853 { 01854 return m_markPixmaps.value(type, QPixmap()); 01855 } 01856 01857 QColor KateDocument::markColor( MarkInterface::MarkTypes type ) const 01858 { 01859 uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1; 01860 if ((uint)type >= (uint)markType01 && (uint)type <= reserved) { 01861 return KateRendererConfig::global()->lineMarkerColor(type); 01862 } else { 01863 return QColor(); 01864 } 01865 } 01866 01867 QString KateDocument::markDescription( MarkInterface::MarkTypes type ) const 01868 { 01869 return m_markDescriptions.value(type, QString()); 01870 } 01871 01872 void KateDocument::setEditableMarks( uint markMask ) 01873 { 01874 m_editableMarks = markMask; 01875 } 01876 01877 uint KateDocument::editableMarks() const 01878 { 01879 return m_editableMarks; 01880 } 01881 //END 01882 01883 //BEGIN KTextEditor::PrintInterface stuff 01884 bool KateDocument::printDialog () 01885 { 01886 return KatePrinter::print (this); 01887 } 01888 01889 bool KateDocument::print () 01890 { 01891 return KatePrinter::print (this); 01892 } 01893 //END 01894 01895 //BEGIN KTextEditor::DocumentInfoInterface (### unfinished) 01896 QString KateDocument::mimeType() 01897 { 01898 KMimeType::Ptr result = KMimeType::defaultMimeTypePtr(); 01899 01900 // if the document has a URL, try KMimeType::findByURL 01901 if ( ! this->url().isEmpty() ) 01902 result = KMimeType::findByUrl( this->url() ); 01903 01904 else if ( this->url().isEmpty() || ! this->url().isLocalFile() ) 01905 result = mimeTypeForContent(); 01906 01907 return result->name(); 01908 } 01909 01910 KMimeType::Ptr KateDocument::mimeTypeForContent() 01911 { 01912 QByteArray buf (1024,'\0'); 01913 uint bufpos = 0; 01914 01915 for (int i=0; i < lines(); ++i) 01916 { 01917 QString line = this->line( i ); 01918 uint len = line.length() + 1; 01919 01920 if (bufpos + len > 1024) 01921 len = 1024 - bufpos; 01922 01923 QString ld (line + QChar::fromAscii('\n')); 01924 buf.replace(bufpos,len,ld.toLatin1()); //memcpy(buf.data() + bufpos, ld.toLatin1().constData(), len); 01925 01926 bufpos += len; 01927 01928 if (bufpos >= 1024) 01929 break; 01930 } 01931 buf.resize( bufpos ); 01932 01933 int accuracy = 0; 01934 KMimeType::Ptr mt = KMimeType::findByContent(buf, &accuracy); 01935 return mt ? mt : KMimeType::defaultMimeTypePtr(); 01936 } 01937 //END KTextEditor::DocumentInfoInterface 01938 01939 01940 //BEGIN KParts::ReadWrite stuff 01941 bool KateDocument::openFile() 01942 { 01946 emit aboutToInvalidateMovingInterfaceContent (this); 01947 01948 // no open errors until now... 01949 setOpeningError(false); 01950 01951 // add new m_file to dirwatch 01952 activateDirWatch (); 01953 01954 // 01955 // mime type magic to get encoding right 01956 // 01957 QString mimeType = arguments().mimeType(); 01958 int pos = mimeType.indexOf(';'); 01959 if (pos != -1) 01960 setEncoding (mimeType.mid(pos+1)); 01961 01962 // do we have success ? 01963 emit KTextEditor::Document::textRemoved(this, documentRange()); 01964 emit KTextEditor::Document::textRemoved(this, documentRange(), m_buffer->text()); 01965 01966 bool success = m_buffer->openFile (localFilePath()); 01967 01968 // disable view updates 01969 foreach (KateView * view, m_views) 01970 view->setUpdatesEnabled (false); 01971 01972 // 01973 // yeah, success 01974 // 01975 if (success) 01976 { 01977 // update file type 01978 updateFileType (KateGlobal::self()->modeManager()->fileType (this)); 01979 01980 // read dir config (if possible and wanted) 01981 readDirConfig (); 01982 01983 // read vars 01984 readVariables(); 01985 01986 // remove trailing space 01987 // NOTE: wait until now because the config or variables might tell us not to do this! 01988 m_buffer->setRemoveTrailingSpaces (config()->removeSpaces()); 01989 if ( m_buffer->removeTrailingSpaces() ) 01990 { 01991 int n = lines(); 01992 while (n--) 01993 removeTrailingSpace (n); 01994 } 01995 01996 // update the md5 digest 01997 createDigest( m_digest ); 01998 01999 if (!m_postLoadFilterChecks.isEmpty()) 02000 { 02001 LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins(); 02002 foreach(const QString& checkplugin, m_postLoadFilterChecks) 02003 { 02004 lscps->postLoadFilter(checkplugin,this); 02005 } 02006 } 02007 } 02008 02009 // 02010 // update views 02011 // 02012 foreach (KateView * view, m_views) 02013 { 02014 // This is needed here because inserting the text moves the view's start position (it is a MovingCursor) 02015 view->setCursorPosition (KTextEditor::Cursor()); 02016 view->setUpdatesEnabled (true); 02017 view->updateView (true); 02018 } 02019 02020 // emit all signals about new text after view updates 02021 emit KTextEditor::Document::textInserted(this, documentRange()); 02022 02023 // Inform that the text has changed (required as we're not inside the usual editStart/End stuff) 02024 emit textChanged (this); 02025 02026 if (!m_reloading) 02027 { 02028 // 02029 // emit the signal we need for example for kate app 02030 // 02031 emit documentUrlChanged (this); 02032 02033 // 02034 // set doc name, dummy value as arg, don't need it 02035 // 02036 setDocName (QString()); 02037 } 02038 // 02039 // to houston, we are not modified 02040 // 02041 if (m_modOnHd) 02042 { 02043 m_modOnHd = false; 02044 m_modOnHdReason = OnDiskUnmodified; 02045 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); 02046 } 02047 02048 // 02049 // display errors 02050 // 02051 QWidget *parentWidget(dialogParent()); 02052 02053 if (!suppressOpeningErrorDialogs()) 02054 { 02055 if (!success) 02056 KMessageBox::error (parentWidget, i18n ("The file %1 could not be loaded, as it was not possible to read from it.\n\nCheck if you have read access to this file.", this->url().pathOrUrl())); 02057 } 02058 02059 if (!success) { 02060 setOpeningError(true); 02061 setOpeningErrorMessage(i18n ("The file %1 could not be loaded, as it was not possible to read from it.\n\nCheck if you have read access to this file.",this->url().pathOrUrl())); 02062 } 02063 02064 // warn: broken encoding 02065 if (m_buffer->brokenEncoding()) 02066 { 02067 // this file can't be saved again without killing it 02068 setReadWrite( false ); 02069 02070 if (!suppressOpeningErrorDialogs()) 02071 KMessageBox::information (parentWidget 02072 , i18n ("The file %1 was opened with %2 encoding but contained invalid characters." 02073 " It is set to read-only mode, as saving might destroy its content." 02074 " Either reopen the file with the correct encoding chosen or enable the read-write mode again in the menu to be able to edit it.", this->url().pathOrUrl(), 02075 QString (m_buffer->textCodec()->name ())) 02076 , i18n ("Broken Encoding") 02077 , "Broken Encoding Warning"); 02078 setOpeningError(true); 02079 setOpeningErrorMessage(i18n ("The file %1 was opened with %2 encoding but contained invalid characters." 02080 " It is set to read-only mode, as saving might destroy its content." 02081 " Either reopen the file with the correct encoding chosen or enable the read-write mode again in the menu to be able to edit it.", this->url().pathOrUrl(), QString (m_buffer->textCodec()->name ()))); 02082 } 02083 02084 // 02085 // return the success 02086 // 02087 return success; 02088 } 02089 02090 bool KateDocument::saveFile() 02091 { 02092 QWidget *parentWidget(dialogParent()); 02093 02094 // 02095 // warn -> try to save binary file!!!!!!! 02096 // 02097 #if 0 02098 if (m_buffer->binary() && (KMessageBox::warningContinueCancel (parentWidget 02099 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", url().pathOrUrl()) 02100 , i18n ("Trying to Save Binary File") 02101 , KGuiItem(i18n("Save Nevertheless")) 02102 , KStandardGuiItem::cancel(), "Binary File Save Warning") != KMessageBox::Continue)) 02103 return false; 02104 #endif 02105 02106 // some warnings, if file was changed by the outside! 02107 if ( !url().isEmpty() ) 02108 { 02109 if (m_fileChangedDialogsActivated && m_modOnHd) 02110 { 02111 QString str = reasonedMOHString() + "\n\n"; 02112 02113 if (!isModified()) 02114 { 02115 if (KMessageBox::warningContinueCancel(parentWidget, 02116 str + i18n("Do you really want to save this unmodified file? You could overwrite changed data in the file on disk."),i18n("Trying to Save Unmodified File"),KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue) 02117 return false; 02118 } 02119 else 02120 { 02121 if (KMessageBox::warningContinueCancel(parentWidget, 02122 str + i18n("Do you really want to save this file? Both your open file and the file on disk were changed. There could be some data lost."),i18n("Possible Data Loss"),KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue) 02123 return false; 02124 } 02125 } 02126 } 02127 02128 // 02129 // can we encode it if we want to save it ? 02130 // 02131 if (!m_buffer->canEncode () 02132 && (KMessageBox::warningContinueCancel(parentWidget, 02133 i18n("The selected encoding cannot encode every unicode character in this document. Do you really want to save it? There could be some data lost."),i18n("Possible Data Loss"),KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue)) 02134 { 02135 return false; 02136 } 02137 02138 // 02139 // try to create backup file.. 02140 // 02141 02142 // local file or not is here the question 02143 bool l ( url().isLocalFile() ); 02144 02145 // does the user want any backup, if not, not our problem? 02146 if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles ) 02147 || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) ) 02148 { 02149 KUrl u( url() ); 02150 u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() ); 02151 02152 kDebug( 13020 ) << "backup src file name: " << url(); 02153 kDebug( 13020 ) << "backup dst file name: " << u; 02154 02155 // handle the backup... 02156 bool backupSuccess = false; 02157 02158 // local file mode, no kio 02159 if (u.isLocalFile ()) 02160 { 02161 if (QFile::exists (url().toLocalFile ())) 02162 { 02163 // first: check if backupFile is already there, if true, unlink it 02164 QFile backupFile (u.toLocalFile ()); 02165 if (backupFile.exists()) backupFile.remove (); 02166 02167 backupSuccess = QFile::copy (url().toLocalFile (), u.toLocalFile ()); 02168 } 02169 else 02170 backupSuccess = true; 02171 } 02172 else // remote file mode, kio 02173 { 02174 QWidget *w = widget (); 02175 if (!w && !m_views.isEmpty ()) 02176 w = m_views.first(); 02177 02178 // get the right permissions, start with safe default 02179 mode_t perms = 0600; 02180 KIO::UDSEntry fentry; 02181 if (KIO::NetAccess::stat (url(), fentry, QApplication::activeWindow())) 02182 { 02183 kDebug( 13020 ) << "stating succesfull: " << url(); 02184 KFileItem item (fentry, url()); 02185 perms = item.permissions(); 02186 02187 // do a evil copy which will overwrite target if possible 02188 KIO::FileCopyJob *job = KIO::file_copy ( url(), u, -1, KIO::Overwrite ); 02189 backupSuccess = KIO::NetAccess::synchronousRun(job, w); 02190 } 02191 else 02192 backupSuccess = true; 02193 } 02194 02195 // backup has failed, ask user how to proceed 02196 if (!backupSuccess && (KMessageBox::warningContinueCancel (parentWidget 02197 , i18n ("For file %1 no backup copy could be created before saving." 02198 " If an error occurs while saving, you might lose the data of this file." 02199 " A reason could be that the media you write to is full or the directory of the file is read-only for you.", url().pathOrUrl()) 02200 , i18n ("Failed to create backup copy.") 02201 , KGuiItem(i18n("Try to Save Nevertheless")) 02202 , KStandardGuiItem::cancel(), "Backup Failed Warning") != KMessageBox::Continue)) 02203 { 02204 return false; 02205 } 02206 } 02207 02208 // update file type 02209 updateFileType (KateGlobal::self()->modeManager()->fileType (this)); 02210 02211 if (!m_preSavePostDialogFilterChecks.isEmpty()) 02212 { 02213 LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins(); 02214 foreach(const QString& checkplugin, m_preSavePostDialogFilterChecks) 02215 { 02216 if (lscps->preSavePostDialogFilterCheck(checkplugin,this,parentWidget)==false) 02217 return false; 02218 } 02219 } 02220 02221 // remember the oldpath... 02222 QString oldPath = m_dirWatchFile; 02223 02224 // remove file from dirwatch 02225 deactivateDirWatch (); 02226 02227 // 02228 // try to save 02229 // 02230 if (!m_buffer->saveFile (localFilePath())) 02231 { 02232 // add m_file again to dirwatch 02233 activateDirWatch (oldPath); 02234 02235 KMessageBox::error (parentWidget, i18n ("The document could not be saved, as it was not possible to write to %1.\n\nCheck that you have write access to this file or that enough disk space is available.", this->url().pathOrUrl())); 02236 02237 return false; 02238 } 02239 02240 // update the md5 digest 02241 createDigest( m_digest ); 02242 02243 // add m_file again to dirwatch 02244 activateDirWatch (); 02245 02246 // update file type 02247 // updateFileType (KateGlobal::self()->modeManager()->fileType (this)); 02248 02249 // read dir config (if possible and wanted) 02250 if ( url().isLocalFile()) 02251 { 02252 QFileInfo fo (oldPath), fn (m_dirWatchFile); 02253 02254 if (fo.path() != fn.path()) 02255 readDirConfig(); 02256 } 02257 02258 // read our vars 02259 readVariables(); 02260 02261 // 02262 // we are not modified 02263 // 02264 if (m_modOnHd) 02265 { 02266 m_modOnHd = false; 02267 m_modOnHdReason = OnDiskUnmodified; 02268 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); 02269 } 02270 02271 // update document name... 02272 setDocName( QString() ); 02273 02274 // url may have changed... 02275 emit documentUrlChanged (this); 02276 02277 m_savingToUrl=true; 02278 02279 // (dominik) mark last undo group as not mergeable, otherwise the next 02280 // edit action might be merged and undo will never stop at the saved state 02281 m_undoManager->undoSafePoint(); 02282 02283 // 02284 // return success 02285 // 02286 return true; 02287 } 02288 02289 void KateDocument::readDirConfig () 02290 { 02291 int depth = config()->searchDirConfigDepth (); 02292 02293 if (this->url().isLocalFile() && (depth > -1)) 02294 { 02295 QString currentDir = QFileInfo (localFilePath()).absolutePath(); 02296 02297 // only search as deep as specified or not at all ;) 02298 while (depth > -1) 02299 { 02300 //kDebug (13020) << "search for config file in path: " << currentDir; 02301 02302 // try to open config file in this dir 02303 QFile f (currentDir + "/.kateconfig"); 02304 02305 if (f.open (QIODevice::ReadOnly)) 02306 { 02307 QTextStream stream (&f); 02308 02309 uint linesRead = 0; 02310 QString line = stream.readLine(); 02311 while ((linesRead < 32) && !line.isNull()) 02312 { 02313 readVariableLine( line ); 02314 02315 line = stream.readLine(); 02316 02317 linesRead++; 02318 } 02319 02320 break; 02321 } 02322 02323 QString newDir = QFileInfo (currentDir).absolutePath(); 02324 02325 // bail out on looping (for example reached /) 02326 if (currentDir == newDir) 02327 break; 02328 02329 currentDir = newDir; 02330 --depth; 02331 } 02332 } 02333 } 02334 02335 void KateDocument::activateDirWatch (const QString &useFileName) 02336 { 02337 QString fileToUse = useFileName; 02338 if (fileToUse.isEmpty()) 02339 fileToUse = localFilePath(); 02340 02341 // same file as we are monitoring, return 02342 if (fileToUse == m_dirWatchFile) 02343 return; 02344 02345 // remove the old watched file 02346 deactivateDirWatch (); 02347 02348 // add new file if needed 02349 if (url().isLocalFile() && !fileToUse.isEmpty()) 02350 { 02351 KateGlobal::self()->dirWatch ()->addFile (fileToUse); 02352 m_dirWatchFile = fileToUse; 02353 } 02354 } 02355 02356 void KateDocument::deactivateDirWatch () 02357 { 02358 if (!m_dirWatchFile.isEmpty()) 02359 KateGlobal::self()->dirWatch ()->removeFile (m_dirWatchFile); 02360 02361 m_dirWatchFile.clear(); 02362 } 02363 02364 bool KateDocument::closeUrl() 02365 { 02366 // 02367 // file mod on hd 02368 // 02369 if ( !m_reloading && !url().isEmpty() ) 02370 { 02371 if (m_fileChangedDialogsActivated && m_modOnHd) 02372 { 02373 QWidget *parentWidget(dialogParent()); 02374 02375 if (!(KMessageBox::warningContinueCancel( 02376 parentWidget, 02377 reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."), 02378 i18n("Possible Data Loss"), KGuiItem(i18n("Close Nevertheless")), KStandardGuiItem::cancel(), 02379 QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue)) 02380 return false; 02381 } 02382 } 02383 02384 // 02385 // first call the normal kparts implementation 02386 // 02387 if (!KParts::ReadWritePart::closeUrl ()) 02388 return false; 02389 02390 // Tell the world that we're about to go ahead with the close 02391 if (!m_reloading) 02392 emit aboutToClose(this); 02393 02397 emit aboutToInvalidateMovingInterfaceContent (this); 02398 02399 // remove file from dirwatch 02400 deactivateDirWatch (); 02401 02402 // 02403 // empty url + fileName 02404 // 02405 setUrl(KUrl()); 02406 setLocalFilePath(QString()); 02407 02408 // we are not modified 02409 if (m_modOnHd) 02410 { 02411 m_modOnHd = false; 02412 m_modOnHdReason = OnDiskUnmodified; 02413 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); 02414 } 02415 02416 emit KTextEditor::Document::textRemoved(this, documentRange()); 02417 emit KTextEditor::Document::textRemoved(this, documentRange(), m_buffer->text()); 02418 02419 { 02420 // remove all marks 02421 clearMarks (); 02422 02423 // clear the buffer 02424 m_buffer->clear(); 02425 02426 // clear undo/redo history 02427 m_undoManager->clearUndo(); 02428 m_undoManager->clearRedo(); 02429 } 02430 02431 // no, we are no longer modified 02432 setModified(false); 02433 02434 // we have no longer any hl 02435 m_buffer->setHighlight(0); 02436 02437 // update all our views 02438 foreach (KateView * view, m_views ) 02439 { 02440 view->clearSelection(); // fix bug #118588 02441 view->clear(); 02442 } 02443 02444 if (!m_reloading) 02445 { 02446 // uh, fileName changed 02447 emit documentUrlChanged (this); 02448 02449 // update doc name 02450 setDocName (QString()); 02451 } 02452 02453 // purge swap file 02454 m_swapfile->fileClosed (); 02455 02456 // success 02457 return true; 02458 } 02459 02460 bool KateDocument::isDataRecoveryAvailable() const 02461 { 02462 return m_swapfile->shouldRecover(); 02463 } 02464 02465 void KateDocument::recoverData() 02466 { 02467 if (isDataRecoveryAvailable()) 02468 m_swapfile->recover(); 02469 } 02470 02471 void KateDocument::discardDataRecovery() 02472 { 02473 if (isDataRecoveryAvailable()) 02474 m_swapfile->discard(); 02475 } 02476 02477 void KateDocument::setReadWrite( bool rw ) 02478 { 02479 if (isReadWrite() != rw) 02480 { 02481 KParts::ReadWritePart::setReadWrite (rw); 02482 02483 foreach( KateView* view, m_views) 02484 { 02485 view->slotUpdateUndo(); 02486 view->slotReadWriteChanged (); 02487 } 02488 } 02489 } 02490 02491 void KateDocument::setModified(bool m) { 02492 02493 if (isModified() != m) { 02494 KParts::ReadWritePart::setModified (m); 02495 02496 foreach( KateView* view,m_views) 02497 { 02498 view->slotUpdateUndo(); 02499 } 02500 02501 emit modifiedChanged (this); 02502 } 02503 02504 m_undoManager->setModified (m); 02505 } 02506 //END 02507 02508 //BEGIN Kate specific stuff ;) 02509 02510 void KateDocument::makeAttribs(bool needInvalidate) 02511 { 02512 foreach(KateView *view,m_views) 02513 view->renderer()->updateAttributes (); 02514 02515 if (needInvalidate) 02516 m_buffer->invalidateHighlighting(); 02517 02518 foreach(KateView *view,m_views) 02519 { 02520 view->tagAll(); 02521 view->updateView (true); 02522 } 02523 } 02524 02525 // the attributes of a hl have changed, update 02526 void KateDocument::internalHlChanged() 02527 { 02528 makeAttribs(); 02529 } 02530 02531 void KateDocument::addView(KTextEditor::View *view) { 02532 if (!view) 02533 return; 02534 02535 m_views.append( static_cast<KateView*>(view) ); 02536 m_textEditViews.append( view ); 02537 02538 // apply the view & renderer vars from the file type 02539 if (!m_fileType.isEmpty()) 02540 readVariableLine(KateGlobal::self()->modeManager()->fileType(m_fileType).varLine, true); 02541 02542 // apply the view & renderer vars from the file 02543 readVariables (true); 02544 02545 setActiveView(view); 02546 } 02547 02548 void KateDocument::removeView(KTextEditor::View *view) { 02549 if (!view) 02550 return; 02551 02552 if (activeView() == view) 02553 setActiveView(0L); 02554 02555 m_views.removeAll( (KateView *) view ); 02556 m_textEditViews.removeAll( view ); 02557 } 02558 02559 void KateDocument::setActiveView(KTextEditor::View* view) 02560 { 02561 if ( m_activeView == view ) 02562 return; 02563 02564 m_activeView = (KateView*)view; 02565 } 02566 02567 bool KateDocument::ownedView(KateView *view) { 02568 // do we own the given view? 02569 return (m_views.contains(view)); 02570 } 02571 02572 uint KateDocument::toVirtualColumn( const KTextEditor::Cursor& cursor ) 02573 { 02574 Kate::TextLine textLine = m_buffer->plainLine(cursor.line()); 02575 02576 if (textLine) 02577 return textLine->toVirtualColumn(cursor.column(), config()->tabWidth()); 02578 else 02579 return 0; 02580 } 02581 02582 bool KateDocument::typeChars ( KateView *view, const QString &chars ) 02583 { 02584 Kate::TextLine textLine = m_buffer->plainLine(view->cursorPosition().line ()); 02585 02586 if (!textLine) 02587 return false; 02588 02589 bool bracketInserted = false; 02590 QString buf; 02591 foreach(const QChar& ch, chars) 02592 { 02593 if (ch.isPrint() || ch == QChar::fromAscii('\t')) 02594 { 02595 buf.append (ch); 02596 02597 if (!bracketInserted && (config()->autoBrackets())) 02598 { 02599 QChar end_ch; 02600 02601 if (ch == '(') { end_ch = ')'; } 02602 if (ch == '[') { end_ch = ']'; } 02603 if (ch == '{') { end_ch = '}'; } 02604 if (ch == '"') { end_ch = '"'; } 02605 if (ch == '\'') { end_ch = '\''; } 02606 02607 if (!end_ch.isNull()) { 02608 bracketInserted = true; 02609 02610 if (view->selection()) { 02611 buf.append(view->selectionText()); 02612 } 02613 02614 buf.append(end_ch); 02615 } 02616 } 02617 } 02618 } 02619 02620 if (buf.isEmpty()) 02621 return false; 02622 02623 editStart (); 02624 02625 if (!view->config()->persistentSelection() && view->selection() ) 02626 view->removeSelectedText(); 02627 02628 KTextEditor::Cursor oldCur (view->cursorPosition()); 02629 02630 if (config()->ovr() 02631 || (view->viInputMode() && view->getViInputModeManager()->getCurrentViMode() == ReplaceMode)) { 02632 02633 KTextEditor::Range r = KTextEditor::Range(view->cursorPosition(), qMin(buf.length(), 02634 textLine->length() - view->cursorPosition().column())); 02635 02636 // replace mode needs to know what was removed so it can be restored with backspace 02637 if (view->viInputMode() && view->getViInputModeManager()->getCurrentViMode() == ReplaceMode 02638 && oldCur.column() < line( view->cursorPosition().line() ).length() ) { 02639 QChar removed = line( view->cursorPosition().line() ).at( r.start().column() ); 02640 view->getViInputModeManager()->getViReplaceMode()->overwrittenChar( removed ); 02641 } 02642 02643 removeText(r); 02644 } 02645 02646 insertText(view->cursorPosition(), buf); 02647 if (bracketInserted) 02648 view->setCursorPositionInternal (view->cursorPosition() - KTextEditor::Cursor(0,1)); 02649 02650 KTextEditor::Cursor b(view->cursorPosition()); 02651 m_indenter->userTypedChar (view, b, chars.isEmpty() ? QChar() : chars.at(chars.length() - 1)); 02652 02653 editEnd (); 02654 02655 view->slotTextInserted (view, oldCur, chars); 02656 return true; 02657 } 02658 02659 void KateDocument::newLine( KateView *v ) 02660 { 02661 editStart(); 02662 02663 if( !v->config()->persistentSelection() && v->selection() ) 02664 v->removeSelectedText(); 02665 02666 // query cursor position 02667 KTextEditor::Cursor c = v->cursorPosition(); 02668 02669 if (c.line() > (int)lastLine()) 02670 c.setLine(lastLine()); 02671 02672 if (c.line() < 0) 02673 c.setLine(0); 02674 02675 uint ln = c.line(); 02676 02677 Kate::TextLine textLine = plainKateTextLine(ln); 02678 02679 if (c.column() > (int)textLine->length()) 02680 c.setColumn(textLine->length()); 02681 02682 // first: wrap line 02683 editWrapLine (c.line(), c.column()); 02684 02685 // second: indent the new line, if needed... 02686 m_indenter->userTypedChar(v, v->cursorPosition(), '\n'); 02687 02688 removeTrailingSpace( ln ); 02689 02690 editEnd(); 02691 } 02692 02693 void KateDocument::transpose( const KTextEditor::Cursor& cursor) 02694 { 02695 Kate::TextLine textLine = m_buffer->plainLine(cursor.line()); 02696 02697 if (!textLine || (textLine->length() < 2)) 02698 return; 02699 02700 uint col = cursor.column(); 02701 02702 if (col > 0) 02703 col--; 02704 02705 if ((textLine->length() - col) < 2) 02706 return; 02707 02708 uint line = cursor.line(); 02709 QString s; 02710 02711 //clever swap code if first character on the line swap right&left 02712 //otherwise left & right 02713 s.append (textLine->at(col+1)); 02714 s.append (textLine->at(col)); 02715 //do the swap 02716 02717 // do it right, never ever manipulate a textline 02718 editStart (); 02719 editRemoveText (line, col, 2); 02720 editInsertText (line, col, s); 02721 editEnd (); 02722 } 02723 02724 void KateDocument::backspace( KateView *view, const KTextEditor::Cursor& c ) 02725 { 02726 if ( !view->config()->persistentSelection() && view->selection() ) { 02727 view->removeSelectedText(); 02728 return; 02729 } 02730 02731 uint col = qMax( c.column(), 0 ); 02732 uint line = qMax( c.line(), 0 ); 02733 02734 if ((col == 0) && (line == 0)) 02735 return; 02736 02737 int complement = 0; 02738 if (col > 0) 02739 { 02740 if (config()->autoBrackets()) 02741 { 02742 // if inside empty (), {}, [], '', "" delete both 02743 Kate::TextLine tl = m_buffer->plainLine(line); 02744 if(!tl) return; 02745 QChar prevChar = tl->at(col-1); 02746 QChar nextChar = tl->at(col); 02747 02748 if ( (prevChar == '"' && nextChar == '"') || 02749 (prevChar == '\'' && nextChar == '\'') || 02750 (prevChar == '(' && nextChar == ')') || 02751 (prevChar == '[' && nextChar == ']') || 02752 (prevChar == '{' && nextChar == '}') ) 02753 { 02754 complement = 1; 02755 } 02756 } 02757 if (!(config()->backspaceIndents())) 02758 { 02759 // ordinary backspace 02760 //c.cursor.col--; 02761 removeText(KTextEditor::Range(line, col-1, line, col+complement)); 02762 } 02763 else 02764 { 02765 // backspace indents: erase to next indent position 02766 Kate::TextLine textLine = m_buffer->plainLine(line); 02767 02768 // don't forget this check!!!! really!!!! 02769 if (!textLine) 02770 return; 02771 02772 int colX = textLine->toVirtualColumn(col, config()->tabWidth()); 02773 int pos = textLine->firstChar(); 02774 if (pos > 0) 02775 pos = textLine->toVirtualColumn(pos, config()->tabWidth()); 02776 02777 if (pos < 0 || pos >= (int)colX) 02778 { 02779 // only spaces on left side of cursor 02780 indent( KTextEditor::Range( line, 0, line, 0), -1); 02781 } 02782 else 02783 removeText(KTextEditor::Range(line, col-1, line, col+complement)); 02784 } 02785 } 02786 else 02787 { 02788 // col == 0: wrap to previous line 02789 if (line >= 1) 02790 { 02791 Kate::TextLine textLine = m_buffer->plainLine(line-1); 02792 02793 // don't forget this check!!!! really!!!! 02794 if (!textLine) 02795 return; 02796 02797 if (config()->wordWrap() && textLine->endsWith(QLatin1String(" "))) 02798 { 02799 // gg: in hard wordwrap mode, backspace must also eat the trailing space 02800 removeText (KTextEditor::Range(line-1, textLine->length()-1, line, 0)); 02801 } 02802 else 02803 removeText (KTextEditor::Range(line-1, textLine->length(), line, 0)); 02804 } 02805 } 02806 } 02807 02808 void KateDocument::del( KateView *view, const KTextEditor::Cursor& c ) 02809 { 02810 if ( !view->config()->persistentSelection() && view->selection() ) { 02811 view->removeSelectedText(); 02812 return; 02813 } 02814 02815 if( c.column() < (int) m_buffer->plainLine(c.line())->length()) 02816 { 02817 removeText(KTextEditor::Range(c, 1)); 02818 } 02819 else if ( c.line() < lastLine() ) 02820 { 02821 removeText(KTextEditor::Range(c.line(), c.column(), c.line()+1, 0)); 02822 } 02823 } 02824 02825 void KateDocument::paste ( KateView* view, QClipboard::Mode mode ) 02826 { 02827 QString s = QApplication::clipboard()->text(mode); 02828 02829 if (s.isEmpty()) 02830 return; 02831 02832 int lines = s.count (QChar::fromAscii ('\n')); 02833 02834 m_undoManager->undoSafePoint(); 02835 02836 editStart (); 02837 02838 KTextEditor::Cursor pos = view->cursorPosition(); 02839 if (!view->config()->persistentSelection() && view->selection()) { 02840 pos = view->selectionRange().start(); 02841 if (view->blockSelection()) 02842 pos = rangeOnLine(view->selectionRange(), pos.line()).start(); 02843 view->removeSelectedText(); 02844 } 02845 02846 if (config()->ovr()) { 02847 QStringList pasteLines = s.split(QLatin1Char('\n')); 02848 02849 if (!view->blockSelectionMode()) { 02850 int endColumn = (pasteLines.count() == 1 ? pos.column() : 0) + pasteLines.last().length(); 02851 removeText(KTextEditor::Range(pos, 02852 pos.line()+pasteLines.count()-1, endColumn)); 02853 } else { 02854 int maxi = qMin(pos.line() + pasteLines.count(), this->lines()); 02855 02856 for (int i = pos.line(); i < maxi; ++i) { 02857 int pasteLength = pasteLines.at(i-pos.line()).length(); 02858 removeText(KTextEditor::Range(i, pos.column(), 02859 i, qMin(pasteLength + pos.column(), lineLength(i)))); 02860 } 02861 } 02862 } 02863 02864 02865 blockRemoveTrailingSpaces(true); 02866 insertText(pos, s, view->blockSelectionMode()); 02867 blockRemoveTrailingSpaces(false); 02868 02869 for (int i = pos.line(); i < pos.line() + lines; ++i) 02870 removeTrailingSpace(i); 02871 02872 editEnd(); 02873 02874 // move cursor right for block select, as the user is moved right internal 02875 // even in that case, but user expects other behavior in block selection 02876 // mode ! 02877 // just let cursor stay, that was it before I changed to moving ranges! 02878 if (view->blockSelectionMode()) 02879 view->setCursorPositionInternal(pos); 02880 02881 if (config()->indentPastedText()) 02882 { 02883 KTextEditor::Range range = KTextEditor::Range(KTextEditor::Cursor(pos.line(), 0), 02884 KTextEditor::Cursor(pos.line() + lines, 0)); 02885 02886 int start = view->selectionRange().start().line(); 02887 const int end = view->selectionRange().end().line(); 02888 02889 editStart(); 02890 02891 blockRemoveTrailingSpaces(true); 02892 m_indenter->indent(view, range); 02893 blockRemoveTrailingSpaces(false); 02894 02895 for (; start <= end; ++start) 02896 removeTrailingSpace(start); 02897 02898 editEnd(); 02899 } 02900 02901 if (!view->blockSelectionMode()) 02902 emit charactersSemiInteractivelyInserted (pos, s); 02903 m_undoManager->undoSafePoint(); 02904 } 02905 02906 void KateDocument::indent (KTextEditor::Range range, int change) 02907 { 02908 // dominik: if there is a selection, iterate afterwards over all lines and 02909 // remove trailing spaces 02910 int start = range.start().line(); 02911 const int end = range.end().line(); 02912 02913 editStart(); 02914 blockRemoveTrailingSpaces(true); 02915 m_indenter->changeIndent(range, change); 02916 blockRemoveTrailingSpaces(false); 02917 02918 if (range.numberOfLines() > 1) { 02919 for (; start <= end; ++start) 02920 removeTrailingSpace(start); 02921 } 02922 editEnd(); 02923 } 02924 02925 void KateDocument::align(KateView *view, const KTextEditor::Range &range) 02926 { 02927 editStart(); 02928 02929 blockRemoveTrailingSpaces(true); 02930 m_indenter->indent(view, range); 02931 blockRemoveTrailingSpaces(false); 02932 02933 for (int start = range.start().line(); start <= range.end().line(); ++start) { 02934 removeTrailingSpace(start); 02935 } 02936 02937 editEnd(); 02938 } 02939 02940 void KateDocument::insertTab( KateView *, const KTextEditor::Cursor& c ) 02941 { 02942 if (!isReadWrite()) 02943 return; 02944 02945 editStart(); 02946 editInsertText(c.line(), c.column(), QChar('\t')); 02947 editEnd(); 02948 } 02949 02950 /* 02951 Remove a given string at the beginning 02952 of the current line. 02953 */ 02954 bool KateDocument::removeStringFromBeginning(int line, const QString &str) 02955 { 02956 Kate::TextLine textline = m_buffer->plainLine(line); 02957 02958 KTextEditor::Cursor cursor (line, 0); 02959 bool there = textline->startsWith(str); 02960 02961 if (!there) 02962 { 02963 cursor.setColumn(textline->firstChar()); 02964 there = textline->matchesAt(cursor.column(), str); 02965 } 02966 02967 if (there) 02968 { 02969 // Remove some chars 02970 removeText (KTextEditor::Range(cursor, str.length())); 02971 } 02972 02973 return there; 02974 } 02975 02976 /* 02977 Remove a given string at the end 02978 of the current line. 02979 */ 02980 bool KateDocument::removeStringFromEnd(int line, const QString &str) 02981 { 02982 Kate::TextLine textline = m_buffer->plainLine(line); 02983 02984 KTextEditor::Cursor cursor (line, 0); 02985 bool there = textline->endsWith(str); 02986 02987 if (there) 02988 { 02989 cursor.setColumn(textline->length() - str.length()); 02990 } 02991 else 02992 { 02993 cursor.setColumn(textline->lastChar() - str.length() + 1); 02994 there = textline->matchesAt(cursor.column(), str); 02995 } 02996 02997 if (there) 02998 { 02999 // Remove some chars 03000 removeText (KTextEditor::Range(cursor, str.length())); 03001 } 03002 03003 return there; 03004 } 03005 03006 /* 03007 Add to the current line a comment line mark at the beginning. 03008 */ 03009 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib ) 03010 { 03011 QString commentLineMark = highlight()->getCommentSingleLineStart(attrib); 03012 int pos = -1; 03013 03014 if (highlight()->getCommentSingleLinePosition(attrib) == KateHighlighting::CSLPosColumn0) 03015 { 03016 pos = 0; 03017 commentLineMark += ' '; 03018 } else { 03019 const Kate::TextLine l = kateTextLine(line); 03020 pos = l->firstChar(); 03021 } 03022 03023 if (pos >= 0) 03024 insertText (KTextEditor::Cursor(line, pos), commentLineMark); 03025 } 03026 03027 /* 03028 Remove from the current line a comment line mark at 03029 the beginning if there is one. 03030 */ 03031 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib ) 03032 { 03033 const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib ); 03034 const QString longCommentMark = shortCommentMark + ' '; 03035 03036 editStart(); 03037 03038 // Try to remove the long comment mark first 03039 bool removed = (removeStringFromBeginning(line, longCommentMark) 03040 || removeStringFromBeginning(line, shortCommentMark)); 03041 03042 editEnd(); 03043 03044 return removed; 03045 } 03046 03047 /* 03048 Add to the current line a start comment mark at the 03049 beginning and a stop comment mark at the end. 03050 */ 03051 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib ) 03052 { 03053 const QString startCommentMark = highlight()->getCommentStart( attrib ) + ' '; 03054 const QString stopCommentMark = ' ' + highlight()->getCommentEnd( attrib ); 03055 03056 editStart(); 03057 03058 // Add the start comment mark 03059 insertText (KTextEditor::Cursor(line, 0), startCommentMark); 03060 03061 // Go to the end of the line 03062 const int col = m_buffer->plainLine(line)->length(); 03063 03064 // Add the stop comment mark 03065 insertText (KTextEditor::Cursor(line, col), stopCommentMark); 03066 03067 editEnd(); 03068 } 03069 03070 /* 03071 Remove from the current line a start comment mark at 03072 the beginning and a stop comment mark at the end. 03073 */ 03074 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib ) 03075 { 03076 QString shortStartCommentMark = highlight()->getCommentStart( attrib ); 03077 QString longStartCommentMark = shortStartCommentMark + ' '; 03078 QString shortStopCommentMark = highlight()->getCommentEnd( attrib ); 03079 QString longStopCommentMark = ' ' + shortStopCommentMark; 03080 03081 editStart(); 03082 03083 // TODO "that's a bad idea, can lead to stray endings, FIXME" 03084 03085 // Try to remove the long start comment mark first 03086 bool removedStart = (removeStringFromBeginning(line, longStartCommentMark) 03087 || removeStringFromBeginning(line, shortStartCommentMark)); 03088 03089 bool removedStop = false; 03090 if (removedStart) 03091 { 03092 // Try to remove the long stop comment mark first 03093 removedStop = (removeStringFromEnd(line, longStopCommentMark) 03094 || removeStringFromEnd(line, shortStopCommentMark)); 03095 } 03096 03097 editEnd(); 03098 03099 return (removedStart || removedStop); 03100 } 03101 03102 /* 03103 Add to the current selection a start comment mark at the beginning 03104 and a stop comment mark at the end. 03105 */ 03106 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib ) 03107 { 03108 const QString startComment = highlight()->getCommentStart( attrib ); 03109 const QString endComment = highlight()->getCommentEnd( attrib ); 03110 03111 KTextEditor::Range range = view->selectionRange(); 03112 03113 if ((range.end().column() == 0) && (range.end().line() > 0)) 03114 range.end().setPosition(range.end().line() - 1, lineLength(range.end().line() - 1)); 03115 03116 editStart(); 03117 03118 if (!view->blockSelection()) { 03119 insertText(range.end(), endComment); 03120 insertText(range.start(), startComment); 03121 } else { 03122 for (int line = range.start().line(); line <= range.end().line(); line++ ) { 03123 KTextEditor::Range subRange = rangeOnLine(range, line); 03124 insertText(subRange.end(), endComment); 03125 insertText(subRange.start(), startComment); 03126 } 03127 } 03128 03129 editEnd (); 03130 // selection automatically updated (MovingRange) 03131 } 03132 03133 /* 03134 Add to the current selection a comment line mark at the beginning of each line. 03135 */ 03136 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib ) 03137 { 03138 const QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + ' '; 03139 03140 int sl = view->selectionRange().start().line(); 03141 int el = view->selectionRange().end().line(); 03142 03143 // if end of selection is in column 0 in last line, omit the last line 03144 if ((view->selectionRange().end().column() == 0) && (el > 0)) 03145 { 03146 el--; 03147 } 03148 03149 editStart(); 03150 03151 // For each line of the selection 03152 for (int z = el; z >= sl; z--) { 03153 //insertText (z, 0, commentLineMark); 03154 addStartLineCommentToSingleLine(z, attrib ); 03155 } 03156 03157 editEnd (); 03158 // selection automatically updated (MovingRange) 03159 } 03160 03161 bool KateDocument::nextNonSpaceCharPos(int &line, int &col) 03162 { 03163 for(; line < (int)m_buffer->count(); line++) { 03164 Kate::TextLine textLine = m_buffer->plainLine(line); 03165 03166 if (!textLine) 03167 break; 03168 03169 col = textLine->nextNonSpaceChar(col); 03170 if(col != -1) 03171 return true; // Next non-space char found 03172 col = 0; 03173 } 03174 // No non-space char found 03175 line = -1; 03176 col = -1; 03177 return false; 03178 } 03179 03180 bool KateDocument::previousNonSpaceCharPos(int &line, int &col) 03181 { 03182 while(true) 03183 { 03184 Kate::TextLine textLine = m_buffer->plainLine(line); 03185 03186 if (!textLine) 03187 break; 03188 03189 col = textLine->previousNonSpaceChar(col); 03190 if(col != -1) return true; 03191 if(line == 0) return false; 03192 --line; 03193 col = textLine->length(); 03194 } 03195 // No non-space char found 03196 line = -1; 03197 col = -1; 03198 return false; 03199 } 03200 03201 /* 03202 Remove from the selection a start comment mark at 03203 the beginning and a stop comment mark at the end. 03204 */ 03205 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib ) 03206 { 03207 const QString startComment = highlight()->getCommentStart( attrib ); 03208 const QString endComment = highlight()->getCommentEnd( attrib ); 03209 03210 int sl = qMax<int> (0, view->selectionRange().start().line()); 03211 int el = qMin<int> (view->selectionRange().end().line(), lastLine()); 03212 int sc = view->selectionRange().start().column(); 03213 int ec = view->selectionRange().end().column(); 03214 03215 // The selection ends on the char before selectEnd 03216 if (ec != 0) { 03217 --ec; 03218 } else if (el > 0) { 03219 --el; 03220 ec = m_buffer->plainLine(el)->length() - 1; 03221 } 03222 03223 const int startCommentLen = startComment.length(); 03224 const int endCommentLen = endComment.length(); 03225 03226 // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$2/ 03227 03228 bool remove = nextNonSpaceCharPos(sl, sc) 03229 && m_buffer->plainLine(sl)->matchesAt(sc, startComment) 03230 && previousNonSpaceCharPos(el, ec) 03231 && ( (ec - endCommentLen + 1) >= 0 ) 03232 && m_buffer->plainLine(el)->matchesAt(ec - endCommentLen + 1, endComment); 03233 03234 if (remove) { 03235 editStart(); 03236 03237 removeText (KTextEditor::Range(el, ec - endCommentLen + 1, el, ec + 1)); 03238 removeText (KTextEditor::Range(sl, sc, sl, sc + startCommentLen)); 03239 03240 editEnd (); 03241 // selection automatically updated (MovingRange) 03242 } 03243 03244 return remove; 03245 } 03246 03247 bool KateDocument::removeStartStopCommentFromRegion(const KTextEditor::Cursor &start,const KTextEditor::Cursor &end,int attrib) 03248 { 03249 const QString startComment = highlight()->getCommentStart( attrib ); 03250 const QString endComment = highlight()->getCommentEnd( attrib ); 03251 const int startCommentLen = startComment.length(); 03252 const int endCommentLen = endComment.length(); 03253 03254 const bool remove = m_buffer->plainLine(start.line())->matchesAt(start.column(), startComment) 03255 && m_buffer->plainLine(end.line())->matchesAt(end.column() - endCommentLen , endComment); 03256 if (remove) { 03257 editStart(); 03258 removeText(KTextEditor::Range(end.line(), end.column() - endCommentLen, end.line(), end.column())); 03259 removeText(KTextEditor::Range(start, startCommentLen)); 03260 editEnd(); 03261 } 03262 return remove; 03263 } 03264 03265 /* 03266 Remove from the beginning of each line of the 03267 selection a start comment line mark. 03268 */ 03269 bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib ) 03270 { 03271 const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib ); 03272 const QString longCommentMark = shortCommentMark + ' '; 03273 03274 int sl = view->selectionRange().start().line(); 03275 int el = view->selectionRange().end().line(); 03276 03277 if ((view->selectionRange().end().column() == 0) && (el > 0)) 03278 { 03279 el--; 03280 } 03281 03282 bool removed = false; 03283 03284 editStart(); 03285 03286 // For each line of the selection 03287 for (int z = el; z >= sl; z--) 03288 { 03289 // Try to remove the long comment mark first 03290 removed = (removeStringFromBeginning(z, longCommentMark) 03291 || removeStringFromBeginning(z, shortCommentMark) 03292 || removed); 03293 } 03294 03295 editEnd(); 03296 // selection automatically updated (MovingRange) 03297 03298 return removed; 03299 } 03300 03301 /* 03302 Comment or uncomment the selection or the current 03303 line if there is no selection. 03304 */ 03305 void KateDocument::comment( KateView *v, uint line,uint column, int change) 03306 { 03307 bool hassel = v->selection(); 03308 int l = line; 03309 int c = 0; 03310 03311 if ( hassel ) 03312 { 03313 l = v->selectionRange().start().line(); 03314 c = v->selectionRange().start().column(); 03315 } 03316 03317 int startAttrib = 0; 03318 Kate::TextLine ln = kateTextLine( line ); 03319 03320 if ( c < ln->length() ) 03321 startAttrib = ln->attribute( c ); 03322 else if ( !ln->ctxArray().isEmpty() ) 03323 startAttrib = highlight()->attribute( ln->ctxArray().last() ); 03324 03325 bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty()); 03326 bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty()) 03327 && !(highlight()->getCommentEnd( startAttrib ).isEmpty()) ); 03328 03329 bool removed = false; 03330 03331 if (change > 0) // comment 03332 { 03333 if ( !hassel ) 03334 { 03335 if ( hasStartLineCommentMark ) 03336 addStartLineCommentToSingleLine( line, startAttrib ); 03337 else if ( hasStartStopCommentMark ) 03338 addStartStopCommentToSingleLine( line, startAttrib ); 03339 } 03340 else 03341 { 03342 // anders: prefer single line comment to avoid nesting probs 03343 // If the selection starts after first char in the first line 03344 // or ends before the last char of the last line, we may use 03345 // multiline comment markers. 03346 // TODO We should try to detect nesting. 03347 // - if selection ends at col 0, most likely she wanted that 03348 // line ignored 03349 if ( hasStartStopCommentMark && 03350 ( !hasStartLineCommentMark || ( 03351 ( v->selectionRange().start().column() > m_buffer->plainLine( v->selectionRange().start().line() )->firstChar() ) || 03352 ( v->selectionRange().end().column() < ((int)m_buffer->plainLine( v->selectionRange().end().line() )->length()) ) 03353 ) ) ) 03354 addStartStopCommentToSelection( v, startAttrib ); 03355 else if ( hasStartLineCommentMark ) 03356 addStartLineCommentToSelection( v, startAttrib ); 03357 } 03358 } 03359 else // uncomment 03360 { 03361 if ( !hassel ) 03362 { 03363 removed = ( hasStartLineCommentMark 03364 && removeStartLineCommentFromSingleLine( line, startAttrib ) ) 03365 || ( hasStartStopCommentMark 03366 && removeStartStopCommentFromSingleLine( line, startAttrib ) ); 03367 if ((!removed) && foldingTree()) { 03368 kDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)"; 03369 int commentRegion=(highlight()->commentRegion(startAttrib)); 03370 if (commentRegion){ 03371 KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column); 03372 if (n) { 03373 KTextEditor::Cursor start,end; 03374 if ((n->nodeType()==(int)commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) { 03375 kDebug(13020)<<"Enclosing region found:"<<start.column()<<"/"<<start.line()<<"-"<<end.column()<<"/"<<end.line(); 03376 removed = removeStartStopCommentFromRegion(start,end,startAttrib); 03377 } else { 03378 kDebug(13020)<<"Enclosing region found, but not valid"; 03379 kDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion; 03380 } 03381 //perhaps nested regions should be hadled here too... 03382 } else kDebug(13020)<<"No enclosing region found"; 03383 } else kDebug(13020)<<"No comment region specified for current hl"; 03384 } 03385 } 03386 else 03387 { 03388 // anders: this seems like it will work with above changes :) 03389 removed = ( hasStartStopCommentMark 03390 && removeStartStopCommentFromSelection( v, startAttrib ) ) 03391 || ( hasStartLineCommentMark 03392 && removeStartLineCommentFromSelection( v, startAttrib ) ); 03393 } 03394 03395 // recursive call for toggle comment 03396 if (!removed && change == 0) 03397 comment(v, line, column, 1); 03398 } 03399 } 03400 03401 void KateDocument::transform( KateView *v, const KTextEditor::Cursor &c, 03402 KateDocument::TextTransform t ) 03403 { 03404 if ( v->selection() ) 03405 { 03406 editStart(); 03407 03408 // remember cursor 03409 KTextEditor::Cursor cursor = c; 03410 03411 // cache the selection and cursor, so we can be sure to restore. 03412 KTextEditor::Range selection = v->selectionRange(); 03413 03414 KTextEditor::Range range(selection.start(), 0); 03415 while ( range.start().line() <= selection.end().line() ) 03416 { 03417 int start = 0; 03418 int end = lineLength( range.start().line() ); 03419 03420 if (range.start().line() == selection.start().line() || v->blockSelectionMode()) 03421 start = selection.start().column(); 03422 03423 if (range.start().line() == selection.end().line() || v->blockSelectionMode()) 03424 end = selection.end().column(); 03425 03426 if ( start > end ) 03427 { 03428 int swapCol = start; 03429 start = end; 03430 end = swapCol; 03431 } 03432 range.start().setColumn( start ); 03433 range.end().setColumn( end ); 03434 03435 QString s = text( range ); 03436 QString old = s; 03437 03438 if ( t == Uppercase ) 03439 s = s.toUpper(); 03440 else if ( t == Lowercase ) 03441 s = s.toLower(); 03442 else // Capitalize 03443 { 03444 Kate::TextLine l = m_buffer->plainLine( range.start().line() ); 03445 int p ( 0 ); 03446 while( p < s.length() ) 03447 { 03448 // If bol or the character before is not in a word, up this one: 03449 // 1. if both start and p is 0, upper char. 03450 // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper 03451 // 3. if p-1 is not in a word, upper. 03452 if ( ( ! range.start().column() && ! p ) || 03453 ( ( range.start().line() == selection.start().line() || v->blockSelectionMode() ) && 03454 ! p && ! highlight()->isInWord( l->at( range.start().column() - 1 )) ) || 03455 ( p && ! highlight()->isInWord( s.at( p-1 ) ) ) 03456 ) { 03457 s[p] = s.at(p).toUpper(); 03458 } 03459 p++; 03460 } 03461 } 03462 03463 if ( s != old ) 03464 { 03465 removeText( range ); 03466 insertText( range.start(), s ); 03467 } 03468 03469 range.setBothLines(range.start().line() + 1); 03470 } 03471 03472 editEnd(); 03473 03474 // restore selection & cursor 03475 v->setSelection( selection ); 03476 v->setCursorPosition( c ); 03477 03478 } else { // no selection 03479 editStart(); 03480 03481 // get cursor 03482 KTextEditor::Cursor cursor = c; 03483 03484 QString old = text( KTextEditor::Range(cursor, 1) ); 03485 QString s; 03486 switch ( t ) { 03487 case Uppercase: 03488 s = old.toUpper(); 03489 break; 03490 case Lowercase: 03491 s = old.toLower(); 03492 break; 03493 case Capitalize: 03494 { 03495 Kate::TextLine l = m_buffer->plainLine( cursor.line() ); 03496 while ( cursor.column() > 0 && highlight()->isInWord( l->at( cursor.column() - 1 ), l->attribute( cursor.column() - 1 ) ) ) 03497 cursor.setColumn(cursor.column() - 1); 03498 old = text( KTextEditor::Range(cursor, 1) ); 03499 s = old.toUpper(); 03500 } 03501 break; 03502 default: 03503 break; 03504 } 03505 03506 removeText( KTextEditor::Range(cursor, 1) ); 03507 insertText( cursor, s ); 03508 03509 editEnd(); 03510 } 03511 03512 } 03513 03514 void KateDocument::joinLines( uint first, uint last ) 03515 { 03516 // if ( first == last ) last += 1; 03517 editStart(); 03518 int line( first ); 03519 while ( first < last ) 03520 { 03521 // Normalize the whitespace in the joined lines by making sure there's 03522 // always exactly one space between the joined lines 03523 // This cannot be done in editUnwrapLine, because we do NOT want this 03524 // behavior when deleting from the start of a line, just when explicitly 03525 // calling the join command 03526 Kate::TextLine l = kateTextLine( line ); 03527 Kate::TextLine tl = kateTextLine( line + 1 ); 03528 03529 if ( !l || !tl ) 03530 { 03531 editEnd(); 03532 return; 03533 } 03534 03535 int pos = tl->firstChar(); 03536 if ( pos >= 0 ) 03537 { 03538 if (pos != 0) 03539 editRemoveText( line + 1, 0, pos ); 03540 if ( !( l->length() == 0 || l->at( l->length() - 1 ).isSpace() ) ) 03541 editInsertText( line + 1, 0, " " ); 03542 } 03543 else 03544 { 03545 // Just remove the whitespace and let Kate handle the rest 03546 editRemoveText( line + 1, 0, tl->length() ); 03547 } 03548 03549 editUnWrapLine( line ); 03550 first++; 03551 } 03552 editEnd(); 03553 } 03554 03555 QString KateDocument::getWord( const KTextEditor::Cursor& cursor ) 03556 { 03557 int start, end, len; 03558 03559 Kate::TextLine textLine = m_buffer->plainLine(cursor.line()); 03560 len = textLine->length(); 03561 start = end = cursor.column(); 03562 if (start > len) // Probably because of non-wrapping cursor mode. 03563 return QString(); 03564 03565 while (start > 0 && highlight()->isInWord(textLine->at(start - 1), textLine->attribute(start - 1))) start--; 03566 while (end < len && highlight()->isInWord(textLine->at(end), textLine->attribute(end))) end++; 03567 len = end - start; 03568 return textLine->string().mid(start, len); 03569 } 03570 03571 void KateDocument::tagLines(int start, int end) 03572 { 03573 foreach(KateView *view,m_views) 03574 view->tagLines (start, end, true); 03575 } 03576 03577 void KateDocument::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end) 03578 { 03579 // May need to switch start/end cols if in block selection mode 03580 /* if (blockSelectionMode() && start.column() > end.column()) { 03581 int sc = start.column(); 03582 start.setColumn(end.column()); 03583 end.setColumn(sc); 03584 } 03585 */ 03586 foreach (KateView* view, m_views) 03587 view->tagLines(start, end, true); 03588 } 03589 03590 void KateDocument::repaintViews(bool paintOnlyDirty) 03591 { 03592 foreach(KateView *view,m_views) 03593 view->repaintText(paintOnlyDirty); 03594 } 03595 03596 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; } 03597 inline bool isEndBracket ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; } 03598 inline bool isBracket ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); } 03599 03600 /* 03601 Bracket matching uses the following algorithm: 03602 If in overwrite mode, match the bracket currently underneath the cursor. 03603 Otherwise, if the character to the left is a bracket, 03604 match it. Otherwise if the character to the right of the cursor is a 03605 bracket, match it. Otherwise, don't match anything. 03606 */ 03607 void KateDocument::newBracketMark( const KTextEditor::Cursor& cursor, KTextEditor::Range& bm, int maxLines ) 03608 { 03609 // search from cursor for brackets 03610 KTextEditor::Range range (cursor, cursor); 03611 03612 // if match found, remember the range 03613 if( findMatchingBracket( range, maxLines ) ) { 03614 bm = range; 03615 return; 03616 } 03617 03618 // else, invalidate, if still valid 03619 if (bm.isValid()) 03620 bm = KTextEditor::Range::invalid(); 03621 } 03622 03623 bool KateDocument::findMatchingBracket( KTextEditor::Range& range, int maxLines ) 03624 { 03625 Kate::TextLine textLine = m_buffer->plainLine( range.start().line() ); 03626 if( !textLine ) 03627 return false; 03628 03629 QChar right = textLine->at( range.start().column() ); 03630 QChar left = textLine->at( range.start().column() - 1 ); 03631 QChar bracket; 03632 03633 if ( config()->ovr() ) { 03634 if( isBracket( right ) ) { 03635 bracket = right; 03636 } else { 03637 return false; 03638 } 03639 } else if ( isBracket( left ) ) { 03640 range.start().setColumn(range.start().column() - 1); 03641 bracket = left; 03642 } else if ( isBracket( right ) ) { 03643 bracket = right; 03644 } else { 03645 return false; 03646 } 03647 03648 QChar opposite; 03649 03650 switch( bracket.toAscii() ) { 03651 case '{': opposite = '}'; break; 03652 case '}': opposite = '{'; break; 03653 case '[': opposite = ']'; break; 03654 case ']': opposite = '['; break; 03655 case '(': opposite = ')'; break; 03656 case ')': opposite = '('; break; 03657 default: return false; 03658 } 03659 03660 const int searchDir = isStartBracket( bracket ) ? 1 : -1; 03661 uint nesting = 0; 03662 03663 int minLine = qMax( range.start().line() - maxLines, 0 ); 03664 int maxLine = qMin( range.start().line() + maxLines, documentEnd().line() ); 03665 03666 range.end() = range.start(); 03667 QScopedPointer<KTextEditor::MovingCursor> cursor(newMovingCursor(range.start())); 03668 int validAttr = kateTextLine(cursor->line())->attribute(cursor->column()); 03669 03670 while( cursor->line() >= minLine && cursor->line() <= maxLine ) { 03671 03672 if (!cursor->move(searchDir)) 03673 return false; 03674 03675 if (kateTextLine(cursor->line())->attribute(cursor->column()) == validAttr ) 03676 { 03677 /* Check for match */ 03678 QChar c = character(cursor->toCursor()); 03679 if( c == bracket ) { 03680 nesting++; 03681 } else if( c == opposite ) { 03682 if( nesting == 0 ) { 03683 if (searchDir > 0) // forward 03684 range.end() = cursor->toCursor(); 03685 else 03686 range.start() = cursor->toCursor(); 03687 return true; 03688 } 03689 nesting--; 03690 } 03691 } 03692 } 03693 03694 return false; 03695 } 03696 03697 void KateDocument::setDocName (QString name ) 03698 { 03702 if ( name == m_docName ) 03703 return; 03704 03708 if ( !name.isEmpty() ) { 03709 m_docName = name; 03710 emit documentNameChanged (this); 03711 return; 03712 } 03713 03714 // if the name is set, and starts with FILENAME, it should not be changed! 03715 if ( ! url().isEmpty() 03716 && (m_docName == url().fileName() || m_docName.startsWith (url().fileName() + " (") ) ) return; 03717 03718 int count = -1; 03719 03720 foreach(KateDocument* doc, KateGlobal::self()->kateDocuments()) 03721 { 03722 if ( (doc != this) && (doc->url().fileName() == url().fileName()) ) 03723 if ( doc->m_docNameNumber > count ) 03724 count = doc->m_docNameNumber; 03725 } 03726 03727 m_docNameNumber = count + 1; 03728 03729 QString oldName = m_docName; 03730 m_docName = url().fileName(); 03731 03732 if (m_docName.isEmpty()) 03733 m_docName = i18n ("Untitled"); 03734 03735 if (m_docNameNumber > 0) 03736 m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1); 03737 03741 if (oldName != m_docName) 03742 emit documentNameChanged (this); 03743 } 03744 03745 void KateDocument::slotModifiedOnDisk( KTextEditor::View * /*v*/ ) 03746 { 03747 if ( m_isasking < 0 ) 03748 { 03749 m_isasking = 0; 03750 return; 03751 } 03752 03753 if ( !m_fileChangedDialogsActivated || m_isasking ) 03754 return; 03755 03756 if (m_modOnHd && !url().isEmpty()) 03757 { 03758 m_isasking = 1; 03759 03760 QWidget *parentWidget(dialogParent()); 03761 03762 KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), parentWidget ); 03763 switch ( p.exec() ) 03764 { 03765 case KateModOnHdPrompt::Save: 03766 { 03767 m_modOnHd = false; 03768 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(), 03769 url().url(),QString(),parentWidget,i18n("Save File")); 03770 03771 kDebug(13020)<<"got "<<res.URLs.count()<<" URLs"; 03772 if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first(), parentWidget ) ) 03773 { 03774 setEncoding( res.encoding ); 03775 03776 if( ! saveAs( res.URLs.first() ) ) 03777 { 03778 KMessageBox::error( parentWidget, i18n("Save failed") ); 03779 m_modOnHd = true; 03780 } 03781 else 03782 emit modifiedOnDisk( this, false, OnDiskUnmodified ); 03783 } 03784 else // the save as dialog was canceled, we are still modified on disk 03785 { 03786 m_modOnHd = true; 03787 } 03788 03789 m_isasking = 0; 03790 break; 03791 } 03792 03793 case KateModOnHdPrompt::Reload: 03794 m_modOnHd = false; 03795 emit modifiedOnDisk( this, false, OnDiskUnmodified ); 03796 documentReload(); 03797 m_isasking = 0; 03798 break; 03799 03800 case KateModOnHdPrompt::Ignore: 03801 m_modOnHd = false; 03802 emit modifiedOnDisk( this, false, OnDiskUnmodified ); 03803 m_isasking = 0; 03804 break; 03805 03806 case KateModOnHdPrompt::Overwrite: 03807 m_modOnHd = false; 03808 emit modifiedOnDisk( this, false, OnDiskUnmodified ); 03809 m_isasking = 0; 03810 save(); 03811 break; 03812 03813 default: // Delay/cancel: ignore next focus event 03814 m_isasking = -1; 03815 } 03816 } 03817 } 03818 03819 void KateDocument::setModifiedOnDisk( ModifiedOnDiskReason reason ) 03820 { 03821 m_modOnHdReason = reason; 03822 m_modOnHd = (reason != OnDiskUnmodified); 03823 emit modifiedOnDisk( this, (reason != OnDiskUnmodified), reason ); 03824 } 03825 03826 class KateDocumentTmpMark 03827 { 03828 public: 03829 QString line; 03830 KTextEditor::Mark mark; 03831 }; 03832 03833 void KateDocument::setModifiedOnDiskWarning (bool on) 03834 { 03835 m_fileChangedDialogsActivated = on; 03836 } 03837 03838 bool KateDocument::documentReload() 03839 { 03840 if ( !url().isEmpty() ) 03841 { 03842 if (m_modOnHd && m_fileChangedDialogsActivated) 03843 { 03844 QWidget *parentWidget(dialogParent()); 03845 03846 int i = KMessageBox::warningYesNoCancel 03847 (parentWidget, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"), 03848 i18n("File Was Changed on Disk"), KGuiItem(i18n("&Reload File")), KGuiItem(i18n("&Ignore Changes"))); 03849 03850 if ( i != KMessageBox::Yes) 03851 { 03852 if (i == KMessageBox::No) 03853 { 03854 m_modOnHd = false; 03855 m_modOnHdReason = OnDiskUnmodified; 03856 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); 03857 } 03858 03859 return false; 03860 } 03861 } 03862 03863 emit aboutToReload(this); 03864 03865 QList<KateDocumentTmpMark> tmp; 03866 03867 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) 03868 { 03869 KateDocumentTmpMark m; 03870 03871 m.line = line (i.value()->line); 03872 m.mark = *i.value(); 03873 03874 tmp.append (m); 03875 } 03876 03877 const QString oldMode = mode (); 03878 const bool byUser = m_fileTypeSetByUser; 03879 const QString hl_mode = highlightingMode (); 03880 KTextEditor::View* oldActiveView = activeView(); 03881 03882 m_storedVariables.clear(); 03883 03884 // save cursor positions for all views 03885 QVector<KTextEditor::Cursor> cursorPositions; 03886 cursorPositions.reserve(m_views.size()); 03887 foreach (KateView *v, m_views) 03888 cursorPositions.append( v->cursorPosition() ); 03889 03890 m_reloading = true; 03891 KateDocument::openUrl( url() ); 03892 m_reloading = false; 03893 03894 // restore cursor positions for all views 03895 QLinkedList<KateView*>::iterator it = m_views.begin(); 03896 for(int i = 0; i < m_views.size(); ++i, ++it) { 03897 setActiveView(*it); 03898 (*it)->setCursorPositionInternal( cursorPositions.at(i), m_config->tabWidth(), false ); 03899 if ((*it)->isVisible()) { 03900 (*it)->repaintText(false); 03901 } 03902 } 03903 setActiveView(oldActiveView); 03904 03905 for (int z=0; z < tmp.size(); z++) 03906 { 03907 if (z < (int)lines()) 03908 { 03909 if (line(tmp.at(z).mark.line) == tmp.at(z).line) 03910 setMark (tmp.at(z).mark.line, tmp.at(z).mark.type); 03911 } 03912 } 03913 03914 if (byUser) 03915 setMode (oldMode); 03916 setHighlightingMode (hl_mode); 03917 03918 emit reloaded(this); 03919 03920 return true; 03921 } 03922 03923 return false; 03924 } 03925 03926 bool KateDocument::documentSave() 03927 { 03928 if( !url().isValid() || !isReadWrite() ) 03929 return documentSaveAs(); 03930 03931 return save(); 03932 } 03933 03934 bool KateDocument::documentSaveAs() 03935 { 03936 QWidget *parentWidget(dialogParent()); 03937 03938 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(), 03939 url().url(),QString(),parentWidget,i18n("Save File")); 03940 03941 if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) ) 03942 return false; 03943 03944 setEncoding( res.encoding ); 03945 03946 return saveAs( res.URLs.first() ); 03947 } 03948 03949 void KateDocument::setWordWrap (bool on) 03950 { 03951 config()->setWordWrap (on); 03952 } 03953 03954 bool KateDocument::wordWrap () const 03955 { 03956 return config()->wordWrap (); 03957 } 03958 03959 void KateDocument::setWordWrapAt (uint col) 03960 { 03961 config()->setWordWrapAt (col); 03962 } 03963 03964 unsigned int KateDocument::wordWrapAt () const 03965 { 03966 return config()->wordWrapAt (); 03967 } 03968 03969 void KateDocument::setPageUpDownMovesCursor (bool on) 03970 { 03971 config()->setPageUpDownMovesCursor (on); 03972 } 03973 03974 bool KateDocument::pageUpDownMovesCursor () const 03975 { 03976 return config()->pageUpDownMovesCursor (); 03977 } 03978 03979 void KateDocument::dumpRegionTree() 03980 { 03981 m_buffer->foldingTree()->debugDump(); 03982 } 03983 //END 03984 03985 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line) 03986 { 03987 m_buffer->lineInfo(info,line); 03988 } 03989 03990 KateCodeFoldingTree *KateDocument::foldingTree () 03991 { 03992 return m_buffer->foldingTree(); 03993 } 03994 03995 bool KateDocument::setEncoding (const QString &e) 03996 { 03997 return m_config->setEncoding(e); 03998 } 03999 04000 const QString &KateDocument::encoding() const 04001 { 04002 return m_config->encoding(); 04003 } 04004 04005 void KateDocument::updateConfig () 04006 { 04007 m_undoManager->updateConfig (); 04008 04009 // switch indenter if needed and update config.... 04010 m_indenter->setMode (m_config->indentationMode()); 04011 m_indenter->updateConfig(); 04012 04013 // set tab width there, too 04014 m_buffer->setTabWidth (config()->tabWidth()); 04015 04016 // update all views, does tagAll and updateView... 04017 foreach (KateView * view, m_views) 04018 view->updateDocumentConfig (); 04019 04020 // update on-the-fly spell checking as spell checking defaults might have changes 04021 if(m_onTheFlyChecker) { 04022 m_onTheFlyChecker->updateConfig(); 04023 } 04024 04025 emit configChanged(); 04026 } 04027 04028 //BEGIN Variable reader 04029 // "local variable" feature by anders, 2003 04030 /* TODO 04031 add config options (how many lines to read, on/off) 04032 add interface for plugins/apps to set/get variables 04033 add view stuff 04034 */ 04035 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)"); 04036 QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)"); 04037 QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)"); 04038 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)"); 04039 04040 void KateDocument::readVariables(bool onlyViewAndRenderer) 04041 { 04042 if (!onlyViewAndRenderer) 04043 m_config->configStart(); 04044 04045 // views! 04046 KateView *v; 04047 foreach (v,m_views) 04048 { 04049 v->config()->configStart(); 04050 v->renderer()->config()->configStart(); 04051 } 04052 // read a number of lines in the top/bottom of the document 04053 for (int i=0; i < qMin( 9, lines() ); ++i ) 04054 { 04055 readVariableLine( line( i ), onlyViewAndRenderer ); 04056 } 04057 if ( lines() > 10 ) 04058 { 04059 for ( int i = qMax( 10, lines() - 10); i < lines(); i++ ) 04060 { 04061 readVariableLine( line( i ), onlyViewAndRenderer ); 04062 } 04063 } 04064 04065 if (!onlyViewAndRenderer) 04066 m_config->configEnd(); 04067 04068 foreach (v,m_views) 04069 { 04070 v->config()->configEnd(); 04071 v->renderer()->config()->configEnd(); 04072 } 04073 } 04074 04075 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer ) 04076 { 04077 // simple check first, no regex 04078 // no kate inside, no vars, simple... 04079 if (!t.contains("kate")) 04080 return; 04081 04082 // found vars, if any 04083 QString s; 04084 04085 // now, try first the normal ones 04086 if ( kvLine.indexIn( t ) > -1 ) 04087 { 04088 s = kvLine.cap(1); 04089 04090 kDebug (13020) << "normal variable line kate: matched: " << s; 04091 } 04092 else if (kvLineWildcard.indexIn( t ) > -1) // regex given 04093 { 04094 const QStringList wildcards (kvLineWildcard.cap(1).split (';', QString::SkipEmptyParts)); 04095 const QString nameOfFile = url().fileName(); 04096 04097 bool found = false; 04098 foreach(const QString& pattern, wildcards) 04099 { 04100 QRegExp wildcard (pattern, Qt::CaseSensitive, QRegExp::Wildcard); 04101 04102 found = wildcard.exactMatch (nameOfFile); 04103 } 04104 04105 // nothing usable found... 04106 if (!found) 04107 return; 04108 04109 s = kvLineWildcard.cap(2); 04110 04111 kDebug (13020) << "guarded variable line kate-wildcard: matched: " << s; 04112 } 04113 else if (kvLineMime.indexIn( t ) > -1) // mime-type given 04114 { 04115 const QStringList types (kvLineMime.cap(1).split (';', QString::SkipEmptyParts)); 04116 04117 // no matching type found 04118 if (!types.contains (mimeType ())) 04119 return; 04120 04121 s = kvLineMime.cap(2); 04122 04123 kDebug (13020) << "guarded variable line kate-mimetype: matched: " << s; 04124 } 04125 else // nothing found 04126 { 04127 return; 04128 } 04129 04130 QStringList vvl; // view variable names 04131 vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators" 04132 << "line-numbers" << "icon-border" << "folding-markers" 04133 << "bookmark-sorting" << "auto-center-lines" 04134 << "icon-bar-color" 04135 // renderer 04136 << "background-color" << "selection-color" 04137 << "current-line-color" << "bracket-highlight-color" 04138 << "word-wrap-marker-color" 04139 << "font" << "font-size" << "scheme"; 04140 int spaceIndent = -1; // for backward compatibility; see below 04141 bool replaceTabsSet = false; 04142 int p( 0 ); 04143 04144 QString var, val; 04145 while ( (p = kvVar.indexIn( s, p )) > -1 ) 04146 { 04147 p += kvVar.matchedLength(); 04148 var = kvVar.cap( 1 ); 04149 val = kvVar.cap( 2 ).trimmed(); 04150 bool state; // store booleans here 04151 int n; // store ints here 04152 04153 // only apply view & renderer config stuff 04154 if (onlyViewAndRenderer) 04155 { 04156 if ( vvl.contains( var ) ) // FIXME define above 04157 setViewVariable( var, val ); 04158 } 04159 else 04160 { 04161 // BOOL SETTINGS 04162 if ( var == "word-wrap" && checkBoolValue( val, &state ) ) 04163 setWordWrap( state ); // ??? FIXME CHECK 04164 // KateConfig::configFlags 04165 // FIXME should this be optimized to only a few calls? how? 04166 else if ( var == "backspace-indents" && checkBoolValue( val, &state ) ) 04167 m_config->setBackspaceIndents( state ); 04168 else if ( var == "replace-tabs" && checkBoolValue( val, &state ) ) 04169 { 04170 m_config->setReplaceTabsDyn( state ); 04171 replaceTabsSet = true; // for backward compatibility; see below 04172 } 04173 else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) ) 04174 m_config->setRemoveTrailingDyn( state ); 04175 else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) ) 04176 m_config->setWrapCursor( state ); 04177 else if ( var == "auto-brackets" && checkBoolValue( val, &state ) ) 04178 m_config->setAutoBrackets( state ); 04179 else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) ) 04180 m_config->setOvr( state ); 04181 else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) ) 04182 m_config->setKeepExtraSpaces( state ); 04183 else if ( var == "tab-indents" && checkBoolValue( val, &state ) ) 04184 m_config->setTabIndents( state ); 04185 else if ( var == "show-tabs" && checkBoolValue( val, &state ) ) 04186 m_config->setShowTabs( state ); 04187 else if ( var == "show-trailing-spaces" && checkBoolValue( val, &state ) ) 04188 m_config->setShowSpaces( state ); 04189 else if ( var == "space-indent" && checkBoolValue( val, &state ) ) 04190 { 04191 // this is for backward compatibility; see below 04192 spaceIndent = state; 04193 } 04194 else if ( var == "smart-home" && checkBoolValue( val, &state ) ) 04195 m_config->setSmartHome( state ); 04196 else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) ) 04197 m_config->setRemoveSpaces( state ); 04198 04199 // INTEGER SETTINGS 04200 else if ( var == "tab-width" && checkIntValue( val, &n ) ) 04201 m_config->setTabWidth( n ); 04202 else if ( var == "indent-width" && checkIntValue( val, &n ) ) 04203 m_config->setIndentationWidth( n ); 04204 else if ( var == "indent-mode" ) 04205 { 04206 m_config->setIndentationMode( val ); 04207 } 04208 else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 ) // uint, but hard word wrap at 0 will be no fun ;) 04209 m_config->setWordWrapAt( n ); 04210 04211 // STRING SETTINGS 04212 else if ( var == "eol" || var == "end-of-line" ) 04213 { 04214 QStringList l; 04215 l << "unix" << "dos" << "mac"; 04216 if ( (n = l.indexOf( val.toLower() )) != -1 ) 04217 m_config->setEol( n ); 04218 } 04219 else if (var == "bom" || var =="byte-order-marker") 04220 { 04221 if (checkBoolValue(val,&state)) { 04222 m_config->setBom(state); 04223 } 04224 } 04225 else if ( var == "encoding" ) 04226 m_config->setEncoding( val ); 04227 else if (var == "presave-postdialog") 04228 setPreSavePostDialogFilterChecks(val.split(',')); 04229 else if (var == "postsave") 04230 setPostSaveFilterChecks(val.split(',')); 04231 else if (var == "postload") 04232 setPostLoadFilterChecks(val.split(',')); 04233 else if ( var == "syntax" || var == "hl" ) 04234 { 04235 setHighlightingMode( val ); 04236 } 04237 else if ( var == "mode" ) 04238 { 04239 setMode( val ); 04240 } 04241 else if ( var == "default-dictionary" ) 04242 { 04243 setDefaultDictionary( val ); 04244 } 04245 else if ( var == "automatic-spell-checking" && checkBoolValue( val, &state ) ) 04246 { 04247 onTheFlySpellCheckingEnabled( state ); 04248 } 04249 04250 // VIEW SETTINGS 04251 else if ( vvl.contains( var ) ) 04252 setViewVariable( var, val ); 04253 else 04254 { 04255 m_storedVariables.insert( var, val ); 04256 emit variableChanged( this, var, val ); 04257 } 04258 } 04259 } 04260 04261 // Backward compatibility 04262 // If space-indent was set, but replace-tabs was not set, we assume 04263 // that the user wants to replace tabulators and set that flag. 04264 // If both were set, replace-tabs has precedence. 04265 // At this point spaceIndent is -1 if it was never set, 04266 // 0 if it was set to off, and 1 if it was set to on. 04267 // Note that if onlyViewAndRenderer was requested, spaceIndent is -1. 04268 if ( !replaceTabsSet && spaceIndent >= 0 ) 04269 { 04270 m_config->setReplaceTabsDyn( spaceIndent > 0 ); 04271 } 04272 } 04273 04274 void KateDocument::setViewVariable( QString var, QString val ) 04275 { 04276 KateView *v; 04277 bool state; 04278 int n; 04279 QColor c; 04280 foreach (v,m_views) 04281 { 04282 if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) ) 04283 v->config()->setDynWordWrap( state ); 04284 else if ( var == "persistent-selection" && checkBoolValue( val, &state ) ) 04285 v->config()->setPersistentSelection( state ); 04286 else if ( var == "block-selection" && checkBoolValue( val, &state ) ) 04287 v->setBlockSelectionMode( state ); 04288 //else if ( var = "dynamic-word-wrap-indicators" ) 04289 else if ( var == "line-numbers" && checkBoolValue( val, &state ) ) 04290 v->config()->setLineNumbers( state ); 04291 else if (var == "icon-border" && checkBoolValue( val, &state ) ) 04292 v->config()->setIconBar( state ); 04293 else if (var == "folding-markers" && checkBoolValue( val, &state ) ) 04294 v->config()->setFoldingBar( state ); 04295 else if ( var == "auto-center-lines" && checkIntValue( val, &n ) ) 04296 v->config()->setAutoCenterLines( n ); // FIXME uint, > N ?? 04297 else if ( var == "icon-bar-color" && checkColorValue( val, c ) ) 04298 v->renderer()->config()->setIconBarColor( c ); 04299 // RENDERER 04300 else if ( var == "background-color" && checkColorValue( val, c ) ) 04301 v->renderer()->config()->setBackgroundColor( c ); 04302 else if ( var == "selection-color" && checkColorValue( val, c ) ) 04303 v->renderer()->config()->setSelectionColor( c ); 04304 else if ( var == "current-line-color" && checkColorValue( val, c ) ) 04305 v->renderer()->config()->setHighlightedLineColor( c ); 04306 else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) ) 04307 v->renderer()->config()->setHighlightedBracketColor( c ); 04308 else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) ) 04309 v->renderer()->config()->setWordWrapMarkerColor( c ); 04310 else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) ) 04311 { 04312 QFont _f( v->renderer()->config()->font() ); 04313 04314 if ( var == "font" ) 04315 { 04316 _f.setFamily( val ); 04317 _f.setFixedPitch( QFont( val ).fixedPitch() ); 04318 } 04319 else 04320 _f.setPointSize( n ); 04321 04322 v->renderer()->config()->setFont( _f ); 04323 } 04324 else if ( var == "scheme" ) 04325 { 04326 v->renderer()->config()->setSchema( val ); 04327 } 04328 } 04329 } 04330 04331 bool KateDocument::checkBoolValue( QString val, bool *result ) 04332 { 04333 val = val.trimmed().toLower(); 04334 QStringList l; 04335 l << "1" << "on" << "true"; 04336 if ( l.contains( val ) ) 04337 { 04338 *result = true; 04339 return true; 04340 } 04341 l.clear(); 04342 l << "0" << "off" << "false"; 04343 if ( l.contains( val ) ) 04344 { 04345 *result = false; 04346 return true; 04347 } 04348 return false; 04349 } 04350 04351 bool KateDocument::checkIntValue( QString val, int *result ) 04352 { 04353 bool ret( false ); 04354 *result = val.toInt( &ret ); 04355 return ret; 04356 } 04357 04358 bool KateDocument::checkColorValue( QString val, QColor &c ) 04359 { 04360 c.setNamedColor( val ); 04361 return c.isValid(); 04362 } 04363 04364 // KTextEditor::variable 04365 QString KateDocument::variable( const QString &name ) const 04366 { 04367 return m_storedVariables.value(name, QString()); 04368 } 04369 04370 //END 04371 04372 void KateDocument::slotModOnHdDirty (const QString &path) 04373 { 04374 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskModified)) 04375 { 04376 // compare md5 with the one we have (if we have one) 04377 if ( ! m_digest.isEmpty() ) 04378 { 04379 QByteArray tmp; 04380 if ( createDigest( tmp ) && tmp == m_digest ) 04381 return; 04382 } 04383 04384 m_modOnHd = true; 04385 m_modOnHdReason = OnDiskModified; 04386 04387 // reenable dialog if not running atm 04388 if (m_isasking == -1) 04389 m_isasking = false; 04390 04391 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); 04392 } 04393 } 04394 04395 void KateDocument::slotModOnHdCreated (const QString &path) 04396 { 04397 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskCreated)) 04398 { 04399 m_modOnHd = true; 04400 m_modOnHdReason = OnDiskCreated; 04401 04402 // reenable dialog if not running atm 04403 if (m_isasking == -1) 04404 m_isasking = false; 04405 04406 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); 04407 } 04408 } 04409 04410 void KateDocument::slotModOnHdDeleted (const QString &path) 04411 { 04412 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskDeleted)) 04413 { 04414 m_modOnHd = true; 04415 m_modOnHdReason = OnDiskDeleted; 04416 04417 // reenable dialog if not running atm 04418 if (m_isasking == -1) 04419 m_isasking = false; 04420 04421 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); 04422 } 04423 } 04424 04425 bool KateDocument::createDigest( QByteArray &result ) 04426 { 04427 bool ret = false; 04428 result.clear(); 04429 if ( url().isLocalFile() ) 04430 { 04431 QFile f ( url().toLocalFile() ); 04432 if ( f.open( QIODevice::ReadOnly) ) 04433 { 04434 KMD5 md5; 04435 ret = md5.update( f ); 04436 md5.hexDigest( result ); 04437 f.close(); 04438 ret = true; 04439 } 04440 } 04441 return ret; 04442 } 04443 04444 QString KateDocument::reasonedMOHString() const 04445 { 04446 // squeeze path 04447 QString str = KStringHandler::csqueeze(url().pathOrUrl()); 04448 04449 switch( m_modOnHdReason ) 04450 { 04451 case OnDiskModified: 04452 return i18n("The file '%1' was modified by another program.", str ); 04453 break; 04454 case OnDiskCreated: 04455 return i18n("The file '%1' was created by another program.", str ); 04456 break; 04457 case OnDiskDeleted: 04458 return i18n("The file '%1' was deleted by another program.", str ); 04459 break; 04460 default: 04461 return QString(); 04462 } 04463 return QString(); 04464 } 04465 04466 void KateDocument::removeTrailingSpace(int line) 04467 { 04468 // if undo/redo is active, never remove trailing spaces, because the undo/redo 04469 // action also sets the cursor position. If the trailing spaces are removed, 04470 // the cursor position can get invalid (i.e. it is behind the last column). 04471 // Then, moving the cursor leads to a crash, see bug #152203. 04472 if (!m_undoManager->isActive()) 04473 return; 04474 04475 // remove trailing spaces from left line if required 04476 if (m_blockRemoveTrailingSpaces 04477 || !(config()->removeTrailingDyn())) 04478 return; 04479 04480 Kate::TextLine ln = plainKateTextLine(line); 04481 04482 if (!ln || ln->length() == 0) 04483 return; 04484 04485 if (activeView() && line == activeView()->cursorPosition().line() 04486 && activeView()->cursorPosition().column() >= qMax(0, ln->lastChar())) 04487 return; 04488 04489 const int p = ln->lastChar() + 1; 04490 const int l = ln->length() - p; 04491 if (l > 0) { 04492 m_blockRemoveTrailingSpaces = true; 04493 editRemoveText(line, p, l); 04494 m_blockRemoveTrailingSpaces = false; 04495 } 04496 } 04497 04498 void KateDocument::updateFileType (const QString &newType, bool user) 04499 { 04500 if (user || !m_fileTypeSetByUser) 04501 { 04502 if (!newType.isEmpty()) 04503 { 04504 m_fileType = newType; 04505 04506 m_config->configStart(); 04507 04508 if (!hlSetByUser && !KateGlobal::self()->modeManager()->fileType(newType).hl.isEmpty()) 04509 { 04510 int hl (KateHlManager::self()->nameFind (KateGlobal::self()->modeManager()->fileType(newType).hl)); 04511 04512 if (hl >= 0) 04513 m_buffer->setHighlight(hl); 04514 } 04515 04519 if (!KateGlobal::self()->modeManager()->fileType(newType).indenter.isEmpty()) 04520 config()->setIndentationMode (KateGlobal::self()->modeManager()->fileType(newType).indenter); 04521 04522 // views! 04523 KateView *v; 04524 foreach (v,m_views) 04525 { 04526 v->config()->configStart(); 04527 v->renderer()->config()->configStart(); 04528 } 04529 04530 bool bom_settings = false; 04531 if (m_bomSetByUser) 04532 bom_settings=m_config->bom(); 04533 readVariableLine( KateGlobal::self()->modeManager()->fileType(newType).varLine ); 04534 if (m_bomSetByUser) 04535 m_config->setBom(bom_settings); 04536 m_config->configEnd(); 04537 foreach (v,m_views) 04538 { 04539 v->config()->configEnd(); 04540 v->renderer()->config()->configEnd(); 04541 } 04542 } 04543 } 04544 04545 // fixme, make this better... 04546 emit modeChanged (this); 04547 } 04548 04549 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) { 04550 *handled=true; 04551 *abortClosing=true; 04552 if (this->url().isEmpty()) 04553 { 04554 QWidget *parentWidget(dialogParent()); 04555 04556 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(), 04557 QString(),QString(),parentWidget,i18n("Save File")); 04558 04559 if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) ) { 04560 *abortClosing=true; 04561 return; 04562 } 04563 setEncoding( res.encoding ); 04564 saveAs( res.URLs.first() ); 04565 *abortClosing=false; 04566 } 04567 else 04568 { 04569 save(); 04570 *abortClosing=false; 04571 } 04572 04573 } 04574 04575 bool KateDocument::checkOverwrite( KUrl u, QWidget *parent ) 04576 { 04577 if( !u.isLocalFile() ) 04578 return true; 04579 04580 QFileInfo info( u.path() ); 04581 if( !info.exists() ) 04582 return true; 04583 04584 return KMessageBox::Cancel != KMessageBox::warningContinueCancel( parent, 04585 i18n( "A file named \"%1\" already exists. " 04586 "Are you sure you want to overwrite it?" , info.fileName() ), 04587 i18n( "Overwrite File?" ), KStandardGuiItem::overwrite(), 04588 KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous ); 04589 } 04590 04591 //BEGIN KTextEditor::ConfigInterface 04592 04593 // BEGIN ConfigInterface stff 04594 QStringList KateDocument::configKeys() const 04595 { 04596 return QStringList() << "auto-brackets"; 04597 } 04598 04599 QVariant KateDocument::configValue(const QString &key) 04600 { 04601 if (key == "auto-brackets") { 04602 return m_config->autoBrackets(); 04603 } else if (key == "backup-on-save-local") { 04604 return m_config->backupFlags() & KateDocumentConfig::LocalFiles; 04605 } else if (key == "backup-on-save-remote") { 04606 return m_config->backupFlags() & KateDocumentConfig::RemoteFiles; 04607 } else if (key == "backup-on-save-suffix") { 04608 return m_config->backupSuffix(); 04609 } else if (key == "backup-on-save-prefix") { 04610 return m_config->backupPrefix(); 04611 } 04612 04613 // return invalid variant 04614 return QVariant(); 04615 } 04616 04617 void KateDocument::setConfigValue(const QString &key, const QVariant &value) 04618 { 04619 if (value.type() == QVariant::String) { 04620 if (key == "backup-on-save-suffix") { 04621 m_config->setBackupSuffix(value.toString()); 04622 } else if (key == "backup-on-save-prefix") { 04623 m_config->setBackupPrefix(value.toString()); 04624 } 04625 } else if (value.canConvert(QVariant::Bool)) { 04626 const bool bValue = value.toBool(); 04627 if (key == "auto-brackets") { 04628 m_config->setAutoBrackets(bValue); 04629 } else if (key == "backup-on-save-local" && value.type() == QVariant::String) { 04630 uint f = m_config->backupFlags(); 04631 if (bValue) { 04632 f |= KateDocumentConfig::LocalFiles; 04633 } else { 04634 f ^= KateDocumentConfig::LocalFiles; 04635 } 04636 04637 m_config->setBackupFlags(f); 04638 } else if (key == "backup-on-save-remote") { 04639 uint f = m_config->backupFlags(); 04640 if (bValue) { 04641 f |= KateDocumentConfig::RemoteFiles; 04642 } else { 04643 f ^= KateDocumentConfig::RemoteFiles; 04644 } 04645 04646 m_config->setBackupFlags(f); 04647 } 04648 } 04649 } 04650 04651 //END KTextEditor::ConfigInterface 04652 04653 //BEGIN KTextEditor::TemplateInterface 04654 bool KateDocument::insertTemplateTextImplementation( const KTextEditor::Cursor &c, 04655 const QString &templateString, 04656 const QMap<QString,QString> &initialValues, 04657 KTextEditor::TemplateScript* templateScript, 04658 KateView* view) 04659 { 04660 if (templateString.isEmpty()) return false; 04661 04662 KateTemplateScript* kateTemplateScript = 04663 KateGlobal::self()->scriptManager()->templateScript(templateScript); 04664 04665 // the handler will delete itself when necessary 04666 new KateTemplateHandler(view, c, templateString, initialValues, m_undoManager, kateTemplateScript); 04667 04668 return true; 04669 } 04670 //END KTextEditor::TemplateInterface 04671 04672 04673 KateView * KateDocument::activeKateView( ) const 04674 { 04675 return static_cast<KateView*>(m_activeView); 04676 } 04677 04678 KTextEditor::Cursor KateDocument::documentEnd( ) const 04679 { 04680 return KTextEditor::Cursor(lastLine(), lineLength(lastLine())); 04681 } 04682 04683 bool KateDocument::replaceText( const KTextEditor::Range & range, const QString & s, bool block ) 04684 { 04685 // TODO more efficient? 04686 editStart(); 04687 bool changed = removeText(range, block); 04688 changed |= insertText(range.start(), s, block); 04689 editEnd(); 04690 return changed; 04691 } 04692 04693 void KateDocument::ignoreModifiedOnDiskOnce( ) 04694 { 04695 m_isasking = -1; 04696 } 04697 04698 KateHighlighting * KateDocument::highlight( ) const 04699 { 04700 return m_buffer->highlight(); 04701 } 04702 04703 uint KateDocument::getRealLine( unsigned int virtualLine ) 04704 { 04705 return m_buffer->lineNumber (virtualLine); 04706 } 04707 04708 uint KateDocument::getVirtualLine( unsigned int realLine ) 04709 { 04710 return m_buffer->lineVisibleNumber (realLine); 04711 } 04712 04713 uint KateDocument::visibleLines( ) 04714 { 04715 return m_buffer->countVisible (); 04716 } 04717 04718 Kate::TextLine KateDocument::kateTextLine( uint i ) 04719 { 04720 m_buffer->ensureHighlighted (i); 04721 return m_buffer->plainLine (i); 04722 } 04723 04724 Kate::TextLine KateDocument::plainKateTextLine( uint i ) 04725 { 04726 return m_buffer->plainLine (i); 04727 } 04728 04729 bool KateDocument::isEditRunning() const 04730 { 04731 return editIsRunning; 04732 } 04733 04734 void KateDocument::setUndoMergeAllEdits(bool merge) 04735 { 04736 m_undoManager->undoSafePoint(); 04737 m_undoManager->setAllowComplexMerge(merge); 04738 } 04739 04740 //BEGIN KTextEditor::MovingInterface 04741 KTextEditor::MovingCursor *KateDocument::newMovingCursor (const KTextEditor::Cursor &position, KTextEditor::MovingCursor::InsertBehavior insertBehavior) 04742 { 04743 return new Kate::TextCursor(buffer(), position, insertBehavior); 04744 } 04745 04746 KTextEditor::MovingRange *KateDocument::newMovingRange (const KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior) 04747 { 04748 return new Kate::TextRange(buffer(), range, insertBehaviors, emptyBehavior); 04749 } 04750 04751 qint64 KateDocument::revision () const 04752 { 04753 return m_buffer->history().revision (); 04754 } 04755 04756 qint64 KateDocument::lastSavedRevision () const 04757 { 04758 return m_buffer->history().lastSavedRevision (); 04759 } 04760 04761 void KateDocument::lockRevision (qint64 revision) 04762 { 04763 m_buffer->history().lockRevision (revision); 04764 } 04765 04766 void KateDocument::unlockRevision (qint64 revision) 04767 { 04768 m_buffer->history().unlockRevision (revision); 04769 } 04770 04771 void KateDocument::transformCursor(int& line, int& column, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision) 04772 { 04773 m_buffer->history().transformCursor (line, column, insertBehavior, fromRevision, toRevision); 04774 } 04775 04776 void KateDocument::transformCursor (KTextEditor::Cursor &cursor, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision) 04777 { 04778 int line = cursor.line(), column = cursor.column(); 04779 m_buffer->history().transformCursor (line, column, insertBehavior, fromRevision, toRevision); 04780 cursor.setLine(line); 04781 cursor.setColumn(column); 04782 } 04783 04784 void KateDocument::transformRange (KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision) 04785 { 04786 m_buffer->history().transformRange (range, insertBehaviors, emptyBehavior, fromRevision, toRevision); 04787 } 04788 04789 //END 04790 04791 bool KateDocument::simpleMode () 04792 { 04793 return KateGlobal::self()->simpleMode () && KateGlobal::self()->documentConfig()->allowSimpleMode (); 04794 } 04795 04796 KateDocument::LoadSaveFilterCheckPlugins* KateDocument::loadSaveFilterCheckPlugins() 04797 { 04798 K_GLOBAL_STATIC(KateDocument::LoadSaveFilterCheckPlugins, s_loadSaveFilterCheckPlugins) 04799 return s_loadSaveFilterCheckPlugins; 04800 } 04801 04802 //BEGIN KTextEditor::AnnotationInterface 04803 void KateDocument::setAnnotationModel( KTextEditor::AnnotationModel* model ) 04804 { 04805 KTextEditor::AnnotationModel* oldmodel = m_annotationModel; 04806 m_annotationModel = model; 04807 emit annotationModelChanged(oldmodel, m_annotationModel); 04808 } 04809 04810 KTextEditor::AnnotationModel* KateDocument::annotationModel() const 04811 { 04812 return m_annotationModel; 04813 } 04814 //END KTextEditor::AnnotationInterface 04815 04816 //TAKEN FROM kparts.h 04817 bool KateDocument::queryClose() 04818 { 04819 if ( !isReadWrite() || !isModified() ) 04820 return true; 04821 04822 QString docName = documentName(); 04823 04824 int res = KMessageBox::warningYesNoCancel( dialogParent(), 04825 i18n( "The document \"%1\" has been modified.\n" 04826 "Do you want to save your changes or discard them?" , docName ), 04827 i18n( "Close Document" ), KStandardGuiItem::save(), KStandardGuiItem::discard() ); 04828 04829 bool abortClose=false; 04830 bool handled=false; 04831 04832 switch(res) { 04833 case KMessageBox::Yes : 04834 sigQueryClose(&handled,&abortClose); 04835 if (!handled) 04836 { 04837 if (url().isEmpty()) 04838 { 04839 KUrl url = KFileDialog::getSaveUrl(KUrl(), QString(), dialogParent()); 04840 if (url.isEmpty()) 04841 return false; 04842 04843 saveAs( url ); 04844 } 04845 else 04846 { 04847 save(); 04848 } 04849 } else if (abortClose) return false; 04850 return waitSaveComplete(); 04851 case KMessageBox::No : 04852 return true; 04853 default : // case KMessageBox::Cancel : 04854 return false; 04855 } 04856 } 04857 04858 04859 void KateDocument::slotCanceled() { 04860 m_savingToUrl=false; 04861 m_saveAs=false; 04862 } 04863 04864 void KateDocument::slotCompleted() { 04865 if (m_savingToUrl) { 04866 if (!m_postSaveFilterChecks.isEmpty()) 04867 { 04868 LoadSaveFilterCheckPlugins *lscps1=loadSaveFilterCheckPlugins(); 04869 foreach(const QString& checkplugin, m_postSaveFilterChecks) 04870 { 04871 if (lscps1->postSaveFilterCheck(checkplugin,this,m_saveAs)==false) 04872 break; 04873 } 04874 } 04875 emit documentSavedOrUploaded(this,m_saveAs); 04876 } 04877 m_savingToUrl=false; 04878 m_saveAs=false; 04879 } 04880 04881 bool KateDocument::save() { 04882 m_saveAs = false; 04883 return KTextEditor::Document::save(); 04884 } 04885 04886 bool KateDocument::saveAs( const KUrl &url ) { 04887 m_saveAs = true; 04888 return KTextEditor::Document::saveAs(url); 04889 } 04890 04891 QString KateDocument::defaultDictionary() const 04892 { 04893 return m_defaultDictionary; 04894 } 04895 04896 QList<QPair<KTextEditor::MovingRange*, QString> > KateDocument::dictionaryRanges() const 04897 { 04898 return m_dictionaryRanges; 04899 } 04900 04901 void KateDocument::clearDictionaryRanges() 04902 { 04903 for(QList<QPair<KTextEditor::MovingRange*, QString> >::iterator i = m_dictionaryRanges.begin(); 04904 i != m_dictionaryRanges.end();++i) 04905 { 04906 delete (*i).first; 04907 } 04908 m_dictionaryRanges.clear(); 04909 if(m_onTheFlyChecker) 04910 { 04911 m_onTheFlyChecker->refreshSpellCheck(); 04912 } 04913 emit dictionaryRangesPresent(false); 04914 } 04915 04916 void KateDocument::setDictionary(const QString& newDictionary, const KTextEditor::Range &range) 04917 { 04918 KTextEditor::Range newDictionaryRange = range; 04919 if(!newDictionaryRange.isValid() || newDictionaryRange.isEmpty()) 04920 { 04921 return; 04922 } 04923 QList<QPair<KTextEditor::MovingRange*, QString> > newRanges; 04924 // all ranges is 'm_dictionaryRanges' are assumed to be mutually disjoint 04925 for(QList<QPair<KTextEditor::MovingRange*, QString> >::iterator i = m_dictionaryRanges.begin(); 04926 i != m_dictionaryRanges.end();) 04927 { 04928 kDebug(13000) << "new iteration" << newDictionaryRange; 04929 if(newDictionaryRange.isEmpty()) 04930 { 04931 break; 04932 } 04933 QPair<KTextEditor::MovingRange*, QString> pair = *i; 04934 QString dictionarySet = pair.second; 04935 KTextEditor::MovingRange *dictionaryRange = pair.first; 04936 kDebug(13000) << *dictionaryRange << dictionarySet; 04937 if(dictionaryRange->contains(newDictionaryRange) && newDictionary == dictionarySet) 04938 { 04939 kDebug(13000) << "dictionaryRange contains newDictionaryRange"; 04940 return; 04941 } 04942 if(newDictionaryRange.contains(*dictionaryRange)) 04943 { 04944 delete dictionaryRange; 04945 i = m_dictionaryRanges.erase(i); 04946 kDebug(13000) << "newDictionaryRange contains dictionaryRange"; 04947 continue; 04948 } 04949 04950 KTextEditor::Range intersection = dictionaryRange->toRange().intersect(newDictionaryRange); 04951 if(!intersection.isEmpty() && intersection.isValid()) 04952 { 04953 if(dictionarySet == newDictionary) // we don't have to do anything for 'intersection' 04954 { // except cut off the intersection 04955 QList<KTextEditor::Range> remainingRanges = KateSpellCheckManager::rangeDifference(newDictionaryRange, intersection); 04956 Q_ASSERT(remainingRanges.size() == 1); 04957 newDictionaryRange = remainingRanges.first(); 04958 ++i; 04959 kDebug(13000) << "dictionarySet == newDictionary"; 04960 continue; 04961 } 04962 QList<KTextEditor::Range> remainingRanges = KateSpellCheckManager::rangeDifference(*dictionaryRange, intersection); 04963 for(QList<KTextEditor::Range>::iterator j = remainingRanges.begin(); j != remainingRanges.end(); ++j) 04964 { 04965 KTextEditor::MovingRange *remainingRange = newMovingRange(*j, 04966 KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); 04967 remainingRange->setFeedback(this); 04968 newRanges.push_back(QPair<KTextEditor::MovingRange*, QString>(remainingRange, dictionarySet)); 04969 } 04970 i = m_dictionaryRanges.erase(i); 04971 delete dictionaryRange; 04972 } else { 04973 ++i; 04974 } 04975 } 04976 m_dictionaryRanges += newRanges; 04977 if(!newDictionaryRange.isEmpty() && !newDictionary.isEmpty()) // we don't add anything for the default dictionary 04978 { 04979 KTextEditor::MovingRange *newDictionaryMovingRange = newMovingRange(newDictionaryRange, 04980 KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); 04981 newDictionaryMovingRange->setFeedback(this); 04982 m_dictionaryRanges.push_back(QPair<KTextEditor::MovingRange*, QString>(newDictionaryMovingRange, newDictionary)); 04983 } 04984 if(m_onTheFlyChecker && !newDictionaryRange.isEmpty()) 04985 { 04986 m_onTheFlyChecker->refreshSpellCheck(newDictionaryRange); 04987 } 04988 emit dictionaryRangesPresent(!m_dictionaryRanges.isEmpty()); 04989 } 04990 04991 void KateDocument::revertToDefaultDictionary(const KTextEditor::Range &range) 04992 { 04993 setDictionary(QString(), range); 04994 } 04995 04996 void KateDocument::setDefaultDictionary(const QString& dict) 04997 { 04998 if(m_defaultDictionary == dict) 04999 { 05000 return; 05001 } 05002 m_defaultDictionary = dict; 05003 if(m_onTheFlyChecker) 05004 { 05005 m_onTheFlyChecker->updateConfig(); 05006 } 05007 refreshOnTheFlyCheck(); 05008 emit defaultDictionaryChanged(this); 05009 } 05010 05011 void KateDocument::onTheFlySpellCheckingEnabled(bool enable) 05012 { 05013 if (isOnTheFlySpellCheckingEnabled() == enable) 05014 { 05015 return; 05016 } 05017 if (enable) 05018 { 05019 if (!m_onTheFlyChecker) { 05020 m_onTheFlyChecker = new KateOnTheFlyChecker(this); 05021 } 05022 } else { 05023 delete m_onTheFlyChecker; 05024 m_onTheFlyChecker = 0; 05025 } 05026 foreach(KateView *view, m_views) { 05027 view->reflectOnTheFlySpellCheckStatus(enable); 05028 } 05029 } 05030 05031 bool KateDocument::isOnTheFlySpellCheckingEnabled() const 05032 { 05033 return m_onTheFlyChecker != 0; 05034 } 05035 05036 QString KateDocument::dictionaryForMisspelledRange(const KTextEditor::Range& range) const 05037 { 05038 if (!m_onTheFlyChecker) 05039 { 05040 return QString(); 05041 } else { 05042 return m_onTheFlyChecker->dictionaryForMisspelledRange(range); 05043 } 05044 } 05045 05046 void KateDocument::clearMisspellingForWord(const QString& word) 05047 { 05048 if(m_onTheFlyChecker) { 05049 m_onTheFlyChecker->clearMisspellingForWord(word); 05050 } 05051 } 05052 05053 void KateDocument::refreshOnTheFlyCheck(const KTextEditor::Range &range) 05054 { 05055 if(m_onTheFlyChecker) { 05056 m_onTheFlyChecker->refreshSpellCheck(range); 05057 } 05058 } 05059 05060 void KateDocument::rangeInvalid(KTextEditor::MovingRange *movingRange) 05061 { 05062 deleteDictionaryRange(movingRange); 05063 } 05064 05065 void KateDocument::rangeEmpty(KTextEditor::MovingRange *movingRange) 05066 { 05067 deleteDictionaryRange(movingRange); 05068 } 05069 05070 void KateDocument::deleteDictionaryRange(KTextEditor::MovingRange *movingRange) 05071 { 05072 kDebug(13020) << "deleting" << movingRange; 05073 for(QList<QPair<KTextEditor::MovingRange*, QString> >::iterator i = m_dictionaryRanges.begin(); 05074 i != m_dictionaryRanges.end();) 05075 { 05076 KTextEditor::MovingRange *dictionaryRange = (*i).first; 05077 if(dictionaryRange == movingRange) 05078 { 05079 delete movingRange; 05080 i = m_dictionaryRanges.erase(i); 05081 } else { 05082 ++i; 05083 } 05084 } 05085 } 05086 05087 bool KateDocument::containsCharacterEncoding(const KTextEditor::Range& range) 05088 { 05089 KateHighlighting *highlighting = highlight(); 05090 Kate::TextLine textLine; 05091 05092 const int rangeStartLine = range.start().line(); 05093 const int rangeStartColumn = range.start().column(); 05094 const int rangeEndLine = range.end().line(); 05095 const int rangeEndColumn = range.end().column(); 05096 05097 for(int line = range.start().line(); line <= rangeEndLine; ++line) { 05098 textLine = kateTextLine(line); 05099 int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0; 05100 int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine->length(); 05101 for(int col = startColumn; col < endColumn; ++col) { 05102 int attr = textLine->attribute(col); 05103 const KatePrefixStore& prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr); 05104 if(!prefixStore.findPrefix(textLine, col).isEmpty()) { 05105 return true; 05106 } 05107 } 05108 } 05109 05110 return false; 05111 } 05112 05113 int KateDocument::computePositionWrtOffsets(const OffsetList& offsetList, int pos) 05114 { 05115 int previousOffset = 0; 05116 for(OffsetList::const_iterator i = offsetList.begin(); i != offsetList.end(); ++i) { 05117 if((*i).first > pos) { 05118 break; 05119 } 05120 previousOffset = (*i).second; 05121 } 05122 return pos + previousOffset; 05123 } 05124 05125 QString KateDocument::decodeCharacters(const KTextEditor::Range& range, KateDocument::OffsetList& decToEncOffsetList, 05126 KateDocument::OffsetList& encToDecOffsetList) 05127 { 05128 QString toReturn; 05129 KTextEditor::Cursor previous = range.start(); 05130 int decToEncCurrentOffset = 0, encToDecCurrentOffset = 0; 05131 int i = 0; 05132 int newI = 0; 05133 05134 KateHighlighting *highlighting = highlight(); 05135 Kate::TextLine textLine; 05136 05137 const int rangeStartLine = range.start().line(); 05138 const int rangeStartColumn = range.start().column(); 05139 const int rangeEndLine = range.end().line(); 05140 const int rangeEndColumn = range.end().column(); 05141 05142 for(int line = range.start().line(); line <= rangeEndLine; ++line) { 05143 textLine = kateTextLine(line); 05144 int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0; 05145 int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine->length(); 05146 for(int col = startColumn; col < endColumn;) { 05147 int attr = textLine->attribute(col); 05148 const KatePrefixStore& prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr); 05149 const QHash<QString, QChar>& characterEncodingsHash = highlighting->getCharacterEncodings(attr); 05150 QString matchingPrefix = prefixStore.findPrefix(textLine, col); 05151 if(!matchingPrefix.isEmpty()) { 05152 toReturn += text(KTextEditor::Range(previous, KTextEditor::Cursor(line, col))); 05153 const QChar& c = characterEncodingsHash.value(matchingPrefix); 05154 const bool isNullChar = c.isNull(); 05155 if(!c.isNull()) { 05156 toReturn += c; 05157 } 05158 i += matchingPrefix.length(); 05159 col += matchingPrefix.length(); 05160 previous = KTextEditor::Cursor(line, col); 05161 decToEncCurrentOffset = decToEncCurrentOffset - (isNullChar ? 0 : 1) + matchingPrefix.length(); 05162 encToDecCurrentOffset = encToDecCurrentOffset - matchingPrefix.length() + (isNullChar ? 0 : 1); 05163 newI += (isNullChar ? 0 : 1); 05164 decToEncOffsetList.push_back(QPair<int, int>(newI, decToEncCurrentOffset)); 05165 encToDecOffsetList.push_back(QPair<int, int>(i, encToDecCurrentOffset)); 05166 continue; 05167 } 05168 ++col; 05169 ++i; 05170 ++newI; 05171 } 05172 ++i; 05173 ++newI; 05174 } 05175 if(previous < range.end()) { 05176 toReturn += text(KTextEditor::Range(previous, range.end())); 05177 } 05178 return toReturn; 05179 } 05180 05181 void KateDocument::replaceCharactersByEncoding(const KTextEditor::Range& range) 05182 { 05183 KateHighlighting *highlighting = highlight(); 05184 Kate::TextLine textLine; 05185 05186 const int rangeStartLine = range.start().line(); 05187 const int rangeStartColumn = range.start().column(); 05188 const int rangeEndLine = range.end().line(); 05189 const int rangeEndColumn = range.end().column(); 05190 05191 for(int line = range.start().line(); line <= rangeEndLine; ++line) { 05192 textLine = kateTextLine(line); 05193 int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0; 05194 int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine->length(); 05195 for(int col = startColumn; col < endColumn;) { 05196 int attr = textLine->attribute(col); 05197 const QHash<QChar, QString>& reverseCharacterEncodingsHash = highlighting->getReverseCharacterEncodings(attr); 05198 QHash<QChar, QString>::const_iterator it = reverseCharacterEncodingsHash.find(textLine->at(col)); 05199 if(it != reverseCharacterEncodingsHash.end()) { 05200 replaceText(KTextEditor::Range(line, col, line, col + 1), *it); 05201 col += (*it).length(); 05202 continue; 05203 } 05204 ++col; 05205 } 05206 } 05207 } 05208 05209 // 05210 // KTextEditor::HighlightInterface 05211 // 05212 05213 KTextEditor::Attribute::Ptr KateDocument::defaultStyle(const KTextEditor::HighlightInterface::DefaultStyle ds) const 05214 { 05216 KateView* view = activeKateView(); 05217 if ( !view ) { 05218 kWarning() << "ATTENTION: cannot access defaultStyle() without any View (will be fixed eventually)"; 05219 return KTextEditor::Attribute::Ptr(0); 05220 } 05221 05222 KTextEditor::Attribute::Ptr style = highlight()->attributes(view->renderer()->config()->schema()).at(ds); 05223 if (!style->hasProperty(QTextFormat::BackgroundBrush)) { 05224 // make sure the returned style has the default background color set 05225 style.attach(new KTextEditor::Attribute(*style)); 05226 style->setBackground( QBrush(view->renderer()->config()->backgroundColor()) ); 05227 } 05228 return style; 05229 } 05230 05231 QList< KTextEditor::HighlightInterface::AttributeBlock > KateDocument::lineAttributes(const unsigned int line) 05232 { 05234 05235 QList< KTextEditor::HighlightInterface::AttributeBlock > attribs; 05236 05237 KateView* view = activeKateView(); 05238 if ( !view ) { 05239 kWarning() << "ATTENTION: cannot access lineAttributes() without any View (will be fixed eventually)"; 05240 return attribs; 05241 } 05242 05243 Kate::TextLine kateLine = kateTextLine(line); 05244 05245 if ( !kateLine ) { 05246 return attribs; 05247 } 05248 05249 const QVector<int> & intAttrs = kateLine->attributesList(); 05250 05251 Q_ASSERT(intAttrs.size() % 3 == 0); 05252 05253 for ( int i = 0; i < intAttrs.size(); i += 3 ) { 05254 attribs << KTextEditor::HighlightInterface::AttributeBlock( 05255 intAttrs.at(i), 05256 intAttrs.at(i+1), 05257 view->renderer()->attribute(intAttrs.at(i+2)) 05258 ); 05259 } 05260 05261 return attribs; 05262 } 05263 05264 QStringList KateDocument::embeddedHighlightingModes() const 05265 { 05266 return highlight()->getEmbeddedHighlightingModes(); 05267 } 05268 05269 QString KateDocument::highlightingModeAt(const KTextEditor::Cursor& position) 05270 { 05271 Kate::TextLine kateLine = kateTextLine(position.line()); 05272 05273 // const QVector< short >& attrs = kateLine->ctxArray(); 05274 // kDebug() << "----------------------------------------------------------------------"; 05275 // foreach( short a, attrs ) { 05276 // kDebug() << a; 05277 // } 05278 // kDebug() << "----------------------------------------------------------------------"; 05279 // kDebug() << "col: " << position.column() << " lastchar:" << kateLine->lastChar() << " length:" << kateLine->length() << "global mode:" << highlightingMode(); 05280 05281 int len = kateLine->length(); 05282 int pos = position.column(); 05283 if ( pos >= len ) { 05284 const QVector< short >& ctxs = kateLine->ctxArray(); 05285 int ctxcnt = ctxs.count(); 05286 if ( ctxcnt == 0 ) { 05287 return highlightingMode(); 05288 } 05289 int ctx = ctxs.at(ctxcnt-1); 05290 if ( ctx == 0 ) { 05291 return highlightingMode(); 05292 } 05293 return KateHlManager::self()->nameForIdentifier(highlight()->hlKeyForContext(ctx)); 05294 } 05295 05296 int attr = kateLine->attribute(pos); 05297 if ( attr == 0 ) { 05298 return mode(); 05299 } 05300 05301 return KateHlManager::self()->nameForIdentifier(highlight()->hlKeyForAttrib(attr)); 05302 05303 } 05304 05305 Kate::SwapFile* KateDocument::swapFile() 05306 { 05307 return m_swapfile; 05308 } 05309 05310 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE 4.6 API Reference