• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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 
00008    This library is free software; you can redistribute it and/or
00009    modify it under the terms of the GNU Library General Public
00010    License version 2 as published by the Free Software Foundation.
00011 
00012    This library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Library General Public License for more details.
00016 
00017    You should have received a copy of the GNU Library General Public License
00018    along with this library; see the file COPYING.LIB.  If not, write to
00019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020    Boston, MA 02111-13020, USA.
00021 */
00022 
00023 //BEGIN includes
00024 #include "katedocument.h"
00025 #include "katedocument.moc"
00026 #include "kateglobal.h"
00027 #include "katedialogs.h"
00028 #include "katehighlight.h"
00029 #include "kateview.h"
00030 #include "kateautoindent.h"
00031 #include "katetextline.h"
00032 #include "katedocumenthelpers.h"
00033 #include "kateprinter.h"
00034 #include "katesmartcursor.h"
00035 #include "katerenderer.h"
00036 #include <ktexteditor/attribute.h>
00037 #include "kateconfig.h"
00038 #include "katemodemanager.h"
00039 #include "kateschema.h"
00040 #include "katetemplatehandler.h"
00041 #include "katesmartmanager.h"
00042 #include <ktexteditor/plugin.h>
00043 #include <ktexteditor/loadsavefiltercheckplugin.h>
00044 #include "kateedit.h"
00045 #include "katebuffer.h"
00046 #include "kateundomanager.h"
00047 #include "katepartpluginmanager.h"
00048 
00049 #include <kio/job.h>
00050 #include <kio/jobuidelegate.h>
00051 #include <kio/netaccess.h>
00052 #include <kfileitem.h>
00053 
00054 #include <kparts/event.h>
00055 
00056 #include <klocale.h>
00057 #include <kglobal.h>
00058 #include <kapplication.h>
00059 #include <kmenu.h>
00060 #include <kconfig.h>
00061 #include <kfiledialog.h>
00062 #include <kmessagebox.h>
00063 #include <kstandardaction.h>
00064 #include <kxmlguifactory.h>
00065 #include <kdebug.h>
00066 #include <kglobalsettings.h>
00067 #include <klibloader.h>
00068 #include <kdirwatch.h>
00069 #include <kencodingfiledialog.h>
00070 #include <ktemporaryfile.h>
00071 #include <kcodecs.h>
00072 #include <kstandarddirs.h>
00073 #include <kstringhandler.h>
00074 
00075 #include <kservicetypetrader.h>
00076 
00077 #include <QtDBus/QtDBus>
00078 #include <QtCore/QTimer>
00079 #include <QtCore/QFile>
00080 #include <QtGui/QClipboard>
00081 #include <QtCore/QTextStream>
00082 #include <QtCore/QTextCodec>
00083 #include <QtCore/QMap>
00084 #include <QtCore/QMutex>
00085 //END  includes
00086 
00087 
00088 
00089 // Turn debug messages on/off here
00090 // #define FAST_DEBUG_ENABLE
00091 
00092 #ifdef FAST_DEBUG_ENABLE
00093 # define FAST_DEBUG(x) (kDebug( 13020 ) << x)
00094 #else
00095 # define FAST_DEBUG(x)
00096 #endif
00097 
00098 
00099 
00100 //BEGIN PRIVATE CLASSES
00101 class KatePartPluginItem
00102 {
00103   public:
00104     KTextEditor::Plugin *plugin;
00105 };
00106 //END PRIVATE CLASSES
00107 
00108 static int dummy = 0;
00109 
00110 
00111 class KateDocument::LoadSaveFilterCheckPlugins
00112 {
00113   public:
00114     LoadSaveFilterCheckPlugins() {
00115       KService::List traderList = KServiceTypeTrader::self()->query("KTextEditor/LoadSaveFilterCheckPlugin");
00116 
00117       foreach(const KService::Ptr &ptr, traderList)
00118       {
00119         QString libname;
00120         libname=ptr->library();
00121         libname=libname.right(libname.length()-12); //ktexteditor_ == 12
00122         m_plugins[libname]=0;//new KatePythonEncodingCheck();
00123       }
00124 
00125     }
00126     ~LoadSaveFilterCheckPlugins() {
00127       if ( m_plugins.count()==0) return;
00128       QHashIterator<QString,KTextEditor::LoadSaveFilterCheckPlugin*>i(m_plugins);
00129         while (i.hasNext())
00130           i.next();
00131           delete i.value();
00132       m_plugins.clear();
00133     }
00134     bool preSavePostDialogFilterCheck(const QString& pluginName,KateDocument *document,QWidget *parentWidget) {
00135       KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00136       if (!plug) {
00137         if (KMessageBox::warningContinueCancel (parentWidget
00138         , i18n ("The filter/check plugin '%1' could not be found, still continue saving of %2", pluginName,document->url().pathOrUrl())
00139         , i18n ("Saving problems")
00140         , KGuiItem(i18n("Save Nevertheless"))
00141         , KStandardGuiItem::cancel()) != KMessageBox::Continue)
00142           return false;
00143         else
00144           return true;
00145       }
00146       return plug->preSavePostDialogFilterCheck(document);
00147     }
00148     void postLoadFilter(const QString& pluginName,KateDocument *document) {
00149       KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00150       if (!plug) return;
00151       plug->postLoadFilter(document);
00152     }
00153     bool postSaveFilterCheck(const QString& pluginName,KateDocument *document,bool saveas) {
00154       KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00155       if (!plug) return false;
00156       return plug->postSaveFilterCheck(document,saveas);
00157     }
00158   private:
00159     KTextEditor::LoadSaveFilterCheckPlugin *getPlugin(const QString & pluginName)
00160     {
00161       if (!m_plugins.contains(pluginName)) return 0;
00162       if (!m_plugins[pluginName]) {
00163         m_plugins[pluginName]=KLibLoader::createInstance<KTextEditor::LoadSaveFilterCheckPlugin>( "ktexteditor_"+pluginName);
00164       }
00165       return m_plugins[pluginName];
00166     }
00167     QHash <QString,KTextEditor::LoadSaveFilterCheckPlugin*> m_plugins;
00168 };
00169 
00170 //BEGIN d'tor, c'tor
00171 //
00172 // KateDocument Constructor
00173 //
00174 KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView,
00175                              bool bReadOnly, QWidget *parentWidget,
00176                              QObject *parent)
00177 : KTextEditor::Document (parent),
00178   m_activeView(0L),
00179   m_undoManager(new KateUndoManager(this)),
00180   m_annotationModel( 0 ),
00181   m_saveAs(false),
00182   m_indenter(this),
00183   m_modOnHd (false),
00184   m_modOnHdReason (OnDiskUnmodified),
00185   s_fileChangedDialogsActivated (false),
00186   m_templateHandler(0),
00187   m_savingToUrl(false)
00188 {
00189   setComponentData ( KateGlobal::self()->componentData () );
00190 
00191   QString pathName ("/Kate/Document/%1");
00192   pathName = pathName.arg (++dummy);
00193 
00194   // my dbus object
00195   QDBusConnection::sessionBus().registerObject (pathName, this);
00196 
00197   // register doc at factory
00198   KateGlobal::self()->registerDocument(this);
00199 
00200   m_reloading = false;
00201 
00202   m_editHistory = new KateEditHistory(this);
00203   m_smartManager = new KateSmartManager(this);
00204   m_buffer = new KateBuffer(this);
00205 
00206   // init the config object, be careful not to use it
00207   // until the initial readConfig() call is done
00208   m_config = new KateDocumentConfig(this);
00209 
00210   // init some more vars !
00211   setActiveView(0L);
00212 
00213   hlSetByUser = false;
00214   m_fileTypeSetByUser = false;
00215 
00216   editSessionNumber = 0;
00217   editIsRunning = false;
00218   editWithUndo = false;
00219 
00220   m_docNameNumber = 0;
00221   m_docName = "need init";
00222 
00223   m_bSingleViewMode = bSingleViewMode;
00224   m_bBrowserView = bBrowserView;
00225   m_bReadOnly = bReadOnly;
00226 
00227   setEditableMarks( markType01 );
00228 
00229   clearMarks ();
00230   setModified (false);
00231 
00232   // normal hl
00233   m_buffer->setHighlight (0);
00234 
00235   m_blockRemoveTrailingSpaces = false;
00236   m_extension = new KateBrowserExtension( this );
00237 
00238   // important, fill in the config into the indenter we use...
00239   m_indenter.updateConfig ();
00240 
00241   // some nice signals from the buffer
00242   connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int)));
00243   connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated()));
00244 
00245   // if the user changes the highlight with the dialog, notify the doc
00246   connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged()));
00247 
00248   // signals for mod on hd
00249   connect( KateGlobal::self()->dirWatch(), SIGNAL(dirty (const QString &)),
00250            this, SLOT(slotModOnHdDirty (const QString &)) );
00251 
00252   connect( KateGlobal::self()->dirWatch(), SIGNAL(created (const QString &)),
00253            this, SLOT(slotModOnHdCreated (const QString &)) );
00254 
00255   connect( KateGlobal::self()->dirWatch(), SIGNAL(deleted (const QString &)),
00256            this, SLOT(slotModOnHdDeleted (const QString &)) );
00257 
00258   connect (this,SIGNAL(completed()),this,SLOT(slotCompleted()));
00259   connect (this,SIGNAL(canceled(const QString&)),this,SLOT(slotCanceled()));
00260   // update doc name
00261   setDocName ("");
00262 
00263   // if single view mode, like in the konqui embedding, create a default view ;)
00264   // be lazy, only create it now, if any parentWidget is given, otherwise widget()
00265   // will create it on demand...
00266   if ( m_bSingleViewMode && parentWidget )
00267   {
00268     KTextEditor::View *view = (KTextEditor::View*)createView( parentWidget );
00269     insertChildClient( view );
00270     view->show();
00271     setWidget( view );
00272   }
00273 
00274   connect(this,SIGNAL(sigQueryClose(bool *, bool*)),this,SLOT(slotQueryClose_save(bool *, bool*)));
00275 
00276   m_isasking = 0;
00277 
00278   // register document in plugins
00279   KatePartPluginManager::self()->addDocument(this);
00280 }
00281 
00282 //
00283 // KateDocument Destructor
00284 //
00285 KateDocument::~KateDocument()
00286 {
00287   // Tell the world that we're about to close (== destruct)
00288   // Apps must receive this in a direct signal-slot connection, and prevent
00289   // any further use of interfaces once they return.
00290   emit aboutToClose(this);
00291 
00292   // remove file from dirwatch
00293   deactivateDirWatch ();
00294 
00295   // thanks for offering, KPart, but we're already self-destructing
00296   setAutoDeleteWidget(false);
00297   setAutoDeletePart(false);
00298 
00299   // clean up remaining views
00300   while (!m_views.isEmpty()) {
00301     delete m_views.takeFirst();
00302   }
00303 
00304   // de-register from plugin
00305   KatePartPluginManager::self()->removeDocument(this);
00306 
00307   // cu marks
00308   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
00309     delete i.value();
00310   m_marks.clear();
00311 
00312   delete m_config;
00313   KateGlobal::self()->deregisterDocument (this);
00314 }
00315 //END
00316 
00317 // on-demand view creation
00318 QWidget *KateDocument::widget()
00319 {
00320   // no singleViewMode -> no widget()...
00321   if (!singleViewMode())
00322     return 0;
00323 
00324   // does a widget exist already? use it!
00325   if (KTextEditor::Document::widget())
00326     return KTextEditor::Document::widget();
00327 
00328   // create and return one...
00329   KTextEditor::View *view = (KTextEditor::View*)createView(0);
00330   insertChildClient( view );
00331   setWidget( view );
00332   return view;
00333 }
00334 
00335 //BEGIN KTextEditor::Document stuff
00336 
00337 KTextEditor::View *KateDocument::createView( QWidget *parent )
00338 {
00339   KateView* newView = new KateView( this, parent);
00340   if ( s_fileChangedDialogsActivated )
00341     connect( newView, SIGNAL(focusIn( KTextEditor::View * )), this, SLOT(slotModifiedOnDisk()) );
00342 
00343   emit viewCreated (this, newView);
00344 
00345   return newView;
00346 }
00347 
00348 const QList<KTextEditor::View*> &KateDocument::views () const
00349 {
00350   return m_textEditViews;
00351 }
00352 
00353 KTextEditor::Editor *KateDocument::editor ()
00354 {
00355   return KateGlobal::self();
00356 }
00357 
00358 //BEGIN KTextEditor::EditInterface stuff
00359 
00360 QString KateDocument::text() const
00361 {
00362   QString s;
00363 
00364   for (int i = 0; i < m_buffer->count(); i++)
00365   {
00366     KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00367 
00368     if (textLine)
00369     {
00370       s.append (textLine->string());
00371 
00372       if ((i+1) < m_buffer->count())
00373         s.append(QChar::fromAscii('\n'));
00374     }
00375   }
00376 
00377   return s;
00378 }
00379 
00380 QString KateDocument::text( const KTextEditor::Range& range, bool blockwise ) const
00381 {
00382   if (!range.isValid()) {
00383     kWarning() << k_funcinfo << "Text requested for invalid range" << range;
00384     return QString();
00385   }
00386 
00387   if ( blockwise && (range.start().column() > range.end().column()) )
00388     return QString ();
00389 
00390   QString s;
00391 
00392   if (range.start().line() == range.end().line())
00393   {
00394     if (range.start().column() > range.end().column())
00395       return QString ();
00396 
00397     KateTextLine::Ptr textLine = m_buffer->plainLine(range.start().line());
00398 
00399     if ( !textLine )
00400       return QString ();
00401 
00402     return textLine->string(range.start().column(), range.end().column()-range.start().column());
00403   }
00404   else
00405   {
00406 
00407     for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
00408     {
00409       KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00410 
00411       if ( !blockwise )
00412       {
00413         if (i == range.start().line())
00414           s.append (textLine->string(range.start().column(), textLine->length()-range.start().column()));
00415         else if (i == range.end().line())
00416           s.append (textLine->string(0, range.end().column()));
00417         else
00418           s.append (textLine->string());
00419       }
00420       else
00421       {
00422         s.append( textLine->string( range.start().column(), range.end().column()-range.start().column()));
00423       }
00424 
00425       if ( i < range.end().line() )
00426         s.append(QChar::fromAscii('\n'));
00427     }
00428   }
00429 
00430   return s;
00431 }
00432 
00433 QChar KateDocument::character( const KTextEditor::Cursor & position ) const
00434 {
00435   KateTextLine::Ptr textLine = m_buffer->plainLine(position.line());
00436 
00437   if ( !textLine )
00438     return QChar();
00439 
00440   if (position.column() >= 0 && position.column() < textLine->string().length())
00441     return textLine->string().at(position.column());
00442 
00443   return QChar();
00444 }
00445 
00446 QStringList KateDocument::textLines( const KTextEditor::Range & range, bool blockwise ) const
00447 {
00448   QStringList ret;
00449 
00450   if (!range.isValid()) {
00451     kWarning() << k_funcinfo << "Text requested for invalid range" << range;
00452     return ret;
00453   }
00454 
00455   if ( blockwise && (range.start().column() > range.end().column()) )
00456     return ret;
00457 
00458   if (range.start().line() == range.end().line())
00459   {
00460     Q_ASSERT(range.start() <= range.end());
00461 
00462     KateTextLine::Ptr textLine = m_buffer->plainLine(range.start().line());
00463 
00464     if ( !textLine )
00465       return ret;
00466 
00467     ret << textLine->string(range.start().column(), range.end().column() - range.start().column());
00468   }
00469   else
00470   {
00471     for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
00472     {
00473       KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00474 
00475       if ( !blockwise )
00476       {
00477         if (i == range.start().line())
00478           ret << textLine->string(range.start().column(), textLine->length() - range.start().column());
00479         else if (i == range.end().line())
00480           ret << textLine->string(0, range.end().column());
00481         else
00482           ret << textLine->string();
00483       }
00484       else
00485       {
00486         ret << textLine->string(range.start().column(), range.end().column() - range.start().column());
00487       }
00488     }
00489   }
00490 
00491   return ret;
00492 }
00493 
00494 QString KateDocument::line( int line ) const
00495 {
00496   KateTextLine::Ptr l = m_buffer->plainLine(line);
00497 
00498   if (!l)
00499     return QString();
00500 
00501   return l->string();
00502 }
00503 
00504 bool KateDocument::setText(const QString &s)
00505 {
00506   if (!isReadWrite())
00507     return false;
00508 
00509   QList<KTextEditor::Mark> msave;
00510 
00511   foreach (KTextEditor::Mark* mark, m_marks)
00512     msave.append(*mark);
00513 
00514   editStart ();
00515 
00516   // delete the text
00517   clear();
00518 
00519   // insert the new text
00520   insertText (KTextEditor::Cursor(), s);
00521 
00522   editEnd ();
00523 
00524   foreach (const KTextEditor::Mark& mark, msave)
00525     setMark (mark.line, mark.type);
00526 
00527   return true;
00528 }
00529 
00530 bool KateDocument::setText( const QStringList & text )
00531 {
00532   if (!isReadWrite())
00533     return false;
00534 
00535   QList<KTextEditor::Mark> msave;
00536 
00537   foreach (KTextEditor::Mark* mark, m_marks)
00538     msave.append(*mark);
00539 
00540   editStart ();
00541 
00542   // delete the text
00543   clear();
00544 
00545   // insert the new text
00546   insertText (KTextEditor::Cursor::start(), text);
00547 
00548   editEnd ();
00549 
00550   foreach (const KTextEditor::Mark& mark, msave)
00551     setMark (mark.line, mark.type);
00552 
00553   return true;
00554 }
00555 
00556 bool KateDocument::clear()
00557 {
00558   if (!isReadWrite())
00559     return false;
00560 
00561   foreach (KateView *view, m_views) {
00562     view->clear();
00563     view->tagAll();
00564     view->update();
00565   }
00566 
00567   clearMarks ();
00568 
00569   return removeText (KTextEditor::Range(KTextEditor::Cursor(), KTextEditor::Cursor(lastLine()+1, 0)));
00570 }
00571 
00572 bool KateDocument::insertText( const KTextEditor::Cursor& position, const QString& text, bool block )
00573 {
00574   if (!isReadWrite())
00575     return false;
00576 
00577   if (text.isEmpty())
00578     return true;
00579 
00580   editStart();
00581 
00582   int currentLine = position.line();
00583   int currentLineStart = 0;
00584   int totalLength = text.length();
00585   int insertColumn = position.column();
00586 
00587   if (position.line() > lines())
00588   {
00589     int line = lines();
00590     while (line != position.line() + totalLength + 1)
00591     {
00592       editInsertLine(line,"");
00593       line++;
00594     }
00595   }
00596 
00597   bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn );
00598   int tabWidth = config()->tabWidth();
00599 
00600   static const QChar newLineChar('\n');
00601   static const QChar tabChar('\t');
00602   static const QChar spaceChar(' ');
00603 
00604   int insertColumnExpanded = insertColumn;
00605   KateTextLine::Ptr l = kateTextLine( currentLine );
00606   if (l)
00607     insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00608 
00609   int pos = 0;
00610   for (; pos < totalLength; pos++)
00611   {
00612     const QChar& ch = text.at(pos);
00613 
00614     if (ch == newLineChar)
00615     {
00616       // Only perform the text insert if there is text to insert
00617       if (currentLineStart < pos)
00618         editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00619 
00620       if ( !block )
00621       {
00622         editWrapLine(currentLine, insertColumn + pos - currentLineStart);
00623         insertColumn = 0;
00624       }
00625       else
00626       {
00627         if ( currentLine == lastLine() )
00628           editWrapLine(currentLine , insertColumn + pos - currentLineStart);
00629         insertColumn = position.column(); // tab expansion might change this
00630       }
00631 
00632       currentLine++;
00633       currentLineStart = pos + 1;
00634       l = kateTextLine( currentLine );
00635       if (l)
00636         insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00637     }
00638     else
00639     {
00640       if ( replacetabs && ch == tabChar )
00641       {
00642         int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth );
00643         editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar));
00644 
00645         insertColumn += pos - currentLineStart + spacesRequired;
00646         currentLineStart = pos + 1;
00647         l = kateTextLine( currentLine );
00648         if (l)
00649           insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00650       }
00651     }
00652   }
00653 
00654   // Only perform the text insert if there is text to insert
00655   if (currentLineStart < pos)
00656     editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00657 
00658   editEnd();
00659   return true;
00660 }
00661 
00662 bool KateDocument::insertText( const KTextEditor::Cursor & position, const QStringList & textLines, bool block )
00663 {
00664   if (!isReadWrite())
00665     return false;
00666 
00667   if (textLines.isEmpty() || (textLines.count() == 1 && textLines.first().isEmpty()))
00668     return true;
00669 
00670   // FIXME - huh, contradicted below
00671   if (position.line() > lines())
00672     return false;
00673 
00674   editStart();
00675 
00676   if (position.line() > lines())
00677     editInsertLine(position.line(),"");
00678 
00679   int currentLine = position.line();
00680   int currentLineStart = 0;
00681   int insertColumn = position.column();
00682 
00683   bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn );
00684   int tabWidth = config()->tabWidth();
00685 
00686   static const QChar newLineChar('\n');
00687   static const QChar tabChar('\t');
00688   static const QChar spaceChar(' ');
00689 
00690   int insertColumnExpanded = insertColumn;
00691   KateTextLine::Ptr l = kateTextLine( currentLine );
00692   if (l)
00693     insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00694 
00695   foreach (const QString &text, textLines)
00696   {
00697     int pos = 0;
00698     for (; pos < text.length(); pos++)
00699     {
00700       const QChar& ch = text.at(pos);
00701 
00702       if (ch == newLineChar)
00703       {
00704         // Only perform the text insert if there is text to insert
00705         if (currentLineStart < pos)
00706           editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00707 
00708         if ( !block )
00709         {
00710           editWrapLine(currentLine, pos + insertColumn);
00711           insertColumn = 0;
00712         }
00713         else
00714         {
00715           if ( currentLine == lastLine() )
00716             editWrapLine(currentLine , insertColumn + pos - currentLineStart);
00717           insertColumn = position.column(); // tab expansion might change this
00718         }
00719 
00720         currentLine++;
00721         currentLineStart = pos + 1;
00722         l = kateTextLine( currentLine );
00723         if (l)
00724           insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00725       }
00726       else
00727       {
00728         if ( replacetabs && ch == tabChar )
00729         {
00730           int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth );
00731           editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar));
00732 
00733           insertColumn += pos - currentLineStart + spacesRequired;
00734           l = kateTextLine( currentLine );
00735           if (l)
00736             insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00737           currentLineStart = pos + 1;
00738         }
00739       }
00740     }
00741 
00742     // Only perform the text insert if there is text to insert
00743     if (currentLineStart < pos - currentLineStart)
00744       editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00745   }
00746 
00747   editEnd();
00748   return true;
00749 }
00750 
00751 
00752 bool KateDocument::removeText ( const KTextEditor::Range &_range, bool block )
00753 {
00754   KTextEditor::Range range = _range;
00755 
00756   if (!isReadWrite())
00757     return false;
00758 
00759   if ( block && (range.start().column() > range.end().column()) )
00760     return false;
00761 
00762   // Should now be impossible to trigger with the new Range class
00763   Q_ASSERT( range.start().line() <= range.end().line() );
00764 
00765   if ( range.start().line() > lastLine() )
00766     return false;
00767 
00768   if (!block)
00769     emit aboutToRemoveText(range);
00770 
00771   editStart();
00772 
00773   if ( !block )
00774   {
00775     if ( range.end().line() > lastLine() )
00776     {
00777       range.end().setPosition(lastLine()+1, 0);
00778     }
00779 
00780     if (range.onSingleLine())
00781     {
00782       editRemoveText(range.start().line(), range.start().column(), range.columnWidth());
00783     }
00784     else if (range.start().line() + 1 == range.end().line())
00785     {
00786       if ( (m_buffer->plainLine(range.start().line())->length() - range.start().column()) > 0 )
00787         editRemoveText(range.start().line(), range.start().column(), m_buffer->plainLine(range.start().line())->length() - range.start().column());
00788 
00789       if (range.end().column())
00790         editRemoveText (range.start().line() + 1, 0, range.end().column());
00791 
00792       editUnWrapLine (range.start().line());
00793     }
00794     else
00795     {
00796       for (int line = range.end().line(); line >= range.start().line(); --line)
00797       {
00798         if ((line > range.start().line()) && (line < range.end().line())) {
00799           editRemoveLine(line);
00800 
00801         } else if (line == range.end().line()) {
00802           if ( range.end().line() <= lastLine() )
00803             editRemoveText(line, 0, range.end().column());
00804 
00805         } else {
00806           if ( (m_buffer->plainLine(line)->length() - range.start().column()) > 0 )
00807             editRemoveText(line, range.start().column(), m_buffer->plainLine(line)->length() - range.start().column());
00808 
00809           editUnWrapLine(range.start().line());
00810         }
00811 
00812         if ( line == 0 )
00813           break;
00814       }
00815     }
00816   } // if ( ! block )
00817   else
00818   {
00819     int startLine = qMax(0, range.start().line());
00820     for (int line = qMin(range.end().line(), lastLine()); line >= startLine; --line)
00821       editRemoveText(line, range.start().column(), range.end().column() - range.start().column());
00822   }
00823 
00824   editEnd ();
00825   emit textRemoved();
00826   return true;
00827 }
00828 
00829 bool KateDocument::insertLine( int l, const QString &str )
00830 {
00831   if (!isReadWrite())
00832     return false;
00833 
00834   if (l < 0 || l > lines())
00835     return false;
00836 
00837   return editInsertLine (l, str);
00838 }
00839 
00840 bool KateDocument::insertLines( int line, const QStringList & text )
00841 {
00842   if (!isReadWrite())
00843     return false;
00844 
00845   if (line < 0 || line > lines())
00846     return false;
00847 
00848   bool success = true;
00849   foreach (const QString &string, text)
00850     // FIXME assumes no \n in each string
00851     success &= editInsertLine (line++, string);
00852 
00853   return success;
00854 }
00855 
00856 bool KateDocument::removeLine( int line )
00857 {
00858   if (!isReadWrite())
00859     return false;
00860 
00861   if (line < 0 || line > lastLine())
00862     return false;
00863 
00864   return editRemoveLine (line);
00865 }
00866 
00867 int KateDocument::totalCharacters() const
00868 {
00869   int l = 0;
00870 
00871   for (int i = 0; i < m_buffer->count(); ++i)
00872   {
00873     KateTextLine::Ptr line = m_buffer->plainLine(i);
00874 
00875     if (line)
00876       l += line->length();
00877   }
00878 
00879   return l;
00880 }
00881 
00882 int KateDocument::lines() const
00883 {
00884   return m_buffer->count();
00885 }
00886 
00887 int KateDocument::numVisLines() const
00888 {
00889   return m_buffer->countVisible ();
00890 }
00891 
00892 int KateDocument::lineLength ( int line ) const
00893 {
00894   if (line < 0 || line > lastLine())
00895     return -1;
00896 
00897   KateTextLine::Ptr l = m_buffer->plainLine(line);
00898 
00899   if (!l)
00900     return -1;
00901 
00902   return l->length();
00903 }
00904 //END
00905 
00906 //BEGIN KTextEditor::EditInterface internal stuff
00907 //
00908 // Starts an edit session with (or without) undo, update of view disabled during session
00909 //
00910 void KateDocument::editStart (bool withUndo, Kate::EditSource editSource)
00911 {
00912   editSessionNumber++;
00913 
00914   if (editSource == Kate::NoEditSource)
00915     m_editSources.push(m_editSources.isEmpty() ? Kate::UserInputEdit : m_editSources.top());
00916   else
00917     m_editSources.push(editSource);
00918 
00919   // Unlocked in editEnd
00920   smartMutex()->lock();
00921 
00922   if (editSessionNumber > 1)
00923     return;
00924 
00925   editIsRunning = true;
00926   editWithUndo = withUndo;
00927 
00928   if (editWithUndo)
00929     m_undoManager->editStart();
00930 
00931   foreach(KateView *view,m_views)
00932   {
00933     view->editStart ();
00934   }
00935 
00936   m_buffer->editStart ();
00937 }
00938 
00939 void KateDocument::undoSafePoint() {
00940   m_undoManager->undoSafePoint();
00941 }
00942 
00943 //
00944 // End edit session and update Views
00945 //
00946 void KateDocument::editEnd ()
00947 {
00948   if (editSessionNumber == 0) {
00949     //Dangerous, because bad editStart() editEnd() mismatches defeat the smart-lock protection,
00950     //which can lead to random crashes
00951     Q_ASSERT(0);
00952     return;
00953   }
00954 
00955   // wrap the new/changed text, if something really changed!
00956   if (m_buffer->editChanged() && (editSessionNumber == 1))
00957     if (editWithUndo && config()->wordWrap())
00958       wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
00959 
00960   editSessionNumber--;
00961 
00962   m_editSources.pop();
00963 
00964   // Was locked in editStart
00965   smartMutex()->unlock();
00966 
00967   if (editSessionNumber > 0)
00968     return;
00969 
00970   // end buffer edit, will trigger hl update
00971   // this will cause some possible adjustment of tagline start/end
00972   m_buffer->editEnd ();
00973 
00974   if (editWithUndo)
00975     m_undoManager->editEnd();
00976 
00977   // edit end for all views !!!!!!!!!
00978   foreach(KateView *view, m_views)
00979     view->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
00980 
00981   if (m_buffer->editChanged())
00982   {
00983     setModified(true);
00984     emit textChanged (this);
00985   }
00986 
00987   editIsRunning = false;
00988 }
00989 
00990 void KateDocument::pushEditState ()
00991 {
00992   editStateStack.push(editSessionNumber);
00993 }
00994 
00995 void KateDocument::popEditState ()
00996 {
00997   if (editStateStack.isEmpty()) return;
00998 
00999   int count = editStateStack.pop() - editSessionNumber;
01000   while (count < 0) { ++count; editEnd(); }
01001   while (count > 0) { --count; editStart(); }
01002 }
01003 
01004 void KateDocument::inputMethodStart()
01005 {
01006   editStart(false);
01007 }
01008 
01009 void KateDocument::inputMethodEnd()
01010 {
01011   editEnd();
01012 }
01013 
01014 bool KateDocument::wrapText(int startLine, int endLine)
01015 {
01016   if (startLine < 0 || endLine < 0)
01017     return false;
01018 
01019   if (!isReadWrite())
01020     return false;
01021 
01022   int col = config()->wordWrapAt();
01023 
01024   if (col == 0)
01025     return false;
01026 
01027   editStart ();
01028 
01029   for (int line = startLine; (line <= endLine) && (line < lines()); line++)
01030   {
01031     KateTextLine::Ptr l = kateTextLine(line);
01032 
01033     if (!l)
01034       return false;
01035 
01036     kDebug (13020) << "try wrap line: " << line;
01037 
01038     if (l->virtualLength(m_buffer->tabWidth()) > col)
01039     {
01040       KateTextLine::Ptr nextl = kateTextLine(line+1);
01041 
01042       kDebug (13020) << "do wrap line: " << line;
01043 
01044       int eolPosition = l->length()-1;
01045 
01046       // take tabs into account here, too
01047       int x = 0;
01048       const QString & t = l->string();
01049       int z2 = 0;
01050       for ( ; z2 < l->length(); z2++)
01051       {
01052         static const QChar tabChar('\t');
01053         if (t.at(z2) == tabChar)
01054           x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
01055         else
01056           x++;
01057 
01058         if (x > col)
01059           break;
01060       }
01061 
01062       int searchStart = qMin (z2, l->length()-1);
01063 
01064       // If where we are wrapping is an end of line and is a space we don't
01065       // want to wrap there
01066       if (searchStart == eolPosition && t.at(searchStart).isSpace())
01067         searchStart--;
01068 
01069       // Scan backwards looking for a place to break the line
01070       // We are not interested in breaking at the first char
01071       // of the line (if it is a space), but we are at the second
01072       // anders: if we can't find a space, try breaking on a word
01073       // boundary, using KateHighlight::canBreakAt().
01074       // This could be a priority (setting) in the hl/filetype/document
01075       int z = 0;
01076       int nw = 0; // alternative position, a non word character
01077       for (z=searchStart; z > 0; z--)
01078       {
01079         if (t.at(z).isSpace()) break;
01080         if ( ! nw && highlight()->canBreakAt( t.at(z) , l->attribute(z) ) )
01081         nw = z;
01082       }
01083 
01084       bool removeTrailingSpace = false;
01085       if (z > 0)
01086       {
01087         // So why don't we just remove the trailing space right away?
01088         // Well, the (view's) cursor may be directly in front of that space
01089         // (user typing text before the last word on the line), and if that
01090         // happens, the cursor would be moved to the next line, which is not
01091         // what we want (bug #106261)
01092         z++;
01093         removeTrailingSpace = true;
01094       }
01095       else
01096       {
01097         // There was no space to break at so break at a nonword character if
01098         // found, or at the wrapcolumn ( that needs be configurable )
01099         // Don't try and add any white space for the break
01100         if ( nw && nw < col ) nw++; // break on the right side of the character
01101         z = nw ? nw : col;
01102       }
01103 
01104       if (nextl && !nextl->isAutoWrapped())
01105       {
01106         editWrapLine (line, z, true);
01107         editMarkLineAutoWrapped (line+1, true);
01108 
01109         endLine++;
01110       }
01111       else
01112       {
01113         if (nextl && (nextl->length() > 0) && !nextl->at(0).isSpace() && ((l->length() < 1) || !l->at(l->length()-1).isSpace()))
01114           editInsertText (line+1, 0, QString (" "));
01115 
01116         bool newLineAdded = false;
01117         editWrapLine (line, z, false, &newLineAdded);
01118 
01119         editMarkLineAutoWrapped (line+1, true);
01120 
01121         endLine++;
01122       }
01123 
01124       if (removeTrailingSpace) {
01125         // cu space
01126         editRemoveText (line, z - 1, 1);
01127       }
01128     }
01129   }
01130 
01131   editEnd ();
01132 
01133   return true;
01134 }
01135 
01136 bool KateDocument::editInsertText ( int line, int col, const QString &s, Kate::EditSource editSource )
01137 {
01138   if (line < 0 || col < 0)
01139     return false;
01140 
01141   if (!isReadWrite())
01142     return false;
01143 
01144   KateTextLine::Ptr l = kateTextLine(line);
01145 
01146   if (!l)
01147     return false;
01148 
01149   editStart (true, editSource);
01150 
01151   m_undoManager->slotTextInserted(line, col, s);
01152 
01153   l->insertText (col, s);
01154 
01155   m_buffer->changeLine(line);
01156 
01157   history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col), QStringList(), KTextEditor::Range(line, col, line, col + s.length()), QStringList(s)) );
01158   emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line, col + s.length()));
01159 
01160   editEnd();
01161 
01162   return true;
01163 }
01164 
01165 bool KateDocument::editRemoveText ( int line, int col, int len, Kate::EditSource editSource )
01166 {
01167   if (line < 0 || col < 0 || len < 0)
01168     return false;
01169 
01170   if (!isReadWrite())
01171     return false;
01172 
01173   KateTextLine::Ptr l = kateTextLine(line);
01174 
01175   if (!l)
01176     return false;
01177 
01178   editStart (true, editSource);
01179 
01180   m_undoManager->slotTextRemoved(line, col, l->string().mid(col, len));
01181 
01182   l->removeText (col, len);
01183   removeTrailingSpace( line );
01184 
01185   m_buffer->changeLine(line);
01186 
01187   history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col + len), QStringList(l->string().mid(col, len)), KTextEditor::Range(line, col, line, col), QStringList()) );
01188   emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len));
01189 
01190   editEnd ();
01191 
01192   return true;
01193 }
01194 
01195 bool KateDocument::editMarkLineAutoWrapped ( int line, bool autowrapped )
01196 {
01197   if (line < 0)
01198     return false;
01199 
01200   if (!isReadWrite())
01201     return false;
01202 
01203   KateTextLine::Ptr l = kateTextLine(line);
01204 
01205   if (!l)
01206     return false;
01207 
01208   editStart ();
01209 
01210   m_undoManager->slotMarkLineAutoWrapped(line, autowrapped);
01211 
01212   l->setAutoWrapped (autowrapped);
01213 
01214   m_buffer->changeLine(line);
01215 
01216   editEnd ();
01217 
01218   return true;
01219 }
01220 
01221 bool KateDocument::editWrapLine ( int line, int col, bool newLine, bool *newLineAdded)
01222 {
01223   if (line < 0 || col < 0)
01224     return false;
01225 
01226   if (!isReadWrite())
01227     return false;
01228 
01229   KateTextLine::Ptr l = kateTextLine(line);
01230 
01231   if (!l)
01232     return false;
01233 
01234   editStart ();
01235 
01236   KateTextLine::Ptr nextLine = kateTextLine(line+1);
01237 
01238   int pos = l->length() - col;
01239 
01240   if (pos < 0)
01241     pos = 0;
01242 
01243   m_undoManager->slotLineWrapped(line, col, pos, (!nextLine || newLine));
01244 
01245   if (!nextLine || newLine)
01246   {
01247     KateTextLine::Ptr textLine(new KateTextLine());
01248 
01249     textLine->insertText (0, l->string().mid(col, pos));
01250     l->truncate(col);
01251 
01252     m_buffer->insertLine (line+1, textLine);
01253     m_buffer->changeLine(line);
01254 
01255     QList<KTextEditor::Mark*> list;
01256     for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01257     {
01258       if( i.value()->line >= line )
01259       {
01260         if ((col == 0) || (i.value()->line > line))
01261           list.append( i.value() );
01262       }
01263     }
01264 
01265     for( int i=0; i < list.size(); ++i )
01266       m_marks.take( list[i]->line );
01267 
01268     for( int i=0; i < list.size(); ++i )
01269     {
01270       list[i]->line++;
01271       m_marks.insert( list[i]->line, list[i] );
01272     }
01273 
01274     if( !list.isEmpty() )
01275       emit marksChanged( this );
01276 
01277     // yes, we added a new line !
01278     if (newLineAdded)
01279       (*newLineAdded) = true;
01280 
01281     history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col), QStringList(), KTextEditor::Range(line, col, line+1, 0), QStringList()) );
01282   }
01283   else
01284   {
01285     nextLine->insertText (0, l->string().mid(col, pos));
01286     l->truncate(col);
01287 
01288     m_buffer->changeLine(line);
01289     m_buffer->changeLine(line+1);
01290 
01291     // no, no new line added !
01292     if (newLineAdded)
01293       (*newLineAdded) = false;
01294 
01295     history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line+1, 0), QStringList(), KTextEditor::Range(line, col, line+1, pos), QStringList()) );
01296   }
01297 
01298   emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line+1, pos));
01299 
01300   editEnd ();
01301 
01302   return true;
01303 }
01304 
01305 bool KateDocument::editUnWrapLine ( int line, bool removeLine, int length )
01306 {
01307   if (line < 0 || length < 0)
01308     return false;
01309 
01310   if (!isReadWrite())
01311     return false;
01312 
01313   KateTextLine::Ptr l = kateTextLine(line);
01314   KateTextLine::Ptr nextLine = kateTextLine(line+1);
01315 
01316   if (!l || !nextLine)
01317     return false;
01318 
01319   editStart ();
01320 
01321   int col = l->length ();
01322 
01323   m_undoManager->slotLineUnWrapped(line, col, length, removeLine);
01324 
01325   if (removeLine)
01326   {
01327     l->insertText (col, nextLine->string());
01328 
01329     m_buffer->changeLine(line);
01330     m_buffer->removeLine(line+1);
01331   }
01332   else
01333   {
01334     l->insertText (col, nextLine->string().left((nextLine->length() < length) ? nextLine->length() : length));
01335     nextLine->removeText (0, (nextLine->length() < length) ? nextLine->length() : length);
01336 
01337     m_buffer->changeLine(line);
01338     m_buffer->changeLine(line+1);
01339   }
01340 
01341   QList<KTextEditor::Mark*> list;
01342   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01343   {
01344     if( i.value()->line >= line+1 )
01345       list.append( i.value() );
01346 
01347     if ( i.value()->line == line+1 )
01348     {
01349       KTextEditor::Mark* mark = m_marks.take( line );
01350 
01351       if (mark)
01352       {
01353         i.value()->type |= mark->type;
01354       }
01355     }
01356   }
01357 
01358    for( int i=0; i < list.size(); ++i )
01359       m_marks.take( list[i]->line );
01360 
01361    for( int i=0; i < list.size(); ++i )
01362    {
01363       list[i]->line--;
01364       m_marks.insert( list[i]->line, list[i] );
01365     }
01366 
01367   if( !list.isEmpty() )
01368     emit marksChanged( this );
01369 
01370   history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line+1, 0), QStringList(QString()), KTextEditor::Range(line, col, line, col), QStringList()) );
01371   emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line+1, 0));
01372 
01373   editEnd ();
01374 
01375   return true;
01376 }
01377 
01378 bool KateDocument::editInsertLine ( int line, const QString &s, Kate::EditSource editSource )
01379 {
01380   if (line < 0)
01381     return false;
01382 
01383   if (!isReadWrite())
01384     return false;
01385 
01386   if ( line > lines() )
01387     return false;
01388 
01389   editStart (true, editSource);
01390 
01391   m_undoManager->slotLineInserted(line, s);
01392 
01393   removeTrailingSpace( line ); // old line
01394 
01395   KateTextLine::Ptr tl(new KateTextLine());
01396   tl->insertText (0, s);
01397   m_buffer->insertLine(line, tl);
01398   m_buffer->changeLine(line);
01399 
01400   removeTrailingSpace( line ); // new line
01401 
01402   QList<KTextEditor::Mark*> list;
01403   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01404   {
01405     if( i.value()->line >= line )
01406       list.append( i.value() );
01407   }
01408 
01409    for( int i=0; i < list.size(); ++i )
01410       m_marks.take( list[i]->line );
01411 
01412    for( int i=0; i < list.size(); ++i )
01413    {
01414       list[i]->line++;
01415       m_marks.insert( list[i]->line, list[i] );
01416     }
01417 
01418   if( !list.isEmpty() )
01419     emit marksChanged( this );
01420 
01421   KTextEditor::Range rangeInserted(line, 0, line, tl->length());
01422 
01423   if (line) {
01424     KateTextLine::Ptr prevLine = plainKateTextLine(line - 1);
01425     rangeInserted.start().setPosition(line - 1, prevLine->length());
01426   } else {
01427     rangeInserted.end().setPosition(line + 1, 0);
01428   }
01429 
01430   history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(rangeInserted.start(), rangeInserted.start()), QStringList(), rangeInserted, QStringList(s)) );
01431   emit KTextEditor::Document::textInserted(this, rangeInserted);
01432 
01433   editEnd ();
01434 
01435   return true;
01436 }
01437 
01438 bool KateDocument::editRemoveLine ( int line, Kate::EditSource editSource )
01439 {
01440   if (line < 0)
01441     return false;
01442 
01443   if (!isReadWrite())
01444     return false;
01445 
01446   if ( line > lastLine() )
01447     return false;
01448 
01449   if ( lines() == 1 )
01450     return editRemoveText (0, 0, kateTextLine(0)->length());
01451 
01452   KateLineInfo info;;
01453   lineInfo (&info, line);
01454   if (info.startsInVisibleBlock)
01455     foldingTree()->toggleRegionVisibility(line);
01456 
01457   editStart (true, editSource);
01458 
01459   QString oldText = this->line(line);
01460 
01461   m_undoManager->slotLineRemoved(line, this->line(line));
01462 
01463   KTextEditor::Range rangeRemoved(line, 0, line, oldText.length());
01464 
01465   if (line < lastLine()) {
01466     rangeRemoved.end().setPosition(line + 1, 0);
01467   } else if (line) {
01468     KateTextLine::Ptr prevLine = plainKateTextLine(line - 1);
01469     rangeRemoved.start().setPosition(line - 1, prevLine->length());
01470   }
01471 
01472   m_buffer->removeLine(line);
01473 
01474   KTextEditor::Mark* rmark = 0;
01475   QList<KTextEditor::Mark*> list;
01476   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01477   {
01478     if ( (i.value()->line > line) )
01479       list.append( i.value() );
01480     else if ( (i.value()->line == line) )
01481       rmark = i.value();
01482   }
01483 
01484   if (rmark)
01485     delete (m_marks.take (rmark->line));
01486 
01487   for( int i=0; i < list.size(); ++i )
01488   {
01489     KTextEditor::Mark* mark = m_marks.take( list[i]->line );
01490     mark->line--;
01491     m_marks.insert( mark->line, mark );
01492   }
01493 
01494   if( !list.isEmpty() )
01495     emit marksChanged( this );
01496 
01497   history()->doEdit( new KateEditInfo(m_editSources.top(), rangeRemoved, QStringList(QString(oldText)), KTextEditor::Range(rangeRemoved.start(), rangeRemoved.start()), QStringList()) );
01498   emit KTextEditor::Document::textRemoved(this, rangeRemoved);
01499 
01500   editEnd();
01501 
01502   return true;
01503 }
01504 //END
01505 
01506 //BEGIN KTextEditor::UndoInterface stuff
01507 
01508 uint KateDocument::undoCount () const
01509 {
01510   return m_undoManager->undoCount ();
01511 }
01512 
01513 uint KateDocument::redoCount () const
01514 {
01515   return m_undoManager->redoCount ();
01516 }
01517 
01518 void KateDocument::undo()
01519 {
01520   m_undoManager->undo();
01521 }
01522 
01523 void KateDocument::redo()
01524 {
01525   m_undoManager->redo();
01526 }
01527 //END
01528 
01529 //BEGIN KTextEditor::SearchInterface stuff
01530 
01531 KTextEditor::Range KateDocument::searchText (const KTextEditor::Range & inputRange, const QString &text, bool casesensitive, bool backwards)
01532 {
01533   FAST_DEBUG("KateDocument::searchText( " << inputRange.start().line() << ", "
01534     << inputRange.start().column() << ", " << text << ", " << backwards << " )");
01535   if (text.isEmpty() || !inputRange.isValid() || (inputRange.start() == inputRange.end()))
01536   {
01537     return KTextEditor::Range::invalid();
01538   }
01539 
01540   // split multi-line needle into single lines
01541   const QString sep("\n");
01542   const QStringList needleLines = text.split(sep);
01543   const int numNeedleLines = needleLines.count();
01544   FAST_DEBUG("searchText | needle has " << numNeedleLines << " lines");
01545 
01546   if (numNeedleLines > 1)
01547   {
01548     // multi-line plaintext search (both forwards or backwards)
01549     const int lastLine = inputRange.end().line();
01550 
01551     const int forMin   = inputRange.start().line(); // first line in range
01552     const int forMax   = lastLine + 1 - numNeedleLines; // last line in range
01553     const int forInit  = backwards ? forMax : forMin;
01554     const int forInc   = backwards ? -1 : +1;
01555 
01556     const int minLeft  = inputRange.start().column();
01557     const uint maxRight = inputRange.end().column(); // first not included
01558 
01559     // init hay line ring buffer
01560     int hayLinesZeroIndex = 0;
01561     QVector<KateTextLine::Ptr> hayLinesWindow;
01562     for (int i = 0; i < numNeedleLines; i++) {
01563       KateTextLine::Ptr textLine = m_buffer->plainLine((backwards ? forMax : forMin) + i);
01564 
01565       if (!textLine)
01566         return KTextEditor::Range::invalid();
01567 
01568       hayLinesWindow.append (textLine);
01569       FAST_DEBUG("searchText | hayLinesWindow[" << i << "] = \"" << hayLinesWindow[i]->string() << "\"");
01570     }
01571 
01572     for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01573     {
01574       // try to match all lines
01575       uint startCol = 0; // init value not important
01576       for (int k = 0; k < numNeedleLines; k++)
01577       {
01578         // which lines to compare
01579         const QString & needleLine = needleLines[k];
01580         KateTextLine::Ptr & hayLine = hayLinesWindow[(k + hayLinesZeroIndex) % numNeedleLines];
01581         FAST_DEBUG("searchText | hayLine = \"" << hayLine->string() << "\"");
01582 
01583         // position specific comparison (first, middle, last)
01584         if (k == 0) {
01585           // first line
01586           if (needleLine.length() == 0) // if needle starts with a newline
01587           {
01588             startCol = hayLine->length();
01589           }
01590           else
01591           {
01592             uint myMatchLen;
01593             const uint colOffset = (j > forMin) ? 0 : minLeft;
01594             const bool matches = hayLine->searchText(colOffset, hayLine->length(),needleLine, &startCol,
01595               &myMatchLen, casesensitive, false);
01596             if (!matches || (startCol + myMatchLen != static_cast<uint>(hayLine->length()))) {
01597               FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01598               break;
01599             }
01600             FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01601           }
01602         } else if (k == numNeedleLines - 1) {
01603           // last line
01604           uint foundAt, myMatchLen;
01605           const bool matches = hayLine->searchText(0,hayLine->length(), needleLine, &foundAt, &myMatchLen, casesensitive, false);
01606           if (matches && (foundAt == 0) && !((k == lastLine)
01607               && (static_cast<uint>(foundAt + myMatchLen) > maxRight))) // full match!
01608           {
01609             FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01610             return KTextEditor::Range(j, startCol, j + k, needleLine.length());
01611           }
01612           FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01613         } else {
01614           // mid lines
01615           uint foundAt, myMatchLen;
01616           const bool matches = hayLine->searchText(0, hayLine->length(),needleLine, &foundAt, &myMatchLen, casesensitive, false);
01617           if (!matches || (foundAt != 0) || (myMatchLen != static_cast<uint>(needleLine.length()))) {
01618             FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01619             break;
01620           }
01621           FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01622         }
01623       }
01624 
01625       // get a fresh line into the ring buffer
01626       if ((backwards && (j > forMin)) || (!backwards && (j < forMax)))
01627       {
01628         if (backwards)
01629         {
01630           hayLinesZeroIndex = (hayLinesZeroIndex + numNeedleLines - 1) % numNeedleLines;
01631 
01632           KateTextLine::Ptr textLine = m_buffer->plainLine(j - 1);
01633 
01634           if (!textLine)
01635             return KTextEditor::Range::invalid();
01636 
01637           hayLinesWindow[hayLinesZeroIndex] = textLine;
01638 
01639           FAST_DEBUG("searchText | filling slot " << hayLinesZeroIndex << " with line "
01640             << j - 1 << ": " << hayLinesWindow[hayLinesZeroIndex]->string());
01641         }
01642         else
01643         {
01644           hayLinesWindow[hayLinesZeroIndex] = m_buffer->plainLine(j + numNeedleLines);
01645           FAST_DEBUG("searchText | filling slot " << hayLinesZeroIndex << " with line "
01646             << j + numNeedleLines << ": " << hayLinesWindow[hayLinesZeroIndex]->string());
01647           hayLinesZeroIndex = (hayLinesZeroIndex + 1) % numNeedleLines;
01648         }
01649       }
01650     }
01651 
01652     // not found
01653     return KTextEditor::Range::invalid();
01654   }
01655   else
01656   {
01657     // single-line plaintext search (both forward of backward mode)
01658     const int minLeft  = inputRange.start().column();
01659     const uint maxRight = inputRange.end().column(); // first not included
01660     const int forMin   = inputRange.start().line();
01661     const int forMax   = inputRange.end().line();
01662     const int forInit  = backwards ? forMax : forMin;
01663     const int forInc   = backwards ? -1 : +1;
01664     FAST_DEBUG("searchText | single line " << (backwards ? forMax : forMin) << ".."
01665       << (backwards ? forMin : forMax));
01666     for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01667     {
01668       KateTextLine::Ptr textLine = m_buffer->plainLine(j);
01669       if (!textLine)
01670       {
01671         FAST_DEBUG("searchText | line " << j << ": no");
01672         return KTextEditor::Range::invalid();
01673       }
01674 
01675       const int offset = (j == forMin) ? minLeft : 0;
01676       const int line_end= (j==forMax) ? maxRight : textLine->length();
01677       uint foundAt, myMatchLen;
01678       FAST_DEBUG("searchText | searching in line line: " << j);
01679       const bool found = textLine->searchText (offset,line_end, text, &foundAt, &myMatchLen, casesensitive, backwards);
01680       if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
01681       {
01682         FAST_DEBUG("searchText | line " << j << ": yes");
01683         return KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
01684       }
01685       else
01686       {
01687         FAST_DEBUG("searchText | line " << j << ": no");
01688       }
01689     }
01690   }
01691   return KTextEditor::Range::invalid();
01692 }
01693 
01694 
01695 
01696 // helper structs for captures re-construction
01697 struct TwoViewCursor {
01698   int index;
01699   int openLine;
01700   int openCol;
01701   int closeLine;
01702   int closeCol;
01703   // note: open/close distinction does not seem needed
01704   // anymore. i keep it to make a potential way back
01705   // easier. overhead is minimal.
01706 };
01707 
01708 struct IndexPair {
01709   int openIndex;
01710   int closeIndex;
01711 };
01712 
01713 
01714 
01715 QVector<KTextEditor::Range> KateDocument::searchRegex(
01716     const KTextEditor::Range & inputRange,
01717     QRegExp &regexp,
01718     bool backwards)
01719 {
01720   FAST_DEBUG("KateDocument::searchRegex( " << inputRange.start().line() << ", "
01721     << inputRange.start().column() << ", " << regexp.pattern() << ", " << backwards << " )");
01722   if (regexp.isEmpty() || !regexp.isValid() || !inputRange.isValid() || (inputRange.start() == inputRange.end()))
01723   {
01724     QVector<KTextEditor::Range> result;
01725     result.append(KTextEditor::Range::invalid());
01726     return result;
01727   }
01728 
01729 
01730   // detect pattern type (single- or mutli-line)
01731   bool isMultiLine;
01732   QString multiLinePattern = regexp.pattern();
01733 
01734   // detect '.' and '\s' and fix them
01735   const bool dotMatchesNewline = false; // TODO
01736   const int replacements = KateDocument::repairPattern(multiLinePattern, isMultiLine);
01737   if (dotMatchesNewline && (replacements > 0))
01738   {
01739     isMultiLine = true;
01740   }
01741 
01742   const int firstLineIndex = inputRange.start().line();
01743   const int minColStart = inputRange.start().column();
01744 //  const int maxColEnd = inputRange.end().column();
01745   if (isMultiLine)
01746   {
01747     // multi-line regex search (both forward and backward mode)
01748     QString wholeDocument;
01749     const int inputLineCount = inputRange.end().line() - inputRange.start().line() + 1;
01750     FAST_DEBUG("multi line search (lines " << firstLineIndex << ".." << firstLineIndex + inputLineCount - 1 << ")");
01751 
01752     // nothing to do...
01753     if (firstLineIndex >= m_buffer->count())
01754     {
01755       QVector<KTextEditor::Range> result;
01756       result.append(KTextEditor::Range::invalid());
01757       return result;
01758     }
01759 
01760     QVector<int> lineLens (inputLineCount);
01761 
01762     // first line
01763     KateTextLine::Ptr firstLine = m_buffer->plainLine(firstLineIndex);
01764     if (!firstLine)
01765     {
01766       QVector<KTextEditor::Range> result;
01767       result.append(KTextEditor::Range::invalid());
01768       return result;
01769     }
01770 
01771     QString firstLineText = firstLine->string();
01772     const int firstLineLen = firstLineText.length() - minColStart;
01773     wholeDocument.append(firstLineText.right(firstLineLen));
01774     lineLens[0] = firstLineLen;
01775     FAST_DEBUG("  line" << 0 << "has length" << lineLens[0]);
01776 
01777     // second line and after
01778     const QString sep("\n");
01779     for (int i = 1; i < inputLineCount; i++)
01780     {
01781       KateTextLine::Ptr textLine = m_buffer->plainLine(firstLineIndex + i);
01782       if (!textLine)
01783       {
01784         QVector<KTextEditor::Range> result;
01785         result.append(KTextEditor::Range::invalid());
01786         return result;
01787       }
01788 
01789       QString text = textLine->string();
01790       lineLens[i] = text.length();
01791       wholeDocument.append(sep);
01792       wholeDocument.append(text);
01793       FAST_DEBUG("  line" << i << "has length" << lineLens[i]);
01794     }
01795 
01796     // apply modified pattern
01797     regexp.setPattern(multiLinePattern);
01798     const int pos = backwards
01799         ? KateDocument::fixedLastIndexIn(regexp, wholeDocument, -1, QRegExp::CaretAtZero)
01800         : regexp.indexIn(wholeDocument, 0, QRegExp::CaretAtZero);
01801     if (pos == -1)
01802     {
01803       // no match
01804       FAST_DEBUG("not found");
01805       {
01806         QVector<KTextEditor::Range> result;
01807         result.append(KTextEditor::Range::invalid());
01808         return result;
01809       }
01810     }
01811 
01812 #ifdef FAST_DEBUG_ENABLE
01813     const int matchLen = regexp.matchedLength();
01814     FAST_DEBUG("found at relative pos " << pos << ", length " << matchLen);
01815 #endif
01816 
01817     // save opening and closing indices and build a map.
01818     // the correct values will be written into it later.
01819     QMap<int, TwoViewCursor *> indicesToCursors;
01820     const int numCaptures = regexp.numCaptures();
01821     QVector<IndexPair> indexPairs(1 + numCaptures);
01822     for (int z = 0; z <= numCaptures; z++)
01823     {
01824       const int openIndex = regexp.pos(z);
01825       IndexPair & pair = indexPairs[z];
01826       if (openIndex == -1)
01827       {
01828         // empty capture gives invalid
01829         pair.openIndex = -1;
01830         pair.closeIndex = -1;
01831         FAST_DEBUG("capture []");
01832       }
01833       else
01834       {
01835         const int closeIndex = openIndex + regexp.cap(z).length();
01836         pair.openIndex = openIndex;
01837         pair.closeIndex = closeIndex;
01838         FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]");
01839 
01840         // each key no more than once
01841         if (!indicesToCursors.contains(openIndex))
01842         {
01843           TwoViewCursor * twoViewCursor = new TwoViewCursor;
01844           twoViewCursor->index = openIndex;
01845           indicesToCursors.insert(openIndex, twoViewCursor);
01846           FAST_DEBUG("  border index added: " << openIndex);
01847         }
01848         if (!indicesToCursors.contains(closeIndex))
01849         {
01850           TwoViewCursor * twoViewCursor = new TwoViewCursor;
01851           twoViewCursor->index = closeIndex;
01852           indicesToCursors.insert(closeIndex, twoViewCursor);
01853           FAST_DEBUG("  border index added: " << closeIndex);
01854         }
01855       }
01856     }
01857 
01858     // find out where they belong
01859     int curRelLine = 0;
01860     int curRelCol = 0;
01861     int curRelIndex = 0;
01862     QMap<int, TwoViewCursor *>::const_iterator iter = indicesToCursors.constBegin();
01863     while (iter != indicesToCursors.constEnd())
01864     {
01865       // forward to index, save line/col
01866       const int index = (*iter)->index;
01867       FAST_DEBUG("resolving position" << index);
01868       TwoViewCursor & twoViewCursor = *(*iter);
01869       while (curRelIndex <= index)
01870       {
01871         FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = "
01872             << curRelIndex << "relative, steps more to go" << index - curRelIndex);
01873         const int curRelLineLen = lineLens[curRelLine];
01874         const int curLineRemainder = curRelLineLen - curRelCol;
01875         const int lineFeedIndex = curRelIndex + curLineRemainder;
01876         if (index <= lineFeedIndex) {
01877             if (index == lineFeedIndex) {
01878                 // on this line _on_ line feed
01879                 FAST_DEBUG("  on line feed");
01880                 const int absLine = curRelLine + firstLineIndex;
01881                 twoViewCursor.openLine
01882                     = twoViewCursor.closeLine
01883                     = absLine;
01884                 twoViewCursor.openCol
01885                     = twoViewCursor.closeCol
01886                     = ((curRelLine == 0) ? minColStart : 0) + curRelLineLen;
01887 
01888                 // advance to next line
01889                 const int advance = (index - curRelIndex) + 1;
01890                 curRelLine++;
01891                 curRelCol = 0;
01892                 curRelIndex += advance;
01893             } else { // index < lineFeedIndex
01894                 // on this line _before_ line feed
01895                 FAST_DEBUG("  before line feed");
01896                 const int diff = (index - curRelIndex);
01897                 const int absLine = curRelLine + firstLineIndex;
01898                 const int absCol = ((curRelLine == 0) ? minColStart : 0) + curRelCol + diff;
01899                 twoViewCursor.openLine
01900                     = twoViewCursor.closeLine
01901                     = absLine;
01902                 twoViewCursor.openCol
01903                     = twoViewCursor.closeCol
01904                     = absCol;
01905 
01906                 // advance on same line
01907                 const int advance = diff + 1;
01908                 curRelCol += advance;
01909                 curRelIndex += advance;
01910             }
01911             FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol
01912                 << ")  close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")");
01913         }
01914         else // if (index > lineFeedIndex)
01915         {
01916           // not on this line
01917           // advance to next line
01918           FAST_DEBUG("  not on this line");
01919           const int advance = curLineRemainder + 1;
01920           curRelLine++;
01921           curRelCol = 0;
01922           curRelIndex += advance;
01923         }
01924       }
01925 
01926       ++iter;
01927     }
01928 
01929     // build result array
01930     QVector<KTextEditor::Range> result(1 + numCaptures);
01931     for (int y = 0; y <= numCaptures; y++)
01932     {
01933       IndexPair & pair = indexPairs[y];
01934       if ((pair.openIndex == -1) || (pair.closeIndex == -1))
01935       {
01936         result[y] = KTextEditor::Range::invalid();
01937       }
01938       else
01939       {
01940         const TwoViewCursor * const openCursors = indicesToCursors[pair.openIndex];
01941         const TwoViewCursor * const closeCursors = indicesToCursors[pair.closeIndex];
01942         const int startLine = openCursors->openLine;
01943         const int startCol = openCursors->openCol;
01944         const int endLine = closeCursors->closeLine;
01945         const int endCol = closeCursors->closeCol;
01946         FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")");
01947         result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol);
01948       }
01949     }
01950 
01951     // free structs allocated for indicesToCursors
01952     iter = indicesToCursors.constBegin();
01953     while (iter != indicesToCursors.constEnd())
01954     {
01955       TwoViewCursor * const twoViewCursor = *iter;
01956       delete twoViewCursor;
01957       ++iter;
01958     }
01959     return result;
01960   }
01961   else
01962   {
01963     // single-line regex search (both forward of backward mode)
01964     const int minLeft  = inputRange.start().column();
01965     const uint maxRight = inputRange.end().column(); // first not included
01966     const int forMin   = inputRange.start().line();
01967     const int forMax   = inputRange.end().line();
01968     const int forInit  = backwards ? forMax : forMin;
01969     const int forInc   = backwards ? -1 : +1;
01970     FAST_DEBUG("single line " << (backwards ? forMax : forMin) << ".."
01971       << (backwards ? forMin : forMax));
01972     for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01973     {
01974       KateTextLine::Ptr textLine = m_buffer->plainLine(j);
01975       if (!textLine)
01976       {
01977         FAST_DEBUG("searchText | line " << j << ": no");
01978         QVector<KTextEditor::Range> result;
01979         result.append(KTextEditor::Range::invalid());
01980         return result;
01981       }
01982 
01983         // Find (and don't match ^ in between...)
01984         const int first = (j == forMin) ? minLeft : 0;
01985         const int afterLast = (j == forMax) ? maxRight : textLine->length();
01986         const QString hay = textLine->string();
01987         bool found = true;
01988         int foundAt;
01989         uint myMatchLen;
01990         if (backwards) {
01991             const int lineLen = textLine->length();
01992             const int offset = afterLast - lineLen - 1;
01993             FAST_DEBUG("lastIndexIn(" << hay << "," << offset << ")");
01994             foundAt = KateDocument::fixedLastIndexIn(regexp, hay, offset);
01995             found = (foundAt != -1) && (foundAt >= first);
01996         } else {
01997             FAST_DEBUG("indexIn(" << hay << "," << first << ")");
01998             foundAt = regexp.indexIn(hay, first);
01999             found = (foundAt != -1);
02000         }
02001         myMatchLen = found ? regexp.matchedLength() : 0;
02002 
02003       /*
02004       TODO do we still need this?
02005 
02006           // A special case which can only occur when searching with a regular expression consisting
02007           // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{').
02008           if (myMatchLen == 0 && line == startPosition.line() && foundAt == (uint) col)
02009           {
02010             if (col < lineLength(line))
02011               col++;
02012             else {
02013               line++;
02014               col = 0;
02015             }
02016             continue;
02017           }
02018       */
02019 
02020       if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
02021       {
02022         FAST_DEBUG("line " << j << ": yes");
02023 
02024         // build result array
02025         const int numCaptures = regexp.numCaptures();
02026         QVector<KTextEditor::Range> result(1 + numCaptures);
02027         result[0] = KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
02028         FAST_DEBUG("result range " << 0 << ": (" << j << ", " << foundAt << ")..(" << j << ", " << foundAt + myMatchLen << ")");
02029         for (int y = 1; y <= numCaptures; y++)
02030         {
02031           const int openIndex = regexp.pos(y);
02032           if (openIndex == -1)
02033           {
02034             result[y] = KTextEditor::Range::invalid();
02035             FAST_DEBUG("capture []");
02036           }
02037           else
02038           {
02039             const int closeIndex = openIndex + regexp.cap(y).length();
02040             FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")");
02041             result[y] = KTextEditor::Range(j, openIndex, j, closeIndex);
02042           }
02043         }
02044         return result;
02045       }
02046       else
02047       {
02048         FAST_DEBUG("searchText | line " << j << ": no");
02049       }
02050     }
02051   }
02052 
02053   QVector<KTextEditor::Range> result;
02054   result.append(KTextEditor::Range::invalid());
02055   return result;
02056 }
02057 
02058 QWidget * KateDocument::dialogParent()
02059 {
02060     QWidget *w=widget();
02061 
02062     if(!w)
02063     {
02064         w=activeView();
02065 
02066         if(!w)
02067             w=QApplication::activeWindow();
02068     }
02069 
02070     return w;
02071 }
02072 
02073 QVector<KTextEditor::Range> KateDocument::searchText(
02074     const KTextEditor::Range & range,
02075     const QString & pattern,
02076     const KTextEditor::Search::SearchOptions options)
02077 {
02078   // TODO
02079   // * support BlockInputRange
02080   // * support DotMatchesNewline
02081   QString workPattern(pattern);
02082 
02083   KTextEditor::Search::SearchOptions finalOptions(options);
02084   const bool escapeSequences = finalOptions.testFlag(KTextEditor::Search::EscapeSequences);
02085 
02086   // abuse regex for whole word plaintext search
02087   if (finalOptions.testFlag(KTextEditor::Search::WholeWords))
02088   {
02089     // resolve escape sequences like \t
02090     if (escapeSequences)
02091     {
02092       KateDocument::escapePlaintext(workPattern);
02093     }
02094 
02095     // escape dot and friends
02096     workPattern = "\\b" + QRegExp::escape(workPattern) + "\\b";
02097 
02098     // regex ON, whole words OFF
02099     finalOptions |= KTextEditor::Search::Regex;
02100     finalOptions &= ~KTextEditor::Search::SearchOptions(KTextEditor::Search::WholeWords);
02101   }
02102 
02103   const bool regexMode = finalOptions.testFlag(KTextEditor::Search::Regex);
02104   const bool caseSensitive = !finalOptions.testFlag(KTextEditor::Search::CaseInsensitive);
02105   const bool backwards = finalOptions.testFlag(KTextEditor::Search::Backwards);
02106 
02107   if (regexMode)
02108   {
02109     // regex search
02110     const Qt::CaseSensitivity caseSensitivity =
02111         caseSensitive
02112         ? Qt::CaseSensitive
02113         : Qt::CaseInsensitive;
02114 
02115     QRegExp matcher(workPattern, caseSensitivity);
02116     if (matcher.isValid())
02117     {
02118       // valid pattern
02119       // run engine
02120       return searchRegex(range, matcher, backwards);
02121     }
02122     else
02123     {
02124       // invalid pattern
02125       QVector<KTextEditor::Range> result;
02126       result.append(KTextEditor::Range::invalid());
02127       return result;
02128     }
02129   }
02130   else
02131   {
02132     // plaintext search
02133 
02134     // resolve escape sequences like \t
02135     if (escapeSequences)
02136     {
02137       KateDocument::escapePlaintext(workPattern);
02138     }
02139 
02140     // run engine
02141     KTextEditor::Range resultRange = searchText(range, workPattern, caseSensitive, backwards);
02142     QVector<KTextEditor::Range> result;
02143     result.append(resultRange);
02144     return result;
02145   }
02146 }
02147 
02148 
02149 
02150 KTextEditor::Search::SearchOptions KateDocument::supportedSearchOptions() const
02151 {
02152   KTextEditor::Search::SearchOptions supported(KTextEditor::Search::Default);
02153   supported |= KTextEditor::Search::Regex;
02154   supported |= KTextEditor::Search::CaseInsensitive;
02155   supported |= KTextEditor::Search::Backwards;
02156 // supported |= KTextEditor::Search::BlockInputRange;
02157   supported |= KTextEditor::Search::EscapeSequences;
02158   supported |= KTextEditor::Search::WholeWords;
02159 // supported |= KTextEditor::Search::DotMatchesNewline;
02160   return supported;
02161 }
02162 
02163 
02164 
02165 /*static*/ void KateDocument::escapePlaintext(QString & text, QList<ReplacementPart> * parts,
02166         bool replacementGoodies) {
02167   // get input
02168   const int inputLen = text.length();
02169   int input = 0; // walker index
02170 
02171   // prepare output
02172   QString output;
02173   output.reserve(inputLen + 1);
02174 
02175   while (input < inputLen)
02176   {
02177     switch (text[input].unicode())
02178     {
02179     case L'\n':
02180       output.append(text[input]);
02181       input++;
02182       break;
02183 
02184     case L'\\':
02185       if (input + 1 >= inputLen)
02186       {
02187         // copy backslash
02188         output.append(text[input]);
02189         input++;
02190         break;
02191       }
02192 
02193       switch (text[input + 1].unicode())
02194       {
02195       case L'0': // "\0000".."\0377"
02196         if (input + 4 >= inputLen)
02197         {
02198           if (parts == NULL)
02199           {
02200             // strip backslash ("\0" -> "0")
02201             output.append(text[input + 1]);
02202           }
02203           else
02204           {
02205             // handle reference
02206             ReplacementPart curPart;
02207 
02208             // append text before the reference
02209             if (!output.isEmpty())
02210             {
02211               curPart.type = ReplacementPart::Text;
02212               curPart.text = output;
02213               output.clear();
02214               parts->append(curPart);
02215               curPart.text.clear();
02216             }
02217 
02218             // append reference
02219             curPart.type = ReplacementPart::Reference;
02220             curPart.index = 0;
02221             parts->append(curPart);
02222           }
02223           input += 2;
02224         }
02225         else
02226         {
02227           bool stripAndSkip = false;
02228           const ushort text_2 = text[input + 2].unicode();
02229           if ((text_2 >= L'0') && (text_2 <= L'3'))
02230           {
02231             const ushort text_3 = text[input + 3].unicode();
02232             if ((text_3 >= L'0') && (text_3 <= L'7'))
02233             {
02234               const ushort text_4 = text[input + 4].unicode();
02235               if ((text_4 >= L'0') && (text_4 <= L'7'))
02236               {
02237                 int digits[3];
02238                 for (int i = 0; i < 3; i++)
02239                 {
02240                   digits[i] = 7 - (L'7' - text[input + 2 + i].unicode());
02241                 }
02242                 const int ch = 64 * digits[0] + 8 * digits[1] + digits[2];
02243                 output.append(QChar(ch));
02244                 input += 5;
02245               }
02246               else
02247               {
02248                 stripAndSkip = true;
02249               }
02250             }
02251             else
02252             {
02253               stripAndSkip = true;
02254             }
02255           }
02256           else
02257           {
02258             stripAndSkip = true;
02259           }
02260 
02261           if (stripAndSkip)
02262           {
02263             if (parts == NULL)
02264             {
02265               // strip backslash ("\0" -> "0")
02266               output.append(text[input + 1]);
02267             }
02268             else
02269             {
02270               // handle reference
02271               ReplacementPart curPart;
02272 
02273               // append text before the reference
02274               if (!output.isEmpty())
02275               {
02276                 curPart.type = ReplacementPart::Text;
02277                 curPart.text = output;
02278                 output.clear();
02279                 parts->append(curPart);
02280                 curPart.text.clear();
02281               }
02282 
02283               // append reference
02284               curPart.type = ReplacementPart::Reference;
02285               curPart.index = 0;
02286               parts->append(curPart);
02287             }
02288             input += 2;
02289           }
02290         }
02291         break;
02292 
02293       case L'1':
02294       case L'2':
02295       case L'3':
02296       case L'4':
02297       case L'5':
02298       case L'6':
02299       case L'7':
02300       case L'8':
02301       case L'9':
02302         if (parts == NULL)
02303         {
02304           // strip backslash ("\?" -> "?")
02305           output.append(text[input + 1]);
02306         }
02307         else
02308         {
02309           // handle reference
02310           ReplacementPart curPart;
02311 
02312           // append text before the reference
02313           if (!output.isEmpty())
02314           {
02315             curPart.type = ReplacementPart::Text;
02316             curPart.text = output;
02317             output.clear();
02318             parts->append(curPart);
02319             curPart.text.clear();
02320           }
02321 
02322           // append reference
02323           curPart.type = ReplacementPart::Reference;
02324           curPart.index = 9 - (L'9' - text[input + 1].unicode());
02325           parts->append(curPart);
02326         }
02327         input += 2;
02328         break;
02329 
02330       case L'E': // FALLTHROUGH
02331       case L'L': // FALLTHROUGH
02332       case L'U':
02333         if ((parts == NULL) || !replacementGoodies) {
02334           // strip backslash ("\?" -> "?")
02335           output.append(text[input + 1]);
02336         } else {
02337           // handle case switcher
02338           ReplacementPart curPart;
02339 
02340           // append text before case switcher
02341           if (!output.isEmpty())
02342           {
02343             curPart.type = ReplacementPart::Text;
02344             curPart.text = output;
02345             output.clear();
02346             parts->append(curPart);
02347             curPart.text.clear();
02348           }
02349 
02350           // append case switcher
02351           switch (text[input + 1].unicode()) {
02352           case L'L':
02353             curPart.type = ReplacementPart::LowerCase;
02354             break;
02355 
02356           case L'U':
02357             curPart.type = ReplacementPart::UpperCase;
02358             break;
02359 
02360           case L'E': // FALLTHROUGH
02361           default:
02362             curPart.type = ReplacementPart::KeepCase;
02363 
02364           }
02365           parts->append(curPart);
02366         }
02367         input += 2;
02368         break;
02369 
02370       case L'#':
02371         if ((parts == NULL) || !replacementGoodies) {
02372           // strip backslash ("\?" -> "?")
02373           output.append(text[input + 1]);
02374           input += 2;
02375         } else {
02376           // handle replacement counter
02377           ReplacementPart curPart;
02378 
02379           // append text before replacement counter
02380           if (!output.isEmpty())
02381           {
02382             curPart.type = ReplacementPart::Text;
02383             curPart.text = output;
02384             output.clear();
02385             parts->append(curPart);
02386             curPart.text.clear();
02387           }
02388 
02389           // eat and count all following hash marks
02390           // each hash stands for a leading zero: \### will produces 001, 002, ...
02391           int count = 1;
02392           while ((input + count + 1 < inputLen) && (text[input + count + 1].unicode() == L'#')) {
02393             count++;
02394           }
02395           curPart.type = ReplacementPart::Counter;
02396           curPart.index = count; // Each hash stands
02397           parts->append(curPart);
02398           input += 1 + count;
02399         }
02400         break;
02401 
02402       case L'a':
02403         output.append(QChar(0x07));
02404         input += 2;
02405         break;
02406 
02407       case L'f':
02408         output.append(QChar(0x0c));
02409         input += 2;
02410         break;
02411 
02412       case L'n':
02413         output.append(QChar(0x0a));
02414         input += 2;
02415         break;
02416 
02417       case L'r':
02418         output.append(QChar(0x0d));
02419         input += 2;
02420         break;
02421 
02422       case L't':
02423         output.append(QChar(0x09));
02424         input += 2;
02425         break;
02426 
02427       case L'v':
02428         output.append(QChar(0x0b));
02429         input += 2;
02430         break;
02431 
02432       case L'x': // "\x0000".."\xffff"
02433         if (input + 5 >= inputLen)
02434         {
02435           // strip backslash ("\x" -> "x")
02436           output.append(text[input + 1]);
02437           input += 2;
02438         }
02439         else
02440         {
02441           bool stripAndSkip = false;
02442           const ushort text_2 = text[input + 2].unicode();
02443           if (((text_2 >= L'0') && (text_2 <= L'9'))
02444               || ((text_2 >= L'a') && (text_2 <= L'f'))
02445               || ((text_2 >= L'A') && (text_2 <= L'F')))
02446           {
02447             const ushort text_3 = text[input + 3].unicode();
02448             if (((text_3 >= L'0') && (text_3 <= L'9'))
02449                 || ((text_3 >= L'a') && (text_3 <= L'f'))
02450                 || ((text_3 >= L'A') && (text_3 <= L'F')))
02451             {
02452               const ushort text_4 = text[input + 4].unicode();
02453               if (((text_4 >= L'0') && (text_4 <= L'9'))
02454                   || ((text_4 >= L'a') && (text_4 <= L'f'))
02455                   || ((text_4 >= L'A') && (text_4 <= L'F')))
02456               {
02457                 const ushort text_5 = text[input + 5].unicode();
02458                 if (((text_5 >= L'0') && (text_5 <= L'9'))
02459                     || ((text_5 >= L'a') && (text_5 <= L'f'))
02460                     || ((text_5 >= L'A') && (text_5 <= L'F')))
02461                 {
02462                   int digits[4];
02463                   for (int i = 0; i < 4; i++)
02464                   {
02465                     const ushort cur = text[input + 2 + i].unicode();
02466                     if ((cur >= L'0') && (cur <= L'9'))
02467                     {
02468                       digits[i] = 9 - (L'9' - cur);
02469                     }
02470                     else if ((cur >= L'a') && (cur <= L'f'))
02471                     {
02472                       digits[i] = 15 - (L'f' - cur);
02473                     }
02474                     else // if ((cur >= L'A') && (cur <= L'F')))
02475                     {
02476                       digits[i] = 15 - (L'F' - cur);
02477                     }
02478                   }
02479 
02480                   const int ch = 4096 * digits[0] + 256 * digits[1] + 16 * digits[2] + digits[3];
02481                   output.append(QChar(ch));
02482                   input += 6;
02483                 }
02484                 else
02485                 {
02486                   stripAndSkip = true;
02487                 }
02488               }
02489               else
02490               {
02491                 stripAndSkip = true;
02492               }
02493             }
02494             else
02495             {
02496               stripAndSkip = true;
02497             }
02498           }
02499 
02500           if (stripAndSkip)
02501           {
02502             // strip backslash ("\x" -> "x")
02503             output.append(text[input + 1]);
02504             input += 2;
02505           }
02506         }
02507         break;
02508 
02509       default:
02510         // strip backslash ("\?" -> "?")
02511         output.append(text[input + 1]);
02512         input += 2;
02513 
02514       }
02515       break;
02516 
02517     default:
02518       output.append(text[input]);
02519       input++;
02520 
02521     }
02522   }
02523 
02524   if (parts == NULL)
02525   {
02526     // overwrite with escaped edition
02527     text = output;
02528   }
02529   else
02530   {
02531     // append text after the last reference if any
02532     if (!output.isEmpty())
02533     {
02534       ReplacementPart curPart;
02535       curPart.type = ReplacementPart::Text;
02536       curPart.text = output;
02537       parts->append(curPart);
02538     }
02539   }
02540 }
02541 
02542 
02543 
02544 // these things can besides '.' and '\s' make apptern multi-line:
02545 // \n, \x000A, \x????-\x????, \0012, \0???-\0???
02546 // a multi-line pattern must not pass as single-line, the other
02547 // way around will just result in slower searches and is therefore
02548 // not as critical
02549 /*static*/ int KateDocument::repairPattern(QString & pattern, bool & stillMultiLine)
02550 {
02551   const QString & text = pattern; // read-only input for parsing
02552 
02553   // get input
02554   const int inputLen = text.length();
02555   int input = 0; // walker index
02556 
02557   // prepare output
02558   QString output;
02559   output.reserve(2 * inputLen + 1); // twice should be enough for the average case
02560 
02561   // parser state
02562   stillMultiLine = false;
02563   int replaceCount = 0;
02564   bool insideClass = false;
02565 
02566   while (input < inputLen)
02567   {
02568     if (insideClass)
02569     {
02570       // wait for closing, unescaped ']'
02571       switch (text[input].unicode())
02572       {
02573       case L'\\':
02574         switch (text[input + 1].unicode())
02575         {
02576         case L'x':
02577           if (input + 5 < inputLen)
02578           {
02579             // copy "\x????" unmodified
02580             output.append(text.mid(input, 6));
02581             input += 6;
02582           } else {
02583             // copy "\x" unmodified
02584             output.append(text.mid(input, 2));
02585             input += 2;
02586           }
02587           stillMultiLine = true;
02588           break;
02589 
02590         case L'0':
02591           if (input + 4 < inputLen)
02592           {
02593             // copy "\0???" unmodified
02594             output.append(text.mid(input, 5));
02595             input += 5;
02596           } else {
02597             // copy "\0" unmodified
02598             output.append(text.mid(input, 2));
02599             input += 2;
02600           }
02601           stillMultiLine = true;
02602           break;
02603 
02604         case L's':
02605           // replace "\s" with "[ \t]"
02606           output.append("[ \\t]");
02607           input += 2;
02608           replaceCount++;
02609           break;
02610 
02611         case L'n':
02612           stillMultiLine = true;
02613           // FALLTROUGH
02614 
02615         default:
02616           // copy "\?" unmodified
02617           output.append(text.mid(input, 2));
02618           input += 2;
02619         }
02620         break;
02621 
02622       case L']':
02623         // copy "]" unmodified
02624         insideClass = false;
02625         output.append(text[input]);
02626         input++;
02627         break;
02628 
02629       default:
02630         // copy "?" unmodified
02631         output.append(text[input]);
02632         input++;
02633 
02634       }
02635     }
02636     else
02637     {
02638       // search for real dots and \S
02639       switch (text[input].unicode())
02640       {
02641       case L'\\':
02642         switch (text[input + 1].unicode())
02643         {
02644         case L'x':
02645           if (input + 5 < inputLen)
02646           {
02647             // copy "\x????" unmodified
02648             output.append(text.mid(input, 6));
02649             input += 6;
02650           } else {
02651             // copy "\x" unmodified
02652             output.append(text.mid(input, 2));
02653             input += 2;
02654           }
02655           stillMultiLine = true;
02656           break;
02657 
02658         case L'0':
02659           if (input + 4 < inputLen)
02660           {
02661             // copy "\0???" unmodified
02662             output.append(text.mid(input, 5));
02663             input += 5;
02664           } else {
02665             // copy "\0" unmodified
02666             output.append(text.mid(input, 2));
02667             input += 2;
02668           }
02669           stillMultiLine = true;
02670           break;
02671 
02672         case L's':
02673           // replace "\s" with "[ \t]"
02674           output.append("[ \\t]");
02675           input += 2;
02676           replaceCount++;
02677           break;
02678 
02679         case L'n':
02680           stillMultiLine = true;
02681           // FALLTROUGH
02682 
02683         default:
02684           // copy "\?" unmodified
02685           output.append(text.mid(input, 2));
02686           input += 2;
02687         }
02688         break;
02689 
02690       case L'.':
02691         // replace " with "[^\n]"
02692         output.append("[^\\n]");
02693         input++;
02694         replaceCount++;
02695         break;
02696 
02697       case L'[':
02698         // copy "]" unmodified
02699         insideClass = true;
02700         output.append(text[input]);
02701         input++;
02702         break;
02703 
02704       default:
02705         // copy "?" unmodified
02706         output.append(text[input]);
02707         input++;
02708 
02709       }
02710     }
02711   }
02712 
02713   // Overwrite with repaired pattern
02714   pattern = output;
02715   return replaceCount;
02716 }
02717 
02718 
02719 
02720 /*static*/ int KateDocument::fixedLastIndexIn(const QRegExp & matcher, const QString & str,
02721         int offset, QRegExp::CaretMode caretMode) {
02722     int prevPos = -1;
02723     int prevLen = 1;
02724     const int strLen = str.length();
02725     for (;;) {
02726         const int pos = matcher.indexIn(str, prevPos + prevLen, caretMode);
02727         if (pos == -1) {
02728             // No more matches
02729             break;
02730         } else {
02731             const int len = matcher.matchedLength();
02732             if (pos > strLen + offset + 1) {
02733                 // Gone too far, match in no way of use
02734                 break;
02735             }
02736 
02737             if (pos + len > strLen + offset + 1) {
02738                 // Gone too far, check if usable
02739                 if (offset == -1) {
02740                     // No shrinking possible
02741                     break;
02742                 }
02743 
02744                 const QString str2 = str.mid(0, strLen + offset + 1);
02745                 const int pos2 = matcher.indexIn(str2, pos, caretMode);
02746                 if (pos2 != -1) {
02747                     // Match usable
02748                     return pos2;
02749                 } else {
02750                     // Match NOT usable
02751                     break;
02752                 }
02753             }
02754 
02755             // Valid match, but maybe not the last one
02756             prevPos = pos;
02757             prevLen = (len == 0) ? 1 : len;
02758         }
02759     }
02760 
02761     // Previous match is what we want
02762     if (prevPos != -1) {
02763         // Do that very search again
02764         matcher.indexIn(str, prevPos, caretMode);
02765         return prevPos;
02766     } else {
02767         return -1;
02768     }
02769 }
02770 //END
02771 
02772 //BEGIN KTextEditor::HighlightingInterface stuff
02773 bool KateDocument::setMode (const QString &name)
02774 {
02775   updateFileType (name);
02776   return true;
02777 }
02778 
02779 QString KateDocument::mode () const
02780 {
02781   return m_fileType;
02782 }
02783 
02784 QStringList KateDocument::modes () const
02785 {
02786   QStringList m;
02787 
02788   const QList<KateFileType *> &modeList = KateGlobal::self()->modeManager()->list();
02789   for (int i = 0; i < modeList.size(); ++i)
02790     m << modeList[i]->name;
02791 
02792   return m;
02793 }
02794 
02795 bool KateDocument::setHighlightingMode (const QString &name)
02796 {
02797   m_buffer->setHighlight (KateHlManager::self()->nameFind(name));
02798   return true;
02799 }
02800 
02801 QString KateDocument::highlightingMode () const
02802 {
02803   return highlight()->name ();
02804 }
02805 
02806 QStringList KateDocument::highlightingModes () const
02807 {
02808   QStringList hls;
02809 
02810   for (int i = 0; i < KateHlManager::self()->highlights(); ++i)
02811     hls << KateHlManager::self()->hlName (i);
02812 
02813   return hls;
02814 }
02815 
02816 QString KateDocument::highlightingModeSection( int index ) const
02817 {
02818   return KateHlManager::self()->hlSection( index );
02819 }
02820 
02821 QString KateDocument::modeSection( int index ) const
02822 {
02823   return KateGlobal::self()->modeManager()->list()[ index ]->section;
02824 }
02825 
02826 void KateDocument::bufferHlChanged ()
02827 {
02828   // update all views
02829   makeAttribs(false);
02830 
02831   // deactivate indenter if necessary
02832   m_indenter.checkRequiredStyle();
02833 
02834   emit highlightingModeChanged(this);
02835 }
02836 
02837 void KateDocument::setDontChangeHlOnSave()
02838 {
02839   hlSetByUser = true;
02840 }
02841 //END
02842 
02843 //BEGIN KTextEditor::ConfigInterface stuff
02844 void KateDocument::readSessionConfig(const KConfigGroup &kconfig)
02845 {
02846   // restore the url
02847   KUrl url (kconfig.readEntry("URL"));
02848 
02849   // get the encoding
02850   QString tmpenc=kconfig.readEntry("Encoding");
02851   if (!tmpenc.isEmpty() && (tmpenc != encoding()))
02852     setEncoding(tmpenc);
02853 
02854   // open the file if url valid
02855   if (!url.isEmpty() && url.isValid())
02856     openUrl (url);
02857   else completed(); //perhaps this should be emitted at the end of this function
02858 
02859   // restore the filetype
02860   updateFileType (kconfig.readEntry("Mode", "Normal"));
02861 
02862   // restore the hl stuff
02863   m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig.readEntry("Highlighting")));
02864 
02865   // indent mode
02866   config()->setIndentationMode( kconfig.readEntry("Indentation Mode", config()->indentationMode() ) );
02867 
02868   // Restore Bookmarks
02869   QList<int> marks = kconfig.readEntry("Bookmarks", QList<int>());
02870   for( int i = 0; i < marks.count(); i++ )
02871     addMark( marks[i], KateDocument::markType01 );
02872 }
02873 
02874 void KateDocument::writeSessionConfig(KConfigGroup &kconfig)
02875 {
02876   if ( this->url().isLocalFile() ) {
02877     const QString path = this->url().toLocalFile();
02878     if ( KGlobal::dirs()->relativeLocation( "tmp", path ) != path ) {
02879       return; // inside tmp resource, do not save
02880     }
02881   }
02882   // save url
02883   kconfig.writeEntry("URL", this->url().prettyUrl() );
02884 
02885   // save encoding
02886   kconfig.writeEntry("Encoding",encoding());
02887 
02888   // save file type
02889   kconfig.writeEntry("Mode", m_fileType);
02890 
02891   // save hl
02892   kconfig.writeEntry("Highlighting", highlight()->name());
02893 
02894   // indent mode
02895   kconfig.writeEntry("Indentation Mode", config()->indentationMode() );
02896 
02897   // Save Bookmarks
02898   QList<int> marks;
02899   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
02900     if (i.value()->type & KTextEditor::MarkInterface::markType01)
02901      marks << i.value()->line;
02902 
02903   kconfig.writeEntry( "Bookmarks", marks );
02904 }
02905 
02906 uint KateDocument::mark( int line )
02907 {
02908   if( !m_marks.value(line) )
02909     return 0;
02910 
02911   return m_marks[line]->type;
02912 }
02913 
02914 void KateDocument::setMark( int line, uint markType )
02915 {
02916   clearMark( line );
02917   addMark( line, markType );
02918 }
02919 
02920 void KateDocument::clearMark( int line )
02921 {
02922   if( line > lastLine() )
02923     return;
02924 
02925   if( !m_marks.value(line) )
02926     return;
02927 
02928   KTextEditor::Mark* mark = m_marks.take( line );
02929   emit markChanged( this, *mark, MarkRemoved );
02930   emit marksChanged( this );
02931   delete mark;
02932   tagLines( line, line );
02933   repaintViews(true);
02934 }
02935 
02936 void KateDocument::addMark( int line, uint markType )
02937 {
02938   if( line > lastLine())
02939     return;
02940 
02941   if( markType == 0 )
02942     return;
02943 
02944   if( m_marks.value(line) ) {
02945     KTextEditor::Mark* mark = m_marks[line];
02946 
02947     // Remove bits already set
02948     markType &= ~mark->type;
02949 
02950     if( markType == 0 )
02951       return;
02952 
02953     // Add bits
02954     mark->type |= markType;
02955   } else {
02956     KTextEditor::Mark *mark = new KTextEditor::Mark;
02957     mark->line = line;
02958     mark->type = markType;
02959     m_marks.insert( line, mark );
02960   }
02961 
02962   // Emit with a mark having only the types added.
02963   KTextEditor::Mark temp;
02964   temp.line = line;
02965   temp.type = markType;
02966   emit markChanged( this, temp, MarkAdded );
02967 
02968   emit marksChanged( this );
02969   tagLines( line, line );
02970   repaintViews(true);
02971 }
02972 
02973 void KateDocument::removeMark( int line, uint markType )
02974 {
02975   if( line > lastLine() )
02976     return;
02977 
02978   if( !m_marks.value(line) )
02979     return;
02980 
02981   KTextEditor::Mark* mark = m_marks[line];
02982 
02983   // Remove bits not set
02984   markType &= mark->type;
02985 
02986   if( markType == 0 )
02987     return;
02988 
02989   // Subtract bits
02990   mark->type &= ~markType;
02991 
02992   // Emit with a mark having only the types removed.
02993   KTextEditor::Mark temp;
02994   temp.line = line;
02995   temp.type = markType;
02996   emit markChanged( this, temp, MarkRemoved );
02997 
02998   if( mark->type == 0 )
02999     m_marks.remove( line );
03000 
03001   emit marksChanged( this );
03002   tagLines( line, line );
03003   repaintViews(true);
03004 }
03005 
03006 const QHash<int, KTextEditor::Mark*> &KateDocument::marks()
03007 {
03008   return m_marks;
03009 }
03010 
03011 void KateDocument::clearMarks()
03012 {
03013   while (!m_marks.isEmpty())
03014   {
03015     QHash<int, KTextEditor::Mark*>::iterator it = m_marks.begin();
03016     KTextEditor::Mark mark = *it.value();
03017     delete it.value();
03018     m_marks.erase (it);
03019 
03020     emit markChanged( this, mark, MarkRemoved );
03021     tagLines( mark.line, mark.line );
03022   }
03023 
03024   m_marks.clear();
03025 
03026   emit marksChanged( this );
03027   repaintViews(true);
03028 }
03029 
03030 void KateDocument::setMarkPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
03031 {
03032   m_markPixmaps.insert( type, pixmap );
03033 }
03034 
03035 void KateDocument::setMarkDescription( MarkInterface::MarkTypes type, const QString& description )
03036 {
03037   m_markDescriptions.insert( type, description );
03038 }
03039 
03040 QPixmap KateDocument::markPixmap( MarkInterface::MarkTypes type ) const
03041 {
03042   return m_markPixmaps.contains(type) ?
03043          m_markPixmaps[type] : QPixmap();
03044 }
03045 
03046 QColor KateDocument::markColor( MarkInterface::MarkTypes type ) const
03047 {
03048   uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
03049   if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
03050     return KateRendererConfig::global()->lineMarkerColor(type);
03051   } else {
03052     return QColor();
03053   }
03054 }
03055 
03056 QString KateDocument::markDescription( MarkInterface::MarkTypes type ) const
03057 {
03058   return m_markDescriptions.contains(type) ?
03059          m_markDescriptions[type] : QString();
03060 }
03061 
03062 void KateDocument::setEditableMarks( uint markMask )
03063 {
03064   m_editableMarks = markMask;
03065 }
03066 
03067 uint KateDocument::editableMarks() const
03068 {
03069   return m_editableMarks;
03070 }
03071 //END
03072 
03073 //BEGIN KTextEditor::PrintInterface stuff
03074 bool KateDocument::printDialog ()
03075 {
03076   return KatePrinter::print (this);
03077 }
03078 
03079 bool KateDocument::print ()
03080 {
03081   return KatePrinter::print (this);
03082 }
03083 //END
03084 
03085 //BEGIN KTextEditor::DocumentInfoInterface (### unfinished)
03086 QString KateDocument::mimeType()
03087 {
03088   KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
03089 
03090   // if the document has a URL, try KMimeType::findByURL
03091   if ( ! this->url().isEmpty() )
03092     result = KMimeType::findByUrl( this->url() );
03093 
03094   else if ( this->url().isEmpty() || ! this->url().isLocalFile() )
03095     result = mimeTypeForContent();
03096 
03097   return result->name();
03098 }
03099 
03100 KMimeType::Ptr KateDocument::mimeTypeForContent()
03101 {
03102   QByteArray buf (1024,'\0');
03103   uint bufpos = 0;
03104 
03105   for (int i=0; i < lines(); ++i)
03106   {
03107     QString line = this->line( i );
03108     uint len = line.length() + 1;
03109 
03110     if (bufpos + len > 1024)
03111       len = 1024 - bufpos;
03112 
03113     QString ld (line + QChar::fromAscii('\n'));
03114     buf.replace(bufpos,len,ld.toLatin1()); //memcpy(buf.data() + bufpos, ld.toLatin1().constData(), len);
03115 
03116     bufpos += len;
03117 
03118     if (bufpos >= 1024)
03119       break;
03120   }
03121   buf.resize( bufpos );
03122 
03123   int accuracy = 0;
03124   KMimeType::Ptr mt = KMimeType::findByContent(buf, &accuracy);
03125   return mt ? mt : KMimeType::defaultMimeTypePtr();
03126 }
03127 //END KTextEditor::DocumentInfoInterface
03128 
03129 
03130 //BEGIN KParts::ReadWrite stuff
03131 bool KateDocument::openFile()
03132 {
03133   // no open errors until now...
03134   setOpeningError(false);
03135 
03136   // add new m_file to dirwatch
03137   activateDirWatch ();
03138 
03139   //
03140   // mime type magic to get encoding right
03141   //
03142   QString mimeType = arguments().mimeType();
03143   int pos = mimeType.indexOf(';');
03144   if (pos != -1)
03145     setEncoding (mimeType.mid(pos+1));
03146 
03147   // do we have success ?
03148   emit KTextEditor::Document::textRemoved(this, documentRange());
03149   history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03150 
03151   bool success = m_buffer->openFile (localFilePath());
03152 
03153   emit KTextEditor::Document::textInserted(this, documentRange());
03154   history()->doEdit( new KateEditInfo(Kate::OpenFileEdit, KTextEditor::Range(0,0,0,0), QStringList(), documentRange(), QStringList()) );
03155 
03156   //
03157   // yeah, success
03158   //
03159   if (success)
03160   {
03161     // update file type
03162     updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03163 
03164     // read dir config (if possible and wanted)
03165     readDirConfig ();
03166 
03167     // read vars
03168     readVariables();
03169 
03170     // update the md5 digest
03171     createDigest( m_digest );
03172 
03173     if (!m_postLoadFilterChecks.isEmpty())
03174     {
03175       LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03176       foreach(const QString& checkplugin, m_postLoadFilterChecks)
03177       {
03178          lscps->postLoadFilter(checkplugin,this);
03179       }
03180     }
03181   }
03182 
03183   // Inform that the text has changed (required as we're not inside the usual editStart/End stuff)
03184   emit textChanged (this);
03185 
03186   //
03187   // update views
03188   //
03189   foreach (KateView * view, m_views)
03190   {
03191     // This is needed here because inserting the text moves the view's start position (it is a SmartCursor)
03192     view->setCursorPosition(KTextEditor::Cursor());
03193     view->updateView(true);
03194   }
03195 
03196   if (!m_reloading)
03197   {
03198     //
03199     // emit the signal we need for example for kate app
03200     //
03201     emit documentUrlChanged (this);
03202 
03203     //
03204     // set doc name, dummy value as arg, don't need it
03205     //
03206     setDocName  (QString());
03207   }
03208   //
03209   // to houston, we are not modified
03210   //
03211   if (m_modOnHd)
03212   {
03213     m_modOnHd = false;
03214     m_modOnHdReason = OnDiskUnmodified;
03215     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03216   }
03217 
03218   //
03219   // display errors
03220   //
03221   QWidget *parentWidget(dialogParent());
03222 
03223   if (!suppressOpeningErrorDialogs())
03224   {
03225     if (!success)
03226       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()));
03227   }
03228 
03229   if (!success) {
03230     setOpeningError(true);
03231     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()));
03232   }
03233 
03234   // warn -> opened binary file!!!!!!!
03235   if (m_buffer->binary())
03236   {
03237     // this file can't be saved again without killing it
03238     setReadWrite( false );
03239 
03240     if(!suppressOpeningErrorDialogs())
03241       KMessageBox::information (parentWidget
03242         , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl())
03243         , i18n ("Binary File Opened")
03244         , "Binary File Opened Warning");
03245 
03246     setOpeningError(true);
03247     setOpeningErrorMessage(i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl()));
03248   }
03249 
03250   // warn: opened broken utf-8 file...
03251   // only warn if not already a binary file!
03252   else if (m_buffer->brokenUTF8())
03253   {
03254     // this file can't be saved again without killing it
03255     setReadWrite( false );
03256 
03257     if (!suppressOpeningErrorDialogs())
03258       KMessageBox::information (parentWidget
03259         , i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03260                 " It is set to read-only mode, as saving might destroy its content."
03261                 " 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())
03262         , i18n ("Broken UTF-8 File Opened")
03263         , "Broken UTF-8 File Opened Warning");
03264     setOpeningError(true);
03265     setOpeningErrorMessage(i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03266               " It is set to read-only mode, as saving might destroy its content."
03267               " 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()));
03268   }
03269 
03270   //
03271   // return the success
03272   //
03273   return success;
03274 }
03275 
03276 bool KateDocument::saveFile()
03277 {
03278   QWidget *parentWidget(dialogParent());
03279 
03280   //
03281   // warn -> try to save binary file!!!!!!!
03282   //
03283   if (m_buffer->binary() && (KMessageBox::warningContinueCancel (parentWidget
03284         , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", url().pathOrUrl())
03285         , i18n ("Trying to Save Binary File")
03286         , KGuiItem(i18n("Save Nevertheless"))
03287         , KStandardGuiItem::cancel(), "Binary File Save Warning") != KMessageBox::Continue))
03288     return false;
03289 
03290   // some warnings, if file was changed by the outside!
03291   if ( !url().isEmpty() )
03292   {
03293     if (s_fileChangedDialogsActivated && m_modOnHd)
03294     {
03295       QString str = reasonedMOHString() + "\n\n";
03296 
03297       if (!isModified())
03298       {
03299         if (KMessageBox::warningContinueCancel(parentWidget,
03300                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)
03301           return false;
03302       }
03303       else
03304       {
03305         if (KMessageBox::warningContinueCancel(parentWidget,
03306                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)
03307           return false;
03308       }
03309     }
03310   }
03311 
03312   //
03313   // can we encode it if we want to save it ?
03314   //
03315   if (!m_buffer->canEncode ()
03316        && (KMessageBox::warningContinueCancel(parentWidget,
03317            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))
03318   {
03319     return false;
03320   }
03321 
03322   //
03323   // try to create backup file..
03324   //
03325 
03326   // local file or not is here the question
03327   bool l ( url().isLocalFile() );
03328 
03329   // does the user want any backup, if not, not our problem?
03330   if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
03331        || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
03332   {
03333     KUrl u( url() );
03334     u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
03335 
03336     kDebug( 13020 ) << "backup src file name: " << url();
03337     kDebug( 13020 ) << "backup dst file name: " << u;
03338 
03339     // handle the backup...
03340     bool backupSuccess = false;
03341 
03342     // local file mode, no kio
03343     if (u.isLocalFile ())
03344     {
03345       if (QFile::exists (url().toLocalFile ()))
03346       {
03347         // first: check if backupFile is already there, if true, unlink it
03348         QFile backupFile (u.toLocalFile ());
03349         if (backupFile.exists()) backupFile.remove ();
03350 
03351         backupSuccess = QFile::copy (url().toLocalFile (), u.toLocalFile ());
03352       }
03353       else
03354         backupSuccess = true;
03355     }
03356     else // remote file mode, kio
03357     {
03358       QWidget *w = widget ();
03359       if (!w && !m_views.isEmpty ())
03360         w = m_views.first();
03361 
03362       // get the right permissions, start with safe default
03363       mode_t  perms = 0600;
03364       KIO::UDSEntry fentry;
03365       if (KIO::NetAccess::stat (url(), fentry, kapp->activeWindow()))
03366       {
03367         kDebug( 13020 ) << "stating succesfull: " << url();
03368         KFileItem item (fentry, url());
03369         perms = item.permissions();
03370 
03371         // do a evil copy which will overwrite target if possible
03372         KIO::FileCopyJob *job = KIO::file_copy ( url(), u, -1, KIO::Overwrite );
03373         backupSuccess = KIO::NetAccess::synchronousRun(job, w);
03374       }
03375       else
03376         backupSuccess = true;
03377     }
03378 
03379     // backup has failed, ask user how to proceed
03380     if (!backupSuccess && (KMessageBox::warningContinueCancel (parentWidget
03381         , i18n ("For file %1 no backup copy could be created before saving."
03382                 " If an error occurs while saving, you might lose the data of this file."
03383                 " 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())
03384         , i18n ("Failed to create backup copy.")
03385         , KGuiItem(i18n("Try to Save Nevertheless"))
03386         , KStandardGuiItem::cancel(), "Backup Failed Warning") != KMessageBox::Continue))
03387     {
03388       return false;
03389     }
03390   }
03391 
03392   // update file type
03393   updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03394 
03395   if (!m_preSavePostDialogFilterChecks.isEmpty())
03396   {
03397     LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03398     foreach(const QString& checkplugin, m_preSavePostDialogFilterChecks)
03399     {
03400        if (lscps->preSavePostDialogFilterCheck(checkplugin,this,parentWidget)==false)
03401          return false;
03402     }
03403   }
03404 
03405   // remember the oldpath...
03406   QString oldPath = m_dirWatchFile;
03407 
03408   // remove file from dirwatch
03409   deactivateDirWatch ();
03410 
03411   //
03412   // try to save
03413   //
03414   if (!m_buffer->saveFile (localFilePath()))
03415   {
03416     // add m_file again to dirwatch
03417     activateDirWatch (oldPath);
03418 
03419     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()));
03420 
03421     return false;
03422   }
03423 
03424   // update the md5 digest
03425   createDigest( m_digest );
03426 
03427   // add m_file again to dirwatch
03428   activateDirWatch ();
03429 
03430   // update file type
03431 //  updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03432 
03433   // read dir config (if possible and wanted)
03434   if ( url().isLocalFile())
03435   {
03436     QFileInfo fo (oldPath), fn (m_dirWatchFile);
03437 
03438     if (fo.path() != fn.path())
03439       readDirConfig();
03440   }
03441 
03442   // read our vars
03443   readVariables();
03444 
03445   //
03446   // we are not modified
03447   //
03448   if (m_modOnHd)
03449   {
03450     m_modOnHd = false;
03451     m_modOnHdReason = OnDiskUnmodified;
03452     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03453   }
03454 
03455   // update document name...
03456   setDocName( QString() );
03457 
03458   // url may have changed...
03459   emit documentUrlChanged (this);
03460 
03461   m_savingToUrl=true;
03462 
03463   // (dominik) mark last undo group as not mergeable, otherwise the next
03464   // edit action might be merged and undo will never stop at the saved state
03465   setUndoDontMerge(true);
03466 
03467   //
03468   // return success
03469   //
03470   return true;
03471 }
03472 
03473 void KateDocument::readDirConfig ()
03474 {
03475   int depth = config()->searchDirConfigDepth ();
03476 
03477   if (this->url().isLocalFile() && (depth > -1))
03478   {
03479     QString currentDir = QFileInfo (localFilePath()).absolutePath();
03480 
03481     // only search as deep as specified or not at all ;)
03482     while (depth > -1)
03483     {
03484       //kDebug (13020) << "search for config file in path: " << currentDir;
03485 
03486       // try to open config file in this dir
03487       QFile f (currentDir + "/.kateconfig");
03488 
03489       if (f.open (QIODevice::ReadOnly))
03490       {
03491         QTextStream stream (&f);
03492 
03493         uint linesRead = 0;
03494         QString line = stream.readLine();
03495         while ((linesRead < 32) && !line.isNull())
03496         {
03497           readVariableLine( line );
03498 
03499           line = stream.readLine();
03500 
03501           linesRead++;
03502         }
03503 
03504         break;
03505       }
03506 
03507       QString newDir = QFileInfo (currentDir).absolutePath();
03508 
03509       // bail out on looping (for example reached /)
03510       if (currentDir == newDir)
03511         break;
03512 
03513       currentDir = newDir;
03514       --depth;
03515     }
03516   }
03517 }
03518 
03519 void KateDocument::activateDirWatch (const QString &useFileName)
03520 {
03521   QString fileToUse = useFileName;
03522   if (fileToUse.isEmpty())
03523     fileToUse = localFilePath();
03524 
03525   // same file as we are monitoring, return
03526   if (fileToUse == m_dirWatchFile)
03527     return;
03528 
03529   // remove the old watched file
03530   deactivateDirWatch ();
03531 
03532   // add new file if needed
03533   if (url().isLocalFile() && !fileToUse.isEmpty())
03534   {
03535     KateGlobal::self()->dirWatch ()->addFile (fileToUse);
03536     m_dirWatchFile = fileToUse;
03537   }
03538 }
03539 
03540 void KateDocument::deactivateDirWatch ()
03541 {
03542   if (!m_dirWatchFile.isEmpty())
03543     KateGlobal::self()->dirWatch ()->removeFile (m_dirWatchFile);
03544 
03545   m_dirWatchFile.clear();
03546 }
03547 
03548 bool KateDocument::closeUrl()
03549 {
03550   //
03551   // file mod on hd
03552   //
03553   if ( !m_reloading && !url().isEmpty() )
03554   {
03555     if (s_fileChangedDialogsActivated && m_modOnHd)
03556     {
03557       QWidget *parentWidget(dialogParent());
03558 
03559       if (!(KMessageBox::warningContinueCancel(
03560             parentWidget,
03561             reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
03562             i18n("Possible Data Loss"), KGuiItem(i18n("Close Nevertheless")), KStandardGuiItem::cancel(),
03563             QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
03564         return false;
03565     }
03566   }
03567 
03568   //
03569   // first call the normal kparts implementation
03570   //
03571   if (!KParts::ReadWritePart::closeUrl ())
03572     return false;
03573 
03574   // Tell the world that we're about to go ahead with the close
03575   if (!m_reloading)
03576     emit aboutToClose(this);
03577 
03578   // remove file from dirwatch
03579   deactivateDirWatch ();
03580 
03581   //
03582   // empty url + fileName
03583   //
03584   setUrl(KUrl());
03585   setLocalFilePath(QString());
03586 
03587   // we are not modified
03588   if (m_modOnHd)
03589   {
03590     m_modOnHd = false;
03591     m_modOnHdReason = OnDiskUnmodified;
03592     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03593   }
03594 
03595   emit KTextEditor::Document::textRemoved(this, documentRange());
03596   
03597   {
03598     QMutexLocker l(smartMutex());
03599     history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03600 
03601     // clear the buffer
03602     m_buffer->clear();
03603 
03604     // remove all marks
03605     clearMarks ();
03606 
03607     // clear undo/redo history
03608     m_undoManager->clearUndo();
03609     m_undoManager->clearRedo();
03610   }
03611 
03612   // no, we are no longer modified
03613   setModified(false);
03614 
03615   // we have no longer any hl
03616   m_buffer->setHighlight(0);
03617 
03618   // update all our views
03619   foreach (KateView * view, m_views )
03620   {
03621     view->clearSelection(); // fix bug #118588
03622     view->clear();
03623   }
03624 
03625   if (!m_reloading)
03626   {
03627 
03628     // uh, fileName changed
03629     emit documentUrlChanged (this);
03630 
03631     // update doc name
03632     setDocName (QString());
03633   }
03634   // success
03635   return true;
03636 }
03637 
03638 void KateDocument::setReadWrite( bool rw )
03639 {
03640   if (isReadWrite() != rw)
03641   {
03642     KParts::ReadWritePart::setReadWrite (rw);
03643 
03644     foreach( KateView* view, m_views)
03645     {
03646       view->slotUpdateUndo();
03647       view->slotReadWriteChanged ();
03648     }
03649   }
03650 }
03651 
03652 void KateDocument::setModified(bool m) {
03653 
03654   if (isModified() != m) {
03655     KParts::ReadWritePart::setModified (m);
03656 
03657     foreach( KateView* view,m_views)
03658     {
03659       view->slotUpdateUndo();
03660     }
03661 
03662     emit modifiedChanged (this);
03663   }
03664 
03665   m_undoManager->setModified (m);
03666 }
03667 //END
03668 
03669 //BEGIN Kate specific stuff ;)
03670 
03671 void KateDocument::makeAttribs(bool needInvalidate)
03672 {
03673   foreach(KateView *view,m_views)
03674     view->renderer()->updateAttributes ();
03675 
03676   if (needInvalidate)
03677     m_buffer->invalidateHighlighting();
03678 
03679   foreach(KateView *view,m_views)
03680   {
03681     view->tagAll();
03682     view->updateView (true);
03683   }
03684 }
03685 
03686 // the attributes of a hl have changed, update
03687 void KateDocument::internalHlChanged()
03688 {
03689   makeAttribs();
03690 }
03691 
03692 void KateDocument::addView(KTextEditor::View *view) {
03693   if (!view)
03694     return;
03695 
03696   m_views.append( static_cast<KateView*>(view) );
03697   m_textEditViews.append( view );
03698 
03699   foreach(KTextEditor::SmartRange* highlight, m_documentHighlights) {
03700     Q_ASSERT(dynamic_cast<KateView*>(view));
03701     static_cast<KateView*>(view)->addExternalHighlight(highlight, m_documentDynamicHighlights.contains(highlight));
03702 }
03703 
03704   // apply the view & renderer vars from the file type
03705   if (!m_fileType.isEmpty())
03706       readVariableLine(KateGlobal::self()->modeManager()->fileType(m_fileType).varLine, true);
03707 
03708   // apply the view & renderer vars from the file
03709   readVariables (true);
03710 
03711   setActiveView(view);
03712 }
03713 
03714 void KateDocument::removeView(KTextEditor::View *view) {
03715   if (!view)
03716     return;
03717 
03718   if (activeView() == view)
03719     setActiveView(0L);
03720 
03721   m_views.removeAll( (KateView *) view );
03722   m_textEditViews.removeAll( view  );
03723 }
03724 
03725 void KateDocument::setActiveView(KTextEditor::View* view)
03726 {
03727   if ( m_activeView == view ) return;
03728 
03729   if (m_activeView) {
03730     disconnect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), this, SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03731   }
03732 
03733   m_activeView = (KateView*)view;
03734 
03735   if (m_activeView) {
03736     connect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03737   }
03738 }
03739 
03740 bool KateDocument::ownedView(KateView *view) {
03741   // do we own the given view?
03742   return (m_views.contains(view));
03743 }
03744 
03745 uint KateDocument::toVirtualColumn( const KTextEditor::Cursor& cursor )
03746 {
03747   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03748 
03749   if (textLine)
03750     return textLine->toVirtualColumn(cursor.column(), config()->tabWidth());
03751   else
03752     return 0;
03753 }
03754 
03755 bool KateDocument::typeChars ( KateView *view, const QString &chars )
03756 {
03757   // Because we want to access text before starting an edit, lock the smart mutex now
03758   QMutexLocker l(smartMutex());
03759 
03760   KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorPosition().line ());
03761 
03762   if (!textLine)
03763     return false;
03764 
03765   bool bracketInserted = false;
03766   QString buf;
03767   QChar c;
03768   for( int z = 0; z < chars.length(); z++ )
03769   {
03770     QChar ch = c = chars[z];
03771 
03772     if (ch.isPrint() || ch == QChar::fromAscii('\t'))
03773     {
03774       buf.append (ch);
03775 
03776       if (!bracketInserted && (config()->configFlags() & KateDocumentConfig::cfAutoBrackets))
03777       {
03778         QChar end_ch;
03779         bool complete = true;
03780         QChar prevChar = textLine->at(view->cursorPosition().column()-1);
03781         QChar nextChar = textLine->at(view->cursorPosition().column());
03782         switch(ch.toAscii()) {
03783           case '(': end_ch = ')'; break;
03784           case '[': end_ch = ']'; break;
03785           case '{': end_ch = '}'; break;
03786           case '\'':end_ch = '\'';break;
03787           case '"': end_ch = '"'; break;
03788           default: complete = false;
03789         }
03790         if (complete)
03791         {
03792           if (view->selection())
03793           { // there is a selection, enclose the selection
03794             buf.append (view->selectionText());
03795             buf.append (end_ch);
03796             bracketInserted = true;
03797           }
03798           else
03799           { // no selection, check whether we should better refuse to complete
03800             if ( ( (ch == '\'' || ch == '"') &&
03801                    (prevChar.isLetterOrNumber() || prevChar == ch) )
03802               || nextChar.isLetterOrNumber()
03803               || (nextChar == end_ch && prevChar != ch) )
03804             {
03805               kDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
03806             }
03807             else
03808             {
03809               buf.append (end_ch);
03810               bracketInserted = true;
03811             }
03812           }
03813         }
03814       }
03815     }
03816   }
03817 
03818   if (buf.isEmpty())
03819     return false;
03820 
03821   l.unlock(); //editStart will lock the smart-mutex again, and it must be un-locked within editEnd. So unlock here.
03822 
03823   editStart ();
03824 
03825   if (!view->config()->persistentSelection() && view->selection() )
03826     view->removeSelectedText();
03827 
03828   KTextEditor::Cursor oldCur (view->cursorPosition());
03829 
03830   if (config()->configFlags()  & KateDocumentConfig::cfOvr)
03831     removeText(KTextEditor::Range(view->cursorPosition(), qMin(buf.length(), textLine->length() - view->cursorPosition().column())));
03832 
03833   insertText(view->cursorPosition(), buf);
03834   if (bracketInserted)
03835     view->