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

Kate

katedocument.cpp

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

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal