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