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