kate Library API Documentation

katedocument.cpp

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