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   /*
01552   How this works:
01553 
01554     After noticing that there where to many scenarios to take into
01555     consideration when using 'if's to toggle the "Modified" flag
01556     I came up with this baby, flexible and repetitive calls are
01557     minimal.
01558 
01559     A numeric unique pattern is generated by toggleing a set of bits,
01560     each bit symbolizes a different state in the Undo Redo structure.
01561 
01562       undoItems.isEmpty() != null          BIT 1
01563       redoItems.isEmpty() != null          BIT 2
01564       docWasSavedWhenUndoWasEmpty == true  BIT 3
01565       docWasSavedWhenRedoWasEmpty == true  BIT 4
01566       lastUndoGroupWhenSavedIsLastUndo     BIT 5
01567       lastUndoGroupWhenSavedIsLastRedo     BIT 6
01568       lastRedoGroupWhenSavedIsLastUndo     BIT 7
01569       lastRedoGroupWhenSavedIsLastRedo     BIT 8
01570 
01571     If you find a new pattern, please add it to the patterns array
01572   */
01573 
01574   unsigned char currentPattern = 0;
01575   const unsigned char patterns[] = {5,16,24,26,88,90,93,133,144,149,165};
01576   const unsigned char patternCount = sizeof(patterns);
01577   KateUndoGroup* undoLast = 0;
01578   KateUndoGroup* redoLast = 0;
01579 
01580   if (undoItems.isEmpty())
01581   {
01582     currentPattern |= 1;
01583   }
01584   else
01585   {
01586     undoLast = undoItems.last();
01587   }
01588 
01589   if (redoItems.isEmpty())
01590   {
01591     currentPattern |= 2;
01592   }
01593   else
01594   {
01595     redoLast = redoItems.last();
01596   }
01597 
01598   if (docWasSavedWhenUndoWasEmpty) currentPattern |= 4;
01599   if (docWasSavedWhenRedoWasEmpty) currentPattern |= 8;
01600   if (lastUndoGroupWhenSaved == undoLast) currentPattern |= 16;
01601   if (lastUndoGroupWhenSaved == redoLast) currentPattern |= 32;
01602   if (lastRedoGroupWhenSaved == undoLast) currentPattern |= 64;
01603   if (lastRedoGroupWhenSaved == redoLast) currentPattern |= 128;
01604 
01605   // This will print out the pattern information
01606 
01607   kdDebug(13020) << k_funcinfo
01608     << "Pattern:" << static_cast<unsigned int>(currentPattern) << endl;
01609 
01610   for (uint patternIndex = 0; patternIndex < patternCount; ++patternIndex)
01611   {
01612     if ( currentPattern == patterns[patternIndex] )
01613     {
01614       setModified( false );
01615       kdDebug(13020) << k_funcinfo << "setting modified to false!" << endl;
01616       break;
01617     }
01618   }
01619 }
01620 
01621 void KateDocument::clearUndo()
01622 {
01623   undoItems.setAutoDelete (true);
01624   undoItems.clear ();
01625   undoItems.setAutoDelete (false);
01626 
01627   lastUndoGroupWhenSaved = 0;
01628   docWasSavedWhenUndoWasEmpty = false;
01629 
01630   emit undoChanged ();
01631 }
01632 
01633 void KateDocument::clearRedo()
01634 {
01635   redoItems.setAutoDelete (true);
01636   redoItems.clear ();
01637   redoItems.setAutoDelete (false);
01638 
01639   lastRedoGroupWhenSaved = 0;
01640   docWasSavedWhenRedoWasEmpty = false;
01641 
01642   emit undoChanged ();
01643 }
01644 
01645 QPtrList<KTextEditor::Cursor> KateDocument::cursors () const
01646 {
01647   return myCursors;
01648 }
01649 //END
01650 
01651 //BEGIN KTextEditor::SearchInterface stuff
01652 
01653 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)
01654 {
01655   if (text.isEmpty())
01656     return false;
01657 
01658   int line = startLine;
01659   int col = startCol;
01660 
01661   if (!backwards)
01662   {
01663     int searchEnd = lastLine();
01664 
01665     while (line <= searchEnd)
01666     {
01667       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01668 
01669       if (!textLine)
01670         return false;
01671 
01672       uint foundAt, myMatchLen;
01673       bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, false);
01674 
01675       if (found)
01676       {
01677         (*foundAtLine) = line;
01678         (*foundAtCol) = foundAt;
01679         (*matchLen) = myMatchLen;
01680         return true;
01681       }
01682 
01683       col = 0;
01684       line++;
01685     }
01686   }
01687   else
01688   {
01689     // backward search
01690     int searchEnd = 0;
01691 
01692     while (line >= searchEnd)
01693     {
01694       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01695 
01696       if (!textLine)
01697         return false;
01698 
01699       uint foundAt, myMatchLen;
01700       bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, true);
01701 
01702       if (found)
01703       {
01704        /* if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
01705             && line == selectStart.line() && foundAt == (uint) selectStart.col()
01706             && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
01707         {
01708           // To avoid getting stuck at one match we skip a match if it is already
01709           // selected (most likely because it has just been found).
01710           if (foundAt > 0)
01711             col = foundAt - 1;
01712           else {
01713             if (--line >= 0)
01714               col = lineLength(line);
01715           }
01716           continue;
01717       }*/
01718 
01719         (*foundAtLine) = line;
01720         (*foundAtCol) = foundAt;
01721         (*matchLen) = myMatchLen;
01722         return true;
01723       }
01724 
01725       if (line >= 1)
01726         col = lineLength(line-1);
01727 
01728       line--;
01729     }
01730   }
01731 
01732   return false;
01733 }
01734 
01735 bool KateDocument::searchText (unsigned int startLine, unsigned int startCol, const QRegExp &regexp, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool backwards)
01736 {
01737   kdDebug(13020)<<"KateDocument::searchText( "<<startLine<<", "<<startCol<<", "<<regexp.pattern()<<", "<<backwards<<" )"<<endl;
01738   if (regexp.isEmpty() || !regexp.isValid())
01739     return false;
01740 
01741   int line = startLine;
01742   int col = startCol;
01743 
01744   if (!backwards)
01745   {
01746     int searchEnd = lastLine();
01747 
01748     while (line <= searchEnd)
01749     {
01750       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01751 
01752       if (!textLine)
01753         return false;
01754 
01755       uint foundAt, myMatchLen;
01756       bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, false);
01757 
01758       if (found)
01759       {
01760         // A special case which can only occur when searching with a regular expression consisting
01761         // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{').
01762         if (myMatchLen == 0 && (uint) line == startLine && foundAt == (uint) col)
01763         {
01764           if (col < lineLength(line))
01765             col++;
01766           else {
01767             line++;
01768             col = 0;
01769           }
01770           continue;
01771         }
01772 
01773         (*foundAtLine) = line;
01774         (*foundAtCol) = foundAt;
01775         (*matchLen) = myMatchLen;
01776         return true;
01777       }
01778 
01779       col = 0;
01780       line++;
01781     }
01782   }
01783   else
01784   {
01785     // backward search
01786     int searchEnd = 0;
01787 
01788     while (line >= searchEnd)
01789     {
01790       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01791 
01792       if (!textLine)
01793         return false;
01794 
01795       uint foundAt, myMatchLen;
01796       bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, true);
01797 
01798       if (found)
01799       {
01800         /*if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
01801             && line == selectStart.line() && foundAt == (uint) selectStart.col()
01802             && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
01803         {
01804           // To avoid getting stuck at one match we skip a match if it is already
01805           // selected (most likely because it has just been found).
01806           if (foundAt > 0)
01807             col = foundAt - 1;
01808           else {
01809             if (--line >= 0)
01810               col = lineLength(line);
01811           }
01812           continue;
01813       }*/
01814 
01815         (*foundAtLine) = line;
01816         (*foundAtCol) = foundAt;
01817         (*matchLen) = myMatchLen;
01818         return true;
01819       }
01820 
01821       if (line >= 1)
01822         col = lineLength(line-1);
01823 
01824       line--;
01825     }
01826   }
01827 
01828   return false;
01829 }
01830 //END
01831 
01832 //BEGIN KTextEditor::HighlightingInterface stuff
01833 
01834 uint KateDocument::hlMode ()
01835 {
01836   return KateHlManager::self()->findHl(highlight());
01837 }
01838 
01839 bool KateDocument::setHlMode (uint mode)
01840 {
01841   m_buffer->setHighlight (mode);
01842 
01843   if (true)
01844   {
01845     setDontChangeHlOnSave();
01846     return true;
01847   }
01848 
01849   return false;
01850 }
01851 
01852 void KateDocument::bufferHlChanged ()
01853 {
01854   // update all views
01855   makeAttribs(false);
01856 
01857   emit hlChanged();
01858 }
01859 
01860 uint KateDocument::hlModeCount ()
01861 {
01862   return KateHlManager::self()->highlights();
01863 }
01864 
01865 QString KateDocument::hlModeName (uint mode)
01866 {
01867   return KateHlManager::self()->hlName (mode);
01868 }
01869 
01870 QString KateDocument::hlModeSectionName (uint mode)
01871 {
01872   return KateHlManager::self()->hlSection (mode);
01873 }
01874 
01875 void KateDocument::setDontChangeHlOnSave()
01876 {
01877   hlSetByUser = true;
01878 }
01879 //END
01880 
01881 //BEGIN KTextEditor::ConfigInterface stuff
01882 void KateDocument::readConfig(KConfig *config)
01883 {
01884   config->setGroup("Kate Document Defaults");
01885 
01886   // read max loadable blocks, more blocks will be swapped out
01887   KateBuffer::setMaxLoadedBlocks (config->readNumEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks()));
01888 
01889   KateDocumentConfig::global()->readConfig (config);
01890 
01891   config->setGroup("Kate View Defaults");
01892   KateViewConfig::global()->readConfig (config);
01893 
01894   config->setGroup("Kate Renderer Defaults");
01895   KateRendererConfig::global()->readConfig (config);
01896 }
01897 
01898 void KateDocument::writeConfig(KConfig *config)
01899 {
01900   config->setGroup("Kate Document Defaults");
01901 
01902   // write max loadable blocks, more blocks will be swapped out
01903   config->writeEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks());
01904 
01905   KateDocumentConfig::global()->writeConfig (config);
01906 
01907   config->setGroup("Kate View Defaults");
01908   KateViewConfig::global()->writeConfig (config);
01909 
01910   config->setGroup("Kate Renderer Defaults");
01911   KateRendererConfig::global()->writeConfig (config);
01912 }
01913 
01914 void KateDocument::readConfig()
01915 {
01916   KConfig *config = kapp->config();
01917   readConfig (config);
01918 }
01919 
01920 void KateDocument::writeConfig()
01921 {
01922   KConfig *config = kapp->config();
01923   writeConfig (config);
01924   config->sync();
01925 }
01926 
01927 void KateDocument::readSessionConfig(KConfig *kconfig)
01928 {
01929   // restore the url
01930   KURL url (kconfig->readEntry("URL"));
01931 
01932   // get the encoding
01933   QString tmpenc=kconfig->readEntry("Encoding");
01934   if (!tmpenc.isEmpty() && (tmpenc != encoding()))
01935     setEncoding(tmpenc);
01936 
01937   // open the file if url valid
01938   if (!url.isEmpty() && url.isValid())
01939     openURL (url);
01940 
01941   // restore the hl stuff
01942   m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig->readEntry("Highlighting")));
01943 
01944   if (hlMode() > 0)
01945     hlSetByUser = true;
01946 
01947   // indent mode
01948   config()->setIndentationMode( (uint)kconfig->readNumEntry("Indentation Mode", config()->indentationMode() ) );
01949 
01950   // Restore Bookmarks
01951   QValueList<int> marks = kconfig->readIntListEntry("Bookmarks");
01952   for( uint i = 0; i < marks.count(); i++ )
01953     addMark( marks[i], KateDocument::markType01 );
01954 }
01955 
01956 void KateDocument::writeSessionConfig(KConfig *kconfig)
01957 {
01958   if ( m_url.isLocalFile() && !KGlobal::dirs()->relativeLocation("tmp", m_url.path()).startsWith("/"))
01959        return;
01960   // save url
01961   kconfig->writeEntry("URL", m_url.prettyURL() );
01962 
01963   // save encoding
01964   kconfig->writeEntry("Encoding",encoding());
01965 
01966   // save hl
01967   kconfig->writeEntry("Highlighting", highlight()->name());
01968 
01969   kconfig->writeEntry("Indentation Mode", config()->indentationMode() );
01970 
01971   // Save Bookmarks
01972   QValueList<int> marks;
01973   for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
01974        it.current() && it.current()->type & KTextEditor::MarkInterface::markType01;
01975        ++it )
01976      marks << it.current()->line;
01977 
01978   kconfig->writeEntry( "Bookmarks", marks );
01979 }
01980 
01981 void KateDocument::configDialog()
01982 {
01983   KDialogBase *kd = new KDialogBase ( KDialogBase::IconList,
01984                                       i18n("Configure"),
01985                                       KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help,
01986                                       KDialogBase::Ok,
01987                                       kapp->mainWidget() );
01988 
01989 #ifndef Q_WS_WIN //TODO: reenable
01990   KWin::setIcons( kd->winId(), kapp->icon(), kapp->miniIcon() );
01991 #endif
01992 
01993   QPtrList<KTextEditor::ConfigPage> editorPages;
01994 
01995   for (uint i = 0; i < KTextEditor::configInterfaceExtension (this)->configPages (); i++)
01996   {
01997     QStringList path;
01998     path.clear();
01999     path << KTextEditor::configInterfaceExtension (this)->configPageName (i);
02000     QVBox *page = kd->addVBoxPage(path, KTextEditor::configInterfaceExtension (this)->configPageFullName (i),
02001                               KTextEditor::configInterfaceExtension (this)->configPagePixmap(i, KIcon::SizeMedium) );
02002 
02003     editorPages.append (KTextEditor::configInterfaceExtension (this)->configPage(i, page));
02004   }
02005 
02006   kd->resize(800, 480);
02007 
02008   if (kd->exec())
02009   {
02010     KateDocumentConfig::global()->configStart ();
02011     KateViewConfig::global()->configStart ();
02012     KateRendererConfig::global()->configStart ();
02013 
02014     for (uint i=0; i<editorPages.count(); i++)
02015     {
02016       editorPages.at(i)->apply();
02017     }
02018 
02019     KateDocumentConfig::global()->configEnd ();
02020     KateViewConfig::global()->configEnd ();
02021     KateRendererConfig::global()->configEnd ();
02022 
02023     writeConfig ();
02024   }
02025 
02026   delete kd;
02027 }
02028 
02029 uint KateDocument::mark( uint line )
02030 {
02031   if( !m_marks[line] )
02032     return 0;
02033   return m_marks[line]->type;
02034 }
02035 
02036 void KateDocument::setMark( uint line, uint markType )
02037 {
02038   clearMark( line );
02039   addMark( line, markType );
02040 }
02041 
02042 void KateDocument::clearMark( uint line )
02043 {
02044   if( line > lastLine() )
02045     return;
02046 
02047   if( !m_marks[line] )
02048     return;
02049 
02050   KTextEditor::Mark* mark = m_marks.take( line );
02051   emit markChanged( *mark, MarkRemoved );
02052   emit marksChanged();
02053   delete mark;
02054   tagLines( line, line );
02055   repaintViews(true);
02056 }
02057 
02058 void KateDocument::addMark( uint line, uint markType )
02059 {
02060   if( line > lastLine())
02061     return;
02062 
02063   if( markType == 0 )
02064     return;
02065 
02066   if( m_marks[line] ) {
02067     KTextEditor::Mark* mark = m_marks[line];
02068 
02069     // Remove bits already set
02070     markType &= ~mark->type;
02071 
02072     if( markType == 0 )
02073       return;
02074 
02075     // Add bits
02076     mark->type |= markType;
02077   } else {
02078     KTextEditor::Mark *mark = new KTextEditor::Mark;
02079     mark->line = line;
02080     mark->type = markType;
02081     m_marks.insert( line, mark );
02082   }
02083 
02084   // Emit with a mark having only the types added.
02085   KTextEditor::Mark temp;
02086   temp.line = line;
02087   temp.type = markType;
02088   emit markChanged( temp, MarkAdded );
02089 
02090   emit marksChanged();
02091   tagLines( line, line );
02092   repaintViews(true);
02093 }
02094 
02095 void KateDocument::removeMark( uint line, uint markType )
02096 {
02097   if( line > lastLine() )
02098     return;
02099   if( !m_marks[line] )
02100     return;
02101 
02102   KTextEditor::Mark* mark = m_marks[line];
02103 
02104   // Remove bits not set
02105   markType &= mark->type;
02106 
02107   if( markType == 0 )
02108     return;
02109 
02110   // Subtract bits
02111   mark->type &= ~markType;
02112 
02113   // Emit with a mark having only the types removed.
02114   KTextEditor::Mark temp;
02115   temp.line = line;
02116   temp.type = markType;
02117   emit markChanged( temp, MarkRemoved );
02118 
02119   if( mark->type == 0 )
02120     m_marks.remove( line );
02121 
02122   emit marksChanged();
02123   tagLines( line, line );
02124   repaintViews(true);
02125 }
02126 
02127 QPtrList<KTextEditor::Mark> KateDocument::marks()
02128 {
02129   QPtrList<KTextEditor::Mark> list;
02130 
02131   for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
02132        it.current(); ++it ) {
02133     list.append( it.current() );
02134   }
02135 
02136   return list;
02137 }
02138 
02139 void KateDocument::clearMarks()
02140 {
02141   for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
02142        it.current(); ++it ) {
02143     KTextEditor::Mark* mark = it.current();
02144     emit markChanged( *mark, MarkRemoved );
02145     tagLines( mark->line, mark->line );
02146   }
02147 
02148   m_marks.clear();
02149 
02150   emit marksChanged();
02151   repaintViews(true);
02152 }
02153 
02154 void KateDocument::setPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
02155 {
02156   m_markPixmaps.replace( type, new QPixmap( pixmap ) );
02157 }
02158 
02159 void KateDocument::setDescription( MarkInterface::MarkTypes type, const QString& description )
02160 {
02161   m_markDescriptions.replace( type, new QString( description ) );
02162 }
02163 
02164 QPixmap *KateDocument::markPixmap( MarkInterface::MarkTypes type )
02165 {
02166   return m_markPixmaps[type];
02167 }
02168 
02169 QColor KateDocument::markColor( MarkInterface::MarkTypes type )
02170 {
02171   uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
02172   if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
02173     return KateRendererConfig::global()->lineMarkerColor(type);
02174   } else {
02175     return QColor();
02176   }
02177 }
02178 
02179 QString KateDocument::markDescription( MarkInterface::MarkTypes type )
02180 {
02181   if( m_markDescriptions[type] )
02182     return *m_markDescriptions[type];
02183   return QString::null;
02184 }
02185 
02186 void KateDocument::setMarksUserChangable( uint markMask )
02187 {
02188   m_editableMarks = markMask;
02189 }
02190 
02191 uint KateDocument::editableMarks()
02192 {
02193   return m_editableMarks;
02194 }
02195 //END
02196 
02197 //BEGIN KTextEditor::PrintInterface stuff
02198 bool KateDocument::printDialog ()
02199 {
02200   return KatePrinter::print (this);
02201 }
02202 
02203 bool KateDocument::print ()
02204 {
02205   return KatePrinter::print (this);
02206 }
02207 //END
02208 
02209 //BEGIN KTextEditor::DocumentInfoInterface (### unfinished)
02210 QString KateDocument::mimeType()
02211 {
02212   KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
02213 
02214   // if the document has a URL, try KMimeType::findByURL
02215   if ( ! m_url.isEmpty() )
02216     result = KMimeType::findByURL( m_url );
02217 
02218   else if ( m_url.isEmpty() || ! m_url.isLocalFile() )
02219     result = mimeTypeForContent();
02220 
02221   return result->name();
02222 }
02223 
02224 // TODO implement this -- how to calculate?
02225 long KateDocument::fileSize()
02226 {
02227   return 0;
02228 }
02229 
02230 // TODO implement this
02231 QString KateDocument::niceFileSize()
02232 {
02233   return "UNKNOWN";
02234 }
02235 
02236 KMimeType::Ptr KateDocument::mimeTypeForContent()
02237 {
02238   QByteArray buf (1024);
02239   uint bufpos = 0;
02240 
02241   for (uint i=0; i < numLines(); i++)
02242   {
02243     QString line = textLine( i );
02244     uint len = line.length() + 1;
02245 
02246     if (bufpos + len > 1024)
02247       len = 1024 - bufpos;
02248 
02249     memcpy(&buf[bufpos], (line + "\n").latin1(), len);
02250 
02251     bufpos += len;
02252 
02253     if (bufpos >= 1024)
02254       break;
02255   }
02256   buf.resize( bufpos );
02257 
02258   int accuracy = 0;
02259   return KMimeType::findByContent( buf, &accuracy );
02260 }
02261 //END KTextEditor::DocumentInfoInterface
02262 
02263 
02264 //BEGIN KParts::ReadWrite stuff
02265 
02266 bool KateDocument::openURL( const KURL &url )
02267 {
02268 //   kdDebug(13020)<<"KateDocument::openURL( "<<url.prettyURL()<<")"<<endl;
02269   // no valid URL
02270   if ( !url.isValid() )
02271     return false;
02272 
02273   // could not close old one
02274   if ( !closeURL() )
02275     return false;
02276 
02277   // set my url
02278   m_url = url;
02279 
02280   if ( m_url.isLocalFile() )
02281   {
02282     // local mode, just like in kpart
02283 
02284     m_file = m_url.path();
02285 
02286     emit started( 0 );
02287 
02288     if (openFile())
02289     {
02290       emit completed();
02291       emit setWindowCaption( m_url.prettyURL() );
02292 
02293       return true;
02294     }
02295 
02296     return false;
02297   }
02298   else
02299   {
02300     // remote mode
02301 
02302     m_bTemp = true;
02303 
02304     m_tempFile = new KTempFile ();
02305     m_file = m_tempFile->name();
02306 
02307     m_job = KIO::get ( url, false, isProgressInfoEnabled() );
02308 
02309     // connect to slots
02310     connect( m_job, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
02311            SLOT( slotDataKate( KIO::Job*, const QByteArray& ) ) );
02312 
02313     connect( m_job, SIGNAL( result( KIO::Job* ) ),
02314            SLOT( slotFinishedKate( KIO::Job* ) ) );
02315 
02316     QWidget *w = widget ();
02317     if (!w && !m_views.isEmpty ())
02318       w = m_views.first();
02319 
02320     if (w)
02321       m_job->setWindow (w->topLevelWidget());
02322 
02323     emit started( m_job );
02324 
02325     return true;
02326   }
02327 }
02328 
02329 void KateDocument::slotDataKate ( KIO::Job *, const QByteArray &data )
02330 {
02331 //   kdDebug(13020) << "KateDocument::slotData" << endl;
02332 
02333   if (!m_tempFile || !m_tempFile->file())
02334     return;
02335 
02336   m_tempFile->file()->writeBlock (data);
02337 }
02338 
02339 void KateDocument::slotFinishedKate ( KIO::Job * job )
02340 {
02341 //   kdDebug(13020) << "KateDocument::slotJobFinished" << endl;
02342 
02343   if (!m_tempFile)
02344     return;
02345 
02346   delete m_tempFile;
02347   m_tempFile = 0;
02348   m_job = 0;
02349 
02350   if (job->error())
02351     emit canceled( job->errorString() );
02352   else
02353   {
02354     if ( openFile(job) )
02355       emit setWindowCaption( m_url.prettyURL() );
02356     emit completed();
02357   }
02358 }
02359 
02360 void KateDocument::abortLoadKate()
02361 {
02362   if ( m_job )
02363   {
02364     kdDebug(13020) << "Aborting job " << m_job << endl;
02365     m_job->kill();
02366     m_job = 0;
02367   }
02368 
02369   delete m_tempFile;
02370   m_tempFile = 0;
02371 }
02372 
02373 bool KateDocument::openFile()
02374 {
02375   return openFile (0);
02376 }
02377 
02378 bool KateDocument::openFile(KIO::Job * job)
02379 {
02380   m_loading = true;
02381   // add new m_file to dirwatch
02382   activateDirWatch ();
02383 
02384   //
02385   // use metadata
02386   //
02387   if (job)
02388   {
02389     QString metaDataCharset = job->queryMetaData("charset");
02390 
02391     // only overwrite config if nothing set
02392     if (!metaDataCharset.isEmpty () && (!m_config->isSetEncoding() || m_config->encoding().isEmpty()))
02393       setEncoding (metaDataCharset);
02394   }
02395 
02396   //
02397   // service type magic to get encoding right
02398   //
02399   QString serviceType = m_extension->urlArgs().serviceType.simplifyWhiteSpace();
02400   int pos = serviceType.find(';');
02401   if (pos != -1)
02402     setEncoding (serviceType.mid(pos+1));
02403 
02404   // if the encoding is set here - on the command line/from the dialog/from KIO
02405   // we prevent file type and document variables from changing it
02406   bool encodingSticky = m_encodingSticky;
02407   m_encodingSticky = m_config->isSetEncoding();
02408 
02409   // Try getting the filetype here, so that variables does not have to be reset.
02410   int fileTypeFound = KateFactory::self()->fileTypeManager()->fileType (this);
02411   if ( fileTypeFound > -1 )
02412     updateFileType( fileTypeFound );
02413 
02414   // do we have success ?
02415   bool success = m_buffer->openFile (m_file);
02416   //
02417   // yeah, success
02418   //
02419   m_loading = false; // done reading file.
02420   if (success)
02421   {
02422     /*if (highlight() && !m_url.isLocalFile()) {
02423       // The buffer's highlighting gets nuked by KateBuffer::clear()
02424       m_buffer->setHighlight(m_highlight);
02425   }*/
02426 
02427     // update our hl type if needed
02428     if (!hlSetByUser)
02429     {
02430       int hl (KateHlManager::self()->detectHighlighting (this));
02431 
02432       if (hl >= 0)
02433         m_buffer->setHighlight(hl);
02434     }
02435 
02436     // update file type if we haven't allready done so.
02437     if ( fileTypeFound < 0 )
02438       updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
02439 
02440     // read dir config (if possible and wanted)
02441     readDirConfig ();
02442 
02443     // read vars
02444     readVariables();
02445 
02446     // update the md5 digest
02447     createDigest( m_digest );
02448   }
02449 
02450   //
02451   // update views
02452   //
02453   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
02454   {
02455     view->updateView(true);
02456   }
02457 
02458   //
02459   // emit the signal we need for example for kate app
02460   //
02461   emit fileNameChanged ();
02462 
02463   //
02464   // set doc name, dummy value as arg, don't need it
02465   //
02466   setDocName  (QString::null);
02467 
02468   //
02469   // to houston, we are not modified
02470   //
02471   if (m_modOnHd)
02472   {
02473     m_modOnHd = false;
02474     m_modOnHdReason = 0;
02475     emit modifiedOnDisc (this, m_modOnHd, 0);
02476   }
02477 
02478   //
02479   // display errors
02480   //
02481   if (s_openErrorDialogsActivated)
02482   {
02483     if (!success && m_buffer->loadingBorked())
02484       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()));
02485     else if (!success)
02486       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()));
02487   }
02488 
02489   // warn -> opened binary file!!!!!!!
02490   if (m_buffer->binary())
02491   {
02492     // this file can't be saved again without killing it
02493     setReadWrite( false );
02494 
02495     KMessageBox::information (widget()
02496       , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
02497       , i18n ("Binary File Opened")
02498       , "Binary File Opened Warning");
02499   }
02500 
02501   m_encodingSticky = encodingSticky;
02502 
02503   //
02504   // return the success
02505   //
02506   return success;
02507 }
02508 
02509 bool KateDocument::save()
02510 {
02511   bool l ( url().isLocalFile() );
02512 
02513   if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
02514        || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
02515   {
02516     KURL u( url() );
02517     u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
02518 
02519     kdDebug () << "backup src file name: " << url() << endl;
02520     kdDebug () << "backup dst file name: " << u << endl;
02521 
02522     // get the right permissions, start with safe default
02523     mode_t  perms = 0600;
02524     KIO::UDSEntry fentry;
02525     if (KIO::NetAccess::stat (url(), fentry, kapp->mainWidget()))
02526     {
02527       kdDebug () << "stating succesfull: " << url() << endl;
02528       KFileItem item (fentry, url());
02529       perms = item.permissions();
02530     }
02531 
02532     // first del existing file if any, than copy over the file we have
02533     // failure if a: the existing file could not be deleted, b: the file could not be copied
02534     if ( (!KIO::NetAccess::exists( u, false, kapp->mainWidget() ) || KIO::NetAccess::del( u, kapp->mainWidget() ))
02535           && KIO::NetAccess::file_copy( url(), u, perms, true, false, kapp->mainWidget() ) )
02536     {
02537       kdDebug(13020)<<"backing up successfull ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
02538     }
02539     else
02540     {
02541       kdDebug(13020)<<"backing up failed ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
02542       // FIXME: notify user for real ;)
02543     }
02544   }
02545 
02546   return KParts::ReadWritePart::save();
02547 }
02548 
02549 bool KateDocument::saveFile()
02550 {
02551   //
02552   // we really want to save this file ?
02553   //
02554   if (m_buffer->loadingBorked() && (KMessageBox::warningContinueCancel(widget(),
02555       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))
02556     return false;
02557 
02558   //
02559   // warn -> try to save binary file!!!!!!!
02560   //
02561   if (m_buffer->binary() && (KMessageBox::warningContinueCancel (widget()
02562         , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
02563         , i18n ("Trying to Save Binary File")
02564         , i18n("Save Nevertheless"), "Binary File Save Warning") != KMessageBox::Continue))
02565     return false;
02566 
02567   if ( !url().isEmpty() )
02568   {
02569     if (s_fileChangedDialogsActivated && m_modOnHd)
02570     {
02571       QString str = reasonedMOHString() + "\n\n";
02572 
02573       if (!isModified())
02574       {
02575         if (KMessageBox::warningContinueCancel(0,
02576                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)
02577           return false;
02578       }
02579       else
02580       {
02581         if (KMessageBox::warningContinueCancel(0,
02582                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)
02583           return false;
02584       }
02585     }
02586   }
02587 
02588   //
02589   // can we encode it if we want to save it ?
02590   //
02591   if (!m_buffer->canEncode ()
02592        && (KMessageBox::warningContinueCancel(0,
02593            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))
02594   {
02595     return false;
02596   }
02597 
02598   // remove file from dirwatch
02599   deactivateDirWatch ();
02600 
02601   //
02602   // try to save
02603   //
02604   bool success = m_buffer->saveFile (m_file);
02605 
02606   // update the md5 digest
02607   createDigest( m_digest );
02608 
02609   // add m_file again to dirwatch
02610   activateDirWatch ();
02611 
02612   //
02613   // hurray, we had success, do stuff we need
02614   //
02615   if (success)
02616   {
02617     // update our hl type if needed
02618     if (!hlSetByUser)
02619     {
02620       int hl (KateHlManager::self()->detectHighlighting (this));
02621 
02622       if (hl >= 0)
02623         m_buffer->setHighlight(hl);
02624     }
02625 
02626     // read our vars
02627     readVariables();
02628   }
02629 
02630   //
02631   // we are not modified
02632   //
02633   if (success && m_modOnHd)
02634   {
02635     m_modOnHd = false;
02636     m_modOnHdReason = 0;
02637     emit modifiedOnDisc (this, m_modOnHd, 0);
02638   }
02639 
02640   //
02641   // display errors
02642   //
02643   if (!success)
02644     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()));
02645 
02646   //
02647   // return success
02648   //
02649   return success;
02650 }
02651 
02652 bool KateDocument::saveAs( const KURL &u )
02653 {
02654   QString oldDir = url().directory();
02655 
02656   if ( KParts::ReadWritePart::saveAs( u ) )
02657   {
02658     // null means base on filename
02659     setDocName( QString::null );
02660 
02661     if ( u.directory() != oldDir )
02662       readDirConfig();
02663 
02664     emit fileNameChanged();
02665     emit nameChanged((Kate::Document *) this);
02666 
02667     return true;
02668   }
02669 
02670   return false;
02671 }
02672 
02673 void KateDocument::readDirConfig ()
02674 {
02675   int depth = config()->searchDirConfigDepth ();
02676 
02677   if (m_url.isLocalFile() && (depth > -1))
02678   {
02679     QString currentDir = QFileInfo (m_file).dirPath();
02680 
02681     // only search as deep as specified or not at all ;)
02682     while (depth > -1)
02683     {
02684       kdDebug (13020) << "search for config file in path: " << currentDir << endl;
02685 
02686       // try to open config file in this dir
02687       QFile f (currentDir + "/.kateconfig");
02688 
02689       if (f.open (IO_ReadOnly))
02690       {
02691         QTextStream stream (&f);
02692 
02693         uint linesRead = 0;
02694         QString line = stream.readLine();
02695         while ((linesRead < 32) && !line.isNull())
02696         {
02697           readVariableLine( line );
02698 
02699           line = stream.readLine();
02700 
02701           linesRead++;
02702         }
02703 
02704         break;
02705       }
02706 
02707       QString newDir = QFileInfo (currentDir).dirPath();
02708 
02709       // bail out on looping (for example reached /)
02710       if (currentDir == newDir)
02711         break;
02712 
02713       currentDir = newDir;
02714       --depth;
02715     }
02716   }
02717 }
02718 
02719 void KateDocument::activateDirWatch ()
02720 {
02721   // same file as we are monitoring, return
02722   if (m_file == m_dirWatchFile)
02723     return;
02724 
02725   // remove the old watched file
02726   deactivateDirWatch ();
02727 
02728   // add new file if needed
02729   if (m_url.isLocalFile() && !m_file.isEmpty())
02730   {
02731     KateFactory::self()->dirWatch ()->addFile (m_file);
02732     m_dirWatchFile = m_file;
02733   }
02734 }
02735 
02736 void KateDocument::deactivateDirWatch ()
02737 {
02738   if (!m_dirWatchFile.isEmpty())
02739     KateFactory::self()->dirWatch ()->removeFile (m_dirWatchFile);
02740 
02741   m_dirWatchFile = QString::null;
02742 }
02743 
02744 bool KateDocument::closeURL()
02745 {
02746   abortLoadKate();
02747 
02748   //
02749   // file mod on hd
02750   //
02751   if ( !m_reloading && !url().isEmpty() )
02752   {
02753     if (s_fileChangedDialogsActivated && m_modOnHd)
02754     {
02755       if (!(KMessageBox::warningContinueCancel(
02756             widget(),
02757             reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
02758             i18n("Possible Data Loss"), i18n("Close Nevertheless"),
02759             QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
02760         return false;
02761     }
02762   }
02763 
02764   //
02765   // first call the normal kparts implementation
02766   //
02767   if (!KParts::ReadWritePart::closeURL ())
02768     return false;
02769 
02770   // remove file from dirwatch
02771   deactivateDirWatch ();
02772 
02773   //
02774   // empty url + filename
02775   //
02776   m_url = KURL ();
02777   m_file = QString::null;
02778 
02779   // we are not modified
02780   if (m_modOnHd)
02781   {
02782     m_modOnHd = false;
02783     m_modOnHdReason = 0;
02784     emit modifiedOnDisc (this, m_modOnHd, 0);
02785   }
02786 
02787   // clear the buffer
02788   m_buffer->clear();
02789 
02790   // remove all marks
02791   clearMarks ();
02792 
02793   // clear undo/redo history
02794   clearUndo();
02795   clearRedo();
02796 
02797   // no, we are no longer modified
02798   setModified(false);
02799 
02800   // we have no longer any hl
02801   m_buffer->setHighlight(0);
02802 
02803   // update all our views
02804   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
02805   {
02806     // Explicitly call the internal version because we don't want this to look like
02807     // an external request (and thus have the view not QWidget::scroll()ed.
02808     view->setCursorPositionInternal(0, 0, 1, false);
02809     view->clearSelection();
02810     view->updateView(true);
02811   }
02812 
02813   // uh, filename changed
02814   emit fileNameChanged ();
02815 
02816   // update doc name
02817   setDocName (QString::null);
02818 
02819   // success
02820   return true;
02821 }
02822 
02823 void KateDocument::setReadWrite( bool rw )
02824 {
02825   if (isReadWrite() != rw)
02826   {
02827     KParts::ReadWritePart::setReadWrite (rw);
02828 
02829     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02830     {
02831       view->slotUpdate();
02832       view->slotReadWriteChanged ();
02833     }
02834   }
02835 }
02836 
02837 void KateDocument::setModified(bool m) {
02838 
02839   if (isModified() != m) {
02840     KParts::ReadWritePart::setModified (m);
02841 
02842     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02843     {
02844       view->slotUpdate();
02845     }
02846 
02847     emit modifiedChanged ();
02848     emit modStateChanged ((Kate::Document *)this);
02849   }
02850   if ( m == false )
02851   {
02852     if ( ! undoItems.isEmpty() )
02853     {
02854       lastUndoGroupWhenSaved = undoItems.last();
02855     }
02856 
02857     if ( ! redoItems.isEmpty() )
02858     {
02859       lastRedoGroupWhenSaved = redoItems.last();
02860     }
02861 
02862     docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
02863     docWasSavedWhenRedoWasEmpty = redoItems.isEmpty();
02864   }
02865 }
02866 //END
02867 
02868 //BEGIN Kate specific stuff ;)
02869 
02870 void KateDocument::makeAttribs(bool needInvalidate)
02871 {
02872   for (uint z = 0; z < m_views.count(); z++)
02873     m_views.at(z)->renderer()->updateAttributes ();
02874 
02875   if (needInvalidate)
02876     m_buffer->invalidateHighlighting();
02877 
02878   tagAll ();
02879 }
02880 
02881 // the attributes of a hl have changed, update
02882 void KateDocument::internalHlChanged()
02883 {
02884   makeAttribs();
02885 }
02886 
02887 void KateDocument::addView(KTextEditor::View *view) {
02888   if (!view)
02889     return;
02890 
02891   m_views.append( (KateView *) view  );
02892   m_textEditViews.append( view );
02893 
02894   // apply the view & renderer vars from the file type
02895   const KateFileType *t = 0;
02896   if ((m_fileType > -1) && (t = KateFactory::self()->fileTypeManager()->fileType(m_fileType)))
02897     readVariableLine (t->varLine, true);
02898 
02899   // apply the view & renderer vars from the file
02900   readVariables (true);
02901 
02902   m_activeView = (KateView *) view;
02903 }
02904 
02905 void KateDocument::removeView(KTextEditor::View *view) {
02906   if (!view)
02907     return;
02908 
02909   if (m_activeView == view)
02910     m_activeView = 0L;
02911 
02912   m_views.removeRef( (KateView *) view );
02913   m_textEditViews.removeRef( view  );
02914 }
02915 
02916 void KateDocument::addSuperCursor(KateSuperCursor *cursor, bool privateC) {
02917   if (!cursor)
02918     return;
02919 
02920   m_superCursors.append( cursor );
02921 
02922   if (!privateC)
02923     myCursors.append( cursor );
02924 }
02925 
02926 void KateDocument::removeSuperCursor(KateSuperCursor *cursor, bool privateC) {
02927   if (!cursor)
02928     return;
02929 
02930   if (!privateC)
02931     myCursors.removeRef( cursor  );
02932 
02933   m_superCursors.removeRef( cursor  );
02934 }
02935 
02936 bool KateDocument::ownedView(KateView *view) {
02937   // do we own the given view?
02938   return (m_views.containsRef(view) > 0);
02939 }
02940 
02941 bool KateDocument::isLastView(int numViews) {
02942   return ((int) m_views.count() == numViews);
02943 }
02944 
02945 uint KateDocument::currentColumn( const KateTextCursor& cursor )
02946 {
02947   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
02948 
02949   if (textLine)
02950     return textLine->cursorX(cursor.col(), config()->tabWidth());
02951   else
02952     return 0;
02953 }
02954 
02955 bool KateDocument::typeChars ( KateView *view, const QString &chars )
02956 {
02957   KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorLine ());
02958 
02959   if (!textLine)
02960     return false;
02961 
02962   bool bracketInserted = false;
02963   QString buf;
02964   QChar c;
02965 
02966   for( uint z = 0; z < chars.length(); z++ )
02967   {
02968     QChar ch = c = chars[z];
02969     if (ch.isPrint() || ch == '\t')
02970     {
02971       buf.append (ch);
02972 
02973       if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets))
02974       {
02975         QChar end_ch;
02976         bool complete = true;
02977         QChar prevChar = textLine->getChar(view->cursorColumnReal()-1);
02978         QChar nextChar = textLine->getChar(view->cursorColumnReal());
02979         switch(ch) {
02980           case '(': end_ch = ')'; break;
02981           case '[': end_ch = ']'; break;
02982           case '{': end_ch = '}'; break;
02983           case '\'':end_ch = '\'';break;
02984           case '"': end_ch = '"'; break;
02985           default: complete = false;
02986         }
02987         if (complete)
02988         {
02989           if (view->hasSelection())
02990           { // there is a selection, enclose the selection
02991             buf.append (view->selection());
02992             buf.append (end_ch);
02993             bracketInserted = true;
02994           }
02995           else
02996           { // no selection, check whether we should better refuse to complete
02997             if ( ( (ch == '\'' || ch == '"') &&
02998                    (prevChar.isLetterOrNumber() || prevChar == ch) )
02999               || nextChar.isLetterOrNumber()
03000               || (nextChar == end_ch && prevChar != ch) )
03001             {
03002               kdDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
03003             }
03004             else
03005             {
03006               buf.append (end_ch);
03007               bracketInserted = true;
03008             }
03009           }
03010         }
03011       }
03012     }
03013   }
03014 
03015   if (buf.isEmpty())
03016     return false;
03017 
03018   editStart ();
03019 
03020   if (!view->config()->persistentSelection() && view->hasSelection() )
03021     view->removeSelectedText();
03022 
03023   int oldLine = view->cursorLine ();
03024   int oldCol = view->cursorColumnReal ();
03025 
03026 
03027   if (config()->configFlags()  & KateDocument::cfOvr)
03028     removeText (view->cursorLine(), view->cursorColumnReal(), view->cursorLine(), kMin( view->cursorColumnReal()+buf.length(), textLine->length() ) );
03029 
03030   insertText (view->cursorLine(), view->cursorColumnReal(), buf);
03031   m_indenter->processChar(c);
03032 
03033   editEnd ();
03034 
03035   if (bracketInserted)
03036     view->setCursorPositionInternal (view->cursorLine(), view->cursorColumnReal()-1);
03037 
03038   emit charactersInteractivelyInserted (oldLine, oldCol, chars);
03039 
03040   return true;
03041 }
03042 
03043 void KateDocument::newLine( KateTextCursor& c, KateViewInternal *v )
03044 {
03045   editStart();
03046 
03047   if( !v->view()->config()->persistentSelection() && v->view()->hasSelection() )
03048     v->view()->removeSelectedText();
03049 
03050   // temporary hack to get the cursor pos right !!!!!!!!!
03051   c = v->getCursor ();
03052 
03053   if (c.line() > (int)lastLine())
03054    c.setLine(lastLine());
03055 
03056   if ( c.line() < 0 )
03057     c.setLine( 0 );
03058 
03059   uint ln = c.line();
03060 
03061   KateTextLine::Ptr textLine = kateTextLine(c.line());
03062 
03063   if (c.col() > (int)textLine->length())
03064     c.setCol(textLine->length());
03065 
03066   if (m_indenter->canProcessNewLine ())
03067   {
03068     int pos = textLine->firstChar();
03069 
03070     // length should do the job better
03071     if (pos < 0)
03072       pos = textLine->length();
03073 
03074     if (c.col() < pos)
03075       c.setCol(pos); // place cursor on first char if before
03076 
03077     editWrapLine (c.line(), c.col());
03078 
03079     KateDocCursor cursor (c.line() + 1, pos, this);
03080     m_indenter->processNewline(cursor, true);
03081 
03082     c.setPos(cursor);
03083   }
03084   else
03085   {
03086     editWrapLine (c.line(), c.col());
03087     c.setPos(c.line() + 1, 0);
03088   }
03089 
03090   removeTrailingSpace( ln );
03091 
03092   editEnd();
03093 }
03094 
03095 void KateDocument::transpose( const KateTextCursor& cursor)
03096 {
03097   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03098 
03099   if (!textLine || (textLine->length() < 2))
03100     return;
03101 
03102   uint col = cursor.col();
03103 
03104   if (col > 0)
03105     col--;
03106 
03107   if ((textLine->length() - col) < 2)
03108     return;
03109 
03110   uint line = cursor.line();
03111   QString s;
03112 
03113   //clever swap code if first character on the line swap right&left
03114   //otherwise left & right
03115   s.append (textLine->getChar(col+1));
03116   s.append (textLine->getChar(col));
03117   //do the swap
03118 
03119   // do it right, never ever manipulate a textline
03120   editStart ();
03121   editRemoveText (line, col, 2);
03122   editInsertText (line, col, s);
03123   editEnd ();
03124 }
03125 
03126 void KateDocument::backspace( KateView *view, const KateTextCursor& c )
03127 {
03128   if ( !view->config()->persistentSelection() && view->hasSelection() ) {
03129     view->removeSelectedText();
03130     return;
03131   }
03132 
03133   uint col = kMax( c.col(), 0 );
03134   uint line = kMax( c.line(), 0 );
03135 
03136   if ((col == 0) && (line == 0))
03137     return;
03138 
03139   int complement = 0;
03140   if (col > 0)
03141   {
03142     if (config()->configFlags() & KateDocument::cfAutoBrackets)
03143     {
03144       // if inside empty (), {}, [], '', "" delete both
03145       KateTextLine::Ptr tl = m_buffer->plainLine(line);
03146       if(!tl) return;
03147       QChar prevChar = tl->getChar(col-1);
03148       QChar nextChar = tl->getChar(col);
03149 
03150       if ( (prevChar == '"' && nextChar == '"') ||
03151            (prevChar == '\'' && nextChar == '\'') ||
03152            (prevChar == '(' && nextChar == ')') ||
03153            (prevChar == '[' && nextChar == ']') ||
03154            (prevChar == '{' && nextChar == '}') )
03155       {
03156         complement = 1;
03157       }
03158     }
03159     if (!(config()->configFlags() & KateDocument::cfBackspaceIndents))
03160     {
03161       // ordinary backspace
03162       //c.cursor.col--;
03163       removeText(line, col-1, line, col+complement);
03164     }
03165     else
03166     {
03167       // backspace indents: erase to next indent position
03168       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03169 
03170       // don't forget this check!!!! really!!!!
03171       if (!textLine)
03172         return;
03173 
03174       int colX = textLine->cursorX(col, config()->tabWidth());
03175       int pos = textLine->firstChar();
03176       if (pos > 0)
03177         pos = textLine->cursorX(pos, config()->tabWidth());
03178 
03179       if (pos < 0 || pos >= (int)colX)
03180       {
03181         // only spaces on left side of cursor
03182         indent( view, line, -1);
03183       }
03184       else
03185         removeText(line, col-1, line, col+complement);
03186     }
03187   }
03188   else
03189   {
03190     // col == 0: wrap to previous line
03191     if (line >= 1)
03192     {
03193       KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
03194 
03195       // don't forget this check!!!! really!!!!
03196       if (!textLine)
03197         return;
03198 
03199       if (config()->wordWrap() && textLine->endingWith(QString::fromLatin1(" ")))
03200       {
03201         // gg: in hard wordwrap mode, backspace must also eat the trailing space
03202         removeText (line-1, textLine->length()-1, line, 0);
03203       }
03204       else
03205         removeText (line-1, textLine->length(), line, 0);
03206     }
03207   }
03208 
03209   emit backspacePressed();
03210 }
03211 
03212 void KateDocument::del( KateView *view, const KateTextCursor& c )
03213 {
03214   if ( !view->config()->persistentSelection() && view->hasSelection() ) {
03215     view->removeSelectedText();
03216     return;
03217   }
03218 
03219   if( c.col() < (int) m_buffer->plainLine(c.line())->length())
03220   {
03221     removeText(c.line(), c.col(), c.line(), c.col()+1);
03222   }
03223   else if ( (uint)c.line() < lastLine() )
03224   {
03225     removeText(c.line(), c.col(), c.line()+1, 0);
03226   }
03227 }
03228 
03229 void KateDocument::paste ( KateView* view )
03230 {
03231   QString s = QApplication::clipboard()->text();
03232 
03233   if (s.isEmpty())
03234     return;
03235 
03236   uint lines = s.contains (QChar ('\n'));
03237 
03238   m_undoDontMerge = true;
03239 
03240   editStart ();
03241 
03242   if (!view->config()->persistentSelection() && view->hasSelection() )
03243     view->removeSelectedText();
03244 
03245   uint line = view->cursorLine ();
03246   uint column = view->cursorColumnReal ();
03247 
03248   insertText ( line, column, s, view->blockSelectionMode() );
03249 
03250   editEnd();
03251 
03252   // move cursor right for block select, as the user is moved right internal
03253   // even in that case, but user expects other behavior in block selection
03254   // mode !
03255   if (view->blockSelectionMode())
03256     view->setCursorPositionInternal (line+lines, column);
03257 
03258   if (m_indenter->canProcessLine()
03259       && config()->configFlags() & KateDocumentConfig::cfIndentPastedText)
03260   {
03261     editStart();
03262 
03263     KateDocCursor begin(line, 0, this);
03264     KateDocCursor end(line + lines, 0, this);
03265 
03266     m_indenter->processSection (begin, end);
03267 
03268     editEnd();
03269   }
03270 
03271   if (!view->blockSelectionMode()) emit charactersSemiInteractivelyInserted (line, column, s);
03272   m_undoDontMerge = true;
03273 }
03274 
03275 void KateDocument::insertIndentChars ( KateView *view )
03276 {
03277   editStart ();
03278 
03279   QString s;
03280   if (config()->configFlags() & KateDocument::cfSpaceIndent)
03281   {
03282     int width = config()->indentationWidth();
03283     s.fill (' ', width - (view->cursorColumnReal() % width));
03284   }
03285   else
03286     s.append ('\t');
03287 
03288   insertText (view->cursorLine(), view->cursorColumnReal(), s);
03289 
03290   editEnd ();
03291 }
03292 
03293 void KateDocument::indent ( KateView *v, uint line, int change)
03294 {
03295   editStart ();
03296 
03297   if (!hasSelection())
03298   {
03299     // single line
03300     optimizeLeadingSpace(line, config()->configFlags(), change);
03301   }
03302   else
03303   {
03304     int sl = v->selStartLine();
03305     int el = v->selEndLine();
03306     int ec = v->selEndCol();
03307 
03308     if ((ec == 0) && ((el-1) >= 0))
03309     {
03310       el--; /* */
03311     }
03312 
03313     if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) {
03314       // unindent so that the existing indent profile doesn't get screwed
03315       // if any line we may unindent is already full left, don't do anything
03316       int adjustedChange = -change;
03317 
03318       for (line = sl; (int) line <= el && adjustedChange > 0; line++) {
03319         KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03320         int firstChar = textLine->firstChar();
03321         if (firstChar >= 0 && (v->lineSelected(line) || v->lineHasSelected(line))) {
03322           int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth();
03323           if (maxUnindent < adjustedChange)
03324             adjustedChange = maxUnindent;
03325         }
03326       }
03327 
03328       change = -adjustedChange;
03329     }
03330 
03331     const bool rts = config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn;
03332     for (line = sl; (int) line <= el; line++) {
03333       if ((v->lineSelected(line) || v->lineHasSelected(line))
03334           && (!rts || lineLength(line) > 0)) {
03335         optimizeLeadingSpace(line, config()->configFlags(), change);
03336       }
03337     }
03338   }
03339 
03340   editEnd ();
03341 }
03342 
03343 void KateDocument::align(KateView *view, uint line)
03344 {
03345   if (m_indenter->canProcessLine())
03346   {
03347     editStart ();
03348 
03349     if (!view->hasSelection())
03350     {
03351       KateDocCursor curLine(line, 0, this);
03352       m_indenter->processLine (curLine);
03353       editEnd ();
03354       activeView()->setCursorPosition (line, curLine.col());
03355     }
03356     else
03357     {
03358       m_indenter->processSection (view->selStart(), view->selEnd());
03359       editEnd ();
03360     }
03361   }
03362 }
03363 
03364 /*
03365   Optimize the leading whitespace for a single line.
03366   If change is > 0, it adds indentation units (indentationChars)
03367   if change is == 0, it only optimizes
03368   If change is < 0, it removes indentation units
03369   This will be used to indent, unindent, and optimal-fill a line.
03370   If excess space is removed depends on the flag cfKeepExtraSpaces
03371   which has to be set by the user
03372 */
03373 void KateDocument::optimizeLeadingSpace(uint line, int flags, int change)
03374 {
03375   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03376 
03377   int first_char = textline->firstChar();
03378 
03379   int w = 0;
03380   if (flags & KateDocument::cfSpaceIndent)
03381     w = config()->indentationWidth();
03382   else
03383     w = config()->tabWidth();
03384 
03385   if (first_char < 0)
03386     first_char = textline->length();
03387 
03388   int space =  textline->cursorX(first_char, config()->tabWidth()) + change * w;
03389   if (space < 0)
03390     space = 0;
03391 
03392   if (!(flags & KateDocument::cfKeepExtraSpaces))
03393   {
03394     uint extra = space % w;
03395 
03396     space -= extra;
03397     if (extra && change < 0) {
03398       // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide)
03399       space += w;
03400     }
03401   }
03402 
03403   //kdDebug(13020)  << "replace With Op: " << line << " " << first_char << " " << space << endl;
03404   replaceWithOptimizedSpace(line, first_char, space, flags);
03405 }
03406 
03407 void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags)
03408 {
03409   uint length;
03410   QString new_space;
03411 
03412   if (flags & KateDocument::cfSpaceIndent && ! (flags & KateDocumentConfig::cfMixedIndent) ) {
03413     length = space;
03414     new_space.fill(' ', length);
03415   }
03416   else {
03417     length = space / config()->tabWidth();
03418     new_space.fill('\t', length);
03419 
03420     QString extra_space;
03421     extra_space.fill(' ', space % config()->tabWidth());
03422     length += space % config()->tabWidth();
03423     new_space += extra_space;
03424   }
03425 
03426   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03427   uint change_from;
03428   for (change_from = 0; change_from < upto_column && change_from < length; change_from++) {
03429     if (textline->getChar(change_from) != new_space[change_from])
03430       break;
03431   }
03432 
03433   editStart();
03434 
03435   if (change_from < upto_column)
03436     removeText(line, change_from, line, upto_column);
03437 
03438   if (change_from < length)
03439     insertText(line, change_from, new_space.right(length - change_from));
03440 
03441   editEnd();
03442 }
03443 
03444 /*
03445   Remove a given string at the begining
03446   of the current line.
03447 */
03448 bool KateDocument::removeStringFromBegining(int line, QString &str)
03449 {
03450   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03451 
03452   int index = 0;
03453   bool there = false;
03454 
03455   if (textline->startingWith(str))
03456     there = true;
03457   else
03458   {
03459     index = textline->firstChar ();
03460 
03461     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03462       there = true;
03463   }
03464 
03465   if (there)
03466   {
03467     // Remove some chars
03468     removeText (line, index, line, index+str.length());
03469   }
03470 
03471   return there;
03472 }
03473 
03474 /*
03475   Remove a given string at the end
03476   of the current line.
03477 */
03478 bool KateDocument::removeStringFromEnd(int line, QString &str)
03479 {
03480   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03481 
03482   int index = 0;
03483   bool there = false;
03484 
03485   if(textline->endingWith(str))
03486   {
03487     index = textline->length() - str.length();
03488     there = true;
03489   }
03490   else
03491   {
03492     index = textline->lastChar ()-str.length()+1;
03493 
03494     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03495       there = true;
03496   }
03497 
03498   if (there)
03499   {
03500     // Remove some chars
03501     removeText (line, index, line, index+str.length());
03502   }
03503 
03504   return there;
03505 }
03506 
03507 /*
03508   Add to the current line a comment line mark at
03509   the begining.
03510 */
03511 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
03512 {
03513   if (highlight()->getCommentSingleLinePosition(attrib)==KateHighlighting::CSLPosColumn0)
03514   {
03515     QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03516     insertText (line, 0, commentLineMark);
03517   }
03518   else
03519   {
03520     QString commentLineMark=highlight()->getCommentSingleLineStart(attrib);
03521     KateTextLine::Ptr l = m_buffer->line(line);
03522     int pos=l->firstChar();
03523     if (pos >=0)
03524       insertText(line,pos,commentLineMark);
03525   }
03526 }
03527 
03528 /*
03529   Remove from the current line a comment line mark at
03530   the begining if there is one.
03531 */
03532 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
03533 {
03534   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
03535   QString longCommentMark = shortCommentMark + " ";
03536 
03537   editStart();
03538 
03539   // Try to remove the long comment mark first
03540   bool removed = (removeStringFromBegining(line, longCommentMark)
03541                   || removeStringFromBegining(line, shortCommentMark));
03542 
03543   editEnd();
03544 
03545   return removed;
03546 }
03547 
03548 /*
03549   Add to the current line a start comment mark at the
03550  begining and a stop comment mark at the end.
03551 */
03552 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
03553 {
03554   QString startCommentMark = highlight()->getCommentStart( attrib ) + " ";
03555   QString stopCommentMark = " " + highlight()->getCommentEnd( attrib );
03556 
03557   editStart();
03558 
03559   // Add the start comment mark
03560   insertText (line, 0, startCommentMark);
03561 
03562   // Go to the end of the line
03563   int col = m_buffer->plainLine(line)->length();
03564 
03565   // Add the stop comment mark
03566   insertText (line, col, stopCommentMark);
03567 
03568   editEnd();
03569 }
03570 
03571 /*
03572   Remove from the current line a start comment mark at
03573   the begining and a stop comment mark at the end.
03574 */
03575 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
03576 {
03577   QString shortStartCommentMark = highlight()->getCommentStart( attrib );
03578   QString longStartCommentMark = shortStartCommentMark + " ";
03579   QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
03580   QString longStopCommentMark = " " + shortStopCommentMark;
03581 
03582   editStart();
03583 
03584 #ifdef __GNUC__
03585 #warning "that's a bad idea, can lead to stray endings, FIXME"
03586 #endif
03587   // Try to remove the long start comment mark first
03588   bool removedStart = (removeStringFromBegining(line, longStartCommentMark)
03589                        || removeStringFromBegining(line, shortStartCommentMark));
03590 
03591   bool removedStop = false;
03592   if (removedStart)
03593   {
03594     // Try to remove the long stop comment mark first
03595     removedStop = (removeStringFromEnd(line, longStopCommentMark)
03596                       || removeStringFromEnd(line, shortStopCommentMark));
03597   }
03598 
03599   editEnd();
03600 
03601   return (removedStart || removedStop);
03602 }
03603 
03604 /*
03605   Add to the current selection a start comment
03606  mark at the begining and a stop comment mark
03607  at the end.
03608 */
03609 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
03610 {
03611   QString startComment = highlight()->getCommentStart( attrib );
03612   QString endComment = highlight()->getCommentEnd( attrib );
03613 
03614   int sl = view->selStartLine();
03615   int el = view->selEndLine();
03616   int sc = view->selStartCol();
03617   int ec = view->selEndCol();
03618 
03619   if ((ec == 0) && ((el-1) >= 0))
03620   {
03621     el--;
03622     ec = m_buffer->plainLine (el)->length();
03623   }
03624 
03625   editStart();
03626 
03627   insertText (el, ec, endComment);
03628   insertText (sl, sc, startComment);
03629 
03630   editEnd ();
03631 
03632   // Set the new selection
03633   ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 );
03634   view->setSelection(sl, sc, el, ec);
03635 }
03636 
03637 /*
03638   Add to the current selection a comment line
03639  mark at the begining of each line.
03640 */
03641 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
03642 {
03643   QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03644 
03645   int sl = view->selStartLine();
03646   int el = view->selEndLine();
03647 
03648   if ((view->selEndCol() == 0) && ((el-1) >= 0))
03649   {
03650     el--;
03651   }
03652 
03653   editStart();
03654 
03655   // For each line of the selection
03656   for (int z = el; z >= sl; z--) {
03657     //insertText (z, 0, commentLineMark);
03658     addStartLineCommentToSingleLine(z, attrib );
03659   }
03660 
03661   editEnd ();
03662 
03663   // Set the new selection
03664 
03665   KateDocCursor end (view->selEnd());
03666   end.setCol(view->selEndCol() + ((el == view->selEndLine()) ? commentLineMark.length() : 0) );
03667 
03668   view->setSelection(view->selStartLine(), 0, end.line(), end.col());
03669 }
03670 
03671 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
03672 {
03673   for(; line < (int)m_buffer->count(); line++) {
03674     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03675 
03676     if (!textLine)
03677       break;
03678 
03679     col = textLine->nextNonSpaceChar(col);
03680     if(col != -1)
03681       return true; // Next non-space char found
03682     col = 0;
03683   }
03684   // No non-space char found
03685   line = -1;
03686   col = -1;
03687   return false;
03688 }
03689 
03690 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
03691 {
03692   while(true)
03693   {
03694     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03695 
03696     if (!textLine)
03697       break;
03698 
03699     col = textLine->previousNonSpaceChar(col);
03700     if(col != -1) return true;
03701     if(line == 0) return false;
03702     --line;
03703     col = textLine->length();
03704 }
03705   // No non-space char found
03706   line = -1;
03707   col = -1;
03708   return false;
03709 }
03710 
03711 /*
03712   Remove from the selection a start comment mark at
03713   the begining and a stop comment mark at the end.
03714 */
03715 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
03716 {
03717   QString startComment = highlight()->getCommentStart( attrib );
03718   QString endComment = highlight()->getCommentEnd( attrib );
03719 
03720   int sl = kMax<int> (0, view->selStartLine());
03721   int el = kMin<int>  (view->selEndLine(), lastLine());
03722   int sc = view->selStartCol();
03723   int ec = view->selEndCol();
03724 
03725   // The selection ends on the char before selectEnd
03726   if (ec != 0) {
03727     ec--;
03728   } else {
03729     if (el > 0) {
03730       el--;
03731       ec = m_buffer->plainLine(el)->length() - 1;
03732     }
03733   }
03734 
03735   int startCommentLen = startComment.length();
03736   int endCommentLen = endComment.length();
03737 
03738   // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/
03739 
03740   bool remove = nextNonSpaceCharPos(sl, sc)
03741       && m_buffer->plainLine(sl)->stringAtPos(sc, startComment)
03742       && previousNonSpaceCharPos(el, ec)
03743       && ( (ec - endCommentLen + 1) >= 0 )
03744       && m_buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment);
03745 
03746   if (remove) {
03747     editStart();
03748 
03749     removeText (el, ec - endCommentLen + 1, el, ec + 1);
03750     removeText (sl, sc, sl, sc + startCommentLen);
03751 
03752     editEnd ();
03753     // set new selection not necessary, as the selection cursors are KateSuperCursors
03754   }
03755 
03756   return remove;
03757 }
03758 
03759 bool KateDocument::removeStartStopCommentFromRegion(const KateTextCursor &start,const KateTextCursor &end,int attrib)
03760 {
03761   QString startComment = highlight()->getCommentStart( attrib );
03762   QString endComment = highlight()->getCommentEnd( attrib );
03763   int startCommentLen = startComment.length();
03764   int endCommentLen = endComment.length();
03765 
03766     bool remove = m_buffer->plainLine(start.line())->stringAtPos(start.col(), startComment)
03767       && ( (end.col() - endCommentLen ) >= 0 )
03768       && m_buffer->plainLine(end.line())->stringAtPos(end.col() - endCommentLen , endComment);
03769       if (remove)  {
03770         editStart();
03771           removeText(end.line(),end.col()-endCommentLen,end.line(),end.col());
03772           removeText(start.line(),start.col(),start.line(),start.col()+startCommentLen);
03773         editEnd();
03774       }
03775       return remove;
03776 }
03777 
03778 /*
03779   Remove from the begining of each line of the
03780   selection a start comment line mark.
03781 */
03782 bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib )
03783 {
03784   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
03785   QString longCommentMark = shortCommentMark + " ";
03786 
03787   int sl = view->selStartLine();
03788   int el = view->selEndLine();
03789 
03790   if ((view->selEndCol() == 0) && ((el-1) >= 0))
03791   {
03792     el--;
03793   }
03794 
03795   // Find out how many char will be removed from the last line
03796   int removeLength = 0;
03797   if (m_buffer->plainLine(el)->startingWith(longCommentMark))
03798     removeLength = longCommentMark.length();
03799   else if (m_buffer->plainLine(el)->startingWith(shortCommentMark))
03800     removeLength = shortCommentMark.length();
03801 
03802   bool removed = false;
03803 
03804   editStart();
03805 
03806   // For each line of the selection
03807   for (int z = el; z >= sl; z--)
03808   {
03809     // Try to remove the long comment mark first
03810     removed = (removeStringFromBegining(z, longCommentMark)
03811                  || removeStringFromBegining(z, shortCommentMark)
03812                  || removed);
03813   }
03814 
03815   editEnd();
03816   // updating selection already done by the KateSuperCursors
03817   return removed;
03818 }
03819 
03820 /*
03821   Comment or uncomment the selection or the current
03822   line if there is no selection.
03823 */
03824 void KateDocument::comment( KateView *v, uint line,uint column, int change)
03825 {
03826   // We need to check that we can sanely comment the selectino or region.
03827   // It is if the attribute of the first and last character of the range to
03828   // comment belongs to the same language definition.
03829   // for lines with no text, we need the attribute for the lines context.
03830   bool hassel = v->hasSelection();
03831   int startAttrib, endAttrib;
03832   if ( hassel )
03833   {
03834     KateTextLine::Ptr ln = kateTextLine( v->selStartLine() );
03835     int l = v->selStartLine(), c = v->selStartCol();
03836     startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03837 
03838     ln = kateTextLine( v->selEndLine() );
03839     l = v->selEndLine(), c = v->selEndCol();
03840     endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03841   }
03842   else
03843   {
03844     KateTextLine::Ptr ln = kateTextLine( line );
03845     if ( ln->length() )
03846     {
03847       startAttrib = ln->attribute( ln->firstChar() );
03848       endAttrib = ln->attribute( ln->lastChar() );
03849     }
03850     else
03851     {
03852       int l = line, c = 0;
03853       if ( nextNonSpaceCharPos( l, c )  || previousNonSpaceCharPos( l, c ) )
03854         startAttrib = endAttrib = kateTextLine( l )->attribute( c );
03855       else
03856         startAttrib = endAttrib = 0;
03857     }
03858   }
03859 
03860   if ( ! highlight()->canComment( startAttrib, endAttrib ) )
03861   {
03862     kdDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!"<<endl;
03863     return;
03864   }
03865 
03866   bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
03867   bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
03868       && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
03869 
03870   bool removed = false;
03871 
03872   if (change > 0) // comment
03873   {
03874     if ( !hassel )
03875     {
03876       if ( hasStartLineCommentMark )
03877         addStartLineCommentToSingleLine( line, startAttrib );
03878       else if ( hasStartStopCommentMark )
03879         addStartStopCommentToSingleLine( line, startAttrib );
03880     }
03881     else
03882     {
03883       // anders: prefer single line comment to avoid nesting probs
03884       // If the selection starts after first char in the first line
03885       // or ends before the last char of the last line, we may use
03886       // multiline comment markers.
03887       // TODO We should try to detect nesting.
03888       //    - if selection ends at col 0, most likely she wanted that
03889       // line ignored
03890       if ( hasStartStopCommentMark &&
03891            ( !hasStartLineCommentMark || (
03892            ( v->selStartCol() > m_buffer->plainLine( v->selStartLine() )->firstChar() ) ||
03893            ( v->selEndCol() < ((int)m_buffer->plainLine( v->selEndLine() )->length()) )
03894          ) ) )
03895         addStartStopCommentToSelection( v, startAttrib );
03896       else if ( hasStartLineCommentMark )
03897         addStartLineCommentToSelection( v, startAttrib );
03898     }
03899   }
03900   else // uncomment
03901   {
03902     if ( !hassel )
03903     {
03904       removed = ( hasStartLineCommentMark
03905                   && removeStartLineCommentFromSingleLine( line, startAttrib ) )
03906         || ( hasStartStopCommentMark
03907              && removeStartStopCommentFromSingleLine( line, startAttrib ) );
03908       if ((!removed) && foldingTree()) {
03909         kdDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)"<<endl;
03910         int commentRegion=(highlight()->commentRegion(startAttrib));
03911         if (commentRegion){
03912            KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
03913            if (n) {
03914             KateTextCursor start,end;
03915              if ((n->nodeType()==commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
03916                 kdDebug(13020)<<"Enclosing region found:"<<start.col()<<"/"<<start.line()<<"-"<<end.col()<<"/"<<end.line()<<endl;
03917                 removeStartStopCommentFromRegion(start,end,startAttrib);
03918              } else {
03919                   kdDebug(13020)<<"Enclosing region found, but not valid"<<endl;
03920                   kdDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion<<endl;
03921              }
03922             //perhaps nested regions should be hadled here too...
03923           } else kdDebug(13020)<<"No enclosing region found"<<endl;
03924         } else kdDebug(13020)<<"No comment region specified for current hl"<<endl;
03925       }
03926     }
03927     else
03928     {
03929       // anders: this seems like it will work with above changes :)
03930       removed = ( hasStartLineCommentMark
03931           && removeStartLineCommentFromSelection( v, startAttrib ) )
03932         || ( hasStartStopCommentMark
03933           && removeStartStopCommentFromSelection( v, startAttrib ) );
03934     }
03935   }
03936 }
03937 
03938 void KateDocument::transform( KateView *v, const KateTextCursor &c,
03939                             KateDocument::TextTransform t )
03940 {
03941   editStart();
03942   uint cl( c.line() ), cc( c.col() );
03943   bool selectionRestored = false;
03944 
03945   if ( hasSelection() )
03946   {
03947     // cache the selection and cursor, so we can be sure to restore.
03948     KateTextCursor selstart = v->selStart();
03949     KateTextCursor selend = v->selEnd();
03950 
03951     int ln = v->selStartLine();
03952     while ( ln <= selend.line() )
03953     {
03954       uint start, end;
03955       start = (ln == selstart.line() || v->blockSelectionMode()) ?
03956           selstart.col() : 0;
03957       end = (ln == selend.line() || v->blockSelectionMode()) ?
03958           selend.col() : lineLength( ln );
03959       if ( start > end )
03960       {
03961         uint t = start;
03962         start = end;
03963         end = t;
03964       }
03965       QString s = text( ln, start, ln, end );
03966       QString o = s;
03967 
03968       if ( t == Uppercase )
03969         s = s.upper();
03970       else if ( t == Lowercase )
03971         s = s.lower();
03972       else // Capitalize
03973       {
03974         KateTextLine::Ptr l = m_buffer->plainLine( ln );
03975         uint p ( 0 );
03976         while( p < s.length() )
03977         {
03978           // If bol or the character before is not in a word, up this one:
03979           // 1. if both start and p is 0, upper char.
03980           // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
03981           // 3. if p-1 is not in a word, upper.
03982           if ( ( ! start && ! p ) ||
03983                    ( ( ln == selstart.line() || v->blockSelectionMode() ) &&
03984                    ! p && ! highlight()->isInWord( l->getChar( start - 1 )) ) ||
03985                    ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
03986              )
03987             s[p] = s.at(p).upper();
03988           p++;
03989         }
03990       }
03991 
03992       if ( o != s )
03993       {
03994       removeText( ln, start, ln, end );
03995       insertText( ln, start, s );
03996       }
03997 
03998       ln++;
03999     }
04000 
04001     // restore selection
04002     v->setSelection( selstart, selend );
04003     selectionRestored = true;
04004 
04005   } else {  // no selection
04006     QString o = text( cl, cc, cl, cc + 1 );
04007     QString s;
04008     int n ( cc );
04009     switch ( t ) {
04010       case Uppercase:
04011       s = o.upper();
04012       break;
04013       case Lowercase:
04014       s = o.lower();
04015       break;
04016       case Capitalize:
04017       {
04018         KateTextLine::Ptr l = m_buffer->plainLine( cl );
04019         while ( n > 0 && highlight()->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) )
04020           n--;
04021         o = text( cl, n, cl, n + 1 );
04022         s = o.upper();
04023       }
04024       break;
04025       default:
04026       break;
04027     }
04028 
04029     if ( s != o )
04030     {
04031     removeText( cl, n, cl, n+1 );
04032     insertText( cl, n, s );
04033   }
04034   }
04035   editEnd();
04036 
04037   if ( ! selectionRestored )
04038     v->setCursorPosition( cl, cc );
04039 }
04040 
04041 void KateDocument::joinLines( uint first, uint last )
04042 {
04043 //   if ( first == last ) last += 1;
04044   editStart();
04045   int line( first );
04046   while ( first < last )
04047   {
04048     // Normalize the whitespace in the joined lines by making sure there's
04049     // always exactly one space between the joined lines
04050     // This cannot be done in editUnwrapLine, because we do NOT want this
04051     // behaviour when deleting from the start of a line, just when explicitly
04052     // calling the join command
04053     KateTextLine::Ptr l = m_buffer->line( line );
04054     KateTextLine::Ptr tl = m_buffer->line( line + 1 );
04055 
04056     if ( !l || !tl )
04057     {
04058       editEnd();
04059       return;
04060     }
04061 
04062     int pos = tl->firstChar();
04063     if ( pos >= 0 )
04064     {
04065       if (pos != 0)
04066         editRemoveText( line + 1, 0, pos );
04067       if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) )
04068         editInsertText( line + 1, 0, " " );
04069     }
04070     else
04071     {
04072       // Just remove the whitespace and let Kate handle the rest
04073       editRemoveText( line + 1, 0, tl->length() );
04074     }
04075 
04076     editUnWrapLine( line );
04077     first++;
04078   }
04079   editEnd();
04080 }
04081 
04082 QString KateDocument::getWord( const KateTextCursor& cursor ) {
04083   int start, end, len;
04084 
04085   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04086   len = textLine->length();
04087   start = end = cursor.col();
04088   if (start > len)        // Probably because of non-wrapping cursor mode.
04089     return QString("");
04090 
04091   while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
04092   while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(end))) end++;
04093   len = end - start;
04094   return QString(&textLine->text()[start], len);
04095 }
04096 
04097 void KateDocument::tagLines(int start, int end)
04098 {
04099   for (uint z = 0; z < m_views.count(); z++)
04100     m_views.at(z)->tagLines (start, end, true);
04101 }
04102 
04103 void KateDocument::tagLines(KateTextCursor start, KateTextCursor end)
04104 {
04105   // May need to switch start/end cols if in block selection mode
04106   if (blockSelectionMode() && start.col() > end.col()) {
04107     int sc = start.col();
04108     start.setCol(end.col());
04109     end.setCol(sc);
04110   }
04111 
04112   for (uint z = 0; z < m_views.count(); z++)
04113     m_views.at(z)->tagLines(start, end, true);
04114 }
04115 
04116 void KateDocument::repaintViews(bool paintOnlyDirty)
04117 {
04118   for (uint z = 0; z < m_views.count(); z++)
04119     m_views.at(z)->repaintText(paintOnlyDirty);
04120 }
04121 
04122 void KateDocument::tagAll()
04123 {
04124   for (uint z = 0; z < m_views.count(); z++)
04125   {
04126     m_views.at(z)->tagAll();
04127     m_views.at(z)->updateView (true);
04128   }
04129 }
04130 
04131 uint KateDocument::configFlags ()
04132 {
04133   return config()->configFlags();
04134 }
04135 
04136 void KateDocument::setConfigFlags (uint flags)
04137 {
04138   config()->setConfigFlags(flags);
04139 }
04140 
04141 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04142 inline bool isEndBracket  ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04143 inline bool isBracket     ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04144 
04145 /*
04146    Bracket matching uses the following algorithm:
04147    If in overwrite mode, match the bracket currently underneath the cursor.
04148    Otherwise, if the character to the right of the cursor is an starting bracket,
04149    match it. Otherwise if the character to the left of the cursor is a
04150    ending bracket, match it. Otherwise, if the the character to the left
04151    of the cursor is an starting bracket, match it. Otherwise, if the character
04152    to the right of the cursor is an ending bracket, match it. Otherwise, don't
04153    match anything.
04154 */
04155 void KateDocument::newBracketMark( const KateTextCursor& cursor, KateBracketRange& bm, int maxLines )
04156 {
04157   bm.setValid(false);
04158 
04159   bm.start() = cursor;
04160 
04161   if( !findMatchingBracket( bm.start(), bm.end(), maxLines ) )
04162     return;
04163 
04164   bm.setValid(true);
04165 
04166   const int tw = config()->tabWidth();
04167   const int indentStart = m_buffer->plainLine(bm.start().line())->indentDepth(tw);
04168   const int indentEnd = m_buffer->plainLine(bm.end().line())->indentDepth(tw);
04169   bm.setIndentMin(kMin(indentStart, indentEnd));
04170 }
04171 
04172 bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines )
04173 {
04174   KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() );
04175   if( !textLine )
04176     return false;
04177 
04178   QChar right = textLine->getChar( start.col() );
04179   QChar left  = textLine->getChar( start.col() - 1 );
04180   QChar bracket;
04181 
04182   if ( config()->configFlags() & cfOvr ) {
04183     if( isBracket( right ) ) {
04184       bracket = right;
04185     } else {
04186       return false;
04187     }
04188   } else if ( isStartBracket( right ) ) {
04189     bracket = right;
04190   } else if ( isEndBracket( left ) ) {
04191     start.setCol(start.col() - 1);
04192     bracket = left;
04193   } else if ( isBracket( left ) ) {
04194     start.setCol(start.col() - 1);
04195     bracket = left;
04196   } else if ( isBracket( right ) ) {
04197     bracket = right;
04198   } else {
04199     return false;
04200   }
04201 
04202   QChar opposite;
04203 
04204   switch( bracket ) {
04205   case '{': opposite = '}'; break;
04206   case '}': opposite = '{'; break;
04207   case '[': opposite = ']'; break;
04208   case ']': opposite = '['; break;
04209   case '(': opposite = ')'; break;
04210   case ')': opposite = '('; break;
04211   default: return false;
04212   }
04213 
04214   bool forward = isStartBracket( bracket );
04215   int startAttr = textLine->attribute( start.col() );
04216   uint count = 0;
04217   int lines = 0;
04218   end = start;
04219 
04220   while( true ) {
04221     /* Increment or decrement, check base cases */
04222     if( forward ) {
04223       end.setCol(end.col() + 1);
04224       if( end.col() >= lineLength( end.line() ) ) {
04225         if( end.line() >= (int)lastLine() )
04226           return false;
04227         end.setPos(end.line() + 1, 0);
04228         textLine = m_buffer->plainLine( end.line() );
04229         lines++;
04230       }
04231     } else {
04232       end.setCol(end.col() - 1);
04233       if( end.col() < 0 ) {
04234         if( end.line() <= 0 )
04235           return false;
04236         end.setLine(end.line() - 1);
04237         end.setCol(lineLength( end.line() ) - 1);
04238         textLine = m_buffer->plainLine( end.line() );
04239         lines++;
04240       }
04241     }
04242 
04243     if ((maxLines != -1) && (lines > maxLines))
04244       return false;
04245 
04246     /* Easy way to skip comments */
04247     if( textLine->attribute( end.col() ) != startAttr )
04248       continue;
04249 
04250     /* Check for match */
04251     QChar c = textLine->getChar( end.col() );
04252     if( c == bracket ) {
04253       count++;
04254     } else if( c == opposite ) {
04255       if( count == 0 )
04256         return true;
04257       count--;
04258     }
04259 
04260   }
04261 }
04262 
04263 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
04264 {
04265   KParts::ReadWritePart::guiActivateEvent( ev );
04266   if ( ev->activated() )
04267     emit selectionChanged();
04268 }
04269 
04270 void KateDocument::setDocName (QString name )
04271 {
04272   if ( name == m_docName )
04273     return;
04274 
04275   if ( !name.isEmpty() )
04276   {
04277     // TODO check for similarly named documents
04278     m_docName = name;
04279     updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04280     emit nameChanged((Kate::Document *) this);
04281     return;
04282   }
04283 
04284   // if the name is set, and starts with FILENAME, it should not be changed!
04285   if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return;
04286 
04287   int count = -1;
04288 
04289   for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
04290   {
04291     if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) )
04292       if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count )
04293         count = KateFactory::self()->documents()->at(z)->m_docNameNumber;
04294   }
04295 
04296   m_docNameNumber = count + 1;
04297 
04298   m_docName = url().filename();
04299 
04300   if (m_docName.isEmpty())
04301     m_docName = i18n ("Untitled");
04302 
04303   if (m_docNameNumber > 0)
04304     m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
04305 
04306   updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04307   emit nameChanged ((Kate::Document *) this);
04308 }
04309 
04310 void KateDocument::slotModifiedOnDisk( Kate::View * /*v*/ )
04311 {
04312   if ( m_isasking < 0 )
04313   {
04314     m_isasking = 0;
04315     return;
04316   }
04317 
04318   if ( !s_fileChangedDialogsActivated || m_isasking )
04319     return;
04320 
04321   if (m_modOnHd && !url().isEmpty())
04322   {
04323     m_isasking = 1;
04324 
04325     KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), widget() );
04326     switch ( p.exec() )
04327     {
04328       case KateModOnHdPrompt::Save:
04329       {
04330         m_modOnHd = false;
04331         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
04332             url().url(),QString::null,widget(),i18n("Save File"));
04333 
04334         kdDebug(13020)<<"got "<<res.URLs.count()<<" URLs"<<endl;
04335         if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first() ) )
04336         {
04337           setEncoding( res.encoding );
04338 
04339           if( ! saveAs( res.URLs.first() ) )
04340           {
04341             KMessageBox::error( widget(), i18n("Save failed") );
04342             m_modOnHd = true;
04343           }
04344           else
04345             emit modifiedOnDisc( this, false, 0 );
04346         }
04347         else // the save as dialog was cancelled, we are still modified on disk
04348         {
04349           m_modOnHd = true;
04350         }
04351 
04352         m_isasking = 0;
04353         break;
04354       }
04355 
04356       case KateModOnHdPrompt::Reload:
04357         m_modOnHd = false;
04358         emit modifiedOnDisc( this, false, 0 );
04359         reloadFile();
04360         m_isasking = 0;
04361         break;
04362 
04363       case KateModOnHdPrompt::Ignore:
04364         m_modOnHd = false;
04365         emit modifiedOnDisc( this, false, 0 );
04366         m_isasking = 0;
04367         break;
04368 
04369       case KateModOnHdPrompt::Overwrite:
04370         m_modOnHd = false;
04371         emit modifiedOnDisc( this, false, 0 );
04372         m_isasking = 0;
04373         save();
04374         break;
04375 
04376       default:               // cancel: ignore next focus event
04377         m_isasking = -1;
04378     }
04379   }
04380 }
04381 
04382 void KateDocument::setModifiedOnDisk( int reason )
04383 {
04384   m_modOnHdReason = reason;
04385   m_modOnHd = (reason > 0);
04386   emit modifiedOnDisc( this, (reason > 0), reason );
04387 }
04388 
04389 class KateDocumentTmpMark
04390 {
04391   public:
04392     QString line;
04393     KTextEditor::Mark mark;
04394 };
04395 
04396 void KateDocument::reloadFile()
04397 {
04398   if ( !url().isEmpty() )
04399   {
04400     if (m_modOnHd && s_fileChangedDialogsActivated)
04401     {
04402       int i = KMessageBox::warningYesNoCancel
04403                 (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
04404                 i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes"));
04405 
04406       if ( i != KMessageBox::Yes)
04407       {
04408         if (i == KMessageBox::No)
04409         {
04410           m_modOnHd = false;
04411           m_modOnHdReason = 0;
04412           emit modifiedOnDisc (this, m_modOnHd, 0);
04413         }
04414 
04415         return;
04416       }
04417     }
04418 
04419     QValueList<KateDocumentTmpMark> tmp;
04420 
04421     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
04422     {
04423       KateDocumentTmpMark m;
04424 
04425       m.line = textLine (it.current()->line);
04426       m.mark = *it.current();
04427 
04428       tmp.append (m);
04429     }
04430 
04431     uint mode = hlMode ();
04432     bool byUser = hlSetByUser;
04433 
04434     m_storedVariables.clear();
04435 
04436     m_reloading = true;
04437 
04438     QValueList<int> lines, cols;
04439     for ( uint i=0; i < m_views.count(); i++ )
04440     {
04441       lines.append( m_views.at( i )->cursorLine() );
04442       cols.append( m_views.at( i )->cursorColumn() );
04443     }
04444 
04445     KateDocument::openURL( url() );
04446 
04447     for ( uint i=0; i < m_views.count(); i++ )
04448       m_views.at( i )->setCursorPositionInternal( lines[ i ], cols[ i ], m_config->tabWidth(), false );
04449 
04450     m_reloading = false;
04451 
04452     for ( QValueList<int>::size_type z=0; z < tmp.size(); z++ )
04453     {
04454       if (z < numLines())
04455       {
04456         if (textLine(tmp[z].mark.line) == tmp[z].line)
04457           setMark (tmp[z].mark.line, tmp[z].mark.type);
04458       }
04459     }
04460 
04461     if (byUser)
04462       setHlMode (mode);
04463   }
04464 }
04465 
04466 void KateDocument::flush ()
04467 {
04468   closeURL ();
04469 }
04470 
04471 void KateDocument::setWordWrap (bool on)
04472 {
04473   config()->setWordWrap (on);
04474 }
04475 
04476 bool KateDocument::wordWrap ()
04477 {
04478   return config()->wordWrap ();
04479 }
04480 
04481 void KateDocument::setWordWrapAt (uint col)
04482 {
04483   config()->setWordWrapAt (col);
04484 }
04485 
04486 unsigned int KateDocument::wordWrapAt ()
04487 {
04488   return config()->wordWrapAt ();
04489 }
04490 
04491 void KateDocument::applyWordWrap ()
04492 {
04493   // dummy to make the API happy
04494 }
04495 
04496 void KateDocument::setPageUpDownMovesCursor (bool on)
04497 {
04498   config()->setPageUpDownMovesCursor (on);
04499 }
04500 
04501 bool KateDocument::pageUpDownMovesCursor ()
04502 {
04503   return config()->pageUpDownMovesCursor ();
04504 }
04505 
04506 void KateDocument::dumpRegionTree()
04507 {
04508   m_buffer->foldingTree()->debugDump();
04509 }
04510 //END
04511 
04512 //BEGIN KTextEditor::CursorInterface stuff
04513 
04514 KTextEditor::Cursor *KateDocument::createCursor ( )
04515 {
04516   return new KateSuperCursor (this, false, 0, 0, this);
04517 }
04518 
04519 void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range)
04520 {
04521   if (view)
04522     view->tagLines(range->start(), range->end());
04523   else
04524     tagLines(range->start(), range->end());
04525 }
04526 
04527 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
04528 {
04529   m_buffer->lineInfo(info,line);
04530 }
04531 
04532 KateCodeFoldingTree *KateDocument::foldingTree ()
04533 {
04534   return m_buffer->foldingTree();
04535 }
04536 
04537 void KateDocument::setEncoding (const QString &e)
04538 {
04539   if ( m_encodingSticky )
04540     return;
04541 
04542   QString ce = m_config->encoding().lower();
04543   if ( e.lower() == ce )
04544     return;
04545 
04546   m_config->setEncoding( e );
04547   if ( ! m_loading )
04548     reloadFile();
04549 }
04550 
04551 QString KateDocument::encoding() const
04552 {
04553   return m_config->encoding();
04554 }
04555 
04556 void KateDocument::updateConfig ()
04557 {
04558   emit undoChanged ();
04559   tagAll();
04560 
04561   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
04562   {
04563     view->updateDocumentConfig ();
04564   }
04565 
04566   // switch indenter if needed
04567   if (m_indenter->modeNumber() != m_config->indentationMode())
04568   {
04569     delete m_indenter;
04570     m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() );
04571   }
04572 
04573   m_indenter->updateConfig();
04574 
04575   m_buffer->setTabWidth (config()->tabWidth());
04576 
04577   // plugins
04578   for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
04579   {
04580     if (config()->plugin (i))
04581       loadPlugin (i);
04582     else
04583       unloadPlugin (i);
04584   }
04585 }
04586 
04587 //BEGIN Variable reader
04588 // "local variable" feature by anders, 2003
04589 /* TODO
04590       add config options (how many lines to read, on/off)
04591       add interface for plugins/apps to set/get variables
04592       add view stuff
04593 */
04594 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
04595 QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)");
04596 QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)");
04597 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
04598 
04599 void KateDocument::readVariables(bool onlyViewAndRenderer)
04600 {
04601   if (!onlyViewAndRenderer)
04602     m_config->configStart();
04603 
04604   // views!
04605   KateView *v;
04606   for (v = m_views.first(); v != 0L; v= m_views.next() )
04607   {
04608     v->config()->configStart();
04609     v->renderer()->config()->configStart();
04610   }
04611   // read a number of lines in the top/bottom of the document
04612   for (uint i=0; i < kMin( 9U, numLines() ); ++i )
04613   {
04614     readVariableLine( textLine( i ), onlyViewAndRenderer );
04615   }
04616   if ( numLines() > 10 )
04617   {
04618     for ( uint i = kMax(10U, numLines() - 10); i < numLines(); ++i )
04619     {
04620       readVariableLine( textLine( i ), onlyViewAndRenderer );
04621     }
04622   }
04623 
04624   if (!onlyViewAndRenderer)
04625     m_config->configEnd();
04626 
04627   for (v = m_views.first(); v != 0L; v= m_views.next() )
04628   {
04629     v->config()->configEnd();
04630     v->renderer()->config()->configEnd();
04631   }
04632 }
04633 
04634 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
04635 {
04636   // simple check first, no regex
04637   // no kate inside, no vars, simple...
04638   if (t.find("kate") < 0)
04639     return;
04640 
04641   // found vars, if any
04642   QString s;
04643 
04644   if ( kvLine.search( t ) > -1 )
04645   {
04646     s = kvLine.cap(1);
04647 
04648     kdDebug (13020) << "normal variable line kate: matched: " << s << endl;
04649   }
04650   else if (kvLineWildcard.search( t ) > -1) // regex given
04651   {
04652     QStringList wildcards (QStringList::split(';', kvLineWildcard.cap(1)));
04653     QString nameOfFile = url().fileName();
04654 
04655     bool found = false;
04656     for (QStringList::size_type i = 0; !found && i < wildcards.size(); ++i)
04657     {
04658       QRegExp wildcard (wildcards[i], true/*Qt::CaseSensitive*/, true/*QRegExp::Wildcard*/);
04659 
04660       found = wildcard.exactMatch (nameOfFile);
04661     }
04662 
04663     // nothing usable found...
04664     if (!found)
04665       return;
04666 
04667     s = kvLineWildcard.cap(2);
04668 
04669     kdDebug (13020) << "guarded variable line kate-wildcard: matched: " << s << endl;
04670   }
04671   else if (kvLineMime.search( t ) > -1) // mime-type given
04672   {
04673     QStringList types (QStringList::split(';', kvLineMime.cap(1)));
04674 
04675     // no matching type found
04676     if (!types.contains (mimeType ()))
04677       return;
04678 
04679     s = kvLineMime.cap(2);
04680 
04681     kdDebug (13020) << "guarded variable line kate-mimetype: matched: " << s << endl;
04682   }
04683   else // nothing found
04684   {
04685     return;
04686   }
04687 
04688   QStringList vvl; // view variable names
04689   vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
04690       << "line-numbers" << "icon-border" << "folding-markers"
04691       << "bookmark-sorting" << "auto-center-lines"
04692       << "icon-bar-color"
04693       // renderer
04694       << "background-color" << "selection-color"
04695       << "current-line-color" << "bracket-highlight-color"
04696       << "word-wrap-marker-color"
04697       << "font" << "font-size" << "scheme";
04698   int p( 0 );
04699 
04700   QString  var, val;
04701   while ( (p = kvVar.search( s, p )) > -1 )
04702   {
04703     p += kvVar.matchedLength();
04704     var = kvVar.cap( 1 );
04705     val = kvVar.cap( 2 ).stripWhiteSpace();
04706     bool state; // store booleans here
04707     int n; // store ints here
04708 
04709     // only apply view & renderer config stuff
04710     if (onlyViewAndRenderer)
04711     {
04712       if ( vvl.contains( var ) ) // FIXME define above
04713         setViewVariable( var, val );
04714     }
04715     else
04716     {
04717       // BOOL  SETTINGS
04718       if ( var == "word-wrap" && checkBoolValue( val, &state ) )
04719         setWordWrap( state ); // ??? FIXME CHECK
04720       else if ( var == "block-selection"  && checkBoolValue( val, &state ) )
04721         setBlockSelectionMode( state );
04722       // KateConfig::configFlags
04723       // FIXME should this be optimized to only a few calls? how?
04724       else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
04725         m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
04726       else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
04727         m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
04728       else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
04729         m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
04730       else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
04731         m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
04732       else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
04733         m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
04734       else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
04735         m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
04736       else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) )
04737         m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state );
04738       else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
04739         m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
04740       else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
04741         m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
04742       else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
04743         m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
04744       else if ( var == "space-indent" && checkBoolValue( val, &state ) )
04745         m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state );
04746       else if ( var == "smart-home" && checkBoolValue( val, &state ) )
04747         m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
04748       else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
04749         m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
04750       else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) )
04751         m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state);
04752       else if ( var == "mixed-indent" && checkBoolValue( val, &state ) )
04753         m_config->setConfigFlags( KateDocumentConfig::cfMixedIndent, state );
04754 
04755       // INTEGER SETTINGS
04756       else if ( var == "tab-width" && checkIntValue( val, &n ) )
04757         m_config->setTabWidth( n );
04758       else if ( var == "indent-width"  && checkIntValue( val, &n ) )
04759         m_config->setIndentationWidth( n );
04760       else if ( var == "indent-mode" )
04761       {
04762         if ( checkIntValue( val, &n ) )
04763           m_config->setIndentationMode( n );
04764         else
04765           m_config->setIndentationMode( KateAutoIndent::modeNumber( val) );
04766       }
04767       else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 ) // uint, but hard word wrap at 0 will be no fun ;)
04768         m_config->setWordWrapAt( n );
04769       else if ( var == "undo-steps" && checkIntValue( val, &n ) && n >= 0 )
04770         setUndoSteps( n );
04771 
04772       // STRING SETTINGS
04773       else if ( var == "eol" || var == "end-of-line" )
04774       {
04775         QStringList l;
04776         l << "unix" << "dos" << "mac";
04777         if ( (n = l.findIndex( val.lower() )) != -1 )
04778           m_config->setEol( n );
04779       }
04780       else if ( var == "encoding" )
04781         m_config->setEncoding( val );
04782       else if ( var == "syntax" || var == "hl" )
04783       {
04784         for ( uint i=0; i < hlModeCount(); i++ )
04785         {
04786           if ( hlModeName( i ).lower() == val.lower() )
04787           {
04788             setHlMode( i );
04789             break;
04790           }
04791         }
04792       }
04793 
04794       // VIEW SETTINGS
04795       else if ( vvl.contains( var ) )
04796         setViewVariable( var, val );
04797       else
04798       {
04799         m_storedVariables.insert( var, val );
04800         emit variableChanged( var, val );
04801       }
04802     }
04803   }
04804 }
04805 
04806 void KateDocument::setViewVariable( QString var, QString val )
04807 {
04808   KateView *v;
04809   bool state;
04810   int n;
04811   QColor c;
04812   for (v = m_views.first(); v != 0L; v= m_views.next() )
04813   {
04814     if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
04815       v->config()->setDynWordWrap( state );
04816     else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
04817       v->config()->setPersistentSelection( state );
04818     //else if ( var = "dynamic-word-wrap-indicators" )
04819     else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
04820       v->config()->setLineNumbers( state );
04821     else if (var == "icon-border" && checkBoolValue( val, &state ) )
04822       v->config()->setIconBar( state );
04823     else if (var == "folding-markers" && checkBoolValue( val, &state ) )
04824       v->config()->setFoldingBar( state );
04825     else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
04826       v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
04827     else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
04828       v->renderer()->config()->setIconBarColor( c );
04829     // RENDERER
04830     else if ( var == "background-color" && checkColorValue( val, c ) )
04831       v->renderer()->config()->setBackgroundColor( c );
04832     else if ( var == "selection-color" && checkColorValue( val, c ) )
04833       v->renderer()->config()->setSelectionColor( c );
04834     else if ( var == "current-line-color" && checkColorValue( val, c ) )
04835       v->renderer()->config()->setHighlightedLineColor( c );
04836     else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
04837       v->renderer()->config()->setHighlightedBracketColor( c );
04838     else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
04839       v->renderer()->config()->setWordWrapMarkerColor( c );
04840     else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
04841     {
04842       QFont _f( *v->renderer()->config()->font(  ) );
04843 
04844       if ( var == "font" )
04845       {
04846         _f.setFamily( val );
04847         _f.setFixedPitch( QFont( val ).fixedPitch() );
04848       }
04849       else
04850         _f.setPointSize( n );
04851 
04852       v->renderer()->config()->setFont( _f );
04853     }
04854     else if ( var == "scheme" )
04855     {
04856       v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) );
04857     }
04858   }
04859 }
04860 
04861 bool KateDocument::checkBoolValue( QString val, bool *result )
04862 {
04863   val = val.stripWhiteSpace().lower();
04864   QStringList l;
04865   l << "1" << "on" << "true";
04866   if ( l.contains( val ) )
04867   {
04868     *result = true;
04869     return true;
04870   }
04871   l.clear();
04872   l << "0" << "off" << "false";
04873   if ( l.contains( val ) )
04874   {
04875     *result = false;
04876     return true;
04877   }
04878   return false;
04879 }
04880 
04881 bool KateDocument::checkIntValue( QString val, int *result )
04882 {
04883   bool ret( false );
04884   *result = val.toInt( &ret );
04885   return ret;
04886 }
04887 
04888 bool KateDocument::checkColorValue( QString val, QColor &c )
04889 {
04890   c.setNamedColor( val );
04891   return c.isValid();
04892 }
04893 
04894 // KTextEditor::variable
04895 QString KateDocument::variable( const QString &name ) const
04896 {
04897   if ( m_storedVariables.contains( name ) )
04898     return m_storedVariables[ name ];
04899 
04900   return "";
04901 }
04902 
04903 //END
04904 
04905 void KateDocument::slotModOnHdDirty (const QString &path)
04906 {
04907   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1))
04908   {
04909     // compare md5 with the one we have (if we have one)
04910     if ( ! m_digest.isEmpty() )
04911     {
04912       QCString tmp;
04913       if ( createDigest( tmp ) && tmp == m_digest )
04914         return;
04915     }
04916 
04917     m_modOnHd = true;
04918     m_modOnHdReason = 1;
04919 
04920     // reenable dialog if not running atm
04921     if (m_isasking == -1)
04922       m_isasking = false;
04923 
04924     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04925   }
04926 }
04927 
04928 void KateDocument::slotModOnHdCreated (const QString &path)
04929 {
04930   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2))
04931   {
04932     m_modOnHd = true;
04933     m_modOnHdReason = 2;
04934 
04935     // reenable dialog if not running atm
04936     if (m_isasking == -1)
04937       m_isasking = false;
04938 
04939     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04940   }
04941 }
04942 
04943 void KateDocument::slotModOnHdDeleted (const QString &path)
04944 {
04945   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3))
04946   {
04947     m_modOnHd = true;
04948     m_modOnHdReason = 3;
04949 
04950     // reenable dialog if not running atm
04951     if (m_isasking == -1)
04952       m_isasking = false;
04953 
04954     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04955   }
04956 }
04957 
04958 bool KateDocument::createDigest( QCString &result )
04959 {
04960   bool ret = false;
04961   result = "";
04962   if ( url().isLocalFile() )
04963   {
04964     QFile f ( url().path() );
04965     if ( f.open( IO_ReadOnly) )
04966     {
04967       KMD5 md5;
04968       ret = md5.update( f );
04969       md5.hexDigest( result );
04970       f.close();
04971       ret = true;
04972     }
04973   }
04974   return ret;
04975 }
04976 
04977 QString KateDocument::reasonedMOHString() const
04978 {
04979   switch( m_modOnHdReason )
04980   {
04981     case 1:
04982       return i18n("The file '%1' was modified by another program.").arg( url().prettyURL() );
04983       break;
04984     case 2:
04985       return i18n("The file '%1' was created by another program.").arg( url().prettyURL() );
04986       break;
04987     case 3:
04988       return i18n("The file '%1' was deleted by another program.").arg( url().prettyURL() );
04989       break;
04990     default:
04991       return QString();
04992   }
04993 }
04994 
04995 void KateDocument::removeTrailingSpace( uint line )
04996 {
04997   // remove trailing spaces from left line if required
04998   if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn )
04999   {
05000     KateTextLine::Ptr ln = kateTextLine( line );
05001 
05002     if ( ! ln ) return;
05003 
05004     if ( line == activeView()->cursorLine()
05005          && activeView()->cursorColumnReal() >= (uint)kMax(0,ln->lastChar()) )
05006       return;
05007 
05008     if ( ln->length() )
05009     {
05010       uint p = ln->lastChar() + 1;
05011       uint l = ln->length() - p;
05012       if ( l )
05013         editRemoveText( line, p, l);
05014     }
05015   }
05016 }
05017 
05018 void KateDocument::updateFileType (int newType, bool user)
05019 {
05020   if (user || !m_fileTypeSetByUser)
05021   {
05022     const KateFileType *t = 0;
05023     if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType)))
05024     {
05025       m_fileType = newType;
05026 
05027       if (t)
05028       {
05029         m_config->configStart();
05030         // views!
05031         KateView *v;
05032         for (v = m_views.first(); v != 0L; v= m_views.next() )
05033         {
05034           v->config()->configStart();
05035           v->renderer()->config()->configStart();
05036         }
05037 
05038         readVariableLine( t->varLine );
05039 
05040         m_config->configEnd();
05041         for (v = m_views.first(); v != 0L; v= m_views.next() )
05042         {
05043           v->config()->configEnd();
05044           v->renderer()->config()->configEnd();
05045         }
05046       }
05047     }
05048   }
05049 }
05050 
05051 uint KateDocument::documentNumber () const
05052 {
05053   return KTextEditor::Document::documentNumber ();
05054 }
05055 
05056 
05057 
05058 
05059 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
05060       *handled=true;
05061       *abortClosing=true;
05062       if (m_url.isEmpty())
05063       {
05064         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
05065                 QString::null,QString::null,0,i18n("Save File"));
05066 
05067         if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) {
05068                 *abortClosing=true;
05069                 return;
05070         }
05071         setEncoding( res.encoding );
05072           saveAs( res.URLs.first() );
05073         *abortClosing=false;
05074       }
05075       else
05076       {
05077           save();
05078           *abortClosing=false;
05079       }
05080 
05081 }
05082 
05083 bool KateDocument::checkOverwrite( KURL u )
05084 {
05085   if( !u.isLocalFile() )
05086     return true;
05087 
05088   QFileInfo info( u.path() );
05089   if( !info.exists() )
05090     return true;
05091 
05092   return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0,
05093     i18n( "A file named \"%1\" already exists. "
05094           "Are you sure you want to overwrite it?" ).arg( info.fileName() ),
05095     i18n( "Overwrite File?" ),
05096     i18n( "&Overwrite" ) );
05097 }
05098 
05099 void KateDocument::setDefaultEncoding (const QString &encoding)
05100 {
05101   s_defaultEncoding = encoding;
05102 }
05103 
05104 //BEGIN KTextEditor::TemplateInterface
05105 bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
05106       return (new KateTemplateHandler(this,line,column,templateString,initialValues))->initOk();
05107 }
05108 
05109 void KateDocument::testTemplateCode() {
05110   int col=activeView()->cursorColumn();
05111   int line=activeView()->cursorLine();
05112   insertTemplateText(line,col,"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05113 }
05114 
05115 bool KateDocument::invokeTabInterceptor(KKey key) {
05116   if (m_tabInterceptor) return (*m_tabInterceptor)(key);
05117   return false;
05118 }
05119 
05120 bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05121   if (m_tabInterceptor) return false;
05122   m_tabInterceptor=interceptor;
05123   return true;
05124 }
05125 
05126 bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05127   if (m_tabInterceptor!=interceptor) return false;
05128   m_tabInterceptor=0;
05129   return true;
05130 }
05131 //END KTextEditor::TemplateInterface
05132 
05133 //BEGIN DEPRECATED STUFF
05134  bool KateDocument::setSelection ( uint startLine, uint startCol, uint endLine, uint endCol )
05135 { if (m_activeView) return m_activeView->setSelection (startLine, startCol, endLine, endCol); return false; }
05136 
05137  bool KateDocument::clearSelection ()
05138  { if (m_activeView) return m_activeView->clearSelection(); return false; }
05139 
05140  bool KateDocument::hasSelection () const
05141  { if (m_activeView) return m_activeView->hasSelection (); return false; }
05142 
05143  QString KateDocument::selection () const
05144  { if (m_activeView) return m_activeView->selection (); return QString(""); }
05145 
05146  bool KateDocument::removeSelectedText ()
05147  { if (m_activeView) return m_activeView->removeSelectedText (); return false; }
05148 
05149  bool KateDocument::selectAll()
05150  { if (m_activeView) return m_activeView->selectAll (); return false; }
05151 
05152  int KateDocument::selStartLine()
05153  { if (m_activeView) return m_activeView->selStartLine (); return 0; }
05154 
05155  int KateDocument::selStartCol()
05156  { if (m_activeView) return m_activeView->selStartCol (); return 0; }
05157 
05158  int KateDocument::selEndLine()
05159  { if (m_activeView) return m_activeView->selEndLine (); return 0; }
05160 
05161  int KateDocument::selEndCol()
05162  { if (m_activeView) return m_activeView->selEndCol (); return 0; }
05163 
05164  bool KateDocument::blockSelectionMode ()
05165     { if (m_activeView) return m_activeView->blockSelectionMode (); return false; }
05166 
05167 bool KateDocument::setBlockSelectionMode (bool on)
05168     { if (m_activeView) return m_activeView->setBlockSelectionMode (on); return false; }
05169 
05170 bool KateDocument::toggleBlockSelectionMode ()
05171     { if (m_activeView) return m_activeView->toggleBlockSelectionMode (); return false; }
05172 //END DEPRECATED
05173 
05174 //END DEPRECATED STUFF
05175 
05176 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Home | KDE Accessibility Home | Description of Access Keys