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

Kate

katedocument.cpp

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