• Skip to content
  • Skip to link menu
KDE 4.2 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 "katekeyinterceptorfunctor.h"
00027 #include "kateglobal.h"
00028 #include "katedialogs.h"
00029 #include "katehighlight.h"
00030 #include "kateview.h"
00031 #include "kateautoindent.h"
00032 #include "katetextline.h"
00033 #include "katedocumenthelpers.h"
00034 #include "kateprinter.h"
00035 #include "katesmartcursor.h"
00036 #include "katerenderer.h"
00037 #include <ktexteditor/attribute.h>
00038 #include "kateconfig.h"
00039 #include "katemodemanager.h"
00040 #include "kateschema.h"
00041 #include "katetemplatehandler.h"
00042 #include "katesmartmanager.h"
00043 #include <ktexteditor/plugin.h>
00044 #include <ktexteditor/loadsavefiltercheckplugin.h>
00045 #include "kateedit.h"
00046 #include "katebuffer.h"
00047 #include "kateundo.h"
00048 #include "katepartpluginmanager.h"
00049 
00050 #include <kio/job.h>
00051 #include <kio/jobuidelegate.h>
00052 #include <kio/netaccess.h>
00053 #include <kio/kfileitem.h>
00054 
00055 #include <kparts/event.h>
00056 
00057 #include <klocale.h>
00058 #include <kglobal.h>
00059 #include <kapplication.h>
00060 #include <kmenu.h>
00061 #include <kconfig.h>
00062 #include <kfiledialog.h>
00063 #include <kmessagebox.h>
00064 #include <kstandardaction.h>
00065 #include <kxmlguifactory.h>
00066 #include <kdebug.h>
00067 #include <kglobalsettings.h>
00068 #include <klibloader.h>
00069 #include <kdirwatch.h>
00070 #include <kencodingfiledialog.h>
00071 #include <ktemporaryfile.h>
00072 #include <kcodecs.h>
00073 #include <kstandarddirs.h>
00074 #include <kstringhandler.h>
00075 
00076 #include <services/kservicetypetrader.h>
00077 
00078 #include <QtDBus/QtDBus>
00079 #include <QtCore/QTimer>
00080 #include <QtCore/QFile>
00081 #include <QtGui/QClipboard>
00082 #include <QtCore/QTextStream>
00083 #include <QtCore/QTextCodec>
00084 #include <QtCore/QMap>
00085 #include <QtCore/QMutex>
00086 //END  includes
00087 
00088 
00089 
00090 // Turn debug messages on/off here
00091 // #define FAST_DEBUG_ENABLE
00092 
00093 #ifdef FAST_DEBUG_ENABLE
00094 # define FAST_DEBUG(x) (kDebug( 13020 ) << x)
00095 #else
00096 # define FAST_DEBUG(x)
00097 #endif
00098 
00099 
00100 
00101 //BEGIN PRIVATE CLASSES
00102 class KatePartPluginItem
00103 {
00104   public:
00105     KTextEditor::Plugin *plugin;
00106 };
00107 //END PRIVATE CLASSES
00108 
00109 static int dummy = 0;
00110 
00111 
00112 class KateDocument::LoadSaveFilterCheckPlugins
00113 {
00114   public:
00115     LoadSaveFilterCheckPlugins() {
00116       KService::List traderList = KServiceTypeTrader::self()->query("KTextEditor/LoadSaveFilterCheckPlugin");
00117 
00118       foreach(const KService::Ptr &ptr, traderList)
00119       {
00120         QString libname;
00121         libname=ptr->library();
00122         libname=libname.right(libname.length()-12); //ktexteditor_ == 12
00123         m_plugins[libname]=0;//new KatePythonEncodingCheck();
00124       }
00125 
00126     }
00127     ~LoadSaveFilterCheckPlugins() {
00128       if ( m_plugins.count()==0) return;
00129       QHashIterator<QString,KTextEditor::LoadSaveFilterCheckPlugin*>i(m_plugins);
00130         while (i.hasNext())
00131           i.next();
00132           delete i.value();
00133       m_plugins.clear();
00134     }
00135     bool preSavePostDialogFilterCheck(const QString& pluginName,KateDocument *document,QWidget *parentWidget) {
00136       KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00137       if (!plug) {
00138         if (KMessageBox::warningContinueCancel (parentWidget
00139         , i18n ("The filter/check plugin '%1' could not be found, still continue saving of %2", pluginName,document->url().pathOrUrl())
00140         , i18n ("Saving problems")
00141         , KGuiItem(i18n("Save Nevertheless"))
00142         , KStandardGuiItem::cancel()) != KMessageBox::Continue)
00143           return false;
00144         else
00145           return true;
00146       }
00147       return plug->preSavePostDialogFilterCheck(document);
00148     }
00149     void postLoadFilter(const QString& pluginName,KateDocument *document) {
00150       KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00151       if (!plug) return;
00152       plug->postLoadFilter(document);
00153     }
00154     bool postSaveFilterCheck(const QString& pluginName,KateDocument *document,bool saveas) {
00155       KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00156       if (!plug) return false;
00157       return plug->postSaveFilterCheck(document,saveas);
00158     }
00159   private:
00160     KTextEditor::LoadSaveFilterCheckPlugin *getPlugin(const QString & pluginName)
00161     {
00162       if (!m_plugins.contains(pluginName)) return 0;
00163       if (!m_plugins[pluginName]) {
00164         m_plugins[pluginName]=KLibLoader::createInstance<KTextEditor::LoadSaveFilterCheckPlugin>( "ktexteditor_"+pluginName);
00165       }
00166       return m_plugins[pluginName];
00167     }
00168     QHash <QString,KTextEditor::LoadSaveFilterCheckPlugin*> m_plugins;
00169 };
00170 
00171 //BEGIN d'tor, c'tor
00172 //
00173 // KateDocument Constructor
00174 //
00175 KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView,
00176                              bool bReadOnly, QWidget *parentWidget,
00177                              QObject *parent)
00178 : KTextEditor::Document (parent),
00179   m_activeView(0L),
00180   m_undoDontMerge(false),
00181   m_undoIgnoreCancel(false),
00182   m_mergeAllEdits(false),
00183   m_firstMergeGroupSkipped(false),
00184   lastUndoGroupWhenSaved( 0 ),
00185   lastRedoGroupWhenSaved( 0 ),
00186   docWasSavedWhenUndoWasEmpty( true ),
00187   docWasSavedWhenRedoWasEmpty( true ),
00188   m_annotationModel( 0 ),
00189   m_saveAs(false),
00190   m_indenter(this),
00191   m_modOnHd (false),
00192   m_modOnHdReason (OnDiskUnmodified),
00193   s_fileChangedDialogsActivated (false),
00194   m_tabInterceptor(0),
00195   m_savingToUrl(false)
00196 {
00197   setComponentData ( KateGlobal::self()->componentData () );
00198 
00199   m_undoComplexMerge=false;
00200 
00201   QString pathName ("/Kate/Document/%1");
00202   pathName = pathName.arg (++dummy);
00203 
00204   // my dbus object
00205   QDBusConnection::sessionBus().registerObject (pathName, this);
00206 
00207   // register doc at factory
00208   KateGlobal::self()->registerDocument(this);
00209 
00210   m_reloading = false;
00211 
00212   m_editHistory = new KateEditHistory(this);
00213   m_smartManager = new KateSmartManager(this);
00214   m_buffer = new KateBuffer(this);
00215 
00216   // init the config object, be careful not to use it
00217   // until the initial readConfig() call is done
00218   m_config = new KateDocumentConfig(this);
00219 
00220   // init some more vars !
00221   setActiveView(0L);
00222 
00223   hlSetByUser = false;
00224   m_fileTypeSetByUser = false;
00225 
00226   editSessionNumber = 0;
00227   editIsRunning = false;
00228   m_editCurrentUndo = 0L;
00229   editWithUndo = false;
00230 
00231   m_docNameNumber = 0;
00232   m_docName = "need init";
00233 
00234   m_bSingleViewMode = bSingleViewMode;
00235   m_bBrowserView = bBrowserView;
00236   m_bReadOnly = bReadOnly;
00237 
00238   setEditableMarks( markType01 );
00239 
00240   m_undoMergeTimer = new QTimer(this);
00241   m_undoMergeTimer->setSingleShot(true);
00242   connect(m_undoMergeTimer, SIGNAL(timeout()), SLOT(undoCancel()));
00243 
00244   clearMarks ();
00245   clearUndo ();
00246   clearRedo ();
00247   setModified (false);
00248   docWasSavedWhenUndoWasEmpty = true;
00249 
00250   // normal hl
00251   m_buffer->setHighlight (0);
00252 
00253   m_blockRemoveTrailingSpaces = false;
00254   m_extension = new KateBrowserExtension( this );
00255 
00256   // important, fill in the config into the indenter we use...
00257   m_indenter.updateConfig ();
00258 
00259   // some nice signals from the buffer
00260   connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int)));
00261   connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated()));
00262 
00263   // if the user changes the highlight with the dialog, notify the doc
00264   connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged()));
00265 
00266   // signals for mod on hd
00267   connect( KateGlobal::self()->dirWatch(), SIGNAL(dirty (const QString &)),
00268            this, SLOT(slotModOnHdDirty (const QString &)) );
00269 
00270   connect( KateGlobal::self()->dirWatch(), SIGNAL(created (const QString &)),
00271            this, SLOT(slotModOnHdCreated (const QString &)) );
00272 
00273   connect( KateGlobal::self()->dirWatch(), SIGNAL(deleted (const QString &)),
00274            this, SLOT(slotModOnHdDeleted (const QString &)) );
00275 
00276   connect (this,SIGNAL(completed()),this,SLOT(slotCompleted()));
00277   connect (this,SIGNAL(canceled(const QString&)),this,SLOT(slotCanceled()));
00278   // update doc name
00279   setDocName ("");
00280 
00281   // if single view mode, like in the konqui embedding, create a default view ;)
00282   // be lazy, only create it now, if any parentWidget is given, otherwise widget()
00283   // will create it on demand...
00284   if ( m_bSingleViewMode && parentWidget )
00285   {
00286     KTextEditor::View *view = (KTextEditor::View*)createView( parentWidget );
00287     insertChildClient( view );
00288     view->show();
00289     setWidget( view );
00290   }
00291 
00292   connect(this,SIGNAL(sigQueryClose(bool *, bool*)),this,SLOT(slotQueryClose_save(bool *, bool*)));
00293 
00294   m_isasking = 0;
00295 
00296   // register document in plugins
00297   KatePartPluginManager::self()->addDocument(this);
00298 }
00299 
00300 //
00301 // KateDocument Destructor
00302 //
00303 KateDocument::~KateDocument()
00304 {
00305   // Tell the world that we're about to close (== destruct)
00306   // Apps must receive this in a direct signal-slot connection, and prevent
00307   // any further use of interfaces once they return.
00308   emit aboutToClose(this);
00309 
00310   // remove file from dirwatch
00311   deactivateDirWatch ();
00312 
00313   // thanks for offering, KPart, but we're already self-destructing
00314   setAutoDeleteWidget(false);
00315   setAutoDeletePart(false);
00316 
00317   // clean up remaining views
00318   while (!m_views.isEmpty()) {
00319     delete m_views.takeFirst();
00320   }
00321 
00322   delete m_editCurrentUndo;
00323 
00324   // cleanup the undo/redo items, very important, truee :/
00325   qDeleteAll(undoItems);
00326   undoItems.clear();
00327   qDeleteAll(redoItems);
00328   redoItems.clear();
00329 
00330   // de-register from plugin
00331   KatePartPluginManager::self()->removeDocument(this);
00332 
00333   // cu marks
00334   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
00335     delete i.value();
00336   m_marks.clear();
00337 
00338   delete m_config;
00339   KateGlobal::self()->deregisterDocument (this);
00340 }
00341 //END
00342 
00343 // on-demand view creation
00344 QWidget *KateDocument::widget()
00345 {
00346   // no singleViewMode -> no widget()...
00347   if (!singleViewMode())
00348     return 0;
00349 
00350   // does a widget exist already? use it!
00351   if (KTextEditor::Document::widget())
00352     return KTextEditor::Document::widget();
00353 
00354   // create and return one...
00355   KTextEditor::View *view = (KTextEditor::View*)createView(0);
00356   insertChildClient( view );
00357   setWidget( view );
00358   return view;
00359 }
00360 
00361 //BEGIN KTextEditor::Document stuff
00362 
00363 KTextEditor::View *KateDocument::createView( QWidget *parent )
00364 {
00365   KateView* newView = new KateView( this, parent);
00366   connect(newView, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), SLOT(undoCancel()));
00367   if ( s_fileChangedDialogsActivated )
00368     connect( newView, SIGNAL(focusIn( KTextEditor::View * )), this, SLOT(slotModifiedOnDisk()) );
00369 
00370   emit viewCreated (this, newView);
00371 
00372   return newView;
00373 }
00374 
00375 const QList<KTextEditor::View*> &KateDocument::views () const
00376 {
00377   return m_textEditViews;
00378 }
00379 
00380 KTextEditor::Editor *KateDocument::editor ()
00381 {
00382   return KateGlobal::self();
00383 }
00384 
00385 //BEGIN KTextEditor::EditInterface stuff
00386 
00387 QString KateDocument::text() const
00388 {
00389   QString s;
00390 
00391   for (int i = 0; i < m_buffer->count(); i++)
00392   {
00393     KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00394 
00395     if (textLine)
00396     {
00397       s.append (textLine->string());
00398 
00399       if ((i+1) < m_buffer->count())
00400         s.append(QChar::fromAscii('\n'));
00401     }
00402   }
00403 
00404   return s;
00405 }
00406 
00407 QString KateDocument::text( const KTextEditor::Range& range, bool blockwise ) const
00408 {
00409   if (!range.isValid()) {
00410     kWarning() << k_funcinfo << "Text requested for invalid range" << range;
00411     return QString();
00412   }
00413 
00414   if ( blockwise && (range.start().column() > range.end().column()) )
00415     return QString ();
00416 
00417   QString s;
00418 
00419   if (range.start().line() == range.end().line())
00420   {
00421     if (range.start().column() > range.end().column())
00422       return QString ();
00423 
00424     KateTextLine::Ptr textLine = m_buffer->plainLine(range.start().line());
00425 
00426     if ( !textLine )
00427       return QString ();
00428 
00429     return textLine->string(range.start().column(), range.end().column()-range.start().column());
00430   }
00431   else
00432   {
00433 
00434     for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
00435     {
00436       KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00437 
00438       if ( !blockwise )
00439       {
00440         if (i == range.start().line())
00441           s.append (textLine->string(range.start().column(), textLine->length()-range.start().column()));
00442         else if (i == range.end().line())
00443           s.append (textLine->string(0, range.end().column()));
00444         else
00445           s.append (textLine->string());
00446       }
00447       else
00448       {
00449         s.append( textLine->string( range.start().column(), range.end().column()-range.start().column()));
00450       }
00451 
00452       if ( i < range.end().line() )
00453         s.append(QChar::fromAscii('\n'));
00454     }
00455   }
00456 
00457   return s;
00458 }
00459 
00460 QChar KateDocument::character( const KTextEditor::Cursor & position ) const
00461 {
00462   KateTextLine::Ptr textLine = m_buffer->plainLine(position.line());
00463 
00464   if ( !textLine )
00465     return QChar();
00466 
00467   if (position.column() >= 0 && position.column() < textLine->string().length())
00468     return textLine->string().at(position.column());
00469 
00470   return QChar();
00471 }
00472 
00473 QStringList KateDocument::textLines( const KTextEditor::Range & range, bool blockwise ) const
00474 {
00475   QStringList ret;
00476 
00477   if (!range.isValid()) {
00478     kWarning() << k_funcinfo << "Text requested for invalid range" << range;
00479     return ret;
00480   }
00481 
00482   if ( blockwise && (range.start().column() > range.end().column()) )
00483     return ret;
00484 
00485   if (range.start().line() == range.end().line())
00486   {
00487     Q_ASSERT(range.start() <= range.end());
00488 
00489     KateTextLine::Ptr textLine = m_buffer->plainLine(range.start().line());
00490 
00491     if ( !textLine )
00492       return ret;
00493 
00494     ret << textLine->string(range.start().column(), range.end().column() - range.start().column());
00495   }
00496   else
00497   {
00498     for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
00499     {
00500       KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00501 
00502       if ( !blockwise )
00503       {
00504         if (i == range.start().line())
00505           ret << textLine->string(range.start().column(), textLine->length() - range.start().column());
00506         else if (i == range.end().line())
00507           ret << textLine->string(0, range.end().column());
00508         else
00509           ret << textLine->string();
00510       }
00511       else
00512       {
00513         ret << textLine->string(range.start().column(), range.end().column() - range.start().column());
00514       }
00515     }
00516   }
00517 
00518   return ret;
00519 }
00520 
00521 QString KateDocument::line( int line ) const
00522 {
00523   KateTextLine::Ptr l = m_buffer->plainLine(line);
00524 
00525   if (!l)
00526     return QString();
00527 
00528   return l->string();
00529 }
00530 
00531 bool KateDocument::setText(const QString &s)
00532 {
00533   if (!isReadWrite())
00534     return false;
00535 
00536   QList<KTextEditor::Mark> msave;
00537 
00538   foreach (KTextEditor::Mark* mark, m_marks)
00539     msave.append(*mark);
00540 
00541   editStart ();
00542 
00543   // delete the text
00544   clear();
00545 
00546   // insert the new text
00547   insertText (KTextEditor::Cursor(), s);
00548 
00549   editEnd ();
00550 
00551   foreach (const KTextEditor::Mark& mark, msave)
00552     setMark (mark.line, mark.type);
00553 
00554   return true;
00555 }
00556 
00557 bool KateDocument::setText( const QStringList & text )
00558 {
00559   if (!isReadWrite())
00560     return false;
00561 
00562   QList<KTextEditor::Mark> msave;
00563 
00564   foreach (KTextEditor::Mark* mark, m_marks)
00565     msave.append(*mark);
00566 
00567   editStart ();
00568 
00569   // delete the text
00570   clear();
00571 
00572   // insert the new text
00573   insertText (KTextEditor::Cursor::start(), text);
00574 
00575   editEnd ();
00576 
00577   foreach (const KTextEditor::Mark& mark, msave)
00578     setMark (mark.line, mark.type);
00579 
00580   return true;
00581 }
00582 
00583 bool KateDocument::clear()
00584 {
00585   if (!isReadWrite())
00586     return false;
00587 
00588   foreach (KateView *view, m_views) {
00589     view->clear();
00590     view->tagAll();
00591     view->update();
00592   }
00593 
00594   clearMarks ();
00595 
00596   return removeText (KTextEditor::Range(KTextEditor::Cursor(), KTextEditor::Cursor(lastLine()+1, 0)));
00597 }
00598 
00599 bool KateDocument::insertText( const KTextEditor::Cursor& position, const QString& text, bool block )
00600 {
00601   if (!isReadWrite())
00602     return false;
00603 
00604   if (text.isEmpty())
00605     return true;
00606 
00607   editStart();
00608 
00609   int currentLine = position.line();
00610   int currentLineStart = 0;
00611   int totalLength = text.length();
00612   int insertColumn = position.column();
00613 
00614   if (position.line() > lines())
00615   {
00616     int line = lines();
00617     while (line != position.line() + totalLength + 1)
00618     {
00619       editInsertLine(line,"");
00620       line++;
00621     }
00622   }
00623 
00624   bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn );
00625   int tabWidth = config()->tabWidth();
00626 
00627   static const QChar newLineChar('\n');
00628   static const QChar tabChar('\t');
00629   static const QChar spaceChar(' ');
00630 
00631   int insertColumnExpanded = insertColumn;
00632   KateTextLine::Ptr l = kateTextLine( currentLine );
00633   if (l)
00634     insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00635 
00636   int pos = 0;
00637   for (; pos < totalLength; pos++)
00638   {
00639     const QChar& ch = text.at(pos);
00640 
00641     if (ch == newLineChar)
00642     {
00643       // Only perform the text insert if there is text to insert
00644       if (currentLineStart < pos)
00645         editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00646 
00647       if ( !block )
00648       {
00649         editWrapLine(currentLine, insertColumn + pos - currentLineStart);
00650         insertColumn = 0;
00651       }
00652       else
00653       {
00654         if ( currentLine == lastLine() )
00655           editWrapLine(currentLine , insertColumn + pos - currentLineStart);
00656         insertColumn = position.column(); // tab expansion might change this
00657       }
00658 
00659       currentLine++;
00660       currentLineStart = pos + 1;
00661       l = kateTextLine( currentLine );
00662       if (l)
00663         insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00664     }
00665     else
00666     {
00667       if ( replacetabs && ch == tabChar )
00668       {
00669         int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth );
00670         editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar));
00671 
00672         insertColumn += pos - currentLineStart + spacesRequired;
00673         currentLineStart = pos + 1;
00674         l = kateTextLine( currentLine );
00675         if (l)
00676           insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00677       }
00678     }
00679   }
00680 
00681   // Only perform the text insert if there is text to insert
00682   if (currentLineStart < pos)
00683     editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00684 
00685   editEnd();
00686   return true;
00687 }
00688 
00689 bool KateDocument::insertText( const KTextEditor::Cursor & position, const QStringList & textLines, bool block )
00690 {
00691   if (!isReadWrite())
00692     return false;
00693 
00694   if (textLines.isEmpty() || (textLines.count() == 1 && textLines.first().isEmpty()))
00695     return true;
00696 
00697   // FIXME - huh, contradicted below
00698   if (position.line() > lines())
00699     return false;
00700 
00701   editStart();
00702 
00703   if (position.line() > lines())
00704     editInsertLine(position.line(),"");
00705 
00706   int currentLine = position.line();
00707   int currentLineStart = 0;
00708   int insertColumn = position.column();
00709 
00710   bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn );
00711   int tabWidth = config()->tabWidth();
00712 
00713   static const QChar newLineChar('\n');
00714   static const QChar tabChar('\t');
00715   static const QChar spaceChar(' ');
00716 
00717   int insertColumnExpanded = insertColumn;
00718   KateTextLine::Ptr l = kateTextLine( currentLine );
00719   if (l)
00720     insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00721 
00722   foreach (const QString &text, textLines)
00723   {
00724     int pos = 0;
00725     for (; pos < text.length(); pos++)
00726     {
00727       const QChar& ch = text.at(pos);
00728 
00729       if (ch == newLineChar)
00730       {
00731         // Only perform the text insert if there is text to insert
00732         if (currentLineStart < pos)
00733           editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00734 
00735         if ( !block )
00736         {
00737           editWrapLine(currentLine, pos + insertColumn);
00738           insertColumn = 0;
00739         }
00740         else
00741         {
00742           if ( currentLine == lastLine() )
00743             editWrapLine(currentLine , insertColumn + pos - currentLineStart);
00744           insertColumn = position.column(); // tab expansion might change this
00745         }
00746 
00747         currentLine++;
00748         currentLineStart = pos + 1;
00749         l = kateTextLine( currentLine );
00750         if (l)
00751           insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00752       }
00753       else
00754       {
00755         if ( replacetabs && ch == tabChar )
00756         {
00757           int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth );
00758           editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar));
00759 
00760           insertColumn += pos - currentLineStart + spacesRequired;
00761           l = kateTextLine( currentLine );
00762           if (l)
00763             insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00764           currentLineStart = pos + 1;
00765         }
00766       }
00767     }
00768 
00769     // Only perform the text insert if there is text to insert
00770     if (currentLineStart < pos - currentLineStart)
00771       editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00772   }
00773 
00774   editEnd();
00775   return true;
00776 }
00777 
00778 
00779 bool KateDocument::removeText ( const KTextEditor::Range &_range, bool block )
00780 {
00781   KTextEditor::Range range = _range;
00782 
00783   if (!isReadWrite())
00784     return false;
00785 
00786   if ( block && (range.start().column() > range.end().column()) )
00787     return false;
00788 
00789   // Should now be impossible to trigger with the new Range class
00790   Q_ASSERT( range.start().line() <= range.end().line() );
00791 
00792   if ( range.start().line() > lastLine() )
00793     return false;
00794 
00795   if (!block)
00796     emit aboutToRemoveText(range);
00797 
00798   editStart();
00799 
00800   if ( !block )
00801   {
00802     if ( range.end().line() > lastLine() )
00803     {
00804       range.end().setPosition(lastLine()+1, 0);
00805     }
00806 
00807     if (range.onSingleLine())
00808     {
00809       editRemoveText(range.start().line(), range.start().column(), range.columnWidth());
00810     }
00811     else if (range.start().line() + 1 == range.end().line())
00812     {
00813       if ( (m_buffer->plainLine(range.start().line())->length() - range.start().column()) > 0 )
00814         editRemoveText(range.start().line(), range.start().column(), m_buffer->plainLine(range.start().line())->length() - range.start().column());
00815 
00816       if (range.end().column())
00817         editRemoveText (range.start().line() + 1, 0, range.end().column());
00818 
00819       editUnWrapLine (range.start().line());
00820     }
00821     else
00822     {
00823       for (int line = range.end().line(); line >= range.start().line(); --line)
00824       {
00825         if ((line > range.start().line()) && (line < range.end().line())) {
00826           editRemoveLine(line);
00827 
00828         } else if (line == range.end().line()) {
00829           if ( range.end().line() <= lastLine() )
00830             editRemoveText(line, 0, range.end().column());
00831 
00832         } else {
00833           if ( (m_buffer->plainLine(line)->length() - range.start().column()) > 0 )
00834             editRemoveText(line, range.start().column(), m_buffer->plainLine(line)->length() - range.start().column());
00835 
00836           editUnWrapLine(range.start().line());
00837         }
00838 
00839         if ( line == 0 )
00840           break;
00841       }
00842     }
00843   } // if ( ! block )
00844   else
00845   {
00846     int startLine = qMax(0, range.start().line());
00847     for (int line = qMin(range.end().line(), lastLine()); line >= startLine; --line)
00848       editRemoveText(line, range.start().column(), range.end().column() - range.start().column());
00849   }
00850 
00851   editEnd ();
00852   emit textRemoved();
00853   return true;
00854 }
00855 
00856 bool KateDocument::insertLine( int l, const QString &str )
00857 {
00858   if (!isReadWrite())
00859     return false;
00860 
00861   if (l < 0 || l > lines())
00862     return false;
00863 
00864   return editInsertLine (l, str);
00865 }
00866 
00867 bool KateDocument::insertLines( int line, const QStringList & text )
00868 {
00869   if (!isReadWrite())
00870     return false;
00871 
00872   if (line < 0 || line > lines())
00873     return false;
00874 
00875   bool success = true;
00876   foreach (const QString &string, text)
00877     // FIXME assumes no \n in each string
00878     success &= editInsertLine (line++, string);
00879 
00880   return success;
00881 }
00882 
00883 bool KateDocument::removeLine( int line )
00884 {
00885   if (!isReadWrite())
00886     return false;
00887 
00888   if (line < 0 || line > lastLine())
00889     return false;
00890 
00891   return editRemoveLine (line);
00892 }
00893 
00894 int KateDocument::totalCharacters() const
00895 {
00896   int l = 0;
00897 
00898   for (int i = 0; i < m_buffer->count(); ++i)
00899   {
00900     KateTextLine::Ptr line = m_buffer->plainLine(i);
00901 
00902     if (line)
00903       l += line->length();
00904   }
00905 
00906   return l;
00907 }
00908 
00909 int KateDocument::lines() const
00910 {
00911   return m_buffer->count();
00912 }
00913 
00914 int KateDocument::numVisLines() const
00915 {
00916   return m_buffer->countVisible ();
00917 }
00918 
00919 int KateDocument::lineLength ( int line ) const
00920 {
00921   if (line < 0 || line > lastLine())
00922     return -1;
00923 
00924   KateTextLine::Ptr l = m_buffer->plainLine(line);
00925 
00926   if (!l)
00927     return -1;
00928 
00929   return l->length();
00930 }
00931 //END
00932 
00933 //BEGIN KTextEditor::EditInterface internal stuff
00934 //
00935 // Starts an edit session with (or without) undo, update of view disabled during session
00936 //
00937 void KateDocument::editStart (bool withUndo, Kate::EditSource editSource)
00938 {
00939   editSessionNumber++;
00940 
00941   if (editSource == Kate::NoEditSource)
00942     m_editSources.push(m_editSources.isEmpty() ? Kate::UserInputEdit : m_editSources.top());
00943   else
00944     m_editSources.push(editSource);
00945 
00946   if (editSessionNumber > 1)
00947     return;
00948 
00949   // Unlocked in editEnd
00950   smartMutex()->lock();
00951 
00952   editIsRunning = true;
00953   editWithUndo = withUndo;
00954 
00955   if (editWithUndo)
00956     undoStart();
00957   else
00958     undoCancel();
00959 
00960   foreach(KateView *view,m_views)
00961   {
00962     view->editStart ();
00963   }
00964 
00965   m_buffer->editStart ();
00966 }
00967 
00968 void KateDocument::undoStart()
00969 {
00970   if (m_editCurrentUndo || (m_activeView && activeKateView()->imComposeEvent())) return;
00971 
00972   // new current undo item
00973   m_editCurrentUndo = new KateUndoGroup(this);
00974   if (m_activeView) {
00975     m_editCurrentUndo->setUndoCursor(m_activeView->cursorPosition());
00976     m_editCurrentUndo->setUndoSelection(m_activeView->selectionRange());
00977   }
00978 }
00979 
00980 void KateDocument::undoEnd()
00981 {
00982   if (m_activeView && activeKateView()->imComposeEvent())
00983     return;
00984 
00985   if (m_editCurrentUndo)
00986   {
00987     bool changedUndo = false;
00988 
00989     if (m_activeView) {
00990         m_editCurrentUndo->setRedoCursor(m_activeView->cursorPosition());
00991         m_editCurrentUndo->setRedoSelection(m_activeView->selectionRange());
00992     }
00993 
00994     if (m_editCurrentUndo->isEmpty()) {
00995       delete m_editCurrentUndo;
00996     } else if (((m_mergeAllEdits && !m_firstMergeGroupSkipped) || !m_undoDontMerge)
00997         && !undoItems.isEmpty() && undoItems.last()
00998         && undoItems.last()->merge(m_editCurrentUndo, m_undoComplexMerge || m_mergeAllEdits)) {
00999       delete m_editCurrentUndo;
01000       m_firstMergeGroupSkipped = true;
01001     } else {
01002       undoItems.append(m_editCurrentUndo);
01003       changedUndo = true;
01004     }
01005 
01006     m_undoDontMerge = false;
01007     m_undoIgnoreCancel = true;
01008 
01009     m_editCurrentUndo = 0L;
01010 
01011     // (Re)Start the single-shot timer to cancel the undo merge
01012     // the user has 5 seconds to input more data, or undo merging gets canceled for the current undo item.
01013     m_undoMergeTimer->start(5000);
01014 
01015     if (changedUndo)
01016       emit undoChanged();
01017   }
01018 }
01019 
01020 void KateDocument::undoCancel()
01021 {
01022   // Don't worry about this when an edit is in progress
01023   if (editIsRunning)
01024     return;
01025 
01026   if (m_undoIgnoreCancel) {
01027     m_undoIgnoreCancel = false;
01028     return;
01029   }
01030 
01031   m_undoDontMerge = true;
01032 
01033   Q_ASSERT(!m_editCurrentUndo);
01034 
01035   // As you can see by the above assert, neither of these should really be required
01036   delete m_editCurrentUndo;
01037   m_editCurrentUndo = 0L;
01038 }
01039 
01040 void KateDocument::undoSafePoint() {
01041   Q_ASSERT(m_editCurrentUndo);
01042   if (!m_editCurrentUndo) return;
01043   m_editCurrentUndo->safePoint();
01044 }
01045 
01046 //
01047 // End edit session and update Views
01048 //
01049 void KateDocument::editEnd ()
01050 {
01051   if (editSessionNumber == 0)
01052     return;
01053 
01054   // wrap the new/changed text, if something really changed!
01055   if (m_buffer->editChanged() && (editSessionNumber == 1))
01056     if (editWithUndo && config()->wordWrap())
01057       wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
01058 
01059   editSessionNumber--;
01060 
01061   m_editSources.pop();
01062 
01063   if (editSessionNumber > 0)
01064     return;
01065 
01066   // end buffer edit, will trigger hl update
01067   // this will cause some possible adjustment of tagline start/end
01068   m_buffer->editEnd ();
01069 
01070   if (editWithUndo)
01071     undoEnd();
01072 
01073   // edit end for all views !!!!!!!!!
01074   foreach(KateView *view, m_views)
01075     view->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
01076 
01077   // Was locked in editStart
01078   smartMutex()->unlock();
01079 
01080   if (m_buffer->editChanged())
01081   {
01082     setModified(true);
01083     emit textChanged (this);
01084   }
01085 
01086   editIsRunning = false;
01087 }
01088 
01089 void KateDocument::pushEditState ()
01090 {
01091   editStateStack.push(editSessionNumber);
01092 }
01093 
01094 void KateDocument::popEditState ()
01095 {
01096   if (editStateStack.isEmpty()) return;
01097 
01098   int count = editStateStack.pop() - editSessionNumber;
01099   while (count < 0) { ++count; editEnd(); }
01100   while (count > 0) { --count; editStart(); }
01101 }
01102 
01103 bool KateDocument::wrapText(int startLine, int endLine)
01104 {
01105   if (startLine < 0 || endLine < 0)
01106     return false;
01107 
01108   if (!isReadWrite())
01109     return false;
01110 
01111   int col = config()->wordWrapAt();
01112 
01113   if (col == 0)
01114     return false;
01115 
01116   editStart ();
01117 
01118   for (int line = startLine; (line <= endLine) && (line < lines()); line++)
01119   {
01120     KateTextLine::Ptr l = kateTextLine(line);
01121 
01122     if (!l)
01123       return false;
01124 
01125     kDebug (13020) << "try wrap line: " << line;
01126 
01127     if (l->virtualLength(m_buffer->tabWidth()) > col)
01128     {
01129       KateTextLine::Ptr nextl = kateTextLine(line+1);
01130 
01131       kDebug (13020) << "do wrap line: " << line;
01132 
01133       int eolPosition = l->length()-1;
01134 
01135       // take tabs into account here, too
01136       int x = 0;
01137       const QString & t = l->string();
01138       int z2 = 0;
01139       for ( ; z2 < l->length(); z2++)
01140       {
01141         static const QChar tabChar('\t');
01142         if (t.at(z2) == tabChar)
01143           x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
01144         else
01145           x++;
01146 
01147         if (x > col)
01148           break;
01149       }
01150 
01151       int searchStart = qMin (z2, l->length()-1);
01152 
01153       // If where we are wrapping is an end of line and is a space we don't
01154       // want to wrap there
01155       if (searchStart == eolPosition && t.at(searchStart).isSpace())
01156         searchStart--;
01157 
01158       // Scan backwards looking for a place to break the line
01159       // We are not interested in breaking at the first char
01160       // of the line (if it is a space), but we are at the second
01161       // anders: if we can't find a space, try breaking on a word
01162       // boundary, using KateHighlight::canBreakAt().
01163       // This could be a priority (setting) in the hl/filetype/document
01164       int z = 0;
01165       int nw = 0; // alternative position, a non word character
01166       for (z=searchStart; z > 0; z--)
01167       {
01168         if (t.at(z).isSpace()) break;
01169         if ( ! nw && highlight()->canBreakAt( t.at(z) , l->attribute(z) ) )
01170         nw = z;
01171       }
01172 
01173       bool removeTrailingSpace = false;
01174       if (z > 0)
01175       {
01176         // So why don't we just remove the trailing space right away?
01177         // Well, the (view's) cursor may be directly in front of that space
01178         // (user typing text before the last word on the line), and if that
01179         // happens, the cursor would be moved to the next line, which is not
01180         // what we want (bug #106261)
01181         z++;
01182         removeTrailingSpace = true;
01183       }
01184       else
01185       {
01186         // There was no space to break at so break at a nonword character if
01187         // found, or at the wrapcolumn ( that needs be configurable )
01188         // Don't try and add any white space for the break
01189         if ( nw && nw < col ) nw++; // break on the right side of the character
01190         z = nw ? nw : col;
01191       }
01192 
01193       if (nextl && !nextl->isAutoWrapped())
01194       {
01195         editWrapLine (line, z, true);
01196         editMarkLineAutoWrapped (line+1, true);
01197 
01198         endLine++;
01199       }
01200       else
01201       {
01202         if (nextl && (nextl->length() > 0) && !nextl->at(0).isSpace() && ((l->length() < 1) || !l->at(l->length()-1).isSpace()))
01203           editInsertText (line+1, 0, QString (" "));
01204 
01205         bool newLineAdded = false;
01206         editWrapLine (line, z, false, &newLineAdded);
01207 
01208         editMarkLineAutoWrapped (line+1, true);
01209 
01210         endLine++;
01211       }
01212 
01213       if (removeTrailingSpace) {
01214         // cu space
01215         editRemoveText (line, z - 1, 1);
01216       }
01217     }
01218   }
01219 
01220   editEnd ();
01221 
01222   return true;
01223 }
01224 
01225 void KateDocument::editAddUndo (int type, uint line, uint col, uint len, const QString &text)
01226 {
01227   if (editIsRunning && editWithUndo && m_editCurrentUndo) {
01228     m_editCurrentUndo->addItem(static_cast<KateUndoGroup::UndoType>(type), line, col, len, text);
01229 
01230     // Clear redo buffer
01231     if (redoItems.count()) {
01232       qDeleteAll(redoItems);
01233       redoItems.clear();
01234     }
01235   }
01236 }
01237 
01238 bool KateDocument::editInsertText ( int line, int col, const QString &s, Kate::EditSource editSource )
01239 {
01240   if (line < 0 || col < 0)
01241     return false;
01242 
01243   if (!isReadWrite())
01244     return false;
01245 
01246   KateTextLine::Ptr l = kateTextLine(line);
01247 
01248   if (!l)
01249     return false;
01250 
01251   editStart (true, editSource);
01252 
01253   editAddUndo (KateUndoGroup::editInsertText, line, col, s.length(), s);
01254 
01255   l->insertText (col, s);
01256 
01257   m_buffer->changeLine(line);
01258 
01259   history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col), QStringList(), KTextEditor::Range(line, col, line, col + s.length()), QStringList(s)) );
01260   emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line, col + s.length()));
01261 
01262   editEnd();
01263 
01264   return true;
01265 }
01266 
01267 bool KateDocument::editRemoveText ( int line, int col, int len, Kate::EditSource editSource )
01268 {
01269   if (line < 0 || col < 0 || len < 0)
01270     return false;
01271 
01272   if (!isReadWrite())
01273     return false;
01274 
01275   KateTextLine::Ptr l = kateTextLine(line);
01276 
01277   if (!l)
01278     return false;
01279 
01280   editStart (true, editSource);
01281 
01282   editAddUndo (KateUndoGroup::editRemoveText, line, col, len, l->string().mid(col, len));
01283 
01284   l->removeText (col, len);
01285   removeTrailingSpace( line );
01286 
01287   m_buffer->changeLine(line);
01288 
01289   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()) );
01290   emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len));
01291 
01292   editEnd ();
01293 
01294   return true;
01295 }
01296 
01297 bool KateDocument::editMarkLineAutoWrapped ( int line, bool autowrapped )
01298 {
01299   if (line < 0)
01300     return false;
01301 
01302   if (!isReadWrite())
01303     return false;
01304 
01305   KateTextLine::Ptr l = kateTextLine(line);
01306 
01307   if (!l)
01308     return false;
01309 
01310   editStart ();
01311 
01312   editAddUndo (KateUndoGroup::editMarkLineAutoWrapped, line, autowrapped ? 1 : 0, 0, QString());
01313 
01314   l->setAutoWrapped (autowrapped);
01315 
01316   m_buffer->changeLine(line);
01317 
01318   editEnd ();
01319 
01320   return true;
01321 }
01322 
01323 bool KateDocument::editWrapLine ( int line, int col, bool newLine, bool *newLineAdded)
01324 {
01325   if (line < 0 || col < 0)
01326     return false;
01327 
01328   if (!isReadWrite())
01329     return false;
01330 
01331   KateTextLine::Ptr l = kateTextLine(line);
01332 
01333   if (!l)
01334     return false;
01335 
01336   editStart ();
01337 
01338   KateTextLine::Ptr nextLine = kateTextLine(line+1);
01339 
01340   int pos = l->length() - col;
01341 
01342   if (pos < 0)
01343     pos = 0;
01344 
01345   editAddUndo (KateUndoGroup::editWrapLine, line, col, pos, (!nextLine || newLine) ? "1" : "0");
01346 
01347   if (!nextLine || newLine)
01348   {
01349     KateTextLine::Ptr textLine(new KateTextLine());
01350 
01351     textLine->insertText (0, l->string().mid(col, pos));
01352     l->truncate(col);
01353 
01354     m_buffer->insertLine (line+1, textLine);
01355     m_buffer->changeLine(line);
01356 
01357     QList<KTextEditor::Mark*> list;
01358     for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01359     {
01360       if( i.value()->line >= line )
01361       {
01362         if ((col == 0) || (i.value()->line > line))
01363           list.append( i.value() );
01364       }
01365     }
01366 
01367     for( int i=0; i < list.size(); ++i )
01368       m_marks.take( list[i]->line );
01369 
01370     for( int i=0; i < list.size(); ++i )
01371     {
01372       list[i]->line++;
01373       m_marks.insert( list[i]->line, list[i] );
01374     }
01375 
01376     if( !list.isEmpty() )
01377       emit marksChanged( this );
01378 
01379     // yes, we added a new line !
01380     if (newLineAdded)
01381       (*newLineAdded) = true;
01382 
01383     history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col), QStringList(), KTextEditor::Range(line, col, line+1, 0), QStringList()) );
01384   }
01385   else
01386   {
01387     nextLine->insertText (0, l->string().mid(col, pos));
01388     l->truncate(col);
01389 
01390     m_buffer->changeLine(line);
01391     m_buffer->changeLine(line+1);
01392 
01393     // no, no new line added !
01394     if (newLineAdded)
01395       (*newLineAdded) = false;
01396 
01397     history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line+1, 0), QStringList(), KTextEditor::Range(line, col, line+1, pos), QStringList()) );
01398   }
01399 
01400   emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line+1, pos));
01401 
01402   editEnd ();
01403 
01404   return true;
01405 }
01406 
01407 bool KateDocument::editUnWrapLine ( int line, bool removeLine, int length )
01408 {
01409   if (line < 0 || length < 0)
01410     return false;
01411 
01412   if (!isReadWrite())
01413     return false;
01414 
01415   KateTextLine::Ptr l = kateTextLine(line);
01416   KateTextLine::Ptr nextLine = kateTextLine(line+1);
01417 
01418   if (!l || !nextLine)
01419     return false;
01420 
01421   editStart ();
01422 
01423   int col = l->length ();
01424 
01425   editAddUndo (KateUndoGroup::editUnWrapLine, line, col, length, removeLine ? "1" : "0");
01426 
01427   if (removeLine)
01428   {
01429     l->insertText (col, nextLine->string());
01430 
01431     m_buffer->changeLine(line);
01432     m_buffer->removeLine(line+1);
01433   }
01434   else
01435   {
01436     l->insertText (col, nextLine->string().left((nextLine->length() < length) ? nextLine->length() : length));
01437     nextLine->removeText (0, (nextLine->length() < length) ? nextLine->length() : length);
01438 
01439     m_buffer->changeLine(line);
01440     m_buffer->changeLine(line+1);
01441   }
01442 
01443   QList<KTextEditor::Mark*> list;
01444   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01445   {
01446     if( i.value()->line >= line+1 )
01447       list.append( i.value() );
01448 
01449     if ( i.value()->line == line+1 )
01450     {
01451       KTextEditor::Mark* mark = m_marks.take( line );
01452 
01453       if (mark)
01454       {
01455         i.value()->type |= mark->type;
01456       }
01457     }
01458   }
01459 
01460    for( int i=0; i < list.size(); ++i )
01461       m_marks.take( list[i]->line );
01462 
01463    for( int i=0; i < list.size(); ++i )
01464    {
01465       list[i]->line--;
01466       m_marks.insert( list[i]->line, list[i] );
01467     }
01468 
01469   if( !list.isEmpty() )
01470     emit marksChanged( this );
01471 
01472   history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line+1, 0), QStringList(QString()), KTextEditor::Range(line, col, line, col), QStringList()) );
01473   emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line+1, 0));
01474 
01475   editEnd ();
01476 
01477   return true;
01478 }
01479 
01480 bool KateDocument::editInsertLine ( int line, const QString &s, Kate::EditSource editSource )
01481 {
01482   if (line < 0)
01483     return false;
01484 
01485   if (!isReadWrite())
01486     return false;
01487 
01488   if ( line > lines() )
01489     return false;
01490 
01491   editStart (true, editSource);
01492 
01493   editAddUndo (KateUndoGroup::editInsertLine, line, 0, s.length(), s);
01494 
01495   removeTrailingSpace( line ); // old line
01496 
01497   KateTextLine::Ptr tl(new KateTextLine());
01498   tl->insertText (0, s);
01499   m_buffer->insertLine(line, tl);
01500   m_buffer->changeLine(line);
01501 
01502   removeTrailingSpace( line ); // new line
01503 
01504   QList<KTextEditor::Mark*> list;
01505   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01506   {
01507     if( i.value()->line >= line )
01508       list.append( i.value() );
01509   }
01510 
01511    for( int i=0; i < list.size(); ++i )
01512       m_marks.take( list[i]->line );
01513 
01514    for( int i=0; i < list.size(); ++i )
01515    {
01516       list[i]->line++;
01517       m_marks.insert( list[i]->line, list[i] );
01518     }
01519 
01520   if( !list.isEmpty() )
01521     emit marksChanged( this );
01522 
01523   KTextEditor::Range rangeInserted(line, 0, line, tl->length());
01524 
01525   if (line) {
01526     KateTextLine::Ptr prevLine = plainKateTextLine(line - 1);
01527     rangeInserted.start().setPosition(line - 1, prevLine->length());
01528   } else {
01529     rangeInserted.end().setPosition(line + 1, 0);
01530   }
01531 
01532   history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(rangeInserted.start(), rangeInserted.start()), QStringList(), rangeInserted, QStringList(s)) );
01533   emit KTextEditor::Document::textInserted(this, rangeInserted);
01534 
01535   editEnd ();
01536 
01537   return true;
01538 }
01539 
01540 bool KateDocument::editRemoveLine ( int line, Kate::EditSource editSource )
01541 {
01542   if (line < 0)
01543     return false;
01544 
01545   if (!isReadWrite())
01546     return false;
01547 
01548   if ( line > lastLine() )
01549     return false;
01550 
01551   if ( lines() == 1 )
01552     return editRemoveText (0, 0, kateTextLine(0)->length());
01553 
01554   KateLineInfo info;;
01555   lineInfo (&info, line);
01556   if (info.startsInVisibleBlock)
01557     foldingTree()->toggleRegionVisibility(line);
01558 
01559   editStart (true, editSource);
01560 
01561   QString oldText = this->line(line);
01562 
01563   editAddUndo (KateUndoGroup::editRemoveLine, line, 0, lineLength(line), this->line(line));
01564 
01565   KTextEditor::Range rangeRemoved(line, 0, line, oldText.length());
01566 
01567   if (line < lastLine()) {
01568     rangeRemoved.end().setPosition(line + 1, 0);
01569   } else if (line) {
01570     KateTextLine::Ptr prevLine = plainKateTextLine(line - 1);
01571     rangeRemoved.start().setPosition(line - 1, prevLine->length());
01572   }
01573 
01574   m_buffer->removeLine(line);
01575 
01576   KTextEditor::Mark* rmark = 0;
01577   QList<KTextEditor::Mark*> list;
01578   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01579   {
01580     if ( (i.value()->line > line) )
01581       list.append( i.value() );
01582     else if ( (i.value()->line == line) )
01583       rmark = i.value();
01584   }
01585 
01586   if (rmark)
01587     delete (m_marks.take (rmark->line));
01588 
01589   for( int i=0; i < list.size(); ++i )
01590   {
01591     KTextEditor::Mark* mark = m_marks.take( list[i]->line );
01592     mark->line--;
01593     m_marks.insert( mark->line, mark );
01594   }
01595 
01596   if( !list.isEmpty() )
01597     emit marksChanged( this );
01598 
01599   history()->doEdit( new KateEditInfo(m_editSources.top(), rangeRemoved, QStringList(QString(oldText)), KTextEditor::Range(rangeRemoved.start(), rangeRemoved.start()), QStringList()) );
01600   emit KTextEditor::Document::textRemoved(this, rangeRemoved);
01601 
01602   editEnd();
01603 
01604   return true;
01605 }
01606 //END
01607 
01608 //BEGIN KTextEditor::UndoInterface stuff
01609 
01610 uint KateDocument::undoCount () const
01611 {
01612   return undoItems.count ();
01613 }
01614 
01615 uint KateDocument::redoCount () const
01616 {
01617   return redoItems.count ();
01618 }
01619 
01620 void KateDocument::undo()
01621 {
01622   if ((undoItems.count() > 0) && undoItems.last())
01623   {
01624     //clearSelection ();
01625 
01626     undoItems.last()->undo();
01627     redoItems.append (undoItems.last());
01628     undoItems.removeLast ();
01629     updateModified();
01630 
01631     emit undoChanged ();
01632   }
01633 }
01634 
01635 void KateDocument::redo()
01636 {
01637   if ((redoItems.count() > 0) && redoItems.last())
01638   {
01639     //clearSelection ();
01640 
01641     redoItems.last()->redo();
01642     undoItems.append (redoItems.last());
01643     redoItems.removeLast ();
01644     updateModified();
01645 
01646     emit undoChanged ();
01647   }
01648 }
01649 
01650 void KateDocument::updateModified()
01651 {
01652   /*
01653   How this works:
01654 
01655     After noticing that there where to many scenarios to take into
01656     consideration when using 'if's to toggle the "Modified" flag
01657     I came up with this baby, flexible and repetitive calls are
01658     minimal.
01659 
01660     A numeric unique pattern is generated by toggling a set of bits,
01661     each bit symbolizes a different state in the Undo Redo structure.
01662 
01663       undoItems.isEmpty() != null          BIT 1
01664       redoItems.isEmpty() != null          BIT 2
01665       docWasSavedWhenUndoWasEmpty == true  BIT 3
01666       docWasSavedWhenRedoWasEmpty == true  BIT 4
01667       lastUndoGroupWhenSavedIsLastUndo     BIT 5
01668       lastUndoGroupWhenSavedIsLastRedo     BIT 6
01669       lastRedoGroupWhenSavedIsLastUndo     BIT 7
01670       lastRedoGroupWhenSavedIsLastRedo     BIT 8
01671 
01672     If you find a new pattern, please add it to the patterns array
01673   */
01674 
01675   unsigned char currentPattern = 0;
01676   const unsigned char patterns[] = {5,16,21,24,26,88,90,93,133,144,149,165};
01677   const unsigned char patternCount = sizeof(patterns);
01678   KateUndoGroup* undoLast = 0;
01679   KateUndoGroup* redoLast = 0;
01680 
01681   if (undoItems.isEmpty())
01682   {
01683     currentPattern |= 1;
01684   }
01685   else
01686   {
01687     undoLast = undoItems.last();
01688   }
01689 
01690   if (redoItems.isEmpty())
01691   {
01692     currentPattern |= 2;
01693   }
01694   else
01695   {
01696     redoLast = redoItems.last();
01697   }
01698 
01699   if (docWasSavedWhenUndoWasEmpty) currentPattern |= 4;
01700   if (docWasSavedWhenRedoWasEmpty) currentPattern |= 8;
01701   if (lastUndoGroupWhenSaved == undoLast) currentPattern |= 16;
01702   if (lastUndoGroupWhenSaved == redoLast) currentPattern |= 32;
01703   if (lastRedoGroupWhenSaved == undoLast) currentPattern |= 64;
01704   if (lastRedoGroupWhenSaved == redoLast) currentPattern |= 128;
01705 
01706   // This will print out the pattern information
01707 
01708   kDebug(13020) << "Pattern:" << static_cast<unsigned int>(currentPattern);
01709 
01710   for (uint patternIndex = 0; patternIndex < patternCount; ++patternIndex)
01711   {
01712     if ( currentPattern == patterns[patternIndex] )
01713     {
01714       setModified( false );
01715       // (dominik) whenever the doc is not modified, succeeding edits
01716       // should not be merged
01717       setUndoDontMerge(true);
01718       kDebug(13020) << "setting modified to false!";
01719       break;
01720     }
01721   }
01722 }
01723 
01724 void KateDocument::clearUndo()
01725 {
01726   qDeleteAll(undoItems);
01727   undoItems.clear ();
01728 
01729   lastUndoGroupWhenSaved = 0;
01730   docWasSavedWhenUndoWasEmpty = false;
01731 
01732   emit undoChanged ();
01733 }
01734 
01735 void KateDocument::clearRedo()
01736 {
01737   qDeleteAll(redoItems);
01738   redoItems.clear ();
01739 
01740   lastRedoGroupWhenSaved = 0;
01741   docWasSavedWhenRedoWasEmpty = false;
01742 
01743   emit undoChanged ();
01744 }
01745 //END
01746 
01747 //BEGIN KTextEditor::SearchInterface stuff
01748 
01749 KTextEditor::Range KateDocument::searchText (const KTextEditor::Range & inputRange, const QString &text, bool casesensitive, bool backwards)
01750 {
01751   FAST_DEBUG("KateDocument::searchText( " << inputRange.start().line() << ", "
01752     << inputRange.start().column() << ", " << text << ", " << backwards << " )");
01753   if (text.isEmpty() || !inputRange.isValid() || (inputRange.start() == inputRange.end()))
01754   {
01755     return KTextEditor::Range::invalid();
01756   }
01757 
01758   // split multi-line needle into single lines
01759   const QString sep("\n");
01760   const QStringList needleLines = text.split(sep);
01761   const int numNeedleLines = needleLines.count();
01762   FAST_DEBUG("searchText | needle has " << numNeedleLines << " lines");
01763 
01764   if (numNeedleLines > 1)
01765   {
01766     // multi-line plaintext search (both forwards or backwards)
01767     const int lastLine = inputRange.end().line();
01768 
01769     const int forMin   = inputRange.start().line(); // first line in range
01770     const int forMax   = lastLine + 1 - numNeedleLines; // last line in range
01771     const int forInit  = backwards ? forMax : forMin;
01772     const int forInc   = backwards ? -1 : +1;
01773 
01774     const int minLeft  = inputRange.start().column();
01775     const uint maxRight = inputRange.end().column(); // first not included
01776 
01777     // init hay line ring buffer
01778     int hayLinesZeroIndex = 0;
01779     QVector<KateTextLine::Ptr> hayLinesWindow;
01780     for (int i = 0; i < numNeedleLines; i++) {
01781       KateTextLine::Ptr textLine = m_buffer->plainLine((backwards ? forMax : forMin) + i);
01782 
01783       if (!textLine)
01784         return KTextEditor::Range::invalid();
01785 
01786       hayLinesWindow.append (textLine);
01787       FAST_DEBUG("searchText | hayLinesWindow[" << i << "] = \"" << hayLinesWindow[i]->string() << "\"");
01788     }
01789 
01790     for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01791     {
01792       // try to match all lines
01793       uint startCol = 0; // init value not important
01794       for (int k = 0; k < numNeedleLines; k++)
01795       {
01796         // which lines to compare
01797         const QString & needleLine = needleLines[k];
01798         KateTextLine::Ptr & hayLine = hayLinesWindow[(k + hayLinesZeroIndex) % numNeedleLines];
01799         FAST_DEBUG("searchText | hayLine = \"" << hayLine->string() << "\"");
01800 
01801         // position specific comparison (first, middle, last)
01802         if (k == 0) {
01803           // first line
01804           if (needleLine.length() == 0) // if needle starts with a newline
01805           {
01806             startCol = hayLine->length();
01807           }
01808           else
01809           {
01810             uint myMatchLen;
01811             const uint colOffset = (j > forMin) ? 0 : minLeft;
01812             const bool matches = hayLine->searchText(colOffset, hayLine->length(),needleLine, &startCol,
01813               &myMatchLen, casesensitive, false);
01814             if (!matches || (startCol + myMatchLen != static_cast<uint>(hayLine->length()))) {
01815               FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01816               break;
01817             }
01818             FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01819           }
01820         } else if (k == numNeedleLines - 1) {
01821           // last line
01822           uint foundAt, myMatchLen;
01823           const bool matches = hayLine->searchText(0,hayLine->length(), needleLine, &foundAt, &myMatchLen, casesensitive, false);
01824           if (matches && (foundAt == 0) && !((k == lastLine)
01825               && (static_cast<uint>(foundAt + myMatchLen) > maxRight))) // full match!
01826           {
01827             FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01828             return KTextEditor::Range(j, startCol, j + k, needleLine.length());
01829           }
01830           FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01831         } else {
01832           // mid lines
01833           uint foundAt, myMatchLen;
01834           const bool matches = hayLine->searchText(0, hayLine->length(),needleLine, &foundAt, &myMatchLen, casesensitive, false);
01835           if (!matches || (foundAt != 0) || (myMatchLen != static_cast<uint>(needleLine.length()))) {
01836             FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01837             break;
01838           }
01839           FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01840         }
01841       }
01842 
01843       // get a fresh line into the ring buffer
01844       if ((backwards && (j > forMin)) || (!backwards && (j < forMax)))
01845       {
01846         if (backwards)
01847         {
01848           hayLinesZeroIndex = (hayLinesZeroIndex + numNeedleLines - 1) % numNeedleLines;
01849 
01850           KateTextLine::Ptr textLine = m_buffer->plainLine(j - 1);
01851 
01852           if (!textLine)
01853             return KTextEditor::Range::invalid();
01854 
01855           hayLinesWindow[hayLinesZeroIndex] = textLine;
01856 
01857           FAST_DEBUG("searchText | filling slot " << hayLinesZeroIndex << " with line "
01858             << j - 1 << ": " << hayLinesWindow[hayLinesZeroIndex]->string());
01859         }
01860         else
01861         {
01862           hayLinesWindow[hayLinesZeroIndex] = m_buffer->plainLine(j + numNeedleLines);
01863           FAST_DEBUG("searchText | filling slot " << hayLinesZeroIndex << " with line "
01864             << j + numNeedleLines << ": " << hayLinesWindow[hayLinesZeroIndex]->string());
01865           hayLinesZeroIndex = (hayLinesZeroIndex + 1) % numNeedleLines;
01866         }
01867       }
01868     }
01869 
01870     // not found
01871     return KTextEditor::Range::invalid();
01872   }
01873   else
01874   {
01875     // single-line plaintext search (both forward of backward mode)
01876     const int minLeft  = inputRange.start().column();
01877     const uint maxRight = inputRange.end().column(); // first not included
01878     const int forMin   = inputRange.start().line();
01879     const int forMax   = inputRange.end().line();
01880     const int forInit  = backwards ? forMax : forMin;
01881     const int forInc   = backwards ? -1 : +1;
01882     FAST_DEBUG("searchText | single line " << (backwards ? forMax : forMin) << ".."
01883       << (backwards ? forMin : forMax));
01884     for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01885     {
01886       KateTextLine::Ptr textLine = m_buffer->plainLine(j);
01887       if (!textLine)
01888       {
01889         FAST_DEBUG("searchText | line " << j << ": no");
01890         return KTextEditor::Range::invalid();
01891       }
01892 
01893       const int offset = (j == forMin) ? minLeft : 0;
01894       const int line_end= (j==forMax) ? maxRight : textLine->length();
01895       uint foundAt, myMatchLen;
01896       FAST_DEBUG("searchText | searching in line line: " << j);
01897       const bool found = textLine->searchText (offset,line_end, text, &foundAt, &myMatchLen, casesensitive, backwards);
01898       if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
01899       {
01900         FAST_DEBUG("searchText | line " << j << ": yes");
01901         return KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
01902       }
01903       else
01904       {
01905         FAST_DEBUG("searchText | line " << j << ": no");
01906       }
01907     }
01908   }
01909   return KTextEditor::Range::invalid();
01910 }
01911 
01912 
01913 
01914 // helper structs for captures re-construction
01915 struct TwoViewCursor {
01916   int index;
01917   int openLine;
01918   int openCol;
01919   int closeLine;
01920   int closeCol;
01921   // note: open/close distinction does not seem needed
01922   // anymore. i keep it to make a potential way back
01923   // easier. overhead is minimal.
01924 };
01925 
01926 struct IndexPair {
01927   int openIndex;
01928   int closeIndex;
01929 };
01930 
01931 
01932 
01933 QVector<KTextEditor::Range> KateDocument::searchRegex(
01934     const KTextEditor::Range & inputRange,
01935     QRegExp &regexp,
01936     bool backwards)
01937 {
01938   FAST_DEBUG("KateDocument::searchRegex( " << inputRange.start().line() << ", "
01939     << inputRange.start().column() << ", " << regexp.pattern() << ", " << backwards << " )");
01940   if (regexp.isEmpty() || !regexp.isValid() || !inputRange.isValid() || (inputRange.start() == inputRange.end()))
01941   {
01942     QVector<KTextEditor::Range> result;
01943     result.append(KTextEditor::Range::invalid());
01944     return result;
01945   }
01946 
01947 
01948   // detect pattern type (single- or mutli-line)
01949   bool isMultiLine;
01950   QString multiLinePattern = regexp.pattern();
01951 
01952   // detect '.' and '\s' and fix them
01953   const bool dotMatchesNewline = false; // TODO
01954   const int replacements = KateDocument::repairPattern(multiLinePattern, isMultiLine);
01955   if (dotMatchesNewline && (replacements > 0))
01956   {
01957     isMultiLine = true;
01958   }
01959 
01960   const int firstLineIndex = inputRange.start().line();
01961   const int minColStart = inputRange.start().column();
01962 //  const int maxColEnd = inputRange.end().column();
01963   if (isMultiLine)
01964   {
01965     // multi-line regex search (both forward and backward mode)
01966     QString wholeDocument;
01967     const int inputLineCount = inputRange.end().line() - inputRange.start().line() + 1;
01968     FAST_DEBUG("multi line search (lines " << firstLineIndex << ".." << firstLineIndex + inputLineCount - 1 << ")");
01969 
01970     // nothing to do...
01971     if (firstLineIndex >= m_buffer->count())
01972     {
01973       QVector<KTextEditor::Range> result;
01974       result.append(KTextEditor::Range::invalid());
01975       return result;
01976     }
01977 
01978     QVector<int> lineLens (inputLineCount);
01979 
01980     // first line
01981     KateTextLine::Ptr firstLine = m_buffer->plainLine(firstLineIndex);
01982     if (!firstLine)
01983     {
01984       QVector<KTextEditor::Range> result;
01985       result.append(KTextEditor::Range::invalid());
01986       return result;
01987     }
01988 
01989     QString firstLineText = firstLine->string();
01990     const int firstLineLen = firstLineText.length() - minColStart;
01991     wholeDocument.append(firstLineText.right(firstLineLen));
01992     lineLens[0] = firstLineLen;
01993     FAST_DEBUG("  line" << 0 << "has length" << lineLens[0]);
01994 
01995     // second line and after
01996     const QString sep("\n");
01997     for (int i = 1; i < inputLineCount; i++)
01998     {
01999       KateTextLine::Ptr textLine = m_buffer->plainLine(firstLineIndex + i);
02000       if (!textLine)
02001       {
02002         QVector<KTextEditor::Range> result;
02003         result.append(KTextEditor::Range::invalid());
02004         return result;
02005       }
02006 
02007       QString text = textLine->string();
02008       lineLens[i] = text.length();
02009       wholeDocument.append(sep);
02010       wholeDocument.append(text);
02011       FAST_DEBUG("  line" << i << "has length" << lineLens[i]);
02012     }
02013 
02014     // apply modified pattern
02015     regexp.setPattern(multiLinePattern);
02016     const int pos = backwards
02017         ? KateDocument::fixedLastIndexIn(regexp, wholeDocument, -1, QRegExp::CaretAtZero)
02018         : regexp.indexIn(wholeDocument, 0, QRegExp::CaretAtZero);
02019     if (pos == -1)
02020     {
02021       // no match
02022       FAST_DEBUG("not found");
02023       {
02024         QVector<KTextEditor::Range> result;
02025         result.append(KTextEditor::Range::invalid());
02026         return result;
02027       }
02028     }
02029 
02030 #ifdef FAST_DEBUG_ENABLE
02031     const int matchLen = regexp.matchedLength();
02032     FAST_DEBUG("found at relative pos " << pos << ", length " << matchLen);
02033 #endif
02034 
02035     // save opening and closing indices and build a map.
02036     // the correct values will be written into it later.
02037     QMap<int, TwoViewCursor *> indicesToCursors;
02038     const int numCaptures = regexp.numCaptures();
02039     QVector<IndexPair> indexPairs(1 + numCaptures);
02040     for (int z = 0; z <= numCaptures; z++)
02041     {
02042       const int openIndex = regexp.pos(z);
02043       IndexPair & pair = indexPairs[z];
02044       if (openIndex == -1)
02045       {
02046         // empty capture gives invalid
02047         pair.openIndex = -1;
02048         pair.closeIndex = -1;
02049         FAST_DEBUG("capture []");
02050       }
02051       else
02052       {
02053         const int closeIndex = openIndex + regexp.cap(z).length();
02054         pair.openIndex = openIndex;
02055         pair.closeIndex = closeIndex;
02056         FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]");
02057 
02058         // each key no more than once
02059         if (!indicesToCursors.contains(openIndex))
02060         {
02061           TwoViewCursor * twoViewCursor = new TwoViewCursor;
02062           twoViewCursor->index = openIndex;
02063           indicesToCursors.insert(openIndex, twoViewCursor);
02064           FAST_DEBUG("  border index added: " << openIndex);
02065         }
02066         if (!indicesToCursors.contains(closeIndex))
02067         {
02068           TwoViewCursor * twoViewCursor = new TwoViewCursor;
02069           twoViewCursor->index = closeIndex;
02070           indicesToCursors.insert(closeIndex, twoViewCursor);
02071           FAST_DEBUG("  border index added: " << closeIndex);
02072         }
02073       }
02074     }
02075 
02076     // find out where they belong
02077     int curRelLine = 0;
02078     int curRelCol = 0;
02079     int curRelIndex = 0;
02080     QMap<int, TwoViewCursor *>::const_iterator iter = indicesToCursors.constBegin();
02081     while (iter != indicesToCursors.constEnd())
02082     {
02083       // forward to index, save line/col
02084       const int index = (*iter)->index;
02085       FAST_DEBUG("resolving position" << index);
02086       TwoViewCursor & twoViewCursor = *(*iter);
02087       while (curRelIndex <= index)
02088       {
02089         FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = "
02090             << curRelIndex << "relative, steps more to go" << index - curRelIndex);
02091         const int curRelLineLen = lineLens[curRelLine];
02092         const int curLineRemainder = curRelLineLen - curRelCol;
02093         const int lineFeedIndex = curRelIndex + curLineRemainder;
02094         if (index <= lineFeedIndex) {
02095             if (index == lineFeedIndex) {
02096                 // on this line _on_ line feed
02097                 FAST_DEBUG("  on line feed");
02098                 const int absLine = curRelLine + firstLineIndex;
02099                 twoViewCursor.openLine
02100                     = twoViewCursor.closeLine
02101                     = absLine;
02102                 twoViewCursor.openCol
02103                     = twoViewCursor.closeCol
02104                     = ((curRelLine == 0) ? minColStart : 0) + curRelLineLen;
02105 
02106                 // advance to next line
02107                 const int advance = (index - curRelIndex) + 1;
02108                 curRelLine++;
02109                 curRelCol = 0;
02110                 curRelIndex += advance;
02111             } else { // index < lineFeedIndex
02112                 // on this line _before_ line feed
02113                 FAST_DEBUG("  before line feed");
02114                 const int diff = (index - curRelIndex);
02115                 const int absLine = curRelLine + firstLineIndex;
02116                 const int absCol = ((curRelLine == 0) ? minColStart : 0) + curRelCol + diff;
02117                 twoViewCursor.openLine
02118                     = twoViewCursor.closeLine
02119                     = absLine;
02120                 twoViewCursor.openCol
02121                     = twoViewCursor.closeCol
02122                     = absCol;
02123 
02124                 // advance on same line
02125                 const int advance = diff + 1;
02126                 curRelCol += advance;
02127                 curRelIndex += advance;
02128             }
02129             FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol
02130                 << ")  close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")");
02131         }
02132         else // if (index > lineFeedIndex)
02133         {
02134           // not on this line
02135           // advance to next line
02136           FAST_DEBUG("  not on this line");
02137           const int advance = curLineRemainder + 1;
02138           curRelLine++;
02139           curRelCol = 0;
02140           curRelIndex += advance;
02141         }
02142       }
02143 
02144       ++iter;
02145     }
02146 
02147     // build result array
02148     QVector<KTextEditor::Range> result(1 + numCaptures);
02149     for (int y = 0; y <= numCaptures; y++)
02150     {
02151       IndexPair & pair = indexPairs[y];
02152       if ((pair.openIndex == -1) || (pair.closeIndex == -1))
02153       {
02154         result[y] = KTextEditor::Range::invalid();
02155       }
02156       else
02157       {
02158         const TwoViewCursor * const openCursors = indicesToCursors[pair.openIndex];
02159         const TwoViewCursor * const closeCursors = indicesToCursors[pair.closeIndex];
02160         const int startLine = openCursors->openLine;
02161         const int startCol = openCursors->openCol;
02162         const int endLine = closeCursors->closeLine;
02163         const int endCol = closeCursors->closeCol;
02164         FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")");
02165         result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol);
02166       }
02167     }
02168 
02169     // free structs allocated for indicesToCursors
02170     iter = indicesToCursors.constBegin();
02171     while (iter != indicesToCursors.constEnd())
02172     {
02173       TwoViewCursor * const twoViewCursor = *iter;
02174       delete twoViewCursor;
02175       ++iter;
02176     }
02177     return result;
02178   }
02179   else
02180   {
02181     // single-line regex search (both forward of backward mode)
02182     const int minLeft  = inputRange.start().column();
02183     const uint maxRight = inputRange.end().column(); // first not included
02184     const int forMin   = inputRange.start().line();
02185     const int forMax   = inputRange.end().line();
02186     const int forInit  = backwards ? forMax : forMin;
02187     const int forInc   = backwards ? -1 : +1;
02188     FAST_DEBUG("single line " << (backwards ? forMax : forMin) << ".."
02189       << (backwards ? forMin : forMax));
02190     for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
02191     {
02192       KateTextLine::Ptr textLine = m_buffer->plainLine(j);
02193       if (!textLine)
02194       {
02195         FAST_DEBUG("searchText | line " << j << ": no");
02196         QVector<KTextEditor::Range> result;
02197         result.append(KTextEditor::Range::invalid());
02198         return result;
02199       }
02200 
02201         // Find (and don't match ^ in between...)
02202         const int first = (j == forMin) ? minLeft : 0;
02203         const int afterLast = (j == forMax) ? maxRight : textLine->length();
02204         const QString hay = textLine->string();
02205         bool found = true;
02206         int foundAt;
02207         uint myMatchLen;
02208         if (backwards) {
02209             const int lineLen = textLine->length();
02210             const int offset = afterLast - lineLen - 1;
02211             FAST_DEBUG("lastIndexIn(" << hay << "," << offset << ")");
02212             foundAt = KateDocument::fixedLastIndexIn(regexp, hay, offset);
02213             found = (foundAt != -1) && (foundAt >= first);
02214         } else {
02215             FAST_DEBUG("indexIn(" << hay << "," << first << ")");
02216             foundAt = regexp.indexIn(hay, first);
02217             found = (foundAt != -1);
02218         }
02219         myMatchLen = found ? regexp.matchedLength() : 0;
02220 
02221       /*
02222       TODO do we still need this?
02223 
02224           // A special case which can only occur when searching with a regular expression consisting
02225           // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{').
02226           if (myMatchLen == 0 && line == startPosition.line() && foundAt == (uint) col)
02227           {
02228             if (col < lineLength(line))
02229               col++;
02230             else {
02231               line++;
02232               col = 0;
02233             }
02234             continue;
02235           }
02236       */
02237 
02238       if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
02239       {
02240         FAST_DEBUG("line " << j << ": yes");
02241 
02242         // build result array
02243         const int numCaptures = regexp.numCaptures();
02244         QVector<KTextEditor::Range> result(1 + numCaptures);
02245         result[0] = KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
02246         FAST_DEBUG("result range " << 0 << ": (" << j << ", " << foundAt << ")..(" << j << ", " << foundAt + myMatchLen << ")");
02247         for (int y = 1; y <= numCaptures; y++)
02248         {
02249           const int openIndex = regexp.pos(y);
02250           if (openIndex == -1)
02251           {
02252             result[y] = KTextEditor::Range::invalid();
02253             FAST_DEBUG("capture []");
02254           }
02255           else
02256           {
02257             const int closeIndex = openIndex + regexp.cap(y).length();
02258             FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")");
02259             result[y] = KTextEditor::Range(j, openIndex, j, closeIndex);
02260           }
02261         }
02262         return result;
02263       }
02264       else
02265       {
02266         FAST_DEBUG("searchText | line " << j << ": no");
02267       }
02268     }
02269   }
02270 
02271   QVector<KTextEditor::Range> result;
02272   result.append(KTextEditor::Range::invalid());
02273   return result;
02274 }
02275 
02276 QWidget * KateDocument::dialogParent()
02277 {
02278     QWidget *w=widget();
02279 
02280     if(!w)
02281     {
02282         w=activeView();
02283 
02284         if(!w)
02285             w=QApplication::activeWindow();
02286     }
02287 
02288     return w;
02289 }
02290 
02291 QVector<KTextEditor::Range> KateDocument::searchText(
02292     const KTextEditor::Range & range,
02293     const QString & pattern,
02294     const KTextEditor::Search::SearchOptions options)
02295 {
02296   // TODO
02297   // * support BlockInputRange
02298   // * support DotMatchesNewline
02299   QString workPattern(pattern);
02300 
02301   KTextEditor::Search::SearchOptions finalOptions(options);
02302   const bool escapeSequences = finalOptions.testFlag(KTextEditor::Search::EscapeSequences);
02303 
02304   // abuse regex for whole word plaintext search
02305   if (finalOptions.testFlag(KTextEditor::Search::WholeWords))
02306   {
02307     // resolve escape sequences like \t
02308     if (escapeSequences)
02309     {
02310       KateDocument::escapePlaintext(workPattern);
02311     }
02312 
02313     // escape dot and friends
02314     workPattern = "\\b" + QRegExp::escape(workPattern) + "\\b";
02315 
02316     // regex ON, whole words OFF
02317     finalOptions |= KTextEditor::Search::Regex;
02318     finalOptions &= ~KTextEditor::Search::SearchOptions(KTextEditor::Search::WholeWords);
02319   }
02320 
02321   const bool regexMode = finalOptions.testFlag(KTextEditor::Search::Regex);
02322   const bool caseSensitive = !finalOptions.testFlag(KTextEditor::Search::CaseInsensitive);
02323   const bool backwards = finalOptions.testFlag(KTextEditor::Search::Backwards);
02324 
02325   if (regexMode)
02326   {
02327     // regex search
02328     const Qt::CaseSensitivity caseSensitivity =
02329         caseSensitive
02330         ? Qt::CaseSensitive
02331         : Qt::CaseInsensitive;
02332 
02333     QRegExp matcher(workPattern, caseSensitivity);
02334     if (matcher.isValid())
02335     {
02336       // valid pattern
02337       // run engine
02338       return searchRegex(range, matcher, backwards);
02339     }
02340     else
02341     {
02342       // invalid pattern
02343       QVector<KTextEditor::Range> result;
02344       result.append(KTextEditor::Range::invalid());
02345       return result;
02346     }
02347   }
02348   else
02349   {
02350     // plaintext search
02351 
02352     // resolve escape sequences like \t
02353     if (escapeSequences)
02354     {
02355       KateDocument::escapePlaintext(workPattern);
02356     }
02357 
02358     // run engine
02359     KTextEditor::Range resultRange = searchText(range, workPattern, caseSensitive, backwards);
02360     QVector<KTextEditor::Range> result;
02361     result.append(resultRange);
02362     return result;
02363   }
02364 }
02365 
02366 
02367 
02368 KTextEditor::Search::SearchOptions KateDocument::supportedSearchOptions() const
02369 {
02370   KTextEditor::Search::SearchOptions supported(KTextEditor::Search::Default);
02371   supported |= KTextEditor::Search::Regex;
02372   supported |= KTextEditor::Search::CaseInsensitive;
02373   supported |= KTextEditor::Search::Backwards;
02374 // supported |= KTextEditor::Search::BlockInputRange;
02375   supported |= KTextEditor::Search::EscapeSequences;
02376   supported |= KTextEditor::Search::WholeWords;
02377 // supported |= KTextEditor::Search::DotMatchesNewline;
02378   return supported;
02379 }
02380 
02381 
02382 
02383 /*static*/ void KateDocument::escapePlaintext(QString & text, QList<ReplacementPart> * parts,
02384         bool replacementGoodies) {
02385   // get input
02386   const int inputLen = text.length();
02387   int input = 0; // walker index
02388 
02389   // prepare output
02390   QString output;
02391   output.reserve(inputLen + 1);
02392 
02393   while (input < inputLen)
02394   {
02395     switch (text[input].unicode())
02396     {
02397     case L'\n':
02398       output.append(text[input]);
02399       input++;
02400       break;
02401 
02402     case L'\\':
02403       if (input + 1 >= inputLen)
02404       {
02405         // copy backslash
02406         output.append(text[input]);
02407         input++;
02408         break;
02409       }
02410 
02411       switch (text[input + 1].unicode())
02412       {
02413       case L'0': // "\0000".."\0377"
02414         if (input + 4 >= inputLen)
02415         {
02416           if (parts == NULL)
02417           {
02418             // strip backslash ("\0" -> "0")
02419             output.append(text[input + 1]);
02420           }
02421           else
02422           {
02423             // handle reference
02424             ReplacementPart curPart;
02425 
02426             // append text before the reference
02427             if (!output.isEmpty())
02428             {
02429               curPart.type = ReplacementPart::Text;
02430               curPart.text = output;
02431               output.clear();
02432               parts->append(curPart);
02433               curPart.text.clear();
02434             }
02435 
02436             // append reference
02437             curPart.type = ReplacementPart::Reference;
02438             curPart.index = 0;
02439             parts->append(curPart);
02440           }
02441           input += 2;
02442         }
02443         else
02444         {
02445           bool stripAndSkip = false;
02446           const ushort text_2 = text[input + 2].unicode();
02447           if ((text_2 >= L'0') && (text_2 <= L'3'))
02448           {
02449             const ushort text_3 = text[input + 3].unicode();
02450             if ((text_3 >= L'0') && (text_3 <= L'7'))
02451             {
02452               const ushort text_4 = text[input + 4].unicode();
02453               if ((text_4 >= L'0') && (text_4 <= L'7'))
02454               {
02455                 int digits[3];
02456                 for (int i = 0; i < 3; i++)
02457                 {
02458                   digits[i] = 7 - (L'7' - text[input + 2 + i].unicode());
02459                 }
02460                 const int ch = 64 * digits[0] + 8 * digits[1] + digits[2];
02461                 output.append(QChar(ch));
02462                 input += 5;
02463               }
02464               else
02465               {
02466                 stripAndSkip = true;
02467               }
02468             }
02469             else
02470             {
02471               stripAndSkip = true;
02472             }
02473           }
02474           else
02475           {
02476             stripAndSkip = true;
02477           }
02478 
02479           if (stripAndSkip)
02480           {
02481             if (parts == NULL)
02482             {
02483               // strip backslash ("\0" -> "0")
02484               output.append(text[input + 1]);
02485             }
02486             else
02487             {
02488               // handle reference
02489               ReplacementPart curPart;
02490 
02491               // append text before the reference
02492               if (!output.isEmpty())
02493               {
02494                 curPart.type = ReplacementPart::Text;
02495                 curPart.text = output;
02496                 output.clear();
02497                 parts->append(curPart);
02498                 curPart.text.clear();
02499               }
02500 
02501               // append reference
02502               curPart.type = ReplacementPart::Reference;
02503               curPart.index = 0;
02504               parts->append(curPart);
02505             }
02506             input += 2;
02507           }
02508         }
02509         break;
02510 
02511       case L'1':
02512       case L'2':
02513       case L'3':
02514       case L'4':
02515       case L'5':
02516       case L'6':
02517       case L'7':
02518       case L'8':
02519       case L'9':
02520         if (parts == NULL)
02521         {
02522           // strip backslash ("\?" -> "?")
02523           output.append(text[input + 1]);
02524         }
02525         else
02526         {
02527           // handle reference
02528           ReplacementPart curPart;
02529 
02530           // append text before the reference
02531           if (!output.isEmpty())
02532           {
02533             curPart.type = ReplacementPart::Text;
02534             curPart.text = output;
02535             output.clear();
02536             parts->append(curPart);
02537             curPart.text.clear();
02538           }
02539 
02540           // append reference
02541           curPart.type = ReplacementPart::Reference;
02542           curPart.index = 9 - (L'9' - text[input + 1].unicode());
02543           parts->append(curPart);
02544         }
02545         input += 2;
02546         break;
02547 
02548       case L'E': // FALLTHROUGH
02549       case L'L': // FALLTHROUGH
02550       case L'U':
02551         if ((parts == NULL) || !replacementGoodies) {
02552           // strip backslash ("\?" -> "?")
02553           output.append(text[input + 1]);
02554         } else {
02555           // handle case switcher
02556           ReplacementPart curPart;
02557 
02558           // append text before case switcher
02559           if (!output.isEmpty())
02560           {
02561             curPart.type = ReplacementPart::Text;
02562             curPart.text = output;
02563             output.clear();
02564             parts->append(curPart);
02565             curPart.text.clear();
02566           }
02567 
02568           // append case switcher
02569           switch (text[input + 1].unicode()) {
02570           case L'L':
02571             curPart.type = ReplacementPart::LowerCase;
02572             break;
02573 
02574           case L'U':
02575             curPart.type = ReplacementPart::UpperCase;
02576             break;
02577 
02578           case L'E': // FALLTHROUGH
02579           default:
02580             curPart.type = ReplacementPart::KeepCase;
02581 
02582           }
02583           parts->append(curPart);
02584         }
02585         input += 2;
02586         break;
02587 
02588       case L'#':
02589         if ((parts == NULL) || !replacementGoodies) {
02590           // strip backslash ("\?" -> "?")
02591           output.append(text[input + 1]);
02592           input += 2;
02593         } else {
02594           // handle replacement counter
02595           ReplacementPart curPart;
02596 
02597           // append text before replacement counter
02598           if (!output.isEmpty())
02599           {
02600             curPart.type = ReplacementPart::Text;
02601             curPart.text = output;
02602             output.clear();
02603             parts->append(curPart);
02604             curPart.text.clear();
02605           }
02606 
02607           // eat and count all following hash marks
02608           // each hash stands for a leading zero: \### will produces 001, 002, ...
02609           int count = 1;
02610           while ((input + count + 1 < inputLen) && (text[input + count + 1].unicode() == L'#')) {
02611             count++;
02612           }
02613           curPart.type = ReplacementPart::Counter;
02614           curPart.index = count; // Each hash stands
02615           parts->append(curPart);
02616           input += 1 + count;
02617         }
02618         break;
02619 
02620       case L'a':
02621         output.append(QChar(0x07));
02622         input += 2;
02623         break;
02624 
02625       case L'f':
02626         output.append(QChar(0x0c));
02627         input += 2;
02628         break;
02629 
02630       case L'n':
02631         output.append(QChar(0x0a));
02632         input += 2;
02633         break;
02634 
02635       case L'r':
02636         output.append(QChar(0x0d));
02637         input += 2;
02638         break;
02639 
02640       case L't':
02641         output.append(QChar(0x09));
02642         input += 2;
02643         break;
02644 
02645       case L'v':
02646         output.append(QChar(0x0b));
02647         input += 2;
02648         break;
02649 
02650       case L'x': // "\x0000".."\xffff"
02651         if (input + 5 >= inputLen)
02652         {
02653           // strip backslash ("\x" -> "x")
02654           output.append(text[input + 1]);
02655           input += 2;
02656         }
02657         else
02658         {
02659           bool stripAndSkip = false;
02660           const ushort text_2 = text[input + 2].unicode();
02661           if (((text_2 >= L'0') && (text_2 <= L'9'))
02662               || ((text_2 >= L'a') && (text_2 <= L'f'))
02663               || ((text_2 >= L'A') && (text_2 <= L'F')))
02664           {
02665             const ushort text_3 = text[input + 3].unicode();
02666             if (((text_3 >= L'0') && (text_3 <= L'9'))
02667                 || ((text_3 >= L'a') && (text_3 <= L'f'))
02668                 || ((text_3 >= L'A') && (text_3 <= L'F')))
02669             {
02670               const ushort text_4 = text[input + 4].unicode();
02671               if (((text_4 >= L'0') && (text_4 <= L'9'))
02672                   || ((text_4 >= L'a') && (text_4 <= L'f'))
02673                   || ((text_4 >= L'A') && (text_4 <= L'F')))
02674               {
02675                 const ushort text_5 = text[input + 5].unicode();
02676                 if (((text_5 >= L'0') && (text_5 <= L'9'))
02677                     || ((text_5 >= L'a') && (text_5 <= L'f'))
02678                     || ((text_5 >= L'A') && (text_5 <= L'F')))
02679                 {
02680                   int digits[4];
02681                   for (int i = 0; i < 4; i++)
02682                   {
02683                     const ushort cur = text[input + 2 + i].unicode();
02684                     if ((cur >= L'0') && (cur <= L'9'))
02685                     {
02686                       digits[i] = 9 - (L'9' - cur);
02687                     }
02688                     else if ((cur >= L'a') && (cur <= L'f'))
02689                     {
02690                       digits[i] = 15 - (L'f' - cur);
02691                     }
02692                     else // if ((cur >= L'A') && (cur <= L'F')))
02693                     {
02694                       digits[i] = 15 - (L'F' - cur);
02695                     }
02696                   }
02697 
02698                   const int ch = 4096 * digits[0] + 256 * digits[1] + 16 * digits[2] + digits[3];
02699                   output.append(QChar(ch));
02700                   input += 6;
02701                 }
02702                 else
02703                 {
02704                   stripAndSkip = true;
02705                 }
02706               }
02707               else
02708               {
02709                 stripAndSkip = true;
02710               }
02711             }
02712             else
02713             {
02714               stripAndSkip = true;
02715             }
02716           }
02717 
02718           if (stripAndSkip)
02719           {
02720             // strip backslash ("\x" -> "x")
02721             output.append(text[input + 1]);
02722             input += 2;
02723           }
02724         }
02725         break;
02726 
02727       default:
02728         // strip backslash ("\?" -> "?")
02729         output.append(text[input + 1]);
02730         input += 2;
02731 
02732       }
02733       break;
02734 
02735     default:
02736       output.append(text[input]);
02737       input++;
02738 
02739     }
02740   }
02741 
02742   if (parts == NULL)
02743   {
02744     // overwrite with escaped edition
02745     text = output;
02746   }
02747   else
02748   {
02749     // append text after the last reference if any
02750     if (!output.isEmpty())
02751     {
02752       ReplacementPart curPart;
02753       curPart.type = ReplacementPart::Text;
02754       curPart.text = output;
02755       parts->append(curPart);
02756     }
02757   }
02758 }
02759 
02760 
02761 
02762 // these things can besides '.' and '\s' make apptern multi-line:
02763 // \n, \x000A, \x????-\x????, \0012, \0???-\0???
02764 // a multi-line pattern must not pass as single-line, the other
02765 // way around will just result in slower searches and is therefore
02766 // not as critical
02767 /*static*/ int KateDocument::repairPattern(QString & pattern, bool & stillMultiLine)
02768 {
02769   const QString & text = pattern; // read-only input for parsing
02770 
02771   // get input
02772   const int inputLen = text.length();
02773   int input = 0; // walker index
02774 
02775   // prepare output
02776   QString output;
02777   output.reserve(2 * inputLen + 1); // twice should be enough for the average case
02778 
02779   // parser state
02780   stillMultiLine = false;
02781   int replaceCount = 0;
02782   bool insideClass = false;
02783 
02784   while (input < inputLen)
02785   {
02786     if (insideClass)
02787     {
02788       // wait for closing, unescaped ']'
02789       switch (text[input].unicode())
02790       {
02791       case L'\\':
02792         switch (text[input + 1].unicode())
02793         {
02794         case L'x':
02795           if (input + 5 < inputLen)
02796           {
02797             // copy "\x????" unmodified
02798             output.append(text.mid(input, 6));
02799             input += 6;
02800           } else {
02801             // copy "\x" unmodified
02802             output.append(text.mid(input, 2));
02803             input += 2;
02804           }
02805           stillMultiLine = true;
02806           break;
02807 
02808         case L'0':
02809           if (input + 4 < inputLen)
02810           {
02811             // copy "\0???" unmodified
02812             output.append(text.mid(input, 5));
02813             input += 5;
02814           } else {
02815             // copy "\0" unmodified
02816             output.append(text.mid(input, 2));
02817             input += 2;
02818           }
02819           stillMultiLine = true;
02820           break;
02821 
02822         case L's':
02823           // replace "\s" with "[ \t]"
02824           output.append("[ \\t]");
02825           input += 2;
02826           replaceCount++;
02827           break;
02828 
02829         case L'n':
02830           stillMultiLine = true;
02831           // FALLTROUGH
02832 
02833         default:
02834           // copy "\?" unmodified
02835           output.append(text.mid(input, 2));
02836           input += 2;
02837         }
02838         break;
02839 
02840       case L']':
02841         // copy "]" unmodified
02842         insideClass = false;
02843         output.append(text[input]);
02844         input++;
02845         break;
02846 
02847       default:
02848         // copy "?" unmodified
02849         output.append(text[input]);
02850         input++;
02851 
02852       }
02853     }
02854     else
02855     {
02856       // search for real dots and \S
02857       switch (text[input].unicode())
02858       {
02859       case L'\\':
02860         switch (text[input + 1].unicode())
02861         {
02862         case L'x':
02863           if (input + 5 < inputLen)
02864           {
02865             // copy "\x????" unmodified
02866             output.append(text.mid(input, 6));
02867             input += 6;
02868           } else {
02869             // copy "\x" unmodified
02870             output.append(text.mid(input, 2));
02871             input += 2;
02872           }
02873           stillMultiLine = true;
02874           break;
02875 
02876         case L'0':
02877           if (input + 4 < inputLen)
02878           {
02879             // copy "\0???" unmodified
02880             output.append(text.mid(input, 5));
02881             input += 5;
02882           } else {
02883             // copy "\0" unmodified
02884             output.append(text.mid(input, 2));
02885             input += 2;
02886           }
02887           stillMultiLine = true;
02888           break;
02889 
02890         case L's':
02891           // replace "\s" with "[ \t]"
02892           output.append("[ \\t]");
02893           input += 2;
02894           replaceCount++;
02895           break;
02896 
02897         case L'n':
02898           stillMultiLine = true;
02899           // FALLTROUGH
02900 
02901         default:
02902           // copy "\?" unmodified
02903           output.append(text.mid(input, 2));
02904           input += 2;
02905         }
02906         break;
02907 
02908       case L'.':
02909         // replace " with "[^\n]"
02910         output.append("[^\\n]");
02911         input++;
02912         replaceCount++;
02913         break;
02914 
02915       case L'[':
02916         // copy "]" unmodified
02917         insideClass = true;
02918         output.append(text[input]);
02919         input++;
02920         break;
02921 
02922       default:
02923         // copy "?" unmodified
02924         output.append(text[input]);
02925         input++;
02926 
02927       }
02928     }
02929   }
02930 
02931   // Overwrite with repaired pattern
02932   pattern = output;
02933   return replaceCount;
02934 }
02935 
02936 
02937 
02938 /*static*/ int KateDocument::fixedLastIndexIn(const QRegExp & matcher, const QString & str,
02939         int offset, QRegExp::CaretMode caretMode) {
02940     int prevPos = -1;
02941     int prevLen = 1;
02942     const int strLen = str.length();
02943     for (;;) {
02944         const int pos = matcher.indexIn(str, prevPos + prevLen, caretMode);
02945         if (pos == -1) {
02946             // No more matches
02947             break;
02948         } else {
02949             const int len = matcher.matchedLength();
02950             if (pos > strLen + offset + 1) {
02951                 // Gone too far, match in no way of use
02952                 break;
02953             }
02954 
02955             if (pos + len > strLen + offset + 1) {
02956                 // Gone too far, check if usable
02957                 if (offset == -1) {
02958                     // No shrinking possible
02959                     break;
02960                 }
02961 
02962                 const QString str2 = str.mid(0, strLen + offset + 1);
02963                 const int pos2 = matcher.indexIn(str2, pos, caretMode);
02964                 if (pos2 != -1) {
02965                     // Match usable
02966                     return pos2;
02967                 } else {
02968                     // Match NOT usable
02969                     break;
02970                 }
02971             }
02972 
02973             // Valid match, but maybe not the last one
02974             prevPos = pos;
02975             prevLen = (len == 0) ? 1 : len;
02976         }
02977     }
02978 
02979     // Previous match is what we want
02980     if (prevPos != -1) {
02981         // Do that very search again
02982         matcher.indexIn(str, prevPos, caretMode);
02983         return prevPos;
02984     } else {
02985         return -1;
02986     }
02987 }
02988 //END
02989 
02990 //BEGIN KTextEditor::HighlightingInterface stuff
02991 bool KateDocument::setMode (const QString &name)
02992 {
02993   updateFileType (name);
02994   return true;
02995 }
02996 
02997 QString KateDocument::mode () const
02998 {
02999   return m_fileType;
03000 }
03001 
03002 QStringList KateDocument::modes () const
03003 {
03004   QStringList m;
03005 
03006   const QList<KateFileType *> &modeList = KateGlobal::self()->modeManager()->list();
03007   for (int i = 0; i < modeList.size(); ++i)
03008     m << modeList[i]->name;
03009 
03010   return m;
03011 }
03012 
03013 bool KateDocument::setHighlightingMode (const QString &name)
03014 {
03015   m_buffer->setHighlight (KateHlManager::self()->nameFind(name));
03016 
03017   if (true)
03018   {
03019     setDontChangeHlOnSave();
03020     return true;
03021   }
03022 
03023   return false;
03024 }
03025 
03026 QString KateDocument::highlightingMode () const
03027 {
03028   return highlight()->name ();
03029 }
03030 
03031 QStringList KateDocument::highlightingModes () const
03032 {
03033   QStringList hls;
03034 
03035   for (int i = 0; i < KateHlManager::self()->highlights(); ++i)
03036     hls << KateHlManager::self()->hlName (i);
03037 
03038   return hls;
03039 }
03040 
03041 QString KateDocument::highlightingModeSection( int index ) const
03042 {
03043   return KateHlManager::self()->hlSection( index );
03044 }
03045 
03046 QString KateDocument::modeSection( int index ) const
03047 {
03048   return KateGlobal::self()->modeManager()->list()[ index ]->section;
03049 }
03050 
03051 void KateDocument::bufferHlChanged ()
03052 {
03053   // update all views
03054   makeAttribs(false);
03055 
03056   // deactivate indenter if necessary
03057   m_indenter.checkRequiredStyle();
03058 
03059   emit highlightingModeChanged(this);
03060 }
03061 
03062 void KateDocument::setDontChangeHlOnSave()
03063 {
03064   hlSetByUser = true;
03065 }
03066 //END
03067 
03068 //BEGIN KTextEditor::ConfigInterface stuff
03069 void KateDocument::readSessionConfig(const KConfigGroup &kconfig)
03070 {
03071   // restore the url
03072   KUrl url (kconfig.readEntry("URL"));
03073 
03074   // get the encoding
03075   QString tmpenc=kconfig.readEntry("Encoding");
03076   if (!tmpenc.isEmpty() && (tmpenc != encoding()))
03077     setEncoding(tmpenc);
03078 
03079   // open the file if url valid
03080   if (!url.isEmpty() && url.isValid())
03081     openUrl (url);
03082   else completed(); //perhaps this should be emitted at the end of this function
03083 
03084   // restore the filetype
03085   updateFileType (kconfig.readEntry("Mode", "Normal"));
03086 
03087   // restore the hl stuff
03088   m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig.readEntry("Highlighting")));
03089 
03090   // indent mode
03091   config()->setIndentationMode( kconfig.readEntry("Indentation Mode", config()->indentationMode() ) );
03092 
03093   // Restore Bookmarks
03094   QList<int> marks = kconfig.readEntry("Bookmarks", QList<int>());
03095   for( int i = 0; i < marks.count(); i++ )
03096     addMark( marks[i], KateDocument::markType01 );
03097 }
03098 
03099 void KateDocument::writeSessionConfig(KConfigGroup &kconfig)
03100 {
03101   if ( this->url().isLocalFile() ) {
03102     const QString path = this->url().path();
03103     if ( KGlobal::dirs()->relativeLocation( "tmp", path ) != path ) {
03104       return; // inside tmp resource, do not save
03105     }
03106   }
03107   // save url
03108   kconfig.writeEntry("URL", this->url().prettyUrl() );
03109 
03110   // save encoding
03111   kconfig.writeEntry("Encoding",encoding());
03112 
03113   // save file type
03114   kconfig.writeEntry("Mode", m_fileType);
03115 
03116   // save hl
03117   kconfig.writeEntry("Highlighting", highlight()->name());
03118 
03119   // indent mode
03120   kconfig.writeEntry("Indentation Mode", config()->indentationMode() );
03121 
03122   // Save Bookmarks
03123   QList<int> marks;
03124   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
03125     if (i.value()->type & KTextEditor::MarkInterface::markType01)
03126      marks << i.value()->line;
03127 
03128   kconfig.writeEntry( "Bookmarks", marks );
03129 }
03130 
03131 uint KateDocument::mark( int line )
03132 {
03133   if( !m_marks.value(line) )
03134     return 0;
03135 
03136   return m_marks[line]->type;
03137 }
03138 
03139 void KateDocument::setMark( int line, uint markType )
03140 {
03141   clearMark( line );
03142   addMark( line, markType );
03143 }
03144 
03145 void KateDocument::clearMark( int line )
03146 {
03147   if( line > lastLine() )
03148     return;
03149 
03150   if( !m_marks.value(line) )
03151     return;
03152 
03153   KTextEditor::Mark* mark = m_marks.take( line );
03154   emit markChanged( this, *mark, MarkRemoved );
03155   emit marksChanged( this );
03156   delete mark;
03157   tagLines( line, line );
03158   repaintViews(true);
03159 }
03160 
03161 void KateDocument::addMark( int line, uint markType )
03162 {
03163   if( line > lastLine())
03164     return;
03165 
03166   if( markType == 0 )
03167     return;
03168 
03169   if( m_marks.value(line) ) {
03170     KTextEditor::Mark* mark = m_marks[line];
03171 
03172     // Remove bits already set
03173     markType &= ~mark->type;
03174 
03175     if( markType == 0 )
03176       return;
03177 
03178     // Add bits
03179     mark->type |= markType;
03180   } else {
03181     KTextEditor::Mark *mark = new KTextEditor::Mark;
03182     mark->line = line;
03183     mark->type = markType;
03184     m_marks.insert( line, mark );
03185   }
03186 
03187   // Emit with a mark having only the types added.
03188   KTextEditor::Mark temp;
03189   temp.line = line;
03190   temp.type = markType;
03191   emit markChanged( this, temp, MarkAdded );
03192 
03193   emit marksChanged( this );
03194   tagLines( line, line );
03195   repaintViews(true);
03196 }
03197 
03198 void KateDocument::removeMark( int line, uint markType )
03199 {
03200   if( line > lastLine() )
03201     return;
03202 
03203   if( !m_marks.value(line) )
03204     return;
03205 
03206   KTextEditor::Mark* mark = m_marks[line];
03207 
03208   // Remove bits not set
03209   markType &= mark->type;
03210 
03211   if( markType == 0 )
03212     return;
03213 
03214   // Subtract bits
03215   mark->type &= ~markType;
03216 
03217   // Emit with a mark having only the types removed.
03218   KTextEditor::Mark temp;
03219   temp.line = line;
03220   temp.type = markType;
03221   emit markChanged( this, temp, MarkRemoved );
03222 
03223   if( mark->type == 0 )
03224     m_marks.remove( line );
03225 
03226   emit marksChanged( this );
03227   tagLines( line, line );
03228   repaintViews(true);
03229 }
03230 
03231 const QHash<int, KTextEditor::Mark*> &KateDocument::marks()
03232 {
03233   return m_marks;
03234 }
03235 
03236 void KateDocument::clearMarks()
03237 {
03238   while (!m_marks.isEmpty())
03239   {
03240     QHash<int, KTextEditor::Mark*>::iterator it = m_marks.begin();
03241     KTextEditor::Mark mark = *it.value();
03242     delete it.value();
03243     m_marks.erase (it);
03244 
03245     emit markChanged( this, mark, MarkRemoved );
03246     tagLines( mark.line, mark.line );
03247   }
03248 
03249   m_marks.clear();
03250 
03251   emit marksChanged( this );
03252   repaintViews(true);
03253 }
03254 
03255 void KateDocument::setMarkPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
03256 {
03257   m_markPixmaps.insert( type, pixmap );
03258 }
03259 
03260 void KateDocument::setMarkDescription( MarkInterface::MarkTypes type, const QString& description )
03261 {
03262   m_markDescriptions.insert( type, description );
03263 }
03264 
03265 QPixmap KateDocument::markPixmap( MarkInterface::MarkTypes type ) const
03266 {
03267   return m_markPixmaps.contains(type) ?
03268          m_markPixmaps[type] : QPixmap();
03269 }
03270 
03271 QColor KateDocument::markColor( MarkInterface::MarkTypes type ) const
03272 {
03273   uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
03274   if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
03275     return KateRendererConfig::global()->lineMarkerColor(type);
03276   } else {
03277     return QColor();
03278   }
03279 }
03280 
03281 QString KateDocument::markDescription( MarkInterface::MarkTypes type ) const
03282 {
03283   return m_markDescriptions.contains(type) ?
03284          m_markDescriptions[type] : QString();
03285 }
03286 
03287 void KateDocument::setEditableMarks( uint markMask )
03288 {
03289   m_editableMarks = markMask;
03290 }
03291 
03292 uint KateDocument::editableMarks() const
03293 {
03294   return m_editableMarks;
03295 }
03296 //END
03297 
03298 //BEGIN KTextEditor::PrintInterface stuff
03299 bool KateDocument::printDialog ()
03300 {
03301   return KatePrinter::print (this);
03302 }
03303 
03304 bool KateDocument::print ()
03305 {
03306   return KatePrinter::print (this);
03307 }
03308 //END
03309 
03310 //BEGIN KTextEditor::DocumentInfoInterface (### unfinished)
03311 QString KateDocument::mimeType()
03312 {
03313   KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
03314 
03315   // if the document has a URL, try KMimeType::findByURL
03316   if ( ! this->url().isEmpty() )
03317     result = KMimeType::findByUrl( this->url() );
03318 
03319   else if ( this->url().isEmpty() || ! this->url().isLocalFile() )
03320     result = mimeTypeForContent();
03321 
03322   return result->name();
03323 }
03324 
03325 KMimeType::Ptr KateDocument::mimeTypeForContent()
03326 {
03327   QByteArray buf (1024,'\0');
03328   uint bufpos = 0;
03329 
03330   for (int i=0; i < lines(); ++i)
03331   {
03332     QString line = this->line( i );
03333     uint len = line.length() + 1;
03334 
03335     if (bufpos + len > 1024)
03336       len = 1024 - bufpos;
03337 
03338     QString ld (line + QChar::fromAscii('\n'));
03339     buf.replace(bufpos,len,ld.toLatin1()); //memcpy(buf.data() + bufpos, ld.toLatin1().constData(), len);
03340 
03341     bufpos += len;
03342 
03343     if (bufpos >= 1024)
03344       break;
03345   }
03346   buf.resize( bufpos );
03347 
03348   int accuracy = 0;
03349   KMimeType::Ptr mt = KMimeType::findByContent(buf, &accuracy);
03350   return mt ? mt : KMimeType::defaultMimeTypePtr();
03351 }
03352 //END KTextEditor::DocumentInfoInterface
03353 
03354 
03355 //BEGIN KParts::ReadWrite stuff
03356 bool KateDocument::openFile()
03357 {
03358   // no open errors until now...
03359   setOpeningError(false);
03360 
03361   // add new m_file to dirwatch
03362   activateDirWatch ();
03363 
03364   //
03365   // mime type magic to get encoding right
03366   //
03367   QString mimeType = arguments().mimeType();
03368   int pos = mimeType.indexOf(';');
03369   if (pos != -1)
03370     setEncoding (mimeType.mid(pos+1));
03371 
03372   // do we have success ?
03373   emit KTextEditor::Document::textRemoved(this, documentRange());
03374   history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03375 
03376   bool success = m_buffer->openFile (localFilePath());
03377 
03378   emit KTextEditor::Document::textInserted(this, documentRange());
03379   history()->doEdit( new KateEditInfo(Kate::OpenFileEdit, KTextEditor::Range(0,0,0,0), QStringList(), documentRange(), QStringList()) );
03380 
03381   //
03382   // yeah, success
03383   //
03384   if (success)
03385   {
03386     // update file type
03387     updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03388 
03389     // read dir config (if possible and wanted)
03390     readDirConfig ();
03391 
03392     // read vars
03393     readVariables();
03394 
03395     // update the md5 digest
03396     createDigest( m_digest );
03397 
03398     if (!m_postLoadFilterChecks.isEmpty())
03399     {
03400       LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03401       foreach(const QString& checkplugin, m_postLoadFilterChecks)
03402       {
03403          lscps->postLoadFilter(checkplugin,this);
03404       }
03405     }
03406   }
03407 
03408   // Inform that the text has changed (required as we're not inside the usual editStart/End stuff)
03409   emit textChanged (this);
03410 
03411   //
03412   // update views
03413   //
03414   foreach (KateView * view, m_views)
03415   {
03416     // This is needed here because inserting the text moves the view's start position (it is a SmartCursor)
03417     view->setCursorPosition(KTextEditor::Cursor());
03418     view->updateView(true);
03419   }
03420 
03421   if (!m_reloading)
03422   {
03423     //
03424     // emit the signal we need for example for kate app
03425     //
03426     emit documentUrlChanged (this);
03427 
03428     //
03429     // set doc name, dummy value as arg, don't need it
03430     //
03431     setDocName  (QString());
03432   }
03433   //
03434   // to houston, we are not modified
03435   //
03436   if (m_modOnHd)
03437   {
03438     m_modOnHd = false;
03439     m_modOnHdReason = OnDiskUnmodified;
03440     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03441   }
03442 
03443   //
03444   // display errors
03445   //
03446   QWidget *parentWidget(dialogParent());
03447 
03448   if (!suppressOpeningErrorDialogs())
03449   {
03450     if (!success)
03451       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()));
03452   }
03453 
03454   if (!success) {
03455     setOpeningError(true);
03456     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()));
03457   }
03458 
03459   // warn -> opened binary file!!!!!!!
03460   if (m_buffer->binary())
03461   {
03462     // this file can't be saved again without killing it
03463     setReadWrite( false );
03464 
03465     if(!suppressOpeningErrorDialogs())
03466       KMessageBox::information (parentWidget
03467         , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl())
03468         , i18n ("Binary File Opened")
03469         , "Binary File Opened Warning");
03470 
03471     setOpeningError(true);
03472     setOpeningErrorMessage(i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl()));
03473   }
03474 
03475   // warn: opened broken utf-8 file...
03476   // only warn if not already a binary file!
03477   else if (m_buffer->brokenUTF8())
03478   {
03479     // this file can't be saved again without killing it
03480     setReadWrite( false );
03481 
03482     if (!suppressOpeningErrorDialogs())
03483       KMessageBox::information (parentWidget
03484         , i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03485                 " It is set to read-only mode, as saving might destroy its content."
03486                 " 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())
03487         , i18n ("Broken UTF-8 File Opened")
03488         , "Broken UTF-8 File Opened Warning");
03489     setOpeningError(true);
03490     setOpeningErrorMessage(i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03491               " It is set to read-only mode, as saving might destroy its content."
03492               " 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()));
03493   }
03494 
03495   //
03496   // return the success
03497   //
03498   return success;
03499 }
03500 
03501 bool KateDocument::saveFile()
03502 {
03503   QWidget *parentWidget(dialogParent());
03504 
03505   //
03506   // warn -> try to save binary file!!!!!!!
03507   //
03508   if (m_buffer->binary() && (KMessageBox::warningContinueCancel (parentWidget
03509         , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", url().pathOrUrl())
03510         , i18n ("Trying to Save Binary File")
03511         , KGuiItem(i18n("Save Nevertheless"))
03512         , KStandardGuiItem::cancel(), "Binary File Save Warning") != KMessageBox::Continue))
03513     return false;
03514 
03515   // some warnings, if file was changed by the outside!
03516   if ( !url().isEmpty() )
03517   {
03518     if (s_fileChangedDialogsActivated && m_modOnHd)
03519     {
03520       QString str = reasonedMOHString() + "\n\n";
03521 
03522       if (!isModified())
03523       {
03524         if (KMessageBox::warningContinueCancel(parentWidget,
03525                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)
03526           return false;
03527       }
03528       else
03529       {
03530         if (KMessageBox::warningContinueCancel(parentWidget,
03531                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)
03532           return false;
03533       }
03534     }
03535   }
03536 
03537   //
03538   // can we encode it if we want to save it ?
03539   //
03540   if (!m_buffer->canEncode ()
03541        && (KMessageBox::warningContinueCancel(parentWidget,
03542            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))
03543   {
03544     return false;
03545   }
03546 
03547   //
03548   // try to create backup file..
03549   //
03550 
03551   // local file or not is here the question
03552   bool l ( url().isLocalFile() );
03553 
03554   // does the user want any backup, if not, not our problem?
03555   if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
03556        || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
03557   {
03558     KUrl u( url() );
03559     u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
03560 
03561     kDebug( 13020 ) << "backup src file name: " << url();
03562     kDebug( 13020 ) << "backup dst file name: " << u;
03563 
03564     // handle the backup...
03565     bool backupSuccess = false;
03566 
03567     // local file mode, no kio
03568     if (u.isLocalFile ())
03569     {
03570       if (QFile::exists (url().toLocalFile ()))
03571       {
03572         // first: check if backupFile is already there, if true, unlink it
03573         QFile backupFile (u.toLocalFile ());
03574         if (backupFile.exists()) backupFile.remove ();
03575 
03576         backupSuccess = QFile::copy (url().toLocalFile (), u.toLocalFile ());
03577       }
03578       else
03579         backupSuccess = true;
03580     }
03581     else // remote file mode, kio
03582     {
03583       QWidget *w = widget ();
03584       if (!w && !m_views.isEmpty ())
03585         w = m_views.first();
03586 
03587       // get the right permissions, start with safe default
03588       mode_t  perms = 0600;
03589       KIO::UDSEntry fentry;
03590       if (KIO::NetAccess::stat (url(), fentry, kapp->activeWindow()))
03591       {
03592         kDebug( 13020 ) << "stating succesfull: " << url();
03593         KFileItem item (fentry, url());
03594         perms = item.permissions();
03595 
03596         // do a evil copy which will overwrite target if possible
03597         KIO::FileCopyJob *job = KIO::file_copy ( url(), u, -1, KIO::Overwrite );
03598         backupSuccess = KIO::NetAccess::synchronousRun(job, w);
03599       }
03600       else
03601         backupSuccess = true;
03602     }
03603 
03604     // backup has failed, ask user how to proceed
03605     if (!backupSuccess && (KMessageBox::warningContinueCancel (parentWidget
03606         , i18n ("For file %1 no backup copy could be created before saving."
03607                 " If an error occurs while saving, you might lose the data of this file."
03608                 " 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())
03609         , i18n ("Failed to create backup copy.")
03610         , KGuiItem(i18n("Try to Save Nevertheless"))
03611         , KStandardGuiItem::cancel(), "Backup Failed Warning") != KMessageBox::Continue))
03612     {
03613       return false;
03614     }
03615   }
03616 
03617   // update file type
03618   updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03619 
03620   if (!m_preSavePostDialogFilterChecks.isEmpty())
03621   {
03622     LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03623     foreach(const QString& checkplugin, m_preSavePostDialogFilterChecks)
03624     {
03625        if (lscps->preSavePostDialogFilterCheck(checkplugin,this,parentWidget)==false)
03626          return false;
03627     }
03628   }
03629 
03630   // remember the oldpath...
03631   QString oldPath = m_dirWatchFile;
03632 
03633   // remove file from dirwatch
03634   deactivateDirWatch ();
03635 
03636   //
03637   // try to save
03638   //
03639   if (!m_buffer->saveFile (localFilePath()))
03640   {
03641     // add m_file again to dirwatch
03642     activateDirWatch (oldPath);
03643 
03644     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()));
03645 
03646     return false;
03647   }
03648 
03649   // update the md5 digest
03650   createDigest( m_digest );
03651 
03652   // add m_file again to dirwatch
03653   activateDirWatch ();
03654 
03655   // update file type
03656 //  updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03657 
03658   // read dir config (if possible and wanted)
03659   if ( url().isLocalFile())
03660   {
03661     QFileInfo fo (oldPath), fn (m_dirWatchFile);
03662 
03663     if (fo.path() != fn.path())
03664       readDirConfig();
03665   }
03666 
03667   // read our vars
03668   readVariables();
03669 
03670   //
03671   // we are not modified
03672   //
03673   if (m_modOnHd)
03674   {
03675     m_modOnHd = false;
03676     m_modOnHdReason = OnDiskUnmodified;
03677     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03678   }
03679 
03680   // update document name...
03681   setDocName( QString() );
03682 
03683   // url may have changed...
03684   emit documentUrlChanged (this);
03685 
03686   m_savingToUrl=true;
03687 
03688   // (dominik) mark last undo group as not mergeable, otherwise the next
03689   // edit action might be merged and undo will never stop at the saved state
03690   setUndoDontMerge(true);
03691 
03692   //
03693   // return success
03694   //
03695   return true;
03696 }
03697 
03698 void KateDocument::readDirConfig ()
03699 {
03700   int depth = config()->searchDirConfigDepth ();
03701 
03702   if (this->url().isLocalFile() && (depth > -1))
03703   {
03704     QString currentDir = QFileInfo (localFilePath()).absolutePath();
03705 
03706     // only search as deep as specified or not at all ;)
03707     while (depth > -1)
03708     {
03709       //kDebug (13020) << "search for config file in path: " << currentDir;
03710 
03711       // try to open config file in this dir
03712       QFile f (currentDir + "/.kateconfig");
03713 
03714       if (f.open (QIODevice::ReadOnly))
03715       {
03716         QTextStream stream (&f);
03717 
03718         uint linesRead = 0;
03719         QString line = stream.readLine();
03720         while ((linesRead < 32) && !line.isNull())
03721         {
03722           readVariableLine( line );
03723 
03724           line = stream.readLine();
03725 
03726           linesRead++;
03727         }
03728 
03729         break;
03730       }
03731 
03732       QString newDir = QFileInfo (currentDir).absolutePath();
03733 
03734       // bail out on looping (for example reached /)
03735       if (currentDir == newDir)
03736         break;
03737 
03738       currentDir = newDir;
03739       --depth;
03740     }
03741   }
03742 }
03743 
03744 void KateDocument::activateDirWatch (const QString &useFileName)
03745 {
03746   QString fileToUse = useFileName;
03747   if (fileToUse.isEmpty())
03748     fileToUse = localFilePath();
03749 
03750   // same file as we are monitoring, return
03751   if (fileToUse == m_dirWatchFile)
03752     return;
03753 
03754   // remove the old watched file
03755   deactivateDirWatch ();
03756 
03757   // add new file if needed
03758   if (url().isLocalFile() && !fileToUse.isEmpty())
03759   {
03760     KateGlobal::self()->dirWatch ()->addFile (fileToUse);
03761     m_dirWatchFile = fileToUse;
03762   }
03763 }
03764 
03765 void KateDocument::deactivateDirWatch ()
03766 {
03767   if (!m_dirWatchFile.isEmpty())
03768     KateGlobal::self()->dirWatch ()->removeFile (m_dirWatchFile);
03769 
03770   m_dirWatchFile.clear();
03771 }
03772 
03773 bool KateDocument::closeUrl()
03774 {
03775   //
03776   // file mod on hd
03777   //
03778   if ( !m_reloading && !url().isEmpty() )
03779   {
03780     if (s_fileChangedDialogsActivated && m_modOnHd)
03781     {
03782       QWidget *parentWidget(dialogParent());
03783 
03784       if (!(KMessageBox::warningContinueCancel(
03785             parentWidget,
03786             reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
03787             i18n("Possible Data Loss"), KGuiItem(i18n("Close Nevertheless")), KStandardGuiItem::cancel(),
03788             QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
03789         return false;
03790     }
03791   }
03792 
03793   //
03794   // first call the normal kparts implementation
03795   //
03796   if (!KParts::ReadWritePart::closeUrl ())
03797     return false;
03798 
03799   // Tell the world that we're about to go ahead with the close
03800   if (!m_reloading)
03801     emit aboutToClose(this);
03802 
03803   // remove file from dirwatch
03804   deactivateDirWatch ();
03805 
03806   //
03807   // empty url + fileName
03808   //
03809   setUrl(KUrl());
03810   setLocalFilePath(QString());
03811 
03812   // we are not modified
03813   if (m_modOnHd)
03814   {
03815     m_modOnHd = false;
03816     m_modOnHdReason = OnDiskUnmodified;
03817     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03818   }
03819 
03820   emit KTextEditor::Document::textRemoved(this, documentRange());
03821   history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03822 
03823   // clear the buffer
03824   m_buffer->clear();
03825 
03826   // remove all marks
03827   clearMarks ();
03828 
03829   // clear undo/redo history
03830   clearUndo();
03831   clearRedo();
03832 
03833   // no, we are no longer modified
03834   setModified(false);
03835 
03836   // we have no longer any hl
03837   m_buffer->setHighlight(0);
03838 
03839   // update all our views
03840   foreach (KateView * view, m_views )
03841   {
03842     view->clearSelection(); // fix bug #118588
03843     view->clear();
03844   }
03845 
03846   if (!m_reloading) 
03847   {
03848 
03849     // uh, fileName changed
03850     emit documentUrlChanged (this);
03851 
03852     // update doc name
03853     setDocName (QString());
03854   }
03855   // success
03856   return true;
03857 }
03858 
03859 void KateDocument::setReadWrite( bool rw )
03860 {
03861   if (isReadWrite() != rw)
03862   {
03863     KParts::ReadWritePart::setReadWrite (rw);
03864 
03865     foreach( KateView* view, m_views)
03866     {
03867       view->slotUpdate();
03868       view->slotReadWriteChanged ();
03869     }
03870   }
03871 }
03872 
03873 void KateDocument::setModified(bool m) {
03874 
03875   if (isModified() != m) {
03876     KParts::ReadWritePart::setModified (m);
03877 
03878     foreach( KateView* view,m_views)
03879     {
03880       view->slotUpdate();
03881     }
03882 
03883     emit modifiedChanged (this);
03884   }
03885   if ( m == false )
03886   {
03887     if ( ! undoItems.isEmpty() )
03888     {
03889       lastUndoGroupWhenSaved = undoItems.last();
03890     }
03891 
03892     if ( ! redoItems.isEmpty() )
03893     {
03894       lastRedoGroupWhenSaved = redoItems.last();
03895     }
03896 
03897     docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
03898     docWasSavedWhenRedoWasEmpty = redoItems.isEmpty();
03899   }
03900 }
03901 //END
03902 
03903 //BEGIN Kate specific stuff ;)
03904 
03905 void KateDocument::makeAttribs(bool needInvalidate)
03906 {
03907   foreach(KateView *view,m_views)
03908     view->renderer()->updateAttributes ();
03909 
03910   if (needInvalidate)
03911     m_buffer->invalidateHighlighting();
03912 
03913   tagAll ();
03914 }
03915 
03916 // the attributes of a hl have changed, update
03917 void KateDocument::internalHlChanged()
03918 {
03919   makeAttribs();
03920 }
03921 
03922 void KateDocument::addView(KTextEditor::View *view) {
03923   if (!view)
03924     return;
03925 
03926   m_views.append( static_cast<KateView*>(view) );
03927   m_textEditViews.append( view );
03928 
03929   foreach(KTextEditor::SmartRange* highlight, m_documentHighlights) {
03930     Q_ASSERT(dynamic_cast<KateView*>(view));
03931     static_cast<KateView*>(view)->addExternalHighlight(highlight, m_documentDynamicHighlights.contains(highlight));
03932 }
03933 
03934   // apply the view & renderer vars from the file type
03935   if (!m_fileType.isEmpty())
03936       readVariableLine(KateGlobal::self()->modeManager()->fileType(m_fileType).varLine, true);
03937 
03938   // apply the view & renderer vars from the file
03939   readVariables (true);
03940 
03941   setActiveView(view);
03942 }
03943 
03944 void KateDocument::removeView(KTextEditor::View *view) {
03945   if (!view)
03946     return;
03947 
03948   if (activeView() == view)
03949     setActiveView(0L);
03950 
03951   m_views.removeAll( (KateView *) view );
03952   m_textEditViews.removeAll( view  );
03953 }
03954 
03955 void KateDocument::setActiveView(KTextEditor::View* view)
03956 {
03957   if ( m_activeView == view ) return;
03958 
03959   if (m_activeView) {
03960     disconnect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), this, SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03961   }
03962 
03963   m_activeView = (KateView*)view;
03964 
03965   if (m_activeView) {
03966     connect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03967   }
03968 }
03969 
03970 bool KateDocument::ownedView(KateView *view) {
03971   // do we own the given view?
03972   return (m_views.contains(view));
03973 }
03974 
03975 uint KateDocument::toVirtualColumn( const KTextEditor::Cursor& cursor )
03976 {
03977   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03978 
03979   if (textLine)
03980     return textLine->toVirtualColumn(cursor.column(), config()->tabWidth());
03981   else
03982     return 0;
03983 }
03984 
03985 bool KateDocument::typeChars ( KateView *view, const QString &chars )
03986 {
03987   // Because we want to access text before starting an edit, lock the smart mutex now
03988   QMutexLocker l(smartMutex());
03989 
03990   KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorPosition().line ());
03991 
03992   if (!textLine)
03993     return false;
03994 
03995   bool bracketInserted = false;
03996   QString buf;
03997   QChar c;
03998   for( int z = 0; z < chars.length(); z++ )
03999   {
04000     QChar ch = c = chars[z];
04001 
04002     if (ch.isPrint() || ch == QChar::fromAscii('\t'))
04003     {
04004       buf.append (ch);
04005 
04006       if (!bracketInserted && (config()->configFlags() & KateDocumentConfig::cfAutoBrackets))
04007       {
04008         QChar end_ch;
04009         bool complete = true;
04010         QChar prevChar = textLine->at(view->cursorPosition().column()-1);
04011         QChar nextChar = textLine->at(view->cursorPosition().column());
04012         switch(ch.toAscii()) {
04013           case '(': end_ch = ')'; break;
04014           case '[': end_ch = ']'; break;
04015           case '{': end_ch = '}'; break;
04016           case '\'':end_ch = '\'';break;
04017           case '"': end_ch = '"'; break;
04018           default: complete = false;
04019         }
04020         if (complete)
04021         {
04022           if (view->selection())
04023           { // there is a selection, enclose the selection
04024             buf.append (view->selectionText());
04025             buf.append (end_ch);
04026             bracketInserted = true;
04027           }
04028           else
04029           { // no selection, check whether we should better refuse to complete
04030             if ( ( (ch == '\'' || ch == '"') &&
04031                    (prevChar.isLetterOrNumber() || prevChar == ch) )
04032               || nextChar.isLetterOrNumber()
04033               || (nextChar == end_ch && prevChar != ch) )
04034             {
04035               kDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
04036             }
04037             else
04038             {
04039               buf.append (end_ch);
04040               bracketInserted = true;
04041             }
04042           }
04043         }
04044       }
04045     }
04046   }
04047 
04048   if (buf.isEmpty())
04049     return false;
04050 
04051   l.unlock(); //editStart will lock the smart-mutex again, and it must be un-locked within editEnd. So unlock here.
04052 
04053   editStart ();
04054 
04055   if (!view->config()->persistentSelection() && view->selection() )
04056     view->removeSelectedText();
04057 
04058   KTextEditor::Cursor oldCur (view->cursorPosition());
04059 
04060   if (config()->configFlags()  & KateDocumentConfig::cfOvr)
04061     removeText(KTextEditor::Range(view->cursorPosition(), qMin(buf.length(), textLine->length() - view->cursorPosition().column())));
04062 
04063   insertText(view->cursorPosition(), buf);
04064   KTextEditor::Cursor b(view->cursorPosition());
04065   m_indenter.userTypedChar (view, b, c);
04066 
04067   editEnd ();
04068 
04069   if (bracketInserted)
04070     view->setCursorPositionInternal (view->cursorPosition() - KTextEditor::Cursor(0,1));
04071 
04072   view->slotTextInserted (view, oldCur, chars);
04073   return true;
04074 }
04075 
04076 void KateDocument::newLine( KateView *v )
04077 {
04078   editStart();
04079 
04080   if( !v->config()->persistentSelection() && v->selection() )
04081     v->removeSelectedText();
04082 
04083   // query cursor position
04084   KTextEditor::Cursor c = v->cursorPosition();
04085 
04086   if (c.line() > (int)lastLine())
04087     c.setLine(lastLine());
04088 
04089   if (c.line() < 0)
04090     c.setLine(0);
04091 
04092   uint ln = c.line();
04093 
04094   KateTextLine::Ptr textLine = plainKateTextLine(ln);
04095 
04096   if (c.column() > (int)textLine->length())
04097     c.setColumn(textLine->length());
04098 
04099   // first: wrap line
04100   editWrapLine (c.line(), c.column());
04101 
04102   // second: indent the new line, if needed...
04103   m_indenter.userTypedChar(v, v->cursorPosition(), '\n');
04104 
04105   removeTrailingSpace( ln );
04106 
04107   editEnd();
04108 }
04109 
04110 void KateDocument::transpose( const KTextEditor::Cursor& cursor)
04111 {
04112   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04113 
04114   if (!textLine || (textLine->length() < 2))
04115     return;
04116 
04117   uint col = cursor.column();
04118 
04119   if (col > 0)
04120     col--;
04121 
04122   if ((textLine->length() - col) < 2)
04123     return;
04124 
04125   uint line = cursor.line();
04126   QString s;
04127 
04128   //clever swap code if first character on the line swap right&left
04129   //otherwise left & right
04130   s.append (textLine->at(col+1));
04131   s.append (textLine->at(col));
04132   //do the swap
04133 
04134   // do it right, never ever manipulate a textline
04135   editStart ();
04136   editRemoveText (line, col, 2);
04137   editInsertText (line, col, s);
04138   editEnd ();
04139 }
04140 
04141 void KateDocument::backspace( KateView *view, const KTextEditor::Cursor& c )
04142 {
04143   if ( !view->config()->persistentSelection() && view->selection() ) {
04144     view->removeSelectedText();
04145     return;
04146   }
04147 
04148   uint col = qMax( c.column(), 0 );
04149   uint line = qMax( c.line(), 0 );
04150 
04151   if ((col == 0) && (line == 0))
04152     return;
04153 
04154   int complement = 0;
04155   if (col > 0)
04156   {
04157     if (config()->configFlags() & KateDocumentConfig::cfAutoBrackets)
04158     {
04159       // if inside empty (), {}, [], '', "" delete both
04160       KateTextLine::Ptr tl = m_buffer->plainLine(line);
04161       if(!tl) return;
04162       QChar prevChar = tl->at(col-1);
04163       QChar nextChar = tl->at(col);
04164 
04165       if ( (prevChar == '"' && nextChar == '"') ||
04166            (prevChar == '\'' && nextChar == '\'') ||
04167            (prevChar == '(' && nextChar == ')') ||
04168            (prevChar == '[' && nextChar == ']') ||
04169            (prevChar == '{' && nextChar == '}') )
04170       {
04171         complement = 1;
04172       }
04173     }
04174     if (!(config()->configFlags() & KateDocumentConfig::cfBackspaceIndents))
04175     {
04176       // ordinary backspace
04177       //c.cursor.col--;
04178       removeText(KTextEditor::Range(line, col-1, line, col+complement));
04179     }
04180     else
04181     {
04182       // backspace indents: erase to next indent position
04183       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
04184 
04185       // don't forget this check!!!! really!!!!
04186       if (!textLine)
04187         return;
04188 
04189       int colX = textLine->toVirtualColumn(col, config()->tabWidth());
04190       int pos = textLine->firstChar();
04191       if (pos > 0)
04192         pos = textLine->toVirtualColumn(pos, config()->tabWidth());
04193 
04194       if (pos < 0 || pos >= (int)colX)
04195       {
04196         // only spaces on left side of cursor
04197         indent( view, line, -1);
04198       }
04199       else
04200         removeText(KTextEditor::Range(line, col-1, line, col+complement));
04201     }
04202   }
04203   else
04204   {
04205     // col == 0: wrap to previous line
04206     if (line >= 1)
04207     {
04208       KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
04209 
04210       // don't forget this check!!!! really!!!!
04211       if (!textLine)
04212         return;
04213 
04214       if (config()->wordWrap() && textLine->endsWith(QLatin1String(" ")))
04215       {
04216         // gg: in hard wordwrap mode, backspace must also eat the trailing space
04217         removeText (KTextEditor::Range(line-1, textLine->length()-1, line, 0));
04218       }
04219       else
04220         removeText (KTextEditor::Range(line-1, textLine->length(), line, 0));
04221     }
04222   }
04223 }
04224 
04225 void KateDocument::del( KateView *view, const KTextEditor::Cursor& c )
04226 {
04227   if ( !view->config()->persistentSelection() && view->selection() ) {
04228     view->removeSelectedText();
04229     return;
04230   }
04231 
04232   if( c.column() < (int) m_buffer->plainLine(c.line())->length())
04233   {
04234     removeText(KTextEditor::Range(c, 1));
04235   }
04236   else if ( c.line() < lastLine() )
04237   {
04238     removeText(KTextEditor::Range(c.line(), c.column(), c.line()+1, 0));
04239   }
04240 }
04241 
04242 void KateDocument::paste ( KateView* view, QClipboard::Mode mode )
04243 {
04244   QString s = QApplication::clipboard()->text(mode);
04245 
04246   if (s.isEmpty())
04247     return;
04248 
04249   int lines = s.count (QChar::fromAscii ('\n'));
04250 
04251   m_undoDontMerge = true;
04252 
04253   editStart (true, Kate::CutCopyPasteEdit);
04254 
04255   if (!view->config()->persistentSelection() && view->selection() )
04256     view->removeSelectedText();
04257 
04258   KTextEditor::Cursor pos = view->cursorPosition();
04259 
04260   blockRemoveTrailingSpaces(true);
04261   insertText(pos, s, view->blockSelectionMode());
04262   blockRemoveTrailingSpaces(false);
04263 
04264   for (int i = pos.line(); i < pos.line() + lines; ++i)
04265     removeTrailingSpace(i);
04266 
04267   editEnd();
04268 
04269   // move cursor right for block select, as the user is moved right internal
04270   // even in that case, but user expects other behavior in block selection
04271   // mode !
04272   if (view->blockSelectionMode())
04273     view->setCursorPositionInternal(pos + KTextEditor::Cursor(lines, 0));
04274 
04275   if (config()->configFlags() & KateDocumentConfig::cfIndentPastedText)
04276   {
04277     KTextEditor::Range range = KTextEditor::Range(KTextEditor::Cursor(pos.line(), 0),
04278                                                   KTextEditor::Cursor(pos.line() + lines, 0));
04279 
04280     int start = view->selectionRange().start().line();
04281     const int end = view->selectionRange().end().line();
04282 
04283     editStart();
04284 
04285     blockRemoveTrailingSpaces(true);
04286     m_indenter.indent(view, range);
04287     blockRemoveTrailingSpaces(false);
04288 
04289     for (; start <= end; ++start)
04290       removeTrailingSpace(start);
04291 
04292     editEnd();
04293   }
04294 
04295   if (!view->blockSelectionMode()) emit charactersSemiInteractivelyInserted (pos, s);
04296   m_undoDontMerge = true;
04297 }
04298 
04299 void KateDocument::indent ( KateView *v, uint line, int change)
04300 {
04301   // dominik: if there is a selection, iterate afterwards over all lines and
04302   // remove trailing spaces
04303   const bool hasSelection = v->selection();
04304   int start = v->selectionRange().start().line();
04305   const int end = v->selectionRange().end().line();
04306 
04307   KTextEditor::Range range = hasSelection ? v->selectionRange() : KTextEditor::Range (KTextEditor::Cursor (line,0), KTextEditor::Cursor (line,0));
04308 
04309   editStart();
04310   blockRemoveTrailingSpaces(true);
04311   m_indenter.changeIndent(v, range, change);
04312   blockRemoveTrailingSpaces(false);
04313 
04314   if (hasSelection) {
04315     for (; start <= end; ++start)
04316       removeTrailingSpace(start);
04317   }
04318   editEnd();
04319 }
04320 
04321 void KateDocument::align(KateView *view, const KTextEditor::Range &range)
04322 {
04323   editStart();
04324 
04325   blockRemoveTrailingSpaces(true);
04326   m_indenter.indent(view, range);
04327   blockRemoveTrailingSpaces(false);
04328 
04329   for (int start = range.start().line(); start <= range.end().line(); ++start) {
04330     removeTrailingSpace(start);
04331   }
04332 
04333   editEnd();
04334 }
04335 
04336 /*
04337   Remove a given string at the beginning
04338   of the current line.
04339 */
04340 bool KateDocument::removeStringFromBeginning(int line, const QString &str)
04341 {
04342   KateTextLine::Ptr textline = m_buffer->plainLine(line);
04343 
04344   KTextEditor::Cursor cursor (line, 0);
04345   bool there = textline->startsWith(str);
04346 
04347   if (!there)
04348   {
04349     cursor.setColumn(textline->firstChar());
04350     there = textline->matchesAt(cursor.column(), str);
04351   }
04352 
04353   if (there)
04354   {
04355     // Remove some chars
04356     removeText (KTextEditor::Range(cursor, str.length()));
04357   }
04358 
04359   return there;
04360 }
04361 
04362 /*
04363   Remove a given string at the end
04364   of the current line.
04365 */
04366 bool KateDocument::removeStringFromEnd(int line, const QString &str)
04367 {
04368   KateTextLine::Ptr textline = m_buffer->plainLine(line);
04369 
04370   KTextEditor::Cursor cursor (line, 0);
04371   bool there = textline->endsWith(str);
04372 
04373   if (there)
04374   {
04375     cursor.setColumn(textline->length() - str.length());
04376   }
04377   else
04378   {
04379     cursor.setColumn(textline->lastChar() - str.length() + 1);
04380     there = textline->matchesAt(cursor.column(), str);
04381   }
04382 
04383   if (there)
04384   {
04385     // Remove some chars
04386     removeText (KTextEditor::Range(cursor, str.length()));
04387   }
04388 
04389   return there;
04390 }
04391 
04392 /*
04393   Add to the current line a comment line mark at the beginning.
04394 */
04395 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
04396 {
04397   QString commentLineMark = highlight()->getCommentSingleLineStart(attrib);
04398   int pos = -1;
04399 
04400   if (highlight()->getCommentSingleLinePosition(attrib) == KateHighlighting::CSLPosColumn0)
04401   {
04402     pos = 0;
04403     commentLineMark += ' ';
04404   } else {
04405     const KateTextLine::Ptr l = kateTextLine(line);
04406     pos = l->firstChar();
04407   }
04408 
04409   if (pos >= 0)
04410     insertText (KTextEditor::Cursor(line, pos), commentLineMark);
04411 }
04412 
04413 /*
04414   Remove from the current line a comment line mark at
04415   the beginning if there is one.
04416 */
04417 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
04418 {
04419   const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
04420   const QString longCommentMark = shortCommentMark + ' ';
04421 
04422   editStart();
04423 
04424   // Try to remove the long comment mark first
04425   bool removed = (removeStringFromBeginning(line, longCommentMark)
04426                || removeStringFromBeginning(line, shortCommentMark));
04427 
04428   editEnd();
04429 
04430   return removed;
04431 }
04432 
04433 /*
04434   Add to the current line a start comment mark at the
04435   beginning and a stop comment mark at the end.
04436 */
04437 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
04438 {
04439   const QString startCommentMark = highlight()->getCommentStart( attrib ) + ' ';
04440   const QString stopCommentMark = ' ' + highlight()->getCommentEnd( attrib );
04441 
04442   editStart();
04443 
04444   // Add the start comment mark
04445   insertText (KTextEditor::Cursor(line, 0), startCommentMark);
04446 
04447   // Go to the end of the line
04448   const int col = m_buffer->plainLine(line)->length();
04449 
04450   // Add the stop comment mark
04451   insertText (KTextEditor::Cursor(line, col), stopCommentMark);
04452 
04453   editEnd();
04454 }
04455 
04456 /*
04457   Remove from the current line a start comment mark at
04458   the beginning and a stop comment mark at the end.
04459 */
04460 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
04461 {
04462   QString shortStartCommentMark = highlight()->getCommentStart( attrib );
04463   QString longStartCommentMark = shortStartCommentMark + ' ';
04464   QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
04465   QString longStopCommentMark = ' ' + shortStopCommentMark;
04466 
04467   editStart();
04468 
04469 #ifdef __GNUC__
04470 #warning "that's a bad idea, can lead to stray endings, FIXME"
04471 #endif
04472   // Try to remove the long start comment mark first
04473   bool removedStart = (removeStringFromBeginning(line, longStartCommentMark)
04474                     || removeStringFromBeginning(line, shortStartCommentMark));
04475 
04476   bool removedStop = false;
04477   if (removedStart)
04478   {
04479     // Try to remove the long stop comment mark first
04480     removedStop = (removeStringFromEnd(line, longStopCommentMark)
04481                 || removeStringFromEnd(line, shortStopCommentMark));
04482   }
04483 
04484   editEnd();
04485 
04486   return (removedStart || removedStop);
04487 }
04488 
04489 /*
04490   Add to the current selection a start comment mark at the beginning
04491   and a stop comment mark at the end.
04492 */
04493 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
04494 {
04495   const QString startComment = highlight()->getCommentStart( attrib );
04496   const QString endComment = highlight()->getCommentEnd( attrib );
04497 
04498   KTextEditor::Range range = view->selectionRange();
04499 
04500   if ((range.end().column() == 0) && (range.end().line() > 0))
04501     range.end().setPosition(range.end().line() - 1, lineLength(range.end().line() - 1));
04502 
04503   editStart();
04504 
04505   insertText (range.end(), endComment);
04506   insertText (range.start(), startComment);
04507 
04508   editEnd ();
04509   // selection automatically updated (KateSmartRange)
04510 }
04511 
04512 /*
04513   Add to the current selection a comment line mark at the beginning of each line.
04514 */
04515 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
04516 {
04517   const QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + ' ';
04518 
04519   int sl = view->selectionRange().start().line();
04520   int el = view->selectionRange().end().line();
04521 
04522   // if end of selection is in column 0 in last line, omit the last line
04523   if ((view->selectionRange().end().column() == 0) && (el > 0))
04524   {
04525     el--;
04526   }
04527 
04528   editStart();
04529 
04530   // For each line of the selection
04531   for (int z = el; z >= sl; z--) {
04532     //insertText (z, 0, commentLineMark);
04533     addStartLineCommentToSingleLine(z, attrib );
04534   }
04535 
04536   editEnd ();
04537   // selection automatically updated (KateSmartRange)
04538 }
04539 
04540 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
04541 {
04542   for(; line < (int)m_buffer->count(); line++) {
04543     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
04544 
04545     if (!textLine)
04546       break;
04547 
04548     col = textLine->nextNonSpaceChar(col);
04549     if(col != -1)
04550       return true; // Next non-space char found
04551     col = 0;
04552   }
04553   // No non-space char found
04554   line = -1;
04555   col = -1;
04556   return false;
04557 }
04558 
04559 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
04560 {
04561   while(true)
04562   {
04563     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
04564 
04565     if (!textLine)
04566       break;
04567 
04568     col = textLine->previousNonSpaceChar(col);
04569     if(col != -1) return true;
04570     if(line == 0) return false;
04571     --line;
04572     col = textLine->length();
04573   }
04574   // No non-space char found
04575   line = -1;
04576   col = -1;
04577   return false;
04578 }
04579 
04580 /*
04581   Remove from the selection a start comment mark at
04582   the beginning and a stop comment mark at the end.
04583 */
04584 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
04585 {
04586   const QString startComment = highlight()->getCommentStart( attrib );
04587   const QString endComment = highlight()->getCommentEnd( attrib );
04588 
04589   int sl = qMax<int> (0, view->selectionRange().start().line());
04590   int el = qMin<int>  (view->selectionRange().end().line(), lastLine());
04591   int sc = view->selectionRange().start().column();
04592   int ec = view->selectionRange().end().column();
04593 
04594   // The selection ends on the char before selectEnd
04595   if (ec != 0) {
04596     --ec;
04597   } else if (el > 0) {
04598     --el;
04599     ec = m_buffer->plainLine(el)->length() - 1;
04600   }
04601 
04602   const int startCommentLen = startComment.length();
04603   const int endCommentLen = endComment.length();
04604 
04605   // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$2/
04606 
04607   bool remove = nextNonSpaceCharPos(sl, sc)
04608       && m_buffer->plainLine(sl)->matchesAt(sc, startComment)
04609       && previousNonSpaceCharPos(el, ec)
04610       && ( (ec - endCommentLen + 1) >= 0 )
04611       && m_buffer->plainLine(el)->matchesAt(ec - endCommentLen + 1, endComment);
04612 
04613   if (remove) {
04614     editStart();
04615 
04616     removeText (KTextEditor::Range(el, ec - endCommentLen + 1, el, ec + 1));
04617     removeText (KTextEditor::Range(sl, sc, sl, sc + startCommentLen));
04618 
04619     editEnd ();
04620     // selection automatically updated (KateSmartRange)
04621   }
04622 
04623   return remove;
04624 }
04625 
04626 bool KateDocument::removeStartStopCommentFromRegion(const KTextEditor::Cursor &start,const KTextEditor::Cursor &end,int attrib)
04627 {
04628   const QString startComment = highlight()->getCommentStart( attrib );
04629   const QString endComment = highlight()->getCommentEnd( attrib );
04630   const int startCommentLen = startComment.length();
04631   const int endCommentLen = endComment.length();
04632 
04633   const bool remove = m_buffer->plainLine(start.line())->matchesAt(start.column(), startComment)
04634                    && m_buffer->plainLine(end.line())->matchesAt(end.column() - endCommentLen , endComment);
04635   if (remove) {
04636     editStart();
04637       removeText(KTextEditor::Range(end.line(), end.column() - endCommentLen, end.line(), end.column()));
04638       removeText(KTextEditor::Range(start, startCommentLen));
04639     editEnd();
04640   }
04641   return remove;
04642 }
04643 
04644 /*
04645   Remove from the beginning of each line of the
04646   selection a start comment line mark.
04647 */
04648 bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib )
04649 {
04650   const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
04651   const QString longCommentMark = shortCommentMark + ' ';
04652 
04653   int sl = view->selectionRange().start().line();
04654   int el = view->selectionRange().end().line();
04655 
04656   if ((view->selectionRange().end().column() == 0) && (el > 0))
04657   {
04658     el--;
04659   }
04660 
04661   bool removed = false;
04662 
04663   editStart();
04664 
04665   // For each line of the selection
04666   for (int z = el; z >= sl; z--)
04667   {
04668     // Try to remove the long comment mark first
04669     removed = (removeStringFromBeginning(z, longCommentMark)
04670             || removeStringFromBeginning(z, shortCommentMark)
04671             || removed);
04672   }
04673 
04674   editEnd();
04675   // selection automatically updated (KateSmartRange)
04676 
04677   return removed;
04678 }
04679 
04680 /*
04681   Comment or uncomment the selection or the current
04682   line if there is no selection.
04683 */
04684 void KateDocument::comment( KateView *v, uint line,uint column, int change)
04685 {
04686   // We need to check that we can sanely comment the selectino or region.
04687   // It is if the attribute of the first and last character of the range to
04688   // comment belongs to the same language definition.
04689   // for lines with no text, we need the attribute for the lines context.
04690   bool hassel = v->selection();
04691   int startAttrib, endAttrib;
04692   if ( hassel )
04693   {
04694     KateTextLine::Ptr ln = kateTextLine( v->selectionRange().start().line() );
04695     int l = v->selectionRange().start().line(), c = v->selectionRange().start().column();
04696     startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
04697 
04698     ln = kateTextLine( v->selectionRange().end().line() );
04699     l = v->selectionRange().end().line(), c = v->selectionRange().end().column();
04700     endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
04701   }
04702   else
04703   {
04704     KateTextLine::Ptr ln = kateTextLine( line );
04705     if ( ln->length() )
04706     {
04707       startAttrib = ln->attribute( ln->firstChar() );
04708       endAttrib = ln->attribute( ln->lastChar() );
04709     }
04710     else
04711     {
04712       int l = line, c = 0;
04713       if ( nextNonSpaceCharPos( l, c )  || previousNonSpaceCharPos( l, c ) )
04714         startAttrib = endAttrib = kateTextLine( l )->attribute( c );
04715       else
04716         startAttrib = endAttrib = 0;
04717     }
04718   }
04719 
04720   if ( ! highlight()->canComment( startAttrib, endAttrib ) )
04721   {
04722     kDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!";
04723     return;
04724   }
04725 
04726   bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
04727   bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
04728       && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
04729 
04730   bool removed = false;
04731 
04732   if (change > 0) // comment
04733   {
04734     if ( !hassel )
04735     {
04736       if ( hasStartLineCommentMark )
04737         addStartLineCommentToSingleLine( line, startAttrib );
04738       else if ( hasStartStopCommentMark )
04739         addStartStopCommentToSingleLine( line, startAttrib );
04740     }
04741     else
04742     {
04743       // anders: prefer single line comment to avoid nesting probs
04744       // If the selection starts after first char in the first line
04745       // or ends before the last char of the last line, we may use
04746       // multiline comment markers.
04747       // TODO We should try to detect nesting.
04748       //    - if selection ends at col 0, most likely she wanted that
04749       // line ignored
04750       if ( hasStartStopCommentMark &&
04751            ( !hasStartLineCommentMark || (
04752            ( v->selectionRange().start().column() > m_buffer->plainLine( v->selectionRange().start().line() )->firstChar() ) ||
04753            ( v->selectionRange().end().column() < ((int)m_buffer->plainLine( v->selectionRange().end().line() )->length()) )
04754          ) ) )
04755         addStartStopCommentToSelection( v, startAttrib );
04756       else if ( hasStartLineCommentMark )
04757         addStartLineCommentToSelection( v, startAttrib );
04758     }
04759   }
04760   else // uncomment
04761   {
04762     if ( !hassel )
04763     {
04764       removed = ( hasStartLineCommentMark
04765                   && removeStartLineCommentFromSingleLine( line, startAttrib ) )
04766         || ( hasStartStopCommentMark
04767              && removeStartStopCommentFromSingleLine( line, startAttrib ) );
04768       if ((!removed) && foldingTree()) {
04769         kDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)";
04770         int commentRegion=(highlight()->commentRegion(startAttrib));
04771         if (commentRegion){
04772            KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
04773            if (n) {
04774             KTextEditor::Cursor start,end;
04775             if ((n->nodeType()==(int)commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
04776                 kDebug(13020)<<"Enclosing region found:"<<start.column()<<"/"<<start.line()<<"-"<<end.column()<<"/"<<end.line();
04777                 removeStartStopCommentFromRegion(start,end,startAttrib);
04778              } else {
04779                   kDebug(13020)<<"Enclosing region found, but not valid";
04780                   kDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion;
04781              }
04782             //perhaps nested regions should be hadled here too...
04783           } else kDebug(13020)<<"No enclosing region found";
04784         } else kDebug(13020)<<"No comment region specified for current hl";
04785       }
04786     }
04787     else
04788     {
04789       // anders: this seems like it will work with above changes :)
04790       removed = ( hasStartLineCommentMark
04791           && removeStartLineCommentFromSelection( v, startAttrib ) )
04792         || ( hasStartStopCommentMark
04793           && removeStartStopCommentFromSelection( v, startAttrib ) );
04794     }
04795   }
04796 }
04797 
04798 void KateDocument::transform( KateView *v, const KTextEditor::Cursor &c,
04799                             KateDocument::TextTransform t )
04800 {
04801   editStart();
04802   KTextEditor::Cursor cursor = c;
04803 
04804   if ( v->selection() )
04805   {
04806     // cache the selection and cursor, so we can be sure to restore.
04807     KTextEditor::Range selection = v->selectionRange();
04808 
04809     KTextEditor::Range range(selection.start(), 0);
04810     while ( range.start().line() <= selection.end().line() )
04811     {
04812       int start = 0;
04813       int end = lineLength( range.start().line() );
04814 
04815       if (range.start().line() == selection.start().line() || v->blockSelectionMode())
04816         start = selection.start().column();
04817 
04818       if (range.start().line() == selection.end().line() || v->blockSelectionMode())
04819         end = selection.end().column();
04820 
04821       if ( start > end )
04822       {
04823         int swapCol = start;
04824         start = end;
04825         end = swapCol;
04826       }
04827       range.start().setColumn( start );
04828       range.end().setColumn( end );
04829 
04830       QString s = text( range );
04831       QString old = s;
04832 
04833       if ( t == Uppercase )
04834         s = s.toUpper();
04835       else if ( t == Lowercase )
04836         s = s.toLower();
04837       else // Capitalize
04838       {
04839         KateTextLine::Ptr l = m_buffer->plainLine( range.start().line() );
04840         int p ( 0 );
04841         while( p < s.length() )
04842         {
04843           // If bol or the character before is not in a word, up this one:
04844           // 1. if both start and p is 0, upper char.
04845           // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
04846           // 3. if p-1 is not in a word, upper.
04847           if ( ( ! range.start().column() && ! p ) ||
04848                    ( ( range.start().line() == selection.start().line() || v->blockSelectionMode() ) &&
04849                    ! p && ! highlight()->isInWord( l->at( range.start().column() - 1 )) ) ||
04850                    ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
04851              )
04852             s[p] = s.at(p).toUpper();
04853           p++;
04854         }
04855       }
04856 
04857       if ( s != old )
04858       {
04859         removeText( range );
04860         insertText( range.start(), s );
04861       }
04862 
04863       range.setBothLines(range.start().line() + 1);
04864     }
04865 
04866     // restore selection
04867     v->setSelection( selection );
04868 
04869   } else {  // no selection
04870     QString old = text( KTextEditor::Range(cursor, 1) );
04871     QString s;
04872     switch ( t ) {
04873       case Uppercase:
04874       s = old.toUpper();
04875       break;
04876       case Lowercase:
04877       s = old.toLower();
04878       break;
04879       case Capitalize:
04880       {
04881         KateTextLine::Ptr l = m_buffer->plainLine( cursor.line() );
04882         while ( cursor.column() > 0 && highlight()->isInWord( l->at( cursor.column() - 1 ), l->attribute( cursor.column() - 1 ) ) )
04883           cursor.setColumn(cursor.column() - 1);
04884         old = text( KTextEditor::Range(cursor, 1) );
04885         s = old.toUpper();
04886       }
04887       break;
04888       default:
04889       break;
04890     }
04891     if ( s != old )
04892     {
04893       removeText( KTextEditor::Range(cursor, 1) );
04894       insertText( cursor, s );
04895     }
04896   }
04897 
04898   editEnd();
04899 
04900   v->setCursorPosition( c );
04901 }
04902 
04903 void KateDocument::joinLines( uint first, uint last )
04904 {
04905 //   if ( first == last ) last += 1;
04906   editStart();
04907   int line( first );
04908   while ( first < last )
04909   {
04910     // Normalize the whitespace in the joined lines by making sure there's
04911     // always exactly one space between the joined lines
04912     // This cannot be done in editUnwrapLine, because we do NOT want this
04913     // behavior when deleting from the start of a line, just when explicitly
04914     // calling the join command
04915     KateTextLine::Ptr l = kateTextLine( line );
04916     KateTextLine::Ptr tl = kateTextLine( line + 1 );
04917 
04918     if ( !l || !tl )
04919     {
04920       editEnd();
04921       return;
04922     }
04923 
04924     int pos = tl->firstChar();
04925     if ( pos >= 0 )
04926     {
04927       if (pos != 0)
04928         editRemoveText( line + 1, 0, pos );
04929       if ( !( l->length() == 0 || l->at( l->length() - 1 ).isSpace() ) )
04930         editInsertText( line + 1, 0, " " );
04931     }
04932     else
04933     {
04934       // Just remove the whitespace and let Kate handle the rest
04935       editRemoveText( line + 1, 0, tl->length() );
04936     }
04937 
04938     editUnWrapLine( line );
04939     first++;
04940   }
04941   editEnd();
04942 }
04943 
04944 QString KateDocument::getWord( const KTextEditor::Cursor& cursor )
04945 {
04946   int start, end, len;
04947 
04948   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04949   len = textLine->length();
04950   start = end = cursor.column();
04951   if (start > len)        // Probably because of non-wrapping cursor mode.
04952     return QString("");
04953 
04954   while (start > 0 && highlight()->isInWord(textLine->at(start - 1), textLine->attribute(start - 1))) start--;
04955   while (end < len && highlight()->isInWord(textLine->at(end), textLine->attribute(end))) end++;
04956   len = end - start;
04957   return textLine->string().mid(start, len);
04958 }
04959 
04960 void KateDocument::tagLines(int start, int end)
04961 {
04962   foreach(KateView *view,m_views)
04963     view->tagLines (start, end, true);
04964 }
04965 
04966 void KateDocument::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end)
04967 {
04968   // May need to switch start/end cols if in block selection mode
04969 /*  if (blockSelectionMode() && start.column() > end.column()) {
04970     int sc = start.column();
04971     start.setColumn(end.column());
04972     end.setColumn(sc);
04973   }
04974 */
04975   foreach (KateView* view, m_views)
04976     view->tagLines(start, end, true);
04977 }
04978 
04979 void KateDocument::repaintViews(bool paintOnlyDirty)
04980 {
04981   foreach(KateView *view,m_views)
04982     view->repaintText(paintOnlyDirty);
04983 }
04984 
04985 void KateDocument::tagAll()
04986 {
04987   foreach(KateView *view,m_views)
04988   {
04989     view->tagAll();
04990     view->updateView (true);
04991   }
04992 }
04993 
04994 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04995 inline bool isEndBracket  ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04996 inline bool isBracket     ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04997 
04998 /*
04999    Bracket matching uses the following algorithm:
05000    If in overwrite mode, match the bracket currently underneath the cursor.
05001    Otherwise, if the character to the left is a bracket,
05002    match it. Otherwise if the character to the right of the cursor is a
05003    bracket, match it. Otherwise, don't match anything.
05004 */
05005 void KateDocument::newBracketMark( const KTextEditor::Cursor& cursor, KTextEditor::Range& bm, int maxLines )
05006 {
05007   bm.start() = cursor;
05008 
05009   if( findMatchingBracket( bm, maxLines ) )
05010     return;
05011 
05012   bm = KTextEditor::Range::invalid();
05013 
05014  // const int tw = config()->tabWidth();
05015  // const int indentStart = m_buffer->plainLine(bm.start().line())->indentDepth(tw);
05016  // const int indentEnd = m_buffer->plainLine(bm.end().line())->indentDepth(tw);
05017   //bm.setIndentMin(qMin(indentStart, indentEnd));
05018 }
05019 
05020 bool KateDocument::findMatchingBracket( KTextEditor::Range& range, int maxLines )
05021 {
05022   KateTextLine::Ptr textLine = m_buffer->plainLine( range.start().line() );
05023   if( !textLine )
05024     return false;
05025 
05026   QChar right = textLine->at( range.start().column() );
05027   QChar left  = textLine->at( range.start().column() - 1 );
05028   QChar bracket;
05029 
05030   if ( config()->configFlags() & KateDocumentConfig::cfOvr ) {
05031     if( isBracket( right ) ) {
05032       bracket = right;
05033     } else {
05034       return false;
05035     }
05036   } else if ( isBracket( left ) ) {
05037     range.start().setColumn(range.start().column() - 1);
05038     bracket = left;
05039   } else if ( isBracket( right ) ) {
05040     bracket = right;
05041   } else {
05042     return false;
05043   }
05044 
05045   QChar opposite;
05046 
05047   switch( bracket.toAscii() ) {
05048   case '{': opposite = '}'; break;
05049   case '}': opposite = '{'; break;
05050   case '[': opposite = ']'; break;
05051   case ']': opposite = '['; break;
05052   case '(': opposite = ')'; break;
05053   case ')': opposite = '('; break;
05054   default: return false;
05055   }
05056 
05057   bool forward = isStartBracket( bracket );
05058   uint nesting = 0;
05059 
05060   int minLine = qMax( range.start().line() - maxLines, 0 );
05061   int maxLine = qMin( range.start().line() + maxLines, documentEnd().line() );
05062 
05063   range.end() = range.start();
05064   KateDocCursor cursor(range.start(), this);
05065   uchar validAttr = cursor.currentAttrib();
05066 
05067   while( cursor.line() >= minLine && cursor.line() <= maxLine ) {
05068 
05069     if( forward )
05070       cursor.moveForward(1);
05071     else
05072       cursor.moveBackward(1);
05073 
05074     if( !cursor.validPosition() )
05075       return false;
05076 
05077     if( cursor.currentAttrib() == validAttr )
05078     {
05079       /* Check for match */
05080       QChar c = cursor.currentChar();
05081       if( c == bracket ) {
05082         nesting++;
05083       } else if( c == opposite ) {
05084         if( nesting == 0 ) {
05085           if( forward )
05086             range.end() = cursor;
05087           else
05088             range.start() = cursor;
05089           return true;
05090         }
05091         nesting--;
05092       }
05093     }
05094 
05095     if(cursor == KTextEditor::Cursor(0,0) || cursor >= documentEnd())
05096       return false;
05097   }
05098 
05099   return false;
05100 }
05101 
05102 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
05103 {
05104   KParts::ReadWritePart::guiActivateEvent( ev );
05105   //if ( ev->activated() )
05106   //  emit selectionChanged();
05107 }
05108 
05109 void KateDocument::setDocName (QString name )
05110 {
05111   if ( name == m_docName )
05112     return;
05113 
05114   if ( !name.isEmpty() )
05115   {
05116     // TODO check for similarly named documents
05117     m_docName = name;
05118     emit documentNameChanged (this);
05119     return;
05120   }
05121 
05122   // if the name is set, and starts with FILENAME, it should not be changed!
05123   if ( ! url().isEmpty()
05124        && (m_docName == url().fileName() || m_docName.startsWith (url().fileName() + " (") ) ) return;
05125 
05126   int count = -1;
05127 
05128   for (int z=0; z < KateGlobal::self()->kateDocuments().size(); ++z)
05129   {
05130     KateDocument *doc = (KateGlobal::self()->kateDocuments())[z];
05131 
05132     if ( (doc != this) && (doc->url().fileName() == url().fileName()) )
05133       if ( doc->m_docNameNumber > count )
05134         count = doc->m_docNameNumber;
05135   }
05136 
05137   m_docNameNumber = count + 1;
05138 
05139   m_docName = url().fileName();
05140 
05141   if (m_docName.isEmpty())
05142     m_docName = i18n ("Untitled");
05143 
05144   if (m_docNameNumber > 0)
05145     m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
05146 
05147   emit documentNameChanged (this);
05148 }
05149 
05150 void KateDocument::slotModifiedOnDisk( KTextEditor::View * /*v*/ )
05151 {
05152   if ( m_isasking < 0 )
05153   {
05154     m_isasking = 0;
05155     return;
05156   }
05157 
05158   if ( !s_fileChangedDialogsActivated || m_isasking )
05159     return;
05160 
05161   if (m_modOnHd && !url().isEmpty())
05162   {
05163     m_isasking = 1;
05164 
05165     QWidget *parentWidget(dialogParent());
05166 
05167     KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), parentWidget );
05168     switch ( p.exec() )
05169     {
05170       case KateModOnHdPrompt::Save:
05171       {
05172         m_modOnHd = false;
05173         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(),
05174             url().url(),QString(),parentWidget,i18n("Save File"));
05175 
05176         kDebug(13020)<<"got "<<res.URLs.count()<<" URLs";
05177         if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first(), parentWidget ) )
05178         {
05179           setEncoding( res.encoding );
05180 
05181           if( ! saveAs( res.URLs.first() ) )
05182           {
05183             KMessageBox::error( parentWidget, i18n("Save failed") );
05184             m_modOnHd = true;
05185           }
05186           else
05187             emit modifiedOnDisk( this, false, OnDiskUnmodified );
05188         }
05189         else // the save as dialog was canceled, we are still modified on disk
05190         {
05191           m_modOnHd = true;
05192         }
05193 
05194         m_isasking = 0;
05195         break;
05196       }
05197 
05198       case KateModOnHdPrompt::Reload:
05199         m_modOnHd = false;
05200         emit modifiedOnDisk( this, false, OnDiskUnmodified );
05201         documentReload();
05202         m_isasking = 0;
05203         break;
05204 
05205       case KateModOnHdPrompt::Ignore:
05206         m_modOnHd = false;
05207         emit modifiedOnDisk( this, false, OnDiskUnmodified );
05208         m_isasking = 0;
05209         break;
05210 
05211       case KateModOnHdPrompt::Overwrite:
05212         m_modOnHd = false;
05213         emit modifiedOnDisk( this, false, OnDiskUnmodified );
05214         m_isasking = 0;
05215         save();
05216         break;
05217 
05218       default: // Delay/cancel: ignore next focus event
05219         m_isasking = -1;
05220     }
05221   }
05222 }
05223 
05224 void KateDocument::setModifiedOnDisk( ModifiedOnDiskReason reason )
05225 {
05226   m_modOnHdReason = reason;
05227   m_modOnHd = (reason != OnDiskUnmodified);
05228   emit modifiedOnDisk( this, (reason != OnDiskUnmodified), reason );
05229 }
05230 
05231 class KateDocumentTmpMark
05232 {
05233   public:
05234     QString line;
05235     KTextEditor::Mark mark;
05236 };
05237 
05238 void KateDocument::setModifiedOnDiskWarning (bool on)
05239 {
05240   s_fileChangedDialogsActivated = on;
05241 }
05242 
05243 bool KateDocument::documentReload()
05244 {
05245   if ( !url().isEmpty() )
05246   {
05247     if (m_modOnHd && s_fileChangedDialogsActivated)
05248     {
05249       QWidget *parentWidget(dialogParent());
05250 
05251       int i = KMessageBox::warningYesNoCancel
05252                 (parentWidget, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
05253                 i18n("File Was Changed on Disk"), KGuiItem(i18n("&Reload File")), KGuiItem(i18n("&Ignore Changes")));
05254 
05255       if ( i != KMessageBox::Yes)
05256       {
05257         if (i == KMessageBox::No)
05258         {
05259           m_modOnHd = false;
05260           m_modOnHdReason = OnDiskUnmodified;
05261           emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05262         }
05263 
05264         return false;
05265       }
05266     }
05267 
05268     emit aboutToReload(this);
05269 
05270     if (clearOnDocumentReload())
05271       m_smartManager->clear(false);
05272 
05273     QList<KateDocumentTmpMark> tmp;
05274 
05275     for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
05276     {
05277       KateDocumentTmpMark m;
05278 
05279       m.line = line (i.value()->line);
05280       m.mark = *i.value();
05281 
05282       tmp.append (m);
05283     }
05284 
05285     QString oldMode = mode ();
05286     bool byUser = m_fileTypeSetByUser;
05287 
05288     m_storedVariables.clear();
05289 
05290     // save cursor positions for all views
05291     QVector<KTextEditor::Cursor> cursorPositions;
05292     cursorPositions.reserve(m_views.size());
05293     foreach (KateView *v, m_views)
05294       cursorPositions.append( v->cursorPosition() );
05295 
05296     m_reloading = true;
05297     KateDocument::openUrl( url() );
05298     m_reloading = false;
05299 
05300 
05301     // restore cursor positions for all views
05302     QLinkedList<KateView*>::iterator it = m_views.begin();
05303     for(int i = 0; i < m_views.size(); ++i, ++it) {
05304       (*it)->setCursorPositionInternal( cursorPositions[i], m_config->tabWidth(), false );
05305       if ((*it)->isVisible()) {
05306         (*it)->repaintText(false);
05307       }
05308     }
05309 
05310     for (int z=0; z < tmp.size(); z++)
05311     {
05312       if (z < (int)lines())
05313       {
05314         if (line(tmp[z].mark.line) == tmp[z].line)
05315           setMark (tmp[z].mark.line, tmp[z].mark.type);
05316       }
05317     }
05318 
05319     if (byUser)
05320       setMode (oldMode);
05321 
05322     return true;
05323   }
05324 
05325   return false;
05326 }
05327 
05328 bool KateDocument::documentSave()
05329 {
05330   if( !url().isValid() || !isReadWrite() )
05331     return documentSaveAs();
05332 
05333   return save();
05334 }
05335 
05336 bool KateDocument::documentSaveAs()
05337 {
05338   QWidget *parentWidget(dialogParent());
05339 
05340   KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(),
05341                 url().url(),QString(),parentWidget,i18n("Save File"));
05342 
05343   if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) )
05344     return false;
05345 
05346   setEncoding( res.encoding );
05347 
05348   return saveAs( res.URLs.first() );
05349 }
05350 
05351 void KateDocument::setWordWrap (bool on)
05352 {
05353   config()->setWordWrap (on);
05354 }
05355 
05356 bool KateDocument::wordWrap () const
05357 {
05358   return config()->wordWrap ();
05359 }
05360 
05361 void KateDocument::setWordWrapAt (uint col)
05362 {
05363   config()->setWordWrapAt (col);
05364 }
05365 
05366 unsigned int KateDocument::wordWrapAt () const
05367 {
05368   return config()->wordWrapAt ();
05369 }
05370 
05371 void KateDocument::setPageUpDownMovesCursor (bool on)
05372 {
05373   config()->setPageUpDownMovesCursor (on);
05374 }
05375 
05376 bool KateDocument::pageUpDownMovesCursor () const
05377 {
05378   return config()->pageUpDownMovesCursor ();
05379 }
05380 
05381 void KateDocument::dumpRegionTree()
05382 {
05383   m_buffer->foldingTree()->debugDump();
05384 }
05385 //END
05386 
05387 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
05388 {
05389   m_buffer->lineInfo(info,line);
05390 }
05391 
05392 KateCodeFoldingTree *KateDocument::foldingTree ()
05393 {
05394   return m_buffer->foldingTree();
05395 }
05396 
05397 bool KateDocument::setEncoding (const QString &e)
05398 {
05399   return m_config->setEncoding(e);
05400 }
05401 
05402 const QString &KateDocument::encoding() const
05403 {
05404   return m_config->encoding();
05405 }
05406 
05407 void KateDocument::setProberTypeForEncodingAutoDetection (KEncodingProber::ProberType proberType)
05408 {
05409   m_config->setEncodingProberType(proberType);
05410 }
05411 
05412 KEncodingProber::ProberType KateDocument::proberTypeForEncodingAutoDetection() const
05413 {
05414   return m_config->encodingProberType();
05415 }
05416 
05417 void KateDocument::updateConfig ()
05418 {
05419   emit undoChanged ();
05420   tagAll();
05421 
05422   foreach (KateView * view,m_views)
05423   {
05424     view->updateDocumentConfig ();
05425   }
05426 
05427   // switch indenter if needed and update config....
05428   m_indenter.setMode (m_config->indentationMode());
05429   m_indenter.updateConfig();
05430 
05431   m_buffer->setTabWidth (config()->tabWidth());
05432 }
05433 
05434 //BEGIN Variable reader
05435 // "local variable" feature by anders, 2003
05436 /* TODO
05437       add config options (how many lines to read, on/off)
05438       add interface for plugins/apps to set/get variables
05439       add view stuff
05440 */
05441 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
05442 QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)");
05443 QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)");
05444 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
05445 
05446 void KateDocument::readVariables(bool onlyViewAndRenderer)
05447 {
05448   if (!onlyViewAndRenderer)
05449     m_config->configStart();
05450 
05451   // views!
05452   KateView *v;
05453   foreach (v,m_views)
05454   {
05455     v->config()->configStart();
05456     v->renderer()->config()->configStart();
05457   }
05458   // read a number of lines in the top/bottom of the document
05459   for (int i=0; i < qMin( 9, lines() ); ++i )
05460   {
05461     readVariableLine( line( i ), onlyViewAndRenderer );
05462   }
05463   if ( lines() > 10 )
05464   {
05465     for ( int i = qMax( 10, lines() - 10); i < lines(); i++ )
05466     {
05467       readVariableLine( line( i ), onlyViewAndRenderer );
05468     }
05469   }
05470 
05471   if (!onlyViewAndRenderer)
05472     m_config->configEnd();
05473 
05474   foreach (v,m_views)
05475   {
05476     v->config()->configEnd();
05477     v->renderer()->config()->configEnd();
05478   }
05479 }
05480 
05481 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
05482 {
05483   // simple check first, no regex
05484   // no kate inside, no vars, simple...
05485   if (!t.contains("kate"))
05486     return;
05487 
05488   // found vars, if any
05489   QString s;
05490 
05491   // now, try first the normal ones
05492   if ( kvLine.indexIn( t ) > -1 )
05493   {
05494     s = kvLine.cap(1);
05495 
05496     kDebug (13020) << "normal variable line kate: matched: " << s;
05497   }
05498   else if (kvLineWildcard.indexIn( t ) > -1) // regex given
05499   {
05500     QStringList wildcards (kvLineWildcard.cap(1).split (';', QString::SkipEmptyParts));
05501     QString nameOfFile = url().fileName();
05502 
05503     bool found = false;
05504     for (int i = 0; !found && i < wildcards.size(); ++i)
05505     {
05506       QRegExp wildcard (wildcards[i], Qt::CaseSensitive, QRegExp::Wildcard);
05507 
05508       found = wildcard.exactMatch (nameOfFile);
05509     }
05510 
05511     // nothing usable found...
05512     if (!found)
05513       return;
05514 
05515     s = kvLineWildcard.cap(2);
05516 
05517     kDebug (13020) << "guarded variable line kate-wildcard: matched: " << s;
05518   }
05519   else if (kvLineMime.indexIn( t ) > -1) // mime-type given
05520   {
05521     QStringList types (kvLineMime.cap(1).split (';', QString::SkipEmptyParts));
05522 
05523     // no matching type found
05524     if (!types.contains (mimeType ()))
05525       return;
05526 
05527     s = kvLineMime.cap(2);
05528 
05529     kDebug (13020) << "guarded variable line kate-mimetype: matched: " << s;
05530   }
05531   else // nothing found
05532   {
05533     return;
05534   }
05535 
05536   QStringList vvl; // view variable names
05537   vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
05538       << "line-numbers" << "icon-border" << "folding-markers"
05539       << "bookmark-sorting" << "auto-center-lines"
05540       << "icon-bar-color"
05541       // renderer
05542       << "background-color" << "selection-color"
05543       << "current-line-color" << "bracket-highlight-color"
05544       << "word-wrap-marker-color"
05545       << "font" << "font-size" << "scheme";
05546   int spaceIndent = -1;  // for backward compatibility; see below
05547   bool replaceTabsSet = false;
05548   int p( 0 );
05549 
05550   QString  var, val;
05551   while ( (p = kvVar.indexIn( s, p )) > -1 )
05552   {
05553     p += kvVar.matchedLength();
05554     var = kvVar.cap( 1 );
05555     val = kvVar.cap( 2 ).trimmed();
05556     bool state; // store booleans here
05557     int n; // store ints here
05558 
05559     // only apply view & renderer config stuff
05560     if (onlyViewAndRenderer)
05561     {
05562       if ( vvl.contains( var ) ) // FIXME define above
05563         setViewVariable( var, val );
05564     }
05565     else
05566     {
05567       // BOOL  SETTINGS
05568       if ( var == "word-wrap" && checkBoolValue( val, &state ) )
05569         setWordWrap( state ); // ??? FIXME CHECK
05570       // KateConfig::configFlags
05571       // FIXME should this be optimized to only a few calls? how?
05572       else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
05573         m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
05574       else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
05575       {
05576         m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
05577         replaceTabsSet = true;  // for backward compatibility; see below
05578       }
05579       else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
05580         m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
05581       else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
05582         m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
05583       else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
05584         m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
05585       else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
05586         m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
05587       else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
05588         m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
05589       else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
05590         m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
05591       else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
05592         m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
05593       else if ( var == "show-trailing-spaces" && checkBoolValue( val, &state ) )
05594         m_config->setConfigFlags( KateDocumentConfig::cfShowSpaces, state );
05595       else if ( var == "space-indent" && checkBoolValue( val, &state ) )
05596       {
05597         // this is for backward compatibility; see below
05598         spaceIndent = state;
05599       }
05600       else if ( var == "smart-home" && checkBoolValue( val, &state ) )
05601         m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
05602       else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
05603         m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
05604 
05605       // INTEGER SETTINGS
05606       else if ( var == "tab-width" && checkIntValue( val, &n ) )
05607         m_config->setTabWidth( n );
05608       else if ( var == "indent-width"  && checkIntValue( val, &n ) )
05609         m_config->setIndentationWidth( n );
05610       else if ( var == "indent-mode" )
05611       {
05612         m_config->setIndentationMode( val );
05613       }
05614       else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 ) // uint, but hard word wrap at 0 will be no fun ;)
05615         m_config->setWordWrapAt( n );
05616 
05617       // STRING SETTINGS
05618       else if ( var == "eol" || var == "end-of-line" )
05619       {
05620         QStringList l;
05621         l << "unix" << "dos" << "mac";
05622         if ( (n = l.indexOf( val.toLower() )) != -1 )
05623           m_config->setEol( n );
05624       }
05625       else if ( var == "encoding" )
05626         m_config->setEncoding( val );
05627       else if (var == "presave-postdialog")
05628         setPreSavePostDialogFilterChecks(val.split(','));
05629       else if (var == "postsave")
05630         setPostSaveFilterChecks(val.split(','));
05631       else if (var == "postload")
05632         setPostLoadFilterChecks(val.split(','));
05633       else if ( var == "syntax" || var == "hl" )
05634       {
05635         setHighlightingMode( val );
05636       }
05637       else if ( var == "mode" )
05638       {
05639         setMode( val );
05640       }
05641 
05642       // VIEW SETTINGS
05643       else if ( vvl.contains( var ) )
05644         setViewVariable( var, val );
05645       else
05646       {
05647         m_storedVariables.insert( var, val );
05648         emit variableChanged( this, var, val );
05649       }
05650     }
05651   }
05652 
05653   // Backward compatibility
05654   // If space-indent was set, but replace-tabs was not set, we assume
05655   // that the user wants to replace tabulators and set that flag.
05656   // If both were set, replace-tabs has precedence.
05657   // At this point spaceIndent is -1 if it was never set,
05658   // 0 if it was set to off, and 1 if it was set to on.
05659   // Note that if onlyViewAndRenderer was requested, spaceIndent is -1.
05660   if ( !replaceTabsSet && spaceIndent >= 0 )
05661   {
05662     m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, spaceIndent > 0 );
05663   }
05664 }
05665 
05666 void KateDocument::setViewVariable( QString var, QString val )
05667 {
05668   KateView *v;
05669   bool state;
05670   int n;
05671   QColor c;
05672   foreach (v,m_views)
05673   {
05674     if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
05675       v->config()->setDynWordWrap( state );
05676     else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
05677       v->config()->setPersistentSelection( state );
05678     else if ( var == "block-selection"  && checkBoolValue( val, &state ) )
05679           v->setBlockSelectionMode( state );
05680     //else if ( var = "dynamic-word-wrap-indicators" )
05681     else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
05682       v->config()->setLineNumbers( state );
05683     else if (var == "icon-border" && checkBoolValue( val, &state ) )
05684       v->config()->setIconBar( state );
05685     else if (var == "folding-markers" && checkBoolValue( val, &state ) )
05686       v->config()->setFoldingBar( state );
05687     else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
05688       v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
05689     else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
05690       v->renderer()->config()->setIconBarColor( c );
05691     // RENDERER
05692     else if ( var == "background-color" && checkColorValue( val, c ) )
05693       v->renderer()->config()->setBackgroundColor( c );
05694     else if ( var == "selection-color" && checkColorValue( val, c ) )
05695       v->renderer()->config()->setSelectionColor( c );
05696     else if ( var == "current-line-color" && checkColorValue( val, c ) )
05697       v->renderer()->config()->setHighlightedLineColor( c );
05698     else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
05699       v->renderer()->config()->setHighlightedBracketColor( c );
05700     else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
05701       v->renderer()->config()->setWordWrapMarkerColor( c );
05702     else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
05703     {
05704       QFont _f( v->renderer()->config()->font() );
05705 
05706       if ( var == "font" )
05707       {
05708         _f.setFamily( val );
05709         _f.setFixedPitch( QFont( val ).fixedPitch() );
05710       }
05711       else
05712         _f.setPointSize( n );
05713 
05714       v->renderer()->config()->setFont( _f );
05715     }
05716     else if ( var == "scheme" )
05717     {
05718       v->renderer()->config()->setSchema( val );
05719     }
05720   }
05721 }
05722 
05723 bool KateDocument::checkBoolValue( QString val, bool *result )
05724 {
05725   val = val.trimmed().toLower();
05726   QStringList l;
05727   l << "1" << "on" << "true";
05728   if ( l.contains( val ) )
05729   {
05730     *result = true;
05731     return true;
05732   }
05733   l.clear();
05734   l << "0" << "off" << "false";
05735   if ( l.contains( val ) )
05736   {
05737     *result = false;
05738     return true;
05739   }
05740   return false;
05741 }
05742 
05743 bool KateDocument::checkIntValue( QString val, int *result )
05744 {
05745   bool ret( false );
05746   *result = val.toInt( &ret );
05747   return ret;
05748 }
05749 
05750 bool KateDocument::checkColorValue( QString val, QColor &c )
05751 {
05752   c.setNamedColor( val );
05753   return c.isValid();
05754 }
05755 
05756 // KTextEditor::variable
05757 QString KateDocument::variable( const QString &name ) const
05758 {
05759   if ( m_storedVariables.contains( name ) )
05760     return m_storedVariables[ name ];
05761 
05762   return "";
05763 }
05764 
05765 //END
05766 
05767 void KateDocument::slotModOnHdDirty (const QString &path)
05768 {
05769   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskModified))
05770   {
05771     // compare md5 with the one we have (if we have one)
05772     if ( ! m_digest.isEmpty() )
05773     {
05774       QByteArray tmp;
05775       if ( createDigest( tmp ) && tmp == m_digest )
05776         return;
05777     }
05778 
05779     m_modOnHd = true;
05780     m_modOnHdReason = OnDiskModified;
05781 
05782     // reenable dialog if not running atm
05783     if (m_isasking == -1)
05784       m_isasking = false;
05785 
05786     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05787   }
05788 }
05789 
05790 void KateDocument::slotModOnHdCreated (const QString &path)
05791 {
05792   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskCreated))
05793   {
05794     m_modOnHd = true;
05795     m_modOnHdReason = OnDiskCreated;
05796 
05797     // reenable dialog if not running atm
05798     if (m_isasking == -1)
05799       m_isasking = false;
05800 
05801     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05802   }
05803 }
05804 
05805 void KateDocument::slotModOnHdDeleted (const QString &path)
05806 {
05807   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskDeleted))
05808   {
05809     m_modOnHd = true;
05810     m_modOnHdReason = OnDiskDeleted;
05811 
05812     // reenable dialog if not running atm
05813     if (m_isasking == -1)
05814       m_isasking = false;
05815 
05816     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05817   }
05818 }
05819 
05820 bool KateDocument::createDigest( QByteArray &result )
05821 {
05822   bool ret = false;
05823   result = "";
05824   if ( url().isLocalFile() )
05825   {
05826     QFile f ( url().path() );
05827     if ( f.open( QIODevice::ReadOnly) )
05828     {
05829       KMD5 md5;
05830       ret = md5.update( f );
05831       md5.hexDigest( result );
05832       f.close();
05833       ret = true;
05834     }
05835   }
05836   return ret;
05837 }
05838 
05839 QString KateDocument::reasonedMOHString() const
05840 {
05841   // squeeze path
05842   QString str = KStringHandler::csqueeze(url().pathOrUrl());
05843 
05844   switch( m_modOnHdReason )
05845   {
05846     case OnDiskModified:
05847       return i18n("The file '%1' was modified by another program.", str );
05848       break;
05849     case OnDiskCreated:
05850       return i18n("The file '%1' was created by another program.", str );
05851       break;
05852     case OnDiskDeleted:
05853       return i18n("The file '%1' was deleted by another program.", str );
05854       break;
05855     default:
05856       return QString();
05857   }
05858   return QString();
05859 }
05860 
05861 void KateDocument::removeTrailingSpace(int line)
05862 {
05863   // remove trailing spaces from left line if required
05864   if (m_blockRemoveTrailingSpaces
05865       || !(config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn))
05866     return;
05867 
05868   KateTextLine::Ptr ln = plainKateTextLine(line);
05869 
05870   if (!ln || ln->length() == 0)
05871     return;
05872 
05873   if (line == activeView()->cursorPosition().line()
05874       && activeView()->cursorPosition().column() >= qMax(0, ln->lastChar()))
05875     return;
05876 
05877   const int p = ln->lastChar() + 1;
05878   const int l = ln->length() - p;
05879   if (l > 0) {
05880     m_blockRemoveTrailingSpaces = true;
05881     editRemoveText(line, p, l);
05882     m_blockRemoveTrailingSpaces = false;
05883   }
05884 }
05885 
05886 void KateDocument::updateFileType (const QString &newType, bool user)
05887 {
05888   if (user || !m_fileTypeSetByUser)
05889   {
05890     if (!newType.isEmpty())
05891     {
05892           m_fileType = newType;
05893 
05894           m_config->configStart();
05895 
05896           if (!hlSetByUser && !KateGlobal::self()->modeManager()->fileType(newType).hl.isEmpty())
05897           {
05898             int hl (KateHlManager::self()->nameFind (KateGlobal::self()->modeManager()->fileType(newType).hl));
05899 
05900             if (hl >= 0)
05901               m_buffer->setHighlight(hl);
05902           }
05903 
05904 
05905           // views!
05906           KateView *v;
05907           foreach (v,m_views)
05908           {
05909             v->config()->configStart();
05910             v->renderer()->config()->configStart();
05911           }
05912 
05913           readVariableLine( KateGlobal::self()->modeManager()->fileType(newType).varLine );
05914 
05915           m_config->configEnd();
05916           foreach (v,m_views)
05917           {
05918             v->config()->configEnd();
05919             v->renderer()->config()->configEnd();
05920           }
05921     }
05922   }
05923 
05924   // fixme, make this better...
05925   emit modeChanged (this);
05926 }
05927 
05928 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
05929       *handled=true;
05930       *abortClosing=true;
05931       if (this->url().isEmpty())
05932       {
05933         QWidget *parentWidget(dialogParent());
05934 
05935         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(),
05936                 QString(),QString(),parentWidget,i18n("Save File"));
05937 
05938         if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) ) {
05939                 *abortClosing=true;
05940                 return;
05941         }
05942         setEncoding( res.encoding );
05943           saveAs( res.URLs.first() );
05944         *abortClosing=false;
05945       }
05946       else
05947       {
05948           save();
05949           *abortClosing=false;
05950       }
05951 
05952 }
05953 
05954 bool KateDocument::checkOverwrite( KUrl u, QWidget *parent )
05955 {
05956   if( !u.isLocalFile() )
05957     return true;
05958 
05959   QFileInfo info( u.path() );
05960   if( !info.exists() )
05961     return true;
05962 
05963   return KMessageBox::Cancel != KMessageBox::warningContinueCancel( parent,
05964     i18n( "A file named \"%1\" already exists. "
05965           "Are you sure you want to overwrite it?" ,  info.fileName() ),
05966     i18n( "Overwrite File?" ),
05967     KGuiItem(i18n( "&Overwrite" )) );
05968 }
05969 
05970 
05971 //BEGIN KTextEditor::TemplateInterface
05972 bool KateDocument::insertTemplateTextImplementation ( const KTextEditor::Cursor &c, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
05973       return (new KateTemplateHandler(this,c,templateString,initialValues))->initOk();
05974 }
05975 
05976 void KateDocument::testTemplateCode() {
05977   //qobject_cast<KTextEditor::TemplateInterface*>(activeView())->insertTemplateText(activeView()->cursorPosition(),"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05978   qobject_cast<KTextEditor::TemplateInterface*>(activeView())->insertTemplateText(activeView()->cursorPosition(),"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05979 }
05980 
05981 
05982 bool KateDocument::invokeTabInterceptor(int key) {
05983   if (m_tabInterceptor) return (*m_tabInterceptor)(key);
05984   return false;
05985 }
05986 
05987 bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05988   if (m_tabInterceptor) return false;
05989   m_tabInterceptor=interceptor;
05990   return true;
05991 }
05992 
05993 bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05994   if (m_tabInterceptor!=interceptor) return false;
05995   m_tabInterceptor=0;
05996   return true;
05997 }
05998 
05999 KateView * KateDocument::activeKateView( ) const
06000 {
06001   return static_cast<KateView*>(m_activeView);
06002 }
06003 
06004 KTextEditor::Cursor KateDocument::documentEnd( ) const
06005 {
06006   return KTextEditor::Cursor(lastLine(), lineLength(lastLine()));
06007 }
06008 //END KTextEditor::TemplateInterface
06009 
06010 //BEGIN KTextEditor::SmartInterface
06011 int KateDocument::currentRevision() const
06012 {
06013   return m_smartManager->currentRevision();
06014 }
06015 
06016 void KateDocument::releaseRevision(int revision) const
06017 {
06018   m_smartManager->releaseRevision(revision);
06019 }
06020 
06021 void KateDocument::useRevision(int revision)
06022 {
06023   m_smartManager->useRevision(revision);
06024 }
06025 
06026 KTextEditor::Cursor KateDocument::translateFromRevision(const KTextEditor::Cursor& cursor, KTextEditor::SmartCursor::InsertBehavior insertBehavior) const
06027 {
06028   return m_smartManager->translateFromRevision(cursor, insertBehavior);
06029 }
06030 
06031 KTextEditor::Range KateDocument::translateFromRevision(const KTextEditor::Range& range, KTextEditor::SmartRange::InsertBehaviors insertBehavior) const
06032 {
06033   return m_smartManager->translateFromRevision(range, insertBehavior);
06034 }
06035 
06036 KTextEditor::SmartCursor* KateDocument::newSmartCursor( const KTextEditor::Cursor & position, KTextEditor::SmartCursor::InsertBehavior insertBehavior )
06037 {
06038   return m_smartManager->newSmartCursor(position, insertBehavior, false);
06039 }
06040 
06041 KTextEditor::SmartRange * KateDocument::newSmartRange( const KTextEditor::Range & range, KTextEditor::SmartRange * parent, KTextEditor::SmartRange::InsertBehaviors insertBehavior )
06042 {
06043   return m_smartManager->newSmartRange( range, parent, insertBehavior, false );
06044 }
06045 
06046 KTextEditor::SmartRange * KateDocument::newSmartRange( KTextEditor::SmartCursor * start, KTextEditor::SmartCursor * end, KTextEditor::SmartRange * parent, KTextEditor::SmartRange::InsertBehaviors insertBehavior )
06047 {
06048   KateSmartCursor* kstart = dynamic_cast<KateSmartCursor*>(start);
06049   KateSmartCursor* kend = dynamic_cast<KateSmartCursor*>(end);
06050   if (!kstart || !kend)
06051     return 0L;
06052   if (kstart->range() || kend->range())
06053     return 0L;
06054   return m_smartManager->newSmartRange(kstart, kend, parent, insertBehavior, false);
06055 }
06056 
06057 void KateDocument::unbindSmartRange( KTextEditor::SmartRange * range )
06058 {
06059   m_smartManager->unbindSmartRange(range);
06060 }
06061 
06062 bool KateDocument::replaceText( const KTextEditor::Range & range, const QString & s, bool block )
06063 {
06064   // TODO more efficient?
06065   editStart();
06066   bool changed = removeText(range, block);
06067   changed |= insertText(range.start(), s, block);
06068   editEnd();
06069   return changed;
06070 }
06071 
06072 void KateDocument::addHighlightToDocument( KTextEditor::SmartRange * topRange, bool supportDynamic )
06073 {
06074   if (m_documentHighlights.contains(topRange))
06075     return;
06076 
06077   m_documentHighlights.append(topRange);
06078 
06079   // Deal with the range being deleted externally
06080   topRange->addWatcher(this);
06081 
06082   if (supportDynamic) {
06083     m_documentDynamicHighlights.append(topRange);
06084     emit dynamicHighlightAdded(static_cast<KateSmartRange*>(topRange));
06085   }
06086 
06087   foreach (KateView * view, m_views)
06088     view->addExternalHighlight(topRange, supportDynamic);
06089 }
06090 
06091 void KateDocument::removeHighlightFromDocument( KTextEditor::SmartRange * topRange )
06092 {
06093   if (!m_documentHighlights.contains(topRange))
06094     return;
06095 
06096   foreach (KateView * view, m_views)
06097     view->removeExternalHighlight(topRange);
06098 
06099   m_documentHighlights.removeAll(topRange);
06100   topRange->removeWatcher(this);
06101 
06102   if (m_documentDynamicHighlights.contains(topRange)) {
06103     m_documentDynamicHighlights.removeAll(topRange);
06104     emit dynamicHighlightRemoved(static_cast<KateSmartRange*>(topRange));
06105   }
06106 }
06107 
06108 const QList< KTextEditor::SmartRange * > KateDocument::documentHighlights( ) const
06109 {
06110   return m_documentHighlights;
06111 }
06112 
06113 void KateDocument::addHighlightToView( KTextEditor::View * view, KTextEditor::SmartRange * topRange, bool supportDynamic )
06114 {
06115   static_cast<KateView*>(view)->addExternalHighlight(topRange, supportDynamic);
06116 }
06117 
06118 void KateDocument::removeHighlightFromView( KTextEditor::View * view, KTextEditor::SmartRange * topRange )
06119 {
06120   static_cast<KateView*>(view)->removeExternalHighlight(topRange);
06121 }
06122 
06123 const QList< KTextEditor::SmartRange * > KateDocument::viewHighlights( KTextEditor::View * view ) const
06124 {
06125   return static_cast<KateView*>(view)->externalHighlights();
06126 }
06127 
06128 void KateDocument::addActionsToDocument( KTextEditor::SmartRange * topRange )
06129 {
06130   if (m_documentActions.contains(topRange))
06131     return;
06132 
06133   m_documentActions.append(topRange);
06134 
06135   // Deal with the range being deleted externally
06136   topRange->addWatcher(this);
06137 }
06138 
06139 void KateDocument::removeActionsFromDocument( KTextEditor::SmartRange * topRange )
06140 {
06141   if (!m_documentActions.contains(topRange))
06142     return;
06143 
06144   m_documentActions.removeAll(topRange);
06145   topRange->removeWatcher(this);
06146 }
06147 
06148 const QList< KTextEditor::SmartRange * > KateDocument::documentActions( ) const
06149 {
06150   return m_documentActions;
06151 }
06152 
06153 void KateDocument::addActionsToView( KTextEditor::View * view, KTextEditor::SmartRange * topRange )
06154 {
06155   static_cast<KateView*>(view)->addActions(topRange);
06156 }
06157 
06158 void KateDocument::removeActionsFromView( KTextEditor::View * view, KTextEditor::SmartRange * topRange )
06159 {
06160   static_cast<KateView*>(view)->removeActions(topRange);
06161 }
06162 
06163 const QList< KTextEditor::SmartRange * > KateDocument::viewActions( KTextEditor::View * view ) const
06164 {
06165   return static_cast<KateView*>(view)->actions();
06166 }
06167 
06168 void KateDocument::attributeDynamic( KTextEditor::Attribute::Ptr )
06169 {
06170   // TODO link in with cursor + mouse tracking
06171 }
06172 
06173 void KateDocument::attributeNotDynamic( KTextEditor::Attribute::Ptr )
06174 {
06175   // TODO de-link cursor + mouse tracking
06176 }
06177 
06178 void KateDocument::clearSmartInterface( )
06179 {
06180   clearDocumentHighlights();
06181   foreach (KateView* view, m_views)
06182     clearViewHighlights(view);
06183 
06184   clearDocumentActions();
06185 
06186   m_smartManager->clear(false);
06187 }
06188 
06189 void KateDocument::deleteCursors( )
06190 {
06191   m_smartManager->deleteCursors(false);
06192 }
06193 
06194 void KateDocument::deleteRanges( )
06195 {
06196   m_smartManager->deleteRanges(false);
06197 }
06198 
06199 void KateDocument::clearDocumentHighlights( )
06200 {
06201   m_documentHighlights.clear();
06202 }
06203 
06204 void KateDocument::clearViewHighlights( KTextEditor::View * view )
06205 {
06206   static_cast<KateView*>(view)->clearExternalHighlights();
06207 }
06208 
06209 void KateDocument::clearDocumentActions( )
06210 {
06211   m_documentActions.clear();
06212 }
06213 
06214 void KateDocument::clearViewActions( KTextEditor::View * view )
06215 {
06216   static_cast<KateView*>(view)->clearActions();
06217 }
06218 
06219 void KateDocument::ignoreModifiedOnDiskOnce( )
06220 {
06221   m_isasking = -1;
06222 }
06223 
06224 KateHighlighting * KateDocument::highlight( ) const
06225 {
06226   return m_buffer->highlight();
06227 }
06228 
06229 uint KateDocument::getRealLine( unsigned int virtualLine )
06230 {
06231   return m_buffer->lineNumber (virtualLine);
06232 }
06233 
06234 uint KateDocument::getVirtualLine( unsigned int realLine )
06235 {
06236   return m_buffer->lineVisibleNumber (realLine);
06237 }
06238 
06239 uint KateDocument::visibleLines( )
06240 {
06241   return m_buffer->countVisible ();
06242 }
06243 
06244 KateTextLine::Ptr KateDocument::kateTextLine( uint i )
06245 {
06246   m_buffer->ensureHighlighted (i);
06247   return m_buffer->plainLine (i);
06248 }
06249 
06250 KateTextLine::Ptr KateDocument::plainKateTextLine( uint i )
06251 {
06252   return m_buffer->plainLine (i);
06253 }
06254 
06255 bool KateDocument::undoDontMerge( ) const
06256 {
06257   return m_undoDontMerge;
06258 }
06259 
06260 void KateDocument::setUndoDontMergeComplex(bool dontMerge)
06261 {
06262   m_undoComplexMerge = dontMerge;
06263 }
06264 
06265 bool KateDocument::undoDontMergeComplex( ) const
06266 {
06267   return m_undoComplexMerge;
06268 }
06269 
06270 void KateDocument::setUndoDontMerge(bool dontMerge)
06271 {
06272   m_undoDontMerge = dontMerge;
06273 }
06274 
06275 bool KateDocument::isEditRunning() const
06276 {
06277   return editIsRunning;
06278 }
06279 
06280 void KateDocument::rangeDeleted( KTextEditor::SmartRange * range )
06281 {
06282   removeHighlightFromDocument(range);
06283   removeActionsFromDocument(range);
06284 }
06285 
06286 //END KTextEditor::SmartInterface
06287 
06288 // kate: space-indent on; indent-width 2; replace-tabs on;
06289 
06290 bool KateDocument::simpleMode ()
06291 {
06292   return KateGlobal::self()->simpleMode () && KateGlobal::self()->documentConfig()->allowSimpleMode ();
06293 }
06294 
06295 KateDocument::LoadSaveFilterCheckPlugins* KateDocument::loadSaveFilterCheckPlugins()
06296 {
06297   K_GLOBAL_STATIC(KateDocument::LoadSaveFilterCheckPlugins, s_loadSaveFilterCheckPlugins)
06298   return s_loadSaveFilterCheckPlugins;
06299 }
06300 
06301 //BEGIN KTextEditor::AnnotationInterface
06302 void KateDocument::setAnnotationModel( KTextEditor::AnnotationModel* model )
06303 {
06304   KTextEditor::AnnotationModel* oldmodel = m_annotationModel;
06305   m_annotationModel = model;
06306   emit annotationModelChanged(oldmodel, m_annotationModel);
06307 }
06308 
06309 KTextEditor::AnnotationModel* KateDocument::annotationModel() const
06310 {
06311   return m_annotationModel;
06312 }
06313 //END KTextEditor::AnnotationInterface
06314 
06315 //TAKEN FROM kparts.h
06316 bool KateDocument::queryClose()
06317 {
06318     if ( !isReadWrite() || !isModified() )
06319         return true;
06320 
06321     QString docName = documentName();
06322 
06323     QWidget *parentWidget=widget();
06324     if(!parentWidget) parentWidget=QApplication::activeWindow();
06325 
06326     int res = KMessageBox::warningYesNoCancel( parentWidget,
06327                                                i18n( "The document \"%1\" has been modified.\n"
06328                                                      "Do you want to save your changes or discard them?" ,  docName ),
06329                                                i18n( "Close Document" ), KStandardGuiItem::save(), KStandardGuiItem::discard() );
06330 
06331     bool abortClose=false;
06332     bool handled=false;
06333 
06334     switch(res) {
06335     case KMessageBox::Yes :
06336         sigQueryClose(&handled,&abortClose);
06337         if (!handled)
06338         {
06339             if (url().isEmpty())
06340             {
06341                 KUrl url = KFileDialog::getSaveUrl(KUrl(), QString(), parentWidget);
06342                 if (url.isEmpty())
06343                     return false;
06344 
06345                 saveAs( url );
06346             }
06347             else
06348             {
06349                 save();
06350             }
06351         } else if (abortClose) return false;
06352         return waitSaveComplete();
06353     case KMessageBox::No :
06354         return true;
06355     default : // case KMessageBox::Cancel :
06356         return false;
06357     }
06358 }
06359 
06360 
06361 void KateDocument::slotCanceled() {
06362   m_savingToUrl=false;
06363   m_saveAs=false;
06364 }
06365 
06366 void KateDocument::slotCompleted() {
06367   if (m_savingToUrl) {
06368     if (!m_postSaveFilterChecks.isEmpty())
06369     {
06370       LoadSaveFilterCheckPlugins *lscps1=loadSaveFilterCheckPlugins();
06371       foreach(const QString& checkplugin, m_postSaveFilterChecks)
06372       {
06373         if (lscps1->postSaveFilterCheck(checkplugin,this,m_saveAs)==false)
06374           break;
06375       }
06376     }
06377     emit documentSavedOrUploaded(this,m_saveAs);
06378   }
06379   m_savingToUrl=false;
06380   m_saveAs=false;
06381 }
06382 
06383 bool KateDocument::save() {
06384   m_saveAs = false;
06385   return KTextEditor::Document::save();
06386 }
06387 
06388 bool KateDocument::saveAs( const KUrl &url ) {
06389   m_saveAs = true;
06390   return KTextEditor::Document::saveAs(url);
06391 }
06392 
06393 // Kill our helpers again
06394 #ifdef FAST_DEBUG_ENABLE
06395 # undef FAST_DEBUG_ENABLE
06396 #endif
06397 #undef FAST_DEBUG
06398 
06399 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

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

kdelibs

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