make sure that the returned default style has the correct background
[kate:kate.git] / part / document / katedocument.cpp
1 /* This file is part of the KDE libraries
2    Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
3    Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
4    Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
5    Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
6    Copyright (C) 2007 Mirko Stocker <me@misto.ch>
7    Copyright (C) 2009-2010 Michel Ludwig <michel.ludwig@kdemail.net>
8
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Library General Public
11    License version 2 as published by the Free Software Foundation.
12
13    This library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Library General Public License for more details.
17
18    You should have received a copy of the GNU Library General Public License
19    along with this library; see the file COPYING.LIB.  If not, write to
20    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21    Boston, MA 02111-13020, USA.
22 */
23
24 //BEGIN includes
25 #include "katedocument.h"
26 #include "katedocument.moc"
27 #include "kateglobal.h"
28 #include "katedialogs.h"
29 #include "katehighlight.h"
30 #include "kateview.h"
31 #include "kateautoindent.h"
32 #include "katetextline.h"
33 #include "katedocumenthelpers.h"
34 #include "kateprinter.h"
35 #include "katerenderer.h"
36 #include "kateregexp.h"
37 #include "kateplaintextsearch.h"
38 #include "kateregexpsearch.h"
39 #include "kateconfig.h"
40 #include "katemodemanager.h"
41 #include "kateschema.h"
42 #include "katetemplatehandler.h"
43 #include "katebuffer.h"
44 #include "kateundomanager.h"
45 #include "katepartpluginmanager.h"
46 #include "katevireplacemode.h"
47 #include "spellcheck/prefixstore.h"
48 #include "spellcheck/ontheflycheck.h"
49 #include "spellcheck/spellcheck.h"
50 #include "katescriptmanager.h"
51 #include "kateswapfile.h"
52
53 #include <ktexteditor/attribute.h>
54 #include <ktexteditor/plugin.h>
55 #include <ktexteditor/loadsavefiltercheckplugin.h>
56
57 #include <kio/job.h>
58 #include <kio/jobuidelegate.h>
59 #include <kio/netaccess.h>
60 #include <kfileitem.h>
61
62 #include <klocale.h>
63 #include <kglobal.h>
64 #include <kmenu.h>
65 #include <kconfig.h>
66 #include <kfiledialog.h>
67 #include <kmessagebox.h>
68 #include <kstandardaction.h>
69 #include <kxmlguifactory.h>
70 #include <kdebug.h>
71 #include <kglobalsettings.h>
72 #include <klibloader.h>
73 #include <kdirwatch.h>
74 #include <kencodingfiledialog.h>
75 #include <kcodecs.h>
76 #include <kstandarddirs.h>
77 #include <kstringhandler.h>
78
79 #include <kservicetypetrader.h>
80
81 #include <QtDBus/QtDBus>
82 #include <QtGui/QApplication>
83 #include <QtCore/QTimer>
84 #include <QtCore/QFile>
85 #include <QtGui/QClipboard>
86 #include <QtCore/QTextStream>
87 #include <QtCore/QTextCodec>
88 #include <QtCore/QMap>
89 //END  includes
90
91
92 static int dummy = 0;
93
94 class KateTemplateScript;
95
96 class KateDocument::LoadSaveFilterCheckPlugins
97 {
98   public:
99     LoadSaveFilterCheckPlugins() {
100       KService::List traderList = KServiceTypeTrader::self()->query("KTextEditor/LoadSaveFilterCheckPlugin");
101
102       foreach(const KService::Ptr &ptr, traderList)
103       {
104         QString libname;
105         libname=ptr->library();
106         libname=libname.right(libname.length()-12); //ktexteditor_ == 12
107         m_plugins[libname]=0;//new KatePythonEncodingCheck();
108         m_plugins2Service[libname] = ptr;
109       }
110
111     }
112     ~LoadSaveFilterCheckPlugins() {
113       if ( m_plugins.count()==0) return;
114       QHashIterator<QString,KTextEditor::LoadSaveFilterCheckPlugin*>i(m_plugins);
115         while (i.hasNext())
116           i.next();
117           delete i.value();
118       m_plugins.clear();
119     }
120     bool preSavePostDialogFilterCheck(const QString& pluginName,KateDocument *document,QWidget *parentWidget) {
121       KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
122       if (!plug) {
123         if (KMessageBox::warningContinueCancel (parentWidget
124         , i18n ("The filter/check plugin '%1' could not be found, still continue saving of %2", pluginName,document->url().pathOrUrl())
125         , i18n ("Saving problems")
126         , KGuiItem(i18n("Save Nevertheless"))
127         , KStandardGuiItem::cancel()) != KMessageBox::Continue)
128           return false;
129         else
130           return true;
131       }
132       return plug->preSavePostDialogFilterCheck(document);
133     }
134     void postLoadFilter(const QString& pluginName,KateDocument *document) {
135       KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
136       if (!plug) return;
137       plug->postLoadFilter(document);
138     }
139     bool postSaveFilterCheck(const QString& pluginName,KateDocument *document,bool saveas) {
140       KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
141       if (!plug) return false;
142       return plug->postSaveFilterCheck(document,saveas);
143     }
144   private:
145     KTextEditor::LoadSaveFilterCheckPlugin *getPlugin(const QString & pluginName)
146     {
147       if (!m_plugins.contains(pluginName)) return 0;
148       if (!m_plugins[pluginName]) {
149         m_plugins[pluginName]=m_plugins2Service[pluginName]->createInstance<KTextEditor::LoadSaveFilterCheckPlugin>();
150       }
151       return m_plugins[pluginName];
152     }
153     QHash <QString,KTextEditor::LoadSaveFilterCheckPlugin*> m_plugins;
154     QHash <QString, KService::Ptr> m_plugins2Service;
155 };
156
157 //BEGIN d'tor, c'tor
158 //
159 // KateDocument Constructor
160 //
161 KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView,
162                              bool bReadOnly, QWidget *parentWidget,
163                              QObject *parent)
164 : KTextEditor::Document (parent),
165   m_bSingleViewMode(bSingleViewMode),
166   m_bBrowserView(bBrowserView),
167   m_bReadOnly(bReadOnly),
168   m_activeView(0),
169   editSessionNumber(0),
170   editIsRunning(false),
171   m_undoManager(new KateUndoManager(this)),
172   m_editableMarks(markType01),
173   m_annotationModel(0),
174   m_saveAs(false),
175   m_isasking(0),
176   m_blockRemoveTrailingSpaces(false),
177   m_buffer(new KateBuffer(this)),
178   m_indenter(new KateAutoIndent(this)),
179   hlSetByUser(false),
180   m_bomSetByUser(false),
181   m_modOnHd(false),
182   m_modOnHdReason(OnDiskUnmodified),
183   m_docName("need init"),
184   m_docNameNumber(0),
185   m_fileTypeSetByUser(false),
186   m_reloading(false),
187   m_config(new KateDocumentConfig(this)),
188   m_fileChangedDialogsActivated(false),
189   m_savingToUrl(false),
190   m_onTheFlyChecker(0)
191 {
192   setComponentData ( KateGlobal::self()->componentData () );
193
194   QString pathName ("/Kate/Document/%1");
195   pathName = pathName.arg (++dummy);
196
197   // my dbus object
198   QDBusConnection::sessionBus().registerObject (pathName, this);
199
200   // register doc at factory
201   KateGlobal::self()->registerDocument(this);
202
203   // normal hl
204   m_buffer->setHighlight (0);
205
206   // swap file
207   m_swapfile = new Kate::SwapFile(this);
208     
209   new KateBrowserExtension( this ); // deleted by QObject memory management
210
211   // important, fill in the config into the indenter we use...
212   m_indenter->updateConfig ();
213
214   // some nice signals from the buffer
215   connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int)));
216   connect(m_buffer, SIGNAL(respellCheckBlock(int, int)), this , SLOT(respellCheckBlock(int, int)));
217   connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated()));
218
219   // if the user changes the highlight with the dialog, notify the doc
220   connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged()));
221
222   // signals for mod on hd
223   connect( KateGlobal::self()->dirWatch(), SIGNAL(dirty (const QString &)),
224            this, SLOT(slotModOnHdDirty (const QString &)) );
225
226   connect( KateGlobal::self()->dirWatch(), SIGNAL(created (const QString &)),
227            this, SLOT(slotModOnHdCreated (const QString &)) );
228
229   connect( KateGlobal::self()->dirWatch(), SIGNAL(deleted (const QString &)),
230            this, SLOT(slotModOnHdDeleted (const QString &)) );
231
232   connect (this,SIGNAL(completed()),this,SLOT(slotCompleted()));
233   connect (this,SIGNAL(canceled(const QString&)),this,SLOT(slotCanceled()));
234   // update doc name
235   setDocName (QString());
236
237   // if single view mode, like in the konqui embedding, create a default view ;)
238   // be lazy, only create it now, if any parentWidget is given, otherwise widget()
239   // will create it on demand...
240   if ( m_bSingleViewMode && parentWidget )
241   {
242     KTextEditor::View *view = (KTextEditor::View*)createView( parentWidget );
243     insertChildClient( view );
244     view->show();
245     setWidget( view );
246   }
247
248   connect(m_undoManager, SIGNAL(undoChanged()), this, SIGNAL(undoChanged()));
249   connect(m_undoManager, SIGNAL(undoStart(KTextEditor::Document*)),   this, SIGNAL(exclusiveEditStart(KTextEditor::Document*)));
250   connect(m_undoManager, SIGNAL(undoEnd(KTextEditor::Document*)),     this, SIGNAL(exclusiveEditEnd(KTextEditor::Document*)));
251   connect(m_undoManager, SIGNAL(redoStart(KTextEditor::Document*)),   this, SIGNAL(exclusiveEditStart(KTextEditor::Document*)));
252   connect(m_undoManager, SIGNAL(redoEnd(KTextEditor::Document*)),     this, SIGNAL(exclusiveEditEnd(KTextEditor::Document*)));
253
254   connect(this,SIGNAL(sigQueryClose(bool *, bool*)),this,SLOT(slotQueryClose_save(bool *, bool*)));
255
256   onTheFlySpellCheckingEnabled(config()->onTheFlySpellCheck());
257
258   // register document in plugins
259   KatePartPluginManager::self()->addDocument(this);
260 }
261
262 //
263 // KateDocument Destructor
264 //
265 KateDocument::~KateDocument()
266 {
267   /**
268    * we are about to delete cursors/ranges/...
269    */
270   emit aboutToDeleteMovingInterfaceContent (this);
271
272   // kill it early, it has ranges!
273   delete m_onTheFlyChecker;
274   m_onTheFlyChecker = NULL;
275
276   clearDictionaryRanges();
277
278   // Tell the world that we're about to close (== destruct)
279   // Apps must receive this in a direct signal-slot connection, and prevent
280   // any further use of interfaces once they return.
281   emit aboutToClose(this);
282
283   // remove file from dirwatch
284   deactivateDirWatch ();
285
286   // thanks for offering, KPart, but we're already self-destructing
287   setAutoDeleteWidget(false);
288   setAutoDeletePart(false);
289
290   // clean up remaining views
291   while (!m_views.isEmpty()) {
292     delete m_views.takeFirst();
293   }
294
295   // de-register from plugin
296   KatePartPluginManager::self()->removeDocument(this);
297
298   // cu marks
299   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
300     delete i.value();
301   m_marks.clear();
302
303   delete m_config;
304   KateGlobal::self()->deregisterDocument (this);
305 }
306 //END
307
308 // on-demand view creation
309 QWidget *KateDocument::widget()
310 {
311   // no singleViewMode -> no widget()...
312   if (!singleViewMode())
313     return 0;
314
315   // does a widget exist already? use it!
316   if (KTextEditor::Document::widget())
317     return KTextEditor::Document::widget();
318
319   // create and return one...
320   KTextEditor::View *view = (KTextEditor::View*)createView(0);
321   insertChildClient( view );
322   setWidget( view );
323   return view;
324 }
325
326 //BEGIN KTextEditor::Document stuff
327
328 KTextEditor::View *KateDocument::createView( QWidget *parent )
329 {
330   KateView* newView = new KateView( this, parent);
331   if ( m_fileChangedDialogsActivated )
332     connect( newView, SIGNAL(focusIn( KTextEditor::View * )), this, SLOT(slotModifiedOnDisk()) );
333
334   emit viewCreated (this, newView);
335
336   return newView;
337 }
338
339 const QList<KTextEditor::View*> &KateDocument::views () const
340 {
341   return m_textEditViews;
342 }
343
344 KTextEditor::Editor *KateDocument::editor ()
345 {
346   return KateGlobal::self();
347 }
348
349 KTextEditor::Range KateDocument::rangeOnLine(KTextEditor::Range range, int line) const
350 {
351   int col1 = const_cast<KateDocument*>(this)->toVirtualColumn(range.start());
352   int col2 = const_cast<KateDocument*>(this)->toVirtualColumn(range.end());
353
354   Kate::TextLine tl = const_cast<KateDocument*>(this)->kateTextLine(line);
355   col1 = tl->fromVirtualColumn(col1, config()->tabWidth());
356   col2 = tl->fromVirtualColumn(col2, config()->tabWidth());
357
358   return KTextEditor::Range(line, col1, line, col2);
359 }
360
361 //BEGIN KTextEditor::EditInterface stuff
362
363 QString KateDocument::text() const
364 {
365   return m_buffer->text ();
366 }
367
368 QString KateDocument::text( const KTextEditor::Range& range, bool blockwise ) const
369 {
370   if (!range.isValid()) {
371     kWarning() << k_funcinfo << "Text requested for invalid range" << range;
372     return QString();
373   }
374
375   QString s;
376
377   if (range.start().line() == range.end().line())
378   {
379     if (range.start().column() > range.end().column())
380       return QString ();
381
382     Kate::TextLine textLine = m_buffer->plainLine(range.start().line());
383
384     if ( !textLine )
385       return QString ();
386
387     return textLine->string(range.start().column(), range.end().column()-range.start().column());
388   }
389   else
390   {
391
392     for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
393     {
394       Kate::TextLine textLine = m_buffer->plainLine(i);
395
396       if ( !blockwise )
397       {
398         if (i == range.start().line())
399           s.append (textLine->string(range.start().column(), textLine->length()-range.start().column()));
400         else if (i == range.end().line())
401           s.append (textLine->string(0, range.end().column()));
402         else
403           s.append (textLine->string());
404       }
405       else
406       {
407         KTextEditor::Range subRange = rangeOnLine(range, i);
408         s.append(textLine->string(subRange.start().column(), subRange.columnWidth()));
409       }
410
411       if ( i < range.end().line() )
412         s.append(QChar::fromAscii('\n'));
413     }
414   }
415
416   return s;
417 }
418
419 QChar KateDocument::character( const KTextEditor::Cursor & position ) const
420 {
421   Kate::TextLine textLine = m_buffer->plainLine(position.line());
422
423   if ( !textLine )
424     return QChar();
425
426   if (position.column() >= 0 && position.column() < textLine->string().length())
427     return textLine->string().at(position.column());
428
429   return QChar();
430 }
431
432 QStringList KateDocument::textLines( const KTextEditor::Range & range, bool blockwise ) const
433 {
434   QStringList ret;
435
436   if (!range.isValid()) {
437     kWarning() << k_funcinfo << "Text requested for invalid range" << range;
438     return ret;
439   }
440
441   if ( blockwise && (range.start().column() > range.end().column()) )
442     return ret;
443
444   if (range.start().line() == range.end().line())
445   {
446     Q_ASSERT(range.start() <= range.end());
447
448     Kate::TextLine textLine = m_buffer->plainLine(range.start().line());
449
450     if ( !textLine )
451       return ret;
452
453     ret << textLine->string(range.start().column(), range.end().column() - range.start().column());
454   }
455   else
456   {
457     for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
458     {
459       Kate::TextLine textLine = m_buffer->plainLine(i);
460
461       if ( !blockwise )
462       {
463         if (i == range.start().line())
464           ret << textLine->string(range.start().column(), textLine->length() - range.start().column());
465         else if (i == range.end().line())
466           ret << textLine->string(0, range.end().column());
467         else
468           ret << textLine->string();
469       }
470       else
471       {
472         KTextEditor::Range subRange = rangeOnLine(range, i);
473         ret << textLine->string(subRange.start().column(), subRange.columnWidth());
474       }
475     }
476   }
477
478   return ret;
479 }
480
481 QString KateDocument::line( int line ) const
482 {
483   Kate::TextLine l = m_buffer->plainLine(line);
484
485   if (!l)
486     return QString();
487
488   return l->string();
489 }
490
491 bool KateDocument::setText(const QString &s)
492 {
493   if (!isReadWrite())
494     return false;
495
496   QList<KTextEditor::Mark> msave;
497
498   foreach (KTextEditor::Mark* mark, m_marks)
499     msave.append(*mark);
500
501   editStart ();
502
503   // delete the text
504   clear();
505
506   // insert the new text
507   insertText (KTextEditor::Cursor(), s);
508
509   editEnd ();
510
511   foreach (const KTextEditor::Mark& mark, msave)
512     setMark (mark.line, mark.type);
513
514   return true;
515 }
516
517 bool KateDocument::setText( const QStringList & text )
518 {
519   if (!isReadWrite())
520     return false;
521
522   QList<KTextEditor::Mark> msave;
523
524   foreach (KTextEditor::Mark* mark, m_marks)
525     msave.append(*mark);
526
527   editStart ();
528
529   // delete the text
530   clear();
531
532   // insert the new text
533   insertText (KTextEditor::Cursor::start(), text);
534
535   editEnd ();
536
537   foreach (const KTextEditor::Mark& mark, msave)
538     setMark (mark.line, mark.type);
539
540   return true;
541 }
542
543 bool KateDocument::clear()
544 {
545   if (!isReadWrite())
546     return false;
547
548   foreach (KateView *view, m_views) {
549     view->clear();
550     view->tagAll();
551     view->update();
552   }
553
554   clearMarks ();
555
556   return removeText (KTextEditor::Range(KTextEditor::Cursor(), KTextEditor::Cursor(lastLine()+1, 0)));
557 }
558
559 bool KateDocument::insertText( const KTextEditor::Cursor& position, const QString& text, bool block )
560 {
561   if (!isReadWrite())
562     return false;
563
564   if (text.isEmpty())
565     return true;
566
567   editStart();
568
569   int currentLine = position.line();
570   int currentLineStart = 0;
571   int totalLength = text.length();
572   int insertColumn = position.column();
573
574   if (position.line() > lines())
575   {
576     int line = lines();
577     while (line != position.line() + totalLength + 1)
578     {
579       editInsertLine(line,QString());
580       line++;
581     }
582   }
583
584   bool replacetabs = ( config()->replaceTabsDyn() );
585   int tabWidth = config()->tabWidth();
586
587   static const QChar newLineChar('\n');
588   static const QChar tabChar('\t');
589   static const QChar spaceChar(' ');
590
591   int insertColumnExpanded = insertColumn;
592   Kate::TextLine l = kateTextLine( currentLine );
593   if (l)
594     insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
595
596   int pos = 0;
597   for (; pos < totalLength; pos++)
598   {
599     const QChar& ch = text.at(pos);
600
601     if (ch == newLineChar)
602     {
603       // Only perform the text insert if there is text to insert
604       if (currentLineStart < pos)
605         editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
606
607       if ( !block )
608       {
609         editWrapLine(currentLine, insertColumn + pos - currentLineStart);
610         insertColumn = 0;
611       }
612       else
613       {
614         if ( currentLine == lastLine() )
615           editWrapLine(currentLine , insertColumn + pos - currentLineStart);
616         insertColumn = position.column(); // tab expansion might change this
617       }
618
619       currentLine++;
620       currentLineStart = pos + 1;
621       l = kateTextLine( currentLine );
622       if (l)
623         insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
624     }
625     else
626     {
627       if ( replacetabs && ch == tabChar )
628       {
629         int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth );
630         editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar));
631
632         insertColumn += pos - currentLineStart + spacesRequired;
633         currentLineStart = pos + 1;
634         l = kateTextLine( currentLine );
635         if (l)
636           insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
637       }
638     }
639   }
640
641   // Only perform the text insert if there is text to insert
642   if (currentLineStart < pos)
643     editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
644
645   editEnd();
646   return true;
647 }
648
649 bool KateDocument::insertText( const KTextEditor::Cursor & position, const QStringList & textLines, bool block )
650 {
651   if (!isReadWrite())
652     return false;
653
654   // just reuse normal function
655   return insertText (position, textLines.join ("\n"), block);
656 }
657
658 bool KateDocument::removeText ( const KTextEditor::Range &_range, bool block )
659 {
660   KTextEditor::Range range = _range;
661
662   if (!isReadWrite())
663     return false;
664
665   // Should now be impossible to trigger with the new Range class
666   Q_ASSERT( range.start().line() <= range.end().line() );
667
668   if ( range.start().line() > lastLine() )
669     return false;
670
671   if (!block)
672     emit aboutToRemoveText(range);
673
674   editStart();
675
676   if ( !block )
677   {
678     if ( range.end().line() > lastLine() )
679     {
680       range.end().setPosition(lastLine()+1, 0);
681     }
682
683     if (range.onSingleLine())
684     {
685       editRemoveText(range.start().line(), range.start().column(), range.columnWidth());
686     }
687     else
688     {
689       int from = range.start().line();
690       int to = range.end().line();
691
692       // remove last line
693       if (to <= lastLine())
694         editRemoveText(to, 0, range.end().column());
695
696       // editRemoveLines() will be called on first line (to remove bookmark)
697       if (range.start().column() == 0 && from > 0)
698         --from;
699
700       // remove middle lines
701       editRemoveLines(from+1, to-1);
702
703       // remove first line if not already removed by editRemoveLines()
704       if (range.start().column() > 0 || range.start().line() == 0) {
705         editRemoveText(from, range.start().column(), m_buffer->plainLine(from)->length() - range.start().column());
706         editUnWrapLine(from);
707       }
708     }
709   } // if ( ! block )
710   else
711   {
712     int startLine = qMax(0, range.start().line());
713     int vc1 = toVirtualColumn(range.start());
714     int vc2 = toVirtualColumn(range.end());
715     for (int line = qMin(range.end().line(), lastLine()); line >= startLine; --line) {
716       Kate::TextLine tl = const_cast<KateDocument*>(this)->kateTextLine(line);
717       int col1 = tl->fromVirtualColumn(vc1, config()->tabWidth());
718       int col2 = tl->fromVirtualColumn(vc2, config()->tabWidth());
719       editRemoveText(line, qMin(col1, col2), qAbs(col2 - col1));
720     }
721   }
722
723   editEnd ();
724   return true;
725 }
726
727 bool KateDocument::insertLine( int l, const QString &str )
728 {
729   if (!isReadWrite())
730     return false;
731
732   if (l < 0 || l > lines())
733     return false;
734
735   return editInsertLine (l, str);
736 }
737
738 bool KateDocument::insertLines( int line, const QStringList & text )
739 {
740   if (!isReadWrite())
741     return false;
742
743   if (line < 0 || line > lines())
744     return false;
745
746   bool success = true;
747   foreach (const QString &string, text)
748     success &= editInsertLine (line++, string);
749
750   return success;
751 }
752
753 bool KateDocument::removeLine( int line )
754 {
755   if (!isReadWrite())
756     return false;
757
758   if (line < 0 || line > lastLine())
759     return false;
760
761   return editRemoveLine (line);
762 }
763
764 int KateDocument::totalCharacters() const
765 {
766   int l = 0;
767
768   for (int i = 0; i < m_buffer->count(); ++i)
769   {
770     Kate::TextLine line = m_buffer->plainLine(i);
771
772     if (line)
773       l += line->length();
774   }
775
776   return l;
777 }
778
779 int KateDocument::lines() const
780 {
781   return m_buffer->count();
782 }
783
784 int KateDocument::numVisLines() const
785 {
786   return m_buffer->countVisible ();
787 }
788
789 int KateDocument::lineLength ( int line ) const
790 {
791   if (line < 0 || line > lastLine())
792     return -1;
793
794   Kate::TextLine l = m_buffer->plainLine(line);
795
796   if (!l)
797     return -1;
798
799   return l->length();
800 }
801 //END
802
803 //BEGIN KTextEditor::EditInterface internal stuff
804 //
805 // Starts an edit session with (or without) undo, update of view disabled during session
806 //
807 void KateDocument::editStart ()
808 {
809   editSessionNumber++;
810
811   if (editSessionNumber > 1)
812     return;
813
814   editIsRunning = true;
815
816   m_undoManager->editStart();
817
818   foreach(KateView *view,m_views)
819     view->editStart ();
820
821   m_buffer->editStart ();
822 }
823
824 //
825 // End edit session and update Views
826 //
827 void KateDocument::editEnd ()
828 {
829   if (editSessionNumber == 0) {
830     Q_ASSERT(0);
831     return;
832   }
833
834   // wrap the new/changed text, if something really changed!
835   if (m_buffer->editChanged() && (editSessionNumber == 1))
836     if (m_undoManager->isActive() && config()->wordWrap())
837       wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
838
839   editSessionNumber--;
840
841   if (editSessionNumber > 0)
842     return;
843
844   // end buffer edit, will trigger hl update
845   // this will cause some possible adjustment of tagline start/end
846   m_buffer->editEnd ();
847
848   m_undoManager->editEnd();
849
850   // edit end for all views !!!!!!!!!
851   foreach(KateView *view, m_views)
852     view->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
853
854   if (m_buffer->editChanged()) {
855     setModified(true);
856     emit textChanged (this);
857   }
858
859   editIsRunning = false;
860 }
861
862 void KateDocument::pushEditState ()
863 {
864   editStateStack.push(editSessionNumber);
865 }
866
867 void KateDocument::popEditState ()
868 {
869   if (editStateStack.isEmpty()) return;
870
871   int count = editStateStack.pop() - editSessionNumber;
872   while (count < 0) { ++count; editEnd(); }
873   while (count > 0) { --count; editStart(); }
874 }
875
876 void KateDocument::inputMethodStart()
877 {
878   m_undoManager->inputMethodStart();
879 }
880
881 void KateDocument::inputMethodEnd()
882 {
883   m_undoManager->inputMethodEnd();
884 }
885
886 bool KateDocument::wrapText(int startLine, int endLine)
887 {
888   if (startLine < 0 || endLine < 0)
889     return false;
890
891   if (!isReadWrite())
892     return false;
893
894   int col = config()->wordWrapAt();
895
896   if (col == 0)
897     return false;
898
899   editStart ();
900
901   for (int line = startLine; (line <= endLine) && (line < lines()); line++)
902   {
903     Kate::TextLine l = kateTextLine(line);
904
905     if (!l)
906       return false;
907
908     kDebug (13020) << "try wrap line: " << line;
909
910     if (l->virtualLength(m_buffer->tabWidth()) > col)
911     {
912       Kate::TextLine nextl = kateTextLine(line+1);
913
914       kDebug (13020) << "do wrap line: " << line;
915
916       int eolPosition = l->length()-1;
917
918       // take tabs into account here, too
919       int x = 0;
920       const QString & t = l->string();
921       int z2 = 0;
922       for ( ; z2 < l->length(); z2++)
923       {
924         static const QChar tabChar('\t');
925         if (t.at(z2) == tabChar)
926           x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
927         else
928           x++;
929
930         if (x > col)
931           break;
932       }
933
934       int searchStart = qMin (z2, l->length()-1);
935
936       // If where we are wrapping is an end of line and is a space we don't
937       // want to wrap there
938       if (searchStart == eolPosition && t.at(searchStart).isSpace())
939         searchStart--;
940
941       // Scan backwards looking for a place to break the line
942       // We are not interested in breaking at the first char
943       // of the line (if it is a space), but we are at the second
944       // anders: if we can't find a space, try breaking on a word
945       // boundary, using KateHighlight::canBreakAt().
946       // This could be a priority (setting) in the hl/filetype/document
947       int z = 0;
948       int nw = 0; // alternative position, a non word character
949       for (z=searchStart; z > 0; z--)
950       {
951         if (t.at(z).isSpace()) break;
952         if ( ! nw && highlight()->canBreakAt( t.at(z) , l->attribute(z) ) )
953         nw = z;
954       }
955
956       bool removeTrailingSpace = false;
957       if (z > 0)
958       {
959         // So why don't we just remove the trailing space right away?
960         // Well, the (view's) cursor may be directly in front of that space
961         // (user typing text before the last word on the line), and if that
962         // happens, the cursor would be moved to the next line, which is not
963         // what we want (bug #106261)
964         z++;
965         removeTrailingSpace = true;
966       }
967       else
968       {
969         // There was no space to break at so break at a nonword character if
970         // found, or at the wrapcolumn ( that needs be configurable )
971         // Don't try and add any white space for the break
972         if ( nw && nw < col ) nw++; // break on the right side of the character
973         z = nw ? nw : col;
974       }
975
976       if (nextl && !nextl->isAutoWrapped())
977       {
978         editWrapLine (line, z, true);
979         editMarkLineAutoWrapped (line+1, true);
980
981         endLine++;
982       }
983       else
984       {
985         if (nextl && (nextl->length() > 0) && !nextl->at(0).isSpace() && ((l->length() < 1) || !l->at(l->length()-1).isSpace()))
986           editInsertText (line+1, 0, QString (" "));
987
988         bool newLineAdded = false;
989         editWrapLine (line, z, false, &newLineAdded);
990
991         editMarkLineAutoWrapped (line+1, true);
992
993         endLine++;
994       }
995
996       if (removeTrailingSpace) {
997         // cu space
998         editRemoveText (line, z - 1, 1);
999       }
1000     }
1001   }
1002
1003   editEnd ();
1004
1005   return true;
1006 }
1007
1008 bool KateDocument::editInsertText ( int line, int col, const QString &s )
1009 {
1010   if (line < 0 || col < 0)
1011     return false;
1012
1013   if (!isReadWrite())
1014     return false;
1015
1016   Kate::TextLine l = kateTextLine(line);
1017
1018   if (!l)
1019     return false;
1020
1021   // nothing to do, do nothing!
1022   if (s.isEmpty())
1023     return true;
1024
1025   editStart ();
1026
1027   QString s2 = s;
1028   int col2 = col;
1029   if (col2 > l->length()) {
1030     s2 = QString(col2 - l->length(), QLatin1Char(' ')) + s;
1031     col2 = l->length();
1032   }
1033
1034   m_undoManager->slotTextInserted(line, col2, s2);
1035
1036   // insert text into line
1037   m_buffer->insertText (KTextEditor::Cursor (line, col2), s2);
1038
1039   emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col2, line, col2 + s2.length()));
1040
1041   editEnd();
1042
1043   return true;
1044 }
1045
1046 bool KateDocument::editRemoveText ( int line, int col, int len )
1047 {
1048   if (line < 0 || col < 0 || len < 0)
1049     return false;
1050
1051   if (!isReadWrite())
1052     return false;
1053
1054   Kate::TextLine l = kateTextLine(line);
1055
1056   if (!l)
1057     return false;
1058
1059   // nothing to do, do nothing!
1060   if (len == 0)
1061     return true;
1062
1063   // wrong column
1064   if (col >= l->text().size())
1065     return false;
1066
1067   editStart ();
1068
1069   QString oldText = l->string().mid(col, len);
1070   
1071   m_undoManager->slotTextRemoved(line, col, oldText);
1072
1073   // remove text from line
1074   m_buffer->removeText (KTextEditor::Range (KTextEditor::Cursor (line, col), KTextEditor::Cursor (line, col+len)));
1075
1076   removeTrailingSpace( line );
1077
1078   emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len));
1079   emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len), oldText);
1080
1081   editEnd ();
1082
1083   return true;
1084 }
1085
1086 bool KateDocument::editMarkLineAutoWrapped ( int line, bool autowrapped )
1087 {
1088   if (line < 0)
1089     return false;
1090
1091   if (!isReadWrite())
1092     return false;
1093
1094   Kate::TextLine l = kateTextLine(line);
1095
1096   if (!l)
1097     return false;
1098
1099   editStart ();
1100
1101   m_undoManager->slotMarkLineAutoWrapped(line, autowrapped);
1102
1103   l->setAutoWrapped (autowrapped);
1104
1105   editEnd ();
1106
1107   return true;
1108 }
1109
1110 bool KateDocument::editWrapLine ( int line, int col, bool newLine, bool *newLineAdded)
1111 {
1112   if (line < 0 || col < 0)
1113     return false;
1114
1115   if (!isReadWrite())
1116     return false;
1117
1118   Kate::TextLine l = kateTextLine(line);
1119
1120   if (!l)
1121     return false;
1122
1123   editStart ();
1124
1125   Kate::TextLine nextLine = kateTextLine(line+1);
1126
1127   int pos = l->length() - col;
1128
1129   if (pos < 0)
1130     pos = 0;
1131
1132   m_undoManager->slotLineWrapped(line, col, pos, (!nextLine || newLine));
1133
1134   if (!nextLine || newLine)
1135   {
1136     m_buffer->wrapLine (KTextEditor::Cursor (line, col));
1137
1138     QList<KTextEditor::Mark*> list;
1139     for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
1140     {
1141       if( i.value()->line >= line )
1142       {
1143         if ((col == 0) || (i.value()->line > line))
1144           list.append( i.value() );
1145       }
1146     }
1147
1148     for( int i=0; i < list.size(); ++i )
1149       m_marks.take( list[i]->line );
1150
1151     for( int i=0; i < list.size(); ++i )
1152     {
1153       list[i]->line++;
1154       m_marks.insert( list[i]->line, list[i] );
1155     }
1156
1157     if( !list.isEmpty() )
1158       emit marksChanged( this );
1159
1160     // yes, we added a new line !
1161     if (newLineAdded)
1162       (*newLineAdded) = true;
1163   }
1164   else
1165   {
1166     m_buffer->wrapLine (KTextEditor::Cursor (line, col));
1167     m_buffer->unwrapLine (line + 2);
1168
1169     // no, no new line added !
1170     if (newLineAdded)
1171       (*newLineAdded) = false;
1172   }
1173
1174   emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line+1, pos));
1175
1176   editEnd ();
1177
1178   return true;
1179 }
1180
1181 bool KateDocument::editUnWrapLine ( int line, bool removeLine, int length )
1182 {
1183   if (line < 0 || length < 0)
1184     return false;
1185
1186   if (!isReadWrite())
1187     return false;
1188
1189   Kate::TextLine l = kateTextLine(line);
1190   Kate::TextLine nextLine = kateTextLine(line+1);
1191
1192   if (!l || !nextLine)
1193     return false;
1194
1195   editStart ();
1196
1197   int col = l->length ();
1198
1199   m_undoManager->slotLineUnWrapped(line, col, length, removeLine);
1200
1201   if (removeLine)
1202   {
1203     m_buffer->unwrapLine (line+1);
1204   }
1205   else
1206   {
1207     m_buffer->insertText (KTextEditor::Cursor (line, col), nextLine->string().left((nextLine->length() < length) ? nextLine->length() : length));
1208     m_buffer->removeText (KTextEditor::Range (KTextEditor::Cursor (line + 1, 0), KTextEditor::Cursor (line + 1, (nextLine->length() < length) ? nextLine->length() : length)));
1209   }
1210
1211   QList<KTextEditor::Mark*> list;
1212   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
1213   {
1214     if( i.value()->line >= line+1 )
1215       list.append( i.value() );
1216
1217     if ( i.value()->line == line+1 )
1218     {
1219       KTextEditor::Mark* mark = m_marks.take( line );
1220
1221       if (mark)
1222       {
1223         i.value()->type |= mark->type;
1224       }
1225     }
1226   }
1227
1228    for( int i=0; i < list.size(); ++i )
1229       m_marks.take( list[i]->line );
1230
1231    for( int i=0; i < list.size(); ++i )
1232    {
1233       list[i]->line--;
1234       m_marks.insert( list[i]->line, list[i] );
1235     }
1236
1237   if( !list.isEmpty() )
1238     emit marksChanged( this );
1239
1240   emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line+1, 0));
1241
1242   editEnd ();
1243
1244   return true;
1245 }
1246
1247 bool KateDocument::editInsertLine ( int line, const QString &s )
1248 {
1249   if (line < 0)
1250     return false;
1251
1252   if (!isReadWrite())
1253     return false;
1254
1255   if ( line > lines() )
1256     return false;
1257
1258   editStart ();
1259
1260   m_undoManager->slotLineInserted(line, s);
1261
1262   removeTrailingSpace( line ); // old line
1263
1264   // wrap line
1265   if (line > 0) {
1266     Kate::TextLine previousLine = m_buffer->line (line-1);
1267     m_buffer->wrapLine (KTextEditor::Cursor (line-1, previousLine->text().size()));
1268   } else {
1269     m_buffer->wrapLine (KTextEditor::Cursor (0, 0));
1270   }
1271
1272   // insert text
1273   m_buffer->insertText (KTextEditor::Cursor (line, 0), s);
1274
1275   removeTrailingSpace( line ); // new line
1276
1277   Kate::TextLine tl = m_buffer->line (line);
1278
1279   QList<KTextEditor::Mark*> list;
1280   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
1281   {
1282     if( i.value()->line >= line )
1283       list.append( i.value() );
1284   }
1285
1286    for( int i=0; i < list.size(); ++i )
1287       m_marks.take( list[i]->line );
1288
1289    for( int i=0; i < list.size(); ++i )
1290    {
1291       list[i]->line++;
1292       m_marks.insert( list[i]->line, list[i] );
1293     }
1294
1295   if( !list.isEmpty() )
1296     emit marksChanged( this );
1297
1298   KTextEditor::Range rangeInserted(line, 0, line, tl->length());
1299
1300   if (line) {
1301     Kate::TextLine prevLine = plainKateTextLine(line - 1);
1302     rangeInserted.start().setPosition(line - 1, prevLine->length());
1303   } else {
1304     rangeInserted.end().setPosition(line + 1, 0);
1305   }
1306
1307   emit KTextEditor::Document::textInserted(this, rangeInserted);
1308
1309   editEnd ();
1310
1311   return true;
1312 }
1313
1314 bool KateDocument::editRemoveLine ( int line )
1315 {
1316   return editRemoveLines(line, line);
1317 }
1318
1319 bool KateDocument::editRemoveLines ( int from, int to )
1320 {
1321   if (to < from || from < 0 || to > lastLine())
1322     return false;
1323
1324   if (!isReadWrite())
1325     return false;
1326
1327   if (lines() == 1)
1328     return editRemoveText(0, 0, kateTextLine(0)->length());
1329
1330   editStart();
1331   QStringList oldText;
1332
1333   for (int line = to; line >= from; line--) {
1334     KateLineInfo info;
1335     lineInfo(&info, line);
1336     if (info.startsInVisibleBlock)
1337       foldingTree()->toggleRegionVisibility(line);
1338   }
1339
1340   for (int line = to; line >= from; line--) {
1341     Kate::TextLine tl = m_buffer->line (line);
1342     oldText << this->line(line);
1343     m_undoManager->slotLineRemoved(line, this->line(line));
1344
1345     m_buffer->removeText (KTextEditor::Range (KTextEditor::Cursor (line, 0), KTextEditor::Cursor (line, tl->text().size())));
1346     if (line + 1 <= lastLine())
1347       m_buffer->unwrapLine (line+1);
1348     else if (line != 0)
1349       m_buffer->unwrapLine (line);
1350   }
1351
1352   QList<int> rmark;
1353   QList<int> list;
1354
1355   foreach (KTextEditor::Mark* mark, m_marks) {
1356     int line = mark->line;
1357     if (line > to)
1358       list << line;
1359     else if (line >= from)
1360       rmark << line;
1361   }
1362
1363   foreach (int line, rmark)
1364     delete m_marks.take(line);
1365
1366   foreach (int line, list)
1367   {
1368     KTextEditor::Mark* mark = m_marks.take(line);
1369     mark->line -= to - from + 1;
1370     m_marks.insert(mark->line, mark);
1371   }
1372
1373   if (!list.isEmpty())
1374     emit marksChanged(this);
1375
1376   KTextEditor::Range rangeRemoved(from, 0, to + 1, 0);
1377
1378   if (to == lastLine() + to - from + 1) {
1379     rangeRemoved.end().setPosition(to, oldText.first().length());
1380     if (from > 0) {
1381       Kate::TextLine prevLine = plainKateTextLine(from - 1);
1382       rangeRemoved.start().setPosition(from - 1, prevLine->length());
1383     }
1384   }
1385
1386   emit KTextEditor::Document::textRemoved(this, rangeRemoved);
1387
1388   editEnd();
1389
1390   return true;
1391 }
1392 //END
1393
1394 //BEGIN KTextEditor::UndoInterface stuff
1395
1396 uint KateDocument::undoCount () const
1397 {
1398   return m_undoManager->undoCount ();
1399 }
1400
1401 uint KateDocument::redoCount () const
1402 {
1403   return m_undoManager->redoCount ();
1404 }
1405
1406 void KateDocument::undo()
1407 {
1408   m_undoManager->undo();
1409 }
1410
1411 void KateDocument::redo()
1412 {
1413   m_undoManager->redo();
1414 }
1415 //END
1416
1417 //BEGIN KTextEditor::SearchInterface stuff
1418 QVector<KTextEditor::Range> KateDocument::searchText(
1419     const KTextEditor::Range & range,
1420     const QString & pattern,
1421     const KTextEditor::Search::SearchOptions options)
1422 {
1423   // TODO
1424   // * support BlockInputRange
1425   // * support DotMatchesNewline
1426
1427   const bool escapeSequences =  options.testFlag(KTextEditor::Search::EscapeSequences);
1428   const bool regexMode       =  options.testFlag(KTextEditor::Search::Regex);
1429   const bool backwards       =  options.testFlag(KTextEditor::Search::Backwards);
1430   const bool wholeWords      =  options.testFlag(KTextEditor::Search::WholeWords);
1431   const Qt::CaseSensitivity caseSensitivity = options.testFlag(KTextEditor::Search::CaseInsensitive) ? Qt::CaseInsensitive : Qt::CaseSensitive;
1432
1433   if (regexMode)
1434   {
1435     // regexp search
1436     // escape sequences are supported by definition
1437     KateRegExpSearch searcher(this, caseSensitivity);
1438     return searcher.search(pattern, range, backwards);
1439   }
1440
1441   if (escapeSequences)
1442   {
1443     // escaped search
1444     KatePlainTextSearch searcher(this, caseSensitivity, wholeWords);
1445     KTextEditor::Range match = searcher.search(KateRegExpSearch::escapePlaintext(pattern), range, backwards);
1446
1447     QVector<KTextEditor::Range> result;
1448     result.append(match);
1449     return result;
1450   }
1451
1452   // plaintext search
1453   KatePlainTextSearch searcher(this, caseSensitivity, wholeWords);
1454   KTextEditor::Range match = searcher.search(pattern, range, backwards);
1455
1456   QVector<KTextEditor::Range> result;
1457   result.append(match);
1458   return result;
1459 }
1460
1461
1462
1463 KTextEditor::Search::SearchOptions KateDocument::supportedSearchOptions() const
1464 {
1465   KTextEditor::Search::SearchOptions supported(KTextEditor::Search::Default);
1466   supported |= KTextEditor::Search::Regex;
1467   supported |= KTextEditor::Search::CaseInsensitive;
1468   supported |= KTextEditor::Search::Backwards;
1469 // supported |= KTextEditor::Search::BlockInputRange;
1470   supported |= KTextEditor::Search::EscapeSequences;
1471   supported |= KTextEditor::Search::WholeWords;
1472 // supported |= KTextEditor::Search::DotMatchesNewline;
1473   return supported;
1474 }
1475 //END
1476
1477 QWidget * KateDocument::dialogParent()
1478 {
1479     QWidget *w=widget();
1480
1481     if(!w)
1482     {
1483         w=activeView();
1484
1485         if(!w)
1486             w=QApplication::activeWindow();
1487     }
1488
1489     return w;
1490 }
1491
1492 //BEGIN KTextEditor::HighlightingInterface stuff
1493 bool KateDocument::setMode (const QString &name)
1494 {
1495   updateFileType (name);
1496   return true;
1497 }
1498
1499 QString KateDocument::mode () const
1500 {
1501   return m_fileType;
1502 }
1503
1504 QStringList KateDocument::modes () const
1505 {
1506   QStringList m;
1507
1508   const QList<KateFileType *> &modeList = KateGlobal::self()->modeManager()->list();
1509   for (int i = 0; i < modeList.size(); ++i)
1510     m << modeList[i]->name;
1511
1512   return m;
1513 }
1514
1515 bool KateDocument::setHighlightingMode (const QString &name)
1516 {
1517   m_buffer->setHighlight (KateHlManager::self()->nameFind(name));
1518   return true;
1519 }
1520
1521 QString KateDocument::highlightingMode () const
1522 {
1523   return highlight()->name ();
1524 }
1525
1526 QStringList KateDocument::highlightingModes () const
1527 {
1528   QStringList hls;
1529
1530   for (int i = 0; i < KateHlManager::self()->highlights(); ++i)
1531     hls << KateHlManager::self()->hlName (i);
1532
1533   return hls;
1534 }
1535
1536 QString KateDocument::highlightingModeSection( int index ) const
1537 {
1538   return KateHlManager::self()->hlSection( index );
1539 }
1540
1541 QString KateDocument::modeSection( int index ) const
1542 {
1543   return KateGlobal::self()->modeManager()->list()[ index ]->section;
1544 }
1545
1546 void KateDocument::bufferHlChanged ()
1547 {
1548   // update all views
1549   makeAttribs(false);
1550
1551   // deactivate indenter if necessary
1552   m_indenter->checkRequiredStyle();
1553
1554   emit highlightingModeChanged(this);
1555 }
1556
1557
1558 void KateDocument::setDontChangeHlOnSave()
1559 {
1560   hlSetByUser = true;
1561 }
1562
1563 void KateDocument::bomSetByUser()
1564 {
1565   m_bomSetByUser=true;
1566 }
1567 //END
1568
1569 //BEGIN KTextEditor::SessionConfigInterface and KTextEditor::ParameterizedSessionConfigInterface stuff
1570 void KateDocument::readSessionConfig(const KConfigGroup &kconfig)
1571 {
1572   readParameterizedSessionConfig(kconfig, SkipNone);
1573 }
1574
1575 void KateDocument::readParameterizedSessionConfig(const KConfigGroup &kconfig,
1576                                                   unsigned long configParameters)
1577 {
1578   if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipEncoding)) {
1579     // get the encoding
1580     QString tmpenc=kconfig.readEntry("Encoding");
1581     if (!tmpenc.isEmpty() && (tmpenc != encoding()))
1582       setEncoding(tmpenc);
1583   }
1584
1585   if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipUrl)) {
1586     // restore the url
1587     KUrl url (kconfig.readEntry("URL"));
1588
1589     // open the file if url valid
1590     if (!url.isEmpty() && url.isValid())
1591       openUrl (url);
1592     else completed(); //perhaps this should be emitted at the end of this function
1593   }
1594   else {
1595     completed(); //perhaps this should be emitted at the end of this function
1596   }
1597
1598   if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipMode)) {
1599     // restore the filetype
1600     updateFileType (kconfig.readEntry("Mode", "Normal"));
1601   }
1602
1603   if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipHighlighting)) {
1604     // restore the hl stuff
1605     m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig.readEntry("Highlighting")));
1606   }
1607
1608   // read only mode
1609   // todo: what does m_bReadOnly mean?
1610   setReadWrite(kconfig.readEntry("ReadWrite", true));
1611
1612   // indent mode
1613   config()->setIndentationMode( kconfig.readEntry("Indentation Mode", config()->indentationMode() ) );
1614
1615   // Restore Bookmarks
1616   const QList<int> marks = kconfig.readEntry("Bookmarks", QList<int>());
1617   for( int i = 0; i < marks.count(); i++ )
1618     addMark( marks[i], KateDocument::markType01 );
1619 }
1620
1621 void KateDocument::writeSessionConfig(KConfigGroup &kconfig)
1622 {
1623   writeParameterizedSessionConfig(kconfig, SkipNone);
1624 }
1625
1626 void KateDocument::writeParameterizedSessionConfig(KConfigGroup &kconfig,
1627                                                    unsigned long configParameters)
1628 {
1629   if ( this->url().isLocalFile() ) {
1630     const QString path = this->url().toLocalFile();
1631     if ( KGlobal::dirs()->relativeLocation( "tmp", path ) != path ) {
1632       return; // inside tmp resource, do not save
1633     }
1634   }
1635
1636   if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipUrl)) {
1637     // save url
1638     kconfig.writeEntry("URL", this->url().prettyUrl() );
1639   }
1640
1641   if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipEncoding)) {
1642     // save encoding
1643     kconfig.writeEntry("Encoding",encoding());
1644   }
1645
1646   if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipMode)) {
1647     // save file type
1648     kconfig.writeEntry("Mode", m_fileType);
1649   }
1650
1651   if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipHighlighting)) {
1652     // save hl
1653     kconfig.writeEntry("Highlighting", highlight()->name());
1654   }
1655
1656   // read only mode
1657   kconfig.writeEntry("ReadWrite", isReadWrite());
1658
1659   // indent mode
1660   kconfig.writeEntry("Indentation Mode", config()->indentationMode() );
1661
1662   // Save Bookmarks
1663   QList<int> marks;
1664   for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
1665     if (i.value()->type & KTextEditor::MarkInterface::markType01)
1666       marks << i.value()->line;
1667
1668   kconfig.writeEntry( "Bookmarks", marks );
1669 }
1670
1671 //END KTextEditor::SessionConfigInterface and KTextEditor::ParameterizedSessionConfigInterface stuff
1672
1673 uint KateDocument::mark( int line )
1674 {
1675   if( !m_marks.value(line) )
1676     return 0;
1677
1678   return m_marks[line]->type;
1679 }
1680
1681 void KateDocument::setMark( int line, uint markType )
1682 {
1683   clearMark( line );
1684   addMark( line, markType );
1685 }
1686
1687 void KateDocument::clearMark( int line )
1688 {
1689   if( line > lastLine() )
1690     return;
1691
1692   if( !m_marks.value(line) )
1693     return;
1694
1695   KTextEditor::Mark* mark = m_marks.take( line );
1696   emit markChanged( this, *mark, MarkRemoved );
1697   emit marksChanged( this );
1698   delete mark;
1699   tagLines( line, line );
1700   repaintViews(true);
1701 }
1702
1703 void KateDocument::addMark( int line, uint markType )
1704 {
1705   if( line > lastLine())
1706     return;
1707
1708   if( markType == 0 )
1709     return;
1710
1711   if( m_marks.value(line) ) {
1712     KTextEditor::Mark* mark = m_marks[line];
1713
1714     // Remove bits already set
1715     markType &= ~mark->type;
1716
1717     if( markType == 0 )
1718       return;
1719
1720     // Add bits
1721     mark->type |= markType;
1722   } else {
1723     KTextEditor::Mark *mark = new KTextEditor::Mark;
1724     mark->line = line;
1725     mark->type = markType;
1726     m_marks.insert( line, mark );
1727   }
1728
1729   // Emit with a mark having only the types added.
1730   KTextEditor::Mark temp;
1731   temp.line = line;
1732   temp.type = markType;
1733   emit markChanged( this, temp, MarkAdded );
1734
1735   emit marksChanged( this );
1736   tagLines( line, line );
1737   repaintViews(true);
1738 }
1739
1740 void KateDocument::removeMark( int line, uint markType )
1741 {
1742   if( line > lastLine() )
1743     return;
1744
1745   if( !m_marks.value(line) )
1746     return;
1747
1748   KTextEditor::Mark* mark = m_marks[line];
1749
1750   // Remove bits not set
1751   markType &= mark->type;
1752
1753   if( markType == 0 )
1754     return;
1755
1756   // Subtract bits
1757   mark->type &= ~markType;
1758
1759   // Emit with a mark having only the types removed.
1760   KTextEditor::Mark temp;
1761   temp.line = line;
1762   temp.type = markType;
1763   emit markChanged( this, temp, MarkRemoved );
1764
1765   if( mark->type == 0 )
1766     m_marks.remove( line );
1767
1768   emit marksChanged( this );
1769   tagLines( line, line );
1770   repaintViews(true);
1771 }
1772
1773 const QHash<int, KTextEditor::Mark*> &KateDocument::marks()
1774 {
1775   return m_marks;
1776 }
1777
1778 void KateDocument::requestMarkTooltip( int line, QPoint position )
1779 {
1780   if(!mark(line))
1781     return;
1782
1783   bool handled = false;
1784   emit markToolTipRequested( this, *marks()[line], position, handled );
1785 }
1786
1787 bool KateDocument::handleMarkClick( int line )
1788 {
1789   bool handled = false;
1790
1791   if(!mark(line))
1792     return false;
1793
1794   emit markClicked( this, *marks()[line], handled );
1795
1796   return handled;
1797 }
1798
1799 bool KateDocument::handleMarkContextMenu( int line, QPoint position )
1800 {
1801   bool handled = false;
1802
1803   if(!mark(line))
1804     return false;
1805
1806   emit markContextMenuRequested( this, *marks()[line], position, handled );
1807
1808   return handled;
1809 }
1810
1811 void KateDocument::clearMarks()
1812 {
1813   while (!m_marks.isEmpty())
1814   {
1815     QHash<int, KTextEditor::Mark*>::iterator it = m_marks.begin();
1816     KTextEditor::Mark mark = *it.value();
1817     delete it.value();
1818     m_marks.erase (it);
1819
1820     emit markChanged( this, mark, MarkRemoved );
1821     tagLines( mark.line, mark.line );
1822   }
1823
1824   m_marks.clear();
1825
1826   emit marksChanged( this );
1827   repaintViews(true);
1828 }
1829
1830 void KateDocument::setMarkPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
1831 {
1832   m_markPixmaps.insert( type, pixmap );
1833 }
1834
1835 void KateDocument::setMarkDescription( MarkInterface::MarkTypes type, const QString& description )
1836 {
1837   m_markDescriptions.insert( type, description );
1838 }
1839
1840 QPixmap KateDocument::markPixmap( MarkInterface::MarkTypes type ) const
1841 {
1842   return m_markPixmaps.contains(type) ?
1843          m_markPixmaps[type] : QPixmap();
1844 }
1845
1846 QColor KateDocument::markColor( MarkInterface::MarkTypes type ) const
1847 {
1848   uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
1849   if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
1850     return KateRendererConfig::global()->lineMarkerColor(type);
1851   } else {
1852     return QColor();
1853   }
1854 }
1855
1856 QString KateDocument::markDescription( MarkInterface::MarkTypes type ) const
1857 {
1858   return m_markDescriptions.contains(type) ?
1859          m_markDescriptions[type] : QString();
1860 }
1861
1862 void KateDocument::setEditableMarks( uint markMask )
1863 {
1864   m_editableMarks = markMask;
1865 }
1866
1867 uint KateDocument::editableMarks() const
1868 {
1869   return m_editableMarks;
1870 }
1871 //END
1872
1873 //BEGIN KTextEditor::PrintInterface stuff
1874 bool KateDocument::printDialog ()
1875 {
1876   return KatePrinter::print (this);
1877 }
1878
1879 bool KateDocument::print ()
1880 {
1881   return KatePrinter::print (this);
1882 }
1883 //END
1884
1885 //BEGIN KTextEditor::DocumentInfoInterface (### unfinished)
1886 QString KateDocument::mimeType()
1887 {
1888   KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
1889
1890   // if the document has a URL, try KMimeType::findByURL
1891   if ( ! this->url().isEmpty() )
1892     result = KMimeType::findByUrl( this->url() );
1893
1894   else if ( this->url().isEmpty() || ! this->url().isLocalFile() )
1895     result = mimeTypeForContent();
1896
1897   return result->name();
1898 }
1899
1900 KMimeType::Ptr KateDocument::mimeTypeForContent()
1901 {
1902   QByteArray buf (1024,'\0');
1903   uint bufpos = 0;
1904
1905   for (int i=0; i < lines(); ++i)
1906   {
1907     QString line = this->line( i );
1908     uint len = line.length() + 1;
1909
1910     if (bufpos + len > 1024)
1911       len = 1024 - bufpos;
1912
1913     QString ld (line + QChar::fromAscii('\n'));
1914     buf.replace(bufpos,len,ld.toLatin1()); //memcpy(buf.data() + bufpos, ld.toLatin1().constData(), len);
1915
1916     bufpos += len;
1917
1918     if (bufpos >= 1024)
1919       break;
1920   }
1921   buf.resize( bufpos );
1922
1923   int accuracy = 0;
1924   KMimeType::Ptr mt = KMimeType::findByContent(buf, &accuracy);
1925   return mt ? mt : KMimeType::defaultMimeTypePtr();
1926 }
1927 //END KTextEditor::DocumentInfoInterface
1928
1929
1930 //BEGIN KParts::ReadWrite stuff
1931 bool KateDocument::openFile()
1932 {
1933   /**
1934    * we are about to invalidate all cursors/ranges/.. => m_buffer->openFile will do so
1935    */
1936   emit aboutToInvalidateMovingInterfaceContent (this);
1937
1938   // no open errors until now...
1939   setOpeningError(false);
1940
1941   // add new m_file to dirwatch
1942   activateDirWatch ();
1943
1944   //
1945   // mime type magic to get encoding right
1946   //
1947   QString mimeType = arguments().mimeType();
1948   int pos = mimeType.indexOf(';');
1949   if (pos != -1)
1950     setEncoding (mimeType.mid(pos+1));
1951
1952   // do we have success ?
1953   emit KTextEditor::Document::textRemoved(this, documentRange());
1954
1955   bool success = m_buffer->openFile (localFilePath());
1956
1957   // disable view updates
1958   foreach (KateView * view, m_views)
1959     view->setUpdatesEnabled (false);
1960
1961   //
1962   // yeah, success
1963   //
1964   if (success)
1965   {
1966     // update file type
1967     updateFileType (KateGlobal::self()->modeManager()->fileType (this));
1968
1969     // read dir config (if possible and wanted)
1970     readDirConfig ();
1971
1972     // read vars
1973     readVariables();
1974
1975     // remove trailing space
1976     // NOTE: wait until now because the config or variables might tell us not to do this!
1977     m_buffer->setRemoveTrailingSpaces (config()->removeSpaces());
1978     if ( m_buffer->removeTrailingSpaces() )
1979     {
1980       int n = lines();
1981       while (n--)
1982         removeTrailingSpace (n);
1983     }
1984
1985     // update the md5 digest
1986     createDigest( m_digest );
1987
1988     if (!m_postLoadFilterChecks.isEmpty())
1989     {
1990       LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
1991       foreach(const QString& checkplugin, m_postLoadFilterChecks)
1992       {
1993          lscps->postLoadFilter(checkplugin,this);
1994       }
1995     }
1996   }
1997
1998   //
1999   // update views
2000   //
2001   foreach (KateView * view, m_views)
2002   {
2003     // This is needed here because inserting the text moves the view's start position (it is a MovingCursor)
2004     view->setCursorPosition (KTextEditor::Cursor());
2005     view->setUpdatesEnabled (true);
2006     view->updateView (true);
2007   }
2008
2009   // emit all signals about new text after view updates
2010   emit KTextEditor::Document::textInserted(this, documentRange());
2011
2012   // Inform that the text has changed (required as we're not inside the usual editStart/End stuff)
2013   emit textChanged (this);
2014
2015   if (!m_reloading)
2016   {
2017     //
2018     // emit the signal we need for example for kate app
2019     //
2020     emit documentUrlChanged (this);
2021
2022     //
2023     // set doc name, dummy value as arg, don't need it
2024     //
2025     setDocName  (QString());
2026   }
2027   //
2028   // to houston, we are not modified
2029   //
2030   if (m_modOnHd)
2031   {
2032     m_modOnHd = false;
2033     m_modOnHdReason = OnDiskUnmodified;
2034     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
2035   }
2036
2037   //
2038   // display errors
2039   //
2040   QWidget *parentWidget(dialogParent());
2041
2042   if (!suppressOpeningErrorDialogs())
2043   {
2044     if (!success)
2045       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()));
2046   }
2047
2048   if (!success) {
2049     setOpeningError(true);
2050     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()));
2051   }
2052
2053   // warn: broken encoding
2054   if (m_buffer->brokenEncoding())
2055   {
2056     // this file can't be saved again without killing it
2057     setReadWrite( false );
2058
2059     if (!suppressOpeningErrorDialogs())
2060       KMessageBox::information (parentWidget
2061         , i18n ("The file %1 was opened with %2 encoding but contained invalid characters."
2062                 " It is set to read-only mode, as saving might destroy its content."
2063                 " 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(),
2064                 QString (m_buffer->textCodec()->name ()))
2065         , i18n ("Broken Encoding")
2066         , "Broken Encoding Warning");
2067     setOpeningError(true);
2068     setOpeningErrorMessage(i18n ("The file %1 was opened with %2 encoding but contained invalid characters."
2069               " It is set to read-only mode, as saving might destroy its content."
2070               " Either reopen the file with the correct encoding chosen or enable the read-write mode again in the menu to be able to edit it.", this->url().pathOrUrl(), QString (m_buffer->textCodec()->name ())));
2071   }
2072
2073   //
2074   // return the success
2075   //
2076   return success;
2077 }
2078
2079 bool KateDocument::saveFile()
2080 {
2081   QWidget *parentWidget(dialogParent());
2082
2083   //
2084   // warn -> try to save binary file!!!!!!!
2085   //
2086 #if 0
2087   if (m_buffer->binary() && (KMessageBox::warningContinueCancel (parentWidget
2088         , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", url().pathOrUrl())
2089         , i18n ("Trying to Save Binary File")
2090         , KGuiItem(i18n("Save Nevertheless"))
2091         , KStandardGuiItem::cancel(), "Binary File Save Warning") != KMessageBox::Continue))
2092     return false;
2093 #endif
2094
2095   // some warnings, if file was changed by the outside!
2096   if ( !url().isEmpty() )
2097   {
2098     if (m_fileChangedDialogsActivated && m_modOnHd)
2099     {
2100       QString str = reasonedMOHString() + "\n\n";
2101
2102       if (!isModified())
2103       {
2104         if (KMessageBox::warningContinueCancel(parentWidget,
2105                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)
2106           return false;
2107       }
2108       else
2109       {
2110         if (KMessageBox::warningContinueCancel(parentWidget,
2111                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)
2112           return false;
2113       }
2114     }
2115   }
2116
2117   //
2118   // can we encode it if we want to save it ?
2119   //
2120   if (!m_buffer->canEncode ()
2121        && (KMessageBox::warningContinueCancel(parentWidget,
2122            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))
2123   {
2124     return false;
2125   }
2126
2127   //
2128   // try to create backup file..
2129   //
2130
2131   // local file or not is here the question
2132   bool l ( url().isLocalFile() );
2133
2134   // does the user want any backup, if not, not our problem?
2135   if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
2136        || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
2137   {
2138     KUrl u( url() );
2139     u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
2140
2141     kDebug( 13020 ) << "backup src file name: " << url();
2142     kDebug( 13020 ) << "backup dst file name: " << u;
2143
2144     // handle the backup...
2145     bool backupSuccess = false;
2146
2147     // local file mode, no kio
2148     if (u.isLocalFile ())
2149     {
2150       if (QFile::exists (url().toLocalFile ()))
2151       {
2152         // first: check if backupFile is already there, if true, unlink it
2153         QFile backupFile (u.toLocalFile ());
2154         if (backupFile.exists()) backupFile.remove ();
2155
2156         backupSuccess = QFile::copy (url().toLocalFile (), u.toLocalFile ());
2157       }
2158       else
2159         backupSuccess = true;
2160     }
2161     else // remote file mode, kio
2162     {
2163       QWidget *w = widget ();
2164       if (!w && !m_views.isEmpty ())
2165         w = m_views.first();
2166
2167       // get the right permissions, start with safe default
2168       mode_t  perms = 0600;
2169       KIO::UDSEntry fentry;
2170       if (KIO::NetAccess::stat (url(), fentry, QApplication::activeWindow()))
2171       {
2172         kDebug( 13020 ) << "stating succesfull: " << url();
2173         KFileItem item (fentry, url());
2174         perms = item.permissions();
2175
2176         // do a evil copy which will overwrite target if possible
2177         KIO::FileCopyJob *job = KIO::file_copy ( url(), u, -1, KIO::Overwrite );
2178         backupSuccess = KIO::NetAccess::synchronousRun(job, w);
2179       }
2180       else
2181         backupSuccess = true;
2182     }
2183
2184     // backup has failed, ask user how to proceed
2185     if (!backupSuccess && (KMessageBox::warningContinueCancel (parentWidget
2186         , i18n ("For file %1 no backup copy could be created before saving."
2187                 " If an error occurs while saving, you might lose the data of this file."
2188                 " 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())
2189         , i18n ("Failed to create backup copy.")
2190         , KGuiItem(i18n("Try to Save Nevertheless"))
2191         , KStandardGuiItem::cancel(), "Backup Failed Warning") != KMessageBox::Continue))
2192     {
2193       return false;
2194     }
2195   }
2196
2197   // update file type
2198   updateFileType (KateGlobal::self()->modeManager()->fileType (this));
2199
2200   if (!m_preSavePostDialogFilterChecks.isEmpty())
2201   {
2202     LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
2203     foreach(const QString& checkplugin, m_preSavePostDialogFilterChecks)
2204     {
2205        if (lscps->preSavePostDialogFilterCheck(checkplugin,this,parentWidget)==false)
2206          return false;
2207     }
2208   }
2209
2210   // remember the oldpath...
2211   QString oldPath = m_dirWatchFile;
2212
2213   // remove file from dirwatch
2214   deactivateDirWatch ();
2215
2216   //
2217   // try to save
2218   //
2219   if (!m_buffer->saveFile (localFilePath()))
2220   {
2221     // add m_file again to dirwatch
2222     activateDirWatch (oldPath);
2223
2224     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()));
2225
2226     return false;
2227   }
2228
2229   // update the md5 digest
2230   createDigest( m_digest );
2231
2232   // add m_file again to dirwatch
2233   activateDirWatch ();
2234
2235   // update file type
2236 //  updateFileType (KateGlobal::self()->modeManager()->fileType (this));
2237
2238   // read dir config (if possible and wanted)
2239   if ( url().isLocalFile())
2240   {
2241     QFileInfo fo (oldPath), fn (m_dirWatchFile);
2242
2243     if (fo.path() != fn.path())
2244       readDirConfig();
2245   }
2246
2247   // read our vars
2248   readVariables();
2249
2250   //
2251   // we are not modified
2252   //
2253   if (m_modOnHd)
2254   {
2255     m_modOnHd = false;
2256     m_modOnHdReason = OnDiskUnmodified;
2257     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
2258   }
2259
2260   // update document name...
2261   setDocName( QString() );
2262
2263   // url may have changed...
2264   emit documentUrlChanged (this);
2265
2266   m_savingToUrl=true;
2267
2268   // (dominik) mark last undo group as not mergeable, otherwise the next
2269   // edit action might be merged and undo will never stop at the saved state
2270   m_undoManager->undoSafePoint();
2271
2272   //
2273   // return success
2274   //
2275   return true;
2276 }
2277
2278 void KateDocument::readDirConfig ()
2279 {
2280   int depth = config()->searchDirConfigDepth ();
2281
2282   if (this->url().isLocalFile() && (depth > -1))
2283   {
2284     QString currentDir = QFileInfo (localFilePath()).absolutePath();
2285
2286     // only search as deep as specified or not at all ;)
2287     while (depth > -1)
2288     {
2289       //kDebug (13020) << "search for config file in path: " << currentDir;
2290
2291       // try to open config file in this dir
2292       QFile f (currentDir + "/.kateconfig");
2293
2294       if (f.open (QIODevice::ReadOnly))
2295       {
2296         QTextStream stream (&f);
2297
2298         uint linesRead = 0;
2299         QString line = stream.readLine();
2300         while ((linesRead < 32) && !line.isNull())
2301         {
2302           readVariableLine( line );
2303
2304           line = stream.readLine();
2305
2306           linesRead++;
2307         }
2308
2309         break;
2310       }
2311
2312       QString newDir = QFileInfo (currentDir).absolutePath();
2313
2314       // bail out on looping (for example reached /)
2315       if (currentDir == newDir)
2316         break;
2317
2318       currentDir = newDir;
2319       --depth;
2320     }
2321   }
2322 }
2323
2324 void KateDocument::activateDirWatch (const QString &useFileName)
2325 {
2326   QString fileToUse = useFileName;
2327   if (fileToUse.isEmpty())
2328     fileToUse = localFilePath();
2329
2330   // same file as we are monitoring, return
2331   if (fileToUse == m_dirWatchFile)
2332     return;
2333
2334   // remove the old watched file
2335   deactivateDirWatch ();
2336
2337   // add new file if needed
2338   if (url().isLocalFile() && !fileToUse.isEmpty())
2339   {
2340     KateGlobal::self()->dirWatch ()->addFile (fileToUse);
2341     m_dirWatchFile = fileToUse;
2342   }
2343 }
2344
2345 void KateDocument::deactivateDirWatch ()
2346 {
2347   if (!m_dirWatchFile.isEmpty())
2348     KateGlobal::self()->dirWatch ()->removeFile (m_dirWatchFile);
2349
2350   m_dirWatchFile.clear();
2351 }
2352
2353 bool KateDocument::closeUrl()
2354 {
2355   //
2356   // file mod on hd
2357   //
2358   if ( !m_reloading && !url().isEmpty() )
2359   {
2360     if (m_fileChangedDialogsActivated && m_modOnHd)
2361     {
2362       QWidget *parentWidget(dialogParent());
2363
2364       if (!(KMessageBox::warningContinueCancel(
2365             parentWidget,
2366             reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
2367             i18n("Possible Data Loss"), KGuiItem(i18n("Close Nevertheless")), KStandardGuiItem::cancel(),
2368             QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
2369         return false;
2370     }
2371   }
2372
2373   //
2374   // first call the normal kparts implementation
2375   //
2376   if (!KParts::ReadWritePart::closeUrl ())
2377     return false;
2378
2379   // Tell the world that we're about to go ahead with the close
2380   if (!m_reloading)
2381     emit aboutToClose(this);
2382
2383   /**
2384    * we are about to invalidate all cursors/ranges/.. => m_buffer->clear will do so
2385    */
2386   emit aboutToInvalidateMovingInterfaceContent (this);
2387
2388   // remove file from dirwatch
2389   deactivateDirWatch ();
2390
2391   //
2392   // empty url + fileName
2393   //
2394   setUrl(KUrl());
2395   setLocalFilePath(QString());
2396
2397   // we are not modified
2398   if (m_modOnHd)
2399   {
2400     m_modOnHd = false;
2401     m_modOnHdReason = OnDiskUnmodified;
2402     emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
2403   }
2404
2405   emit KTextEditor::Document::textRemoved(this, documentRange());
2406
2407   {
2408     // remove all marks
2409     clearMarks ();
2410
2411     // clear the buffer
2412     m_buffer->clear();
2413
2414     // clear undo/redo history
2415     m_undoManager->clearUndo();
2416     m_undoManager->clearRedo();
2417   }
2418
2419   // no, we are no longer modified
2420   setModified(false);
2421
2422   // we have no longer any hl
2423   m_buffer->setHighlight(0);
2424
2425   // update all our views
2426   foreach (KateView * view, m_views )
2427   {
2428     view->clearSelection(); // fix bug #118588
2429     view->clear();
2430   }
2431
2432   if (!m_reloading)
2433   {
2434     // uh, fileName changed
2435     emit documentUrlChanged (this);
2436
2437     // update doc name
2438     setDocName (QString());
2439   }
2440   
2441   // purge swap file
2442   m_swapfile->fileClosed ();
2443   
2444   // success
2445   return true;
2446 }
2447
2448 void KateDocument::setReadWrite( bool rw )
2449 {
2450   if (isReadWrite() != rw)
2451   {
2452     KParts::ReadWritePart::setReadWrite (rw);
2453
2454     foreach( KateView* view, m_views)
2455     {
2456       view->slotUpdateUndo();
2457       view->slotReadWriteChanged ();
2458     }
2459   }
2460 }
2461
2462 void KateDocument::setModified(bool m) {
2463
2464   if (isModified() != m) {
2465     KParts::ReadWritePart::setModified (m);
2466
2467     foreach( KateView* view,m_views)
2468     {
2469       view->slotUpdateUndo();
2470     }
2471
2472     emit modifiedChanged (this);
2473   }
2474
2475   m_undoManager->setModified (m);
2476 }
2477 //END
2478
2479 //BEGIN Kate specific stuff ;)
2480
2481 void KateDocument::makeAttribs(bool needInvalidate)
2482 {
2483   foreach(KateView *view,m_views)
2484     view->renderer()->updateAttributes ();
2485
2486   if (needInvalidate)
2487     m_buffer->invalidateHighlighting();
2488
2489   foreach(KateView *view,m_views)
2490   {
2491     view->tagAll();
2492     view->updateView (true);
2493   }
2494 }
2495
2496 // the attributes of a hl have changed, update
2497 void KateDocument::internalHlChanged()
2498 {
2499   makeAttribs();
2500 }
2501
2502 void KateDocument::addView(KTextEditor::View *view) {
2503   if (!view)
2504     return;
2505
2506   m_views.append( static_cast<KateView*>(view) );
2507   m_textEditViews.append( view );
2508
2509   // apply the view & renderer vars from the file type
2510   if (!m_fileType.isEmpty())
2511       readVariableLine(KateGlobal::self()->modeManager()->fileType(m_fileType).varLine, true);
2512
2513   // apply the view & renderer vars from the file
2514   readVariables (true);
2515
2516   setActiveView(view);
2517 }
2518
2519 void KateDocument::removeView(KTextEditor::View *view) {
2520   if (!view)
2521     return;
2522
2523   if (activeView() == view)
2524     setActiveView(0L);
2525
2526   m_views.removeAll( (KateView *) view );
2527   m_textEditViews.removeAll( view  );
2528 }
2529
2530 void KateDocument::setActiveView(KTextEditor::View* view)
2531 {
2532   if ( m_activeView == view )
2533     return;
2534
2535   m_activeView = (KateView*)view;
2536 }
2537
2538 bool KateDocument::ownedView(KateView *view) {
2539   // do we own the given view?
2540   return (m_views.contains(view));
2541 }
2542
2543 uint KateDocument::toVirtualColumn( const KTextEditor::Cursor& cursor )
2544 {
2545   Kate::TextLine textLine = m_buffer->plainLine(cursor.line());
2546
2547   if (textLine)
2548     return textLine->toVirtualColumn(cursor.column(), config()->tabWidth());
2549   else
2550     return 0;
2551 }
2552
2553 bool KateDocument::typeChars ( KateView *view, const QString &chars )
2554 {
2555   Kate::TextLine textLine = m_buffer->plainLine(view->cursorPosition().line ());
2556
2557   if (!textLine)
2558     return false;
2559
2560   bool bracketInserted = false;
2561   QString buf;
2562   QChar c;
2563   for( int z = 0; z < chars.length(); z++ )
2564   {
2565     QChar ch = c = chars[z];
2566
2567     if (ch.isPrint() || ch == QChar::fromAscii('\t'))
2568     {
2569       buf.append (ch);
2570
2571       if (!bracketInserted && (config()->autoBrackets()))
2572       {
2573         QChar end_ch;
2574
2575         if (ch == '(') { end_ch = ')'; }
2576         if (ch == '[') { end_ch = ']'; }
2577         if (ch == '{') { end_ch = '}'; }
2578         if (ch == '"') { end_ch = '"'; }
2579         if (ch == '\'') { end_ch = '\''; }
2580
2581         if (!end_ch.isNull()) {
2582           bracketInserted = true;
2583
2584           if (view->selection()) {
2585             buf.append(view->selectionText());
2586           }
2587
2588           buf.append(end_ch);
2589         }
2590       }
2591     }
2592   }
2593
2594   if (buf.isEmpty())
2595     return false;
2596
2597   editStart ();
2598
2599   if (!view->config()->persistentSelection() && view->selection() )
2600     view->removeSelectedText();
2601
2602   KTextEditor::Cursor oldCur (view->cursorPosition());
2603
2604   if (config()->ovr()
2605       || (view->viInputMode() && view->getViInputModeManager()->getCurrentViMode() == ReplaceMode)) {
2606
2607     KTextEditor::Range r = KTextEditor::Range(view->cursorPosition(), qMin(buf.length(),
2608           textLine->length() - view->cursorPosition().column()));
2609
2610     // replace mode needs to know what was removed so it can be restored with backspace
2611     if (view->viInputMode() && view->getViInputModeManager()->getCurrentViMode() == ReplaceMode
2612             && oldCur.column() < line( view->cursorPosition().line() ).length() ) {
2613       QChar removed = line( view->cursorPosition().line() ).at( r.start().column() );
2614       view->getViInputModeManager()->getViReplaceMode()->overwrittenChar( removed );
2615     }
2616
2617     removeText(r);
2618   }
2619
2620   insertText(view->cursorPosition(), buf);
2621   if (bracketInserted)
2622     view->setCursorPositionInternal (view->cursorPosition() - KTextEditor::Cursor(0,1));
2623
2624   KTextEditor::Cursor b(view->cursorPosition());
2625   m_indenter->userTypedChar (view, b, c);
2626
2627   editEnd ();
2628
2629   view->slotTextInserted (view, oldCur, chars);
2630   return true;
2631 }
2632
2633 void KateDocument::newLine( KateView *v )
2634 {
2635   editStart();
2636
2637   if( !v->config()->persistentSelection() && v->selection() )
2638     v->removeSelectedText();
2639
2640   // query cursor position
2641   KTextEditor::Cursor c = v->cursorPosition();
2642
2643   if (c.line() > (int)lastLine())
2644     c.setLine(lastLine());
2645
2646   if (c.line() < 0)
2647     c.setLine(0);
2648
2649   uint ln = c.line();
2650
2651   Kate::TextLine textLine = plainKateTextLine(ln);
2652
2653   if (c.column() > (int)textLine->length())
2654     c.setColumn(textLine->length());
2655
2656   // first: wrap line
2657   editWrapLine (c.line(), c.column());
2658
2659   // second: indent the new line, if needed...
2660   m_indenter->userTypedChar(v, v->cursorPosition(), '\n');
2661
2662   removeTrailingSpace( ln );
2663
2664   editEnd();
2665 }
2666
2667 void KateDocument::transpose( const KTextEditor::Cursor& cursor)
2668 {
2669   Kate::TextLine textLine = m_buffer->plainLine(cursor.line());
2670
2671   if (!textLine || (textLine->length() < 2))
2672     return;
2673
2674   uint col = cursor.column();
2675
2676   if (col > 0)
2677     col--;
2678
2679   if ((textLine->length() - col) < 2)
2680     return;
2681
2682   uint line = cursor.line();
2683   QString s;
2684
2685   //clever swap code if first character on the line swap right&left
2686   //otherwise left & right
2687   s.append (textLine->at(col+1));
2688   s.append (textLine->at(col));
2689   //do the swap
2690
2691   // do it right, never ever manipulate a textline
2692   editStart ();
2693   editRemoveText (line, col, 2);
2694   editInsertText (line, col, s);
2695   editEnd ();
2696 }
2697
2698 void KateDocument::backspace( KateView *view, const KTextEditor::Cursor& c )
2699 {
2700   if ( !view->config()->persistentSelection() && view->selection() ) {
2701     view->removeSelectedText();
2702     return;
2703   }
2704
2705   uint col = qMax( c.column(), 0 );
2706   uint line = qMax( c.line(), 0 );
2707
2708   if ((col == 0) && (line == 0))
2709     return;
2710
2711   int complement = 0;
2712   if (col > 0)
2713   {
2714     if (config()->autoBrackets())
2715     {
2716       // if inside empty (), {}, [], '', "" delete both
2717       Kate::TextLine tl = m_buffer->plainLine(line);
2718       if(!tl) return;
2719       QChar prevChar = tl->at(col-1);
2720       QChar nextChar = tl->at(col);
2721
2722       if ( (prevChar == '"' && nextChar == '"') ||
2723            (prevChar == '\'' && nextChar == '\'') ||
2724            (prevChar == '(' && nextChar == ')') ||
2725            (prevChar == '[' && nextChar == ']') ||
2726            (prevChar == '{' && nextChar == '}') )
2727       {
2728         complement = 1;
2729       }
2730     }
2731     if (!(config()->backspaceIndents()))
2732     {
2733       // ordinary backspace
2734       //c.cursor.col--;
2735       removeText(KTextEditor::Range(line, col-1, line, col+complement));
2736     }
2737     else
2738     {
2739       // backspace indents: erase to next indent position
2740       Kate::TextLine textLine = m_buffer->plainLine(line);
2741
2742       // don't forget this check!!!! really!!!!
2743       if (!textLine)
2744         return;
2745
2746       int colX = textLine->toVirtualColumn(col, config()->tabWidth());
2747       int pos = textLine->firstChar();
2748       if (pos > 0)
2749         pos = textLine->toVirtualColumn(pos, config()->tabWidth());
2750
2751       if (pos < 0 || pos >= (int)colX)
2752       {
2753         // only spaces on left side of cursor
2754         indent( KTextEditor::Range( line, 0, line, 0), -1);
2755       }
2756       else
2757         removeText(KTextEditor::Range(line, col-1, line, col+complement));
2758     }
2759   }
2760   else
2761   {
2762     // col == 0: wrap to previous line
2763     if (line >= 1)
2764     {
2765       Kate::TextLine textLine = m_buffer->plainLine(line-1);
2766
2767       // don't forget this check!!!! really!!!!
2768       if (!textLine)
2769         return;
2770
2771       if (config()->wordWrap() && textLine->endsWith(QLatin1String(" ")))
2772       {
2773         // gg: in hard wordwrap mode, backspace must also eat the trailing space
2774         removeText (KTextEditor::Range(line-1, textLine->length()-1, line, 0));
2775       }
2776       else
2777         removeText (KTextEditor::Range(line-1, textLine->length(), line, 0));
2778     }
2779   }
2780 }
2781
2782 void KateDocument::del( KateView *view, const KTextEditor::Cursor& c )
2783 {
2784   if ( !view->config()->persistentSelection() && view->selection() ) {
2785     view->removeSelectedText();
2786     return;
2787   }
2788
2789   if( c.column() < (int) m_buffer->plainLine(c.line())->length())
2790   {
2791     removeText(KTextEditor::Range(c, 1));
2792   }
2793   else if ( c.line() < lastLine() )
2794   {
2795     removeText(KTextEditor::Range(c.line(), c.column(), c.line()+1, 0));
2796   }
2797 }
2798
2799 void KateDocument::paste ( KateView* view, QClipboard::Mode mode )
2800 {
2801   QString s = QApplication::clipboard()->text(mode);
2802
2803   if (s.isEmpty())
2804     return;
2805
2806   int lines = s.count (QChar::fromAscii ('\n'));
2807
2808   m_undoManager->undoSafePoint();
2809
2810   editStart ();
2811
2812   KTextEditor::Cursor pos = view->cursorPosition();
2813   if (!view->config()->persistentSelection() && view->selection()) {
2814     pos = view->selectionRange().start();
2815     if (view->blockSelection())
2816       pos = rangeOnLine(view->selectionRange(), pos.line()).start();
2817     view->removeSelectedText();
2818   }
2819
2820   if (config()->ovr()) {
2821     QStringList pasteLines = s.split(QLatin1Char('\n'));
2822
2823     if (!view->blockSelectionMode()) {
2824       int endColumn = (pasteLines.count() == 1 ? pos.column() : 0) + pasteLines.last().length();
2825       removeText(KTextEditor::Range(pos,
2826                                     pos.line()+pasteLines.count()-1, endColumn));
2827     } else {
2828       int maxi = qMin(pos.line() + pasteLines.count(), this->lines());
2829
2830       for (int i = pos.line(); i < maxi; ++i) {
2831         int pasteLength = pasteLines[i-pos.line()].length();
2832         removeText(KTextEditor::Range(i, pos.column(),
2833                                       i, qMin(pasteLength + pos.column(), lineLength(i))));
2834       }
2835     }
2836   }
2837
2838
2839   blockRemoveTrailingSpaces(true);
2840   insertText(pos, s, view->blockSelectionMode());
2841   blockRemoveTrailingSpaces(false);
2842
2843   for (int i = pos.line(); i < pos.line() + lines; ++i)
2844     removeTrailingSpace(i);
2845
2846   editEnd();
2847
2848   // move cursor right for block select, as the user is moved right internal
2849   // even in that case, but user expects other behavior in block selection
2850   // mode !
2851   if (view->blockSelectionMode())
2852     view->setCursorPositionInternal(view->cursorPosition() + KTextEditor::Cursor(lines, 0));
2853
2854   if (config()->indentPastedText())
2855   {
2856     KTextEditor::Range range = KTextEditor::Range(KTextEditor::Cursor(pos.line(), 0),
2857                                                   KTextEditor::Cursor(pos.line() + lines, 0));
2858
2859     int start = view->selectionRange().start().line();
2860     const int end = view->selectionRange().end().line();
2861
2862     editStart();
2863
2864     blockRemoveTrailingSpaces(true);
2865     m_indenter->indent(view, range);
2866     blockRemoveTrailingSpaces(false);
2867
2868     for (; start <= end; ++start)
2869       removeTrailingSpace(start);
2870
2871     editEnd();
2872   }
2873
2874   if (!view->blockSelectionMode())
2875     emit charactersSemiInteractivelyInserted (pos, s);
2876   m_undoManager->undoSafePoint();
2877 }
2878
2879 void KateDocument::indent (KTextEditor::Range range, int change)
2880 {
2881   // dominik: if there is a selection, iterate afterwards over all lines and
2882   // remove trailing spaces
2883   int start = range.start().line();
2884   const int end = range.end().line();
2885
2886   editStart();
2887   blockRemoveTrailingSpaces(true);
2888   m_indenter->changeIndent(range, change);
2889   blockRemoveTrailingSpaces(false);
2890
2891   if (range.numberOfLines() > 1) {
2892     for (; start <= end; ++start)
2893       removeTrailingSpace(start);
2894   }
2895   editEnd();
2896 }
2897
2898 void KateDocument::align(KateView *view, const KTextEditor::Range &range)
2899 {
2900   editStart();
2901
2902   blockRemoveTrailingSpaces(true);
2903   m_indenter->indent(view, range);
2904   blockRemoveTrailingSpaces(false);
2905
2906   for (int start = range.start().line(); start <= range.end().line(); ++start) {
2907     removeTrailingSpace(start);
2908   }
2909
2910   editEnd();
2911 }
2912
2913 void KateDocument::insertTab( KateView *, const KTextEditor::Cursor& c )
2914 {
2915   if (!isReadWrite())
2916     return;
2917
2918   editStart();
2919   editInsertText(c.line(), c.column(), QChar('\t'));
2920   editEnd();
2921 }
2922
2923 /*
2924   Remove a given string at the beginning
2925   of the current line.
2926 */
2927 bool KateDocument::removeStringFromBeginning(int line, const QString &str)
2928 {
2929   Kate::TextLine textline = m_buffer->plainLine(line);
2930
2931   KTextEditor::Cursor cursor (line, 0);
2932   bool there = textline->startsWith(str);
2933
2934   if (!there)
2935   {
2936     cursor.setColumn(textline->firstChar());
2937     there = textline->matchesAt(cursor.column(), str);
2938   }
2939
2940   if (there)
2941   {
2942     // Remove some chars
2943     removeText (KTextEditor::Range(cursor, str.length()));
2944   }
2945
2946   return there;
2947 }
2948
2949 /*
2950   Remove a given string at the end
2951   of the current line.
2952 */
2953 bool KateDocument::removeStringFromEnd(int line, const QString &str)
2954 {
2955   Kate::TextLine textline = m_buffer->plainLine(line);
2956
2957   KTextEditor::Cursor cursor (line, 0);
2958   bool there = textline->endsWith(str);
2959
2960   if (there)
2961   {
2962     cursor.setColumn(textline->length() - str.length());
2963   }
2964   else
2965   {
2966     cursor.setColumn(textline->lastChar() - str.length() + 1);
2967     there = textline->matchesAt(cursor.column(), str);
2968   }
2969
2970   if (there)
2971   {
2972     // Remove some chars
2973     removeText (KTextEditor::Range(cursor, str.length()));
2974   }
2975
2976   return there;
2977 }
2978
2979 /*
2980   Add to the current line a comment line mark at the beginning.
2981 */
2982 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
2983 {
2984   QString commentLineMark = highlight()->getCommentSingleLineStart(attrib);
2985   int pos = -1;
2986
2987   if (highlight()->getCommentSingleLinePosition(attrib) == KateHighlighting::CSLPosColumn0)
2988   {
2989     pos = 0;
2990     commentLineMark += ' ';
2991   } else {
2992     const Kate::TextLine l = kateTextLine(line);
2993     pos = l->firstChar();
2994   }
2995
2996   if (pos >= 0)
2997     insertText (KTextEditor::Cursor(line, pos), commentLineMark);
2998 }
2999
3000 /*
3001   Remove from the current line a comment line mark at
3002   the beginning if there is one.
3003 */
3004 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
3005 {
3006   const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
3007   const QString longCommentMark = shortCommentMark + ' ';
3008
3009   editStart();
3010
3011   // Try to remove the long comment mark first
3012   bool removed = (removeStringFromBeginning(line, longCommentMark)
3013                || removeStringFromBeginning(line, shortCommentMark));
3014
3015   editEnd();
3016
3017   return removed;
3018 }
3019
3020 /*
3021   Add to the current line a start comment mark at the
3022   beginning and a stop comment mark at the end.
3023 */
3024 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
3025 {
3026   const QString startCommentMark = highlight()->getCommentStart( attrib ) + ' ';
3027   const QString stopCommentMark = ' ' + highlight()->getCommentEnd( attrib );
3028
3029   editStart();
3030
3031   // Add the start comment mark
3032   insertText (KTextEditor::Cursor(line, 0), startCommentMark);
3033
3034   // Go to the end of the line
3035   const int col = m_buffer->plainLine(line)->length();
3036
3037   // Add the stop comment mark
3038   insertText (KTextEditor::Cursor(line, col), stopCommentMark);
3039
3040   editEnd();
3041 }
3042
3043 /*
3044   Remove from the current line a start comment mark at
3045   the beginning and a stop comment mark at the end.
3046 */
3047 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
3048 {
3049   QString shortStartCommentMark = highlight()->getCommentStart( attrib );
3050   QString longStartCommentMark = shortStartCommentMark + ' ';
3051   QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
3052   QString longStopCommentMark = ' ' + shortStopCommentMark;
3053
3054   editStart();
3055
3056   // TODO "that's a bad idea, can lead to stray endings, FIXME"
3057
3058   // Try to remove the long start comment mark first
3059   bool removedStart = (removeStringFromBeginning(line, longStartCommentMark)
3060                     || removeStringFromBeginning(line, shortStartCommentMark));
3061
3062   bool removedStop = false;
3063   if (removedStart)
3064   {
3065     // Try to remove the long stop comment mark first
3066     removedStop = (removeStringFromEnd(line, longStopCommentMark)
3067                 || removeStringFromEnd(line, shortStopCommentMark));
3068   }
3069
3070   editEnd();
3071
3072   return (removedStart || removedStop);
3073 }
3074
3075 /*
3076   Add to the current selection a start comment mark at the beginning
3077   and a stop comment mark at the end.
3078 */
3079 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
3080 {
3081   const QString startComment = highlight()->getCommentStart( attrib );
3082   const QString endComment = highlight()->getCommentEnd( attrib );
3083
3084   KTextEditor::Range range = view->selectionRange();
3085
3086   if ((range.end().column() == 0) && (range.end().line() > 0))
3087     range.end().setPosition(range.end().line() - 1, lineLength(range.end().line() - 1));
3088
3089   editStart();
3090
3091   if (!view->blockSelection()) {
3092     insertText(range.end(), endComment);
3093     insertText(range.start(), startComment);
3094   } else {
3095     for (int line = range.start().line(); line <= range.end().line(); line++ ) {
3096       KTextEditor::Range subRange = rangeOnLine(range, line);
3097       insertText(subRange.end(), endComment);
3098       insertText(subRange.start(), startComment);
3099     }
3100   }
3101
3102   editEnd ();
3103   // selection automatically updated (MovingRange)
3104 }
3105
3106 /*
3107   Add to the current selection a comment line mark at the beginning of each line.
3108 */
3109 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
3110 {
3111   const QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + ' ';
3112
3113   int sl = view->selectionRange().start().line();
3114   int el = view->selectionRange().end().line();
3115
3116   // if end of selection is in column 0 in last line, omit the last line
3117   if ((view->selectionRange().end().column() == 0) && (el > 0))
3118   {
3119     el--;
3120   }
3121
3122   editStart();
3123
3124   // For each line of the selection
3125   for (int z = el; z >= sl; z--) {
3126     //insertText (z, 0, commentLineMark);
3127     addStartLineCommentToSingleLine(z, attrib );
3128   }
3129
3130   editEnd ();
3131   // selection automatically updated (MovingRange)
3132 }
3133
3134 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
3135 {
3136   for(; line < (int)m_buffer->count(); line++) {
3137     Kate::TextLine textLine = m_buffer->plainLine(line);
3138
3139     if (!textLine)
3140       break;
3141
3142     col = textLine->nextNonSpaceChar(col);
3143     if(col != -1)
3144       return true; // Next non-space char found
3145     col = 0;
3146   }
3147   // No non-space char found
3148   line = -1;
3149   col = -1;
3150   return false;
3151 }
3152
3153 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
3154 {
3155   while(true)
3156   {
3157     Kate::TextLine textLine = m_buffer->plainLine(line);
3158
3159     if (!textLine)
3160       break;
3161
3162     col = textLine->previousNonSpaceChar(col);
3163     if(col != -1) return true;
3164     if(line == 0) return false;
3165     --line;
3166     col = textLine->length();
3167   }
3168   // No non-space char found
3169   line = -1;
3170   col = -1;
3171   return false;
3172 }
3173
3174 /*
3175   Remove from the selection a start comment mark at
3176   the beginning and a stop comment mark at the end.
3177 */
3178 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
3179 {
3180   const QString startComment = highlight()->getCommentStart( attrib );
3181   const QString endComment = highlight()->getCommentEnd( attrib );
3182
3183   int sl = qMax<int> (0, view->selectionRange().start().line());
3184   int el = qMin<int>  (view->selectionRange().end().line(), lastLine());
3185   int sc = view->selectionRange().start().column();
3186   int ec = view->selectionRange().end().column();
3187
3188   // The selection ends on the char before selectEnd
3189   if (ec != 0) {
3190     --ec;
3191   } else if (el > 0) {
3192     --el;
3193     ec = m_buffer->plainLine(el)->length() - 1;
3194   }
3195
3196   const int startCommentLen = startComment.length();
3197   const int endCommentLen = endComment.length();
3198
3199 &