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