kate Library API Documentation

katedocument.cpp

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