katedocument.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
00003    Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
00004    Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02111-13020, USA.
00019 */
00020 
00021 //BEGIN includes
00022 #include "katedocument.h"
00023 #include "katedocument.moc"
00024 #include "katekeyinterceptorfunctor.h"
00025 #include "katefactory.h"
00026 #include "katedialogs.h"
00027 #include "katehighlight.h"
00028 #include "kateview.h"
00029 #include "katesearch.h"
00030 #include "kateautoindent.h"
00031 #include "katetextline.h"
00032 #include "katedocumenthelpers.h"
00033 #include "kateprinter.h"
00034 #include "katelinerange.h"
00035 #include "katesupercursor.h"
00036 #include "katearbitraryhighlight.h"
00037 #include "katerenderer.h"
00038 #include "kateattribute.h"
00039 #include "kateconfig.h"
00040 #include "katefiletype.h"
00041 #include "kateschema.h"
00042 #include "katetemplatehandler.h"
00043 #include <ktexteditor/plugin.h>
00044 
00045 #include <kio/job.h>
00046 #include <kio/netaccess.h>
00047 #include <kio/kfileitem.h>
00048 
00049 
00050 #include <kparts/event.h>
00051 
00052 #include <klocale.h>
00053 #include <kglobal.h>
00054 #include <kapplication.h>
00055 #include <kpopupmenu.h>
00056 #include <kconfig.h>
00057 #include <kfiledialog.h>
00058 #include <kmessagebox.h>
00059 #include <kstdaction.h>
00060 #include <kiconloader.h>
00061 #include <kxmlguifactory.h>
00062 #include <kdialogbase.h>
00063 #include <kdebug.h>
00064 #include <kglobalsettings.h>
00065 #include <klibloader.h>
00066 #include <kdirwatch.h>
00067 #include <kwin.h>
00068 #include <kencodingfiledialog.h>
00069 #include <ktempfile.h>
00070 #include <kmdcodec.h>
00071 #include <kstandarddirs.h>
00072 
00073 #include <qtimer.h>
00074 #include <qfile.h>
00075 #include <qclipboard.h>
00076 #include <qtextstream.h>
00077 #include <qtextcodec.h>
00078 #include <qmap.h>
00079 //END  includes
00080 
00081 //BEGIN PRIVATE CLASSES
00082 class KatePartPluginItem
00083 {
00084   public:
00085     KTextEditor::Plugin *plugin;
00086 };
00087 //END PRIVATE CLASSES
00088 
00089 //BEGIN d'tor, c'tor
00090 //
00091 // KateDocument Constructor
00092 //
00093 KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView,
00094                              bool bReadOnly, QWidget *parentWidget,
00095                              const char *widgetName, QObject *parent, const char *name)
00096 : Kate::Document(parent, name),
00097   m_plugins (KateFactory::self()->plugins().count()),
00098   m_undoDontMerge(false),
00099   m_undoIgnoreCancel(false),
00100   lastUndoGroupWhenSaved( 0 ),
00101   lastRedoGroupWhenSaved( 0 ),
00102   docWasSavedWhenUndoWasEmpty( true ),
00103   docWasSavedWhenRedoWasEmpty( true ),
00104   m_modOnHd (false),
00105   m_modOnHdReason (0),
00106   m_job (0),
00107   m_tempFile (0),
00108   m_tabInterceptor(0)
00109 {
00110   m_undoComplexMerge=false;
00111   m_isInUndo = false;
00112   // my dcop object
00113   setObjId ("KateDocument#"+documentDCOPSuffix());
00114 
00115   // ktexteditor interfaces
00116   setBlockSelectionInterfaceDCOPSuffix (documentDCOPSuffix());
00117   setConfigInterfaceDCOPSuffix (documentDCOPSuffix());
00118   setConfigInterfaceExtensionDCOPSuffix (documentDCOPSuffix());
00119   setCursorInterfaceDCOPSuffix (documentDCOPSuffix());
00120   setEditInterfaceDCOPSuffix (documentDCOPSuffix());
00121   setEncodingInterfaceDCOPSuffix (documentDCOPSuffix());
00122   setHighlightingInterfaceDCOPSuffix (documentDCOPSuffix());
00123   setMarkInterfaceDCOPSuffix (documentDCOPSuffix());
00124   setMarkInterfaceExtensionDCOPSuffix (documentDCOPSuffix());
00125   setPrintInterfaceDCOPSuffix (documentDCOPSuffix());
00126   setSearchInterfaceDCOPSuffix (documentDCOPSuffix());
00127   setSelectionInterfaceDCOPSuffix (documentDCOPSuffix());
00128   setSelectionInterfaceExtDCOPSuffix (documentDCOPSuffix());
00129   setSessionConfigInterfaceDCOPSuffix (documentDCOPSuffix());
00130   setUndoInterfaceDCOPSuffix (documentDCOPSuffix());
00131   setWordWrapInterfaceDCOPSuffix (documentDCOPSuffix());
00132 
00133   // init local plugin array
00134   m_plugins.fill (0);
00135 
00136   // register doc at factory
00137   KateFactory::self()->registerDocument (this);
00138 
00139   m_reloading = false;
00140   m_loading = false;
00141   m_encodingSticky = false;
00142 
00143   m_buffer = new KateBuffer (this);
00144 
00145   // init the config object, be careful not to use it
00146   // until the initial readConfig() call is done
00147   m_config = new KateDocumentConfig (this);
00148 
00149   // init some more vars !
00150   m_activeView = 0L;
00151 
00152   hlSetByUser = false;
00153   m_fileType = -1;
00154   m_fileTypeSetByUser = false;
00155   setInstance( KateFactory::self()->instance() );
00156 
00157   editSessionNumber = 0;
00158   editIsRunning = false;
00159   m_editCurrentUndo = 0L;
00160   editWithUndo = false;
00161 
00162   m_docNameNumber = 0;
00163 
00164   m_bSingleViewMode = bSingleViewMode;
00165   m_bBrowserView = bBrowserView;
00166   m_bReadOnly = bReadOnly;
00167 
00168   m_marks.setAutoDelete( true );
00169   m_markPixmaps.setAutoDelete( true );
00170   m_markDescriptions.setAutoDelete( true );
00171   setMarksUserChangable( markType01 );
00172 
00173   m_undoMergeTimer = new QTimer(this);
00174   connect(m_undoMergeTimer, SIGNAL(timeout()), SLOT(undoCancel()));
00175 
00176   clearMarks ();
00177   clearUndo ();
00178   clearRedo ();
00179   setModified (false);
00180   docWasSavedWhenUndoWasEmpty = true;
00181 
00182   // normal hl
00183   m_buffer->setHighlight (0);
00184 
00185   m_extension = new KateBrowserExtension( this );
00186   m_arbitraryHL = new KateArbitraryHighlight();
00187   m_indenter = KateAutoIndent::createIndenter ( this, 0 );
00188 
00189   m_indenter->updateConfig ();
00190 
00191   // some nice signals from the buffer
00192   connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int)));
00193   connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated()));
00194 
00195   // if the user changes the highlight with the dialog, notify the doc
00196   connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged()));
00197 
00198   // signal for the arbitrary HL
00199   connect(m_arbitraryHL, SIGNAL(tagLines(KateView*, KateSuperRange*)), SLOT(tagArbitraryLines(KateView*, KateSuperRange*)));
00200 
00201   // signals for mod on hd
00202   connect( KateFactory::self()->dirWatch(), SIGNAL(dirty (const QString &)),
00203            this, SLOT(slotModOnHdDirty (const QString &)) );
00204 
00205   connect( KateFactory::self()->dirWatch(), SIGNAL(created (const QString &)),
00206            this, SLOT(slotModOnHdCreated (const QString &)) );
00207 
00208   connect( KateFactory::self()->dirWatch(), SIGNAL(deleted (const QString &)),
00209            this, SLOT(slotModOnHdDeleted (const QString &)) );
00210 
00211   // update doc name
00212   setDocName ("");
00213 
00214   // if single view mode, like in the konqui embedding, create a default view ;)
00215   if ( m_bSingleViewMode )
00216   {
00217     KTextEditor::View *view = createView( parentWidget, widgetName );
00218     insertChildClient( view );
00219     view->show();
00220     setWidget( view );
00221   }
00222 
00223   connect(this,SIGNAL(sigQueryClose(bool *, bool*)),this,SLOT(slotQueryClose_save(bool *, bool*)));
00224 
00225   m_isasking = 0;
00226 
00227   // plugins
00228   for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
00229   {
00230     if (config()->plugin (i))
00231       loadPlugin (i);
00232   }
00233 }
00234 
00235 //
00236 // KateDocument Destructor
00237 //
00238 KateDocument::~KateDocument()
00239 {
00240   // remove file from dirwatch
00241   deactivateDirWatch ();
00242 
00243   if (!singleViewMode())
00244   {
00245     // clean up remaining views
00246     m_views.setAutoDelete( true );
00247     m_views.clear();
00248   }
00249 
00250   delete m_editCurrentUndo;
00251 
00252   delete m_arbitraryHL;
00253 
00254   // cleanup the undo items, very important, truee :/
00255   undoItems.setAutoDelete(true);
00256   undoItems.clear();
00257 
00258   // clean up plugins
00259   unloadAllPlugins ();
00260 
00261   delete m_config;
00262   delete m_indenter;
00263   KateFactory::self()->deregisterDocument (this);
00264 }
00265 //END
00266 
00267 //BEGIN Plugins
00268 void KateDocument::unloadAllPlugins ()
00269 {
00270   for (uint i=0; i<m_plugins.count(); i++)
00271     unloadPlugin (i);
00272 }
00273 
00274 void KateDocument::enableAllPluginsGUI (KateView *view)
00275 {
00276   for (uint i=0; i<m_plugins.count(); i++)
00277     enablePluginGUI (m_plugins[i], view);
00278 }
00279 
00280 void KateDocument::disableAllPluginsGUI (KateView *view)
00281 {
00282   for (uint i=0; i<m_plugins.count(); i++)
00283     disablePluginGUI (m_plugins[i], view);
00284 }
00285 
00286 void KateDocument::loadPlugin (uint pluginIndex)
00287 {
00288   if (m_plugins[pluginIndex]) return;
00289 
00290   m_plugins[pluginIndex] = KTextEditor::createPlugin (QFile::encodeName((KateFactory::self()->plugins())[pluginIndex]->library()), this);
00291 
00292   enablePluginGUI (m_plugins[pluginIndex]);
00293 }
00294 
00295 void KateDocument::unloadPlugin (uint pluginIndex)
00296 {
00297   if (!m_plugins[pluginIndex]) return;
00298 
00299   disablePluginGUI (m_plugins[pluginIndex]);
00300 
00301   delete m_plugins[pluginIndex];
00302   m_plugins[pluginIndex] = 0L;
00303 }
00304 
00305 void KateDocument::enablePluginGUI (KTextEditor::Plugin *plugin, KateView *view)
00306 {
00307   if (!plugin) return;
00308   if (!KTextEditor::pluginViewInterface(plugin)) return;
00309 
00310   KXMLGUIFactory *factory = view->factory();
00311   if ( factory )
00312     factory->removeClient( view );
00313 
00314   KTextEditor::pluginViewInterface(plugin)->addView(view);
00315 
00316   if ( factory )
00317     factory->addClient( view );
00318 }
00319 
00320 void KateDocument::enablePluginGUI (KTextEditor::Plugin *plugin)
00321 {
00322   if (!plugin) return;
00323   if (!KTextEditor::pluginViewInterface(plugin)) return;
00324 
00325   for (uint i=0; i< m_views.count(); i++)
00326     enablePluginGUI (plugin, m_views.at(i));
00327 }
00328 
00329 void KateDocument::disablePluginGUI (KTextEditor::Plugin *plugin, KateView *view)
00330 {
00331   if (!plugin) return;
00332   if (!KTextEditor::pluginViewInterface(plugin)) return;
00333 
00334   KXMLGUIFactory *factory = view->factory();
00335   if ( factory )
00336     factory->removeClient( view );
00337 
00338   KTextEditor::pluginViewInterface( plugin )->removeView( view );
00339 
00340   if ( factory )
00341     factory->addClient( view );
00342 }
00343 
00344 void KateDocument::disablePluginGUI (KTextEditor::Plugin *plugin)
00345 {
00346   if (!plugin) return;
00347   if (!KTextEditor::pluginViewInterface(plugin)) return;
00348 
00349   for (uint i=0; i< m_views.count(); i++)
00350     disablePluginGUI (plugin, m_views.at(i));
00351 }
00352 //END
00353 
00354 //BEGIN KTextEditor::Document stuff
00355 
00356 KTextEditor::View *KateDocument::createView( QWidget *parent, const char *name )
00357 {
00358   KateView* newView = new KateView( this, parent, name);
00359   connect(newView, SIGNAL(cursorPositionChanged()), SLOT(undoCancel()));
00360   if ( s_fileChangedDialogsActivated )
00361     connect( newView, SIGNAL(gotFocus( Kate::View * )), this, SLOT(slotModifiedOnDisk()) );
00362   return newView;
00363 }
00364 
00365 QPtrList<KTextEditor::View> KateDocument::views () const
00366 {
00367   return m_textEditViews;
00368 }
00369 
00370 void KateDocument::setActiveView( KateView *view )
00371 {
00372   if ( m_activeView == view ) return;
00373 
00374   m_activeView = view;
00375 }
00376 //END
00377 
00378 //BEGIN KTextEditor::ConfigInterfaceExtension stuff
00379 
00380 uint KateDocument::configPages () const
00381 {
00382   return 10;
00383 }
00384 
00385 KTextEditor::ConfigPage *KateDocument::configPage (uint number, QWidget *parent, const char * )
00386 {
00387   switch( number )
00388   {
00389     case 0:
00390       return new KateViewDefaultsConfig (parent);
00391 
00392     case 1:
00393       return new KateSchemaConfigPage (parent, this);
00394 
00395     case 2:
00396       return new KateSelectConfigTab (parent);
00397 
00398     case 3:
00399       return new KateEditConfigTab (parent);
00400 
00401     case 4:
00402       return new KateIndentConfigTab (parent);
00403 
00404     case 5:
00405       return new KateSaveConfigTab (parent);
00406 
00407     case 6:
00408       return new KateHlConfigPage (parent, this);
00409 
00410     case 7:
00411       return new KateFileTypeConfigTab (parent);
00412 
00413     case 8:
00414       return new KateEditKeyConfiguration (parent, this);
00415 
00416     case 9:
00417       return new KatePartPluginConfigPage (parent);
00418 
00419     default:
00420       return 0;
00421   }
00422 
00423   return 0;
00424 }
00425 
00426 QString KateDocument::configPageName (uint number) const
00427 {
00428   switch( number )
00429   {
00430     case 0:
00431       return i18n ("Appearance");
00432 
00433     case 1:
00434       return i18n ("Fonts & Colors");
00435 
00436     case 2:
00437       return i18n ("Cursor & Selection");
00438 
00439     case 3:
00440       return i18n ("Editing");
00441 
00442     case 4:
00443       return i18n ("Indentation");
00444 
00445     case 5:
00446       return i18n("Open/Save");
00447 
00448     case 6:
00449       return i18n ("Highlighting");
00450 
00451     case 7:
00452       return i18n("Filetypes");
00453 
00454     case 8:
00455       return i18n ("Shortcuts");
00456 
00457     case 9:
00458       return i18n ("Plugins");
00459 
00460     default:
00461       return QString ("");
00462   }
00463 
00464   return QString ("");
00465 }
00466 
00467 QString KateDocument::configPageFullName (uint number) const
00468 {
00469   switch( number )
00470   {
00471     case 0:
00472       return i18n("Appearance");
00473 
00474     case 1:
00475       return i18n ("Font & Color Schemas");
00476 
00477     case 2:
00478       return i18n ("Cursor & Selection Behavior");
00479 
00480     case 3:
00481       return i18n ("Editing Options");
00482 
00483     case 4:
00484       return i18n ("Indentation Rules");
00485 
00486     case 5:
00487       return i18n("File Opening & Saving");
00488 
00489     case 6:
00490       return i18n ("Highlighting Rules");
00491 
00492     case 7:
00493       return i18n("Filetype Specific Settings");
00494 
00495     case 8:
00496       return i18n ("Shortcuts Configuration");
00497 
00498     case 9:
00499       return i18n ("Plugin Manager");
00500 
00501     default:
00502       return QString ("");
00503   }
00504 
00505   return QString ("");
00506 }
00507 
00508 QPixmap KateDocument::configPagePixmap (uint number, int size) const
00509 {
00510   switch( number )
00511   {
00512     case 0:
00513       return BarIcon("view_text",size);
00514 
00515     case 1:
00516       return BarIcon("colorize", size);
00517 
00518     case 2:
00519         return BarIcon("frame_edit", size);
00520 
00521     case 3:
00522       return BarIcon("edit", size);
00523 
00524     case 4:
00525       return BarIcon("rightjust", size);
00526 
00527     case 5:
00528       return BarIcon("filesave", size);
00529 
00530     case 6:
00531       return BarIcon("source", size);
00532 
00533     case 7:
00534       return BarIcon("edit", size);
00535 
00536     case 8:
00537       return BarIcon("key_enter", size);
00538 
00539     case 9:
00540       return BarIcon("connect_established", size);
00541 
00542     default:
00543       return BarIcon("edit", size);
00544   }
00545 
00546   return BarIcon("edit", size);
00547 }
00548 //END
00549 
00550 //BEGIN KTextEditor::EditInterface stuff
00551 
00552 QString KateDocument::text() const
00553 {
00554   QString s;
00555 
00556   for (uint i = 0; i < m_buffer->count(); i++)
00557   {
00558     KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00559 
00560     if (textLine)
00561     {
00562       s.append (textLine->string());
00563 
00564       if ((i+1) < m_buffer->count())
00565         s.append('\n');
00566     }
00567   }
00568 
00569   return s;
00570 }
00571 
00572 QString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol ) const
00573 {
00574   return text(startLine, startCol, endLine, endCol, false);
00575 }
00576 
00577 QString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise) const
00578 {
00579   if ( blockwise && (startCol > endCol) )
00580     return QString ();
00581 
00582   QString s;
00583 
00584   if (startLine == endLine)
00585   {
00586     if (startCol > endCol)
00587       return QString ();
00588 
00589     KateTextLine::Ptr textLine = m_buffer->plainLine(startLine);
00590 
00591     if ( !textLine )
00592       return QString ();
00593 
00594     return textLine->string(startCol, endCol-startCol);
00595   }
00596   else
00597   {
00598 
00599     for (uint i = startLine; (i <= endLine) && (i < m_buffer->count()); i++)
00600     {
00601       KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00602 
00603       if ( !blockwise )
00604       {
00605         if (i == startLine)
00606           s.append (textLine->string(startCol, textLine->length()-startCol));
00607         else if (i == endLine)
00608           s.append (textLine->string(0, endCol));
00609         else
00610           s.append (textLine->string());
00611       }
00612       else
00613       {
00614         s.append( textLine->string( startCol, endCol-startCol));
00615       }
00616 
00617       if ( i < endLine )
00618         s.append('\n');
00619     }
00620   }
00621 
00622   return s;
00623 }
00624 
00625 QString KateDocument::textLine( uint line ) const
00626 {
00627   KateTextLine::Ptr l = m_buffer->plainLine(line);
00628 
00629   if (!l)
00630     return QString();
00631 
00632   return l->string();
00633 }
00634 
00635 bool KateDocument::setText(const QString &s)
00636 {
00637   if (!isReadWrite())
00638     return false;
00639 
00640   QPtrList<KTextEditor::Mark> m = marks ();
00641   QValueList<KTextEditor::Mark> msave;
00642 
00643   for (uint i=0; i < m.count(); i++)
00644     msave.append (*m.at(i));
00645 
00646   editStart ();
00647 
00648   // delete the text
00649   clear();
00650 
00651   // insert the new text
00652   insertText (0, 0, s);
00653 
00654   editEnd ();
00655 
00656   for (uint i=0; i < msave.count(); i++)
00657     setMark (msave[i].line, msave[i].type);
00658 
00659   return true;
00660 }
00661 
00662 bool KateDocument::clear()
00663 {
00664   if (!isReadWrite())
00665     return false;
00666 
00667   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) {
00668     view->clear();
00669     view->tagAll();
00670     view->update();
00671   }
00672 
00673   clearMarks ();
00674 
00675   return removeText (0,0,lastLine()+1, 0);
00676 }
00677 
00678 bool KateDocument::insertText( uint line, uint col, const QString &s)
00679 {
00680   return insertText (line, col, s, false);
00681 }
00682 
00683 bool KateDocument::insertText( uint line, uint col, const QString &s, bool blockwise )
00684 {
00685   if (!isReadWrite())
00686     return false;
00687 
00688   if (s.isEmpty())
00689     return true;
00690 
00691   if (line == numLines())
00692     editInsertLine(line,"");
00693   else if (line > lastLine())
00694     return false;
00695 
00696   editStart ();
00697 
00698   uint insertPos = col;
00699   uint len = s.length();
00700 
00701   QString buf;
00702 
00703   bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn && ! m_isInUndo );
00704   uint tw = config()->tabWidth();
00705   uint insertPosExpanded = insertPos;
00706   KateTextLine::Ptr l = m_buffer->line( line );
00707   if (l != 0)
00708     insertPosExpanded = l->cursorX( insertPos, tw );
00709 
00710   for (uint pos = 0; pos < len; pos++)
00711   {
00712     QChar ch = s[pos];
00713 
00714     if (ch == '\n')
00715     {
00716       editInsertText (line, insertPos, buf);
00717 
00718       if ( !blockwise )
00719       {
00720         editWrapLine (line, insertPos + buf.length());
00721         insertPos = insertPosExpanded = 0;
00722       }
00723       else
00724       {
00725         if ( line == lastLine() )
00726           editWrapLine (line, insertPos + buf.length());
00727       }
00728 
00729       line++;
00730       buf.truncate(0);
00731       l = m_buffer->line( line );
00732       if (l)
00733         insertPosExpanded = l->cursorX( insertPos, tw );
00734     }
00735     else
00736     {
00737       if ( replacetabs && ch == '\t' )
00738       {
00739         uint tr = tw - ( insertPosExpanded+buf.length() )%tw;
00740         for ( uint i=0; i < tr; i++ )
00741           buf += ' ';
00742       }
00743       else
00744         buf += ch; // append char to buffer
00745     }
00746   }
00747 
00748   editInsertText (line, insertPos, buf);
00749 
00750   editEnd ();
00751   emit textInserted(line,insertPos);
00752   return true;
00753 }
00754 
00755 bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol )
00756 {
00757   return removeText (startLine, startCol, endLine, endCol, false);
00758 }
00759 
00760 bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise)
00761 {
00762   if (!isReadWrite())
00763     return false;
00764 
00765   if ( blockwise && (startCol > endCol) )
00766     return false;
00767 
00768   if ( startLine > endLine )
00769     return false;
00770 
00771   if ( startLine > lastLine() )
00772     return false;
00773 
00774   if (!blockwise) {
00775     emit aboutToRemoveText(KateTextRange(startLine,startCol,endLine,endCol));
00776   }
00777   editStart ();
00778 
00779   if ( !blockwise )
00780   {
00781     if ( endLine > lastLine() )
00782     {
00783       endLine = lastLine()+1;
00784       endCol = 0;
00785     }
00786 
00787     if (startLine == endLine)
00788     {
00789       editRemoveText (startLine, startCol, endCol-startCol);
00790     }
00791     else if ((startLine+1) == endLine)
00792     {
00793       if ( (m_buffer->plainLine(startLine)->length()-startCol) > 0 )
00794         editRemoveText (startLine, startCol, m_buffer->plainLine(startLine)->length()-startCol);
00795 
00796       editRemoveText (startLine+1, 0, endCol);
00797       editUnWrapLine (startLine);
00798     }
00799     else
00800     {
00801       for (uint line = endLine; line >= startLine; line--)
00802       {
00803         if ((line > startLine) && (line < endLine))
00804         {
00805           editRemoveLine (line);
00806         }
00807         else
00808         {
00809           if (line == endLine)
00810           {
00811             if ( endLine <= lastLine() )
00812               editRemoveText (line, 0, endCol);
00813           }
00814           else
00815           {
00816             if ( (m_buffer->plainLine(line)->length()-startCol) > 0 )
00817               editRemoveText (line, startCol, m_buffer->plainLine(line)->length()-startCol);
00818 
00819             editUnWrapLine (startLine);
00820           }
00821         }
00822 
00823         if ( line == 0 )
00824           break;
00825       }
00826     }
00827   } // if ( ! blockwise )
00828   else
00829   {
00830     if ( endLine > lastLine() )
00831       endLine = lastLine ();
00832 
00833     for (uint line = endLine; line >= startLine; line--)
00834     {
00835 
00836       editRemoveText (line, startCol, endCol-startCol);
00837 
00838       if ( line == 0 )
00839         break;
00840     }
00841   }
00842 
00843   editEnd ();
00844   emit textRemoved();
00845   return true;
00846 }
00847 
00848 bool KateDocument::insertLine( uint l, const QString &str )
00849 {
00850   if (!isReadWrite())
00851     return false;
00852 
00853   if (l > numLines())
00854     return false;
00855 
00856   return editInsertLine (l, str);
00857 }
00858 
00859 bool KateDocument::removeLine( uint line )
00860 {
00861   if (!isReadWrite())
00862     return false;
00863 
00864   if (line > lastLine())
00865     return false;
00866 
00867   return editRemoveLine (line);
00868 }
00869 
00870 uint KateDocument::length() const
00871 {
00872   uint l = 0;
00873 
00874   for (uint i = 0; i < m_buffer->count(); i++)
00875   {
00876     KateTextLine::Ptr line = m_buffer->plainLine(i);
00877 
00878     if (line)
00879       l += line->length();
00880   }
00881 
00882   return l;
00883 }
00884 
00885 uint KateDocument::numLines() const
00886 {
00887   return m_buffer->count();
00888 }
00889 
00890 uint KateDocument::numVisLines() const
00891 {
00892   return m_buffer->countVisible ();
00893 }
00894 
00895 int KateDocument::lineLength ( uint line ) const
00896 {
00897   KateTextLine::Ptr l = m_buffer->plainLine(line);
00898 
00899   if (!l)
00900     return -1;
00901 
00902   return l->length();
00903 }
00904 //END
00905 
00906 //BEGIN KTextEditor::EditInterface internal stuff
00907 //
00908 // Starts an edit session with (or without) undo, update of view disabled during session
00909 //
00910 void KateDocument::editStart (bool withUndo)
00911 {
00912   editSessionNumber++;
00913 
00914   if (editSessionNumber > 1)
00915     return;
00916 
00917   editIsRunning = true;
00918   editWithUndo = withUndo;
00919 
00920   if (editWithUndo)
00921     undoStart();
00922   else
00923     undoCancel();
00924 
00925   for (uint z = 0; z < m_views.count(); z++)
00926   {
00927     m_views.at(z)->editStart ();
00928   }
00929 
00930   m_buffer->editStart ();
00931 }
00932 
00933 void KateDocument::undoStart()
00934 {
00935   if (m_editCurrentUndo || (m_activeView && m_activeView->imComposeEvent())) return;
00936 
00937   // Make sure the buffer doesn't get bigger than requested
00938   if ((config()->undoSteps() > 0) && (undoItems.count() > config()->undoSteps()))
00939   {
00940     undoItems.setAutoDelete(true);
00941     undoItems.removeFirst();
00942     undoItems.setAutoDelete(false);
00943     docWasSavedWhenUndoWasEmpty = false;
00944   }
00945 
00946   // new current undo item
00947   m_editCurrentUndo = new KateUndoGroup(this);
00948 }
00949 
00950 void KateDocument::undoEnd()
00951 {
00952   if (m_activeView && m_activeView->imComposeEvent())
00953     return;
00954 
00955   if (m_editCurrentUndo)
00956   {
00957     bool changedUndo = false;
00958 
00959     if (m_editCurrentUndo->isEmpty())
00960       delete m_editCurrentUndo;
00961     else if (!m_undoDontMerge && undoItems.last() && undoItems.last()->merge(m_editCurrentUndo,m_undoComplexMerge))
00962       delete m_editCurrentUndo;
00963     else
00964     {
00965       undoItems.append(m_editCurrentUndo);
00966       changedUndo = true;
00967     }
00968 
00969     m_undoDontMerge = false;
00970     m_undoIgnoreCancel = true;
00971 
00972     m_editCurrentUndo = 0L;
00973 
00974     // (Re)Start the single-shot timer to cancel the undo merge
00975     // the user has 5 seconds to input more data, or undo merging gets canceled for the current undo item.
00976     m_undoMergeTimer->start(5000, true);
00977 
00978     if (changedUndo)
00979       emit undoChanged();
00980   }
00981 }
00982 
00983 void KateDocument::undoCancel()
00984 {
00985   if (m_undoIgnoreCancel) {
00986     m_undoIgnoreCancel = false;
00987     return;
00988   }
00989 
00990   m_undoDontMerge = true;
00991 
00992   Q_ASSERT(!m_editCurrentUndo);
00993 
00994   // As you can see by the above assert, neither of these should really be required
00995   delete m_editCurrentUndo;
00996   m_editCurrentUndo = 0L;
00997 }
00998 
00999 void KateDocument::undoSafePoint() {
01000   Q_ASSERT(m_editCurrentUndo);
01001   if (!m_editCurrentUndo) return;
01002   m_editCurrentUndo->safePoint();
01003 }
01004 
01005 //
01006 // End edit session and update Views
01007 //
01008 void KateDocument::editEnd ()
01009 {
01010   if (editSessionNumber == 0)
01011     return;
01012 
01013   // wrap the new/changed text, if something really changed!
01014   if (m_buffer->editChanged() && (editSessionNumber == 1))
01015     if (editWithUndo && config()->wordWrap())
01016       wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
01017 
01018   editSessionNumber--;
01019 
01020   if (editSessionNumber > 0)
01021     return;
01022 
01023   // end buffer edit, will trigger hl update
01024   // this will cause some possible adjustment of tagline start/end
01025   m_buffer->editEnd ();
01026 
01027   if (editWithUndo)
01028     undoEnd();
01029 
01030   // edit end for all views !!!!!!!!!
01031   for (uint z = 0; z < m_views.count(); z++)
01032     m_views.at(z)->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
01033 
01034   if (m_buffer->editChanged())
01035   {
01036     setModified(true);
01037     emit textChanged ();
01038   }
01039 
01040   editIsRunning = false;
01041 }
01042 
01043 bool KateDocument::wrapText (uint startLine, uint endLine)
01044 {
01045   uint col = config()->wordWrapAt();
01046 
01047   if (col == 0)
01048     return false;
01049 
01050   editStart ();
01051 
01052   for (uint line = startLine; (line <= endLine) && (line < numLines()); line++)
01053   {
01054     KateTextLine::Ptr l = m_buffer->line(line);
01055 
01056     if (!l)
01057       return false;
01058 
01059     kdDebug (13020) << "try wrap line: " << line << endl;
01060 
01061     if (l->lengthWithTabs(m_buffer->tabWidth()) > col)
01062     {
01063       KateTextLine::Ptr nextl = m_buffer->line(line+1);
01064 
01065       kdDebug (13020) << "do wrap line: " << line << endl;
01066 
01067       const QChar *text = l->text();
01068       uint eolPosition = l->length()-1;
01069 
01070       // take tabs into account here, too
01071       uint x = 0;
01072       const QString & t = l->string();
01073       uint z2 = 0;
01074       for ( ; z2 < l->length(); z2++)
01075       {
01076         if (t[z2] == QChar('\t'))
01077           x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
01078         else
01079           x++;
01080 
01081         if (x > col)
01082           break;
01083       }
01084 
01085       uint searchStart = kMin (z2, l->length()-1);
01086 
01087       // If where we are wrapping is an end of line and is a space we don't
01088       // want to wrap there
01089       if (searchStart == eolPosition && text[searchStart].isSpace())
01090         searchStart--;
01091 
01092       // Scan backwards looking for a place to break the line
01093       // We are not interested in breaking at the first char
01094       // of the line (if it is a space), but we are at the second
01095       // anders: if we can't find a space, try breaking on a word
01096       // boundry, using KateHighlight::canBreakAt().
01097       // This could be a priority (setting) in the hl/filetype/document
01098       int z = 0;
01099       uint nw = 0; // alternative position, a non word character
01100       for (z=searchStart; z > 0; z--)
01101       {
01102         if (text[z].isSpace()) break;
01103         if ( ! nw && highlight()->canBreakAt( text[z] , l->attribute(z) ) )
01104         nw = z;
01105       }
01106 
01107       if (z > 0)
01108       {
01109         // cu space
01110         editRemoveText (line, z, 1);
01111       }
01112       else
01113       {
01114         // There was no space to break at so break at a nonword character if
01115         // found, or at the wrapcolumn ( that needs be configurable )
01116         // Don't try and add any white space for the break
01117         if ( nw && nw < col ) nw++; // break on the right side of the character
01118         z = nw ? nw : col;
01119       }
01120 
01121       if (nextl && !nextl->isAutoWrapped())
01122       {
01123         editWrapLine (line, z, true);
01124         editMarkLineAutoWrapped (line+1, true);
01125 
01126         endLine++;
01127       }
01128       else
01129       {
01130         if (nextl && (nextl->length() > 0) && !nextl->getChar(0).isSpace() && ((l->length() < 1) || !l->getChar(l->length()-1).isSpace()))
01131           editInsertText (line+1, 0, QString (" "));
01132 
01133         bool newLineAdded = false;
01134         editWrapLine (line, z, false, &newLineAdded);
01135 
01136         editMarkLineAutoWrapped (line+1, true);
01137 
01138         endLine++;
01139       }
01140     }
01141   }
01142 
01143   editEnd ();
01144 
01145   return true;
01146 }
01147 
01148 void KateDocument::editAddUndo (KateUndoGroup::UndoType type, uint line, uint col, uint len, const QString &text)
01149 {
01150   if (editIsRunning && editWithUndo && m_editCurrentUndo) {
01151     m_editCurrentUndo->addItem(type, line, col, len, text);
01152 
01153     // Clear redo buffer
01154     if (redoItems.count()) {
01155       redoItems.setAutoDelete(true);
01156       redoItems.clear();
01157       redoItems.setAutoDelete(false);
01158     }
01159   }
01160 }
01161 
01162 bool KateDocument::editInsertText ( uint line, uint col, const QString &str )
01163 {
01164   if (!isReadWrite())
01165     return false;
01166 
01167   QString s = str;
01168 
01169   KateTextLine::Ptr l = m_buffer->line(line);
01170 
01171   if (!l)
01172     return false;
01173 
01174     if ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn && ! m_isInUndo )
01175     {
01176       uint tw = config()->tabWidth();
01177       int pos = 0;
01178       uint l = 0;
01179       while ( (pos = s.find('\t')) > -1 )
01180       {
01181         l = tw - ( (col + pos)%tw );
01182         s.replace( pos, 1, QString().fill( ' ', l ) );
01183       }
01184     }
01185 
01186   editStart ();
01187 
01188   editAddUndo (KateUndoGroup::editInsertText, line, col, s.length(), s);
01189 
01190   l->insertText (col, s.length(), s.unicode());
01191 //   removeTrailingSpace(line); // ### nessecary?
01192 
01193   m_buffer->changeLine(line);
01194 
01195   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01196     it.current()->editTextInserted (line, col, s.length());
01197 
01198   editEnd ();
01199 
01200   return true;
01201 }
01202 
01203 bool KateDocument::editRemoveText ( uint line, uint col, uint len )
01204 {
01205   if (!isReadWrite())
01206     return false;
01207 
01208   KateTextLine::Ptr l = m_buffer->line(line);
01209 
01210   if (!l)
01211     return false;
01212 
01213   editStart ();
01214 
01215   editAddUndo (KateUndoGroup::editRemoveText, line, col, len, l->string().mid(col, len));
01216 
01217   l->removeText (col, len);
01218   removeTrailingSpace( line );
01219 
01220   m_buffer->changeLine(line);
01221 
01222   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01223     it.current()->editTextRemoved (line, col, len);
01224 
01225   editEnd ();
01226 
01227   return true;
01228 }
01229 
01230 bool KateDocument::editMarkLineAutoWrapped ( uint line, bool autowrapped )
01231 {
01232   if (!isReadWrite())
01233     return false;
01234 
01235   KateTextLine::Ptr l = m_buffer->line(line);
01236 
01237   if (!l)
01238     return false;
01239 
01240   editStart ();
01241 
01242   editAddUndo (KateUndoGroup::editMarkLineAutoWrapped, line, autowrapped ? 1 : 0, 0, QString::null);
01243 
01244   l->setAutoWrapped (autowrapped);
01245 
01246   m_buffer->changeLine(line);
01247 
01248   editEnd ();
01249 
01250   return true;
01251 }
01252 
01253 bool KateDocument::editWrapLine ( uint line, uint col, bool newLine, bool *newLineAdded)
01254 {
01255   if (!isReadWrite())
01256     return false;
01257 
01258   KateTextLine::Ptr l = m_buffer->line(line);
01259 
01260   if (!l)
01261     return false;
01262 
01263   editStart ();
01264 
01265   KateTextLine::Ptr nextLine = m_buffer->line(line+1);
01266 
01267   int pos = l->length() - col;
01268 
01269   if (pos < 0)
01270     pos = 0;
01271 
01272   editAddUndo (KateUndoGroup::editWrapLine, line, col, pos, (!nextLine || newLine) ? "1" : "0");
01273 
01274   if (!nextLine || newLine)
01275   {
01276     KateTextLine::Ptr textLine = new KateTextLine();
01277 
01278     textLine->insertText (0, pos, l->text()+col, l->attributes()+col);
01279     l->truncate(col);
01280 
01281     m_buffer->insertLine (line+1, textLine);
01282     m_buffer->changeLine(line);
01283 
01284     QPtrList<KTextEditor::Mark> list;
01285     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
01286     {
01287       if( it.current()->line >= line )
01288       {
01289         if ((col == 0) || (it.current()->line > line))
01290           list.append( it.current() );
01291       }
01292     }
01293 
01294     for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
01295     {
01296       KTextEditor::Mark* mark = m_marks.take( it.current()->line );
01297       mark->line++;
01298       m_marks.insert( mark->line, mark );
01299     }
01300 
01301     if( !list.isEmpty() )
01302       emit marksChanged();
01303 
01304     // yes, we added a new line !
01305     if (newLineAdded)
01306       (*newLineAdded) = true;
01307   }
01308   else
01309   {
01310     nextLine->insertText (0, pos, l->text()+col, l->attributes()+col);
01311     l->truncate(col);
01312 
01313     m_buffer->changeLine(line);
01314     m_buffer->changeLine(line+1);
01315 
01316     // no, no new line added !
01317     if (newLineAdded)
01318       (*newLineAdded) = false;
01319   }
01320 
01321   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01322     it.current()->editLineWrapped (line, col, !nextLine || newLine);
01323 
01324   editEnd ();
01325 
01326   return true;
01327 }
01328 
01329 bool KateDocument::editUnWrapLine ( uint line, bool removeLine, uint length )
01330 {
01331   if (!isReadWrite())
01332     return false;
01333 
01334   KateTextLine::Ptr l = m_buffer->line(line);
01335   KateTextLine::Ptr nextLine = m_buffer->line(line+1);
01336 
01337   if (!l || !nextLine)
01338     return false;
01339 
01340   editStart ();
01341 
01342   uint col = l->length ();
01343 
01344   editAddUndo (KateUndoGroup::editUnWrapLine, line, col, length, removeLine ? "1" : "0");
01345 
01346   if (removeLine)
01347   {
01348     l->insertText (col, nextLine->length(), nextLine->text(), nextLine->attributes());
01349 
01350     m_buffer->changeLine(line);
01351     m_buffer->removeLine(line+1);
01352   }
01353   else
01354   {
01355     l->insertText (col, (nextLine->length() < length) ? nextLine->length() : length,
01356       nextLine->text(), nextLine->attributes());
01357     nextLine->removeText (0, (nextLine->length() < length) ? nextLine->length() : length);
01358 
01359     m_buffer->changeLine(line);
01360     m_buffer->changeLine(line+1);
01361   }
01362 
01363   QPtrList<KTextEditor::Mark> list;
01364   for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
01365   {
01366     if( it.current()->line >= line+1 )
01367       list.append( it.current() );
01368 
01369     if ( it.current()->line == line+1 )
01370     {
01371       KTextEditor::Mark* mark = m_marks.take( line );
01372 
01373       if (mark)
01374       {
01375         it.current()->type |= mark->type;
01376       }
01377     }
01378   }
01379 
01380   for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
01381   {
01382     KTextEditor::Mark* mark = m_marks.take( it.current()->line );
01383     mark->line--;
01384     m_marks.insert( mark->line, mark );
01385   }
01386 
01387   if( !list.isEmpty() )
01388     emit marksChanged();
01389 
01390   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01391     it.current()->editLineUnWrapped (line, col, removeLine, length);
01392 
01393   editEnd ();
01394 
01395   return true;
01396 }
01397 
01398 bool KateDocument::editInsertLine ( uint line, const QString &s )
01399 {
01400   if (!isReadWrite())
01401     return false;
01402 
01403   if ( line > numLines() )
01404     return false;
01405 
01406   editStart ();
01407 
01408   editAddUndo (KateUndoGroup::editInsertLine, line, 0, s.length(), s);
01409 
01410   removeTrailingSpace( line ); // old line
01411 
01412   KateTextLine::Ptr tl = new KateTextLine();
01413   tl->insertText (0, s.length(), s.unicode(), 0);
01414   m_buffer->insertLine(line, tl);
01415   m_buffer->changeLine(line);
01416 
01417   removeTrailingSpace( line ); // new line
01418 
01419   QPtrList<KTextEditor::Mark> list;
01420   for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
01421   {
01422     if( it.current()->line >= line )
01423       list.append( it.current() );
01424   }
01425 
01426   for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
01427   {
01428     KTextEditor::Mark* mark = m_marks.take( it.current()->line );
01429     mark->line++;
01430     m_marks.insert( mark->line, mark );
01431   }
01432 
01433   if( !list.isEmpty() )
01434     emit marksChanged();
01435 
01436   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01437     it.current()->editLineInserted (line);
01438 
01439   editEnd ();
01440 
01441   return true;
01442 }
01443 
01444 bool KateDocument::editRemoveLine ( uint line )
01445 {
01446   if (!isReadWrite())
01447     return false;
01448 
01449   if ( line > lastLine() )
01450     return false;
01451 
01452   if ( numLines() == 1 )
01453     return editRemoveText (0, 0, m_buffer->line(0)->length());
01454 
01455   editStart ();
01456 
01457   editAddUndo (KateUndoGroup::editRemoveLine, line, 0, lineLength(line), textLine(line));
01458 
01459   m_buffer->removeLine(line);
01460 
01461   QPtrList<KTextEditor::Mark> list;
01462   KTextEditor::Mark* rmark = 0;
01463   for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
01464   {
01465     if ( (it.current()->line > line) )
01466       list.append( it.current() );
01467     else if ( (it.current()->line == line) )
01468       rmark = it.current();
01469   }
01470 
01471   if (rmark)
01472     delete (m_marks.take (rmark->line));
01473 
01474   for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
01475   {
01476     KTextEditor::Mark* mark = m_marks.take( it.current()->line );
01477     mark->line--;
01478     m_marks.insert( mark->line, mark );
01479   }
01480 
01481   if( !list.isEmpty() )
01482     emit marksChanged();
01483 
01484   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01485     it.current()->editLineRemoved (line);
01486 
01487   editEnd();
01488 
01489   return true;
01490 }
01491 //END
01492 
01493 //BEGIN KTextEditor::UndoInterface stuff
01494 
01495 uint KateDocument::undoCount () const
01496 {
01497   return undoItems.count ();
01498 }
01499 
01500 uint KateDocument::redoCount () const
01501 {
01502   return redoItems.count ();
01503 }
01504 
01505 uint KateDocument::undoSteps () const
01506 {
01507   return m_config->undoSteps();
01508 }
01509 
01510 void KateDocument::setUndoSteps(uint steps)
01511 {
01512   m_config->setUndoSteps (steps);
01513 }
01514 
01515 void KateDocument::undo()
01516 {
01517   m_isInUndo = true;
01518   if ((undoItems.count() > 0) && undoItems.last())
01519   {
01520     clearSelection ();
01521 
01522     undoItems.last()->undo();
01523     redoItems.append (undoItems.last());
01524     undoItems.removeLast ();
01525     updateModified();
01526 
01527     emit undoChanged ();
01528   }
01529   m_isInUndo = false;
01530 }
01531 
01532 void KateDocument::redo()
01533 {
01534   m_isInUndo = true;
01535   if ((redoItems.count() > 0) && redoItems.last())
01536   {
01537     clearSelection ();
01538 
01539     redoItems.last()->redo();
01540     undoItems.append (redoItems.last());
01541     redoItems.removeLast ();
01542     updateModified();
01543 
01544     emit undoChanged ();
01545   }
01546   m_isInUndo = false;
01547 }
01548 
01549 void KateDocument::updateModified()
01550 {
01551   // I noticed that there to many variables to take into consideration
01552   // with a simple 'if' or two.
01553   //
01554   // Patterns Detected:
01555   //
01556   //  pattern=223  1==7:true  1==8:false  2==7:false  2==8:false
01557   //  pattern=201  1==7:true  1==8:false  2==7:false  2==8:false
01558   //  pattern=195  1==7:true  1==8:false  2==7:false  2==8:true
01559   //  pattern=105  1==7:true  1==8:false  2==7:false  2==8:true
01560   //  pattern=148  1==7:false 1==8:false  2==7:false  2==8:false
01561   //  pattern=156  1==7:false 1==8:false  2==7:false  2==8:false
01562   //  pattern=149  1==7:false 1==8:true   2==7:false  2==8:false
01563 
01564   unsigned char pattern = 0;
01565   const bool oneEqualToSeven = (lastUndoGroupWhenSaved == undoItems.last());
01566   const bool oneEqualToEight = (lastUndoGroupWhenSaved == redoItems.last());
01567   const bool twoEqualToSeven = (lastRedoGroupWhenSaved == undoItems.last());
01568   const bool twoEqualToEight = (lastRedoGroupWhenSaved == redoItems.last());
01569   bool doModify = false;
01570 
01571   if (lastUndoGroupWhenSaved) pattern |= 1;
01572   if (lastRedoGroupWhenSaved) pattern |= 2;
01573   if (docWasSavedWhenUndoWasEmpty) pattern |= 4;
01574   if (docWasSavedWhenRedoWasEmpty) pattern |= 8;
01575   if (undoItems.isEmpty()) pattern |= 16;
01576   if (redoItems.isEmpty()) pattern |= 32;
01577   if (undoItems.last()) pattern |= 64;
01578   if (redoItems.last()) pattern |= 128;
01579 
01580   switch (pattern)
01581   {
01582     case 223: case 201:
01583         doModify = (oneEqualToSeven);
01584       break;
01585 
01586     case 195: case 105:
01587         doModify = (oneEqualToSeven && twoEqualToEight);
01588       break;
01589 
01590     case 148: case 156:
01591         doModify = true;
01592       break;
01593 
01594     case 149: case 151:
01595         doModify = (oneEqualToEight);
01596       break;
01597   }  
01598 
01599   if (doModify)
01600   {
01601     setModified( false );
01602     kdDebug(13020) << k_funcinfo << "setting modified to false!" << endl;
01603   };
01604 
01605   kdDebug(13020) << k_funcinfo << "pattern=" << static_cast<unsigned int>(pattern) << " 1==7:" << oneEqualToSeven 
01606   << " 1==8:" << oneEqualToEight << " 2==7:" << twoEqualToSeven << " 2==8:" << twoEqualToEight << endl;
01607 }
01608 
01609 void KateDocument::clearUndo()
01610 {
01611   undoItems.setAutoDelete (true);
01612   undoItems.clear ();
01613   undoItems.setAutoDelete (false);
01614 
01615   lastUndoGroupWhenSaved = 0;
01616   docWasSavedWhenUndoWasEmpty = false;
01617 
01618   emit undoChanged ();
01619 }
01620 
01621 void KateDocument::clearRedo()
01622 {
01623   redoItems.setAutoDelete (true);
01624   redoItems.clear ();
01625   redoItems.setAutoDelete (false);
01626 
01627   lastRedoGroupWhenSaved = 0;
01628   docWasSavedWhenRedoWasEmpty = false;
01629 
01630   emit undoChanged ();
01631 }
01632 
01633 QPtrList<KTextEditor::Cursor> KateDocument::cursors () const
01634 {
01635   return myCursors;
01636 }
01637 //END
01638 
01639 //BEGIN KTextEditor::SearchInterface stuff
01640 
01641 bool KateDocument::searchText (unsigned int startLine, unsigned int startCol, const QString &text, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool casesensitive, bool backwards)
01642 {
01643   if (text.isEmpty())
01644     return false;
01645 
01646   int line = startLine;
01647   int col = startCol;
01648 
01649   if (!backwards)
01650   {
01651     int searchEnd = lastLine();
01652 
01653     while (line <= searchEnd)
01654     {
01655       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01656 
01657       if (!textLine)
01658         return false;
01659 
01660       uint foundAt, myMatchLen;
01661       bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, false);
01662 
01663       if (found)
01664       {
01665         (*foundAtLine) = line;
01666         (*foundAtCol) = foundAt;
01667         (*matchLen) = myMatchLen;
01668         return true;
01669       }
01670 
01671       col = 0;
01672       line++;
01673     }
01674   }
01675   else
01676   {
01677     // backward search
01678     int searchEnd = 0;
01679 
01680     while (line >= searchEnd)
01681     {
01682       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01683 
01684       if (!textLine)
01685         return false;
01686 
01687       uint foundAt, myMatchLen;
01688       bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, true);
01689 
01690       if (found)
01691       {
01692        /* if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
01693             && line == selectStart.line() && foundAt == (uint) selectStart.col()
01694             && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
01695         {
01696           // To avoid getting stuck at one match we skip a match if it is already
01697           // selected (most likely because it has just been found).
01698           if (foundAt > 0)
01699             col = foundAt - 1;
01700           else {
01701             if (--line >= 0)
01702               col = lineLength(line);
01703           }
01704           continue;
01705       }*/
01706 
01707         (*foundAtLine) = line;
01708         (*foundAtCol) = foundAt;
01709         (*matchLen) = myMatchLen;
01710         return true;
01711       }
01712 
01713       if (line >= 1)
01714         col = lineLength(line-1);
01715 
01716       line--;
01717     }
01718   }
01719 
01720   return false;
01721 }
01722 
01723 bool KateDocument::searchText (unsigned int startLine, unsigned int startCol, const QRegExp &regexp, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool backwards)
01724 {
01725   kdDebug(13020)<<"KateDocument::searchText( "<<startLine<<", "<<startCol<<", "<<regexp.pattern()<<", "<<backwards<<" )"<<endl;
01726   if (regexp.isEmpty() || !regexp.isValid())
01727     return false;
01728 
01729   int line = startLine;
01730   int col = startCol;
01731 
01732   if (!backwards)
01733   {
01734     int searchEnd = lastLine();
01735 
01736     while (line <= searchEnd)
01737     {
01738       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01739 
01740       if (!textLine)
01741         return false;
01742 
01743       uint foundAt, myMatchLen;
01744       bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, false);
01745 
01746       if (found)
01747       {
01748         // A special case which can only occur when searching with a regular expression consisting
01749         // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{').
01750         if (myMatchLen == 0 && (uint) line == startLine && foundAt == (uint) col)
01751         {
01752           if (col < lineLength(line))
01753             col++;
01754           else {
01755             line++;
01756             col = 0;
01757           }
01758           continue;
01759         }
01760 
01761         (*foundAtLine) = line;
01762         (*foundAtCol) = foundAt;
01763         (*matchLen) = myMatchLen;
01764         return true;
01765       }
01766 
01767       col = 0;
01768       line++;
01769     }
01770   }
01771   else
01772   {
01773     // backward search
01774     int searchEnd = 0;
01775 
01776     while (line >= searchEnd)
01777     {
01778       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01779 
01780       if (!textLine)
01781         return false;
01782 
01783       uint foundAt, myMatchLen;
01784       bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, true);
01785 
01786       if (found)
01787       {
01788         /*if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
01789             && line == selectStart.line() && foundAt == (uint) selectStart.col()
01790             && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
01791         {
01792           // To avoid getting stuck at one match we skip a match if it is already
01793           // selected (most likely because it has just been found).
01794           if (foundAt > 0)
01795             col = foundAt - 1;
01796           else {
01797             if (--line >= 0)
01798               col = lineLength(line);
01799           }
01800           continue;
01801       }*/
01802 
01803         (*foundAtLine) = line;
01804         (*foundAtCol) = foundAt;
01805         (*matchLen) = myMatchLen;
01806         return true;
01807       }
01808 
01809       if (line >= 1)
01810         col = lineLength(line-1);
01811 
01812       line--;
01813     }
01814   }
01815 
01816   return false;
01817 }
01818 //END
01819 
01820 //BEGIN KTextEditor::HighlightingInterface stuff
01821 
01822 uint KateDocument::hlMode ()
01823 {
01824   return KateHlManager::self()->findHl(highlight());
01825 }
01826 
01827 bool KateDocument::setHlMode (uint mode)
01828 {
01829   m_buffer->setHighlight (mode);
01830 
01831   if (true)
01832   {
01833     setDontChangeHlOnSave();
01834     return true;
01835   }
01836 
01837   return false;
01838 }
01839 
01840 void KateDocument::bufferHlChanged ()
01841 {
01842   // update all views
01843   makeAttribs(false);
01844 
01845   emit hlChanged();
01846 }
01847 
01848 uint KateDocument::hlModeCount ()
01849 {
01850   return KateHlManager::self()->highlights();
01851 }
01852 
01853 QString KateDocument::hlModeName (uint mode)
01854 {
01855   return KateHlManager::self()->hlName (mode);
01856 }
01857 
01858 QString KateDocument::hlModeSectionName (uint mode)
01859 {
01860   return KateHlManager::self()->hlSection (mode);
01861 }
01862 
01863 void KateDocument::setDontChangeHlOnSave()
01864 {
01865   hlSetByUser = true;
01866 }
01867 //END
01868 
01869 //BEGIN KTextEditor::ConfigInterface stuff
01870 void KateDocument::readConfig(KConfig *config)
01871 {
01872   config->setGroup("Kate Document Defaults");
01873 
01874   // read max loadable blocks, more blocks will be swapped out
01875   KateBuffer::setMaxLoadedBlocks (config->readNumEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks()));
01876 
01877   KateDocumentConfig::global()->readConfig (config);
01878 
01879   config->setGroup("Kate View Defaults");
01880   KateViewConfig::global()->readConfig (config);
01881 
01882   config->setGroup("Kate Renderer Defaults");
01883   KateRendererConfig::global()->readConfig (config);
01884 }
01885 
01886 void KateDocument::writeConfig(KConfig *config)
01887 {
01888   config->setGroup("Kate Document Defaults");
01889 
01890   // write max loadable blocks, more blocks will be swapped out
01891   config->writeEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks());
01892 
01893   KateDocumentConfig::global()->writeConfig (config);
01894 
01895   config->setGroup("Kate View Defaults");
01896   KateViewConfig::global()->writeConfig (config);
01897 
01898   config->setGroup("Kate Renderer Defaults");
01899   KateRendererConfig::global()->writeConfig (config);
01900 }
01901 
01902 void KateDocument::readConfig()
01903 {
01904   KConfig *config = kapp->config();
01905   readConfig (config);
01906 }
01907 
01908 void KateDocument::writeConfig()
01909 {
01910   KConfig *config = kapp->config();
01911   writeConfig (config);
01912   config->sync();
01913 }
01914 
01915 void KateDocument::readSessionConfig(KConfig *kconfig)
01916 {
01917   // restore the url
01918   KURL url (kconfig->readEntry("URL"));
01919 
01920   // get the encoding
01921   QString tmpenc=kconfig->readEntry("Encoding");
01922   if (!tmpenc.isEmpty() && (tmpenc != encoding()))
01923     setEncoding(tmpenc);
01924 
01925   // open the file if url valid
01926   if (!url.isEmpty() && url.isValid())
01927     openURL (url);
01928 
01929   // restore the hl stuff
01930   m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig->readEntry("Highlighting")));
01931 
01932   if (hlMode() > 0)
01933     hlSetByUser = true;
01934 
01935   // indent mode
01936   config()->setIndentationMode( (uint)kconfig->readNumEntry("Indentation Mode", config()->indentationMode() ) );
01937 
01938   // Restore Bookmarks
01939   QValueList<int> marks = kconfig->readIntListEntry("Bookmarks");
01940   for( uint i = 0; i < marks.count(); i++ )
01941     addMark( marks[i], KateDocument::markType01 );
01942 }
01943 
01944 void KateDocument::writeSessionConfig(KConfig *kconfig)
01945 {
01946   if ( m_url.isLocalFile() && !KGlobal::dirs()->relativeLocation("tmp", m_url.path()).startsWith("/"))
01947        return;
01948   // save url
01949   kconfig->writeEntry("URL", m_url.prettyURL() );
01950 
01951   // save encoding
01952   kconfig->writeEntry("Encoding",encoding());
01953 
01954   // save hl
01955   kconfig->writeEntry("Highlighting", highlight()->name());
01956 
01957   kconfig->writeEntry("Indentation Mode", config()->indentationMode() );
01958 
01959   // Save Bookmarks
01960   QValueList<int> marks;
01961   for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
01962        it.current() && it.current()->type & KTextEditor::MarkInterface::markType01;
01963        ++it )
01964      marks << it.current()->line;
01965 
01966   kconfig->writeEntry( "Bookmarks", marks );
01967 }
01968 
01969 void KateDocument::configDialog()
01970 {
01971   KDialogBase *kd = new KDialogBase ( KDialogBase::IconList,
01972                                       i18n("Configure"),
01973                                       KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help,
01974                                       KDialogBase::Ok,
01975                                       kapp->mainWidget() );
01976 
01977 #ifndef Q_WS_WIN //TODO: reenable
01978   KWin::setIcons( kd->winId(), kapp->icon(), kapp->miniIcon() );
01979 #endif
01980 
01981   QPtrList<KTextEditor::ConfigPage> editorPages;
01982 
01983   for (uint i = 0; i < KTextEditor::configInterfaceExtension (this)->configPages (); i++)
01984   {
01985     QStringList path;
01986     path.clear();
01987     path << KTextEditor::configInterfaceExtension (this)->configPageName (i);
01988     QVBox *page = kd->addVBoxPage(path, KTextEditor::configInterfaceExtension (this)->configPageFullName (i),
01989                               KTextEditor::configInterfaceExtension (this)->configPagePixmap(i, KIcon::SizeMedium) );
01990 
01991     editorPages.append (KTextEditor::configInterfaceExtension (this)->configPage(i, page));
01992   }
01993 
01994   if (kd->exec())
01995   {
01996     KateDocumentConfig::global()->configStart ();
01997     KateViewConfig::global()->configStart ();
01998     KateRendererConfig::global()->configStart ();
01999 
02000     for (uint i=0; i<editorPages.count(); i++)
02001     {
02002       editorPages.at(i)->apply();
02003     }
02004 
02005     KateDocumentConfig::global()->configEnd ();
02006     KateViewConfig::global()->configEnd ();
02007     KateRendererConfig::global()->configEnd ();
02008 
02009     writeConfig ();
02010   }
02011 
02012   delete kd;
02013 }
02014 
02015 uint KateDocument::mark( uint line )
02016 {
02017   if( !m_marks[line] )
02018     return 0;
02019   return m_marks[line]->type;
02020 }
02021 
02022 void KateDocument::setMark( uint line, uint markType )
02023 {
02024   clearMark( line );
02025   addMark( line, markType );
02026 }
02027 
02028 void KateDocument::clearMark( uint line )
02029 {
02030   if( line > lastLine() )
02031     return;
02032 
02033   if( !m_marks[line] )
02034     return;
02035 
02036   KTextEditor::Mark* mark = m_marks.take( line );
02037   emit markChanged( *mark, MarkRemoved );
02038   emit marksChanged();
02039   delete mark;
02040   tagLines( line, line );
02041   repaintViews(true);
02042 }
02043 
02044 void KateDocument::addMark( uint line, uint markType )
02045 {
02046   if( line > lastLine())
02047     return;
02048 
02049   if( markType == 0 )
02050     return;
02051 
02052   if( m_marks[line] ) {
02053     KTextEditor::Mark* mark = m_marks[line];
02054 
02055     // Remove bits already set
02056     markType &= ~mark->type;
02057 
02058     if( markType == 0 )
02059       return;
02060 
02061     // Add bits
02062     mark->type |= markType;
02063   } else {
02064     KTextEditor::Mark *mark = new KTextEditor::Mark;
02065     mark->line = line;
02066     mark->type = markType;
02067     m_marks.insert( line, mark );
02068   }
02069 
02070   // Emit with a mark having only the types added.
02071   KTextEditor::Mark temp;
02072   temp.line = line;
02073   temp.type = markType;
02074   emit markChanged( temp, MarkAdded );
02075 
02076   emit marksChanged();
02077   tagLines( line, line );
02078   repaintViews(true);
02079 }
02080 
02081 void KateDocument::removeMark( uint line, uint markType )
02082 {
02083   if( line > lastLine() )
02084     return;
02085   if( !m_marks[line] )
02086     return;
02087 
02088   KTextEditor::Mark* mark = m_marks[line];
02089 
02090   // Remove bits not set
02091   markType &= mark->type;
02092 
02093   if( markType == 0 )
02094     return;
02095 
02096   // Subtract bits
02097   mark->type &= ~markType;
02098 
02099   // Emit with a mark having only the types removed.
02100   KTextEditor::Mark temp;
02101   temp.line = line;
02102   temp.type = markType;
02103   emit markChanged( temp, MarkRemoved );
02104 
02105   if( mark->type == 0 )
02106     m_marks.remove( line );
02107 
02108   emit marksChanged();
02109   tagLines( line, line );
02110   repaintViews(true);
02111 }
02112 
02113 QPtrList<KTextEditor::Mark> KateDocument::marks()
02114 {
02115   QPtrList<KTextEditor::Mark> list;
02116 
02117   for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
02118        it.current(); ++it ) {
02119     list.append( it.current() );
02120   }
02121 
02122   return list;
02123 }
02124 
02125 void KateDocument::clearMarks()
02126 {
02127   for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
02128        it.current(); ++it ) {
02129     KTextEditor::Mark* mark = it.current();
02130     emit markChanged( *mark, MarkRemoved );
02131     tagLines( mark->line, mark->line );
02132   }
02133 
02134   m_marks.clear();
02135 
02136   emit marksChanged();
02137   repaintViews(true);
02138 }
02139 
02140 void KateDocument::setPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
02141 {
02142   m_markPixmaps.replace( type, new QPixmap( pixmap ) );
02143 }
02144 
02145 void KateDocument::setDescription( MarkInterface::MarkTypes type, const QString& description )
02146 {
02147   m_markDescriptions.replace( type, new QString( description ) );
02148 }
02149 
02150 QPixmap *KateDocument::markPixmap( MarkInterface::MarkTypes type )
02151 {
02152   return m_markPixmaps[type];
02153 }
02154 
02155 QColor KateDocument::markColor( MarkInterface::MarkTypes type )
02156 {
02157   uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
02158   if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
02159     return KateRendererConfig::global()->lineMarkerColor(type);
02160   } else {
02161     return QColor();
02162   }
02163 }
02164 
02165 QString KateDocument::markDescription( MarkInterface::MarkTypes type )
02166 {
02167   if( m_markDescriptions[type] )
02168     return *m_markDescriptions[type];
02169   return QString::null;
02170 }
02171 
02172 void KateDocument::setMarksUserChangable( uint markMask )
02173 {
02174   m_editableMarks = markMask;
02175 }
02176 
02177 uint KateDocument::editableMarks()
02178 {
02179   return m_editableMarks;
02180 }
02181 //END
02182 
02183 //BEGIN KTextEditor::PrintInterface stuff
02184 bool KateDocument::printDialog ()
02185 {
02186   return KatePrinter::print (this);
02187 }
02188 
02189 bool KateDocument::print ()
02190 {
02191   return KatePrinter::print (this);
02192 }
02193 //END
02194 
02195 //BEGIN KTextEditor::DocumentInfoInterface (### unfinished)
02196 QString KateDocument::mimeType()
02197 {
02198   KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
02199 
02200   // if the document has a URL, try KMimeType::findByURL
02201   if ( ! m_url.isEmpty() )
02202     result = KMimeType::findByURL( m_url );
02203 
02204   else if ( m_url.isEmpty() || ! m_url.isLocalFile() )
02205     result = mimeTypeForContent();
02206 
02207   return result->name();
02208 }
02209 
02210 // TODO implement this -- how to calculate?
02211 long KateDocument::fileSize()
02212 {
02213   return 0;
02214 }
02215 
02216 // TODO implement this
02217 QString KateDocument::niceFileSize()
02218 {
02219   return "UNKNOWN";
02220 }
02221 
02222 KMimeType::Ptr KateDocument::mimeTypeForContent()
02223 {
02224   QByteArray buf (1024);
02225   uint bufpos = 0;
02226 
02227   for (uint i=0; i < numLines(); i++)
02228   {
02229     QString line = textLine( i );
02230     uint len = line.length() + 1;
02231 
02232     if (bufpos + len > 1024)
02233       len = 1024 - bufpos;
02234 
02235     memcpy(&buf[bufpos], (line + "\n").latin1(), len);
02236 
02237     bufpos += len;
02238 
02239     if (bufpos >= 1024)
02240       break;
02241   }
02242   buf.resize( bufpos );
02243 
02244   int accuracy = 0;
02245   return KMimeType::findByContent( buf, &accuracy );
02246 }
02247 //END KTextEditor::DocumentInfoInterface
02248 
02249 
02250 //BEGIN KParts::ReadWrite stuff
02251 
02252 bool KateDocument::openURL( const KURL &url )
02253 {
02254 //   kdDebug(13020)<<"KateDocument::openURL( "<<url.prettyURL()<<")"<<endl;
02255   // no valid URL
02256   if ( !url.isValid() )
02257     return false;
02258 
02259   // could not close old one
02260   if ( !closeURL() )
02261     return false;
02262 
02263   // set my url
02264   m_url = url;
02265 
02266   if ( m_url.isLocalFile() )
02267   {
02268     // local mode, just like in kpart
02269 
02270     m_file = m_url.path();
02271 
02272     emit started( 0 );
02273 
02274     if (openFile())
02275     {
02276       emit completed();
02277       emit setWindowCaption( m_url.prettyURL() );
02278 
02279       return true;
02280     }
02281 
02282     return false;
02283   }
02284   else
02285   {
02286     // remote mode
02287 
02288     m_bTemp = true;
02289 
02290     m_tempFile = new KTempFile ();
02291     m_file = m_tempFile->name();
02292 
02293     m_job = KIO::get ( url, false, isProgressInfoEnabled() );
02294 
02295     // connect to slots
02296     connect( m_job, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
02297            SLOT( slotDataKate( KIO::Job*, const QByteArray& ) ) );
02298 
02299     connect( m_job, SIGNAL( result( KIO::Job* ) ),
02300            SLOT( slotFinishedKate( KIO::Job* ) ) );
02301 
02302     QWidget *w = widget ();
02303     if (!w && !m_views.isEmpty ())
02304       w = m_views.first();
02305 
02306     if (w)
02307       m_job->setWindow (w->topLevelWidget());
02308 
02309     emit started( m_job );
02310 
02311     return true;
02312   }
02313 }
02314 
02315 void KateDocument::slotDataKate ( KIO::Job *, const QByteArray &data )
02316 {
02317 //   kdDebug(13020) << "KateDocument::slotData" << endl;
02318 
02319   if (!m_tempFile || !m_tempFile->file())
02320     return;
02321 
02322   m_tempFile->file()->writeBlock (data);
02323 }
02324 
02325 void KateDocument::slotFinishedKate ( KIO::Job * job )
02326 {
02327 //   kdDebug(13020) << "KateDocument::slotJobFinished" << endl;
02328 
02329   if (!m_tempFile)
02330     return;
02331 
02332   delete m_tempFile;
02333   m_tempFile = 0;
02334   m_job = 0;
02335 
02336   if (job->error())
02337     emit canceled( job->errorString() );
02338   else
02339   {
02340     if ( openFile(job) )
02341       emit setWindowCaption( m_url.prettyURL() );
02342     emit completed();
02343   }
02344 }
02345 
02346 void KateDocument::abortLoadKate()
02347 {
02348   if ( m_job )
02349   {
02350     kdDebug(13020) << "Aborting job " << m_job << endl;
02351     m_job->kill();
02352     m_job = 0;
02353   }
02354 
02355   delete m_tempFile;
02356   m_tempFile = 0;
02357 }
02358 
02359 bool KateDocument::openFile()
02360 {
02361   return openFile (0);
02362 }
02363 
02364 bool KateDocument::openFile(KIO::Job * job)
02365 {
02366   m_loading = true;
02367   // add new m_file to dirwatch
02368   activateDirWatch ();
02369 
02370   //
02371   // use metadata
02372   //
02373   if (job)
02374   {
02375     QString metaDataCharset = job->queryMetaData("charset");
02376 
02377     // only overwrite config if nothing set
02378     if (!metaDataCharset.isEmpty () && (!m_config->isSetEncoding() || m_config->encoding().isEmpty()))
02379       setEncoding (metaDataCharset);
02380   }
02381 
02382   //
02383   // service type magic to get encoding right
02384   //
02385   QString serviceType = m_extension->urlArgs().serviceType.simplifyWhiteSpace();
02386   int pos = serviceType.find(';');
02387   if (pos != -1)
02388     setEncoding (serviceType.mid(pos+1));
02389 
02390   // if the encoding is set here - on the command line/from the dialog/from KIO
02391   // we prevent file type and document variables from changing it
02392   bool encodingSticky = m_encodingSticky;
02393   m_encodingSticky = m_config->isSetEncoding();
02394 
02395   // Try getting the filetype here, so that variables does not have to be reset.
02396   int fileTypeFound = KateFactory::self()->fileTypeManager()->fileType (this);
02397   if ( fileTypeFound > -1 )
02398     updateFileType( fileTypeFound );
02399 
02400   // do we have success ?
02401   bool success = m_buffer->openFile (m_file);
02402   //
02403   // yeah, success
02404   //
02405   m_loading = false; // done reading file.
02406   if (success)
02407   {
02408     /*if (highlight() && !m_url.isLocalFile()) {
02409       // The buffer's highlighting gets nuked by KateBuffer::clear()
02410       m_buffer->setHighlight(m_highlight);
02411   }*/
02412 
02413     // update our hl type if needed
02414     if (!hlSetByUser)
02415     {
02416       int hl (KateHlManager::self()->detectHighlighting (this));
02417 
02418       if (hl >= 0)
02419         m_buffer->setHighlight(hl);
02420     }
02421 
02422     // update file type if we haven't allready done so.
02423     if ( fileTypeFound < 0 )
02424       updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
02425 
02426     // read dir config (if possible and wanted)
02427     readDirConfig ();
02428 
02429     // read vars
02430     readVariables();
02431 
02432     // update the md5 digest
02433     createDigest( m_digest );
02434   }
02435 
02436   //
02437   // update views
02438   //
02439   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
02440   {
02441     view->updateView(true);
02442   }
02443 
02444   //
02445   // emit the signal we need for example for kate app
02446   //
02447   emit fileNameChanged ();
02448 
02449   //
02450   // set doc name, dummy value as arg, don't need it
02451   //
02452   setDocName  (QString::null);
02453 
02454   //
02455   // to houston, we are not modified
02456   //
02457   if (m_modOnHd)
02458   {
02459     m_modOnHd = false;
02460     m_modOnHdReason = 0;
02461     emit modifiedOnDisc (this, m_modOnHd, 0);
02462   }
02463 
02464   //
02465   // display errors
02466   //
02467   if (s_openErrorDialogsActivated)
02468   {
02469     if (!success && m_buffer->loadingBorked())
02470       KMessageBox::error (widget(), i18n ("The file %1 could not be loaded completely, as there is not enough temporary disk storage for it.").arg(m_url.url()));
02471     else if (!success)
02472       KMessageBox::error (widget(), 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.").arg(m_url.url()));
02473   }
02474 
02475   // warn -> opened binary file!!!!!!!
02476   if (m_buffer->binary())
02477   {
02478     // this file can't be saved again without killing it
02479     setReadWrite( false );
02480 
02481     KMessageBox::information (widget()
02482       , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
02483       , i18n ("Binary File Opened")
02484       , "Binary File Opened Warning");
02485   }
02486 
02487   m_encodingSticky = encodingSticky;
02488 
02489   //
02490   // return the success
02491   //
02492   return success;
02493 }
02494 
02495 bool KateDocument::save()
02496 {
02497   bool l ( url().isLocalFile() );
02498 
02499   if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
02500        || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
02501   {
02502     KURL u( url() );
02503     u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
02504 
02505     kdDebug () << "backup src file name: " << url() << endl;
02506     kdDebug () << "backup dst file name: " << u << endl;
02507 
02508     // get the right permissions, start with safe default
02509     mode_t  perms = 0600;
02510     KIO::UDSEntry fentry;
02511     if (KIO::NetAccess::stat (url(), fentry, kapp->mainWidget()))
02512     {
02513       kdDebug () << "stating succesfull: " << url() << endl;
02514       KFileItem item (fentry, url());
02515       perms = item.permissions();
02516     }
02517 
02518     // first del existing file if any, than copy over the file we have
02519     // failure if a: the existing file could not be deleted, b: the file could not be copied
02520     if ( (!KIO::NetAccess::exists( u, false, kapp->mainWidget() ) || KIO::NetAccess::del( u, kapp->mainWidget() ))
02521           && KIO::NetAccess::file_copy( url(), u, perms, true, false, kapp->mainWidget() ) )
02522     {
02523       kdDebug(13020)<<"backing up successfull ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
02524     }
02525     else
02526     {
02527       kdDebug(13020)<<"backing up failed ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
02528       // FIXME: notify user for real ;)
02529     }
02530   }
02531 
02532   return KParts::ReadWritePart::save();
02533 }
02534 
02535 bool KateDocument::saveFile()
02536 {
02537   //
02538   // we really want to save this file ?
02539   //
02540   if (m_buffer->loadingBorked() && (KMessageBox::warningContinueCancel(widget(),
02541       i18n("This file could not be loaded correctly due to lack of temporary disk space. Saving it could cause data loss.\n\nDo you really want to save it?"),i18n("Possible Data Loss"),i18n("Save Nevertheless")) != KMessageBox::Continue))
02542     return false;
02543 
02544   //
02545   // warn -> try to save binary file!!!!!!!
02546   //
02547   if (m_buffer->binary() && (KMessageBox::warningContinueCancel (widget()
02548         , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
02549         , i18n ("Trying to Save Binary File")
02550         , i18n("Save Nevertheless"), "Binary File Save Warning") != KMessageBox::Continue))
02551     return false;
02552 
02553   if ( !url().isEmpty() )
02554   {
02555     if (s_fileChangedDialogsActivated && m_modOnHd)
02556     {
02557       QString str = reasonedMOHString() + "\n\n";
02558 
02559       if (!isModified())
02560       {
02561         if (KMessageBox::warningContinueCancel(0,
02562                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"),i18n("Save Nevertheless")) != KMessageBox::Continue)
02563           return false;
02564       }
02565       else
02566       {
02567         if (KMessageBox::warningContinueCancel(0,
02568                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"),i18n("Save Nevertheless")) != KMessageBox::Continue)
02569           return false;
02570       }
02571     }
02572   }
02573 
02574   //
02575   // can we encode it if we want to save it ?
02576   //
02577   if (!m_buffer->canEncode ()
02578        && (KMessageBox::warningContinueCancel(0,
02579            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"),i18n("Save Nevertheless")) != KMessageBox::Continue))
02580   {
02581     return false;
02582   }
02583 
02584   // remove file from dirwatch
02585   deactivateDirWatch ();
02586 
02587   //
02588   // try to save
02589   //
02590   bool success = m_buffer->saveFile (m_file);
02591 
02592   // update the md5 digest
02593   createDigest( m_digest );
02594 
02595   // add m_file again to dirwatch
02596   activateDirWatch ();
02597 
02598   //
02599   // hurray, we had success, do stuff we need
02600   //
02601   if (success)
02602   {
02603     // update our hl type if needed
02604     if (!hlSetByUser)
02605     {
02606       int hl (KateHlManager::self()->detectHighlighting (this));
02607 
02608       if (hl >= 0)
02609         m_buffer->setHighlight(hl);
02610     }
02611 
02612     // read our vars
02613     readVariables();
02614   }
02615 
02616   //
02617   // we are not modified
02618   //
02619   if (success && m_modOnHd)
02620   {
02621     m_modOnHd = false;
02622     m_modOnHdReason = 0;
02623     emit modifiedOnDisc (this, m_modOnHd, 0);
02624   }
02625 
02626   //
02627   // display errors
02628   //
02629   if (!success)
02630     KMessageBox::error (widget(), 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.").arg(m_url.url()));
02631 
02632   //
02633   // return success
02634   //
02635   return success;
02636 }
02637 
02638 bool KateDocument::saveAs( const KURL &u )
02639 {
02640   QString oldDir = url().directory();
02641 
02642   if ( KParts::ReadWritePart::saveAs( u ) )
02643   {
02644     // null means base on filename
02645     setDocName( QString::null );
02646 
02647     if ( u.directory() != oldDir )
02648       readDirConfig();
02649 
02650     emit fileNameChanged();
02651     emit nameChanged((Kate::Document *) this);
02652 
02653     return true;
02654   }
02655 
02656   return false;
02657 }
02658 
02659 void KateDocument::readDirConfig ()
02660 {
02661   int depth = config()->searchDirConfigDepth ();
02662 
02663   if (m_url.isLocalFile() && (depth > -1))
02664   {
02665     QString currentDir = QFileInfo (m_file).dirPath();
02666 
02667     // only search as deep as specified or not at all ;)
02668     while (depth > -1)
02669     {
02670       kdDebug (13020) << "search for config file in path: " << currentDir << endl;
02671 
02672       // try to open config file in this dir
02673       QFile f (currentDir + "/.kateconfig");
02674 
02675       if (f.open (IO_ReadOnly))
02676       {
02677         QTextStream stream (&f);
02678 
02679         uint linesRead = 0;
02680         QString line = stream.readLine();
02681         while ((linesRead < 32) && !line.isNull())
02682         {
02683           readVariableLine( line );
02684 
02685           line = stream.readLine();
02686 
02687           linesRead++;
02688         }
02689 
02690         break;
02691       }
02692 
02693       QString newDir = QFileInfo (currentDir).dirPath();
02694 
02695       // bail out on looping (for example reached /)
02696       if (currentDir == newDir)
02697         break;
02698 
02699       currentDir = newDir;
02700       --depth;
02701     }
02702   }
02703 }
02704 
02705 void KateDocument::activateDirWatch ()
02706 {
02707   // same file as we are monitoring, return
02708   if (m_file == m_dirWatchFile)
02709     return;
02710 
02711   // remove the old watched file
02712   deactivateDirWatch ();
02713 
02714   // add new file if needed
02715   if (m_url.isLocalFile() && !m_file.isEmpty())
02716   {
02717     KateFactory::self()->dirWatch ()->addFile (m_file);
02718     m_dirWatchFile = m_file;
02719   }
02720 }
02721 
02722 void KateDocument::deactivateDirWatch ()
02723 {
02724   if (!m_dirWatchFile.isEmpty())
02725     KateFactory::self()->dirWatch ()->removeFile (m_dirWatchFile);
02726 
02727   m_dirWatchFile = QString::null;
02728 }
02729 
02730 bool KateDocument::closeURL()
02731 {
02732   abortLoadKate();
02733 
02734   //
02735   // file mod on hd
02736   //
02737   if ( !m_reloading && !url().isEmpty() )
02738   {
02739     if (s_fileChangedDialogsActivated && m_modOnHd)
02740     {
02741       if (!(KMessageBox::warningContinueCancel(
02742             widget(),
02743             reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
02744             i18n("Possible Data Loss"), i18n("Close Nevertheless"),
02745             QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
02746         return false;
02747     }
02748   }
02749 
02750   //
02751   // first call the normal kparts implementation
02752   //
02753   if (!KParts::ReadWritePart::closeURL ())
02754     return false;
02755 
02756   // remove file from dirwatch
02757   deactivateDirWatch ();
02758 
02759   //
02760   // empty url + filename
02761   //
02762   m_url = KURL ();
02763   m_file = QString::null;
02764 
02765   // we are not modified
02766   if (m_modOnHd)
02767   {
02768     m_modOnHd = false;
02769     m_modOnHdReason = 0;
02770     emit modifiedOnDisc (this, m_modOnHd, 0);
02771   }
02772 
02773   // clear the buffer
02774   m_buffer->clear();
02775 
02776   // remove all marks
02777   clearMarks ();
02778 
02779   // clear undo/redo history
02780   clearUndo();
02781   clearRedo();
02782 
02783   // no, we are no longer modified
02784   setModified(false);
02785 
02786   // we have no longer any hl
02787   m_buffer->setHighlight(0);
02788 
02789   // update all our views
02790   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
02791   {
02792     // Explicitly call the internal version because we don't want this to look like
02793     // an external request (and thus have the view not QWidget::scroll()ed.
02794     view->setCursorPositionInternal(0, 0, 1, false);
02795     view->clearSelection();
02796     view->updateView(true);
02797   }
02798 
02799   // uh, filename changed
02800   emit fileNameChanged ();
02801 
02802   // update doc name
02803   setDocName (QString::null);
02804 
02805   // success
02806   return true;
02807 }
02808 
02809 void KateDocument::setReadWrite( bool rw )
02810 {
02811   if (isReadWrite() != rw)
02812   {
02813     KParts::ReadWritePart::setReadWrite (rw);
02814 
02815     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02816     {
02817       view->slotUpdate();
02818       view->slotReadWriteChanged ();
02819     }
02820   }
02821 }
02822 
02823 void KateDocument::setModified(bool m) {
02824 
02825   if (isModified() != m) {
02826     KParts::ReadWritePart::setModified (m);
02827 
02828     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02829     {
02830       view->slotUpdate();
02831     }
02832 
02833     emit modifiedChanged ();
02834     emit modStateChanged ((Kate::Document *)this);
02835   }
02836   if ( m == false && ! undoItems.isEmpty() )
02837   {
02838     lastUndoGroupWhenSaved = undoItems.last();
02839     lastRedoGroupWhenSaved = redoItems.last();
02840   }
02841 
02842   if ( m == false )
02843   {
02844     docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
02845     docWasSavedWhenRedoWasEmpty = redoItems.isEmpty();
02846   }
02847 }
02848 //END
02849 
02850 //BEGIN Kate specific stuff ;)
02851 
02852 void KateDocument::makeAttribs(bool needInvalidate)
02853 {
02854   for (uint z = 0; z < m_views.count(); z++)
02855     m_views.at(z)->renderer()->updateAttributes ();
02856 
02857   if (needInvalidate)
02858     m_buffer->invalidateHighlighting();
02859 
02860   tagAll ();
02861 }
02862 
02863 // the attributes of a hl have changed, update
02864 void KateDocument::internalHlChanged()
02865 {
02866   makeAttribs();
02867 }
02868 
02869 void KateDocument::addView(KTextEditor::View *view) {
02870   if (!view)
02871     return;
02872 
02873   m_views.append( (KateView *) view  );
02874   m_textEditViews.append( view );
02875 
02876   // apply the view & renderer vars from the file type
02877   const KateFileType *t = 0;
02878   if ((m_fileType > -1) && (t = KateFactory::self()->fileTypeManager()->fileType(m_fileType)))
02879     readVariableLine (t->varLine, true);
02880 
02881   // apply the view & renderer vars from the file
02882   readVariables (true);
02883 
02884   m_activeView = (KateView *) view;
02885 }
02886 
02887 void KateDocument::removeView(KTextEditor::View *view) {
02888   if (!view)
02889     return;
02890 
02891   if (m_activeView == view)
02892     m_activeView = 0L;
02893 
02894   m_views.removeRef( (KateView *) view );
02895   m_textEditViews.removeRef( view  );
02896 }
02897 
02898 void KateDocument::addSuperCursor(KateSuperCursor *cursor, bool privateC) {
02899   if (!cursor)
02900     return;
02901 
02902   m_superCursors.append( cursor );
02903 
02904   if (!privateC)
02905     myCursors.append( cursor );
02906 }
02907 
02908 void KateDocument::removeSuperCursor(KateSuperCursor *cursor, bool privateC) {
02909   if (!cursor)
02910     return;
02911 
02912   if (!privateC)
02913     myCursors.removeRef( cursor  );
02914 
02915   m_superCursors.removeRef( cursor  );
02916 }
02917 
02918 bool KateDocument::ownedView(KateView *view) {
02919   // do we own the given view?
02920   return (m_views.containsRef(view) > 0);
02921 }
02922 
02923 bool KateDocument::isLastView(int numViews) {
02924   return ((int) m_views.count() == numViews);
02925 }
02926 
02927 uint KateDocument::currentColumn( const KateTextCursor& cursor )
02928 {
02929   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
02930 
02931   if (textLine)
02932     return textLine->cursorX(cursor.col(), config()->tabWidth());
02933   else
02934     return 0;
02935 }
02936 
02937 bool KateDocument::typeChars ( KateView *view, const QString &chars )
02938 {
02939   KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorLine ());
02940 
02941   if (!textLine)
02942     return false;
02943 
02944   bool bracketInserted = false;
02945   QString buf;
02946   QChar c;
02947 
02948   for( uint z = 0; z < chars.length(); z++ )
02949   {
02950     QChar ch = c = chars[z];
02951     if (ch.isPrint() || ch == '\t')
02952     {
02953       buf.append (ch);
02954 
02955       if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets))
02956       {
02957         QChar end_ch;
02958         bool complete = true;
02959         QChar prevChar = textLine->getChar(view->cursorColumnReal()-1);
02960         QChar nextChar = textLine->getChar(view->cursorColumnReal());
02961         switch(ch) {
02962           case '(': end_ch = ')'; break;
02963           case '[': end_ch = ']'; break;
02964           case '{': end_ch = '}'; break;
02965           case '\'':end_ch = '\'';break;
02966           case '"': end_ch = '"'; break;
02967           default: complete = false;
02968         }
02969         if (complete)
02970         {
02971           if (view->hasSelection())
02972           { // there is a selection, enclose the selection
02973             buf.append (view->selection());
02974             buf.append (end_ch);
02975             bracketInserted = true;
02976           }
02977           else
02978           { // no selection, check whether we should better refuse to complete
02979             if ( ( (ch == '\'' || ch == '"') &&
02980                    (prevChar.isLetterOrNumber() || prevChar == ch) )
02981               || nextChar.isLetterOrNumber()
02982               || (nextChar == end_ch && prevChar != ch) )
02983             {
02984               kdDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
02985             }
02986             else
02987             {
02988               buf.append (end_ch);
02989               bracketInserted = true;
02990             }
02991           }
02992         }
02993       }
02994     }
02995   }
02996 
02997   if (buf.isEmpty())
02998     return false;
02999 
03000   editStart ();
03001 
03002   if (!view->config()->persistentSelection() && view->hasSelection() )
03003     view->removeSelectedText();
03004 
03005   int oldLine = view->cursorLine ();
03006   int oldCol = view->cursorColumnReal ();
03007 
03008 
03009   if (config()->configFlags()  & KateDocument::cfOvr)
03010     removeText (view->cursorLine(), view->cursorColumnReal(), view->cursorLine(), kMin( view->cursorColumnReal()+buf.length(), textLine->length() ) );
03011 
03012   insertText (view->cursorLine(), view->cursorColumnReal(), buf);
03013   m_indenter->processChar(c);
03014 
03015   editEnd ();
03016 
03017   if (bracketInserted)
03018     view->setCursorPositionInternal (view->cursorLine(), view->cursorColumnReal()-1);
03019 
03020   emit charactersInteractivelyInserted (oldLine, oldCol, chars);
03021 
03022   return true;
03023 }
03024 
03025 void KateDocument::newLine( KateTextCursor& c, KateViewInternal *v )
03026 {
03027   editStart();
03028 
03029   if( !v->view()->config()->persistentSelection() && v->view()->hasSelection() )
03030     v->view()->removeSelectedText();
03031 
03032   // temporary hack to get the cursor pos right !!!!!!!!!
03033   c = v->getCursor ();
03034 
03035   if (c.line() > (int)lastLine())
03036    c.setLine(lastLine());
03037 
03038   if ( c.line() < 0 )
03039     c.setLine( 0 );
03040 
03041   uint ln = c.line();
03042 
03043   KateTextLine::Ptr textLine = kateTextLine(c.line());
03044 
03045   if (c.col() > (int)textLine->length())
03046     c.setCol(textLine->length());
03047 
03048   if (m_indenter->canProcessNewLine ())
03049   {
03050     int pos = textLine->firstChar();
03051 
03052     // length should do the job better
03053     if (pos < 0)
03054       pos = textLine->length();
03055 
03056     if (c.col() < pos)
03057       c.setCol(pos); // place cursor on first char if before
03058 
03059     editWrapLine (c.line(), c.col());
03060 
03061     KateDocCursor cursor (c.line() + 1, pos, this);
03062     m_indenter->processNewline(cursor, true);
03063 
03064     c.setPos(cursor);
03065   }
03066   else
03067   {
03068     editWrapLine (c.line(), c.col());
03069     c.setPos(c.line() + 1, 0);
03070   }
03071 
03072   removeTrailingSpace( ln );
03073 
03074   editEnd();
03075 }
03076 
03077 void KateDocument::transpose( const KateTextCursor& cursor)
03078 {
03079   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03080 
03081   if (!textLine || (textLine->length() < 2))
03082     return;
03083 
03084   uint col = cursor.col();
03085 
03086   if (col > 0)
03087     col--;
03088 
03089   if ((textLine->length() - col) < 2)
03090     return;
03091 
03092   uint line = cursor.line();
03093   QString s;
03094 
03095   //clever swap code if first character on the line swap right&left
03096   //otherwise left & right
03097   s.append (textLine->getChar(col+1));
03098   s.append (textLine->getChar(col));
03099   //do the swap
03100 
03101   // do it right, never ever manipulate a textline
03102   editStart ();
03103   editRemoveText (line, col, 2);
03104   editInsertText (line, col, s);
03105   editEnd ();
03106 }
03107 
03108 void KateDocument::backspace( KateView *view, const KateTextCursor& c )
03109 {
03110   if ( !view->config()->persistentSelection() && view->hasSelection() ) {
03111     view->removeSelectedText();
03112     return;
03113   }
03114 
03115   uint col = kMax( c.col(), 0 );
03116   uint line = kMax( c.line(), 0 );
03117 
03118   if ((col == 0) && (line == 0))
03119     return;
03120 
03121   int complement = 0;
03122   if (col > 0)
03123   {
03124     if (config()->configFlags() & KateDocument::cfAutoBrackets)
03125     {
03126       // if inside empty (), {}, [], '', "" delete both
03127       KateTextLine::Ptr tl = m_buffer->plainLine(line);
03128       if(!tl) return;
03129       QChar prevChar = tl->getChar(col-1);
03130       QChar nextChar = tl->getChar(col);
03131 
03132       if ( (prevChar == '"' && nextChar == '"') ||
03133            (prevChar == '\'' && nextChar == '\'') ||
03134            (prevChar == '(' && nextChar == ')') ||
03135            (prevChar == '[' && nextChar == ']') ||
03136            (prevChar == '{' && nextChar == '}') )
03137       {
03138         complement = 1;
03139       }
03140     }
03141     if (!(config()->configFlags() & KateDocument::cfBackspaceIndents))
03142     {
03143       // ordinary backspace
03144       //c.cursor.col--;
03145       removeText(line, col-1, line, col+complement);
03146     }
03147     else
03148     {
03149       // backspace indents: erase to next indent position
03150       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03151 
03152       // don't forget this check!!!! really!!!!
03153       if (!textLine)
03154         return;
03155 
03156       int colX = textLine->cursorX(col, config()->tabWidth());
03157       int pos = textLine->firstChar();
03158       if (pos > 0)
03159         pos = textLine->cursorX(pos, config()->tabWidth());
03160 
03161       if (pos < 0 || pos >= (int)colX)
03162       {
03163         // only spaces on left side of cursor
03164         indent( view, line, -1);
03165       }
03166       else
03167         removeText(line, col-1, line, col+complement);
03168     }
03169   }
03170   else
03171   {
03172     // col == 0: wrap to previous line
03173     if (line >= 1)
03174     {
03175       KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
03176 
03177       // don't forget this check!!!! really!!!!
03178       if (!textLine)
03179         return;
03180 
03181       if (config()->wordWrap() && textLine->endingWith(QString::fromLatin1(" ")))
03182       {
03183         // gg: in hard wordwrap mode, backspace must also eat the trailing space
03184         removeText (line-1, textLine->length()-1, line, 0);
03185       }
03186       else
03187         removeText (line-1, textLine->length(), line, 0);
03188     }
03189   }
03190 
03191   emit backspacePressed();
03192 }
03193 
03194 void KateDocument::del( KateView *view, const KateTextCursor& c )
03195 {
03196   if ( !view->config()->persistentSelection() && view->hasSelection() ) {
03197     view->removeSelectedText();
03198     return;
03199   }
03200 
03201   if( c.col() < (int) m_buffer->plainLine(c.line())->length())
03202   {
03203     removeText(c.line(), c.col(), c.line(), c.col()+1);
03204   }
03205   else if ( (uint)c.line() < lastLine() )
03206   {
03207     removeText(c.line(), c.col(), c.line()+1, 0);
03208   }
03209 }
03210 
03211 void KateDocument::paste ( KateView* view )
03212 {
03213   QString s = QApplication::clipboard()->text();
03214 
03215   if (s.isEmpty())
03216     return;
03217 
03218   uint lines = s.contains (QChar ('\n'));
03219 
03220   m_undoDontMerge = true;
03221 
03222   editStart ();
03223 
03224   if (!view->config()->persistentSelection() && view->hasSelection() )
03225     view->removeSelectedText();
03226 
03227   uint line = view->cursorLine ();
03228   uint column = view->cursorColumnReal ();
03229 
03230   insertText ( line, column, s, view->blockSelectionMode() );
03231 
03232   editEnd();
03233 
03234   // move cursor right for block select, as the user is moved right internal
03235   // even in that case, but user expects other behavior in block selection
03236   // mode !
03237   if (view->blockSelectionMode())
03238     view->setCursorPositionInternal (line+lines, column);
03239 
03240   if (m_indenter->canProcessLine()
03241       && config()->configFlags() & KateDocumentConfig::cfIndentPastedText)
03242   {
03243     editStart();
03244 
03245     KateDocCursor begin(line, 0, this);
03246     KateDocCursor end(line + lines, 0, this);
03247 
03248     m_indenter->processSection (begin, end);
03249 
03250     editEnd();
03251   }
03252 
03253   if (!view->blockSelectionMode()) emit charactersSemiInteractivelyInserted (line, column, s);
03254   m_undoDontMerge = true;
03255 }
03256 
03257 void KateDocument::insertIndentChars ( KateView *view )
03258 {
03259   editStart ();
03260 
03261   QString s;
03262   if (config()->configFlags() & KateDocument::cfSpaceIndent)
03263   {
03264     int width = config()->indentationWidth();
03265     s.fill (' ', width - (view->cursorColumnReal() % width));
03266   }
03267   else
03268     s.append ('\t');
03269 
03270   insertText (view->cursorLine(), view->cursorColumnReal(), s);
03271 
03272   editEnd ();
03273 }
03274 
03275 void KateDocument::indent ( KateView *v, uint line, int change)
03276 {
03277   editStart ();
03278 
03279   if (!hasSelection())
03280   {
03281     // single line
03282     optimizeLeadingSpace(line, config()->configFlags(), change);
03283   }
03284   else
03285   {
03286     int sl = v->selStartLine();
03287     int el = v->selEndLine();
03288     int ec = v->selEndCol();
03289 
03290     if ((ec == 0) && ((el-1) >= 0))
03291     {
03292       el--; /* */
03293     }
03294 
03295     if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) {
03296       // unindent so that the existing indent profile doesn't get screwed
03297       // if any line we may unindent is already full left, don't do anything
03298       int adjustedChange = -change;
03299 
03300       for (line = sl; (int) line <= el && adjustedChange > 0; line++) {
03301         KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03302         int firstChar = textLine->firstChar();
03303         if (firstChar >= 0 && (v->lineSelected(line) || v->lineHasSelected(line))) {
03304           int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth();
03305           if (maxUnindent < adjustedChange)
03306             adjustedChange = maxUnindent;
03307         }
03308       }
03309 
03310       change = -adjustedChange;
03311     }
03312 
03313     const bool rts = config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn;
03314     for (line = sl; (int) line <= el; line++) {
03315       if ((v->lineSelected(line) || v->lineHasSelected(line))
03316           && (!rts || lineLength(line) > 0)) {
03317         optimizeLeadingSpace(line, config()->configFlags(), change);
03318       }
03319     }
03320   }
03321 
03322   editEnd ();
03323 }
03324 
03325 void KateDocument::align(KateView *view, uint line)
03326 {
03327   if (m_indenter->canProcessLine())
03328   {
03329     editStart ();
03330 
03331     if (!view->hasSelection())
03332     {
03333       KateDocCursor curLine(line, 0, this);
03334       m_indenter->processLine (curLine);
03335       editEnd ();
03336       activeView()->setCursorPosition (line, curLine.col());
03337     }
03338     else
03339     {
03340       m_indenter->processSection (view->selStart(), view->selEnd());
03341       editEnd ();
03342     }
03343   }
03344 }
03345 
03346 /*
03347   Optimize the leading whitespace for a single line.
03348   If change is > 0, it adds indentation units (indentationChars)
03349   if change is == 0, it only optimizes
03350   If change is < 0, it removes indentation units
03351   This will be used to indent, unindent, and optimal-fill a line.
03352   If excess space is removed depends on the flag cfKeepExtraSpaces
03353   which has to be set by the user
03354 */
03355 void KateDocument::optimizeLeadingSpace(uint line, int flags, int change)
03356 {
03357   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03358 
03359   int first_char = textline->firstChar();
03360 
03361   int w = 0;
03362   if (flags & KateDocument::cfSpaceIndent)
03363     w = config()->indentationWidth();
03364   else
03365     w = config()->tabWidth();
03366 
03367   if (first_char < 0)
03368     first_char = textline->length();
03369 
03370   int space =  textline->cursorX(first_char, config()->tabWidth()) + change * w;
03371   if (space < 0)
03372     space = 0;
03373 
03374   if (!(flags & KateDocument::cfKeepExtraSpaces))
03375   {
03376     uint extra = space % w;
03377 
03378     space -= extra;
03379     if (extra && change < 0) {
03380       // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide)
03381       space += w;
03382     }
03383   }
03384 
03385   //kdDebug(13020)  << "replace With Op: " << line << " " << first_char << " " << space << endl;
03386   replaceWithOptimizedSpace(line, first_char, space, flags);
03387 }
03388 
03389 void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags)
03390 {
03391   uint length;
03392   QString new_space;
03393 
03394   if (flags & KateDocument::cfSpaceIndent && ! (flags & KateDocumentConfig::cfMixedIndent) ) {
03395     length = space;
03396     new_space.fill(' ', length);
03397   }
03398   else {
03399     length = space / config()->tabWidth();
03400     new_space.fill('\t', length);
03401 
03402     QString extra_space;
03403     extra_space.fill(' ', space % config()->tabWidth());
03404     length += space % config()->tabWidth();
03405     new_space += extra_space;
03406   }
03407 
03408   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03409   uint change_from;
03410   for (change_from = 0; change_from < upto_column && change_from < length; change_from++) {
03411     if (textline->getChar(change_from) != new_space[change_from])
03412       break;
03413   }
03414 
03415   editStart();
03416 
03417   if (change_from < upto_column)
03418     removeText(line, change_from, line, upto_column);
03419 
03420   if (change_from < length)
03421     insertText(line, change_from, new_space.right(length - change_from));
03422 
03423   editEnd();
03424 }
03425 
03426 /*
03427   Remove a given string at the begining
03428   of the current line.
03429 */
03430 bool KateDocument::removeStringFromBegining(int line, QString &str)
03431 {
03432   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03433 
03434   int index = 0;
03435   bool there = false;
03436 
03437   if (textline->startingWith(str))
03438     there = true;
03439   else
03440   {
03441     index = textline->firstChar ();
03442 
03443     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03444       there = true;
03445   }
03446 
03447   if (there)
03448   {
03449     // Remove some chars
03450     removeText (line, index, line, index+str.length());
03451   }
03452 
03453   return there;
03454 }
03455 
03456 /*
03457   Remove a given string at the end
03458   of the current line.
03459 */
03460 bool KateDocument::removeStringFromEnd(int line, QString &str)
03461 {
03462   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03463 
03464   int index = 0;
03465   bool there = false;
03466 
03467   if(textline->endingWith(str))
03468   {
03469     index = textline->length() - str.length();
03470     there = true;
03471   }
03472   else
03473   {
03474     index = textline->lastChar ()-str.length()+1;
03475 
03476     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03477       there = true;
03478   }
03479 
03480   if (there)
03481   {
03482     // Remove some chars
03483     removeText (line, index, line, index+str.length());
03484   }
03485 
03486   return there;
03487 }
03488 
03489 /*
03490   Add to the current line a comment line mark at
03491   the begining.
03492 */
03493 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
03494 {
03495   if (highlight()->getCommentSingleLinePosition(attrib)==KateHighlighting::CSLPosColumn0)
03496   {
03497     QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03498     insertText (line, 0, commentLineMark);
03499   }
03500   else
03501   {
03502     QString commentLineMark=highlight()->getCommentSingleLineStart(attrib);
03503     KateTextLine::Ptr l = m_buffer->line(line);
03504     int pos=l->firstChar();
03505     if (pos >=0)
03506       insertText(line,pos,commentLineMark);
03507   }
03508 }
03509 
03510 /*
03511   Remove from the current line a comment line mark at
03512   the begining if there is one.
03513 */
03514 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
03515 {
03516   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
03517   QString longCommentMark = shortCommentMark + " ";
03518 
03519   editStart();
03520 
03521   // Try to remove the long comment mark first
03522   bool removed = (removeStringFromBegining(line, longCommentMark)
03523                   || removeStringFromBegining(line, shortCommentMark));
03524 
03525   editEnd();
03526 
03527   return removed;
03528 }
03529 
03530 /*
03531   Add to the current line a start comment mark at the
03532  begining and a stop comment mark at the end.
03533 */
03534 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
03535 {
03536   QString startCommentMark = highlight()->getCommentStart( attrib ) + " ";
03537   QString stopCommentMark = " " + highlight()->getCommentEnd( attrib );
03538 
03539   editStart();
03540 
03541   // Add the start comment mark
03542   insertText (line, 0, startCommentMark);
03543 
03544   // Go to the end of the line
03545   int col = m_buffer->plainLine(line)->length();
03546 
03547   // Add the stop comment mark
03548   insertText (line, col, stopCommentMark);
03549 
03550   editEnd();
03551 }
03552 
03553 /*
03554   Remove from the current line a start comment mark at
03555   the begining and a stop comment mark at the end.
03556 */
03557 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
03558 {
03559   QString shortStartCommentMark = highlight()->getCommentStart( attrib );
03560   QString longStartCommentMark = shortStartCommentMark + " ";
03561   QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
03562   QString longStopCommentMark = " " + shortStopCommentMark;
03563 
03564   editStart();
03565 
03566 #ifdef __GNUC__
03567 #warning "that's a bad idea, can lead to stray endings, FIXME"
03568 #endif
03569   // Try to remove the long start comment mark first
03570   bool removedStart = (removeStringFromBegining(line, longStartCommentMark)
03571                        || removeStringFromBegining(line, shortStartCommentMark));
03572 
03573   bool removedStop = false;
03574   if (removedStart)
03575   {
03576     // Try to remove the long stop comment mark first
03577     removedStop = (removeStringFromEnd(line, longStopCommentMark)
03578                       || removeStringFromEnd(line, shortStopCommentMark));
03579   }
03580 
03581   editEnd();
03582 
03583   return (removedStart || removedStop);
03584 }
03585 
03586 /*
03587   Add to the current selection a start comment
03588  mark at the begining and a stop comment mark
03589  at the end.
03590 */
03591 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
03592 {
03593   QString startComment = highlight()->getCommentStart( attrib );
03594   QString endComment = highlight()->getCommentEnd( attrib );
03595 
03596   int sl = view->selStartLine();
03597   int el = view->selEndLine();
03598   int sc = view->selStartCol();
03599   int ec = view->selEndCol();
03600 
03601   if ((ec == 0) && ((el-1) >= 0))
03602   {
03603     el--;
03604     ec = m_buffer->plainLine (el)->length();
03605   }
03606 
03607   editStart();
03608 
03609   insertText (el, ec, endComment);
03610   insertText (sl, sc, startComment);
03611 
03612   editEnd ();
03613 
03614   // Set the new selection
03615   ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 );
03616   view->setSelection(sl, sc, el, ec);
03617 }
03618 
03619 /*
03620   Add to the current selection a comment line
03621  mark at the begining of each line.
03622 */
03623 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
03624 {
03625   QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03626 
03627   int sl = view->selStartLine();
03628   int el = view->selEndLine();
03629 
03630   if ((view->selEndCol() == 0) && ((el-1) >= 0))
03631   {
03632     el--;
03633   }
03634 
03635   editStart();
03636 
03637   // For each line of the selection
03638   for (int z = el; z >= sl; z--) {
03639     //insertText (z, 0, commentLineMark);
03640     addStartLineCommentToSingleLine(z, attrib );
03641   }
03642 
03643   editEnd ();
03644 
03645   // Set the new selection
03646 
03647   KateDocCursor end (view->selEnd());
03648   end.setCol(view->selEndCol() + ((el == view->selEndLine()) ? commentLineMark.length() : 0) );
03649 
03650   view->setSelection(view->selStartLine(), 0, end.line(), end.col());
03651 }
03652 
03653 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
03654 {
03655   for(; line < (int)m_buffer->count(); line++) {
03656     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03657 
03658     if (!textLine)
03659       break;
03660 
03661     col = textLine->nextNonSpaceChar(col);
03662     if(col != -1)
03663       return true; // Next non-space char found
03664     col = 0;
03665   }
03666   // No non-space char found
03667   line = -1;
03668   col = -1;
03669   return false;
03670 }
03671 
03672 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
03673 {
03674   while(true)
03675   {
03676     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03677 
03678     if (!textLine)
03679       break;
03680 
03681     col = textLine->previousNonSpaceChar(col);
03682     if(col != -1) return true;
03683     if(line == 0) return false;
03684     --line;
03685     col = textLine->length();
03686 }
03687   // No non-space char found
03688   line = -1;
03689   col = -1;
03690   return false;
03691 }
03692 
03693 /*
03694   Remove from the selection a start comment mark at
03695   the begining and a stop comment mark at the end.
03696 */
03697 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
03698 {
03699   QString startComment = highlight()->getCommentStart( attrib );
03700   QString endComment = highlight()->getCommentEnd( attrib );
03701 
03702   int sl = kMax<int> (0, view->selStartLine());
03703   int el = kMin<int>  (view->selEndLine(), lastLine());
03704   int sc = view->selStartCol();
03705   int ec = view->selEndCol();
03706 
03707   // The selection ends on the char before selectEnd
03708   if (ec != 0) {
03709     ec--;
03710   } else {
03711     if (el > 0) {
03712       el--;
03713       ec = m_buffer->plainLine(el)->length() - 1;
03714     }
03715   }
03716 
03717   int startCommentLen = startComment.length();
03718   int endCommentLen = endComment.length();
03719 
03720   // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/
03721 
03722   bool remove = nextNonSpaceCharPos(sl, sc)
03723       && m_buffer->plainLine(sl)->stringAtPos(sc, startComment)
03724       && previousNonSpaceCharPos(el, ec)
03725       && ( (ec - endCommentLen + 1) >= 0 )
03726       && m_buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment);
03727 
03728   if (remove) {
03729     editStart();
03730 
03731     removeText (el, ec - endCommentLen + 1, el, ec + 1);
03732     removeText (sl, sc, sl, sc + startCommentLen);
03733 
03734     editEnd ();
03735     // set new selection not necessary, as the selection cursors are KateSuperCursors
03736   }
03737 
03738   return remove;
03739 }
03740 
03741 bool KateDocument::removeStartStopCommentFromRegion(const KateTextCursor &start,const KateTextCursor &end,int attrib)
03742 {
03743   QString startComment = highlight()->getCommentStart( attrib );
03744   QString endComment = highlight()->getCommentEnd( attrib );
03745   int startCommentLen = startComment.length();
03746   int endCommentLen = endComment.length();
03747 
03748     bool remove = m_buffer->plainLine(start.line())->stringAtPos(start.col(), startComment)
03749       && ( (end.col() - endCommentLen ) >= 0 )
03750       && m_buffer->plainLine(end.line())->stringAtPos(end.col() - endCommentLen , endComment);
03751       if (remove)  {
03752         editStart();
03753           removeText(end.line(),end.col()-endCommentLen,end.line(),end.col());
03754           removeText(start.line(),start.col(),start.line(),start.col()+startCommentLen);
03755         editEnd();
03756       }
03757       return remove;
03758 }
03759 
03760 /*
03761   Remove from the begining of each line of the
03762   selection a start comment line mark.
03763 */
03764 bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib )
03765 {
03766   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
03767   QString longCommentMark = shortCommentMark + " ";
03768 
03769   int sl = view->selStartLine();
03770   int el = view->selEndLine();
03771 
03772   if ((view->selEndCol() == 0) && ((el-1) >= 0))
03773   {
03774     el--;
03775   }
03776 
03777   // Find out how many char will be removed from the last line
03778   int removeLength = 0;
03779   if (m_buffer->plainLine(el)->startingWith(longCommentMark))
03780     removeLength = longCommentMark.length();
03781   else if (m_buffer->plainLine(el)->startingWith(shortCommentMark))
03782     removeLength = shortCommentMark.length();
03783 
03784   bool removed = false;
03785 
03786   editStart();
03787 
03788   // For each line of the selection
03789   for (int z = el; z >= sl; z--)
03790   {
03791     // Try to remove the long comment mark first
03792     removed = (removeStringFromBegining(z, longCommentMark)
03793                  || removeStringFromBegining(z, shortCommentMark)
03794                  || removed);
03795   }
03796 
03797   editEnd();
03798   // updating selection already done by the KateSuperCursors
03799   return removed;
03800 }
03801 
03802 /*
03803   Comment or uncomment the selection or the current
03804   line if there is no selection.
03805 */
03806 void KateDocument::comment( KateView *v, uint line,uint column, int change)
03807 {
03808   // We need to check that we can sanely comment the selectino or region.
03809   // It is if the attribute of the first and last character of the range to
03810   // comment belongs to the same language definition.
03811   // for lines with no text, we need the attribute for the lines context.
03812   bool hassel = v->hasSelection();
03813   int startAttrib, endAttrib;
03814   if ( hassel )
03815   {
03816     KateTextLine::Ptr ln = kateTextLine( v->selStartLine() );
03817     int l = v->selStartLine(), c = v->selStartCol();
03818     startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03819 
03820     ln = kateTextLine( v->selEndLine() );
03821     l = v->selEndLine(), c = v->selEndCol();
03822     endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03823   }
03824   else
03825   {
03826     KateTextLine::Ptr ln = kateTextLine( line );
03827     if ( ln->length() )
03828     {
03829       startAttrib = ln->attribute( ln->firstChar() );
03830       endAttrib = ln->attribute( ln->lastChar() );
03831     }
03832     else
03833     {
03834       int l = line, c = 0;
03835       if ( nextNonSpaceCharPos( l, c )  || previousNonSpaceCharPos( l, c ) )
03836         startAttrib = endAttrib = kateTextLine( l )->attribute( c );
03837       else
03838         startAttrib = endAttrib = 0;
03839     }
03840   }
03841 
03842   if ( ! highlight()->canComment( startAttrib, endAttrib ) )
03843   {
03844     kdDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!"<<endl;
03845     return;
03846   }
03847 
03848   bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
03849   bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
03850       && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
03851 
03852   bool removed = false;
03853 
03854   if (change > 0) // comment
03855   {
03856     if ( !hassel )
03857     {
03858       if ( hasStartLineCommentMark )
03859         addStartLineCommentToSingleLine( line, startAttrib );
03860       else if ( hasStartStopCommentMark )
03861         addStartStopCommentToSingleLine( line, startAttrib );
03862     }
03863     else
03864     {
03865       // anders: prefer single line comment to avoid nesting probs
03866       // If the selection starts after first char in the first line
03867       // or ends before the last char of the last line, we may use
03868       // multiline comment markers.
03869       // TODO We should try to detect nesting.
03870       //    - if selection ends at col 0, most likely she wanted that
03871       // line ignored
03872       if ( hasStartStopCommentMark &&
03873            ( !hasStartLineCommentMark || (
03874            ( v->selStartCol() > m_buffer->plainLine( v->selStartLine() )->firstChar() ) ||
03875            ( v->selEndCol() < ((int)m_buffer->plainLine( v->selEndLine() )->length()) )
03876          ) ) )
03877         addStartStopCommentToSelection( v, startAttrib );
03878       else if ( hasStartLineCommentMark )
03879         addStartLineCommentToSelection( v, startAttrib );
03880     }
03881   }
03882   else // uncomment
03883   {
03884     if ( !hassel )
03885     {
03886       removed = ( hasStartLineCommentMark
03887                   && removeStartLineCommentFromSingleLine( line, startAttrib ) )
03888         || ( hasStartStopCommentMark
03889              && removeStartStopCommentFromSingleLine( line, startAttrib ) );
03890       if ((!removed) && foldingTree()) {
03891         kdDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)"<<endl;
03892         int commentRegion=(highlight()->commentRegion(startAttrib));
03893         if (commentRegion){
03894            KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
03895            if (n) {
03896             KateTextCursor start,end;
03897              if ((n->nodeType()==commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
03898                 kdDebug(13020)<<"Enclosing region found:"<<start.col()<<"/"<<start.line()<<"-"<<end.col()<<"/"<<end.line()<<endl;
03899                 removeStartStopCommentFromRegion(start,end,startAttrib);
03900              } else {
03901                   kdDebug(13020)<<"Enclosing region found, but not valid"<<endl;
03902                   kdDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion<<endl;
03903              }
03904             //perhaps nested regions should be hadled here too...
03905           } else kdDebug(13020)<<"No enclosing region found"<<endl;
03906         } else kdDebug(13020)<<"No comment region specified for current hl"<<endl;
03907       }
03908     }
03909     else
03910     {
03911       // anders: this seems like it will work with above changes :)
03912       removed = ( hasStartLineCommentMark
03913           && removeStartLineCommentFromSelection( v, startAttrib ) )
03914         || ( hasStartStopCommentMark
03915           && removeStartStopCommentFromSelection( v, startAttrib ) );
03916     }
03917   }
03918 }
03919 
03920 void KateDocument::transform( KateView *v, const KateTextCursor &c,
03921                             KateDocument::TextTransform t )
03922 {
03923   editStart();
03924   uint cl( c.line() ), cc( c.col() );
03925   bool selectionRestored = false;
03926 
03927   if ( hasSelection() )
03928   {
03929     // cache the selection and cursor, so we can be sure to restore.
03930     KateTextCursor selstart = v->selStart();
03931     KateTextCursor selend = v->selEnd();
03932 
03933     int ln = v->selStartLine();
03934     while ( ln <= selend.line() )
03935     {
03936       uint start, end;
03937       start = (ln == selstart.line() || v->blockSelectionMode()) ?
03938           selstart.col() : 0;
03939       end = (ln == selend.line() || v->blockSelectionMode()) ?
03940           selend.col() : lineLength( ln );
03941       if ( start > end )
03942       {
03943         uint t = start;
03944         start = end;
03945         end = t;
03946       }
03947       QString s = text( ln, start, ln, end );
03948       QString o = s;
03949 
03950       if ( t == Uppercase )
03951         s = s.upper();
03952       else if ( t == Lowercase )
03953         s = s.lower();
03954       else // Capitalize
03955       {
03956         KateTextLine::Ptr l = m_buffer->plainLine( ln );
03957         uint p ( 0 );
03958         while( p < s.length() )
03959         {
03960           // If bol or the character before is not in a word, up this one:
03961           // 1. if both start and p is 0, upper char.
03962           // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
03963           // 3. if p-1 is not in a word, upper.
03964           if ( ( ! start && ! p ) ||
03965                    ( ( ln == selstart.line() || v->blockSelectionMode() ) &&
03966                    ! p && ! highlight()->isInWord( l->getChar( start - 1 )) ) ||
03967                    ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
03968              )
03969             s[p] = s.at(p).upper();
03970           p++;
03971         }
03972       }
03973 
03974       if ( o != s )
03975       {
03976       removeText( ln, start, ln, end );
03977       insertText( ln, start, s );
03978       }
03979 
03980       ln++;
03981     }
03982 
03983     // restore selection
03984     v->setSelection( selstart, selend );
03985     selectionRestored = true;
03986 
03987   } else {  // no selection
03988     QString o = text( cl, cc, cl, cc + 1 );
03989     QString s;
03990     int n ( cc );
03991     switch ( t ) {
03992       case Uppercase:
03993       s = o.upper();
03994       break;
03995       case Lowercase:
03996       s = o.lower();
03997       break;
03998       case Capitalize:
03999       {
04000         KateTextLine::Ptr l = m_buffer->plainLine( cl );
04001         while ( n > 0 && highlight()->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) )
04002           n--;
04003         o = text( cl, n, cl, n + 1 );
04004         s = o.upper();
04005       }
04006       break;
04007       default:
04008       break;
04009     }
04010 
04011     if ( s != o )
04012     {
04013     removeText( cl, n, cl, n+1 );
04014     insertText( cl, n, s );
04015   }
04016   }
04017   editEnd();
04018 
04019   if ( ! selectionRestored )
04020     v->setCursorPosition( cl, cc );
04021 }
04022 
04023 void KateDocument::joinLines( uint first, uint last )
04024 {
04025 //   if ( first == last ) last += 1;
04026   editStart();
04027   int line( first );
04028   while ( first < last )
04029   {
04030     // Normalize the whitespace in the joined lines by making sure there's
04031     // always exactly one space between the joined lines
04032     // This cannot be done in editUnwrapLine, because we do NOT want this
04033     // behaviour when deleting from the start of a line, just when explicitly
04034     // calling the join command
04035     KateTextLine::Ptr l = m_buffer->line( line );
04036     KateTextLine::Ptr tl = m_buffer->line( line + 1 );
04037 
04038     if ( !l || !tl )
04039     {
04040       editEnd();
04041       return;
04042     }
04043 
04044     int pos = tl->firstChar();
04045     if ( pos >= 0 )
04046     {
04047       if (pos != 0)
04048         editRemoveText( line + 1, 0, pos );
04049       if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) )
04050         editInsertText( line + 1, 0, " " );
04051     }
04052     else
04053     {
04054       // Just remove the whitespace and let Kate handle the rest
04055       editRemoveText( line + 1, 0, tl->length() );
04056     }
04057 
04058     editUnWrapLine( line );
04059     first++;
04060   }
04061   editEnd();
04062 }
04063 
04064 QString KateDocument::getWord( const KateTextCursor& cursor ) {
04065   int start, end, len;
04066 
04067   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04068   len = textLine->length();
04069   start = end = cursor.col();
04070   if (start > len)        // Probably because of non-wrapping cursor mode.
04071     return QString("");
04072 
04073   while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
04074   while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(end))) end++;
04075   len = end - start;
04076   return QString(&textLine->text()[start], len);
04077 }
04078 
04079 void KateDocument::tagLines(int start, int end)
04080 {
04081   for (uint z = 0; z < m_views.count(); z++)
04082     m_views.at(z)->tagLines (start, end, true);
04083 }
04084 
04085 void KateDocument::tagLines(KateTextCursor start, KateTextCursor end)
04086 {
04087   // May need to switch start/end cols if in block selection mode
04088   if (blockSelectionMode() && start.col() > end.col()) {
04089     int sc = start.col();
04090     start.setCol(end.col());
04091     end.setCol(sc);
04092   }
04093 
04094   for (uint z = 0; z < m_views.count(); z++)
04095     m_views.at(z)->tagLines(start, end, true);
04096 }
04097 
04098 void KateDocument::repaintViews(bool paintOnlyDirty)
04099 {
04100   for (uint z = 0; z < m_views.count(); z++)
04101     m_views.at(z)->repaintText(paintOnlyDirty);
04102 }
04103 
04104 void KateDocument::tagAll()
04105 {
04106   for (uint z = 0; z < m_views.count(); z++)
04107   {
04108     m_views.at(z)->tagAll();
04109     m_views.at(z)->updateView (true);
04110   }
04111 }
04112 
04113 uint KateDocument::configFlags ()
04114 {
04115   return config()->configFlags();
04116 }
04117 
04118 void KateDocument::setConfigFlags (uint flags)
04119 {
04120   config()->setConfigFlags(flags);
04121 }
04122 
04123 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04124 inline bool isEndBracket  ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04125 inline bool isBracket     ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04126 
04127 /*
04128    Bracket matching uses the following algorithm:
04129    If in overwrite mode, match the bracket currently underneath the cursor.
04130    Otherwise, if the character to the right of the cursor is an starting bracket,
04131    match it. Otherwise if the character to the left of the cursor is a
04132    ending bracket, match it. Otherwise, if the the character to the left
04133    of the cursor is an starting bracket, match it. Otherwise, if the character
04134    to the right of the cursor is an ending bracket, match it. Otherwise, don't
04135    match anything.
04136 */
04137 void KateDocument::newBracketMark( const KateTextCursor& cursor, KateBracketRange& bm, int maxLines )
04138 {
04139   bm.setValid(false);
04140 
04141   bm.start() = cursor;
04142 
04143   if( !findMatchingBracket( bm.start(), bm.end(), maxLines ) )
04144     return;
04145 
04146   bm.setValid(true);
04147 
04148   const int tw = config()->tabWidth();
04149   const int indentStart = m_buffer->plainLine(bm.start().line())->indentDepth(tw);
04150   const int indentEnd = m_buffer->plainLine(bm.end().line())->indentDepth(tw);
04151   bm.setIndentMin(kMin(indentStart, indentEnd));
04152 }
04153 
04154 bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines )
04155 {
04156   KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() );
04157   if( !textLine )
04158     return false;
04159 
04160   QChar right = textLine->getChar( start.col() );
04161   QChar left  = textLine->getChar( start.col() - 1 );
04162   QChar bracket;
04163 
04164   if ( config()->configFlags() & cfOvr ) {
04165     if( isBracket( right ) ) {
04166       bracket = right;
04167     } else {
04168       return false;
04169     }
04170   } else if ( isStartBracket( right ) ) {
04171     bracket = right;
04172   } else if ( isEndBracket( left ) ) {
04173     start.setCol(start.col() - 1);
04174     bracket = left;
04175   } else if ( isBracket( left ) ) {
04176     start.setCol(start.col() - 1);
04177     bracket = left;
04178   } else if ( isBracket( right ) ) {
04179     bracket = right;
04180   } else {
04181     return false;
04182   }
04183 
04184   QChar opposite;
04185 
04186   switch( bracket ) {
04187   case '{': opposite = '}'; break;
04188   case '}': opposite = '{'; break;
04189   case '[': opposite = ']'; break;
04190   case ']': opposite = '['; break;
04191   case '(': opposite = ')'; break;
04192   case ')': opposite = '('; break;
04193   default: return false;
04194   }
04195 
04196   bool forward = isStartBracket( bracket );
04197   int startAttr = textLine->attribute( start.col() );
04198   uint count = 0;
04199   int lines = 0;
04200   end = start;
04201 
04202   while( true ) {
04203     /* Increment or decrement, check base cases */
04204     if( forward ) {
04205       end.setCol(end.col() + 1);
04206       if( end.col() >= lineLength( end.line() ) ) {
04207         if( end.line() >= (int)lastLine() )
04208           return false;
04209         end.setPos(end.line() + 1, 0);
04210         textLine = m_buffer->plainLine( end.line() );
04211         lines++;
04212       }
04213     } else {
04214       end.setCol(end.col() - 1);
04215       if( end.col() < 0 ) {
04216         if( end.line() <= 0 )
04217           return false;
04218         end.setLine(end.line() - 1);
04219         end.setCol(lineLength( end.line() ) - 1);
04220         textLine = m_buffer->plainLine( end.line() );
04221         lines++;
04222       }
04223     }
04224 
04225     if ((maxLines != -1) && (lines > maxLines))
04226       return false;
04227 
04228     /* Easy way to skip comments */
04229     if( textLine->attribute( end.col() ) != startAttr )
04230       continue;
04231 
04232     /* Check for match */
04233     QChar c = textLine->getChar( end.col() );
04234     if( c == bracket ) {
04235       count++;
04236     } else if( c == opposite ) {
04237       if( count == 0 )
04238         return true;
04239       count--;
04240     }
04241 
04242   }
04243 }
04244 
04245 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
04246 {
04247   KParts::ReadWritePart::guiActivateEvent( ev );
04248   if ( ev->activated() )
04249     emit selectionChanged();
04250 }
04251 
04252 void KateDocument::setDocName (QString name )
04253 {
04254   if ( name == m_docName )
04255     return;
04256 
04257   if ( !name.isEmpty() )
04258   {
04259     // TODO check for similarly named documents
04260     m_docName = name;
04261     updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04262     emit nameChanged((Kate::Document *) this);
04263     return;
04264   }
04265 
04266   // if the name is set, and starts with FILENAME, it should not be changed!
04267   if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return;
04268 
04269   int count = -1;
04270 
04271   for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
04272   {
04273     if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) )
04274       if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count )
04275         count = KateFactory::self()->documents()->at(z)->m_docNameNumber;
04276   }
04277 
04278   m_docNameNumber = count + 1;
04279 
04280   m_docName = url().filename();
04281 
04282   if (m_docName.isEmpty())
04283     m_docName = i18n ("Untitled");
04284 
04285   if (m_docNameNumber > 0)
04286     m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
04287 
04288   updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04289   emit nameChanged ((Kate::Document *) this);
04290 }
04291 
04292 void KateDocument::slotModifiedOnDisk( Kate::View * /*v*/ )
04293 {
04294   if ( m_isasking < 0 )
04295   {
04296     m_isasking = 0;
04297     return;
04298   }
04299 
04300   if ( !s_fileChangedDialogsActivated || m_isasking )
04301     return;
04302 
04303   if (m_modOnHd && !url().isEmpty())
04304   {
04305     m_isasking = 1;
04306 
04307     KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), widget() );
04308     switch ( p.exec() )
04309     {
04310       case KateModOnHdPrompt::Save:
04311       {
04312         m_modOnHd = false;
04313         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
04314             url().url(),QString::null,widget(),i18n("Save File"));
04315 
04316         kdDebug(13020)<<"got "<<res.URLs.count()<<" URLs"<<endl;
04317         if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first() ) )
04318         {
04319           setEncoding( res.encoding );
04320 
04321           if( ! saveAs( res.URLs.first() ) )
04322           {
04323             KMessageBox::error( widget(), i18n("Save failed") );
04324             m_modOnHd = true;
04325           }
04326           else
04327             emit modifiedOnDisc( this, false, 0 );
04328         }
04329         else // the save as dialog was cancelled, we are still modified on disk
04330         {
04331           m_modOnHd = true;
04332         }
04333 
04334         m_isasking = 0;
04335         break;
04336       }
04337 
04338       case KateModOnHdPrompt::Reload:
04339         m_modOnHd = false;
04340         emit modifiedOnDisc( this, false, 0 );
04341         reloadFile();
04342         m_isasking = 0;
04343         break;
04344 
04345       case KateModOnHdPrompt::Ignore:
04346         m_modOnHd = false;
04347         emit modifiedOnDisc( this, false, 0 );
04348         m_isasking = 0;
04349         break;
04350 
04351       case KateModOnHdPrompt::Overwrite:
04352         m_modOnHd = false;
04353         emit modifiedOnDisc( this, false, 0 );
04354         m_isasking = 0;
04355         save();
04356         break;
04357 
04358       default:               // cancel: ignore next focus event
04359         m_isasking = -1;
04360     }
04361   }
04362 }
04363 
04364 void KateDocument::setModifiedOnDisk( int reason )
04365 {
04366   m_modOnHdReason = reason;
04367   m_modOnHd = (reason > 0);
04368   emit modifiedOnDisc( this, (reason > 0), reason );
04369 }
04370 
04371 class KateDocumentTmpMark
04372 {
04373   public:
04374     QString line;
04375     KTextEditor::Mark mark;
04376 };
04377 
04378 void KateDocument::reloadFile()
04379 {
04380   if ( !url().isEmpty() )
04381   {
04382     if (m_modOnHd && s_fileChangedDialogsActivated)
04383     {
04384       int i = KMessageBox::warningYesNoCancel
04385                 (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
04386                 i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes"));
04387 
04388       if ( i != KMessageBox::Yes)
04389       {
04390         if (i == KMessageBox::No)
04391         {
04392           m_modOnHd = false;
04393           m_modOnHdReason = 0;
04394           emit modifiedOnDisc (this, m_modOnHd, 0);
04395         }
04396 
04397         return;
04398       }
04399     }
04400 
04401     QValueList<KateDocumentTmpMark> tmp;
04402 
04403     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
04404     {
04405       KateDocumentTmpMark m;
04406 
04407       m.line = textLine (it.current()->line);
04408       m.mark = *it.current();
04409 
04410       tmp.append (m);
04411     }
04412 
04413     uint mode = hlMode ();
04414     bool byUser = hlSetByUser;
04415 
04416     m_storedVariables.clear();
04417 
04418     m_reloading = true;
04419 
04420     QValueList<int> lines, cols;
04421     for ( uint i=0; i < m_views.count(); i++ )
04422     {
04423       lines.append( m_views.at( i )->cursorLine() );
04424       cols.append( m_views.at( i )->cursorColumn() );
04425     }
04426 
04427     KateDocument::openURL( url() );
04428 
04429     for ( uint i=0; i < m_views.count(); i++ )
04430       m_views.at( i )->setCursorPositionInternal( lines[ i ], cols[ i ], m_config->tabWidth(), false );
04431 
04432     m_reloading = false;
04433 
04434     for ( QValueList<int>::size_type z=0; z < tmp.size(); z++ )
04435     {
04436       if (z < numLines())
04437       {
04438         if (textLine(tmp[z].mark.line) == tmp[z].line)
04439           setMark (tmp[z].mark.line, tmp[z].mark.type);
04440       }
04441     }
04442 
04443     if (byUser)
04444       setHlMode (mode);
04445   }
04446 }
04447 
04448 void KateDocument::flush ()
04449 {
04450   closeURL ();
04451 }
04452 
04453 void KateDocument::setWordWrap (bool on)
04454 {
04455   config()->setWordWrap (on);
04456 }
04457 
04458 bool KateDocument::wordWrap ()
04459 {
04460   return config()->wordWrap ();
04461 }
04462 
04463 void KateDocument::setWordWrapAt (uint col)
04464 {
04465   config()->setWordWrapAt (col);
04466 }
04467 
04468 unsigned int KateDocument::wordWrapAt ()
04469 {
04470   return config()->wordWrapAt ();
04471 }
04472 
04473 void KateDocument::applyWordWrap ()
04474 {
04475   // dummy to make the API happy
04476 }
04477 
04478 void KateDocument::setPageUpDownMovesCursor (bool on)
04479 {
04480   config()->setPageUpDownMovesCursor (on);
04481 }
04482 
04483 bool KateDocument::pageUpDownMovesCursor ()
04484 {
04485   return config()->pageUpDownMovesCursor ();
04486 }
04487 
04488 void KateDocument::dumpRegionTree()
04489 {
04490   m_buffer->foldingTree()->debugDump();
04491 }
04492 //END
04493 
04494 //BEGIN KTextEditor::CursorInterface stuff
04495 
04496 KTextEditor::Cursor *KateDocument::createCursor ( )
04497 {
04498   return new KateSuperCursor (this, false, 0, 0, this);
04499 }
04500 
04501 void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range)
04502 {
04503   if (view)
04504     view->tagLines(range->start(), range->end());
04505   else
04506     tagLines(range->start(), range->end());
04507 }
04508 
04509 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
04510 {
04511   m_buffer->lineInfo(info,line);
04512 }
04513 
04514 KateCodeFoldingTree *KateDocument::foldingTree ()
04515 {
04516   return m_buffer->foldingTree();
04517 }
04518 
04519 void KateDocument::setEncoding (const QString &e)
04520 {
04521   if ( m_encodingSticky )
04522     return;
04523 
04524   QString ce = m_config->encoding().lower();
04525   if ( e.lower() == ce )
04526     return;
04527 
04528   m_config->setEncoding( e );
04529   if ( ! m_loading )
04530     reloadFile();
04531 }
04532 
04533 QString KateDocument::encoding() const
04534 {
04535   return m_config->encoding();
04536 }
04537 
04538 void KateDocument::updateConfig ()
04539 {
04540   emit undoChanged ();
04541   tagAll();
04542 
04543   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
04544   {
04545     view->updateDocumentConfig ();
04546   }
04547 
04548   // switch indenter if needed
04549   if (m_indenter->modeNumber() != m_config->indentationMode())
04550   {
04551     delete m_indenter;
04552     m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() );
04553   }
04554 
04555   m_indenter->updateConfig();
04556 
04557   m_buffer->setTabWidth (config()->tabWidth());
04558 
04559   // plugins
04560   for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
04561   {
04562     if (config()->plugin (i))
04563       loadPlugin (i);
04564     else
04565       unloadPlugin (i);
04566   }
04567 }
04568 
04569 //BEGIN Variable reader
04570 // "local variable" feature by anders, 2003
04571 /* TODO
04572       add config options (how many lines to read, on/off)
04573       add interface for plugins/apps to set/get variables
04574       add view stuff
04575 */
04576 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
04577 QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)");
04578 QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)");
04579 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
04580 
04581 void KateDocument::readVariables(bool onlyViewAndRenderer)
04582 {
04583   if (!onlyViewAndRenderer)
04584     m_config->configStart();
04585 
04586   // views!
04587   KateView *v;
04588   for (v = m_views.first(); v != 0L; v= m_views.next() )
04589   {
04590     v->config()->configStart();
04591     v->renderer()->config()->configStart();
04592   }
04593   // read a number of lines in the top/bottom of the document
04594   for (uint i=0; i < kMin( 9U, numLines() ); ++i )
04595   {
04596     readVariableLine( textLine( i ), onlyViewAndRenderer );
04597   }
04598   if ( numLines() > 10 )
04599   {
04600     for ( uint i = kMax(10U, numLines() - 10); i < numLines(); ++i )
04601     {
04602       readVariableLine( textLine( i ), onlyViewAndRenderer );
04603     }
04604   }
04605 
04606   if (!onlyViewAndRenderer)
04607     m_config->configEnd();
04608 
04609   for (v = m_views.first(); v != 0L; v= m_views.next() )
04610   {
04611     v->config()->configEnd();
04612     v->renderer()->config()->configEnd();
04613   }
04614 }
04615 
04616 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
04617 {
04618   // simple check first, no regex
04619   // no kate inside, no vars, simple...
04620   if (t.find("kate") < 0)
04621     return;
04622 
04623   // found vars, if any
04624   QString s;
04625 
04626   if ( kvLine.search( t ) > -1 )
04627   {
04628     s = kvLine.cap(1);
04629 
04630     kdDebug (13020) << "normal variable line kate: matched: " << s << endl;
04631   }
04632   else if (kvLineWildcard.search( t ) > -1) // regex given
04633   {
04634     QStringList wildcards (QStringList::split(';', kvLineWildcard.cap(1)));
04635     QString nameOfFile = url().fileName();
04636 
04637     bool found = false;
04638     for (QStringList::size_type i = 0; !found && i < wildcards.size(); ++i)
04639     {
04640       QRegExp wildcard (wildcards[i], true/*Qt::CaseSensitive*/, true/*QRegExp::Wildcard*/);
04641 
04642       found = wildcard.exactMatch (nameOfFile);
04643     }
04644 
04645     // nothing usable found...
04646     if (!found)
04647       return;
04648 
04649     s = kvLineWildcard.cap(2);
04650 
04651     kdDebug (13020) << "guarded variable line kate-wildcard: matched: " << s << endl;
04652   }
04653   else if (kvLineMime.search( t ) > -1) // mime-type given
04654   {
04655     QStringList types (QStringList::split(';', kvLineMime.cap(1)));
04656 
04657     // no matching type found
04658     if (!types.contains (mimeType ()))
04659       return;
04660 
04661     s = kvLineMime.cap(2);
04662 
04663     kdDebug (13020) << "guarded variable line kate-mimetype: matched: " << s << endl;
04664   }
04665   else // nothing found
04666   {
04667     return;
04668   }
04669 
04670   QStringList vvl; // view variable names
04671   vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
04672       << "line-numbers" << "icon-border" << "folding-markers"
04673       << "bookmark-sorting" << "auto-center-lines"
04674       << "icon-bar-color"
04675       // renderer
04676       << "background-color" << "selection-color"
04677       << "current-line-color" << "bracket-highlight-color"
04678       << "word-wrap-marker-color"
04679       << "font" << "font-size" << "scheme";
04680   int p( 0 );
04681 
04682   QString  var, val;
04683   while ( (p = kvVar.search( s, p )) > -1 )
04684   {
04685     p += kvVar.matchedLength();
04686     var = kvVar.cap( 1 );
04687     val = kvVar.cap( 2 ).stripWhiteSpace();
04688     bool state; // store booleans here
04689     int n; // store ints here
04690 
04691     // only apply view & renderer config stuff
04692     if (onlyViewAndRenderer)
04693     {
04694       if ( vvl.contains( var ) ) // FIXME define above
04695         setViewVariable( var, val );
04696     }
04697     else
04698     {
04699       // BOOL  SETTINGS
04700       if ( var == "word-wrap" && checkBoolValue( val, &state ) )
04701         setWordWrap( state ); // ??? FIXME CHECK
04702       else if ( var == "block-selection"  && checkBoolValue( val, &state ) )
04703         setBlockSelectionMode( state );
04704       // KateConfig::configFlags
04705       // FIXME should this be optimized to only a few calls? how?
04706       else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
04707         m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
04708       else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
04709         m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
04710       else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
04711         m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
04712       else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
04713         m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
04714       else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
04715         m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
04716       else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
04717         m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
04718       else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) )
04719         m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state );
04720       else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
04721         m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
04722       else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
04723         m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
04724       else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
04725         m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
04726       else if ( var == "space-indent" && checkBoolValue( val, &state ) )
04727         m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state );
04728       else if ( var == "smart-home" && checkBoolValue( val, &state ) )
04729         m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
04730       else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
04731         m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
04732       else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) )
04733         m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state);
04734       else if ( var == "mixed-indent" && checkBoolValue( val, &state ) )
04735         m_config->setConfigFlags( KateDocumentConfig::cfMixedIndent, state );
04736 
04737       // INTEGER SETTINGS
04738       else if ( var == "tab-width" && checkIntValue( val, &n ) )
04739         m_config->setTabWidth( n );
04740       else if ( var == "indent-width"  && checkIntValue( val, &n ) )
04741         m_config->setIndentationWidth( n );
04742       else if ( var == "indent-mode" )
04743       {
04744         if ( checkIntValue( val, &n ) )
04745           m_config->setIndentationMode( n );
04746         else
04747           m_config->setIndentationMode( KateAutoIndent::modeNumber( val) );
04748       }
04749       else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 ) // uint, but hard word wrap at 0 will be no fun ;)
04750         m_config->setWordWrapAt( n );
04751       else if ( var == "undo-steps" && checkIntValue( val, &n ) && n >= 0 )
04752         setUndoSteps( n );
04753 
04754       // STRING SETTINGS
04755       else if ( var == "eol" || var == "end-of-line" )
04756       {
04757         QStringList l;
04758         l << "unix" << "dos" << "mac";
04759         if ( (n = l.findIndex( val.lower() )) != -1 )
04760           m_config->setEol( n );
04761       }
04762       else if ( var == "encoding" )
04763         m_config->setEncoding( val );
04764       else if ( var == "syntax" || var == "hl" )
04765       {
04766         for ( uint i=0; i < hlModeCount(); i++ )
04767         {
04768           if ( hlModeName( i ).lower() == val.lower() )
04769           {
04770             setHlMode( i );
04771             break;
04772           }
04773         }
04774       }
04775 
04776       // VIEW SETTINGS
04777       else if ( vvl.contains( var ) )
04778         setViewVariable( var, val );
04779       else
04780       {
04781         m_storedVariables.insert( var, val );
04782         emit variableChanged( var, val );
04783       }
04784     }
04785   }
04786 }
04787 
04788 void KateDocument::setViewVariable( QString var, QString val )
04789 {
04790   KateView *v;
04791   bool state;
04792   int n;
04793   QColor c;
04794   for (v = m_views.first(); v != 0L; v= m_views.next() )
04795   {
04796     if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
04797       v->config()->setDynWordWrap( state );
04798     else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
04799       v->config()->setPersistentSelection( state );
04800     //else if ( var = "dynamic-word-wrap-indicators" )
04801     else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
04802       v->config()->setLineNumbers( state );
04803     else if (var == "icon-border" && checkBoolValue( val, &state ) )
04804       v->config()->setIconBar( state );
04805     else if (var == "folding-markers" && checkBoolValue( val, &state ) )
04806       v->config()->setFoldingBar( state );
04807     else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
04808       v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
04809     else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
04810       v->renderer()->config()->setIconBarColor( c );
04811     // RENDERER
04812     else if ( var == "background-color" && checkColorValue( val, c ) )
04813       v->renderer()->config()->setBackgroundColor( c );
04814     else if ( var == "selection-color" && checkColorValue( val, c ) )
04815       v->renderer()->config()->setSelectionColor( c );
04816     else if ( var == "current-line-color" && checkColorValue( val, c ) )
04817       v->renderer()->config()->setHighlightedLineColor( c );
04818     else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
04819       v->renderer()->config()->setHighlightedBracketColor( c );
04820     else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
04821       v->renderer()->config()->setWordWrapMarkerColor( c );
04822     else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
04823     {
04824       QFont _f( *v->renderer()->config()->font(  ) );
04825 
04826       if ( var == "font" )
04827       {
04828         _f.setFamily( val );
04829         _f.setFixedPitch( QFont( val ).fixedPitch() );
04830       }
04831       else
04832         _f.setPointSize( n );
04833 
04834       v->renderer()->config()->setFont( _f );
04835     }
04836     else if ( var == "scheme" )
04837     {
04838       v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) );
04839     }
04840   }
04841 }
04842 
04843 bool KateDocument::checkBoolValue( QString val, bool *result )
04844 {
04845   val = val.stripWhiteSpace().lower();
04846   QStringList l;
04847   l << "1" << "on" << "true";
04848   if ( l.contains( val ) )
04849   {
04850     *result = true;
04851     return true;
04852   }
04853   l.clear();
04854   l << "0" << "off" << "false";
04855   if ( l.contains( val ) )
04856   {
04857     *result = false;
04858     return true;
04859   }
04860   return false;
04861 }
04862 
04863 bool KateDocument::checkIntValue( QString val, int *result )
04864 {
04865   bool ret( false );
04866   *result = val.toInt( &ret );
04867   return ret;
04868 }
04869 
04870 bool KateDocument::checkColorValue( QString val, QColor &c )
04871 {
04872   c.setNamedColor( val );
04873   return c.isValid();
04874 }
04875 
04876 // KTextEditor::variable
04877 QString KateDocument::variable( const QString &name ) const
04878 {
04879   if ( m_storedVariables.contains( name ) )
04880     return m_storedVariables[ name ];
04881 
04882   return "";
04883 }
04884 
04885 //END
04886 
04887 void KateDocument::slotModOnHdDirty (const QString &path)
04888 {
04889   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1))
04890   {
04891     // compare md5 with the one we have (if we have one)
04892     if ( ! m_digest.isEmpty() )
04893     {
04894       QCString tmp;
04895       if ( createDigest( tmp ) && tmp == m_digest )
04896         return;
04897     }
04898 
04899     m_modOnHd = true;
04900     m_modOnHdReason = 1;
04901 
04902     // reenable dialog if not running atm
04903     if (m_isasking == -1)
04904       m_isasking = false;
04905 
04906     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04907   }
04908 }
04909 
04910 void KateDocument::slotModOnHdCreated (const QString &path)
04911 {
04912   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2))
04913   {
04914     m_modOnHd = true;
04915     m_modOnHdReason = 2;
04916 
04917     // reenable dialog if not running atm
04918     if (m_isasking == -1)
04919       m_isasking = false;
04920 
04921     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04922   }
04923 }
04924 
04925 void KateDocument::slotModOnHdDeleted (const QString &path)
04926 {
04927   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3))
04928   {
04929     m_modOnHd = true;
04930     m_modOnHdReason = 3;
04931 
04932     // reenable dialog if not running atm
04933     if (m_isasking == -1)
04934       m_isasking = false;
04935 
04936     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04937   }
04938 }
04939 
04940 bool KateDocument::createDigest( QCString &result )
04941 {
04942   bool ret = false;
04943   result = "";
04944   if ( url().isLocalFile() )
04945   {
04946     QFile f ( url().path() );
04947     if ( f.open( IO_ReadOnly) )
04948     {
04949       KMD5 md5;
04950       ret = md5.update( f );
04951       md5.hexDigest( result );
04952       f.close();
04953       ret = true;
04954     }
04955   }
04956   return ret;
04957 }
04958 
04959 QString KateDocument::reasonedMOHString() const
04960 {
04961   switch( m_modOnHdReason )
04962   {
04963     case 1:
04964       return i18n("The file '%1' was modified by another program.").arg( url().prettyURL() );
04965       break;
04966     case 2:
04967       return i18n("The file '%1' was created by another program.").arg( url().prettyURL() );
04968       break;
04969     case 3:
04970       return i18n("The file '%1' was deleted by another program.").arg( url().prettyURL() );
04971       break;
04972     default:
04973       return QString();
04974   }
04975 }
04976 
04977 void KateDocument::removeTrailingSpace( uint line )
04978 {
04979   // remove trailing spaces from left line if required
04980   if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn )
04981   {
04982     KateTextLine::Ptr ln = kateTextLine( line );
04983 
04984     if ( ! ln ) return;
04985 
04986     if ( line == activeView()->cursorLine()
04987          && activeView()->cursorColumnReal() >= (uint)kMax(0,ln->lastChar()) )
04988       return;
04989 
04990     if ( ln->length() )
04991     {
04992       uint p = ln->lastChar() + 1;
04993       uint l = ln->length() - p;
04994       if ( l )
04995         editRemoveText( line, p, l);
04996     }
04997   }
04998 }
04999 
05000 void KateDocument::updateFileType (int newType, bool user)
05001 {
05002   if (user || !m_fileTypeSetByUser)
05003   {
05004     const KateFileType *t = 0;
05005     if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType)))
05006     {
05007       m_fileType = newType;
05008 
05009       if (t)
05010       {
05011         m_config->configStart();
05012         // views!
05013         KateView *v;
05014         for (v = m_views.first(); v != 0L; v= m_views.next() )
05015         {
05016           v->config()->configStart();
05017           v->renderer()->config()->configStart();
05018         }
05019 
05020         readVariableLine( t->varLine );
05021 
05022         m_config->configEnd();
05023         for (v = m_views.first(); v != 0L; v= m_views.next() )
05024         {
05025           v->config()->configEnd();
05026           v->renderer()->config()->configEnd();
05027         }
05028       }
05029     }
05030   }
05031 }
05032 
05033 uint KateDocument::documentNumber () const
05034 {
05035   return KTextEditor::Document::documentNumber ();
05036 }
05037 
05038 
05039 
05040 
05041 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
05042       *handled=true;
05043       *abortClosing=true;
05044       if (m_url.isEmpty())
05045       {
05046         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
05047                 QString::null,QString::null,0,i18n("Save File"));
05048 
05049         if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) {
05050                 *abortClosing=true;
05051                 return;
05052         }
05053         setEncoding( res.encoding );
05054           saveAs( res.URLs.first() );
05055         *abortClosing=false;
05056       }
05057       else
05058       {
05059           save();
05060           *abortClosing=false;
05061       }
05062 
05063 }
05064 
05065 bool KateDocument::checkOverwrite( KURL u )
05066 {
05067   if( !u.isLocalFile() )
05068     return true;
05069 
05070   QFileInfo info( u.path() );
05071   if( !info.exists() )
05072     return true;
05073 
05074   return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0,
05075     i18n( "A file named \"%1\" already exists. "
05076           "Are you sure you want to overwrite it?" ).arg( info.fileName() ),
05077     i18n( "Overwrite File?" ),
05078     i18n( "&Overwrite" ) );
05079 }
05080 
05081 void KateDocument::setDefaultEncoding (const QString &encoding)
05082 {
05083   s_defaultEncoding = encoding;
05084 }
05085 
05086 //BEGIN KTextEditor::TemplateInterface
05087 bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
05088       return (new KateTemplateHandler(this,line,column,templateString,initialValues))->initOk();
05089 }
05090 
05091 void KateDocument::testTemplateCode() {
05092   int col=activeView()->cursorColumn();
05093   int line=activeView()->cursorLine();
05094   insertTemplateText(line,col,"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05095 }
05096 
05097 bool KateDocument::invokeTabInterceptor(KKey key) {
05098   if (m_tabInterceptor) return (*m_tabInterceptor)(key);
05099   return false;
05100 }
05101 
05102 bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05103   if (m_tabInterceptor) return false;
05104   m_tabInterceptor=interceptor;
05105   return true;
05106 }
05107 
05108 bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05109   if (m_tabInterceptor!=interceptor) return false;
05110   m_tabInterceptor=0;
05111   return true;
05112 }
05113 //END KTextEditor::TemplateInterface
05114 
05115 //BEGIN DEPRECATED STUFF
05116  bool KateDocument::setSelection ( uint startLine, uint startCol, uint endLine, uint endCol )
05117 { if (m_activeView) return m_activeView->setSelection (startLine, startCol, endLine, endCol); return false; }
05118 
05119  bool KateDocument::clearSelection ()
05120  { if (m_activeView) return m_activeView->clearSelection(); return false; }
05121 
05122  bool KateDocument::hasSelection () const
05123  { if (m_activeView) return m_activeView->hasSelection (); return false; }
05124 
05125  QString KateDocument::selection () const
05126  { if (m_activeView) return m_activeView->selection (); return QString(""); }
05127 
05128  bool KateDocument::removeSelectedText ()
05129  { if (m_activeView) return m_activeView->removeSelectedText (); return false; }
05130 
05131  bool KateDocument::selectAll()
05132  { if (m_activeView) return m_activeView->selectAll (); return false; }
05133 
05134  int KateDocument::selStartLine()
05135  { if (m_activeView) return m_activeView->selStartLine (); return 0; }
05136 
05137  int KateDocument::selStartCol()
05138  { if (m_activeView) return m_activeView->selStartCol (); return 0; }
05139 
05140  int KateDocument::selEndLine()
05141  { if (m_activeView) return m_activeView->selEndLine (); return 0; }
05142 
05143  int KateDocument::selEndCol()
05144  { if (m_activeView) return m_activeView->selEndCol (); return 0; }
05145 
05146  bool KateDocument::blockSelectionMode ()
05147     { if (m_activeView) return m_activeView->blockSelectionMode (); return false; }
05148 
05149 bool KateDocument::setBlockSelectionMode (bool on)
05150     { if (m_activeView) return m_activeView->setBlockSelectionMode (on); return false; }
05151 
05152 bool KateDocument::toggleBlockSelectionMode ()
05153     { if (m_activeView) return m_activeView->toggleBlockSelectionMode (); return false; }
05154 //END DEPRECATED
05155 
05156 //END DEPRECATED STUFF
05157 
05158 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Home | KDE Accessibility Home | Description of Access Keys