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