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 
03320       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03321       int colX = textLine->cursorX(col, config()->tabWidth());
03322       int pos = textLine->firstChar();
03323       if (pos > 0)
03324         pos = textLine->cursorX(pos, config()->tabWidth());
03325 
03326       if (pos < 0 || pos >= (int)colX)
03327       {
03328         // only spaces on left side of cursor
03329         // search a line with less spaces
03330         int y = line;
03331         while (--y >= 0)
03332         {
03333           textLine = m_buffer->plainLine(y);
03334           pos = textLine->firstChar();
03335 
03336           if (pos >= 0)
03337           {
03338             pos = textLine->cursorX(pos, config()->tabWidth());
03339             if (pos < (int)colX)
03340             {
03341               replaceWithOptimizedSpace(line, col, pos, config()->configFlags());
03342               break;
03343             }
03344           }
03345         }
03346         if (y < 0) {
03347           // FIXME: what shoud we do in this case?
03348           removeText(line, 0, line, col);
03349         }
03350       }
03351       else
03352         removeText(line, col-1, line, col);
03353     }
03354   }
03355   else
03356   {
03357     // col == 0: wrap to previous line
03358     if (line >= 1)
03359     {
03360       KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
03361       if (config()->wordWrap() && textLine->endingWith(QString::fromLatin1(" ")))
03362       {
03363         // gg: in hard wordwrap mode, backspace must also eat the trailing space
03364         removeText (line-1, textLine->length()-1, line, 0);
03365       }
03366       else
03367         removeText (line-1, textLine->length(), line, 0);
03368     }
03369   }
03370 
03371   emit backspacePressed();
03372 }
03373 
03374 void KateDocument::del( const KateTextCursor& c )
03375 {
03376   if ( !(config()->configFlags() & cfPersistent) && hasSelection() ) {
03377     removeSelectedText();
03378     return;
03379   }
03380 
03381   if( c.col() < (int) m_buffer->plainLine(c.line())->length())
03382   {
03383     removeText(c.line(), c.col(), c.line(), c.col()+1);
03384   }
03385   else if ( c.line() < lastLine() )
03386   {
03387     removeText(c.line(), c.col(), c.line()+1, 0);
03388   }
03389 }
03390 
03391 void KateDocument::cut()
03392 {
03393   if (!hasSelection())
03394     return;
03395 
03396   copy();
03397   removeSelectedText();
03398 }
03399 
03400 void KateDocument::copy()
03401 {
03402   kdDebug(13020) << "in katedocument::copy()" << endl;
03403   if (!hasSelection())
03404     return;
03405 #ifndef QT_NO_MIMECLIPBOARD
03406   QClipboard *cb = QApplication::clipboard();
03407 
03408   KMultipleDrag *drag = new KMultipleDrag();
03409   QString htmltext;
03410   if(!cb->selectionModeEnabled())
03411     htmltext = selectionAsHtml();
03412 
03413   if(!htmltext.isEmpty()) {
03414     QTextDrag *htmltextdrag = new QTextDrag(htmltext) ;
03415     htmltextdrag->setSubtype("html");
03416 
03417     drag->addDragObject( htmltextdrag);
03418   }
03419   drag->addDragObject( new QTextDrag( selection()));
03420 
03421   QApplication::clipboard()->setData(drag);
03422 #else
03423   QApplication::clipboard()->setText(selection ());
03424 #endif
03425 }
03426 
03427 void KateDocument::paste ( KateView* view )
03428 {
03429   QString s = QApplication::clipboard()->text();
03430 
03431   if (s.isEmpty())
03432     return;
03433 
03434   uint lines = s.contains (QChar ('\n'));
03435 
03436   m_undoDontMerge = true;
03437 
03438   editStart ();
03439 
03440   if (!(config()->configFlags() & KateDocument::cfPersistent) && hasSelection() )
03441     removeSelectedText();
03442 
03443   uint line = view->cursorLine ();
03444   uint column = view->cursorColumnReal ();
03445 
03446   insertText ( line, column, s, blockSelect );
03447 
03448   editEnd();
03449 
03450   // move cursor right for block select, as the user is moved right internal
03451   // even in that case, but user expects other behavior in block selection
03452   // mode !
03453   if (blockSelect)
03454     view->setCursorPositionInternal (line+lines, column);
03455 
03456   if (m_indenter->canProcessLine())
03457   {
03458     editStart();
03459 
03460     KateDocCursor begin(line, 0, this);
03461     KateDocCursor end(line + lines, 0, this);
03462 
03463     m_indenter->processSection (begin, end);
03464 
03465     editEnd();
03466   }
03467 
03468   if (!blockSelect) emit charactersSemiInteractivelyInserted (line, column, s);
03469   m_undoDontMerge = true;
03470 }
03471 
03472 void KateDocument::selectWord( const KateTextCursor& cursor )
03473 {
03474   int start, end, len;
03475 
03476   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03477 
03478   if (!textLine)
03479     return;
03480 
03481   len = textLine->length();
03482   start = end = cursor.col();
03483   while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
03484   while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(start - 1))) end++;
03485   if (end <= start) return;
03486 
03487   if (!(config()->configFlags() & KateDocument::cfKeepSelection))
03488     clearSelection ();
03489 
03490   setSelection (cursor.line(), start, cursor.line(), end);
03491 }
03492 
03493 void KateDocument::selectLine( const KateTextCursor& cursor )
03494 {
03495   if (!(config()->configFlags() & KateDocument::cfKeepSelection))
03496     clearSelection ();
03497 
03498   setSelection (cursor.line(), 0, cursor.line()+1, 0);
03499 }
03500 
03501 void KateDocument::selectLength( const KateTextCursor& cursor, int length )
03502 {
03503   int start, end;
03504 
03505   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03506   start = cursor.col();
03507   end = start + length;
03508   if (end <= start) return;
03509 
03510   if (!(config()->configFlags() & KateDocument::cfKeepSelection))
03511     clearSelection ();
03512   setSelection (cursor.line(), start, cursor.line(), end);
03513 }
03514 
03515 void KateDocument::insertIndentChars ( KateView *view )
03516 {
03517   editStart ();
03518 
03519   QString s;
03520   if (config()->configFlags() & KateDocument::cfSpaceIndent)
03521   {
03522     int width = config()->indentationWidth();
03523     s.fill (' ', width - (view->cursorColumnReal() % width));
03524   }
03525   else
03526     s.append ('\t');
03527 
03528   insertText (view->cursorLine(), view->cursorColumnReal(), s);
03529 
03530   editEnd ();
03531 }
03532 
03533 void KateDocument::indent ( KateView *, uint line, int change)
03534 {
03535   editStart ();
03536 
03537   if (!hasSelection())
03538   {
03539     // single line
03540     optimizeLeadingSpace(line, config()->configFlags(), change);
03541   }
03542   else
03543   {
03544     int sl = selectStart.line();
03545     int el = selectEnd.line();
03546     int ec = selectEnd.col();
03547 
03548     if ((ec == 0) && ((el-1) >= 0))
03549     {
03550       el--; /* */
03551     }
03552 
03553     if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) {
03554       // unindent so that the existing indent profile doesn't get screwed
03555       // if any line we may unindent is already full left, don't do anything
03556       int adjustedChange = -change;
03557 
03558       for (line = sl; (int) line <= el && adjustedChange > 0; line++) {
03559         KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03560         int firstChar = textLine->firstChar();
03561         if (firstChar >= 0 && (lineSelected(line) || lineHasSelected(line))) {
03562           int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth();
03563           if (maxUnindent < adjustedChange)
03564             adjustedChange = maxUnindent;
03565         }
03566       }
03567 
03568       change = -adjustedChange;
03569     }
03570 
03571     for (line = sl; (int) line <= el; line++) {
03572       if (lineSelected(line) || lineHasSelected(line)) {
03573         optimizeLeadingSpace(line, config()->configFlags(), change);
03574       }
03575     }
03576   }
03577 
03578   editEnd ();
03579 }
03580 
03581 void KateDocument::align(uint line)
03582 {
03583   if (m_indenter->canProcessLine())
03584   {
03585     editStart ();
03586 
03587     if (!hasSelection())
03588     {
03589       KateDocCursor curLine(line, 0, this);
03590       m_indenter->processLine (curLine);
03591       editEnd ();
03592       activeView()->setCursorPosition (line, curLine.col());
03593     }
03594     else
03595     {
03596       m_indenter->processSection(selectStart, selectEnd);
03597       editEnd ();
03598     }
03599   }
03600 }
03601 
03602 /*
03603   Optimize the leading whitespace for a single line.
03604   If change is > 0, it adds indentation units (indentationChars)
03605   if change is == 0, it only optimizes
03606   If change is < 0, it removes indentation units
03607   This will be used to indent, unindent, and optimal-fill a line.
03608   If excess space is removed depends on the flag cfKeepExtraSpaces
03609   which has to be set by the user
03610 */
03611 void KateDocument::optimizeLeadingSpace(uint line, int flags, int change)
03612 {
03613   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03614 
03615   int first_char = textline->firstChar();
03616 
03617   int w = 0;
03618   if (flags & KateDocument::cfSpaceIndent)
03619     w = config()->indentationWidth();
03620   else
03621     w = config()->tabWidth();
03622 
03623   if (first_char < 0)
03624     first_char = textline->length();
03625 
03626   int space =  textline->cursorX(first_char, config()->tabWidth()) + change * w;
03627   if (space < 0)
03628     space = 0;
03629 
03630   if (!(flags & KateDocument::cfKeepExtraSpaces))
03631   {
03632     uint extra = space % w;
03633 
03634     space -= extra;
03635     if (extra && change < 0) {
03636       // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide)
03637       space += w;
03638     }
03639   }
03640 
03641   //kdDebug(13020)  << "replace With Op: " << line << " " << first_char << " " << space << endl;
03642   replaceWithOptimizedSpace(line, first_char, space, flags);
03643 }
03644 
03645 void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags)
03646 {
03647   uint length;
03648   QString new_space;
03649 
03650   if (flags & KateDocument::cfSpaceIndent && ! (flags & KateDocumentConfig::cfMixedIndent) ) {
03651     length = space;
03652     new_space.fill(' ', length);
03653   }
03654   else {
03655     length = space / config()->tabWidth();
03656     new_space.fill('\t', length);
03657 
03658     QString extra_space;
03659     extra_space.fill(' ', space % config()->tabWidth());
03660     length += space % config()->tabWidth();
03661     new_space += extra_space;
03662   }
03663 
03664   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03665   uint change_from;
03666   for (change_from = 0; change_from < upto_column && change_from < length; change_from++) {
03667     if (textline->getChar(change_from) != new_space[change_from])
03668       break;
03669   }
03670 
03671   editStart();
03672 
03673   if (change_from < upto_column)
03674     removeText(line, change_from, line, upto_column);
03675 
03676   if (change_from < length)
03677     insertText(line, change_from, new_space.right(length - change_from));
03678 
03679   editEnd();
03680 }
03681 
03682 /*
03683   Remove a given string at the begining
03684   of the current line.
03685 */
03686 bool KateDocument::removeStringFromBegining(int line, QString &str)
03687 {
03688   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03689 
03690   int index = 0;
03691   bool there = false;
03692 
03693   if (textline->startingWith(str))
03694     there = true;
03695   else
03696   {
03697     index = textline->firstChar ();
03698 
03699     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03700       there = true;
03701   }
03702 
03703   if (there)
03704   {
03705     // Remove some chars
03706     removeText (line, index, line, index+str.length());
03707   }
03708 
03709   return there;
03710 }
03711 
03712 /*
03713   Remove a given string at the end
03714   of the current line.
03715 */
03716 bool KateDocument::removeStringFromEnd(int line, QString &str)
03717 {
03718   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03719 
03720   int index = 0;
03721   bool there = false;
03722 
03723   if(textline->endingWith(str))
03724   {
03725     index = textline->length() - str.length();
03726     there = true;
03727   }
03728   else
03729   {
03730     index = textline->lastChar ()-str.length()+1;
03731 
03732     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03733       there = true;
03734   }
03735 
03736   if (there)
03737   {
03738     // Remove some chars
03739     removeText (line, index, line, index+str.length());
03740   }
03741 
03742   return there;
03743 }
03744 
03745 /*
03746   Add to the current line a comment line mark at
03747   the begining.
03748 */
03749 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
03750 {
03751   QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03752   insertText (line, 0, commentLineMark);
03753 }
03754 
03755 /*
03756   Remove from the current line a comment line mark at
03757   the begining if there is one.
03758 */
03759 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
03760 {
03761   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
03762   QString longCommentMark = shortCommentMark + " ";
03763 
03764   editStart();
03765 
03766   // Try to remove the long comment mark first
03767   bool removed = (removeStringFromBegining(line, longCommentMark)
03768                   || removeStringFromBegining(line, shortCommentMark));
03769 
03770   editEnd();
03771 
03772   return removed;
03773 }
03774 
03775 /*
03776   Add to the current line a start comment mark at the
03777  begining and a stop comment mark at the end.
03778 */
03779 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
03780 {
03781   QString startCommentMark = highlight()->getCommentStart( attrib ) + " ";
03782   QString stopCommentMark = " " + highlight()->getCommentEnd( attrib );
03783 
03784   editStart();
03785 
03786   // Add the start comment mark
03787   insertText (line, 0, startCommentMark);
03788 
03789   // Go to the end of the line
03790   int col = m_buffer->plainLine(line)->length();
03791 
03792   // Add the stop comment mark
03793   insertText (line, col, stopCommentMark);
03794 
03795   editEnd();
03796 }
03797 
03798 /*
03799   Remove from the current line a start comment mark at
03800   the begining and a stop comment mark at the end.
03801 */
03802 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
03803 {
03804   QString shortStartCommentMark = highlight()->getCommentStart( attrib );
03805   QString longStartCommentMark = shortStartCommentMark + " ";
03806   QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
03807   QString longStopCommentMark = " " + shortStopCommentMark;
03808 
03809   editStart();
03810 
03811 #ifdef __GNUC__
03812 #warning "that's a bad idea, can lead to stray endings, FIXME"
03813 #endif
03814   // Try to remove the long start comment mark first
03815   bool removedStart = (removeStringFromBegining(line, longStartCommentMark)
03816                        || removeStringFromBegining(line, shortStartCommentMark));
03817 
03818   bool removedStop = false;
03819   if (removedStart)
03820   {
03821     // Try to remove the long stop comment mark first
03822     removedStop = (removeStringFromEnd(line, longStopCommentMark)
03823                       || removeStringFromEnd(line, shortStopCommentMark));
03824   }
03825 
03826   editEnd();
03827 
03828   return (removedStart || removedStop);
03829 }
03830 
03831 /*
03832   Add to the current selection a start comment
03833  mark at the begining and a stop comment mark
03834  at the end.
03835 */
03836 void KateDocument::addStartStopCommentToSelection( int attrib )
03837 {
03838   QString startComment = highlight()->getCommentStart( attrib );
03839   QString endComment = highlight()->getCommentEnd( attrib );
03840 
03841   int sl = selectStart.line();
03842   int el = selectEnd.line();
03843   int sc = selectStart.col();
03844   int ec = selectEnd.col();
03845 
03846   if ((ec == 0) && ((el-1) >= 0))
03847   {
03848     el--;
03849     ec = m_buffer->plainLine (el)->length();
03850   }
03851 
03852   editStart();
03853 
03854   insertText (el, ec, endComment);
03855   insertText (sl, sc, startComment);
03856 
03857   editEnd ();
03858 
03859   // Set the new selection
03860   ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 );
03861   setSelection(sl, sc, el, ec);
03862 }
03863 
03864 /*
03865   Add to the current selection a comment line
03866  mark at the begining of each line.
03867 */
03868 void KateDocument::addStartLineCommentToSelection( int attrib )
03869 {
03870   QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03871 
03872   int sl = selectStart.line();
03873   int el = selectEnd.line();
03874 
03875   if ((selectEnd.col() == 0) && ((el-1) >= 0))
03876   {
03877     el--;
03878   }
03879 
03880   editStart();
03881 
03882   // For each line of the selection
03883   for (int z = el; z >= sl; z--) {
03884     insertText (z, 0, commentLineMark);
03885   }
03886 
03887   editEnd ();
03888 
03889   // Set the new selection
03890   selectEnd.setCol(selectEnd.col() + ((el == selectEnd.line()) ? commentLineMark.length() : 0) );
03891   setSelection(selectStart.line(), 0, selectEnd.line(), selectEnd.col());
03892 }
03893 
03894 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
03895 {
03896   for(; line < (int)m_buffer->count(); line++) {
03897     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03898 
03899     if (!textLine)
03900       break;
03901 
03902     col = textLine->nextNonSpaceChar(col);
03903     if(col != -1)
03904       return true; // Next non-space char found
03905     col = 0;
03906   }
03907   // No non-space char found
03908   line = -1;
03909   col = -1;
03910   return false;
03911 }
03912 
03913 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
03914 {
03915   while(true)
03916   {
03917     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03918 
03919     if (!textLine)
03920       break;
03921 
03922     col = textLine->previousNonSpaceChar(col);
03923     if(col != -1) return true;
03924     if(line == 0) return false;
03925     --line;
03926     col = textLine->length();
03927 }
03928   // No non-space char found
03929   line = -1;
03930   col = -1;
03931   return false;
03932 }
03933 
03934 /*
03935   Remove from the selection a start comment mark at
03936   the begining and a stop comment mark at the end.
03937 */
03938 bool KateDocument::removeStartStopCommentFromSelection( int attrib )
03939 {
03940   QString startComment = highlight()->getCommentStart( attrib );
03941   QString endComment = highlight()->getCommentEnd( attrib );
03942 
03943   int sl = kMax<int> (0, selectStart.line());
03944   int el = kMin<int>  (selectEnd.line(), lastLine());
03945   int sc = selectStart.col();
03946   int ec = selectEnd.col();
03947 
03948   // The selection ends on the char before selectEnd
03949   if (ec != 0) {
03950     ec--;
03951   } else {
03952     if (el > 0) {
03953       el--;
03954       ec = m_buffer->plainLine(el)->length() - 1;
03955     }
03956   }
03957 
03958   int startCommentLen = startComment.length();
03959   int endCommentLen = endComment.length();
03960 
03961   // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/
03962 
03963   bool remove = nextNonSpaceCharPos(sl, sc)
03964       && m_buffer->plainLine(sl)->stringAtPos(sc, startComment)
03965       && previousNonSpaceCharPos(el, ec)
03966       && ( (ec - endCommentLen + 1) >= 0 )
03967       && m_buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment);
03968 
03969   if (remove) {
03970     editStart();
03971 
03972     removeText (el, ec - endCommentLen + 1, el, ec + 1);
03973     removeText (sl, sc, sl, sc + startCommentLen);
03974 
03975     editEnd ();
03976 
03977     // Set the new selection
03978     ec -= endCommentLen + ( (el == sl) ? startCommentLen : 0 );
03979     setSelection(sl, sc, el, ec + 1);
03980   }
03981 
03982   return remove;
03983 }
03984 
03985 bool KateDocument::removeStartStopCommentFromRegion(const KateTextCursor &start,const KateTextCursor &end,int attrib) {
03986   QString startComment = highlight()->getCommentStart( attrib );
03987   QString endComment = highlight()->getCommentEnd( attrib );
03988   int startCommentLen = startComment.length();
03989   int endCommentLen = endComment.length();
03990 
03991     bool remove = m_buffer->plainLine(start.line())->stringAtPos(start.col(), startComment)
03992       && ( (end.col() - endCommentLen ) >= 0 )
03993       && m_buffer->plainLine(end.line())->stringAtPos(end.col() - endCommentLen , endComment);
03994       if (remove)  {
03995         editStart();
03996           removeText(end.line(),end.col()-endCommentLen,end.line(),end.col());
03997           removeText(start.line(),start.col(),start.line(),start.col()+startCommentLen);
03998         editEnd();
03999       }
04000       return remove;
04001 }
04002 
04003 /*
04004   Remove from the begining of each line of the
04005   selection a start comment line mark.
04006 */
04007 bool KateDocument::removeStartLineCommentFromSelection( int attrib )
04008 {
04009   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
04010   QString longCommentMark = shortCommentMark + " ";
04011 
04012   int sl = selectStart.line();
04013   int el = selectEnd.line();
04014 
04015   if ((selectEnd.col() == 0) && ((el-1) >= 0))
04016   {
04017     el--;
04018   }
04019 
04020   // Find out how many char will be removed from the last line
04021   int removeLength = 0;
04022   if (m_buffer->plainLine(el)->startingWith(longCommentMark))
04023     removeLength = longCommentMark.length();
04024   else if (m_buffer->plainLine(el)->startingWith(shortCommentMark))
04025     removeLength = shortCommentMark.length();
04026 
04027   bool removed = false;
04028 
04029   editStart();
04030 
04031   // For each line of the selection
04032   for (int z = el; z >= sl; z--)
04033   {
04034     // Try to remove the long comment mark first
04035     removed = (removeStringFromBegining(z, longCommentMark)
04036                  || removeStringFromBegining(z, shortCommentMark)
04037                  || removed);
04038   }
04039 
04040   editEnd();
04041 
04042   if(removed) {
04043     // Set the new selection
04044     selectEnd.setCol(selectEnd.col() - ((el == selectEnd.line()) ? removeLength : 0) );
04045     setSelection(selectStart.line(), selectStart.col(), selectEnd.line(), selectEnd.col());
04046   }
04047 
04048   return removed;
04049 }
04050 
04051 /*
04052   Comment or uncomment the selection or the current
04053   line if there is no selection.
04054 */
04055 void KateDocument::comment( KateView *, uint line,uint column, int change)
04056 {
04057   // We need to check that we can sanely comment the selectino or region.
04058   // It is if the attribute of the first and last character of the range to
04059   // comment belongs to the same language definition.
04060   // for lines with no text, we need the attribute for the lines context.
04061   bool hassel = hasSelection();
04062   int startAttrib, endAttrib;
04063   if ( hassel )
04064   {
04065     KateTextLine::Ptr ln = kateTextLine( selectStart.line() );
04066     int l = selectStart.line(), c = selectStart.col();
04067     startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
04068 
04069     ln = kateTextLine( selectEnd.line() );
04070     l = selectEnd.line(), c = selectEnd.col();
04071     endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
04072   }
04073   else
04074   {
04075     KateTextLine::Ptr ln = kateTextLine( line );
04076     if ( ln->length() )
04077     {
04078       startAttrib = ln->attribute( ln->firstChar() );
04079       endAttrib = ln->attribute( ln->lastChar() );
04080     }
04081     else
04082     {
04083       int l = line, c = 0;
04084       if ( nextNonSpaceCharPos( l, c )  || previousNonSpaceCharPos( l, c ) )
04085         startAttrib = endAttrib = kateTextLine( l )->attribute( c );
04086       else
04087         startAttrib = endAttrib = 0;
04088     }
04089   }
04090 
04091   if ( ! highlight()->canComment( startAttrib, endAttrib ) )
04092   {
04093     kdDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!"<<endl;
04094     return;
04095   }
04096 
04097   bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
04098   bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
04099       && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
04100 
04101   bool removed = false;
04102 
04103   if (change > 0) // comment
04104   {
04105     if ( !hassel )
04106     {
04107       if ( hasStartLineCommentMark )
04108         addStartLineCommentToSingleLine( line, startAttrib );
04109       else if ( hasStartStopCommentMark )
04110         addStartStopCommentToSingleLine( line, startAttrib );
04111     }
04112     else
04113     {
04114       // anders: prefer single line comment to avoid nesting probs
04115       // If the selection starts after first char in the first line
04116       // or ends before the last char of the last line, we may use
04117       // multiline comment markers.
04118       // TODO We should try to detect nesting.
04119       //    - if selection ends at col 0, most likely she wanted that
04120       // line ignored
04121       if ( hasStartStopCommentMark &&
04122            ( !hasStartLineCommentMark || (
04123              ( selectStart.col() > m_buffer->plainLine( selectStart.line() )->firstChar() ) ||
04124                ( selectEnd.col() < ((int)m_buffer->plainLine( selectEnd.line() )->length()) )
04125          ) ) )
04126         addStartStopCommentToSelection( startAttrib );
04127       else if ( hasStartLineCommentMark )
04128         addStartLineCommentToSelection( startAttrib );
04129     }
04130   }
04131   else // uncomment
04132   {
04133     if ( !hassel )
04134     {
04135       removed = ( hasStartLineCommentMark
04136                   && removeStartLineCommentFromSingleLine( line, startAttrib ) )
04137         || ( hasStartStopCommentMark
04138              && removeStartStopCommentFromSingleLine( line, startAttrib ) );
04139       if ((!removed) && foldingTree()) {
04140         kdDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)"<<endl;
04141         uint commentRegion=(highlight()->commentRegion(startAttrib));
04142         if (commentRegion){
04143            KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
04144            if (n) {
04145             KateTextCursor start,end;
04146              if ((n->nodeType()==commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
04147                 kdDebug(13020)<<"Enclosing region found:"<<start.col()<<"/"<<start.line()<<"-"<<end.col()<<"/"<<end.line()<<endl;
04148                 removeStartStopCommentFromRegion(start,end,startAttrib);
04149              } else {
04150                   kdDebug(13020)<<"Enclosing region found, but not valid"<<endl;
04151                   kdDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion<<endl;
04152              }
04153             //perhaps nested regions should be hadled here too...
04154           } else kdDebug(13020)<<"No enclosing region found"<<endl;
04155         } else kdDebug(13020)<<"No comment region specified for current hl"<<endl;
04156       }
04157     }
04158     else
04159     {
04160       // anders: this seems like it will work with above changes :)
04161       removed = ( hasStartLineCommentMark
04162                   && removeStartLineCommentFromSelection( startAttrib ) )
04163         || ( hasStartStopCommentMark
04164              && removeStartStopCommentFromSelection( startAttrib ) );
04165     }
04166   }
04167 }
04168 
04169 void KateDocument::transform( KateView *, const KateTextCursor &c,
04170                             KateDocument::TextTransform t )
04171 {
04172   editStart();
04173   uint cl( c.line() ), cc( c.col() );
04174 
04175   if ( hasSelection() )
04176   {
04177     // cache the selection and cursor, so we can be sure to restore.
04178     KateTextCursor s = selectStart;
04179     KateTextCursor e = selectEnd;
04180 
04181     int ln = selStartLine();
04182     while ( ln <= selEndLine() )
04183     {
04184       uint start, end;
04185       start = (ln == selStartLine() || blockSelectionMode()) ?
04186           selStartCol() : 0;
04187       end = (ln == selEndLine() || blockSelectionMode()) ?
04188           selEndCol() : lineLength( ln );
04189       QString s = text( ln, start, ln, end );
04190 
04191       if ( t == Uppercase )
04192         s = s.upper();
04193       else if ( t == Lowercase )
04194         s = s.lower();
04195       else // Capitalize
04196       {
04197         KateTextLine::Ptr l = m_buffer->plainLine( ln );
04198         uint p ( 0 );
04199         while( p < s.length() )
04200         {
04201           // If bol or the character before is not in a word, up this one:
04202           // 1. if both start and p is 0, upper char.
04203           // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
04204           // 3. if p-1 is not in a word, upper.
04205           if ( ( ! start && ! p ) ||
04206                ( ( ln == selStartLine() || blockSelectionMode() ) &&
04207                    ! p && ! highlight()->isInWord( l->getChar( start - 1 )) ) ||
04208                    ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
04209              )
04210             s[p] = s.at(p).upper();
04211           p++;
04212         }
04213       }
04214 
04215       removeText( ln, start, ln, end );
04216       insertText( ln, start, s );
04217 
04218       ln++;
04219     }
04220 
04221     // restore selection
04222     setSelection( s, e );
04223 
04224   } else {  // no selection
04225     QString s;
04226     int n ( cc );
04227     switch ( t ) {
04228       case Uppercase:
04229       s = text( cl, cc, cl, cc + 1 ).upper();
04230       break;
04231       case Lowercase:
04232       s = text( cl, cc, cl, cc + 1 ).lower();
04233       break;
04234       case Capitalize:
04235       {
04236         KateTextLine::Ptr l = m_buffer->plainLine( cl );
04237         while ( n > 0 && highlight()->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) )
04238           n--;
04239         s = text( cl, n, cl, n + 1 ).upper();
04240       }
04241       break;
04242       default:
04243       break;
04244     }
04245     removeText( cl, n, cl, n+1 );
04246     insertText( cl, n, s );
04247   }
04248 
04249   editEnd();
04250 
04251   if ( activeView() )
04252     activeView()->setCursorPosition( cl, cc );
04253 }
04254 
04255 void KateDocument::joinLines( uint first, uint last )
04256 {
04257 //   if ( first == last ) last += 1;
04258   editStart();
04259   int line( first );
04260   while ( first < last )
04261   {
04262     // Normalize the whitespace in the joined lines by making sure there's
04263     // always exactly one space between the joined lines
04264     // This cannot be done in editUnwrapLine, because we do NOT want this
04265     // behaviour when deleting from the start of a line, just when explicitly
04266     // calling the join command
04267     KateTextLine::Ptr l = m_buffer->line( line );
04268     KateTextLine::Ptr tl = m_buffer->line( line + 1 );
04269 
04270     if ( !l || !tl )
04271     {
04272       editEnd();
04273       return;
04274     }
04275 
04276     int pos = tl->firstChar();
04277     if ( pos >= 0 )
04278     {
04279       if (pos != 0)
04280         editRemoveText( line + 1, 0, pos );
04281       if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) )
04282         editInsertText( line + 1, 0, " " );
04283     }
04284     else
04285     {
04286       // Just remove the whitespace and let Kate handle the rest
04287       editRemoveText( line + 1, 0, tl->length() );
04288     }
04289 
04290     editUnWrapLine( line );
04291     first++;
04292   }
04293   editEnd();
04294 }
04295 
04296 QString KateDocument::getWord( const KateTextCursor& cursor ) {
04297   int start, end, len;
04298 
04299   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04300   len = textLine->length();
04301   start = end = cursor.col();
04302   if (start > len)        // Probably because of non-wrapping cursor mode.
04303     return QString("");
04304 
04305   while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
04306   while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(end))) end++;
04307   len = end - start;
04308   return QString(&textLine->text()[start], len);
04309 }
04310 
04311 void KateDocument::tagLines(int start, int end)
04312 {
04313   for (uint z = 0; z < m_views.count(); z++)
04314     m_views.at(z)->tagLines (start, end, true);
04315 }
04316 
04317 void KateDocument::tagLines(KateTextCursor start, KateTextCursor end)
04318 {
04319   // May need to switch start/end cols if in block selection mode
04320   if (blockSelectionMode() && start.col() > end.col()) {
04321     int sc = start.col();
04322     start.setCol(end.col());
04323     end.setCol(sc);
04324   }
04325 
04326   for (uint z = 0; z < m_views.count(); z++)
04327     m_views.at(z)->tagLines(start, end, true);
04328 }
04329 
04330 void KateDocument::tagSelection(const KateTextCursor &oldSelectStart, const KateTextCursor &oldSelectEnd)
04331 {
04332   if (hasSelection()) {
04333     if (oldSelectStart.line() == -1) {
04334       // We have to tag the whole lot if
04335       // 1) we have a selection, and:
04336       //  a) it's new; or
04337       tagLines(selectStart, selectEnd);
04338 
04339     } else if (blockSelectionMode() && (oldSelectStart.col() != selectStart.col() || oldSelectEnd.col() != selectEnd.col())) {
04340       //  b) we're in block selection mode and the columns have changed
04341       tagLines(selectStart, selectEnd);
04342       tagLines(oldSelectStart, oldSelectEnd);
04343 
04344     } else {
04345       if (oldSelectStart != selectStart) {
04346         if (oldSelectStart < selectStart)
04347           tagLines(oldSelectStart, selectStart);
04348         else
04349           tagLines(selectStart, oldSelectStart);
04350       }
04351 
04352       if (oldSelectEnd != selectEnd) {
04353         if (oldSelectEnd < selectEnd)
04354           tagLines(oldSelectEnd, selectEnd);
04355         else
04356           tagLines(selectEnd, oldSelectEnd);
04357       }
04358     }
04359 
04360   } else {
04361     // No more selection, clean up
04362     tagLines(oldSelectStart, oldSelectEnd);
04363   }
04364 }
04365 
04366 void KateDocument::repaintViews(bool paintOnlyDirty)
04367 {
04368   for (uint z = 0; z < m_views.count(); z++)
04369     m_views.at(z)->repaintText(paintOnlyDirty);
04370 }
04371 
04372 void KateDocument::tagAll()
04373 {
04374   for (uint z = 0; z < m_views.count(); z++)
04375   {
04376     m_views.at(z)->tagAll();
04377     m_views.at(z)->updateView (true);
04378   }
04379 }
04380 
04381 void KateDocument::updateViews()
04382 {
04383   if (noViewUpdates)
04384     return;
04385 
04386   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
04387   {
04388     view->updateView(true);
04389   }
04390 }
04391 
04392 uint KateDocument::configFlags ()
04393 {
04394   return config()->configFlags();
04395 }
04396 
04397 void KateDocument::setConfigFlags (uint flags)
04398 {
04399   config()->setConfigFlags(flags);
04400 }
04401 
04402 bool KateDocument::lineColSelected (int line, int col)
04403 {
04404   if ( (!blockSelect) && (col < 0) )
04405     col = 0;
04406 
04407   KateTextCursor cursor(line, col);
04408 
04409   if (blockSelect)
04410     return cursor.line() >= selectStart.line() && cursor.line() <= selectEnd.line() && cursor.col() >= selectStart.col() && cursor.col() < selectEnd.col();
04411   else
04412     return (cursor >= selectStart) && (cursor < selectEnd);
04413 }
04414 
04415 bool KateDocument::lineSelected (int line)
04416 {
04417   return (!blockSelect)
04418     && (selectStart <= KateTextCursor(line, 0))
04419     && (line < selectEnd.line());
04420 }
04421 
04422 bool KateDocument::lineEndSelected (int line, int endCol)
04423 {
04424   return (!blockSelect)
04425     && (line > selectStart.line() || (line == selectStart.line() && (selectStart.col() < endCol || endCol == -1)))
04426     && (line < selectEnd.line() || (line == selectEnd.line() && (endCol <= selectEnd.col() && endCol != -1)));
04427 }
04428 
04429 bool KateDocument::lineHasSelected (int line)
04430 {
04431   return (selectStart < selectEnd)
04432     && (line >= selectStart.line())
04433     && (line <= selectEnd.line());
04434 }
04435 
04436 bool KateDocument::lineIsSelection (int line)
04437 {
04438   return (line == selectStart.line() && line == selectEnd.line());
04439 }
04440 
04441 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04442 inline bool isEndBracket  ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04443 inline bool isBracket     ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04444 
04445 /*
04446    Bracket matching uses the following algorithm:
04447    If in overwrite mode, match the bracket currently underneath the cursor.
04448    Otherwise, if the character to the right of the cursor is an starting bracket,
04449    match it. Otherwise if the character to the left of the cursor is a
04450    ending bracket, match it. Otherwise, if the the character to the left
04451    of the cursor is an starting bracket, match it. Otherwise, if the character
04452    to the right of the cursor is an ending bracket, match it. Otherwise, don't
04453    match anything.
04454 */
04455 void KateDocument::newBracketMark( const KateTextCursor& cursor, KateTextRange& bm, int maxLines )
04456 {
04457   bm.setValid(false);
04458 
04459   bm.start() = cursor;
04460 
04461   if( !findMatchingBracket( bm.start(), bm.end(), maxLines ) )
04462     return;
04463 
04464   bm.setValid(true);
04465 }
04466 
04467 bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines )
04468 {
04469   KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() );
04470   if( !textLine )
04471     return false;
04472 
04473   QChar right = textLine->getChar( start.col() );
04474   QChar left  = textLine->getChar( start.col() - 1 );
04475   QChar bracket;
04476 
04477   if ( config()->configFlags() & cfOvr ) {
04478     if( isBracket( right ) ) {
04479       bracket = right;
04480     } else {
04481       return false;
04482     }
04483   } else if ( isStartBracket( right ) ) {
04484     bracket = right;
04485   } else if ( isEndBracket( left ) ) {
04486     start.setCol(start.col() - 1);
04487     bracket = left;
04488   } else if ( isBracket( left ) ) {
04489     start.setCol(start.col() - 1);
04490     bracket = left;
04491   } else if ( isBracket( right ) ) {
04492     bracket = right;
04493   } else {
04494     return false;
04495   }
04496 
04497   QChar opposite;
04498 
04499   switch( bracket ) {
04500   case '{': opposite = '}'; break;
04501   case '}': opposite = '{'; break;
04502   case '[': opposite = ']'; break;
04503   case ']': opposite = '['; break;
04504   case '(': opposite = ')'; break;
04505   case ')': opposite = '('; break;
04506   default: return false;
04507   }
04508 
04509   bool forward = isStartBracket( bracket );
04510   int startAttr = textLine->attribute( start.col() );
04511   uint count = 0;
04512   int lines = 0;
04513   end = start;
04514 
04515   while( true ) {
04516     /* Increment or decrement, check base cases */
04517     if( forward ) {
04518       end.setCol(end.col() + 1);
04519       if( end.col() >= lineLength( end.line() ) ) {
04520         if( end.line() >= (int)lastLine() )
04521           return false;
04522         end.setPos(end.line() + 1, 0);
04523         textLine = m_buffer->plainLine( end.line() );
04524         lines++;
04525       }
04526     } else {
04527       end.setCol(end.col() - 1);
04528       if( end.col() < 0 ) {
04529         if( end.line() <= 0 )
04530           return false;
04531         end.setLine(end.line() - 1);
04532         end.setCol(lineLength( end.line() ) - 1);
04533         textLine = m_buffer->plainLine( end.line() );
04534         lines++;
04535       }
04536     }
04537 
04538     if ((maxLines != -1) && (lines > maxLines))
04539       return false;
04540 
04541     /* Easy way to skip comments */
04542     if( textLine->attribute( end.col() ) != startAttr )
04543       continue;
04544 
04545     /* Check for match */
04546     QChar c = textLine->getChar( end.col() );
04547     if( c == bracket ) {
04548       count++;
04549     } else if( c == opposite ) {
04550       if( count == 0 )
04551         return true;
04552       count--;
04553     }
04554 
04555   }
04556 }
04557 
04558 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
04559 {
04560   KParts::ReadWritePart::guiActivateEvent( ev );
04561   if ( ev->activated() )
04562     emit selectionChanged();
04563 }
04564 
04565 void KateDocument::setDocName (QString name )
04566 {
04567   if ( name == m_docName )
04568     return;
04569 
04570   if ( !name.isEmpty() )
04571   {
04572     // TODO check for similarly named documents
04573     m_docName = name;
04574     updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04575     emit nameChanged((Kate::Document *) this);
04576     return;
04577   }
04578 
04579   // if the name is set, and starts with FILENAME, it should not be changed!
04580   if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return;
04581 
04582   int count = -1;
04583 
04584   for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
04585   {
04586     if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) )
04587       if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count )
04588         count = KateFactory::self()->documents()->at(z)->m_docNameNumber;
04589   }
04590 
04591   m_docNameNumber = count + 1;
04592 
04593   m_docName = url().filename();
04594 
04595   if (m_docName.isEmpty())
04596     m_docName = i18n ("Untitled");
04597 
04598   if (m_docNameNumber > 0)
04599     m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
04600 
04601   updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04602   emit nameChanged ((Kate::Document *) this);
04603 }
04604 
04605 void KateDocument::slotModifiedOnDisk( Kate::View * /*v*/ )
04606 {
04607   if ( m_isasking < 0 )
04608   {
04609     m_isasking = 0;
04610     return;
04611   }
04612 
04613   if ( !s_fileChangedDialogsActivated || m_isasking )
04614     return;
04615 
04616   if (m_modOnHd && !url().isEmpty())
04617   {
04618     m_isasking = 1;
04619 
04620     KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), widget() );
04621     switch ( p.exec() )
04622     {
04623       case KateModOnHdPrompt::Save:
04624       {
04625         m_modOnHd = false;
04626         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
04627             url().url(),QString::null,widget(),i18n("Save File"));
04628 
04629         kdDebug(13020)<<"got "<<res.URLs.count()<<" URLs"<<endl;
04630         if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first() ) )
04631         {
04632           setEncoding( res.encoding );
04633 
04634           if( ! saveAs( res.URLs.first() ) )
04635           {
04636             KMessageBox::error( widget(), i18n("Save failed") );
04637             m_modOnHd = true;
04638           }
04639           else
04640             emit modifiedOnDisc( this, false, 0 );
04641         }
04642         else // the save as dialog was cancelled, we are still modified on disk
04643         {
04644           m_modOnHd = true;
04645         }
04646 
04647         m_isasking = 0;
04648         break;
04649       }
04650 
04651       case KateModOnHdPrompt::Reload:
04652         m_modOnHd = false;
04653         emit modifiedOnDisc( this, false, 0 );
04654         reloadFile();
04655         m_isasking = 0;
04656         break;
04657 
04658       case KateModOnHdPrompt::Ignore:
04659         m_modOnHd = false;
04660         emit modifiedOnDisc( this, false, 0 );
04661         m_isasking = 0;
04662         break;
04663 
04664       case KateModOnHdPrompt::Overwrite:
04665         m_modOnHd = false;
04666         emit modifiedOnDisc( this, false, 0 );
04667         m_isasking = 0;
04668         save();
04669         break;
04670 
04671       default:               // cancel: ignore next focus event
04672         m_isasking = -1;
04673     }
04674   }
04675 }
04676 
04677 void KateDocument::setModifiedOnDisk( int reason )
04678 {
04679   m_modOnHdReason = reason;
04680   m_modOnHd = (reason > 0);
04681   emit modifiedOnDisc( this, (reason > 0), reason );
04682 }
04683 
04684 class KateDocumentTmpMark
04685 {
04686   public:
04687     QString line;
04688     KTextEditor::Mark mark;
04689 };
04690 
04691 void KateDocument::reloadFile()
04692 {
04693   if ( !url().isEmpty() )
04694   {
04695     if (m_modOnHd && s_fileChangedDialogsActivated)
04696     {
04697       int i = KMessageBox::warningYesNoCancel
04698                 (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
04699                 i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes"));
04700 
04701       if ( i != KMessageBox::Yes)
04702       {
04703         if (i == KMessageBox::No)
04704         {
04705           m_modOnHd = false;
04706           m_modOnHdReason = 0;
04707           emit modifiedOnDisc (this, m_modOnHd, 0);
04708         }
04709 
04710         return;
04711       }
04712     }
04713 
04714     QValueList<KateDocumentTmpMark> tmp;
04715 
04716     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
04717     {
04718       KateDocumentTmpMark m;
04719 
04720       m.line = textLine (it.current()->line);
04721       m.mark = *it.current();
04722 
04723       tmp.append (m);
04724     }
04725 
04726     uint mode = hlMode ();
04727     bool byUser = hlSetByUser;
04728 
04729     m_storedVariables.clear();
04730 
04731     m_reloading = true;
04732     KateDocument::openURL( url() );
04733     m_reloading = false;
04734 
04735     for (uint z=0; z < tmp.size(); z++)
04736     {
04737       if (z < numLines())
04738       {
04739         if (textLine(tmp[z].mark.line) == tmp[z].line)
04740           setMark (tmp[z].mark.line, tmp[z].mark.type);
04741       }
04742     }
04743 
04744     if (byUser)
04745       setHlMode (mode);
04746   }
04747 }
04748 
04749 void KateDocument::flush ()
04750 {
04751   closeURL ();
04752 }
04753 
04754 void KateDocument::setWordWrap (bool on)
04755 {
04756   config()->setWordWrap (on);
04757 }
04758 
04759 bool KateDocument::wordWrap ()
04760 {
04761   return config()->wordWrap ();
04762 }
04763 
04764 void KateDocument::setWordWrapAt (uint col)
04765 {
04766   config()->setWordWrapAt (col);
04767 }
04768 
04769 unsigned int KateDocument::wordWrapAt ()
04770 {
04771   return config()->wordWrapAt ();
04772 }
04773 
04774 void KateDocument::applyWordWrap ()
04775 {
04776   if (hasSelection())
04777     wrapText (selectStart.line(), selectEnd.line());
04778   else
04779     wrapText (0, lastLine());
04780 }
04781 
04782 void KateDocument::setPageUpDownMovesCursor (bool on)
04783 {
04784   config()->setPageUpDownMovesCursor (on);
04785 }
04786 
04787 bool KateDocument::pageUpDownMovesCursor ()
04788 {
04789   return config()->pageUpDownMovesCursor ();
04790 }
04791 
04792 void KateDocument::exportAs(const QString& filter)
04793 {
04794   if (filter=="kate_html_export")
04795   {
04796     KURL url = KFileDialog::getSaveURL(QString::null,"text/html",0,i18n("Export File As"));
04797     if ( url.isEmpty() )
04798       return;
04799 
04800     QString filename;
04801     KTempFile tmp; // ### only used for network export
04802 
04803     if ( url.isLocalFile() )
04804       filename = url.path();
04805     else
04806       filename = tmp.name();
04807 
04808     KSaveFile *savefile=new KSaveFile(filename);
04809     if (!savefile->status())
04810     {
04811       if (exportDocumentToHTML(savefile->textStream(),filename))
04812         savefile->close();
04813       else savefile->abort();
04814       //if (!savefile->status()) --> Error
04815     }
04816 //     else
04817 //       {/*ERROR*/}
04818     delete savefile;
04819 
04820     if ( url.isLocalFile() )
04821         return;
04822 
04823     KIO::NetAccess::upload( filename, url, 0 );
04824   }
04825 }
04826 
04827 /* For now, this should become an plugin */
04828 bool KateDocument::exportDocumentToHTML(QTextStream *outputStream,const QString &name)
04829 {
04830   outputStream->setEncoding(QTextStream::UnicodeUTF8);
04831   // let's write the HTML header :
04832   (*outputStream) << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
04833   (*outputStream) << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">" << endl;
04834   (*outputStream) << "<html xmlns=\"http://www.w3.org/1999/xhtml\">" << endl;
04835   (*outputStream) << "<head>" << endl;
04836   (*outputStream) << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />" << endl;
04837   (*outputStream) << "<meta name=\"Generator\" content=\"Kate, the KDE Advanced Text Editor\" />" << endl;
04838   // for the title, we write the name of the file (/usr/local/emmanuel/myfile.cpp -> myfile.cpp)
04839   (*outputStream) << "<title>" << name.right(name.length() - name.findRev('/')-1) << "</title>" << endl;
04840   (*outputStream) << "</head>" << endl;
04841   (*outputStream) << "<body>" << endl;
04842 
04843   textAsHtmlStream(0,0,lastLine(), lineLength(lastLine()), false, outputStream);
04844 
04845   (*outputStream) << "</body>" << endl;
04846   (*outputStream) << "</html>" << endl;
04847   return true;
04848 }
04849 
04850 QString KateDocument::HTMLEncode(QChar theChar)
04851 {
04852   switch (theChar.latin1())
04853   {
04854   case '>':
04855     return QString("&gt;");
04856   case '<':
04857     return QString("&lt;");
04858   case '&':
04859     return QString("&amp;");
04860   };
04861   return theChar;
04862 }
04863 
04864 Kate::ConfigPage *KateDocument::colorConfigPage (QWidget *p)
04865 {
04866   return (Kate::ConfigPage*) new KateSchemaConfigPage (p, this);
04867 }
04868 
04869 Kate::ConfigPage *KateDocument::viewDefaultsConfigPage (QWidget *p)
04870 {
04871   return (Kate::ConfigPage*) new KateViewDefaultsConfig(p);
04872 }
04873 
04874 Kate::ConfigPage *KateDocument::fontConfigPage (QWidget *p)
04875 {
04876   return (Kate::ConfigPage*) new KateSchemaConfigPage ( p );
04877 }
04878 
04879 Kate::ConfigPage *KateDocument::indentConfigPage (QWidget *p)
04880 {
04881   return (Kate::ConfigPage*) new KateIndentConfigTab(p);
04882 }
04883 
04884 Kate::ConfigPage *KateDocument::selectConfigPage (QWidget *p)
04885 {
04886   return (Kate::ConfigPage*) new KateSelectConfigTab(p);
04887 }
04888 
04889 Kate::ConfigPage *KateDocument::editConfigPage (QWidget *p)
04890 {
04891   return (Kate::ConfigPage*) new KateEditConfigTab(p);
04892 }
04893 
04894 Kate::ConfigPage *KateDocument::keysConfigPage (QWidget *p)
04895 {
04896   return (Kate::ConfigPage*) new KateEditKeyConfiguration(p, this);
04897 }
04898 
04899 Kate::ConfigPage *KateDocument::hlConfigPage (QWidget *p)
04900 {
04901   return (Kate::ConfigPage*) new KateHlConfigPage (p);
04902 }
04903 
04904 Kate::ConfigPage *KateDocument::saveConfigPage(QWidget *p)
04905 {
04906   return (Kate::ConfigPage*) new KateSaveConfigTab(p);
04907 }
04908 
04909 Kate::ActionMenu *KateDocument::hlActionMenu (const QString& text, QObject* parent, const char* name)
04910 {
04911   KateViewHighlightAction *menu = new KateViewHighlightAction (text, parent, name);
04912   menu->setWhatsThis(i18n("Here you can choose how the current document should be highlighted."));
04913   menu->updateMenu (this);
04914 
04915   return (Kate::ActionMenu *)menu;
04916 }
04917 
04918 Kate::ActionMenu *KateDocument::exportActionMenu (const QString& text, QObject* parent, const char* name)
04919 {
04920   KateExportAction *menu = new KateExportAction (text, parent, name);
04921   menu->updateMenu (this);
04922   menu->setWhatsThis(i18n("This command allows you to export the current document"
04923     " with all highlighting information into a markup document, e.g. HTML."));
04924   return (Kate::ActionMenu *)menu;
04925 }
04926 
04927 void KateDocument::dumpRegionTree()
04928 {
04929   m_buffer->foldingTree()->debugDump();
04930 }
04931 //END
04932 
04933 //BEGIN KTextEditor::CursorInterface stuff
04934 
04935 KTextEditor::Cursor *KateDocument::createCursor ( )
04936 {
04937   return new KateSuperCursor (this, false, 0, 0, this);
04938 }
04939 
04940 void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range)
04941 {
04942   if (view)
04943     view->tagLines(range->start(), range->end());
04944   else
04945     tagLines(range->start(), range->end());
04946 }
04947 
04948 //
04949 // Spellchecking IN again
04950 //
04951 
04952 void KateDocument::spellcheck()
04953 {
04954   spellcheck( KateTextCursor( 0, 0 ) );
04955 }
04956 
04957 void KateDocument::spellcheck( const KateTextCursor &from, const KateTextCursor &to )
04958 {
04959   if( !isReadWrite() || text().isEmpty() )
04960     return;
04961 
04962 
04963   m_spellStart = from;
04964   m_spellEnd = to;
04965 
04966   if ( to.line() == 0 && to.col() == 0 )
04967   {
04968     int lln = lastLine();
04969     m_spellEnd.setLine( lln );
04970     m_spellEnd.setCol( lineLength( lln ) );
04971   }
04972 
04973   m_spellPosCursor = from;
04974   m_spellLastPos = 0;
04975 
04976   QString mt = mimeType()/*->name()*/;
04977 
04978   KSpell::SpellerType type = KSpell::Text;
04979   if ( mt == "text/x-tex" || mt == "text/x-latex" )
04980     type = KSpell::TeX;
04981   else if ( mt == "text/html" || mt == "text/xml" )
04982     type = KSpell::HTML;
04983 
04984   m_kspell = new KSpell( 0, i18n("Spellcheck"),
04985                          this, SLOT(ready(KSpell *)), 0, true, false, type );
04986 
04987   connect( m_kspell, SIGNAL(death()),
04988            this, SLOT(spellCleanDone()) );
04989 
04990   connect( m_kspell, SIGNAL(misspelling(const QString&, const QStringList&, unsigned int)),
04991            this, SLOT(misspelling(const QString&, const QStringList&, unsigned int)) );
04992   connect( m_kspell, SIGNAL(corrected(const QString&, const QString&, unsigned int)),
04993            this, SLOT(corrected(const QString&, const QString&, unsigned int)) );
04994   connect( m_kspell, SIGNAL(done(const QString&)),
04995            this, SLOT(spellResult(const QString&)) );
04996 }
04997 
04998 void KateDocument::ready(KSpell *)
04999 {
05000   m_kspell->setProgressResolution( 1 );
05001 
05002   m_kspell->check( text( m_spellStart.line(), m_spellStart.col(), m_spellEnd.line(), m_spellEnd.col() ) );
05003 
05004   kdDebug (13020) << "SPELLING READY STATUS: " << m_kspell->status () << endl;
05005 }
05006 
05007 void KateDocument::locatePosition( uint pos, uint& line, uint& col )
05008 {
05009   uint remains;
05010 
05011   while ( m_spellLastPos < pos )
05012   {
05013     remains = pos - m_spellLastPos;
05014     uint l = lineLength( m_spellPosCursor.line() ) - m_spellPosCursor.col();
05015     if ( l > remains )
05016     {
05017       m_spellPosCursor.setCol( m_spellPosCursor.col() + remains );
05018       m_spellLastPos = pos;
05019     }
05020     else
05021     {
05022       m_spellPosCursor.setLine( m_spellPosCursor.line() + 1 );
05023       m_spellPosCursor.setCol(0);
05024       m_spellLastPos += l + 1;
05025     }
05026   }
05027 
05028   line = m_spellPosCursor.line();
05029   col = m_spellPosCursor.col();
05030 }
05031 
05032 void KateDocument::misspelling( const QString& origword, const QStringList&, unsigned int pos )
05033 {
05034   uint line, col;
05035 
05036   locatePosition( pos, line, col );
05037 
05038   if (activeView())
05039     activeView()->setCursorPositionInternal (line, col, 1);
05040 
05041   setSelection( line, col, line, col + origword.length() );
05042 }
05043 
05044 void KateDocument::corrected( const QString& originalword, const QString& newword, unsigned int pos )
05045 {
05046   uint line, col;
05047 
05048   locatePosition( pos, line, col );
05049 
05050   removeText( line, col, line, col + originalword.length() );
05051   insertText( line, col, newword );
05052 }
05053 
05054 void KateDocument::spellResult( const QString& )
05055 {
05056   clearSelection();
05057   m_kspell->cleanUp();
05058 }
05059 
05060 void KateDocument::spellCleanDone()
05061 {
05062   KSpell::spellStatus status = m_kspell->status();
05063 
05064   if( status == KSpell::Error ) {
05065     KMessageBox::sorry( 0,
05066       i18n("The spelling program could not be started. "
05067            "Please make sure you have set the correct spelling program "
05068            "and that it is properly configured and in your PATH."));
05069   } else if( status == KSpell::Crashed ) {
05070     KMessageBox::sorry( 0,
05071       i18n("The spelling program seems to have crashed."));
05072   }
05073 
05074   delete m_kspell;
05075   m_kspell = 0;
05076 
05077   kdDebug (13020) << "SPELLING END" << endl;
05078 }
05079 //END
05080 
05081 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
05082 {
05083   m_buffer->lineInfo(info,line);
05084 }
05085 
05086 KateCodeFoldingTree *KateDocument::foldingTree ()
05087 {
05088   return m_buffer->foldingTree();
05089 }
05090 
05091 void KateDocument::setEncoding (const QString &e)
05092 {
05093   m_config->setEncoding(e);
05094 }
05095 
05096 QString KateDocument::encoding() const
05097 {
05098   return m_config->encoding();
05099 }
05100 
05101 void KateDocument::updateConfig ()
05102 {
05103   emit undoChanged ();
05104   tagAll();
05105 
05106   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
05107   {
05108     view->updateDocumentConfig ();
05109   }
05110 
05111   // switch indenter if needed
05112   if (m_indenter->modeNumber() != m_config->indentationMode())
05113   {
05114     delete m_indenter;
05115     m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() );
05116   }
05117 
05118   m_indenter->updateConfig();
05119 
05120   m_buffer->setTabWidth (config()->tabWidth());
05121 
05122   // plugins
05123   for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
05124   {
05125     if (config()->plugin (i))
05126       loadPlugin (i);
05127     else
05128       unloadPlugin (i);
05129   }
05130 }
05131 
05132 //BEGIN Variable reader
05133 // "local variable" feature by anders, 2003
05134 /* TODO
05135       add config options (how many lines to read, on/off)
05136       add interface for plugins/apps to set/get variables
05137       add view stuff
05138 */
05139 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
05140 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
05141 
05142 void KateDocument::readVariables(bool onlyViewAndRenderer)
05143 {
05144   if (!onlyViewAndRenderer)
05145     m_config->configStart();
05146 
05147   // views!
05148   KateView *v;
05149   for (v = m_views.first(); v != 0L; v= m_views.next() )
05150   {
05151     v->config()->configStart();
05152     v->renderer()->config()->configStart();
05153   }
05154   // read a number of lines in the top/bottom of the document
05155   for (uint i=0; i < QMIN( 9, numLines() ); ++i )
05156   {
05157     readVariableLine( textLine( i ), onlyViewAndRenderer );
05158   }
05159   if ( numLines() > 10 )
05160   {
05161     for ( uint i = QMAX(10, numLines() - 10); i < numLines(); ++i )
05162     {
05163       readVariableLine( textLine( i ), onlyViewAndRenderer );
05164     }
05165   }
05166 
05167   if (!onlyViewAndRenderer)
05168     m_config->configEnd();
05169 
05170   for (v = m_views.first(); v != 0L; v= m_views.next() )
05171   {
05172     v->config()->configEnd();
05173     v->renderer()->config()->configEnd();
05174   }
05175 }
05176 
05177 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
05178 {
05179   if ( kvLine.search( t ) > -1 )
05180   {
05181     QStringList vvl; // view variable names
05182     vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
05183         << "line-numbers" << "icon-border" << "folding-markers"
05184         << "bookmark-sorting" << "auto-center-lines"
05185         << "icon-bar-color"
05186         // renderer
05187         << "background-color" << "selection-color"
05188         << "current-line-color" << "bracket-highlight-color"
05189         << "word-wrap-marker-color"
05190         << "font" << "font-size" << "scheme";
05191     int p( 0 );
05192     QString s = kvLine.cap(1);
05193     QString  var, val;
05194     while ( (p = kvVar.search( s, p )) > -1 )
05195     {
05196       p += kvVar.matchedLength();
05197       var = kvVar.cap( 1 );
05198       val = kvVar.cap( 2 ).stripWhiteSpace();
05199       bool state; // store booleans here
05200       int n; // store ints here
05201 
05202       // only apply view & renderer config stuff
05203       if (onlyViewAndRenderer)
05204       {
05205         if ( vvl.contains( var ) ) // FIXME define above
05206           setViewVariable( var, val );
05207       }
05208       else
05209       {
05210         // BOOL  SETTINGS
05211         if ( var == "word-wrap" && checkBoolValue( val, &state ) )
05212           setWordWrap( state ); // ??? FIXME CHECK
05213         else if ( var == "block-selection"  && checkBoolValue( val, &state ) )
05214           setBlockSelectionMode( state );
05215         // KateConfig::configFlags
05216         // FIXME should this be optimized to only a few calls? how?
05217         else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
05218           m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
05219         else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
05220           m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
05221         else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
05222           m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
05223         else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
05224           m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
05225         else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
05226           m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
05227         else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
05228           m_config->setConfigFlags( KateDocumentConfig::cfPersistent, state );
05229 //         else if ( var == "keep-selection" && checkBoolValue( val, &state ) )
05230 //           m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
05231         else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
05232           m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
05233         else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) )
05234           m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state );
05235         else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
05236           m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
05237         else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
05238           m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
05239         else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
05240           m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
05241         else if ( var == "space-indent" && checkBoolValue( val, &state ) )
05242           m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state );
05243         else if ( var == "smart-home" && checkBoolValue( val, &state ) )
05244           m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
05245         else if ( var == "replace-tabs-save" && checkBoolValue( val, &state ) )
05246           m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabs, state );
05247         else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
05248           m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
05249         else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) )
05250           m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state);
05251         else if ( var == "mixed-indent" && checkBoolValue( val, &state ) )
05252           m_config->setConfigFlags( KateDocumentConfig::cfMixedIndent, &state );
05253 
05254         // INTEGER SETTINGS
05255         else if ( var == "tab-width" && checkIntValue( val, &n ) )
05256           m_config->setTabWidth( n );
05257         else if ( var == "indent-width"  && checkIntValue( val, &n ) )
05258           m_config->setIndentationWidth( n );
05259         else if ( var == "indent-mode" )
05260         {
05261           if ( checkIntValue( val, &n ) )
05262             m_config->setIndentationMode( n );
05263           else
05264             m_config->setIndentationMode( KateAutoIndent::modeNumber( val) );
05265         }
05266         else if ( var == "word-wrap-column" && n > 0  && checkIntValue( val, &n ) ) // uint, but hard word wrap at 0 will be no fun ;)
05267           m_config->setWordWrapAt( n );
05268         else if ( var == "undo-steps"  && n >= 0  && checkIntValue( val, &n ) )
05269           setUndoSteps( n );
05270 
05271         // STRING SETTINGS
05272         else if ( var == "eol" || var == "end-of-line" )
05273         {
05274           QStringList l;
05275           l << "unix" << "dos" << "mac";
05276           if ( (n = l.findIndex( val.lower() )) != -1 )
05277             m_config->setEol( n );
05278         }
05279         else if ( var == "encoding" )
05280           m_config->setEncoding( val );
05281         else if ( var == "syntax" || var == "hl" )
05282         {
05283           for ( uint i=0; i < hlModeCount(); i++ )
05284           {
05285             if ( hlModeName( i ).lower() == val.lower() )
05286             {
05287               setHlMode( i );
05288               break;
05289             }
05290           }
05291         }
05292 
05293         // VIEW SETTINGS
05294         else if ( vvl.contains( var ) )
05295           setViewVariable( var, val );
05296         else
05297         {
05298           m_storedVariables.insert( var, val );
05299           emit variableChanged( var, val );
05300         }
05301       }
05302     }
05303   }
05304 }
05305 
05306 void KateDocument::setViewVariable( QString var, QString val )
05307 {
05308   KateView *v;
05309   bool state;
05310   int n;
05311   QColor c;
05312   for (v = m_views.first(); v != 0L; v= m_views.next() )
05313   {
05314     if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
05315       v->config()->setDynWordWrap( state );
05316     //else if ( var = "dynamic-word-wrap-indicators" )
05317     else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
05318       v->config()->setLineNumbers( state );
05319     else if (var == "icon-border" && checkBoolValue( val, &state ) )
05320       v->config()->setIconBar( state );
05321     else if (var == "folding-markers" && checkBoolValue( val, &state ) )
05322       v->config()->setFoldingBar( state );
05323     else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
05324       v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
05325     else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
05326       v->renderer()->config()->setIconBarColor( c );
05327     // RENDERER
05328     else if ( var == "background-color" && checkColorValue( val, c ) )
05329       v->renderer()->config()->setBackgroundColor( c );
05330     else if ( var == "selection-color" && checkColorValue( val, c ) )
05331       v->renderer()->config()->setSelectionColor( c );
05332     else if ( var == "current-line-color" && checkColorValue( val, c ) )
05333       v->renderer()->config()->setHighlightedLineColor( c );
05334     else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
05335       v->renderer()->config()->setHighlightedBracketColor( c );
05336     else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
05337       v->renderer()->config()->setWordWrapMarkerColor( c );
05338     else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
05339     {
05340       QFont _f( *v->renderer()->config()->font(  ) );
05341 
05342       if ( var == "font" )
05343       {
05344         _f.setFamily( val );
05345         _f.setFixedPitch( QFont( val ).fixedPitch() );
05346       }
05347       else
05348         _f.setPointSize( n );
05349 
05350       v->renderer()->config()->setFont( _f );
05351     }
05352     else if ( var == "scheme" )
05353     {
05354       v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) );
05355     }
05356   }
05357 }
05358 
05359 bool KateDocument::checkBoolValue( QString val, bool *result )
05360 {
05361   val = val.stripWhiteSpace().lower();
05362   QStringList l;
05363   l << "1" << "on" << "true";
05364   if ( l.contains( val ) )
05365   {
05366     *result = true;
05367     return true;
05368   }
05369   l.clear();
05370   l << "0" << "off" << "false";
05371   if ( l.contains( val ) )
05372   {
05373     *result = false;
05374     return true;
05375   }
05376   return false;
05377 }
05378 
05379 bool KateDocument::checkIntValue( QString val, int *result )
05380 {
05381   bool ret( false );
05382   *result = val.toInt( &ret );
05383   return ret;
05384 }
05385 
05386 bool KateDocument::checkColorValue( QString val, QColor &c )
05387 {
05388   c.setNamedColor( val );
05389   return c.isValid();
05390 }
05391 
05392 // KTextEditor::variable
05393 QString KateDocument::variable( const QString &name ) const
05394 {
05395   if ( m_storedVariables.contains( name ) )
05396     return m_storedVariables[ name ];
05397 
05398   return "";
05399 }
05400 
05401 //END
05402 
05403 void KateDocument::slotModOnHdDirty (const QString &path)
05404 {
05405   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1))
05406   {
05407     // compare md5 with the one we have (if we have one)
05408     if ( ! m_digest.isEmpty() )
05409     {
05410       QCString tmp;
05411       if ( createDigest( tmp ) && tmp == m_digest )
05412         return;
05413     }
05414 
05415     m_modOnHd = true;
05416     m_modOnHdReason = 1;
05417 
05418     // reenable dialog if not running atm
05419     if (m_isasking == -1)
05420       m_isasking = false;
05421 
05422     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
05423   }
05424 }
05425 
05426 void KateDocument::slotModOnHdCreated (const QString &path)
05427 {
05428   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2))
05429   {
05430     m_modOnHd = true;
05431     m_modOnHdReason = 2;
05432 
05433     // reenable dialog if not running atm
05434     if (m_isasking == -1)
05435       m_isasking = false;
05436 
05437     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
05438   }
05439 }
05440 
05441 void KateDocument::slotModOnHdDeleted (const QString &path)
05442 {
05443   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3))
05444   {
05445     m_modOnHd = true;
05446     m_modOnHdReason = 3;
05447 
05448     // reenable dialog if not running atm
05449     if (m_isasking == -1)
05450       m_isasking = false;
05451 
05452     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
05453   }
05454 }
05455 
05456 bool KateDocument::createDigest( QCString &result )
05457 {
05458   bool ret = false;
05459   result = "";
05460   if ( url().isLocalFile() )
05461   {
05462     QFile f ( url().path() );
05463     if ( f.open( IO_ReadOnly) )
05464     {
05465       KMD5 md5;
05466       ret = md5.update( f );
05467       md5.hexDigest( result );
05468       f.close();
05469     }
05470   }
05471   return ret;
05472 }
05473 
05474 QString KateDocument::reasonedMOHString() const
05475 {
05476   switch( m_modOnHdReason )
05477   {
05478     case 1:
05479       return i18n("The file '%1' was modified by another program.").arg( url().prettyURL() );
05480       break;
05481     case 2:
05482       return i18n("The file '%1' was created by another program.").arg( url().prettyURL() );
05483       break;
05484     case 3:
05485       return i18n("The file '%1' was deleted by another program.").arg( url().prettyURL() );
05486       break;
05487     default:
05488       return QString();
05489   }
05490 }
05491 
05492 void KateDocument::removeTrailingSpace( uint line )
05493 {
05494   // remove trailing spaces from left line if required
05495   if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn )
05496   {
05497     KateTextLine::Ptr ln = kateTextLine( line );
05498 
05499     if ( ! ln ) return;
05500 
05501     if ( line == activeView()->cursorLine()
05502          && activeView()->cursorColumnReal() >= (uint)QMAX(0,ln->lastChar()) )
05503       return;
05504 
05505     if ( ln->length() )
05506     {
05507       uint p = ln->lastChar() + 1;
05508       uint l = ln->length() - p;
05509       if ( l )
05510         editRemoveText( line, p, l);
05511     }
05512   }
05513 }
05514 
05515 bool KateDocument::wrapCursor ()
05516 {
05517   return !blockSelect && (configFlags() & KateDocument::cfWrapCursor);
05518 }
05519 
05520 void KateDocument::updateFileType (int newType, bool user)
05521 {
05522   if (user || !m_fileTypeSetByUser)
05523   {
05524     const KateFileType *t = 0;
05525     if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType)))
05526     {
05527       m_fileType = newType;
05528 
05529       if (t)
05530       {
05531         m_config->configStart();
05532         // views!
05533         KateView *v;
05534         for (v = m_views.first(); v != 0L; v= m_views.next() )
05535         {
05536           v->config()->configStart();
05537           v->renderer()->config()->configStart();
05538         }
05539 
05540         readVariableLine( t->varLine );
05541 
05542         m_config->configEnd();
05543         for (v = m_views.first(); v != 0L; v= m_views.next() )
05544         {
05545           v->config()->configEnd();
05546           v->renderer()->config()->configEnd();
05547         }
05548       }
05549     }
05550   }
05551 }
05552 
05553 uint KateDocument::documentNumber () const
05554 {
05555   return KTextEditor::Document::documentNumber ();
05556 }
05557 
05558 
05559 
05560 
05561 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
05562       *handled=true;
05563       *abortClosing=true;
05564       if (m_url.isEmpty())
05565       {
05566         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
05567                 QString::null,QString::null,0,i18n("Save File"));
05568 
05569         if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) {
05570                 *abortClosing=true;
05571                 return;
05572         }
05573         setEncoding( res.encoding );
05574           saveAs( res.URLs.first() );
05575         *abortClosing=false;
05576       }
05577       else
05578       {
05579           save();
05580           *abortClosing=false;
05581       }
05582 
05583 }
05584 
05585 bool KateDocument::checkOverwrite( KURL u )
05586 {
05587   if( !u.isLocalFile() )
05588     return true;
05589 
05590   QFileInfo info( u.path() );
05591   if( !info.exists() )
05592     return true;
05593 
05594   return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0,
05595     i18n( "A file named \"%1\" already exists. "
05596           "Are you sure you want to overwrite it?" ).arg( info.fileName() ),
05597     i18n( "Overwrite File?" ),
05598     i18n( "&Overwrite" ) );
05599 }
05600 
05601 void KateDocument::setDefaultEncoding (const QString &encoding)
05602 {
05603   s_defaultEncoding = encoding;
05604 }
05605 
05606 //BEGIN KTextEditor::TemplateInterface
05607 bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
05608       return (new KateTemplateHandler(this,line,column,templateString,initialValues))->initOk();
05609 }
05610 
05611 void KateDocument::testTemplateCode() {
05612   int col=activeView()->cursorColumn();
05613   int line=activeView()->cursorLine();
05614   insertTemplateText(line,col,"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05615 }
05616 
05617 bool KateDocument::invokeTabInterceptor(KKey key) {
05618   if (m_tabInterceptor) return (*m_tabInterceptor)(key);
05619   return false;
05620 }
05621 
05622 bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05623   if (m_tabInterceptor) return false;
05624   m_tabInterceptor=interceptor;
05625   return true;
05626 }
05627 
05628 bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05629   if (m_tabInterceptor!=interceptor) return false;
05630   m_tabInterceptor=0;
05631   return true;
05632 }
05633 //END KTextEditor::TemplateInterface
05634 
05635 
05636 void KateDocument::setIMSelectionValue( uint imStartLine, uint imStart, uint imEnd,
05637                                         uint imSelStart, uint imSelEnd, bool imComposeEvent )
05638 {
05639   m_imStartLine = imStartLine;
05640   m_imStart = imStart;
05641   m_imEnd = imEnd;
05642   m_imSelStart = imSelStart;
05643   m_imSelEnd = imSelEnd;
05644   m_imComposeEvent = imComposeEvent;
05645 }
05646 
05647 bool KateDocument::isIMSelection( int _line, int _column )
05648 {
05649   return ( ( int( m_imStartLine ) == _line ) && ( m_imSelStart < m_imSelEnd ) && ( _column >= int( m_imSelStart ) ) &&
05650     ( _column < int( m_imSelEnd ) ) );
05651 }
05652 
05653 bool KateDocument::isIMEdit( int _line, int _column )
05654 {
05655   return ( ( int( m_imStartLine ) == _line ) && ( m_imStart < m_imEnd ) && ( _column >= int( m_imStart ) ) &&
05656     ( _column < int( m_imEnd ) ) );
05657 }
05658 
05659 void KateDocument::getIMSelectionValue( uint *imStartLine, uint *imStart, uint *imEnd,
05660                                         uint *imSelStart, uint *imSelEnd )
05661 {
05662   *imStartLine = m_imStartLine;
05663   *imStart = m_imStart;
05664   *imEnd = m_imEnd;
05665   *imSelStart = m_imSelStart;
05666   *imSelEnd = m_imSelEnd;
05667 }
05668 
05669 // 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 Fri Jul 21 13:15:44 2006 by doxygen 1.4.0 written by Dimitri van Heesch, © 1997-2003