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 
03891       if ( t == Uppercase )
03892         s = s.upper();
03893       else if ( t == Lowercase )
03894         s = s.lower();
03895       else // Capitalize
03896       {
03897         KateTextLine::Ptr l = m_buffer->plainLine( ln );
03898         uint p ( 0 );
03899         while( p < s.length() )
03900         {
03901           // If bol or the character before is not in a word, up this one:
03902           // 1. if both start and p is 0, upper char.
03903           // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
03904           // 3. if p-1 is not in a word, upper.
03905           if ( ( ! start && ! p ) ||
03906                    ( ( ln == selstart.line() || v->blockSelectionMode() ) &&
03907                    ! p && ! highlight()->isInWord( l->getChar( start - 1 )) ) ||
03908                    ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
03909              )
03910             s[p] = s.at(p).upper();
03911           p++;
03912         }
03913       }
03914 
03915       removeText( ln, start, ln, end );
03916       insertText( ln, start, s );
03917 
03918       ln++;
03919     }
03920 
03921     // restore selection
03922     v->setSelection( selstart, selend );
03923     selectionRestored = true;
03924 
03925   } else {  // no selection
03926     QString s;
03927     int n ( cc );
03928     switch ( t ) {
03929       case Uppercase:
03930       s = text( cl, cc, cl, cc + 1 ).upper();
03931       break;
03932       case Lowercase:
03933       s = text( cl, cc, cl, cc + 1 ).lower();
03934       break;
03935       case Capitalize:
03936       {
03937         KateTextLine::Ptr l = m_buffer->plainLine( cl );
03938         while ( n > 0 && highlight()->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) )
03939           n--;
03940         s = text( cl, n, cl, n + 1 ).upper();
03941       }
03942       break;
03943       default:
03944       break;
03945     }
03946     removeText( cl, n, cl, n+1 );
03947     insertText( cl, n, s );
03948   }
03949 
03950   if ( ! selectionRestored )
03951     v->setCursorPosition( cl, cc );
03952 
03953   editEnd();
03954 }
03955 
03956 void KateDocument::joinLines( uint first, uint last )
03957 {
03958 //   if ( first == last ) last += 1;
03959   editStart();
03960   int line( first );
03961   while ( first < last )
03962   {
03963     // Normalize the whitespace in the joined lines by making sure there's
03964     // always exactly one space between the joined lines
03965     // This cannot be done in editUnwrapLine, because we do NOT want this
03966     // behaviour when deleting from the start of a line, just when explicitly
03967     // calling the join command
03968     KateTextLine::Ptr l = m_buffer->line( line );
03969     KateTextLine::Ptr tl = m_buffer->line( line + 1 );
03970 
03971     if ( !l || !tl )
03972     {
03973       editEnd();
03974       return;
03975     }
03976 
03977     int pos = tl->firstChar();
03978     if ( pos >= 0 )
03979     {
03980       if (pos != 0)
03981         editRemoveText( line + 1, 0, pos );
03982       if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) )
03983         editInsertText( line + 1, 0, " " );
03984     }
03985     else
03986     {
03987       // Just remove the whitespace and let Kate handle the rest
03988       editRemoveText( line + 1, 0, tl->length() );
03989     }
03990 
03991     editUnWrapLine( line );
03992     first++;
03993   }
03994   editEnd();
03995 }
03996 
03997 QString KateDocument::getWord( const KateTextCursor& cursor ) {
03998   int start, end, len;
03999 
04000   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04001   len = textLine->length();
04002   start = end = cursor.col();
04003   if (start > len)        // Probably because of non-wrapping cursor mode.
04004     return QString("");
04005 
04006   while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
04007   while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(end))) end++;
04008   len = end - start;
04009   return QString(&textLine->text()[start], len);
04010 }
04011 
04012 void KateDocument::tagLines(int start, int end)
04013 {
04014   for (uint z = 0; z < m_views.count(); z++)
04015     m_views.at(z)->tagLines (start, end, true);
04016 }
04017 
04018 void KateDocument::tagLines(KateTextCursor start, KateTextCursor end)
04019 {
04020   // May need to switch start/end cols if in block selection mode
04021   if (blockSelectionMode() && start.col() > end.col()) {
04022     int sc = start.col();
04023     start.setCol(end.col());
04024     end.setCol(sc);
04025   }
04026 
04027   for (uint z = 0; z < m_views.count(); z++)
04028     m_views.at(z)->tagLines(start, end, true);
04029 }
04030 
04031 void KateDocument::repaintViews(bool paintOnlyDirty)
04032 {
04033   for (uint z = 0; z < m_views.count(); z++)
04034     m_views.at(z)->repaintText(paintOnlyDirty);
04035 }
04036 
04037 void KateDocument::tagAll()
04038 {
04039   for (uint z = 0; z < m_views.count(); z++)
04040   {
04041     m_views.at(z)->tagAll();
04042     m_views.at(z)->updateView (true);
04043   }
04044 }
04045 
04046 uint KateDocument::configFlags ()
04047 {
04048   return config()->configFlags();
04049 }
04050 
04051 void KateDocument::setConfigFlags (uint flags)
04052 {
04053   config()->setConfigFlags(flags);
04054 }
04055 
04056 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04057 inline bool isEndBracket  ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04058 inline bool isBracket     ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04059 
04060 /*
04061    Bracket matching uses the following algorithm:
04062    If in overwrite mode, match the bracket currently underneath the cursor.
04063    Otherwise, if the character to the right of the cursor is an starting bracket,
04064    match it. Otherwise if the character to the left of the cursor is a
04065    ending bracket, match it. Otherwise, if the the character to the left
04066    of the cursor is an starting bracket, match it. Otherwise, if the character
04067    to the right of the cursor is an ending bracket, match it. Otherwise, don't
04068    match anything.
04069 */
04070 void KateDocument::newBracketMark( const KateTextCursor& cursor, KateBracketRange& bm, int maxLines )
04071 {
04072   bm.setValid(false);
04073 
04074   bm.start() = cursor;
04075 
04076   if( !findMatchingBracket( bm.start(), bm.end(), maxLines ) )
04077     return;
04078 
04079   bm.setValid(true);
04080 
04081   const int tw = config()->tabWidth();
04082   const int indentStart = m_buffer->plainLine(bm.start().line())->indentDepth(tw);
04083   const int indentEnd = m_buffer->plainLine(bm.end().line())->indentDepth(tw);
04084   bm.setIndentMin(kMin(indentStart, indentEnd));
04085 }
04086 
04087 bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines )
04088 {
04089   KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() );
04090   if( !textLine )
04091     return false;
04092 
04093   QChar right = textLine->getChar( start.col() );
04094   QChar left  = textLine->getChar( start.col() - 1 );
04095   QChar bracket;
04096 
04097   if ( config()->configFlags() & cfOvr ) {
04098     if( isBracket( right ) ) {
04099       bracket = right;
04100     } else {
04101       return false;
04102     }
04103   } else if ( isStartBracket( right ) ) {
04104     bracket = right;
04105   } else if ( isEndBracket( left ) ) {
04106     start.setCol(start.col() - 1);
04107     bracket = left;
04108   } else if ( isBracket( left ) ) {
04109     start.setCol(start.col() - 1);
04110     bracket = left;
04111   } else if ( isBracket( right ) ) {
04112     bracket = right;
04113   } else {
04114     return false;
04115   }
04116 
04117   QChar opposite;
04118 
04119   switch( bracket ) {
04120   case '{': opposite = '}'; break;
04121   case '}': opposite = '{'; break;
04122   case '[': opposite = ']'; break;
04123   case ']': opposite = '['; break;
04124   case '(': opposite = ')'; break;
04125   case ')': opposite = '('; break;
04126   default: return false;
04127   }
04128 
04129   bool forward = isStartBracket( bracket );
04130   int startAttr = textLine->attribute( start.col() );
04131   uint count = 0;
04132   int lines = 0;
04133   end = start;
04134 
04135   while( true ) {
04136     /* Increment or decrement, check base cases */
04137     if( forward ) {
04138       end.setCol(end.col() + 1);
04139       if( end.col() >= lineLength( end.line() ) ) {
04140         if( end.line() >= (int)lastLine() )
04141           return false;
04142         end.setPos(end.line() + 1, 0);
04143         textLine = m_buffer->plainLine( end.line() );
04144         lines++;
04145       }
04146     } else {
04147       end.setCol(end.col() - 1);
04148       if( end.col() < 0 ) {
04149         if( end.line() <= 0 )
04150           return false;
04151         end.setLine(end.line() - 1);
04152         end.setCol(lineLength( end.line() ) - 1);
04153         textLine = m_buffer->plainLine( end.line() );
04154         lines++;
04155       }
04156     }
04157 
04158     if ((maxLines != -1) && (lines > maxLines))
04159       return false;
04160 
04161     /* Easy way to skip comments */
04162     if( textLine->attribute( end.col() ) != startAttr )
04163       continue;
04164 
04165     /* Check for match */
04166     QChar c = textLine->getChar( end.col() );
04167     if( c == bracket ) {
04168       count++;
04169     } else if( c == opposite ) {
04170       if( count == 0 )
04171         return true;
04172       count--;
04173     }
04174 
04175   }
04176 }
04177 
04178 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
04179 {
04180   KParts::ReadWritePart::guiActivateEvent( ev );
04181   if ( ev->activated() )
04182     emit selectionChanged();
04183 }
04184 
04185 void KateDocument::setDocName (QString name )
04186 {
04187   if ( name == m_docName )
04188     return;
04189 
04190   if ( !name.isEmpty() )
04191   {
04192     // TODO check for similarly named documents
04193     m_docName = name;
04194     updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04195     emit nameChanged((Kate::Document *) this);
04196     return;
04197   }
04198 
04199   // if the name is set, and starts with FILENAME, it should not be changed!
04200   if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return;
04201 
04202   int count = -1;
04203 
04204   for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
04205   {
04206     if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) )
04207       if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count )
04208         count = KateFactory::self()->documents()->at(z)->m_docNameNumber;
04209   }
04210 
04211   m_docNameNumber = count + 1;
04212 
04213   m_docName = url().filename();
04214 
04215   if (m_docName.isEmpty())
04216     m_docName = i18n ("Untitled");
04217 
04218   if (m_docNameNumber > 0)
04219     m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
04220 
04221   updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04222   emit nameChanged ((Kate::Document *) this);
04223 }
04224 
04225 void KateDocument::slotModifiedOnDisk( Kate::View * /*v*/ )
04226 {
04227   if ( m_isasking < 0 )
04228   {
04229     m_isasking = 0;
04230     return;
04231   }
04232 
04233   if ( !s_fileChangedDialogsActivated || m_isasking )
04234     return;
04235 
04236   if (m_modOnHd && !url().isEmpty())
04237   {
04238     m_isasking = 1;
04239 
04240     KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), widget() );
04241     switch ( p.exec() )
04242     {
04243       case KateModOnHdPrompt::Save:
04244       {
04245         m_modOnHd = false;
04246         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
04247             url().url(),QString::null,widget(),i18n("Save File"));
04248 
04249         kdDebug(13020)<<"got "<<res.URLs.count()<<" URLs"<<endl;
04250         if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first() ) )
04251         {
04252           setEncoding( res.encoding );
04253 
04254           if( ! saveAs( res.URLs.first() ) )
04255           {
04256             KMessageBox::error( widget(), i18n("Save failed") );
04257             m_modOnHd = true;
04258           }
04259           else
04260             emit modifiedOnDisc( this, false, 0 );
04261         }
04262         else // the save as dialog was cancelled, we are still modified on disk
04263         {
04264           m_modOnHd = true;
04265         }
04266 
04267         m_isasking = 0;
04268         break;
04269       }
04270 
04271       case KateModOnHdPrompt::Reload:
04272         m_modOnHd = false;
04273         emit modifiedOnDisc( this, false, 0 );
04274         reloadFile();
04275         m_isasking = 0;
04276         break;
04277 
04278       case KateModOnHdPrompt::Ignore:
04279         m_modOnHd = false;
04280         emit modifiedOnDisc( this, false, 0 );
04281         m_isasking = 0;
04282         break;
04283 
04284       case KateModOnHdPrompt::Overwrite:
04285         m_modOnHd = false;
04286         emit modifiedOnDisc( this, false, 0 );
04287         m_isasking = 0;
04288         save();
04289         break;
04290 
04291       default:               // cancel: ignore next focus event
04292         m_isasking = -1;
04293     }
04294   }
04295 }
04296 
04297 void KateDocument::setModifiedOnDisk( int reason )
04298 {
04299   m_modOnHdReason = reason;
04300   m_modOnHd = (reason > 0);
04301   emit modifiedOnDisc( this, (reason > 0), reason );
04302 }
04303 
04304 class KateDocumentTmpMark
04305 {
04306   public:
04307     QString line;
04308     KTextEditor::Mark mark;
04309 };
04310 
04311 void KateDocument::reloadFile()
04312 {
04313   if ( !url().isEmpty() )
04314   {
04315     if (m_modOnHd && s_fileChangedDialogsActivated)
04316     {
04317       int i = KMessageBox::warningYesNoCancel
04318                 (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
04319                 i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes"));
04320 
04321       if ( i != KMessageBox::Yes)
04322       {
04323         if (i == KMessageBox::No)
04324         {
04325           m_modOnHd = false;
04326           m_modOnHdReason = 0;
04327           emit modifiedOnDisc (this, m_modOnHd, 0);
04328         }
04329 
04330         return;
04331       }
04332     }
04333 
04334     QValueList<KateDocumentTmpMark> tmp;
04335 
04336     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
04337     {
04338       KateDocumentTmpMark m;
04339 
04340       m.line = textLine (it.current()->line);
04341       m.mark = *it.current();
04342 
04343       tmp.append (m);
04344     }
04345 
04346     uint mode = hlMode ();
04347     bool byUser = hlSetByUser;
04348 
04349     m_storedVariables.clear();
04350 
04351     m_reloading = true;
04352 
04353     QValueList<int> lines, cols;
04354     for ( uint i=0; i < m_views.count(); i++ )
04355     {
04356       lines.append( m_views.at( i )->cursorLine() );
04357       cols.append( m_views.at( i )->cursorColumn() );
04358     }
04359 
04360     KateDocument::openURL( url() );
04361 
04362     for ( uint i=0; i < m_views.count(); i++ )
04363       m_views.at( i )->setCursorPositionInternal( lines[ i ], cols[ i ], m_config->tabWidth(), false );
04364 
04365     m_reloading = false;
04366 
04367     for (uint z=0; z < tmp.size(); z++)
04368     {
04369       if (z < numLines())
04370       {
04371         if (textLine(tmp[z].mark.line) == tmp[z].line)
04372           setMark (tmp[z].mark.line, tmp[z].mark.type);
04373       }
04374     }
04375 
04376     if (byUser)
04377       setHlMode (mode);
04378   }
04379 }
04380 
04381 void KateDocument::flush ()
04382 {
04383   closeURL ();
04384 }
04385 
04386 void KateDocument::setWordWrap (bool on)
04387 {
04388   config()->setWordWrap (on);
04389 }
04390 
04391 bool KateDocument::wordWrap ()
04392 {
04393   return config()->wordWrap ();
04394 }
04395 
04396 void KateDocument::setWordWrapAt (uint col)
04397 {
04398   config()->setWordWrapAt (col);
04399 }
04400 
04401 unsigned int KateDocument::wordWrapAt ()
04402 {
04403   return config()->wordWrapAt ();
04404 }
04405 
04406 void KateDocument::applyWordWrap ()
04407 {
04408   // dummy to make the API happy
04409 }
04410 
04411 void KateDocument::setPageUpDownMovesCursor (bool on)
04412 {
04413   config()->setPageUpDownMovesCursor (on);
04414 }
04415 
04416 bool KateDocument::pageUpDownMovesCursor ()
04417 {
04418   return config()->pageUpDownMovesCursor ();
04419 }
04420 
04421 void KateDocument::dumpRegionTree()
04422 {
04423   m_buffer->foldingTree()->debugDump();
04424 }
04425 //END
04426 
04427 //BEGIN KTextEditor::CursorInterface stuff
04428 
04429 KTextEditor::Cursor *KateDocument::createCursor ( )
04430 {
04431   return new KateSuperCursor (this, false, 0, 0, this);
04432 }
04433 
04434 void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range)
04435 {
04436   if (view)
04437     view->tagLines(range->start(), range->end());
04438   else
04439     tagLines(range->start(), range->end());
04440 }
04441 
04442 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
04443 {
04444   m_buffer->lineInfo(info,line);
04445 }
04446 
04447 KateCodeFoldingTree *KateDocument::foldingTree ()
04448 {
04449   return m_buffer->foldingTree();
04450 }
04451 
04452 void KateDocument::setEncoding (const QString &e)
04453 {
04454   if ( m_encodingSticky )
04455     return;
04456 
04457   QString ce = m_config->encoding().lower();
04458   if ( e.lower() == ce )
04459     return;
04460 
04461   m_config->setEncoding( e );
04462   if ( ! m_loading )
04463     reloadFile();
04464 }
04465 
04466 QString KateDocument::encoding() const
04467 {
04468   return m_config->encoding();
04469 }
04470 
04471 void KateDocument::updateConfig ()
04472 {
04473   emit undoChanged ();
04474   tagAll();
04475 
04476   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
04477   {
04478     view->updateDocumentConfig ();
04479   }
04480 
04481   // switch indenter if needed
04482   if (m_indenter->modeNumber() != m_config->indentationMode())
04483   {
04484     delete m_indenter;
04485     m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() );
04486   }
04487 
04488   m_indenter->updateConfig();
04489 
04490   m_buffer->setTabWidth (config()->tabWidth());
04491 
04492   // plugins
04493   for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
04494   {
04495     if (config()->plugin (i))
04496       loadPlugin (i);
04497     else
04498       unloadPlugin (i);
04499   }
04500 }
04501 
04502 //BEGIN Variable reader
04503 // "local variable" feature by anders, 2003
04504 /* TODO
04505       add config options (how many lines to read, on/off)
04506       add interface for plugins/apps to set/get variables
04507       add view stuff
04508 */
04509 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
04510 QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)");
04511 QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)");
04512 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
04513 
04514 void KateDocument::readVariables(bool onlyViewAndRenderer)
04515 {
04516   if (!onlyViewAndRenderer)
04517     m_config->configStart();
04518 
04519   // views!
04520   KateView *v;
04521   for (v = m_views.first(); v != 0L; v= m_views.next() )
04522   {
04523     v->config()->configStart();
04524     v->renderer()->config()->configStart();
04525   }
04526   // read a number of lines in the top/bottom of the document
04527   for (uint i=0; i < kMin( 9U, numLines() ); ++i )
04528   {
04529     readVariableLine( textLine( i ), onlyViewAndRenderer );
04530   }
04531   if ( numLines() > 10 )
04532   {
04533     for ( uint i = kMax(10U, numLines() - 10); i < numLines(); ++i )
04534     {
04535       readVariableLine( textLine( i ), onlyViewAndRenderer );
04536     }
04537   }
04538 
04539   if (!onlyViewAndRenderer)
04540     m_config->configEnd();
04541 
04542   for (v = m_views.first(); v != 0L; v= m_views.next() )
04543   {
04544     v->config()->configEnd();
04545     v->renderer()->config()->configEnd();
04546   }
04547 }
04548 
04549 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
04550 {
04551   // simple check first, no regex
04552   // no kate inside, no vars, simple...
04553   if (t.find("kate") < 0)
04554     return;
04555 
04556   // found vars, if any
04557   QString s;
04558 
04559   if ( kvLine.search( t ) > -1 )
04560   {
04561     s = kvLine.cap(1);
04562 
04563     kdDebug (13020) << "normal variable line kate: matched: " << s << endl;
04564   }
04565   else if (kvLineWildcard.search( t ) > -1) // regex given
04566   {
04567     QStringList wildcards (QStringList::split(';', kvLineWildcard.cap(1)));
04568     QString nameOfFile = url().fileName();
04569 
04570     bool found = false;
04571     for (int i = 0; !found && i < wildcards.size(); ++i)
04572     {
04573       QRegExp wildcard (wildcards[i], true/*Qt::CaseSensitive*/, true/*QRegExp::Wildcard*/);
04574 
04575       found = wildcard.exactMatch (nameOfFile);
04576     }
04577 
04578     // nothing usable found...
04579     if (!found)
04580       return;
04581 
04582     s = kvLineWildcard.cap(2);
04583 
04584     kdDebug (13020) << "guarded variable line kate-wildcard: matched: " << s << endl;
04585   }
04586   else if (kvLineMime.search( t ) > -1) // mime-type given
04587   {
04588     QStringList types (QStringList::split(';', kvLineMime.cap(1)));
04589 
04590     // no matching type found
04591     if (!types.contains (mimeType ()))
04592       return;
04593 
04594     s = kvLineMime.cap(2);
04595 
04596     kdDebug (13020) << "guarded variable line kate-mimetype: matched: " << s << endl;
04597   }
04598   else // nothing found
04599   {
04600     return;
04601   }
04602 
04603   QStringList vvl; // view variable names
04604   vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
04605       << "line-numbers" << "icon-border" << "folding-markers"
04606       << "bookmark-sorting" << "auto-center-lines"
04607       << "icon-bar-color"
04608       // renderer
04609       << "background-color" << "selection-color"
04610       << "current-line-color" << "bracket-highlight-color"
04611       << "word-wrap-marker-color"
04612       << "font" << "font-size" << "scheme";
04613   int p( 0 );
04614 
04615   QString  var, val;
04616   while ( (p = kvVar.search( s, p )) > -1 )
04617   {
04618     p += kvVar.matchedLength();
04619     var = kvVar.cap( 1 );
04620     val = kvVar.cap( 2 ).stripWhiteSpace();
04621     bool state; // store booleans here
04622     int n; // store ints here
04623 
04624     // only apply view & renderer config stuff
04625     if (onlyViewAndRenderer)
04626     {
04627       if ( vvl.contains( var ) ) // FIXME define above
04628         setViewVariable( var, val );
04629     }
04630     else
04631     {
04632       // BOOL  SETTINGS
04633       if ( var == "word-wrap" && checkBoolValue( val, &state ) )
04634         setWordWrap( state ); // ??? FIXME CHECK
04635       else if ( var == "block-selection"  && checkBoolValue( val, &state ) )
04636         setBlockSelectionMode( state );
04637       // KateConfig::configFlags
04638       // FIXME should this be optimized to only a few calls? how?
04639       else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
04640         m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
04641       else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
04642         m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
04643       else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
04644         m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
04645       else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
04646         m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
04647       else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
04648         m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
04649       else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
04650         m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
04651       else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) )
04652         m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state );
04653       else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
04654         m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
04655       else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
04656         m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
04657       else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
04658         m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
04659       else if ( var == "space-indent" && checkBoolValue( val, &state ) )
04660         m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state );
04661       else if ( var == "smart-home" && checkBoolValue( val, &state ) )
04662         m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
04663       else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
04664         m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
04665       else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) )
04666         m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state);
04667       else if ( var == "mixed-indent" && checkBoolValue( val, &state ) )
04668         m_config->setConfigFlags( KateDocumentConfig::cfMixedIndent, state );
04669 
04670       // INTEGER SETTINGS
04671       else if ( var == "tab-width" && checkIntValue( val, &n ) )
04672         m_config->setTabWidth( n );
04673       else if ( var == "indent-width"  && checkIntValue( val, &n ) )
04674         m_config->setIndentationWidth( n );
04675       else if ( var == "indent-mode" )
04676       {
04677         if ( checkIntValue( val, &n ) )
04678           m_config->setIndentationMode( n );
04679         else
04680           m_config->setIndentationMode( KateAutoIndent::modeNumber( val) );
04681       }
04682       else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 ) // uint, but hard word wrap at 0 will be no fun ;)
04683         m_config->setWordWrapAt( n );
04684       else if ( var == "undo-steps" && checkIntValue( val, &n ) && n >= 0 )
04685         setUndoSteps( n );
04686 
04687       // STRING SETTINGS
04688       else if ( var == "eol" || var == "end-of-line" )
04689       {
04690         QStringList l;
04691         l << "unix" << "dos" << "mac";
04692         if ( (n = l.findIndex( val.lower() )) != -1 )
04693           m_config->setEol( n );
04694       }
04695       else if ( var == "encoding" )
04696         m_config->setEncoding( val );
04697       else if ( var == "syntax" || var == "hl" )
04698       {
04699         for ( uint i=0; i < hlModeCount(); i++ )
04700         {
04701           if ( hlModeName( i ).lower() == val.lower() )
04702           {
04703             setHlMode( i );
04704             break;
04705           }
04706         }
04707       }
04708 
04709       // VIEW SETTINGS
04710       else if ( vvl.contains( var ) )
04711         setViewVariable( var, val );
04712       else
04713       {
04714         m_storedVariables.insert( var, val );
04715         emit variableChanged( var, val );
04716       }
04717     }
04718   }
04719 }
04720 
04721 void KateDocument::setViewVariable( QString var, QString val )
04722 {
04723   KateView *v;
04724   bool state;
04725   int n;
04726   QColor c;
04727   for (v = m_views.first(); v != 0L; v= m_views.next() )
04728   {
04729     if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
04730       v->config()->setDynWordWrap( state );
04731     else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
04732       v->config()->setPersistentSelection( state );
04733     //else if ( var = "dynamic-word-wrap-indicators" )
04734     else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
04735       v->config()->setLineNumbers( state );
04736     else if (var == "icon-border" && checkBoolValue( val, &state ) )
04737       v->config()->setIconBar( state );
04738     else if (var == "folding-markers" && checkBoolValue( val, &state ) )
04739       v->config()->setFoldingBar( state );
04740     else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
04741       v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
04742     else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
04743       v->renderer()->config()->setIconBarColor( c );
04744     // RENDERER
04745     else if ( var == "background-color" && checkColorValue( val, c ) )
04746       v->renderer()->config()->setBackgroundColor( c );
04747     else if ( var == "selection-color" && checkColorValue( val, c ) )
04748       v->renderer()->config()->setSelectionColor( c );
04749     else if ( var == "current-line-color" && checkColorValue( val, c ) )
04750       v->renderer()->config()->setHighlightedLineColor( c );
04751     else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
04752       v->renderer()->config()->setHighlightedBracketColor( c );
04753     else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
04754       v->renderer()->config()->setWordWrapMarkerColor( c );
04755     else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
04756     {
04757       QFont _f( *v->renderer()->config()->font(  ) );
04758 
04759       if ( var == "font" )
04760       {
04761         _f.setFamily( val );
04762         _f.setFixedPitch( QFont( val ).fixedPitch() );
04763       }
04764       else
04765         _f.setPointSize( n );
04766 
04767       v->renderer()->config()->setFont( _f );
04768     }
04769     else if ( var == "scheme" )
04770     {
04771       v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) );
04772     }
04773   }
04774 }
04775 
04776 bool KateDocument::checkBoolValue( QString val, bool *result )
04777 {
04778   val = val.stripWhiteSpace().lower();
04779   QStringList l;
04780   l << "1" << "on" << "true";
04781   if ( l.contains( val ) )
04782   {
04783     *result = true;
04784     return true;
04785   }
04786   l.clear();
04787   l << "0" << "off" << "false";
04788   if ( l.contains( val ) )
04789   {
04790     *result = false;
04791     return true;
04792   }
04793   return false;
04794 }
04795 
04796 bool KateDocument::checkIntValue( QString val, int *result )
04797 {
04798   bool ret( false );
04799   *result = val.toInt( &ret );
04800   return ret;
04801 }
04802 
04803 bool KateDocument::checkColorValue( QString val, QColor &c )
04804 {
04805   c.setNamedColor( val );
04806   return c.isValid();
04807 }
04808 
04809 // KTextEditor::variable
04810 QString KateDocument::variable( const QString &name ) const
04811 {
04812   if ( m_storedVariables.contains( name ) )
04813     return m_storedVariables[ name ];
04814 
04815   return "";
04816 }
04817 
04818 //END
04819 
04820 void KateDocument::slotModOnHdDirty (const QString &path)
04821 {
04822   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1))
04823   {
04824     // compare md5 with the one we have (if we have one)
04825     if ( ! m_digest.isEmpty() )
04826     {
04827       QCString tmp;
04828       if ( createDigest( tmp ) && tmp == m_digest )
04829         return;
04830     }
04831 
04832     m_modOnHd = true;
04833     m_modOnHdReason = 1;
04834 
04835     // reenable dialog if not running atm
04836     if (m_isasking == -1)
04837       m_isasking = false;
04838 
04839     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04840   }
04841 }
04842 
04843 void KateDocument::slotModOnHdCreated (const QString &path)
04844 {
04845   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2))
04846   {
04847     m_modOnHd = true;
04848     m_modOnHdReason = 2;
04849 
04850     // reenable dialog if not running atm
04851     if (m_isasking == -1)
04852       m_isasking = false;
04853 
04854     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04855   }
04856 }
04857 
04858 void KateDocument::slotModOnHdDeleted (const QString &path)
04859 {
04860   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3))
04861   {
04862     m_modOnHd = true;
04863     m_modOnHdReason = 3;
04864 
04865     // reenable dialog if not running atm
04866     if (m_isasking == -1)
04867       m_isasking = false;
04868 
04869     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04870   }
04871 }
04872 
04873 bool KateDocument::createDigest( QCString &result )
04874 {
04875   bool ret = false;
04876   result = "";
04877   if ( url().isLocalFile() )
04878   {
04879     QFile f ( url().path() );
04880     if ( f.open( IO_ReadOnly) )
04881     {
04882       KMD5 md5;
04883       ret = md5.update( f );
04884       md5.hexDigest( result );
04885       f.close();
04886       ret = true;
04887     }
04888   }
04889   return ret;
04890 }
04891 
04892 QString KateDocument::reasonedMOHString() const
04893 {
04894   switch( m_modOnHdReason )
04895   {
04896     case 1:
04897       return i18n("The file '%1' was modified by another program.").arg( url().prettyURL() );
04898       break;
04899     case 2:
04900       return i18n("The file '%1' was created by another program.").arg( url().prettyURL() );
04901       break;
04902     case 3:
04903       return i18n("The file '%1' was deleted by another program.").arg( url().prettyURL() );
04904       break;
04905     default:
04906       return QString();
04907   }
04908 }
04909 
04910 void KateDocument::removeTrailingSpace( uint line )
04911 {
04912   // remove trailing spaces from left line if required
04913   if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn )
04914   {
04915     KateTextLine::Ptr ln = kateTextLine( line );
04916 
04917     if ( ! ln ) return;
04918 
04919     if ( line == activeView()->cursorLine()
04920          && activeView()->cursorColumnReal() >= (uint)kMax(0,ln->lastChar()) )
04921       return;
04922 
04923     if ( ln->length() )
04924     {
04925       uint p = ln->lastChar() + 1;
04926       uint l = ln->length() - p;
04927       if ( l )
04928         editRemoveText( line, p, l);
04929     }
04930   }
04931 }
04932 
04933 void KateDocument::updateFileType (int newType, bool user)
04934 {
04935   if (user || !m_fileTypeSetByUser)
04936   {
04937     const KateFileType *t = 0;
04938     if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType)))
04939     {
04940       m_fileType = newType;
04941 
04942       if (t)
04943       {
04944         m_config->configStart();
04945         // views!
04946         KateView *v;
04947         for (v = m_views.first(); v != 0L; v= m_views.next() )
04948         {
04949           v->config()->configStart();
04950           v->renderer()->config()->configStart();
04951         }
04952 
04953         readVariableLine( t->varLine );
04954 
04955         m_config->configEnd();
04956         for (v = m_views.first(); v != 0L; v= m_views.next() )
04957         {
04958           v->config()->configEnd();
04959           v->renderer()->config()->configEnd();
04960         }
04961       }
04962     }
04963   }
04964 }
04965 
04966 uint KateDocument::documentNumber () const
04967 {
04968   return KTextEditor::Document::documentNumber ();
04969 }
04970 
04971 
04972 
04973 
04974 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
04975       *handled=true;
04976       *abortClosing=true;
04977       if (m_url.isEmpty())
04978       {
04979         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
04980                 QString::null,QString::null,0,i18n("Save File"));
04981 
04982         if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) {
04983                 *abortClosing=true;
04984                 return;
04985         }
04986         setEncoding( res.encoding );
04987           saveAs( res.URLs.first() );
04988         *abortClosing=false;
04989       }
04990       else
04991       {
04992           save();
04993           *abortClosing=false;
04994       }
04995 
04996 }
04997 
04998 bool KateDocument::checkOverwrite( KURL u )
04999 {
05000   if( !u.isLocalFile() )
05001     return true;
05002 
05003   QFileInfo info( u.path() );
05004   if( !info.exists() )
05005     return true;
05006 
05007   return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0,
05008     i18n( "A file named \"%1\" already exists. "
05009           "Are you sure you want to overwrite it?" ).arg( info.fileName() ),
05010     i18n( "Overwrite File?" ),
05011     i18n( "&Overwrite" ) );
05012 }
05013 
05014 void KateDocument::setDefaultEncoding (const QString &encoding)
05015 {
05016   s_defaultEncoding = encoding;
05017 }
05018 
05019 //BEGIN KTextEditor::TemplateInterface
05020 bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
05021       return (new KateTemplateHandler(this,line,column,templateString,initialValues))->initOk();
05022 }
05023 
05024 void KateDocument::testTemplateCode() {
05025   int col=activeView()->cursorColumn();
05026   int line=activeView()->cursorLine();
05027   insertTemplateText(line,col,"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05028 }
05029 
05030 bool KateDocument::invokeTabInterceptor(KKey key) {
05031   if (m_tabInterceptor) return (*m_tabInterceptor)(key);
05032   return false;
05033 }
05034 
05035 bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05036   if (m_tabInterceptor) return false;
05037   m_tabInterceptor=interceptor;
05038   return true;
05039 }
05040 
05041 bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05042   if (m_tabInterceptor!=interceptor) return false;
05043   m_tabInterceptor=0;
05044   return true;
05045 }
05046 //END KTextEditor::TemplateInterface
05047 
05048 //BEGIN DEPRECATED STUFF
05049  bool KateDocument::setSelection ( uint startLine, uint startCol, uint endLine, uint endCol )
05050 { if (m_activeView) return m_activeView->setSelection (startLine, startCol, endLine, endCol); return false; }
05051 
05052  bool KateDocument::clearSelection ()
05053  { if (m_activeView) return m_activeView->clearSelection(); return false; }
05054 
05055  bool KateDocument::hasSelection () const
05056  { if (m_activeView) return m_activeView->hasSelection (); return false; }
05057 
05058  QString KateDocument::selection () const
05059  { if (m_activeView) return m_activeView->selection (); return QString(""); }
05060 
05061  bool KateDocument::removeSelectedText ()
05062  { if (m_activeView) return m_activeView->removeSelectedText (); return false; }
05063 
05064  bool KateDocument::selectAll()
05065  { if (m_activeView) return m_activeView->selectAll (); return false; }
05066 
05067  int KateDocument::selStartLine()
05068  { if (m_activeView) return m_activeView->selStartLine (); return 0; }
05069 
05070  int KateDocument::selStartCol()
05071  { if (m_activeView) return m_activeView->selStartCol (); return 0; }
05072 
05073  int KateDocument::selEndLine()
05074  { if (m_activeView) return m_activeView->selEndLine (); return 0; }
05075 
05076  int KateDocument::selEndCol()
05077  { if (m_activeView) return m_activeView->selEndCol (); return 0; }
05078 
05079  bool KateDocument::blockSelectionMode ()
05080     { if (m_activeView) return m_activeView->blockSelectionMode (); return false; }
05081 
05082 bool KateDocument::setBlockSelectionMode (bool on)
05083     { if (m_activeView) return m_activeView->setBlockSelectionMode (on); return false; }
05084 
05085 bool KateDocument::toggleBlockSelectionMode ()
05086     { if (m_activeView) return m_activeView->toggleBlockSelectionMode (); return false; }
05087 //END DEPRECATED
05088 
05089 //END DEPRECATED STUFF
05090 
05091 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Home | KDE Accessibility Home | Description of Access Keys