• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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;

Kate

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

kdelibs

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