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