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., 51 Franklin Street, Fifth Floor,
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 "katesearch.h"
00030 #include "kateautoindent.h"
00031 #include "katetextline.h"
00032 #include "katedocumenthelpers.h"
00033 #include "kateprinter.h"
00034 #include "katelinerange.h"
00035 #include "katesupercursor.h"
00036 #include "katearbitraryhighlight.h"
00037 #include "katerenderer.h"
00038 #include "kateattribute.h"
00039 #include "kateconfig.h"
00040 #include "katefiletype.h"
00041 #include "kateschema.h"
00042 #include "katetemplatehandler.h"
00043 #include <ktexteditor/plugin.h>
00044 
00045 #include <kio/job.h>
00046 #include <kio/netaccess.h>
00047 #include <kio/kfileitem.h>
00048 
00049 
00050 #include <kparts/event.h>
00051 
00052 #include <klocale.h>
00053 #include <kglobal.h>
00054 #include <kapplication.h>
00055 #include <kpopupmenu.h>
00056 #include <kconfig.h>
00057 #include <kfiledialog.h>
00058 #include <kmessagebox.h>
00059 #include <kstdaction.h>
00060 #include <kiconloader.h>
00061 #include <kxmlguifactory.h>
00062 #include <kdialogbase.h>
00063 #include <kdebug.h>
00064 #include <kglobalsettings.h>
00065 #include <klibloader.h>
00066 #include <kdirwatch.h>
00067 #include <kwin.h>
00068 #include <kencodingfiledialog.h>
00069 #include <ktempfile.h>
00070 #include <kmdcodec.h>
00071 
00072 #include <qtimer.h>
00073 #include <qfile.h>
00074 #include <qclipboard.h>
00075 #include <qtextstream.h>
00076 #include <qtextcodec.h>
00077 #include <qmap.h>
00078 //END  includes
00079 
00080 //BEGIN PRIVATE CLASSES
00081 class KatePartPluginItem
00082 {
00083   public:
00084     KTextEditor::Plugin *plugin;
00085 };
00086 //END PRIVATE CLASSES
00087 
00088 //BEGIN d'tor, c'tor
00089 //
00090 // KateDocument Constructor
00091 //
00092 KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView,
00093                              bool bReadOnly, QWidget *parentWidget,
00094                              const char *widgetName, QObject *parent, const char *name)
00095 : Kate::Document(parent, name),
00096   m_plugins (KateFactory::self()->plugins().count()),
00097   m_undoDontMerge(false),
00098   m_undoIgnoreCancel(false),
00099   lastUndoGroupWhenSaved( 0 ),
00100   docWasSavedWhenUndoWasEmpty( true ),
00101   m_modOnHd (false),
00102   m_modOnHdReason (0),
00103   m_job (0),
00104   m_tempFile (0),
00105   m_tabInterceptor(0)
00106 {
00107   m_undoComplexMerge=false;
00108   m_isInUndo = false;
00109   // my dcop object
00110   setObjId ("KateDocument#"+documentDCOPSuffix());
00111 
00112   // ktexteditor interfaces
00113   setBlockSelectionInterfaceDCOPSuffix (documentDCOPSuffix());
00114   setConfigInterfaceDCOPSuffix (documentDCOPSuffix());
00115   setConfigInterfaceExtensionDCOPSuffix (documentDCOPSuffix());
00116   setCursorInterfaceDCOPSuffix (documentDCOPSuffix());
00117   setEditInterfaceDCOPSuffix (documentDCOPSuffix());
00118   setEncodingInterfaceDCOPSuffix (documentDCOPSuffix());
00119   setHighlightingInterfaceDCOPSuffix (documentDCOPSuffix());
00120   setMarkInterfaceDCOPSuffix (documentDCOPSuffix());
00121   setMarkInterfaceExtensionDCOPSuffix (documentDCOPSuffix());
00122   setPrintInterfaceDCOPSuffix (documentDCOPSuffix());
00123   setSearchInterfaceDCOPSuffix (documentDCOPSuffix());
00124   setSelectionInterfaceDCOPSuffix (documentDCOPSuffix());
00125   setSelectionInterfaceExtDCOPSuffix (documentDCOPSuffix());
00126   setSessionConfigInterfaceDCOPSuffix (documentDCOPSuffix());
00127   setUndoInterfaceDCOPSuffix (documentDCOPSuffix());
00128   setWordWrapInterfaceDCOPSuffix (documentDCOPSuffix());
00129 
00130   // init local plugin array
00131   m_plugins.fill (0);
00132 
00133   // register doc at factory
00134   KateFactory::self()->registerDocument (this);
00135 
00136   m_reloading = false;
00137   m_loading = false;
00138   m_encodingSticky = false;
00139 
00140   m_buffer = new KateBuffer (this);
00141 
00142   // init the config object, be careful not to use it
00143   // until the initial readConfig() call is done
00144   m_config = new KateDocumentConfig (this);
00145 
00146   // init some more vars !
00147   m_activeView = 0L;
00148 
00149   hlSetByUser = false;
00150   m_fileType = -1;
00151   m_fileTypeSetByUser = false;
00152   setInstance( KateFactory::self()->instance() );
00153 
00154   editSessionNumber = 0;
00155   editIsRunning = false;
00156   m_editCurrentUndo = 0L;
00157   editWithUndo = false;
00158 
00159   m_docNameNumber = 0;
00160 
00161   m_bSingleViewMode = bSingleViewMode;
00162   m_bBrowserView = bBrowserView;
00163   m_bReadOnly = bReadOnly;
00164 
00165   m_marks.setAutoDelete( true );
00166   m_markPixmaps.setAutoDelete( true );
00167   m_markDescriptions.setAutoDelete( true );
00168   setMarksUserChangable( markType01 );
00169 
00170   m_undoMergeTimer = new QTimer(this);
00171   connect(m_undoMergeTimer, SIGNAL(timeout()), SLOT(undoCancel()));
00172 
00173   clearMarks ();
00174   clearUndo ();
00175   clearRedo ();
00176   setModified (false);
00177   docWasSavedWhenUndoWasEmpty = true;
00178 
00179   // normal hl
00180   m_buffer->setHighlight (0);
00181 
00182   m_extension = new KateBrowserExtension( this );
00183   m_arbitraryHL = new KateArbitraryHighlight();
00184   m_indenter = KateAutoIndent::createIndenter ( this, 0 );
00185 
00186   m_indenter->updateConfig ();
00187 
00188   // some nice signals from the buffer
00189   connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int)));
00190   connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated()));
00191 
00192   // if the user changes the highlight with the dialog, notify the doc
00193   connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged()));
00194 
00195   // signal for the arbitrary HL
00196   connect(m_arbitraryHL, SIGNAL(tagLines(KateView*, KateSuperRange*)), SLOT(tagArbitraryLines(KateView*, KateSuperRange*)));
00197 
00198   // signals for mod on hd
00199   connect( KateFactory::self()->dirWatch(), SIGNAL(dirty (const QString &)),
00200            this, SLOT(slotModOnHdDirty (const QString &)) );
00201 
00202   connect( KateFactory::self()->dirWatch(), SIGNAL(created (const QString &)),
00203            this, SLOT(slotModOnHdCreated (const QString &)) );
00204 
00205   connect( KateFactory::self()->dirWatch(), SIGNAL(deleted (const QString &)),
00206            this, SLOT(slotModOnHdDeleted (const QString &)) );
00207 
00208   // update doc name
00209   setDocName ("");
00210 
00211   // if single view mode, like in the konqui embedding, create a default view ;)
00212   if ( m_bSingleViewMode )
00213   {
00214     KTextEditor::View *view = createView( parentWidget, widgetName );
00215     insertChildClient( view );
00216     view->show();
00217     setWidget( view );
00218   }
00219 
00220   connect(this,SIGNAL(sigQueryClose(bool *, bool*)),this,SLOT(slotQueryClose_save(bool *, bool*)));
00221 
00222   m_isasking = 0;
00223 
00224   // plugins
00225   for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
00226   {
00227     if (config()->plugin (i))
00228       loadPlugin (i);
00229   }
00230 }
00231 
00232 //
00233 // KateDocument Destructor
00234 //
00235 KateDocument::~KateDocument()
00236 {
00237   // remove file from dirwatch
00238   deactivateDirWatch ();
00239 
00240   if (!singleViewMode())
00241   {
00242     // clean up remaining views
00243     m_views.setAutoDelete( true );
00244     m_views.clear();
00245   }
00246 
00247   delete m_editCurrentUndo;
00248 
00249   delete m_arbitraryHL;
00250 
00251   // cleanup the undo items, very important, truee :/
00252   undoItems.setAutoDelete(true);
00253   undoItems.clear();
00254 
00255   // clean up plugins
00256   unloadAllPlugins ();
00257 
00258   delete m_config;
00259   delete m_indenter;
00260   KateFactory::self()->deregisterDocument (this);
00261 }
00262 //END
00263 
00264 //BEGIN Plugins
00265 void KateDocument::unloadAllPlugins ()
00266 {
00267   for (uint i=0; i<m_plugins.count(); i++)
00268     unloadPlugin (i);
00269 }
00270 
00271 void KateDocument::enableAllPluginsGUI (KateView *view)
00272 {
00273   for (uint i=0; i<m_plugins.count(); i++)
00274     enablePluginGUI (m_plugins[i], view);
00275 }
00276 
00277 void KateDocument::disableAllPluginsGUI (KateView *view)
00278 {
00279   for (uint i=0; i<m_plugins.count(); i++)
00280     disablePluginGUI (m_plugins[i], view);
00281 }
00282 
00283 void KateDocument::loadPlugin (uint pluginIndex)
00284 {
00285   if (m_plugins[pluginIndex]) return;
00286 
00287   m_plugins[pluginIndex] = KTextEditor::createPlugin (QFile::encodeName((KateFactory::self()->plugins())[pluginIndex]->library()), this);
00288 
00289   enablePluginGUI (m_plugins[pluginIndex]);
00290 }
00291 
00292 void KateDocument::unloadPlugin (uint pluginIndex)
00293 {
00294   if (!m_plugins[pluginIndex]) return;
00295 
00296   disablePluginGUI (m_plugins[pluginIndex]);
00297 
00298   delete m_plugins[pluginIndex];
00299   m_plugins[pluginIndex] = 0L;
00300 }
00301 
00302 void KateDocument::enablePluginGUI (KTextEditor::Plugin *plugin, KateView *view)
00303 {
00304   if (!plugin) return;
00305   if (!KTextEditor::pluginViewInterface(plugin)) return;
00306 
00307   KXMLGUIFactory *factory = view->factory();
00308   if ( factory )
00309     factory->removeClient( view );
00310 
00311   KTextEditor::pluginViewInterface(plugin)->addView(view);
00312 
00313   if ( factory )
00314     factory->addClient( view );
00315 }
00316 
00317 void KateDocument::enablePluginGUI (KTextEditor::Plugin *plugin)
00318 {
00319   if (!plugin) return;
00320   if (!KTextEditor::pluginViewInterface(plugin)) return;
00321 
00322   for (uint i=0; i< m_views.count(); i++)
00323     enablePluginGUI (plugin, m_views.at(i));
00324 }
00325 
00326 void KateDocument::disablePluginGUI (KTextEditor::Plugin *plugin, KateView *view)
00327 {
00328   if (!plugin) return;
00329   if (!KTextEditor::pluginViewInterface(plugin)) return;
00330 
00331   KXMLGUIFactory *factory = view->factory();
00332   if ( factory )
00333     factory->removeClient( view );
00334 
00335   KTextEditor::pluginViewInterface( plugin )->removeView( view );
00336 
00337   if ( factory )
00338     factory->addClient( view );
00339 }
00340 
00341 void KateDocument::disablePluginGUI (KTextEditor::Plugin *plugin)
00342 {
00343   if (!plugin) return;
00344   if (!KTextEditor::pluginViewInterface(plugin)) return;
00345 
00346   for (uint i=0; i< m_views.count(); i++)
00347     disablePluginGUI (plugin, m_views.at(i));
00348 }
00349 //END
00350 
00351 //BEGIN KTextEditor::Document stuff
00352 
00353 KTextEditor::View *KateDocument::createView( QWidget *parent, const char *name )
00354 {
00355   KateView* newView = new KateView( this, parent, name);
00356   connect(newView, SIGNAL(cursorPositionChanged()), SLOT(undoCancel()));
00357   if ( s_fileChangedDialogsActivated )
00358     connect( newView, SIGNAL(gotFocus( Kate::View * )), this, SLOT(slotModifiedOnDisk()) );
00359   return newView;
00360 }
00361 
00362 QPtrList<KTextEditor::View> KateDocument::views () const
00363 {
00364   return m_textEditViews;
00365 }
00366 
00367 void KateDocument::setActiveView( KateView *view )
00368 {
00369   if ( m_activeView == view ) return;
00370 
00371   m_activeView = view;
00372 }
00373 //END
00374 
00375 //BEGIN KTextEditor::ConfigInterfaceExtension stuff
00376 
00377 uint KateDocument::configPages () const
00378 {
00379   return 10;
00380 }
00381 
00382 KTextEditor::ConfigPage *KateDocument::configPage (uint number, QWidget *parent, const char * )
00383 {
00384   switch( number )
00385   {
00386     case 0:
00387       return new KateViewDefaultsConfig (parent);
00388 
00389     case 1:
00390       return new KateSchemaConfigPage (parent, this);
00391 
00392     case 2:
00393       return new KateSelectConfigTab (parent);
00394 
00395     case 3:
00396       return new KateEditConfigTab (parent);
00397 
00398     case 4:
00399       return new KateIndentConfigTab (parent);
00400 
00401     case 5:
00402       return new KateSaveConfigTab (parent);
00403 
00404     case 6:
00405       return new KateHlConfigPage (parent, this);
00406 
00407     case 7:
00408       return new KateFileTypeConfigTab (parent);
00409 
00410     case 8:
00411       return new KateEditKeyConfiguration (parent, this);
00412 
00413     case 9:
00414       return new KatePartPluginConfigPage (parent);
00415 
00416     default:
00417       return 0;
00418   }
00419 
00420   return 0;
00421 }
00422 
00423 QString KateDocument::configPageName (uint number) const
00424 {
00425   switch( number )
00426   {
00427     case 0:
00428       return i18n ("Appearance");
00429 
00430     case 1:
00431       return i18n ("Fonts & Colors");
00432 
00433     case 2:
00434       return i18n ("Cursor & Selection");
00435 
00436     case 3:
00437       return i18n ("Editing");
00438 
00439     case 4:
00440       return i18n ("Indentation");
00441 
00442     case 5:
00443       return i18n("Open/Save");
00444 
00445     case 6:
00446       return i18n ("Highlighting");
00447 
00448     case 7:
00449       return i18n("Filetypes");
00450 
00451     case 8:
00452       return i18n ("Shortcuts");
00453 
00454     case 9:
00455       return i18n ("Plugins");
00456 
00457     default:
00458       return QString ("");
00459   }
00460 
00461   return QString ("");
00462 }
00463 
00464 QString KateDocument::configPageFullName (uint number) const
00465 {
00466   switch( number )
00467   {
00468     case 0:
00469       return i18n("Appearance");
00470 
00471     case 1:
00472       return i18n ("Font & Color Schemas");
00473 
00474     case 2:
00475       return i18n ("Cursor & Selection Behavior");
00476 
00477     case 3:
00478       return i18n ("Editing Options");
00479 
00480     case 4:
00481       return i18n ("Indentation Rules");
00482 
00483     case 5:
00484       return i18n("File Opening & Saving");
00485 
00486     case 6:
00487       return i18n ("Highlighting Rules");
00488 
00489     case 7:
00490       return i18n("Filetype Specific Settings");
00491 
00492     case 8:
00493       return i18n ("Shortcuts Configuration");
00494 
00495     case 9:
00496       return i18n ("Plugin Manager");
00497 
00498     default:
00499       return QString ("");
00500   }
00501 
00502   return QString ("");
00503 }
00504 
00505 QPixmap KateDocument::configPagePixmap (uint number, int size) const
00506 {
00507   switch( number )
00508   {
00509     case 0:
00510       return BarIcon("view_text",size);
00511 
00512     case 1:
00513       return BarIcon("colorize", size);
00514 
00515     case 2:
00516         return BarIcon("frame_edit", size);
00517 
00518     case 3:
00519       return BarIcon("edit", size);
00520 
00521     case 4:
00522       return BarIcon("rightjust", size);
00523 
00524     case 5:
00525       return BarIcon("filesave", size);
00526 
00527     case 6:
00528       return BarIcon("source", size);
00529 
00530     case 7:
00531       return BarIcon("edit", size);
00532 
00533     case 8:
00534       return BarIcon("key_enter", size);
00535 
00536     case 9:
00537       return BarIcon("connect_established", size);
00538 
00539     default:
00540       return BarIcon("edit", size);
00541   }
00542 
00543   return BarIcon("edit", size);
00544 }
00545 //END
00546 
00547 //BEGIN KTextEditor::EditInterface stuff
00548 
00549 QString KateDocument::text() const
00550 {
00551   QString s;
00552 
00553   for (uint i = 0; i < m_buffer->count(); i++)
00554   {
00555     KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00556 
00557     if (textLine)
00558     {
00559       s.append (textLine->string());
00560 
00561       if ((i+1) < m_buffer->count())
00562         s.append('\n');
00563     }
00564   }
00565 
00566   return s;
00567 }
00568 
00569 QString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol ) const
00570 {
00571   return text(startLine, startCol, endLine, endCol, false);
00572 }
00573 
00574 QString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise) const
00575 {
00576   if ( blockwise && (startCol > endCol) )
00577     return QString ();
00578 
00579   QString s;
00580 
00581   if (startLine == endLine)
00582   {
00583     if (startCol > endCol)
00584       return QString ();
00585 
00586     KateTextLine::Ptr textLine = m_buffer->plainLine(startLine);
00587 
00588     if ( !textLine )
00589       return QString ();
00590 
00591     return textLine->string(startCol, endCol-startCol);
00592   }
00593   else
00594   {
00595 
00596     for (uint i = startLine; (i <= endLine) && (i < m_buffer->count()); i++)
00597     {
00598       KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00599 
00600       if ( !blockwise )
00601       {
00602         if (i == startLine)
00603           s.append (textLine->string(startCol, textLine->length()-startCol));
00604         else if (i == endLine)
00605           s.append (textLine->string(0, endCol));
00606         else
00607           s.append (textLine->string());
00608       }
00609       else
00610       {
00611         s.append( textLine->string( startCol, endCol-startCol));
00612       }
00613 
00614       if ( i < endLine )
00615         s.append('\n');
00616     }
00617   }
00618 
00619   return s;
00620 }
00621 
00622 QString KateDocument::textLine( uint line ) const
00623 {
00624   KateTextLine::Ptr l = m_buffer->plainLine(line);
00625 
00626   if (!l)
00627     return QString();
00628 
00629   return l->string();
00630 }
00631 
00632 bool KateDocument::setText(const QString &s)
00633 {
00634   if (!isReadWrite())
00635     return false;
00636 
00637   QPtrList<KTextEditor::Mark> m = marks ();
00638   QValueList<KTextEditor::Mark> msave;
00639 
00640   for (uint i=0; i < m.count(); i++)
00641     msave.append (*m.at(i));
00642 
00643   editStart ();
00644 
00645   // delete the text
00646   clear();
00647 
00648   // insert the new text
00649   insertText (0, 0, s);
00650 
00651   editEnd ();
00652 
00653   for (uint i=0; i < msave.count(); i++)
00654     setMark (msave[i].line, msave[i].type);
00655 
00656   return true;
00657 }
00658 
00659 bool KateDocument::clear()
00660 {
00661   if (!isReadWrite())
00662     return false;
00663 
00664   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) {
00665     view->clear();
00666     view->tagAll();
00667     view->update();
00668   }
00669 
00670   clearMarks ();
00671 
00672   return removeText (0,0,lastLine()+1, 0);
00673 }
00674 
00675 bool KateDocument::insertText( uint line, uint col, const QString &s)
00676 {
00677   return insertText (line, col, s, false);
00678 }
00679 
00680 bool KateDocument::insertText( uint line, uint col, const QString &s, bool blockwise )
00681 {
00682   if (!isReadWrite())
00683     return false;
00684 
00685   if (s.isEmpty())
00686     return true;
00687 
00688   if (line == numLines())
00689     editInsertLine(line,"");
00690   else if (line > lastLine())
00691     return false;
00692 
00693   editStart ();
00694 
00695   uint insertPos = col;
00696   uint len = s.length();
00697 
00698   QString buf;
00699 
00700   bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn && ! m_isInUndo );
00701   uint tw = config()->tabWidth();
00702   uint insertPosExpanded = insertPos;
00703   KateTextLine::Ptr l = m_buffer->line( line );
00704   if (l != 0)
00705     insertPosExpanded = l->cursorX( insertPos, tw );
00706 
00707   for (uint pos = 0; pos < len; pos++)
00708   {
00709     QChar ch = s[pos];
00710 
00711     if (ch == '\n')
00712     {
00713       editInsertText (line, insertPos, buf);
00714 
00715       if ( !blockwise )
00716       {
00717         editWrapLine (line, insertPos + buf.length());
00718         insertPos = insertPosExpanded = 0;
00719       }
00720       else
00721       {
00722         if ( line == lastLine() )
00723           editWrapLine (line, insertPos + buf.length());
00724       }
00725 
00726       line++;
00727       buf.truncate(0);
00728       l = m_buffer->line( line );
00729       if (l)
00730         insertPosExpanded = l->cursorX( insertPos, tw );
00731     }
00732     else
00733     {
00734       if ( replacetabs && ch == '\t' )
00735       {
00736         uint tr = tw - ( insertPosExpanded+buf.length() )%tw;
00737         for ( uint i=0; i < tr; i++ )
00738           buf += ' ';
00739       }
00740       else
00741         buf += ch; // append char to buffer
00742     }
00743   }
00744 
00745   editInsertText (line, insertPos, buf);
00746 
00747   editEnd ();
00748   emit textInserted(line,insertPos);
00749   return true;
00750 }
00751 
00752 bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol )
00753 {
00754   return removeText (startLine, startCol, endLine, endCol, false);
00755 }
00756 
00757 bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise)
00758 {
00759   if (!isReadWrite())
00760     return false;
00761 
00762   if ( blockwise && (startCol > endCol) )
00763     return false;
00764 
00765   if ( startLine > endLine )
00766     return false;
00767 
00768   if ( startLine > lastLine() )
00769     return false;
00770 
00771   if (!blockwise) {
00772     emit aboutToRemoveText(KateTextRange(startLine,startCol,endLine,endCol));
00773   }
00774   editStart ();
00775 
00776   if ( !blockwise )
00777   {
00778     if ( endLine > lastLine() )
00779     {
00780       endLine = lastLine()+1;
00781       endCol = 0;
00782     }
00783 
00784     if (startLine == endLine)
00785     {
00786       editRemoveText (startLine, startCol, endCol-startCol);
00787     }
00788     else if ((startLine+1) == endLine)
00789     {
00790       if ( (m_buffer->plainLine(startLine)->length()-startCol) > 0 )
00791         editRemoveText (startLine, startCol, m_buffer->plainLine(startLine)->length()-startCol);
00792 
00793       editRemoveText (startLine+1, 0, endCol);
00794       editUnWrapLine (startLine);
00795     }
00796     else
00797     {
00798       for (uint line = endLine; line >= startLine; line--)
00799       {
00800         if ((line > startLine) && (line < endLine))
00801         {
00802           editRemoveLine (line);
00803         }
00804         else
00805         {
00806           if (line == endLine)
00807           {
00808             if ( endLine <= lastLine() )
00809               editRemoveText (line, 0, endCol);
00810           }
00811           else
00812           {
00813             if ( (m_buffer->plainLine(line)->length()-startCol) > 0 )
00814               editRemoveText (line, startCol, m_buffer->plainLine(line)->length()-startCol);
00815 
00816             editUnWrapLine (startLine);
00817           }
00818         }
00819 
00820         if ( line == 0 )
00821           break;
00822       }
00823     }
00824   } // if ( ! blockwise )
00825   else
00826   {
00827     if ( endLine > lastLine() )
00828       endLine = lastLine ();
00829 
00830     for (uint line = endLine; line >= startLine; line--)
00831     {
00832 
00833       editRemoveText (line, startCol, endCol-startCol);
00834 
00835       if ( line == 0 )
00836         break;
00837     }
00838   }
00839 
00840   editEnd ();
00841   emit textRemoved();
00842   return true;
00843 }
00844 
00845 bool KateDocument::insertLine( uint l, const QString &str )
00846 {
00847   if (!isReadWrite())
00848     return false;
00849 
00850   if (l > numLines())
00851     return false;
00852 
00853   return editInsertLine (l, str);
00854 }
00855 
00856 bool KateDocument::removeLine( uint line )
00857 {
00858   if (!isReadWrite())
00859     return false;
00860 
00861   if (line > lastLine())
00862     return false;
00863 
00864   return editRemoveLine (line);
00865 }
00866 
00867 uint KateDocument::length() const
00868 {
00869   uint l = 0;
00870 
00871   for (uint i = 0; i < m_buffer->count(); i++)
00872   {
00873     KateTextLine::Ptr line = m_buffer->plainLine(i);
00874 
00875     if (line)
00876       l += line->length();
00877   }
00878 
00879   return l;
00880 }
00881 
00882 uint KateDocument::numLines() const
00883 {
00884   return m_buffer->count();
00885 }
00886 
00887 uint KateDocument::numVisLines() const
00888 {
00889   return m_buffer->countVisible ();
00890 }
00891 
00892 int KateDocument::lineLength ( uint line ) const
00893 {
00894   KateTextLine::Ptr l = m_buffer->plainLine(line);
00895 
00896   if (!l)
00897     return -1;
00898 
00899   return l->length();
00900 }
00901 //END
00902 
00903 //BEGIN KTextEditor::EditInterface internal stuff
00904 //
00905 // Starts an edit session with (or without) undo, update of view disabled during session
00906 //
00907 void KateDocument::editStart (bool withUndo)
00908 {
00909   editSessionNumber++;
00910 
00911   if (editSessionNumber > 1)
00912     return;
00913 
00914   editIsRunning = true;
00915   editWithUndo = withUndo;
00916 
00917   if (editWithUndo)
00918     undoStart();
00919   else
00920     undoCancel();
00921 
00922   for (uint z = 0; z < m_views.count(); z++)
00923   {
00924     m_views.at(z)->editStart ();
00925   }
00926 
00927   m_buffer->editStart ();
00928 }
00929 
00930 void KateDocument::undoStart()
00931 {
00932   if (m_editCurrentUndo || (m_activeView && m_activeView->imComposeEvent())) return;
00933 
00934   // Make sure the buffer doesn't get bigger than requested
00935   if ((config()->undoSteps() > 0) && (undoItems.count() > config()->undoSteps()))
00936   {
00937     undoItems.setAutoDelete(true);
00938     undoItems.removeFirst();
00939     undoItems.setAutoDelete(false);
00940     docWasSavedWhenUndoWasEmpty = false;
00941   }
00942 
00943   // new current undo item
00944   m_editCurrentUndo = new KateUndoGroup(this);
00945 }
00946 
00947 void KateDocument::undoEnd()
00948 {
00949   if (m_activeView && m_activeView->imComposeEvent())
00950     return;
00951 
00952   if (m_editCurrentUndo)
00953   {
00954     bool changedUndo = false;
00955 
00956     if (m_editCurrentUndo->isEmpty())
00957       delete m_editCurrentUndo;
00958     else if (!m_undoDontMerge && undoItems.last() && undoItems.last()->merge(m_editCurrentUndo,m_undoComplexMerge))
00959       delete m_editCurrentUndo;
00960     else
00961     {
00962       undoItems.append(m_editCurrentUndo);
00963       changedUndo = true;
00964     }
00965 
00966     m_undoDontMerge = false;
00967     m_undoIgnoreCancel = true;
00968 
00969     m_editCurrentUndo = 0L;
00970 
00971     // (Re)Start the single-shot timer to cancel the undo merge
00972     // the user has 5 seconds to input more data, or undo merging gets canceled for the current undo item.
00973     m_undoMergeTimer->start(5000, true);
00974 
00975     if (changedUndo)
00976       emit undoChanged();
00977   }
00978 }
00979 
00980 void KateDocument::undoCancel()
00981 {
00982   if (m_undoIgnoreCancel) {
00983     m_undoIgnoreCancel = false;
00984     return;
00985   }
00986 
00987   m_undoDontMerge = true;
00988 
00989   Q_ASSERT(!m_editCurrentUndo);
00990 
00991   // As you can see by the above assert, neither of these should really be required
00992   delete m_editCurrentUndo;
00993   m_editCurrentUndo = 0L;
00994 }
00995 
00996 void KateDocument::undoSafePoint() {
00997   Q_ASSERT(m_editCurrentUndo);
00998   if (!m_editCurrentUndo) return;
00999   m_editCurrentUndo->safePoint();
01000 }
01001 
01002 //
01003 // End edit session and update Views
01004 //
01005 void KateDocument::editEnd ()
01006 {
01007   if (editSessionNumber == 0)
01008     return;
01009 
01010   // wrap the new/changed text, if something really changed!
01011   if (m_buffer->editChanged() && (editSessionNumber == 1))
01012     if (editWithUndo && config()->wordWrap())
01013       wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
01014 
01015   editSessionNumber--;
01016 
01017   if (editSessionNumber > 0)
01018     return;
01019 
01020   // end buffer edit, will trigger hl update
01021   // this will cause some possible adjustment of tagline start/end
01022   m_buffer->editEnd ();
01023 
01024   if (editWithUndo)
01025     undoEnd();
01026 
01027   // edit end for all views !!!!!!!!!
01028   for (uint z = 0; z < m_views.count(); z++)
01029     m_views.at(z)->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
01030 
01031   if (m_buffer->editChanged())
01032   {
01033     setModified(true);
01034     emit textChanged ();
01035   }
01036 
01037   editIsRunning = false;
01038 }
01039 
01040 bool KateDocument::wrapText (uint startLine, uint endLine)
01041 {
01042   uint col = config()->wordWrapAt();
01043 
01044   if (col == 0)
01045     return false;
01046 
01047   editStart ();
01048 
01049   for (uint line = startLine; (line <= endLine) && (line < numLines()); line++)
01050   {
01051     KateTextLine::Ptr l = m_buffer->line(line);
01052 
01053     if (!l)
01054       return false;
01055 
01056     kdDebug (13020) << "try wrap line: " << line << endl;
01057 
01058     if (l->lengthWithTabs(m_buffer->tabWidth()) > col)
01059     {
01060       KateTextLine::Ptr nextl = m_buffer->line(line+1);
01061 
01062       kdDebug (13020) << "do wrap line: " << line << endl;
01063 
01064       const QChar *text = l->text();
01065       uint eolPosition = l->length()-1;
01066 
01067       // take tabs into account here, too
01068       uint x = 0;
01069       const QString & t = l->string();
01070       uint z2 = 0;
01071       for ( ; z2 < l->length(); z2++)
01072       {
01073         if (t[z2] == QChar('\t'))
01074           x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
01075         else
01076           x++;
01077 
01078         if (x > col)
01079           break;
01080       }
01081 
01082       uint searchStart = kMin (z2, l->length()-1);
01083 
01084       // If where we are wrapping is an end of line and is a space we don't
01085       // want to wrap there
01086       if (searchStart == eolPosition && text[searchStart].isSpace())
01087         searchStart--;
01088 
01089       // Scan backwards looking for a place to break the line
01090       // We are not interested in breaking at the first char
01091       // of the line (if it is a space), but we are at the second
01092       // anders: if we can't find a space, try breaking on a word
01093       // boundry, using KateHighlight::canBreakAt().
01094       // This could be a priority (setting) in the hl/filetype/document
01095       int z = 0;
01096       uint nw = 0; // alternative position, a non word character
01097       for (z=searchStart; z > 0; z--)
01098       {
01099         if (text[z].isSpace()) break;
01100         if ( ! nw && highlight()->canBreakAt( text[z] , l->attribute(z) ) )
01101         nw = z;
01102       }
01103 
01104       if (z > 0)
01105       {
01106         // cu space
01107         editRemoveText (line, z, 1);
01108       }
01109       else
01110       {
01111         // There was no space to break at so break at a nonword character if
01112         // found, or at the wrapcolumn ( that needs be configurable )
01113         // Don't try and add any white space for the break
01114         if ( nw && nw < col ) nw++; // break on the right side of the character
01115         z = nw ? nw : col;
01116       }
01117 
01118       if (nextl && !nextl->isAutoWrapped())
01119       {
01120         editWrapLine (line, z, true);
01121         editMarkLineAutoWrapped (line+1, true);
01122 
01123         endLine++;
01124       }
01125       else
01126       {
01127         if (nextl && (nextl->length() > 0) && !nextl->getChar(0).isSpace() && ((l->length() < 1) || !l->getChar(l->length()-1).isSpace()))
01128           editInsertText (line+1, 0, QString (" "));
01129 
01130         bool newLineAdded = false;
01131         editWrapLine (line, z, false, &newLineAdded);
01132 
01133         editMarkLineAutoWrapped (line+1, true);
01134 
01135         endLine++;
01136       }
01137     }
01138   }
01139 
01140   editEnd ();
01141 
01142   return true;
01143 }
01144 
01145 void KateDocument::editAddUndo (KateUndoGroup::UndoType type, uint line, uint col, uint len, const QString &text)
01146 {
01147   if (editIsRunning && editWithUndo && m_editCurrentUndo) {
01148     m_editCurrentUndo->addItem(type, line, col, len, text);
01149 
01150     // Clear redo buffer
01151     if (redoItems.count()) {
01152       redoItems.setAutoDelete(true);
01153       redoItems.clear();
01154       redoItems.setAutoDelete(false);
01155     }
01156   }
01157 }
01158 
01159 bool KateDocument::editInsertText ( uint line, uint col, const QString &str )
01160 {
01161   if (!isReadWrite())
01162     return false;
01163 
01164   QString s = str;
01165 
01166   KateTextLine::Ptr l = m_buffer->line(line);
01167 
01168   if (!l)
01169     return false;
01170 
01171     if ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn && ! m_isInUndo )
01172     {
01173       uint tw = config()->tabWidth();
01174       int pos = 0;
01175       uint l = 0;
01176       while ( (pos = s.find('\t')) > -1 )
01177       {
01178         l = tw - ( (col + pos)%tw );
01179         s.replace( pos, 1, QString().fill( ' ', l ) );
01180       }
01181     }
01182 
01183   editStart ();
01184 
01185   editAddUndo (KateUndoGroup::editInsertText, line, col, s.length(), s);
01186 
01187   l->insertText (col, s.length(), s.unicode());
01188 //   removeTrailingSpace(line); // ### nessecary?
01189 
01190   m_buffer->changeLine(line);
01191 
01192   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01193     it.current()->editTextInserted (line, col, s.length());
01194 
01195   editEnd ();
01196 
01197   return true;
01198 }
01199 
01200 bool KateDocument::editRemoveText ( uint line, uint col, uint len )
01201 {
01202   if (!isReadWrite())
01203     return false;
01204 
01205   KateTextLine::Ptr l = m_buffer->line(line);
01206 
01207   if (!l)
01208     return false;
01209 
01210   editStart ();
01211 
01212   editAddUndo (KateUndoGroup::editRemoveText, line, col, len, l->string().mid(col, len));
01213 
01214   l->removeText (col, len);
01215   removeTrailingSpace( line );
01216 
01217   m_buffer->changeLine(line);
01218 
01219   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01220     it.current()->editTextRemoved (line, col, len);
01221 
01222   editEnd ();
01223 
01224   return true;
01225 }
01226 
01227 bool KateDocument::editMarkLineAutoWrapped ( uint line, bool autowrapped )
01228 {
01229   if (!isReadWrite())
01230     return false;
01231 
01232   KateTextLine::Ptr l = m_buffer->line(line);
01233 
01234   if (!l)
01235     return false;
01236 
01237   editStart ();
01238 
01239   editAddUndo (KateUndoGroup::editMarkLineAutoWrapped, line, autowrapped ? 1 : 0, 0, QString::null);
01240 
01241   l->setAutoWrapped (autowrapped);
01242 
01243   m_buffer->changeLine(line);
01244 
01245   editEnd ();
01246 
01247   return true;
01248 }
01249 
01250 bool KateDocument::editWrapLine ( uint line, uint col, bool newLine, bool *newLineAdded)
01251 {
01252   if (!isReadWrite())
01253     return false;
01254 
01255   KateTextLine::Ptr l = m_buffer->line(line);
01256 
01257   if (!l)
01258     return false;
01259 
01260   editStart ();
01261 
01262   KateTextLine::Ptr nextLine = m_buffer->line(line+1);
01263 
01264   int pos = l->length() - col;
01265 
01266   if (pos < 0)
01267     pos = 0;
01268 
01269   editAddUndo (KateUndoGroup::editWrapLine, line, col, pos, (!nextLine || newLine) ? "1" : "0");
01270 
01271   if (!nextLine || newLine)
01272   {
01273     KateTextLine::Ptr textLine = new KateTextLine();
01274 
01275     textLine->insertText (0, pos, l->text()+col, l->attributes()+col);
01276     l->truncate(col);
01277 
01278     m_buffer->insertLine (line+1, textLine);
01279     m_buffer->changeLine(line);
01280 
01281     QPtrList<KTextEditor::Mark> list;
01282     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
01283     {
01284       if( it.current()->line >