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