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