katedocument.cpp

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