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     QWidget *w = widget ();
02461     if (!w && !m_views.isEmpty ())
02462       w = m_views.first();
02463 
02464     if (w)
02465       m_job->setWindow (w->topLevelWidget());
02466 
02467     emit started( m_job );
02468 
02469     return true;
02470   }
02471 }
02472 
02473 void KateDocument::slotDataKate ( KIO::Job *, const QByteArray &data )
02474 {
02475   kdDebug(13020) << "KateDocument::slotData" << endl;
02476 
02477   if (!m_tempFile || !m_tempFile->file())
02478     return;
02479 
02480   m_tempFile->file()->writeBlock (data);
02481 }
02482 
02483 void KateDocument::slotFinishedKate ( KIO::Job * job )
02484 {
02485   kdDebug(13020) << "KateDocument::slotJobFinished" << endl;
02486 
02487   if (!m_tempFile)
02488     return;
02489 
02490   delete m_tempFile;
02491   m_tempFile = 0;
02492   m_job = 0;
02493 
02494   if (job->error())
02495     emit canceled( job->errorString() );
02496   else
02497   {
02498     if ( openFile(job) )
02499       emit setWindowCaption( m_url.prettyURL() );
02500 
02501     emit completed();
02502   }
02503 }
02504 
02505 void KateDocument::abortLoadKate()
02506 {
02507   if ( m_job )
02508   {
02509     kdDebug(13020) << "Aborting job " << m_job << endl;
02510     m_job->kill();
02511     m_job = 0;
02512   }
02513 
02514   delete m_tempFile;
02515   m_tempFile = 0;
02516 }
02517 
02518 bool KateDocument::openFile()
02519 {
02520   return openFile (0);
02521 }
02522 
02523 bool KateDocument::openFile(KIO::Job * job)
02524 {
02525   // add new m_file to dirwatch
02526   activateDirWatch ();
02527 
02528   //
02529   // use metadata
02530   //
02531   if (job)
02532   {
02533     QString metaDataCharset = job->queryMetaData("charset");
02534 
02535     if (!metaDataCharset.isEmpty ())
02536       setEncoding (metaDataCharset);
02537   }
02538 
02539   //
02540   // service type magic to get encoding right
02541   //
02542   QString serviceType = m_extension->urlArgs().serviceType.simplifyWhiteSpace();
02543   int pos = serviceType.find(';');
02544   if (pos != -1)
02545     setEncoding (serviceType.mid(pos+1));
02546 
02547   // do we have success ?
02548   bool success = m_buffer->openFile (m_file);
02549 
02550   //
02551   // yeah, success
02552   //
02553   if (success)
02554   {
02555     if (m_highlight && !m_url.isLocalFile()) {
02556       // The buffer's highlighting gets nuked by KateBuffer::clear()
02557       m_buffer->setHighlight(m_highlight);
02558     }
02559 
02560     // update our hl type if needed
02561     if (!hlSetByUser)
02562     {
02563       int hl (KateHlManager::self()->detectHighlighting (this));
02564 
02565       if (hl >= 0)
02566         internalSetHlMode(hl);
02567     }
02568     // update file type
02569     updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
02570 
02571     // read vars
02572     readVariables();
02573 
02574     // update the md5 digest
02575     createDigest( m_digest );
02576   }
02577 
02578   //
02579   // update views
02580   //
02581   updateViews();
02582 
02583   //
02584   // emit the signal we need for example for kate app
02585   //
02586   emit fileNameChanged ();
02587 
02588   //
02589   // set doc name, dummy value as arg, don't need it
02590   //
02591   setDocName  (QString::null);
02592 
02593   //
02594   // to houston, we are not modified
02595   //
02596   if (m_modOnHd)
02597   {
02598     m_modOnHd = false;
02599     m_modOnHdReason = 0;
02600     emit modifiedOnDisc (this, m_modOnHd, 0);
02601   }
02602 
02603   //
02604   // display errors
02605   //
02606   if (s_openErrorDialogsActivated)
02607   {
02608     if (!success && m_buffer->loadingBorked())
02609       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()));
02610     else if (!success)
02611       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()));
02612   }
02613 
02614   //
02615   // return the success
02616   //
02617   return success;
02618 }
02619 
02620 bool KateDocument::save()
02621 {
02622   // FIXME reorder for efficiency, prompt user in case of failure
02623   bool l ( url().isLocalFile() );
02624   if ( ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles ) ||
02625          ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
02626        && isModified() ) {
02627    
02628     KURL u( url() );
02629     u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() ); 
02630     if ( ! KIO::NetAccess::upload( url().path(), u, kapp->mainWidget() ) )
02631       kdDebug(13020)<<"backing up failed ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
02632   }
02633 
02634   return KParts::ReadWritePart::save();
02635 }
02636 
02637 bool KateDocument::saveFile()
02638 {
02639   //
02640   // we really want to save this file ?
02641   //
02642   bool reallySaveIt = !m_buffer->loadingBorked() || (KMessageBox::warningYesNo(widget(),
02643       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);
02644 
02645   if ( !url().isEmpty() )
02646   {
02647     if (s_fileChangedDialogsActivated && m_modOnHd)
02648     {
02649       QString str = reasonedMOHString() + "\n\n";
02650 
02651       if (!isModified())
02652       {
02653         if (!(KMessageBox::warningYesNo(0,
02654                str + i18n("Do you really want to save this unmodified file? You could overwrite changed data in the file on disk.")) == KMessageBox::Yes))
02655           reallySaveIt = false;
02656       }
02657       else
02658       {
02659         if (!(KMessageBox::warningYesNo(0,
02660                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))
02661           reallySaveIt = false;
02662       }
02663     }
02664   }
02665 
02666   //
02667   // can we encode it if we want to save it ?
02668   //
02669   bool canEncode = true;
02670 
02671   if (reallySaveIt)
02672     canEncode = m_buffer->canEncode ();
02673 
02674   //
02675   // start with worst case, we had no success
02676   //
02677   bool success = false;
02678 
02679   // remove file from dirwatch
02680   deactivateDirWatch ();
02681 
02682   //
02683   // try to save
02684   //
02685   if (reallySaveIt && canEncode)
02686     success = m_buffer->saveFile (m_file);
02687 
02688   // update the md5 digest
02689   createDigest( m_digest );
02690 
02691   // add m_file again to dirwatch
02692   activateDirWatch ();
02693 
02694   //
02695   // hurray, we had success, do stuff we need
02696   //
02697   if (success)
02698   {
02699     // update our hl type if needed
02700     if (!hlSetByUser)
02701     {
02702       int hl (KateHlManager::self()->detectHighlighting (this));
02703 
02704       if (hl >= 0)
02705         internalSetHlMode(hl);
02706     }
02707 
02708     // update our file type
02709     updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
02710 
02711     // read our vars
02712     readVariables();
02713   }
02714 
02715   //
02716   // emit the signal we need for example for kate app
02717   //
02718   emit fileNameChanged ();
02719 
02720   //
02721   // set doc name, dummy value as arg, don't need it
02722   //
02723   setDocName  (QString::null);
02724 
02725   //
02726   // we are not modified
02727   //
02728   if (success && m_modOnHd)
02729   {
02730     m_modOnHd = false;
02731     m_modOnHdReason = 0;
02732     emit modifiedOnDisc (this, m_modOnHd, 0);
02733   }
02734 
02735   //
02736   // display errors
02737   //
02738   if (reallySaveIt && !canEncode)
02739     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."));
02740   else if (reallySaveIt && !success)
02741     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()));
02742 
02743   //
02744   // return success
02745   //
02746   return success;
02747 }
02748 
02749 void KateDocument::activateDirWatch ()
02750 {
02751   // same file as we are monitoring, return
02752   if (m_file == m_dirWatchFile)
02753     return;
02754 
02755   // remove the old watched file
02756   deactivateDirWatch ();
02757 
02758   // add new file if needed
02759   if (m_url.isLocalFile() && !m_file.isEmpty())
02760   {
02761     KateFactory::self()->dirWatch ()->addFile (m_file);
02762     m_dirWatchFile = m_file;
02763   }
02764 }
02765 
02766 void KateDocument::deactivateDirWatch ()
02767 {
02768   if (!m_dirWatchFile.isEmpty())
02769     KateFactory::self()->dirWatch ()->removeFile (m_dirWatchFile);
02770 
02771   m_dirWatchFile = QString::null;
02772 }
02773 
02774 bool KateDocument::closeURL()
02775 {
02776   abortLoadKate();
02777 
02778   //
02779   // file mod on hd
02780   //
02781   if ( !m_reloading && !url().isEmpty() )
02782   {
02783     if (s_fileChangedDialogsActivated && m_modOnHd)
02784     {
02785       if (!(KMessageBox::warningYesNo(0,
02786                reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur.")) == KMessageBox::Yes))
02787         return false;
02788     }
02789   }
02790 
02791   //
02792   // first call the normal kparts implementation
02793   //
02794   if (!KParts::ReadWritePart::closeURL ())
02795     return false;
02796 
02797   // remove file from dirwatch
02798   deactivateDirWatch ();
02799 
02800   //
02801   // empty url + filename
02802   //
02803   m_url = KURL ();
02804   m_file = QString::null;
02805 
02806   // we are not modified
02807   if (m_modOnHd)
02808   {
02809     m_modOnHd = false;
02810     m_modOnHdReason = 0;
02811     emit modifiedOnDisc (this, m_modOnHd, 0);
02812   }
02813 
02814   // clear the buffer
02815   m_buffer->clear();
02816 
02817   // remove all marks
02818   clearMarks ();
02819 
02820   // clear undo/redo history
02821   clearUndo();
02822   clearRedo();
02823 
02824   // no, we are no longer modified
02825   setModified(false);
02826 
02827   // we have no longer any hl
02828   internalSetHlMode(0);
02829 
02830   // update all our views
02831   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
02832   {
02833     // Explicitly call the internal version because we don't want this to look like
02834     // an external request (and thus have the view not QWidget::scroll()ed.
02835     view->setCursorPositionInternal(0, 0, 1, false);
02836     view->updateView(true);
02837   }
02838 
02839   // uh, filename changed
02840   emit fileNameChanged ();
02841 
02842   // update doc name
02843   setDocName (QString::null);
02844 
02845   // success
02846   return true;
02847 }
02848 
02849 void KateDocument::setReadWrite( bool rw )
02850 {
02851   if (isReadWrite() != rw)
02852   {
02853     KParts::ReadWritePart::setReadWrite (rw);
02854 
02855     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02856     {
02857       view->slotUpdate();
02858       view->slotReadWriteChanged ();
02859     }
02860   }
02861 }
02862 
02863 void KateDocument::setModified(bool m) {
02864 
02865   if (isModified() != m) {
02866     KParts::ReadWritePart::setModified (m);
02867 
02868     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02869     {
02870       view->slotUpdate();
02871     }
02872 
02873     emit modifiedChanged ();
02874     emit modStateChanged ((Kate::Document *)this);
02875   }
02876   if ( m == false && ! undoItems.isEmpty() )
02877   {
02878     lastUndoGroupWhenSaved = undoItems.last();
02879   }
02880 
02881   if ( m == false ) docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
02882 }
02883 //END
02884 
02885 //BEGIN Kate specific stuff ;)
02886 
02887 void KateDocument::makeAttribs()
02888 {
02889   m_highlight->clearAttributeArrays ();
02890 
02891   for (uint z = 0; z < m_views.count(); z++)
02892     m_views.at(z)->renderer()->updateAttributes ();
02893 
02894   m_buffer->invalidateHighlighting();
02895 
02896   tagAll ();
02897 }
02898 
02899 // the attributes of a hl have changed, update
02900 void KateDocument::internalHlChanged()
02901 {
02902   makeAttribs();
02903 }
02904 
02905 void KateDocument::addView(KTextEditor::View *view) {
02906   if (!view)
02907     return;
02908 
02909   m_views.append( (KateView *) view  );
02910   m_textEditViews.append( view );
02911 
02912   // apply the view & renderer vars from the file type
02913   const KateFileType *t = 0;
02914   if ((m_fileType > -1) && (t = KateFactory::self()->fileTypeManager()->fileType(m_fileType)))
02915     readVariableLine (t->varLine, true);
02916 
02917   // apply the view & renderer vars from the file
02918   readVariables (true);
02919 
02920   m_activeView = (KateView *) view;
02921 }
02922 
02923 void KateDocument::removeView(KTextEditor::View *view) {
02924   if (!view)
02925     return;
02926 
02927   if (m_activeView == view)
02928     m_activeView = 0L;
02929 
02930   m_views.removeRef( (KateView *) view );
02931   m_textEditViews.removeRef( view  );
02932 }
02933 
02934 void KateDocument::addSuperCursor(KateSuperCursor *cursor, bool privateC) {
02935   if (!cursor)
02936     return;
02937 
02938   m_superCursors.append( cursor );
02939 
02940   if (!privateC)
02941     myCursors.append( cursor );
02942 }
02943 
02944 void KateDocument::removeSuperCursor(KateSuperCursor *cursor, bool privateC) {
02945   if (!cursor)
02946     return;
02947 
02948   if (!privateC)
02949     myCursors.removeRef( cursor  );
02950 
02951   m_superCursors.removeRef( cursor  );
02952 }
02953 
02954 bool KateDocument::ownedView(KateView *view) {
02955   // do we own the given view?
02956   return (m_views.containsRef(view) > 0);
02957 }
02958 
02959 bool KateDocument::isLastView(int numViews) {
02960   return ((int) m_views.count() == numViews);
02961 }
02962 
02963 uint KateDocument::currentColumn( const KateTextCursor& cursor )
02964 {
02965   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
02966 
02967   if (textLine)
02968     return textLine->cursorX(cursor.col(), config()->tabWidth());
02969   else
02970     return 0;
02971 }
02972 
02973 bool KateDocument::typeChars ( KateView *view, const QString &chars )
02974 {
02975   KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorLine ());
02976 
02977   if (!textLine)
02978     return false;
02979 
02980   int oldLine = view->cursorLine ();
02981   int oldCol = view->cursorColumnReal ();
02982 
02983   bool bracketInserted = false;
02984   QString buf;
02985   QChar c;
02986   for( uint z = 0; z < chars.length(); z++ )
02987   {
02988     QChar ch = c = chars[z];
02989 
02990     if (ch.isPrint() || ch == '\t')
02991     {
02992       buf.append (ch);
02993 
02994       if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets))
02995       {
02996         if (ch == '(') { bracketInserted = true; buf.append (')'); }
02997         if (ch == '[') { bracketInserted = true; buf.append (']'); }
02998         if (ch == '{') { bracketInserted = true; buf.append ('}'); }
02999       }
03000     }
03001   }
03002 
03003   if (buf.isEmpty())
03004     return false;
03005 
03006   editStart ();
03007 
03008   if (!(config()->configFlags() & KateDocument::cfPersistent) && hasSelection() )
03009     removeSelectedText();
03010 
03011   if (config()->configFlags()  & KateDocument::cfOvr)
03012     removeText (view->cursorLine(), view->cursorColumnReal(), view->cursorLine(), QMIN( view->cursorColumnReal()+buf.length(), textLine->length() ) );
03013 
03014   insertText (view->cursorLine(), view->cursorColumnReal(), buf);
03015   m_indenter->processChar(c);
03016 
03017   editEnd ();
03018 
03019   if (bracketInserted)
03020     view->setCursorPositionInternal (view->cursorLine(), view->cursorColumnReal()-1);
03021 
03022   emit charactersInteractivelyInserted (oldLine, oldCol, chars);
03023 
03024   return true;
03025 }
03026 
03027 void KateDocument::newLine( KateTextCursor& c, KateViewInternal *v )
03028 {
03029   editStart();
03030 
03031   if( !(config()->configFlags()  & cfPersistent) && hasSelection() )
03032     removeSelectedText();
03033 
03034   // temporary hack to get the cursor pos right !!!!!!!!!
03035   c = v->getCursor ();
03036 
03037   if (c.line() > (int)lastLine())
03038    c.setLine(lastLine());
03039 
03040   uint ln = c.line();
03041 
03042   KateTextLine::Ptr textLine = kateTextLine(c.line());
03043   if (c.col() > (int)textLine->length())
03044     c.setCol(textLine->length());
03045 
03046   if (!(config()->configFlags() & KateDocument::cfAutoIndent))
03047   {
03048     insertText( c.line(), c.col(), "\n" );
03049     c.setPos(c.line() + 1, 0);
03050   }
03051   else
03052   {
03053     int pos = textLine->firstChar();
03054     if (c.col() < pos)
03055       c.setCol(pos); // place cursor on first char if before
03056 
03057     insertText (c.line(), c.col(), "\n");
03058 
03059     KateDocCursor cursor (c.line() + 1, pos, this);
03060     m_indenter->processNewline(cursor, true);
03061     c.setPos(cursor);
03062   }
03063 
03064   removeTrailingSpace( ln );
03065 
03066   editEnd();
03067 }
03068 
03069 void KateDocument::transpose( const KateTextCursor& cursor)
03070 {
03071   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03072 
03073   if (!textLine || (textLine->length() < 2))
03074     return;
03075 
03076   uint col = cursor.col();
03077 
03078   if (col > 0)
03079     col--;
03080 
03081   if ((textLine->length() - col) < 2)
03082     return;
03083 
03084   uint line = cursor.line();
03085   QString s;
03086 
03087   //clever swap code if first character on the line swap right&left
03088   //otherwise left & right
03089   s.append (textLine->getChar(col+1));
03090   s.append (textLine->getChar(col));
03091   //do the swap
03092 
03093   // do it right, never ever manipulate a textline
03094   editStart ();
03095   editRemoveText (line, col, 2);
03096   editInsertText (line, col, s);
03097   editEnd ();
03098 }
03099 
03100 void KateDocument::backspace( const KateTextCursor& c )
03101 {
03102   if( !(config()->configFlags() & cfPersistent) && hasSelection() ) {
03103     removeSelectedText();
03104     return;
03105   }
03106 
03107   uint col = QMAX( c.col(), 0 );
03108   uint line = QMAX( c.line(), 0 );
03109 
03110   if ((col == 0) && (line == 0))
03111     return;
03112 
03113   if (col > 0)
03114   {
03115     if (!(config()->configFlags() & KateDocument::cfBackspaceIndents))
03116     {
03117       // ordinary backspace
03118       //c.cursor.col--;
03119       removeText(line, col-1, line, col);
03120     }
03121     else
03122     {
03123       // backspace indents: erase to next indent position
03124 
03125       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03126       int colX = textLine->cursorX(col, config()->tabWidth());
03127       int pos = textLine->firstChar();
03128       if (pos > 0)
03129         pos = textLine->cursorX(pos, config()->tabWidth());
03130 
03131       if (pos < 0 || pos >= (int)colX)
03132       {
03133         // only spaces on left side of cursor
03134         // search a line with less spaces
03135         int y = line;
03136         while (--y >= 0)
03137         {
03138           textLine = m_buffer->plainLine(y);
03139           pos = textLine->firstChar();
03140 
03141           if (pos >= 0)
03142           {
03143             pos = textLine->cursorX(pos, config()->tabWidth());
03144             if (pos < (int)colX)
03145             {
03146               replaceWithOptimizedSpace(line, col, pos, config()->configFlags());
03147               break;
03148             }
03149           }
03150         }
03151         if (y < 0) {
03152           // FIXME: what shoud we do in this case?
03153           removeText(line, 0, line, col);
03154         }
03155       }
03156       else
03157         removeText(line, col-1, line, col);
03158     }
03159   }
03160   else
03161   {
03162     // col == 0: wrap to previous line
03163     if (line >= 1)
03164     {
03165       KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
03166       if (config()->wordWrap() && textLine->endingWith(QString::fromLatin1(" ")))
03167       {
03168         // gg: in hard wordwrap mode, backspace must also eat the trailing space
03169         removeText (line-1, textLine->length()-1, line, 0);
03170       }
03171       else
03172         removeText (line-1, textLine->length(), line, 0);
03173     }
03174   }
03175 
03176   emit backspacePressed();
03177 }
03178 
03179 void KateDocument::del( const KateTextCursor& c )
03180 {
03181   if ( !(config()->configFlags() & cfPersistent) && hasSelection() ) {
03182     removeSelectedText();
03183     return;
03184   }
03185 
03186   if( c.col() < (int) m_buffer->plainLine(c.line())->length())
03187   {
03188     removeText(c.line(), c.col(), c.line(), c.col()+1);
03189   }
03190   else
03191   {
03192     removeText(c.line(), c.col(), c.line()+1, 0);
03193   }
03194 }
03195 
03196 void KateDocument::cut()
03197 {
03198   if (!hasSelection())
03199     return;
03200 
03201   copy();
03202   removeSelectedText();
03203 }
03204 
03205 void KateDocument::copy()
03206 {
03207   if (!hasSelection())
03208     return;
03209 
03210   QApplication::clipboard()->setText(selection ());
03211 }
03212 
03213 void KateDocument::paste ( KateView* view )
03214 {
03215   QString s = QApplication::clipboard()->text();
03216 
03217   if (s.isEmpty())
03218     return;
03219 
03220   m_undoDontMerge = true;
03221 
03222   editStart ();
03223 
03224   if (!(config()->configFlags() & KateDocument::cfPersistent) && hasSelection() )
03225     removeSelectedText();
03226 
03227   uint line = view->cursorLine ();
03228   uint column = view->cursorColumnReal ();
03229 
03230   insertText ( line, column, s, blockSelect );
03231 
03232   KateDocCursor begin((int)editTagLineStart, 0, this);
03233   KateDocCursor end((int)editTagLineEnd, 0, this);
03234 
03235   editEnd();
03236 
03237   // move cursor right for block select, as the user is moved right internal
03238   // even in that case, but user expects other behavior in block selection
03239   // mode !
03240   if (blockSelect)
03241   {
03242     uint lines = s.contains (QChar ('\n'));
03243     view->setCursorPositionInternal (line+lines, column);
03244   }
03245 
03246   if (m_indenter->canProcessLine())
03247   {
03248     editStart();
03249     m_indenter->processSection (begin, end);
03250     editEnd();
03251   }
03252 
03253   m_undoDontMerge = true;
03254 }
03255 
03256 void KateDocument::selectWord( const KateTextCursor& cursor )
03257 {
03258   int start, end, len;
03259 
03260   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03261   if(!textLine)
03262           return;
03263 
03264   len = textLine->length();
03265   start = end = cursor.col();
03266   while (start > 0 && m_highlight->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
03267   while (end < len && m_highlight->isInWord(textLine->getChar(end), textLine->attribute(start - 1))) end++;
03268   if (end <= start) return;
03269 
03270   if (!(config()->configFlags() & KateDocument::cfKeepSelection))
03271     clearSelection ();
03272 
03273   setSelection (cursor.line(), start, cursor.line(), end);
03274 }
03275 
03276 void KateDocument::selectLine( const KateTextCursor& cursor )
03277 {
03278   if (!(config()->configFlags() & KateDocument::cfKeepSelection))
03279     clearSelection ();
03280 
03281   setSelection (cursor.line(), 0, cursor.line()+1, 0 );
03282 }
03283 
03284 void KateDocument::selectLength( const KateTextCursor& cursor, int length )
03285 {
03286   int start, end;
03287 
03288   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03289   start = cursor.col();
03290   end = start + length;
03291   if (end <= start) return;
03292 
03293   if (!(config()->configFlags() & KateDocument::cfKeepSelection))
03294     clearSelection ();
03295   setSelection (cursor.line(), start, cursor.line(), end);
03296 }
03297 
03298 void KateDocument::insertIndentChars ( KateView *view )
03299 {
03300   editStart ();
03301 
03302   QString s;
03303   if (config()->configFlags() & KateDocument::cfSpaceIndent)
03304   {
03305     int width = config()->indentationWidth();
03306     s.fill (' ', width - (view->cursorColumnReal() % width));
03307   }
03308   else
03309     s.append ('\t');
03310 
03311   insertText (view->cursorLine(), view->cursorColumnReal(), s);
03312 
03313   editEnd ();
03314 }
03315 
03316 void KateDocument::indent ( KateView *, uint line, int change)
03317 {
03318   editStart ();
03319 
03320   if (!hasSelection())
03321   {
03322     // single line
03323     optimizeLeadingSpace(line, config()->configFlags(), change);
03324   }
03325   else
03326   {
03327     int sl = selectStart.line();
03328     int el = selectEnd.line();
03329     int ec = selectEnd.col();
03330 
03331     if ((ec == 0) && ((el-1) >= 0))
03332     {
03333       el--; /* */
03334     }
03335 
03336     if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) {
03337       // unindent so that the existing indent profile doesn't get screwed
03338       // if any line we may unindent is already full left, don't do anything
03339       int adjustedChange = -change;
03340 
03341       for (line = sl; (int) line <= el && adjustedChange > 0; line++) {
03342         KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03343         int firstChar = textLine->firstChar();
03344         if (firstChar >= 0 && (lineSelected(line) || lineHasSelected(line))) {
03345           int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth();
03346           if (maxUnindent < adjustedChange)
03347             adjustedChange = maxUnindent;
03348         }
03349       }
03350 
03351       change = -adjustedChange;
03352     }
03353 
03354     for (line = sl; (int) line <= el; line++) {
03355       if (lineSelected(line) || lineHasSelected(line)) {
03356         optimizeLeadingSpace(line, config()->configFlags(), change);
03357       }
03358     }
03359   }
03360 
03361   editEnd ();
03362 }
03363 
03364 void KateDocument::align(uint line)
03365 {
03366   if (m_indenter->canProcessLine())
03367   {
03368     editStart ();
03369 
03370     if (!hasSelection())
03371     {
03372       KateDocCursor curLine(line, 0, this);
03373       m_indenter->processLine (curLine);
03374       editEnd ();
03375       activeView()->setCursorPosition (line, curLine.col());
03376     }
03377     else
03378     {
03379       m_indenter->processSection(selectStart, selectEnd);
03380       editEnd ();
03381     }
03382   }
03383 }
03384 
03385 /*
03386   Optimize the leading whitespace for a single line.
03387   If change is > 0, it adds indentation units (indentationChars)
03388   if change is == 0, it only optimizes
03389   If change is < 0, it removes indentation units
03390   This will be used to indent, unindent, and optimal-fill a line.
03391   If excess space is removed depends on the flag cfKeepExtraSpaces
03392   which has to be set by the user
03393 */
03394 void KateDocument::optimizeLeadingSpace(uint line, int flags, int change)
03395 {
03396   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03397 
03398   int first_char = textline->firstChar();
03399 
03400   int w = 0;
03401   if (flags & KateDocument::cfSpaceIndent)
03402     w = config()->indentationWidth();
03403   else
03404     w = config()->tabWidth();
03405 
03406   if (first_char < 0)
03407     first_char = textline->length();
03408 
03409   int space =  textline->cursorX(first_char, config()->tabWidth()) + change * w;
03410   if (space < 0)
03411     space = 0;
03412 
03413   if (!(flags & KateDocument::cfKeepExtraSpaces))
03414   {
03415     uint extra = space % w;
03416 
03417     space -= extra;
03418     if (extra && change < 0) {
03419       // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide)
03420       space += w;
03421     }
03422   }
03423 
03424   //kdDebug(13020)  << "replace With Op: " << line << " " << first_char << " " << space << endl;
03425   replaceWithOptimizedSpace(line, first_char, space, flags);
03426 }
03427 
03428 void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags)
03429 {
03430   uint length;
03431   QString new_space;
03432 
03433   if (flags & KateDocument::cfSpaceIndent) {
03434     length = space;
03435     new_space.fill(' ', length);
03436   }
03437   else {
03438     length = space / config()->tabWidth();
03439     new_space.fill('\t', length);
03440 
03441     QString extra_space;
03442     extra_space.fill(' ', space % config()->tabWidth());
03443     length += space % config()->tabWidth();
03444     new_space += extra_space;
03445   }
03446 
03447   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03448   uint change_from;
03449   for (change_from = 0; change_from < upto_column && change_from < length; change_from++) {
03450     if (textline->getChar(change_from) != new_space[change_from])
03451       break;
03452   }
03453 
03454   editStart();
03455 
03456   if (change_from < upto_column)
03457     removeText(line, change_from, line, upto_column);
03458 
03459   if (change_from < length)
03460     insertText(line, change_from, new_space.right(length - change_from));
03461 
03462   editEnd();
03463 }
03464 
03465 /*
03466   Remove a given string at the begining
03467   of the current line.
03468 */
03469 bool KateDocument::removeStringFromBegining(int line, QString &str)
03470 {
03471   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03472 
03473   int index = 0;
03474   bool there = false;
03475 
03476   if (textline->startingWith(str))
03477     there = true;
03478   else
03479   {
03480     index = textline->firstChar ();
03481 
03482     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03483       there = true;
03484   }
03485 
03486   if (there)
03487   {
03488     // Remove some chars
03489     removeText (line, index, line, index+str.length());
03490   }
03491 
03492   return there;
03493 }
03494 
03495 /*
03496   Remove a given string at the end
03497   of the current line.
03498 */
03499 bool KateDocument::removeStringFromEnd(int line, QString &str)
03500 {
03501   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03502 
03503   int index = 0;
03504   bool there = false;
03505 
03506   if(textline->endingWith(str))
03507   {
03508     index = textline->length() - str.length();
03509     there = true;
03510   }
03511   else
03512   {
03513     index = textline->lastChar ()-str.length()+1;
03514 
03515     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03516       there = true;
03517   }
03518 
03519   if (there)
03520   {
03521     // Remove some chars
03522     removeText (line, index, line, index+str.length());
03523   }
03524 
03525   return there;
03526 }
03527 
03528 /*
03529   Add to the current line a comment line mark at
03530   the begining.
03531 */
03532 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
03533 {
03534   QString commentLineMark = m_highlight->getCommentSingleLineStart( attrib ) + " ";
03535   insertText (line, 0, commentLineMark);
03536 }
03537 
03538 /*
03539   Remove from the current line a comment line mark at
03540   the begining if there is one.
03541 */
03542 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
03543 {
03544   QString shortCommentMark = m_highlight->getCommentSingleLineStart( attrib );
03545   QString longCommentMark = shortCommentMark + " ";
03546 
03547   editStart();
03548 
03549   // Try to remove the long comment mark first
03550   bool removed = (removeStringFromBegining(line, longCommentMark)
03551                   || removeStringFromBegining(line, shortCommentMark));
03552 
03553   editEnd();
03554 
03555   return removed;
03556 }
03557 
03558 /*
03559   Add to the current line a start comment mark at the
03560  begining and a stop comment mark at the end.
03561 */
03562 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
03563 {
03564   QString startCommentMark = m_highlight->getCommentStart( attrib ) + " ";
03565   QString stopCommentMark = " " + m_highlight->getCommentEnd( attrib );
03566 
03567   editStart();
03568 
03569   // Add the start comment mark
03570   insertText (line, 0, startCommentMark);
03571 
03572   // Go to the end of the line
03573   int col = m_buffer->plainLine(line)->length();
03574 
03575   // Add the stop comment mark
03576   insertText (line, col, stopCommentMark);
03577 
03578   editEnd();
03579 }
03580 
03581 /*
03582   Remove from the current line a start comment mark at
03583   the begining and a stop comment mark at the end.
03584 */
03585 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
03586 {
03587   QString shortStartCommentMark = m_highlight->getCommentStart( attrib );
03588   QString longStartCommentMark = shortStartCommentMark + " ";
03589   QString shortStopCommentMark = m_highlight->getCommentEnd( attrib );
03590   QString longStopCommentMark = " " + shortStopCommentMark;
03591 
03592   editStart();
03593 
03594   // Try to remove the long start comment mark first
03595   bool removedStart = (removeStringFromBegining(line, longStartCommentMark)
03596                        || removeStringFromBegining(line, shortStartCommentMark));
03597 
03598   bool removedStop = false;
03599   if (removedStart)
03600   {
03601     // Try to remove the long stop comment mark first
03602     removedStop = (removeStringFromEnd(line, longStopCommentMark)
03603                       || removeStringFromEnd(line, shortStopCommentMark));
03604   }
03605 
03606   editEnd();
03607 
03608   return (removedStart || removedStop);
03609 }
03610 
03611 /*
03612   Add to the current selection a start comment
03613  mark at the begining and a stop comment mark
03614  at the end.
03615 */
03616 void KateDocument::addStartStopCommentToSelection( int attrib )
03617 {
03618   QString startComment = m_highlight->getCommentStart( attrib );
03619   QString endComment = m_highlight->getCommentEnd( attrib );
03620 
03621   int sl = selectStart.line();
03622   int el = selectEnd.line();
03623   int sc = selectStart.col();
03624   int ec = selectEnd.col();
03625 
03626   if ((ec == 0) && ((el-1) >= 0))
03627   {
03628     el--;
03629     ec = m_buffer->plainLine (el)->length();
03630   }
03631 
03632   editStart();
03633 
03634   insertText (el, ec, endComment);
03635   insertText (sl, sc, startComment);
03636 
03637   editEnd ();
03638 
03639   // Set the new selection
03640   ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 );
03641   setSelection(sl, sc, el, ec);
03642 }
03643 
03644 /*
03645   Add to the current selection a comment line
03646  mark at the begining of each line.
03647 */
03648 void KateDocument::addStartLineCommentToSelection( int attrib )
03649 {
03650   QString commentLineMark = m_highlight->getCommentSingleLineStart( attrib ) + " ";
03651 
03652   int sl = selectStart.line();
03653   int el = selectEnd.line();
03654 
03655   if ((selectEnd.col() == 0) && ((el-1) >= 0))
03656   {
03657     el--;
03658   }
03659 
03660   editStart();
03661 
03662   // For each line of the selection
03663   for (int z = el; z >= sl; z--) {
03664     insertText (z, 0, commentLineMark);
03665   }
03666 
03667   editEnd ();
03668 
03669   // Set the new selection
03670   selectEnd.setCol(selectEnd.col() + ((el == selectEnd.line()) ? commentLineMark.length() : 0) );
03671   setSelection(selectStart.line(), 0, selectEnd.line(), selectEnd.col());
03672 }
03673 
03674 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
03675 {
03676   for(; line < (int)m_buffer->count(); line++) {
03677     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03678 
03679     if (!textLine)
03680       break;
03681 
03682     col = textLine->nextNonSpaceChar(col);
03683     if(col != -1)
03684       return true; // Next non-space char found
03685     col = 0;
03686   }
03687   // No non-space char found
03688   line = -1;
03689   col = -1;
03690   return false;
03691 }
03692 
03693 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
03694 {
03695   while(true)
03696   {
03697     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03698 
03699     if (!textLine)
03700       break;
03701 
03702     col = textLine->previousNonSpaceChar(col);
03703     if(col != -1) return true;
03704     if(line == 0) return false;
03705     --line;
03706     col = textLine->length();
03707 }
03708   // No non-space char found
03709   line = -1;
03710   col = -1;
03711   return false;
03712 }
03713 
03714 /*
03715   Remove from the selection a start comment mark at
03716   the begining and a stop comment mark at the end.
03717 */
03718 bool KateDocument::removeStartStopCommentFromSelection( int attrib )
03719 {
03720   QString startComment = m_highlight->getCommentStart( attrib );
03721   QString endComment = m_highlight->getCommentEnd( attrib );
03722 
03723   int sl = kMax<int> (0, selectStart.line());
03724   int el = kMin<int>  (selectEnd.line(), lastLine());
03725   int sc = selectStart.col();
03726   int ec = selectEnd.col();
03727 
03728   // The selection ends on the char before selectEnd
03729   if (ec != 0) {
03730     ec--;
03731   } else {
03732     if (el > 0) {
03733       el--;
03734       ec = m_buffer->plainLine(el)->length() - 1;
03735     }
03736   }
03737 
03738   int startCommentLen = startComment.length();
03739   int endCommentLen = endComment.length();
03740 
03741   // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/
03742 
03743   bool remove = nextNonSpaceCharPos(sl, sc)
03744       && m_buffer->plainLine(sl)->stringAtPos(sc, startComment)
03745       && previousNonSpaceCharPos(el, ec)
03746       && ( (ec - endCommentLen + 1) >= 0 )
03747       && m_buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment);
03748 
03749   if (remove) {
03750     editStart();
03751 
03752     removeText (el, ec - endCommentLen + 1, el, ec + 1);
03753     removeText (sl, sc, sl, sc + startCommentLen);
03754 
03755     editEnd ();
03756 
03757     // Set the new selection
03758     ec -= endCommentLen + ( (el == sl) ? startCommentLen : 0 );
03759     setSelection(sl, sc, el, ec + 1);
03760   }
03761 
03762   return remove;
03763 }
03764 
03765 /*
03766   Remove from the begining of each line of the
03767   selection a start comment line mark.
03768 */
03769 bool KateDocument::removeStartLineCommentFromSelection( int attrib )
03770 {
03771   QString shortCommentMark = m_highlight->getCommentSingleLineStart( attrib );
03772   QString longCommentMark = shortCommentMark + " ";
03773 
03774   int sl = selectStart.line();
03775   int el = selectEnd.line();
03776 
03777   if ((selectEnd.col() == 0) && ((el-1) >= 0))
03778   {
03779     el--;
03780   }
03781 
03782   // Find out how many char will be removed from the last line
03783   int removeLength = 0;
03784   if (m_buffer->plainLine(el)->startingWith(longCommentMark))
03785     removeLength = longCommentMark.length();
03786   else if (m_buffer->plainLine(el)->startingWith(shortCommentMark))
03787     removeLength = shortCommentMark.length();
03788 
03789   bool removed = false;
03790 
03791   editStart();
03792 
03793   // For each line of the selection
03794   for (int z = el; z >= sl; z--)
03795   {
03796     // Try to remove the long comment mark first
03797     removed = (removeStringFromBegining(z, longCommentMark)
03798                  || removeStringFromBegining(z, shortCommentMark)
03799                  || removed);
03800   }
03801 
03802   editEnd();
03803 
03804   if(removed) {
03805     // Set the new selection
03806     selectEnd.setCol(selectEnd.col() - ((el == selectEnd.line()) ? removeLength : 0) );
03807     setSelection(selectStart.line(), selectStart.col(), selectEnd.line(), selectEnd.col());
03808   }
03809 
03810   return removed;
03811 }
03812 
03813 /*
03814   Comment or uncomment the selection or the current
03815   line if there is no selection.
03816 */
03817 void KateDocument::comment( KateView *, uint line, int change)
03818 {
03819   // We need to check that we can sanely comment the selectino or region.
03820   // It is if the attribute of the first and last character of the range to
03821   // comment belongs to the same language definition.
03822   // for lines with no text, we need the attribute for the lines context.
03823   bool hassel = hasSelection();
03824   int startAttrib, endAttrib;
03825   if ( hassel )
03826   {
03827     KateTextLine::Ptr ln = kateTextLine( selectStart.line() );
03828     int l = selectStart.line(), c = selectStart.col();
03829     startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03830 
03831     ln = kateTextLine( selectEnd.line() );
03832     l = selectEnd.line(), c = selectEnd.col();
03833     endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03834   }
03835   else
03836   {
03837     KateTextLine::Ptr ln = kateTextLine( line );
03838     if ( ln->length() )
03839     {
03840       startAttrib = ln->attribute( ln->firstChar() );
03841       endAttrib = ln->attribute( ln->lastChar() );
03842     }
03843     else
03844     {
03845       int l = line, c = 0;
03846       if ( nextNonSpaceCharPos( l, c )  || previousNonSpaceCharPos( l, c ) )
03847         startAttrib = endAttrib = kateTextLine( l )->attribute( c );
03848       else
03849         startAttrib = endAttrib = 0;
03850     }
03851   }
03852 
03853   if ( ! m_highlight->canComment( startAttrib, endAttrib ) )
03854   {
03855     kdDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!"<<endl;
03856     return;
03857   }
03858 
03859   bool hasStartLineCommentMark = !(m_highlight->getCommentSingleLineStart( startAttrib ).isEmpty());
03860   bool hasStartStopCommentMark = ( !(m_highlight->getCommentStart( startAttrib ).isEmpty())
03861       && !(m_highlight->getCommentEnd( endAttrib ).isEmpty()) );
03862 
03863   bool removed = false;
03864 
03865   if (change > 0)
03866   {
03867     if ( !hassel )
03868     {
03869       if ( hasStartLineCommentMark )
03870         addStartLineCommentToSingleLine( line, startAttrib );
03871       else if ( hasStartStopCommentMark )
03872         addStartStopCommentToSingleLine( line, startAttrib );
03873     }
03874     else
03875     {
03876       // anders: prefer single line comment to avoid nesting probs
03877       // If the selection starts after first char in the first line
03878       // or ends before the last char of the last line, we may use
03879       // multiline comment markers.
03880       // TODO We should try to detect nesting.
03881       //    - if selection ends at col 0, most likely she wanted that
03882       // line ignored
03883       if ( hasStartStopCommentMark &&
03884            ( !hasStartLineCommentMark || (
03885              ( selectStart.col() > m_buffer->plainLine( selectStart.line() )->firstChar() ) ||
03886                ( selectEnd.col() < ((int)m_buffer->plainLine( selectEnd.line() )->length()) )
03887          ) ) )
03888         addStartStopCommentToSelection( startAttrib );
03889       else if ( hasStartLineCommentMark )
03890         addStartLineCommentToSelection( startAttrib );
03891     }
03892   }
03893   else
03894   {
03895     if ( !hassel )
03896     {
03897       removed = ( hasStartLineCommentMark
03898                   && removeStartLineCommentFromSingleLine( line, startAttrib ) )
03899         || ( hasStartStopCommentMark
03900              && removeStartStopCommentFromSingleLine( line, startAttrib ) );
03901     }
03902     else
03903     {
03904       // anders: this seems like it will work with above changes :)
03905       removed = ( hasStartLineCommentMark
03906                   && removeStartLineCommentFromSelection( startAttrib ) )
03907         || ( hasStartStopCommentMark
03908              && removeStartStopCommentFromSelection( startAttrib ) );
03909     }
03910   }
03911 }
03912 
03913 void KateDocument::transform( KateView *, const KateTextCursor &c,
03914                             KateDocument::TextTransform t )
03915 {
03916   editStart();
03917   uint cl( c.line() ), cc( c.col() );
03918 
03919   if ( hasSelection() )
03920   {
03921     // cache the selection and cursor, so we can be sure to restore.
03922     KateTextCursor s = selectStart;
03923     KateTextCursor e = selectEnd;
03924 
03925     int ln = selStartLine();
03926     while ( ln <= selEndLine() )
03927     {
03928       uint start, end;
03929       start = (ln == selStartLine() || blockSelectionMode()) ?
03930           selStartCol() : 0;
03931       end = (ln == selEndLine() || blockSelectionMode()) ?
03932           selEndCol() : lineLength( ln );
03933       QString s = text( ln, start, ln, end );
03934 
03935       if ( t == Uppercase )
03936         s = s.upper();
03937       else if ( t == Lowercase )
03938         s = s.lower();
03939       else // Capitalize
03940       {
03941         KateTextLine::Ptr l = m_buffer->plainLine( ln );
03942         uint p ( 0 );
03943         while( p < s.length() )
03944         {
03945           // If bol or the character before is not in a word, up this one:
03946           // 1. if both start and p is 0, upper char.
03947           // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
03948           // 3. if p-1 is not in a word, upper.
03949           if ( ( ! start && ! p ) ||
03950                ( ( ln == selStartLine() || blockSelectionMode() ) &&
03951                  ! p && ! m_highlight->isInWord( l->getChar( start - 1 )) ) ||
03952                ( p && ! m_highlight->isInWord( s.at( p-1 ) ) )
03953              )
03954             s[p] = s.at(p).upper();
03955           p++;
03956         }
03957       }
03958 
03959       removeText( ln, start, ln, end );
03960       insertText( ln, start, s );
03961 
03962       ln++;
03963     }
03964 
03965     // restore selection
03966     setSelection( s, e );
03967 
03968   } else {  // no selection
03969     QString s;
03970     int n ( cc );
03971     switch ( t ) {
03972       case Uppercase:
03973       s = text( cl, cc, cl, cc + 1 ).upper();
03974       break;
03975       case Lowercase:
03976       s = text( cl, cc, cl, cc + 1 ).lower();
03977       break;
03978       case Capitalize:
03979       {
03980         KateTextLine::Ptr l = m_buffer->plainLine( cl );
03981         while ( n > 0 && m_highlight->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) )
03982           n--;
03983         s = text( cl, n, cl, n + 1 ).upper();
03984       }
03985       break;
03986       default:
03987       break;
03988     }
03989     removeText( cl, n, cl, n+1 );
03990     insertText( cl, n, s );
03991   }
03992 
03993   editEnd();
03994 
03995   if ( activeView() )
03996     activeView()->setCursorPosition( cl, cc );
03997 }
03998 
03999 void KateDocument::joinLines( uint first, uint last )
04000 {
04001 //   if ( first == last ) last += 1;
04002   editStart();
04003   int line( first );
04004   while ( first < last )
04005   {
04006     // Normalize the whitespace in the joined lines by making sure there's
04007     // always exactly one space between the joined lines
04008     // This cannot be done in editUnwrapLine, because we do NOT want this
04009     // behaviour when deleting from the start of a line, just when explicitly
04010     // calling the join command
04011     KateTextLine::Ptr l = m_buffer->line( line );
04012     KateTextLine::Ptr tl = m_buffer->line( line + 1 );
04013 
04014     if ( !l || !tl )
04015     {
04016       editEnd();
04017       return;
04018     }
04019 
04020     int pos = tl->firstChar();
04021     if ( pos >= 0 )
04022     {
04023       if (pos != 0)
04024         editRemoveText( line + 1, 0, pos );
04025       if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) )
04026         editInsertText( line + 1, 0, " " );
04027     }
04028     else
04029     {
04030       // Just remove the whitespace and let Kate handle the rest
04031       editRemoveText( line + 1, 0, tl->length() );
04032     }
04033 
04034     editUnWrapLine( line );
04035     first++;
04036   }
04037   editEnd();
04038 }
04039 
04040 QString KateDocument::getWord( const KateTextCursor& cursor ) {
04041   int start, end, len;
04042 
04043   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04044   len = textLine->length();
04045   start = end = cursor.col();
04046   if (start > len)        // Probably because of non-wrapping cursor mode.
04047     return QString("");
04048 
04049   while (start > 0 && m_highlight->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
04050   while (end < len && m_highlight->isInWord(textLine->getChar(end), textLine->attribute(end))) end++;
04051   len = end - start;
04052   return QString(&textLine->text()[start], len);
04053 }
04054 
04055 void KateDocument::tagLines(int start, int end)
04056 {
04057   for (uint z = 0; z < m_views.count(); z++)
04058     m_views.at(z)->tagLines (start, end, true);
04059 }
04060 
04061 void KateDocument::tagLines(KateTextCursor start, KateTextCursor end)
04062 {
04063   // May need to switch start/end cols if in block selection mode
04064   if (blockSelectionMode() && start.col() > end.col()) {
04065     int sc = start.col();
04066     start.setCol(end.col());
04067     end.setCol(sc);
04068   }
04069 
04070   for (uint z = 0; z < m_views.count(); z++)
04071     m_views.at(z)->tagLines(start, end, true);
04072 }
04073 
04074 void KateDocument::tagSelection(const KateTextCursor &oldSelectStart, const KateTextCursor &oldSelectEnd)
04075 {
04076   if (hasSelection()) {
04077     if (oldSelectStart.line() == -1) {
04078       // We have to tag the whole lot if
04079       // 1) we have a selection, and:
04080       //  a) it's new; or
04081       tagLines(selectStart, selectEnd);
04082 
04083     } else if (blockSelectionMode() && (oldSelectStart.col() != selectStart.col() || oldSelectEnd.col() != selectEnd.col())) {
04084       //  b) we're in block selection mode and the columns have changed
04085       tagLines(selectStart, selectEnd);
04086       tagLines(oldSelectStart, oldSelectEnd);
04087 
04088     } else {
04089       if (oldSelectStart != selectStart) {
04090         if (oldSelectStart < selectStart)
04091           tagLines(oldSelectStart, selectStart);
04092         else
04093           tagLines(selectStart, oldSelectStart);
04094       }
04095 
04096       if (oldSelectEnd != selectEnd) {
04097         if (oldSelectEnd < selectEnd)
04098           tagLines(oldSelectEnd, selectEnd);
04099         else
04100           tagLines(selectEnd, oldSelectEnd);
04101       }
04102     }
04103 
04104   } else {
04105     // No more selection, clean up
04106     tagLines(oldSelectStart, oldSelectEnd);
04107   }
04108 }
04109 
04110 void KateDocument::repaintViews(bool paintOnlyDirty)
04111 {
04112   for (uint z = 0; z < m_views.count(); z++)
04113     m_views.at(z)->repaintText(paintOnlyDirty);
04114 }
04115 
04116 void KateDocument::tagAll()
04117 {
04118   for (uint z = 0; z < m_views.count(); z++)
04119   {
04120     m_views.at(z)->tagAll();
04121     m_views.at(z)->updateView (true);
04122   }
04123 }
04124 
04125 void KateDocument::updateViews()
04126 {
04127   if (noViewUpdates)
04128     return;
04129 
04130   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
04131   {
04132     view->updateView(true);
04133   }
04134 }
04135 
04136 uint KateDocument::configFlags ()
04137 {
04138   return config()->configFlags();
04139 }
04140 
04141 void KateDocument::setConfigFlags (uint flags)
04142 {
04143   config()->setConfigFlags(flags);
04144 }
04145 
04146 bool KateDocument::lineColSelected (int line, int col)
04147 {
04148   if ( (!blockSelect) && (col < 0) )
04149     col = 0;
04150 
04151   KateTextCursor cursor(line, col);
04152 
04153   if (blockSelect)
04154     return cursor.line() >= selectStart.line() && cursor.line() <= selectEnd.line() && cursor.col() >= selectStart.col() && cursor.col() < selectEnd.col();
04155   else
04156     return (cursor >= selectStart) && (cursor < selectEnd);
04157 }
04158 
04159 bool KateDocument::lineSelected (int line)
04160 {
04161   return (!blockSelect)
04162     && (selectStart <= KateTextCursor(line, 0))
04163     && (line < selectEnd.line());
04164 }
04165 
04166 bool KateDocument::lineEndSelected (int line, int endCol)
04167 {
04168   return (!blockSelect)
04169     && (line > selectStart.line() || (line == selectStart.line() && (selectStart.col() < endCol || endCol == -1)))
04170     && (line < selectEnd.line() || (line == selectEnd.line() && (endCol <= selectEnd.col() && endCol != -1)));
04171 }
04172 
04173 bool KateDocument::lineHasSelected (int line)
04174 {
04175   return (selectStart < selectEnd)
04176     && (line >= selectStart.line())
04177     && (line <= selectEnd.line());
04178 }
04179 
04180 bool KateDocument::lineIsSelection (int line)
04181 {
04182   return (line == selectStart.line() && line == selectEnd.line());
04183 }
04184 
04185 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04186 inline bool isEndBracket  ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04187 inline bool isBracket     ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04188 
04189 /*
04190    Bracket matching uses the following algorithm:
04191    If in overwrite mode, match the bracket currently underneath the cursor.
04192    Otherwise, if the character to the right of the cursor is an starting bracket,
04193    match it. Otherwise if the character to the left of the cursor is a
04194    ending bracket, match it. Otherwise, if the the character to the left
04195    of the cursor is an starting bracket, match it. Otherwise, if the character
04196    to the right of the cursor is an ending bracket, match it. Otherwise, don't
04197    match anything.
04198 */
04199 void KateDocument::newBracketMark( const KateTextCursor& cursor, KateTextRange& bm )
04200 {
04201   bm.setValid(false);
04202 
04203   bm.start() = cursor;
04204 
04205   if( !findMatchingBracket( bm.start(), bm.end() ) )
04206     return;
04207 
04208   bm.setValid(true);
04209 }
04210 
04211 bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end )
04212 {
04213   KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() );
04214   if( !textLine )
04215     return false;
04216 
04217   QChar right = textLine->getChar( start.col() );
04218   QChar left  = textLine->getChar( start.col() - 1 );
04219   QChar bracket;
04220 
04221   if ( config()->configFlags() & cfOvr ) {
04222     if( isBracket( right ) ) {
04223       bracket = right;
04224     } else {
04225       return false;
04226     }
04227   } else if ( isStartBracket( right ) ) {
04228     bracket = right;
04229   } else if ( isEndBracket( left ) ) {
04230     start.setCol(start.col() - 1);
04231     bracket = left;
04232   } else if ( isBracket( left ) ) {
04233     start.setCol(start.col() - 1);
04234     bracket = left;
04235   } else if ( isBracket( right ) ) {
04236     bracket = right;
04237   } else {
04238     return false;
04239   }
04240 
04241   QChar opposite;
04242 
04243   switch( bracket ) {
04244   case '{': opposite = '}'; break;
04245   case '}': opposite = '{'; break;
04246   case '[': opposite = ']'; break;
04247   case ']': opposite = '['; break;
04248   case '(': opposite = ')'; break;
04249   case ')': opposite = '('; break;
04250   default: return false;
04251   }
04252 
04253   bool forward = isStartBracket( bracket );
04254   int startAttr = textLine->attribute( start.col() );
04255   uint count = 0;
04256   end = start;
04257 
04258   while( true ) {
04259     /* Increment or decrement, check base cases */
04260     if( forward ) {
04261       end.setCol(end.col() + 1);
04262       if( end.col() >= lineLength( end.line() ) ) {
04263         if( end.line() >= (int)lastLine() )
04264           return false;
04265         end.setPos(end.line() + 1, 0);
04266         textLine = m_buffer->plainLine( end.line() );
04267       }
04268     } else {
04269       end.setCol(end.col() - 1);
04270       if( end.col() < 0 ) {
04271         if( end.line() <= 0 )
04272           return false;
04273         end.setLine(end.line() - 1);
04274         end.setCol(lineLength( end.line() ) - 1);
04275         textLine = m_buffer->plainLine( end.line() );
04276       }
04277     }
04278 
04279     /* Easy way to skip comments */
04280     if( textLine->attribute( end.col() ) != startAttr )
04281       continue;
04282 
04283     /* Check for match */
04284     QChar c = textLine->getChar( end.col() );
04285     if( c == bracket ) {
04286       count++;
04287     } else if( c == opposite ) {
04288       if( count == 0 )
04289         return true;
04290       count--;
04291     }
04292 
04293   }
04294 }
04295 
04296 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
04297 {
04298   KParts::ReadWritePart::guiActivateEvent( ev );
04299   if ( ev->activated() )
04300     emit selectionChanged();
04301 }
04302 
04303 void KateDocument::setDocName (QString name )
04304 {
04305   if ( !name.isEmpty() )
04306   {
04307     // TODO check for similarly named documents
04308     m_docName = name;
04309     emit nameChanged((Kate::Document *) this);
04310     return;
04311   }
04312 
04313   // if the name is set, and starts with FILENAME, it should not be changed!
04314   if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return;
04315 
04316   int count = -1;
04317 
04318   for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
04319   {
04320     if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) )
04321       if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count )
04322         count = KateFactory::self()->documents()->at(z)->m_docNameNumber;
04323   }
04324 
04325   m_docNameNumber = count + 1;
04326 
04327   m_docName = url().filename();
04328 
04329   if (m_docName.isEmpty())
04330     m_docName = i18n ("Untitled");
04331 
04332   if (m_docNameNumber > 0)
04333     m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
04334 
04335   emit nameChanged ((Kate::Document *) this);
04336 }
04337 
04338 void KateDocument::slotModifiedOnDisk( Kate::View *v )
04339 {
04340   if ( !s_fileChangedDialogsActivated || m_isasking )
04341     return;
04342 
04343   if (m_modOnHd && !url().isEmpty())
04344   {
04345     m_isasking = 1;
04346 
04347     if ( m_modOnHdReason == 3 ) // deleted
04348     {
04349       switch ( KMessageBox::warningYesNoCancel( widget(),
04350                reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
04351                i18n("File Was Deleted on Disk"),
04352                i18n("&Save As..."), i18n("&Ignore Changes")) )
04353       {
04354         case KMessageBox::Yes: // "save file"
04355           m_modOnHd = false; // trick save() to not ask again
04356           emit modifiedOnDisc( this, false, 0 );
04357           saveAs(url());
04358           m_isasking = 0;
04359           break;
04360 
04361           case KMessageBox::No:  // "ignore changes"
04362             m_modOnHd = false;
04363             emit modifiedOnDisc( this, false, 0 );
04364             m_isasking = 0;
04365             break;
04366 
04367             default:               // cancel: ignore next focus event
04368               m_isasking = -1;
04369       }
04370     } else {
04371       switch ( KMessageBox::warningYesNoCancel( widget(),
04372                reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
04373                i18n("File Was Modified on Disk"),
04374                i18n("&Reload File"), i18n("&Ignore Changes")) )
04375       {
04376         case KMessageBox::Yes: // "reload file"
04377           m_modOnHd = false; // trick reloadFile() to not ask again
04378           emit modifiedOnDisc( this, false, 0 );
04379           reloadFile();
04380           m_isasking = 0;
04381           break;
04382 
04383           case KMessageBox::No:  // "ignore changes"
04384             m_modOnHd = false;
04385             emit modifiedOnDisc( this, false, 0 );
04386             m_isasking = 0;
04387             break;
04388 
04389             default:               // cancel: ignore next focus event
04390               m_isasking = -1;
04391       }
04392     }
04393   }
04394 }
04395 
04396 void KateDocument::setModifiedOnDisk( int reason )
04397 {
04398   m_modOnHdReason = reason;
04399   emit modifiedOnDisc( this, (reason > 0), reason );
04400 }
04401 
04402 class KateDocumentTmpMark
04403 {
04404   public:
04405     QString line;
04406     KTextEditor::Mark mark;
04407 };
04408 
04409 void KateDocument::reloadFile()
04410 {
04411   if ( !url().isEmpty() )
04412   {
04413     if (m_modOnHd && s_fileChangedDialogsActivated)
04414     {
04415       int i = KMessageBox::warningYesNoCancel
04416                 (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
04417                 i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes"));
04418 
04419       if ( i != KMessageBox::Yes)
04420       {
04421         if (i == KMessageBox::No)
04422         {
04423           m_modOnHd = false;
04424           m_modOnHdReason = 0;
04425           emit modifiedOnDisc (this, m_modOnHd, 0);
04426         }
04427 
04428         return;
04429       }
04430     }
04431 
04432     QValueList<KateDocumentTmpMark> tmp;
04433 
04434     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
04435     {
04436       KateDocumentTmpMark m;
04437 
04438       m.line = textLine (it.current()->line);
04439       m.mark = *it.current();
04440 
04441       tmp.append (m);
04442     }
04443 
04444     uint mode = hlMode ();
04445     bool byUser = hlSetByUser;
04446 
04447     m_storedVariables.clear();
04448 
04449     m_reloading = true;
04450     KateDocument::openURL( url() );
04451     m_reloading = false;
04452 
04453     for (uint z=0; z < tmp.size(); z++)
04454     {
04455       if (z < numLines())
04456       {
04457         if (textLine(tmp[z].mark.line) == tmp[z].line)
04458           setMark (tmp[z].mark.line, tmp[z].mark.type);
04459       }
04460     }
04461 
04462     if (byUser)
04463       setHlMode (mode);
04464   }
04465 }
04466 
04467 void KateDocument::flush ()
04468 {
04469   closeURL ();
04470 }
04471 
04472 void KateDocument::setWordWrap (bool on)
04473 {
04474   config()->setWordWrap (on);
04475 }
04476 
04477 bool KateDocument::wordWrap ()
04478 {
04479   return config()->wordWrap ();
04480 }
04481 
04482 void KateDocument::setWordWrapAt (uint col)
04483 {
04484   config()->setWordWrapAt (col);
04485 }
04486 
04487 unsigned int KateDocument::wordWrapAt ()
04488 {
04489   return config()->wordWrapAt ();
04490 }
04491 
04492 void KateDocument::applyWordWrap ()
04493 {
04494   if (hasSelection())
04495     wrapText (selectStart.line(), selectEnd.line());
04496   else
04497     wrapText (0, lastLine());
04498 }
04499 
04500 void KateDocument::setPageUpDownMovesCursor (bool on)
04501 {
04502   config()->setPageUpDownMovesCursor (on);
04503 }
04504 
04505 bool KateDocument::pageUpDownMovesCursor ()
04506 {
04507   return config()->pageUpDownMovesCursor ();
04508 }
04509 
04510 void KateDocument::exportAs(const QString& filter)
04511 {
04512   if (filter=="kate_html_export")
04513   {
04514     KURL url = KFileDialog::getSaveURL(QString::null,"text/html",0,i18n("Export File As"));
04515     if ( url.isEmpty() )
04516       return;
04517 
04518     QString filename;
04519     KTempFile tmp; // ### only used for network export
04520 
04521     if ( url.isLocalFile() )
04522       filename = url.path();
04523     else
04524       filename = tmp.name();
04525 
04526     KSaveFile *savefile=new KSaveFile(filename);
04527     if (!savefile->status())
04528     {
04529       if (exportDocumentToHTML(savefile->textStream(),filename))
04530         savefile->close();
04531       else savefile->abort();
04532       //if (!savefile->status()) --> Error
04533     }
04534 //     else
04535 //       {/*ERROR*/}
04536     delete savefile;
04537 
04538     if ( url.isLocalFile() )
04539         return;
04540 
04541     KIO::NetAccess::upload( filename, url, 0 );
04542   }
04543 }
04544 
04545 /* For now, this should become an plugin */
04546 bool KateDocument::exportDocumentToHTML(QTextStream *outputStream,const QString &name)
04547 {
04548   outputStream->setEncoding(QTextStream::UnicodeUTF8);
04549   // let's write the HTML header :
04550   (*outputStream) << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
04551   (*outputStream) << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">" << endl;
04552   (*outputStream) << "<html xmlns=\"http://www.w3.org/1999/xhtml\">" << endl;
04553   (*outputStream) << "<head>" << endl;
04554   (*outputStream) << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />" << endl;
04555   (*outputStream) << "<meta name=\"Generator\" content=\"Kate, the KDE Advanced Text Editor\" />" << endl;
04556   // for the title, we write the name of the file (/usr/local/emmanuel/myfile.cpp -> myfile.cpp)
04557   (*outputStream) << "<title>" << name.right(name.length() - name.findRev('/') -1) << "</title>" << endl;
04558   (*outputStream) << "</head>" << endl;
04559 
04560   (*outputStream) << "<body><pre>" << endl;
04561   // for each line :
04562 
04563   // some variables :
04564   bool previousCharacterWasBold = false;
04565   bool previousCharacterWasItalic = false;
04566   // when entering a new color, we'll close all the <b> & <i> tags,
04567   // for HTML compliancy. that means right after that font tag, we'll
04568   // need to reinitialize the <b> and <i> tags.
04569   bool needToReinitializeTags = false;
04570   QColor previousCharacterColor(0,0,0); // default color of HTML characters is black
04571   (*outputStream) << "<span style='color: #000000'>";
04572 
04573   for (uint curLine=0;curLine<numLines();curLine++)
04574   { // html-export that line :
04575     KateTextLine::Ptr textLine = m_buffer->plainLine(curLine);
04576     //ASSERT(textLine != NULL);
04577     // for each character of the line : (curPos is the position in the line)
04578     for (uint curPos=0;curPos<textLine->length();curPos++)
04579     {
04580       // atm hardcode default schema, later add selector to the exportAs methode :)
04581       QMemArray<KateAttribute> *attributes = m_highlight->attributes (0);
04582       KateAttribute* charAttributes = 0;
04583 
04584       if (textLine->attribute(curPos) < attributes->size())
04585         charAttributes = &attributes->at(textLine->attribute(curPos));
04586       else
04587         charAttributes = &attributes->at(0);
04588 
04589       //ASSERT(charAttributes != NULL);
04590       // let's give the color for that character :
04591       if ( (charAttributes->textColor() != previousCharacterColor))
04592       {  // the new character has a different color :
04593         // if we were in a bold or italic section, close it
04594         if (previousCharacterWasBold)
04595           (*outputStream) << "</b>";
04596         if (previousCharacterWasItalic)
04597           (*outputStream) << "</i>";
04598 
04599         // close the previous font tag :
04600         (*outputStream) << "</span>";
04601         // let's read that color :
04602         int red, green, blue;
04603         // getting the red, green, blue values of the color :
04604         charAttributes->textColor().rgb(&red, &green, &blue);
04605         (*outputStream) << "<span style='color: #"
04606               << ( (red < 0x10)?"0":"")  // need to put 0f, NOT f for instance. don't touch 1f.
04607               << QString::number(red, 16) // html wants the hex value here (hence the 16)
04608               << ( (green < 0x10)?"0":"")
04609               << QString::number(green, 16)
04610               << ( (blue < 0x10)?"0":"")
04611               << QString::number(blue, 16)
04612               << "'>";
04613         // we need to reinitialize the bold/italic status, since we closed all the tags
04614         needToReinitializeTags = true;
04615       }
04616       // bold status :
04617       if ( (needToReinitializeTags && charAttributes->bold()) ||
04618           (!previousCharacterWasBold && charAttributes->bold()) )
04619         // we enter a bold section
04620         (*outputStream) << "<b>";
04621       if ( !needToReinitializeTags && (previousCharacterWasBold && !charAttributes->bold()) )
04622         // we leave a bold section
04623         (*outputStream) << "</b>";
04624 
04625       // italic status :
04626       if ( (needToReinitializeTags && charAttributes->italic()) ||
04627            (!previousCharacterWasItalic && charAttributes->italic()) )
04628         // we enter an italic section
04629         (*outputStream) << "<i>";
04630       if ( !needToReinitializeTags && (previousCharacterWasItalic && !charAttributes->italic()) )
04631         // we leave an italic section
04632         (*outputStream) << "</i>";
04633 
04634       // write the actual character :
04635       (*outputStream) << HTMLEncode(textLine->getChar(curPos));
04636 
04637       // save status for the next character :
04638       previousCharacterWasItalic = charAttributes->italic();
04639       previousCharacterWasBold = charAttributes->bold();
04640       previousCharacterColor = charAttributes->textColor();
04641       needToReinitializeTags = false;
04642     }
04643     // finish the line :
04644     (*outputStream) << endl;
04645   }
04646 
04647   // Be good citizens and close our tags
04648   if (previousCharacterWasBold)
04649     (*outputStream) << "</b>";
04650   if (previousCharacterWasItalic)
04651     (*outputStream) << "</i>";
04652 
04653   // HTML document end :
04654   (*outputStream) << "</span>";  // i'm guaranteed a span is started (i started one at the beginning of the output).
04655   (*outputStream) << "</pre></body>";
04656   (*outputStream) << "</html>";
04657   // close the file :
04658   return true;
04659 }
04660 
04661 QString KateDocument::HTMLEncode(QChar theChar)
04662 {
04663   switch (theChar.latin1())
04664   {
04665   case '>':
04666     return QString("&gt;");
04667   case '<':
04668     return QString("&lt;");
04669   case '&':
04670     return QString("&amp;");
04671   };
04672   return theChar;
04673 }
04674 
04675 Kate::ConfigPage *KateDocument::colorConfigPage (QWidget *p)
04676 {
04677   return (Kate::ConfigPage*) new KateSchemaConfigPage (p, this);
04678 }
04679 
04680 Kate::ConfigPage *KateDocument::viewDefaultsConfigPage (QWidget *p)
04681 {
04682   return (Kate::ConfigPage*) new KateViewDefaultsConfig(p);
04683 }
04684 
04685 Kate::ConfigPage *KateDocument::fontConfigPage (QWidget *p)
04686 {
04687   return (Kate::ConfigPage*) new KateSchemaConfigPage ( p );
04688 }
04689 
04690 Kate::ConfigPage *KateDocument::indentConfigPage (QWidget *p)
04691 {
04692   return (Kate::ConfigPage*) new KateIndentConfigTab(p);
04693 }
04694 
04695 Kate::ConfigPage *KateDocument::selectConfigPage (QWidget *p)
04696 {
04697   return (Kate::ConfigPage*) new KateSelectConfigTab(p);
04698 }
04699 
04700 Kate::ConfigPage *KateDocument::editConfigPage (QWidget *p)
04701 {
04702   return (Kate::ConfigPage*) new KateEditConfigTab(p);
04703 }
04704 
04705 Kate::ConfigPage *KateDocument::keysConfigPage (QWidget *p)
04706 {
04707   return (Kate::ConfigPage*) new KateEditKeyConfiguration(p, this);
04708 }
04709 
04710 Kate::ConfigPage *KateDocument::hlConfigPage (QWidget *p)
04711 {
04712   return (Kate::ConfigPage*) new KateHlConfigPage (p);
04713 }
04714 
04715 Kate::ConfigPage *KateDocument::saveConfigPage(QWidget *p)
04716 {
04717   return (Kate::ConfigPage*) new KateSaveConfigTab(p);
04718 }
04719 
04720 Kate::ActionMenu *KateDocument::hlActionMenu (const QString& text, QObject* parent, const char* name)
04721 {
04722   KateViewHighlightAction *menu = new KateViewHighlightAction (text, parent, name);
04723   menu->setWhatsThis(i18n("Here you can choose how the current document should be highlighted."));
04724   menu->updateMenu (this);
04725 
04726   return (Kate::ActionMenu *)menu;
04727 }
04728 
04729 Kate::ActionMenu *KateDocument::exportActionMenu (const QString& text, QObject* parent, const char* name)
04730 {
04731   KateExportAction *menu = new KateExportAction (text, parent, name);
04732   menu->updateMenu (this);
04733   menu->setWhatsThis(i18n("This command allows you to export the current document"
04734     " with all highlighting information into a markup document, e.g. HTML."));
04735   return (Kate::ActionMenu *)menu;
04736 }
04737 
04738 void KateDocument::dumpRegionTree()
04739 {
04740   m_buffer->foldingTree()->debugDump();
04741 }
04742 
04743 unsigned int KateDocument::getRealLine(unsigned int virtualLine)
04744 {
04745   return m_buffer->lineNumber (virtualLine);
04746 }
04747 
04748 unsigned int KateDocument::getVirtualLine(unsigned int realLine)
04749 {
04750   return m_buffer->lineVisibleNumber (realLine);
04751 }
04752 
04753 unsigned int KateDocument::visibleLines ()
04754 {
04755   return m_buffer->countVisible ();
04756 }
04757 
04758 KateTextLine::Ptr KateDocument::kateTextLine(uint i)
04759 {
04760   return m_buffer->line (i);
04761 }
04762 
04763 KateTextLine::Ptr KateDocument::plainKateTextLine(uint i)
04764 {
04765   return m_buffer->plainLine (i);
04766 }
04767 //END
04768 
04769 //BEGIN KTextEditor::CursorInterface stuff
04770 
04771 KTextEditor::Cursor *KateDocument::createCursor ( )
04772 {
04773   return new KateSuperCursor (this, false, 0, 0, this);
04774 }
04775 
04776 void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range)
04777 {
04778   if (view)
04779     view->tagLines(range->start(), range->end());
04780   else
04781     tagLines(range->start(), range->end());
04782 }
04783 
04784 //
04785 // Spellchecking IN again
04786 //
04787 void KateDocument::spellcheck()
04788 {
04789   if( !isReadWrite() || text().isEmpty())
04790     return;
04791 
04792   QString mt = mimeType()/*->name()*/;
04793 
04794   KSpell::SpellerType type = KSpell::Text;
04795   if ( mt == "text/x-tex" || mt == "text/x-latex" )
04796     type = KSpell::TeX;
04797   else if ( mt == "text/html" || mt == "text/xml" )
04798     type = KSpell::HTML;
04799 
04800   m_kspell = new KSpell( 0, i18n("Spellcheck"),
04801                          this, SLOT(ready(KSpell *)), 0, true, false, type );
04802 
04803   connect( m_kspell, SIGNAL(death()),
04804            this, SLOT(spellCleanDone()) );
04805 
04806   connect( m_kspell, SIGNAL(misspelling(const QString&, const QStringList&, unsigned int)),
04807            this, SLOT(misspelling(const QString&, const QStringList&, unsigned int)) );
04808   connect( m_kspell, SIGNAL(corrected(const QString&, const QString&, unsigned int)),
04809            this, SLOT(corrected(const QString&, const QString&, unsigned int)) );
04810   connect( m_kspell, SIGNAL(done(const QString&)),
04811            this, SLOT(spellResult(const QString&)) );
04812 }
04813 
04814 void KateDocument::ready(KSpell *)
04815 {
04816   m_kspell->setProgressResolution( 1 );
04817 
04818   m_kspell->check( text() );
04819 
04820   kdDebug () << "SPELLING READY STATUS: " << m_kspell->status () << endl;
04821 }
04822 
04823 void KateDocument::locatePosition( uint pos, uint& line, uint& col )
04824 {
04825   uint cnt = 0;
04826 
04827   line = col = 0;
04828 
04829   // Find pos  -- CHANGEME: store the last found pos's cursor
04830   //   and do these searched relative to that to
04831   //   (significantly) increase the speed of the spellcheck
04832   for( ; line < numLines() && cnt <= pos; line++ )
04833     cnt += lineLength(line) + 1;
04834 
04835   line--;
04836   col = pos - (cnt - lineLength(line)) + 1;
04837 }
04838 
04839 void KateDocument::misspelling( const QString& origword, const QStringList&, unsigned int pos )
04840 {
04841   uint line, col;
04842 
04843   locatePosition( pos, line, col );
04844 
04845   if (activeView())
04846     activeView()->setCursorPositionInternal (line, col, 1);
04847 
04848   setSelection( line, col, line, col + origword.length() );
04849 }
04850 
04851 void KateDocument::corrected( const QString& originalword, const QString& newword, unsigned int pos )
04852 {
04853   uint line, col;
04854 
04855   locatePosition( pos, line, col );
04856 
04857   removeText( line, col, line, col + originalword.length() );
04858   insertText( line, col, newword );
04859 }
04860 
04861 void KateDocument::spellResult( const QString& )
04862 {
04863   clearSelection();
04864   m_kspell->cleanUp();
04865 }
04866 
04867 void KateDocument::spellCleanDone()
04868 {
04869   KSpell::spellStatus status = m_kspell->status();
04870 
04871   if( status == KSpell::Error ) {
04872     KMessageBox::sorry( 0,
04873       i18n("ISpell could not be started. "
04874            "Please make sure you have ISpell "
04875            "properly configured and in your PATH."));
04876   } else if( status == KSpell::Crashed ) {
04877     KMessageBox::sorry( 0,
04878       i18n("ISpell seems to have crashed."));
04879   }
04880 
04881   delete m_kspell;
04882   m_kspell = 0;
04883 
04884   kdDebug () << "SPELLING END" << endl;
04885 }
04886 //END
04887 
04888 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
04889 {
04890   m_buffer->lineInfo(info,line);
04891 }
04892 
04893 KateCodeFoldingTree *KateDocument::foldingTree ()
04894 {
04895   return m_buffer->foldingTree();
04896 }
04897 
04898 void KateDocument::setEncoding (const QString &e)
04899 {
04900   m_config->setEncoding(e);
04901 }
04902 
04903 QString KateDocument::encoding() const
04904 {
04905   return m_config->encoding();
04906 }
04907 
04908 void KateDocument::updateConfig ()
04909 {
04910   emit undoChanged ();
04911   tagAll();
04912 
04913   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
04914   {
04915     view->updateDocumentConfig ();
04916   }
04917 
04918   // switch indenter if needed
04919   if (m_indenter->modeNumber() != m_config->indentationMode())
04920   {
04921     delete m_indenter;
04922     m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() );
04923   }
04924 
04925   m_indenter->updateConfig();
04926 
04927   m_buffer->setTabWidth (config()->tabWidth());
04928 
04929   // plugins
04930   for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
04931   {
04932     if (config()->plugin (i))
04933       loadPlugin (i);
04934     else
04935       unloadPlugin (i);
04936   }
04937 }
04938 
04939 //BEGIN Variable reader
04940 // "local variable" feature by anders, 2003
04941 /* TODO
04942       add config options (how many lines to read, on/off)
04943       add interface for plugins/apps to set/get variables
04944       add view stuff
04945 */
04946 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
04947 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
04948 
04949 void KateDocument::readVariables(bool onlyViewAndRenderer)
04950 {
04951   if (!onlyViewAndRenderer)
04952     m_config->configStart();
04953 
04954   // views!
04955   KateView *v;
04956   for (v = m_views.first(); v != 0L; v= m_views.next() )
04957   {
04958     v->config()->configStart();
04959     v->renderer()->config()->configStart();
04960   }
04961   // read a number of lines in the top/bottom of the document
04962   for (uint i=0; i < QMIN( 9, numLines() ); ++i )
04963   {
04964     readVariableLine( textLine( i ), onlyViewAndRenderer );
04965   }
04966   if ( numLines() > 10 )
04967   {
04968     for ( uint i = QMAX(10, numLines() - 10); i < numLines(); ++i )
04969     {
04970       readVariableLine( textLine( i ), onlyViewAndRenderer );
04971     }
04972   }
04973 
04974   if (!onlyViewAndRenderer)
04975     m_config->configEnd();
04976 
04977   for (v = m_views.first(); v != 0L; v= m_views.next() )
04978   {
04979     v->config()->configEnd();
04980     v->renderer()->config()->configEnd();
04981   }
04982 }
04983 
04984 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
04985 {
04986   if ( kvLine.search( t ) > -1 )
04987   {
04988     QStringList vvl; // view variable names
04989     vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
04990         << "line-numbers" << "icon-border" << "folding-markers"
04991         << "bookmark-sorting" << "auto-center-lines"
04992         << "icon-bar-color"
04993         // renderer
04994         << "background-color" << "selection-color"
04995         << "current-line-color" << "bracket-highlight-color"
04996         << "word-wrap-marker-color"
04997         << "font" << "font-size" << "scheme";
04998     int p( 0 );
04999     QString s = kvLine.cap(1);
05000     QString  var, val;
05001     while ( (p = kvVar.search( s, p )) > -1 )
05002     {
05003       p += kvVar.matchedLength();
05004       var = kvVar.cap( 1 );
05005       val = kvVar.cap( 2 ).stripWhiteSpace();
05006       bool state; // store booleans here
05007       int n; // store ints here
05008 
05009       // only apply view & renderer config stuff
05010       if (onlyViewAndRenderer)
05011       {
05012         if ( vvl.contains( var ) ) // FIXME define above
05013           setViewVariable( var, val );
05014       }
05015       else
05016       {
05017         // BOOL  SETTINGS
05018         if ( var == "word-wrap" && checkBoolValue( val, &state ) )
05019           setWordWrap( state ); // ??? FIXME CHECK
05020         else if ( var == "block-selection"  && checkBoolValue( val, &state ) )
05021           setBlockSelectionMode( state );
05022         // KateConfig::configFlags
05023         // FIXME should this be optimized to only a few calls? how?
05024         else if ( var == "auto-indent" && checkBoolValue( val, &state ) )
05025           m_config->setConfigFlags( KateDocumentConfig::cfAutoIndent, state );
05026         else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
05027           m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
05028         else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
05029           m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
05030         else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
05031           m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
05032         else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
05033           m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
05034         else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
05035           m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
05036         else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
05037           m_config->setConfigFlags( KateDocumentConfig::cfPersistent, state );
05038         else if ( var == "keep-selection" && checkBoolValue( val, &state ) )
05039           m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
05040         else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
05041           m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
05042         else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) )
05043           m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state );
05044         else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
05045           m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
05046         else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
05047           m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
05048         else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
05049           m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
05050         else if ( var == "space-indent" && checkBoolValue( val, &state ) )
05051           m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state );
05052         else if ( var == "smart-home" && checkBoolValue( val, &state ) )
05053           m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
05054         else if ( var == "replace-tabs-save" && checkBoolValue( val, &state ) )
05055           m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabs, state );
05056         else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
05057           m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
05058         else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) )
05059           m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state);
05060 
05061         // INTEGER SETTINGS
05062         else if ( var == "tab-width" && checkIntValue( val, &n ) )
05063           m_config->setTabWidth( n );
05064         else if ( var == "indent-width"  && checkIntValue( val, &n ) )
05065           m_config->setIndentationWidth( n );
05066         else if ( var == "indent-mode" )
05067         {
05068           if ( checkIntValue( val, &n ) )
05069             m_config->setIndentationMode( n );
05070           else
05071             m_config->setIndentationMode( KateAutoIndent::modeNumber( val) );
05072         }
05073         else if ( var == "word-wrap-column" && n > 0  && checkIntValue( val, &n ) ) // uint, but hard word wrap at 0 will be no fun ;)
05074           m_config->setWordWrapAt( n );
05075         else if ( var == "undo-steps"  && n >= 0  && checkIntValue( val, &n ) )
05076           setUndoSteps( n );
05077 
05078         // STRING SETTINGS
05079         else if ( var == "eol" || var == "end-of-line" )
05080         {
05081           QStringList l;
05082           l << "unix" << "dos" << "mac";
05083           if ( (n = l.findIndex( val.lower() )) != -1 )
05084             m_config->setEol( n );
05085         }
05086         else if ( var == "encoding" )
05087           m_config->setEncoding( val );
05088         else if ( var == "syntax" || var == "hl" )
05089         {
05090           for ( uint i=0; i < hlModeCount(); i++ )
05091           {
05092             if ( hlModeName( i ) == val )
05093             {
05094               setHlMode( i );
05095               break;
05096             }
05097           }
05098         }
05099 
05100         // VIEW SETTINGS
05101         else if ( vvl.contains( var ) )
05102           setViewVariable( var, val );
05103         else
05104         {
05105           m_storedVariables.insert( var, val );
05106           emit variableChanged( var, val );
05107         }
05108       }
05109     }
05110   }
05111 }
05112 
05113 void KateDocument::setViewVariable( QString var, QString val )
05114 {
05115   KateView *v;
05116   bool state;
05117   int n;
05118   QColor c;
05119   for (v = m_views.first(); v != 0L; v= m_views.next() )
05120   {
05121     if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
05122       v->config()->setDynWordWrap( state );
05123     //else if ( var = "dynamic-word-wrap-indicators" )
05124     else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
05125       v->config()->setLineNumbers( state );
05126     else if (var == "icon-border" && checkBoolValue( val, &state ) )
05127       v->config()->setIconBar( state );
05128     else if (var == "folding-markers" && checkBoolValue( val, &state ) )
05129       v->config()->setFoldingBar( state );
05130     else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
05131       v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
05132     else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
05133       v->renderer()->config()->setIconBarColor( c );
05134     // RENDERER
05135     else if ( var == "background-color" && checkColorValue( val, c ) )
05136       v->renderer()->config()->setBackgroundColor( c );
05137     else if ( var == "selection-color" && checkColorValue( val, c ) )
05138       v->renderer()->config()->setSelectionColor( c );
05139     else if ( var == "current-line-color" && checkColorValue( val, c ) )
05140       v->renderer()->config()->setHighlightedLineColor( c );
05141     else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
05142       v->renderer()->config()->setHighlightedBracketColor( c );
05143     else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
05144       v->renderer()->config()->setWordWrapMarkerColor( c );
05145     else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
05146     {
05147       QFont _f( *v->renderer()->config()->font(  ) );
05148 
05149       if ( var == "font" )
05150       {
05151         _f.setFamily( val );
05152         _f.setFixedPitch( QFont( val ).fixedPitch() );
05153       }
05154       else
05155         _f.setPointSize( n );
05156 
05157       v->renderer()->config()->setFont( _f );
05158     }
05159     else if ( var == "scheme" )
05160     {
05161       v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) );
05162     }
05163   }
05164 }
05165 
05166 bool KateDocument::checkBoolValue( QString val, bool *result )
05167 {
05168   val = val.stripWhiteSpace().lower();
05169   QStringList l;
05170   l << "1" << "on" << "true";
05171   if ( l.contains( val ) )
05172   {
05173     *result = true;
05174     return true;
05175   }
05176   l.clear();
05177   l << "0" << "off" << "false";
05178   if ( l.contains( val ) )
05179   {
05180     *result = false;
05181     return true;
05182   }
05183   return false;
05184 }
05185 
05186 bool KateDocument::checkIntValue( QString val, int *result )
05187 {
05188   bool ret( false );
05189   *result = val.toInt( &ret );
05190   return ret;
05191 }
05192 
05193 bool KateDocument::checkColorValue( QString val, QColor &c )
05194 {
05195   c.setNamedColor( val );
05196   return c.isValid();
05197 }
05198 
05199 // KTextEditor::variable
05200 QString KateDocument::variable( const QString &name ) const
05201 {
05202   if ( m_storedVariables.contains( name ) )
05203     return m_storedVariables[ name ];
05204 
05205   return "";
05206 }
05207 
05208 //END
05209 
05210 void KateDocument::slotModOnHdDirty (const QString &path)
05211 {
05212   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1))
05213   {
05214     // compare md5 with the one we have (if we have one)
05215     if ( ! m_digest.isEmpty() )
05216     {
05217       QCString tmp;
05218       if ( createDigest( tmp ) && tmp == m_digest )
05219         return;
05220     }
05221 
05222     m_modOnHd = true;
05223     m_modOnHdReason = 1;
05224 
05225     // reenable dialog if not running atm
05226     if (m_isasking == -1)
05227       m_isasking = false;
05228 
05229     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
05230   }
05231 }
05232 
05233 void KateDocument::slotModOnHdCreated (const QString &path)
05234 {
05235   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2))
05236   {
05237     m_modOnHd = true;
05238     m_modOnHdReason = 2;
05239 
05240     // reenable dialog if not running atm
05241     if (m_isasking == -1)
05242       m_isasking = false;
05243 
05244     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
05245   }
05246 }
05247 
05248 void KateDocument::slotModOnHdDeleted (const QString &path)
05249 {
05250   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3))
05251   {
05252     m_modOnHd = true;
05253     m_modOnHdReason = 3;
05254 
05255     // reenable dialog if not running atm
05256     if (m_isasking == -1)
05257       m_isasking = false;
05258 
05259     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
05260   }
05261 }
05262 
05263 bool KateDocument::createDigest( QCString &result )
05264 {
05265   bool ret = false;
05266   result = "";
05267   if ( url().isLocalFile() )
05268   {
05269     QFile f ( url().path() );
05270     if ( f.open( IO_ReadOnly) )
05271     {
05272       KMD5 md5;
05273       ret = md5.update( f );
05274       md5.hexDigest( result );
05275       f.close();
05276     }
05277   }
05278   return ret;
05279 }
05280 
05281 QString KateDocument::reasonedMOHString() const
05282 {
05283   QString reason;
05284   if ( m_modOnHdReason == 1 )
05285     reason = i18n("modified");
05286   else if ( m_modOnHdReason == 2 )
05287     reason = i18n("created");
05288   else if ( m_modOnHdReason == 3 )
05289     reason = i18n("deleted");
05290 
05291   return i18n("The file '%1' was changed (%2) on disk by another program!").arg( url().prettyURL() ).arg( reason );
05292 }
05293 
05294 
05295 void KateDocument::removeTrailingSpace( uint line )
05296 {
05297   // remove trailing spaces from left line if required
05298   if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn )
05299   {
05300     KateTextLine::Ptr ln = kateTextLine( line );
05301 
05302     if ( ! ln ) return;
05303 
05304     if ( line == activeView()->cursorLine()
05305          && activeView()->cursorColumnReal() >= (uint)QMAX(0,ln->lastChar()) )
05306       return;
05307 
05308     if ( ln->length() )
05309     {
05310       uint p = ln->lastChar() + 1;
05311       uint l = ln->length() - p;
05312       if ( l )
05313         editRemoveText( line, p, l);
05314     }
05315   }
05316 }
05317 
05318 bool KateDocument::wrapCursor ()
05319 {
05320   return !blockSelect && (configFlags() & KateDocument::cfWrapCursor);
05321 }
05322 
05323 void KateDocument::updateFileType (int newType, bool user)
05324 {
05325   if (user || !m_fileTypeSetByUser)
05326   {
05327     const KateFileType *t = 0;
05328     if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType)))
05329     {
05330       m_fileType = newType;
05331 
05332       if (t)
05333       {
05334         m_config->configStart();
05335         // views!
05336         KateView *v;
05337         for (v = m_views.first(); v != 0L; v= m_views.next() )
05338         {
05339           v->config()->configStart();
05340           v->renderer()->config()->configStart();
05341         }
05342 
05343         readVariableLine( t->varLine );
05344 
05345         m_config->configEnd();
05346         for (v = m_views.first(); v != 0L; v= m_views.next() )
05347         {
05348           v->config()->configEnd();
05349           v->renderer()->config()->configEnd();
05350         }
05351       }
05352     }
05353   }
05354 }
05355 
05356 uint KateDocument::documentNumber () const
05357 {
05358   return KTextEditor::Document::documentNumber ();
05359 }
05360 
05361 
05362 
05363 
05364 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
05365       *handled=true;
05366       *abortClosing=true;
05367       if (m_url.isEmpty())
05368       {
05369         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
05370                 QString::null,QString::null,0,i18n("Save File"));
05371 
05372         if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) {
05373                 *abortClosing=true;
05374                 return;
05375         }
05376         setEncoding( res.encoding );
05377           saveAs( res.URLs.first() );
05378         *abortClosing=false;
05379       }
05380       else
05381       {
05382           save();
05383           *abortClosing=false;
05384       }
05385 
05386 }
05387 
05388 bool KateDocument::checkOverwrite( KURL u )
05389 {
05390   if( !u.isLocalFile() )
05391     return true;
05392 
05393   QFileInfo info( u.path() );
05394   if( !info.exists() )
05395     return true;
05396 
05397   return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0,
05398     i18n( "A file named \"%1\" already exists. "
05399           "Are you sure you want to overwrite it?" ).arg( info.fileName() ),
05400     i18n( "Overwrite File?" ),
05401     i18n( "&Overwrite" ) );
05402 }
05403 
05404 void KateDocument::setDefaultEncoding (const QString &encoding)
05405 {
05406   s_defaultEncoding = encoding;
05407 }
05408 
05409 void KateDocument::setIMSelectionValue( uint imStartLine, uint imStart, uint imEnd,
05410                                         uint imSelStart, uint imSelEnd, bool imComposeEvent )
05411 {
05412   m_imStartLine = imStartLine;
05413   m_imStart = imStart;
05414   m_imEnd = imEnd;
05415   m_imSelStart = imSelStart;
05416   m_imSelEnd = imSelEnd;
05417   m_imComposeEvent = imComposeEvent;
05418 }
05419 
05420 void KateDocument::getIMSelectionValue( uint *imStartLine, uint *imStart, uint *imEnd,
05421                                         uint *imSelStart, uint *imSelEnd )
05422 {
05423   *imStartLine = m_imStartLine;
05424   *imStart = m_imStart;
05425   *imEnd = m_imEnd;
05426   *imSelStart = m_imSelStart;
05427   *imSelEnd = m_imSelEnd;
05428 }
05429 
05430 // 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.90.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 30 10:24:43 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003