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);
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 
00703   for (uint pos = 0; pos < len; pos++)
00704   {
00705     QChar ch = s[pos];
00706 
00707     if (ch == '\n')
00708     {
00709       if ( !blockwise )
00710       {
00711         editInsertText (line, insertPos, buf);
00712         editWrapLine (line, insertPos + buf.length());
00713       }
00714       else
00715       {
00716         editInsertText (line, col, buf);
00717 
00718         if ( line == lastLine() )
00719           editWrapLine (line, col + buf.length());
00720       }
00721 
00722       line++;
00723       insertPos = 0;
00724       buf.truncate(0);
00725     }
00726     else
00727     {
00728       if ( replacetabs && ch == '\t' )
00729       {
00730         uint tr = tw - ( ((blockwise?col:insertPos)+buf.length())%tw ); //###
00731         for ( uint i=0; i < tr; i++ )
00732           buf += ' ';
00733       }
00734       else
00735         buf += ch; // append char to buffer
00736     }
00737   }
00738 
00739   if ( !blockwise )
00740     editInsertText (line, insertPos, buf);
00741   else
00742     editInsertText (line, col, buf);
00743 
00744   editEnd ();
00745   emit textInserted(line,insertPos);
00746   return true;
00747 }
00748 
00749 bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol )
00750 {
00751   return removeText (startLine, startCol, endLine, endCol, false);
00752 }
00753 
00754 bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise)
00755 {
00756   if (!isReadWrite())
00757     return false;
00758 
00759   if ( blockwise && (startCol > endCol) )
00760     return false;
00761 
00762   if ( startLine > endLine )
00763     return false;
00764 
00765   if ( startLine > lastLine() )
00766     return false;
00767 
00768   if (!blockwise) {
00769     emit aboutToRemoveText(KateTextRange(startLine,startCol,endLine,endCol));
00770   }
00771   editStart ();
00772 
00773   if ( !blockwise )
00774   {
00775     if ( endLine > lastLine() )
00776     {
00777       endLine = lastLine()+1;
00778       endCol = 0;
00779     }
00780 
00781     if (startLine == endLine)
00782     {
00783       editRemoveText (startLine, startCol, endCol-startCol);
00784     }
00785     else if ((startLine+1) == endLine)
00786     {
00787       if ( (m_buffer->plainLine(startLine)->length()-startCol) > 0 )
00788         editRemoveText (startLine, startCol, m_buffer->plainLine(startLine)->length()-startCol);
00789 
00790       editRemoveText (startLine+1, 0, endCol);
00791       editUnWrapLine (startLine);
00792     }
00793     else
00794     {
00795       for (uint line = endLine; line >= startLine; line--)
00796       {
00797         if ((line > startLine) && (line < endLine))
00798         {
00799           editRemoveLine (line);
00800         }
00801         else
00802         {
00803           if (line == endLine)
00804           {
00805             if ( endLine <= lastLine() )
00806               editRemoveText (line, 0, endCol);
00807           }
00808           else
00809           {
00810             if ( (m_buffer->plainLine(line)->length()-startCol) > 0 )
00811               editRemoveText (line, startCol, m_buffer->plainLine(line)->length()-startCol);
00812 
00813             editUnWrapLine (startLine);
00814           }
00815         }
00816 
00817         if ( line == 0 )
00818           break;
00819       }
00820     }
00821   } // if ( ! blockwise )
00822   else
00823   {
00824     if ( endLine > lastLine() )
00825       endLine = lastLine ();
00826 
00827     for (uint line = endLine; line >= startLine; line--)
00828     {
00829 
00830       editRemoveText (line, startCol, endCol-startCol);
00831 
00832       if ( line == 0 )
00833         break;
00834     }
00835   }
00836 
00837   editEnd ();
00838   emit textRemoved();
00839   return true;
00840 }
00841 
00842 bool KateDocument::insertLine( uint l, const QString &str )
00843 {
00844   if (!isReadWrite())
00845     return false;
00846 
00847   if (l > numLines())
00848     return false;
00849 
00850   return editInsertLine (l, str);
00851 }
00852 
00853 bool KateDocument::removeLine( uint line )
00854 {
00855   if (!isReadWrite())
00856     return false;
00857 
00858   if (line > lastLine())
00859     return false;
00860 
00861   return editRemoveLine (line);
00862 }
00863 
00864 uint KateDocument::length() const
00865 {
00866   uint l = 0;
00867 
00868   for (uint i = 0; i < m_buffer->count(); i++)
00869   {
00870     KateTextLine::Ptr line = m_buffer->plainLine(i);
00871 
00872     if (line)
00873       l += line->length();
00874   }
00875 
00876   return l;
00877 }
00878 
00879 uint KateDocument::numLines() const
00880 {
00881   return m_buffer->count();
00882 }
00883 
00884 uint KateDocument::numVisLines() const
00885 {
00886   return m_buffer->countVisible ();
00887 }
00888 
00889 int KateDocument::lineLength ( uint line ) const
00890 {
00891   KateTextLine::Ptr l = m_buffer->plainLine(line);
00892 
00893   if (!l)
00894     return -1;
00895 
00896   return l->length();
00897 }
00898 //END
00899 
00900 //BEGIN KTextEditor::EditInterface internal stuff
00901 //
00902 // Starts an edit session with (or without) undo, update of view disabled during session
00903 //
00904 void KateDocument::editStart (bool withUndo)
00905 {
00906   editSessionNumber++;
00907 
00908   if (editSessionNumber > 1)
00909     return;
00910 
00911   editIsRunning = true;
00912   editWithUndo = withUndo;
00913 
00914   if (editWithUndo)
00915     undoStart();
00916   else
00917     undoCancel();
00918 
00919   for (uint z = 0; z < m_views.count(); z++)
00920   {
00921     m_views.at(z)->editStart ();
00922   }
00923 
00924   m_buffer->editStart ();
00925 }
00926 
00927 void KateDocument::undoStart()
00928 {
00929   if (m_editCurrentUndo || (m_activeView && m_activeView->imComposeEvent())) return;
00930 
00931   // Make sure the buffer doesn't get bigger than requested
00932   if ((config()->undoSteps() > 0) && (undoItems.count() > config()->undoSteps()))
00933   {
00934     undoItems.setAutoDelete(true);
00935     undoItems.removeFirst();
00936     undoItems.setAutoDelete(false);
00937     docWasSavedWhenUndoWasEmpty = false;
00938   }
00939 
00940   // new current undo item
00941   m_editCurrentUndo = new KateUndoGroup(this);
00942 }
00943 
00944 void KateDocument::undoEnd()
00945 {
00946   if (m_activeView && m_activeView->imComposeEvent())
00947     return;
00948 
00949   if (m_editCurrentUndo)
00950   {
00951     bool changedUndo = false;
00952 
00953     if (m_editCurrentUndo->isEmpty())
00954       delete m_editCurrentUndo;
00955     else if (!m_undoDontMerge && undoItems.last() && undoItems.last()->merge(m_editCurrentUndo,m_undoComplexMerge))
00956       delete m_editCurrentUndo;
00957     else
00958     {
00959       undoItems.append(m_editCurrentUndo);
00960       changedUndo = true;
00961     }
00962 
00963     m_undoDontMerge = false;
00964     m_undoIgnoreCancel = true;
00965 
00966     m_editCurrentUndo = 0L;
00967 
00968     // (Re)Start the single-shot timer to cancel the undo merge
00969     // the user has 5 seconds to input more data, or undo merging gets canceled for the current undo item.
00970     m_undoMergeTimer->start(5000, true);
00971 
00972     if (changedUndo)
00973       emit undoChanged();
00974   }
00975 }
00976 
00977 void KateDocument::undoCancel()
00978 {
00979   if (m_undoIgnoreCancel) {
00980     m_undoIgnoreCancel = false;
00981     return;
00982   }
00983 
00984   m_undoDontMerge = true;
00985 
00986   Q_ASSERT(!m_editCurrentUndo);
00987 
00988   // As you can see by the above assert, neither of these should really be required
00989   delete m_editCurrentUndo;
00990   m_editCurrentUndo = 0L;
00991 }
00992 
00993 void KateDocument::undoSafePoint() {
00994   Q_ASSERT(m_editCurrentUndo);
00995   if (!m_editCurrentUndo) return;
00996   m_editCurrentUndo->safePoint();
00997 }
00998 
00999 //
01000 // End edit session and update Views
01001 //
01002 void KateDocument::editEnd ()
01003 {
01004   if (editSessionNumber == 0)
01005     return;
01006 
01007   // wrap the new/changed text, if something really changed!
01008   if (m_buffer->editChanged() && (editSessionNumber == 1))
01009     if (editWithUndo && config()->wordWrap())
01010       wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
01011 
01012   editSessionNumber--;
01013 
01014   if (editSessionNumber > 0)
01015     return;
01016 
01017   // end buffer edit, will trigger hl update
01018   // this will cause some possible adjustment of tagline start/end
01019   m_buffer->editEnd ();
01020 
01021   if (editWithUndo)
01022     undoEnd();
01023 
01024   // edit end for all views !!!!!!!!!
01025   for (uint z = 0; z < m_views.count(); z++)
01026     m_views.at(z)->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
01027 
01028   if (m_buffer->editChanged())
01029   {
01030     setModified(true);
01031     emit textChanged ();
01032   }
01033 
01034   editIsRunning = false;
01035 }
01036 
01037 bool KateDocument::wrapText (uint startLine, uint endLine)
01038 {
01039   uint col = config()->wordWrapAt();
01040 
01041   if (col == 0)
01042     return false;
01043 
01044   editStart ();
01045 
01046   for (uint line = startLine; (line <= endLine) && (line < numLines()); line++)
01047   {
01048     KateTextLine::Ptr l = m_buffer->line(line);
01049 
01050     if (!l)
01051       return false;
01052 
01053     kdDebug (13020) << "try wrap line: " << line << endl;
01054 
01055     if (l->lengthWithTabs(m_buffer->tabWidth()) > col)
01056     {
01057       KateTextLine::Ptr nextl = m_buffer->line(line+1);
01058 
01059       kdDebug (13020) << "do wrap line: " << line << endl;
01060 
01061       const QChar *text = l->text();
01062       uint eolPosition = l->length()-1;
01063 
01064       // take tabs into account here, too
01065       uint x = 0;
01066       const QString & t = l->string();
01067       uint z2 = 0;
01068       for ( ; z2 < l->length(); z2++)
01069       {
01070         if (t[z2] == QChar('\t'))
01071           x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
01072         else
01073           x++;
01074 
01075         if (x > col)
01076           break;
01077       }
01078 
01079       uint searchStart = KMIN (z2, l->length()-1);
01080 
01081       // If where we are wrapping is an end of line and is a space we don't
01082       // want to wrap there
01083       if (searchStart == eolPosition && text[searchStart].isSpace())
01084         searchStart--;
01085 
01086       // Scan backwards looking for a place to break the line
01087       // We are not interested in breaking at the first char
01088       // of the line (if it is a space), but we are at the second
01089       // anders: if we can't find a space, try breaking on a word
01090       // boundry, using KateHighlight::canBreakAt().
01091       // This could be a priority (setting) in the hl/filetype/document
01092       int z = 0;
01093       uint nw = 0; // alternative position, a non word character
01094       for (z=searchStart; z > 0; z--)
01095       {
01096         if (text[z].isSpace()) break;
01097         if ( ! nw && highlight()->canBreakAt( text[z] , l->attribute(z) ) )
01098         nw = z;
01099       }
01100 
01101       if (z > 0)
01102       {
01103         // cu space
01104         editRemoveText (line, z, 1);
01105       }
01106       else
01107       {
01108         // There was no space to break at so break at a nonword character if
01109         // found, or at the wrapcolumn ( that needs be configurable )
01110         // Don't try and add any white space for the break
01111         if ( nw && nw < col ) nw++; // break on the right side of the character
01112         z = nw ? nw : col;
01113       }
01114 
01115       if (nextl && !nextl->isAutoWrapped())
01116       {
01117         editWrapLine (line, z, true);
01118         editMarkLineAutoWrapped (line+1, true);
01119 
01120         endLine++;
01121       }
01122       else
01123       {
01124         if (nextl && (nextl->length() > 0) && !nextl->getChar(0).isSpace() && ((l->length() < 1) || !l->getChar(l->length()-1).isSpace()))
01125           editInsertText (line+1, 0, QString (" "));
01126 
01127         bool newLineAdded = false;
01128         editWrapLine (line, z, false, &newLineAdded);
01129 
01130         editMarkLineAutoWrapped (line+1, true);
01131 
01132         endLine++;
01133       }
01134     }
01135   }
01136 
01137   editEnd ();
01138 
01139   return true;
01140 }
01141 
01142 void KateDocument::editAddUndo (KateUndoGroup::UndoType type, uint line, uint col, uint len, const QString &text)
01143 {
01144   if (editIsRunning && editWithUndo && m_editCurrentUndo) {
01145     m_editCurrentUndo->addItem(type, line, col, len, text);
01146 
01147     // Clear redo buffer
01148     if (redoItems.count()) {
01149       redoItems.setAutoDelete(true);
01150       redoItems.clear();
01151       redoItems.setAutoDelete(false);
01152     }
01153   }
01154 }
01155 
01156 bool KateDocument::editInsertText ( uint line, uint col, const QString &str )
01157 {
01158   if (!isReadWrite())
01159     return false;
01160 
01161   QString s = str;
01162 
01163   KateTextLine::Ptr l = m_buffer->line(line);
01164 
01165   if (!l)
01166     return false;
01167 
01168     if ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn && ! m_isInUndo )
01169     {
01170       uint tw = config()->tabWidth();
01171       int pos = 0;
01172       uint l = 0;
01173       while ( (pos = s.find('\t')) > -1 )
01174       {
01175         l = tw - ( (col + pos)%tw );
01176         s.replace( pos, 1, QString().fill( ' ', l ) );
01177       }
01178     }
01179 
01180   editStart ();
01181 
01182   editAddUndo (KateUndoGroup::editInsertText, line, col, s.length(), s);
01183 
01184   l->insertText (col, s.length(), s.unicode());
01185 //   removeTrailingSpace(line); // ### nessecary?
01186 
01187   m_buffer->changeLine(line);
01188 
01189   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01190     it.current()->editTextInserted (line, col, s.length());
01191 
01192   editEnd ();
01193 
01194   return true;
01195 }
01196 
01197 bool KateDocument::editRemoveText ( uint line, uint col, uint len )
01198 {
01199   if (!isReadWrite())
01200     return false;
01201 
01202   KateTextLine::Ptr l = m_buffer->line(line);
01203 
01204   if (!l)
01205     return false;
01206 
01207   editStart ();
01208 
01209   editAddUndo (KateUndoGroup::editRemoveText, line, col, len, l->string().mid(col, len));
01210 
01211   l->removeText (col, len);
01212   removeTrailingSpace( line );
01213 
01214   m_buffer->changeLine(line);
01215 
01216   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01217     it.current()->editTextRemoved (line, col, len);
01218 
01219   editEnd ();
01220 
01221   return true;
01222 }
01223 
01224 bool KateDocument::editMarkLineAutoWrapped ( uint line, bool autowrapped )
01225 {
01226   if (!isReadWrite())
01227     return false;
01228 
01229   KateTextLine::Ptr l = m_buffer->line(line);
01230 
01231   if (!l)
01232     return false;
01233 
01234   editStart ();
01235 
01236   editAddUndo (KateUndoGroup::editMarkLineAutoWrapped, line, autowrapped ? 1 : 0, 0, QString::null);
01237 
01238   l->setAutoWrapped (autowrapped);
01239 
01240   m_buffer->changeLine(line);
01241 
01242   editEnd ();
01243 
01244   return true;
01245 }
01246 
01247 bool KateDocument::editWrapLine ( uint line, uint col, bool newLine, bool *newLineAdded)
01248 {
01249   if (!isReadWrite())
01250     return false;
01251 
01252   KateTextLine::Ptr l = m_buffer->line(line);
01253 
01254   if (!l)
01255     return false;
01256 
01257   editStart ();
01258 
01259   KateTextLine::Ptr nextLine = m_buffer->line(line+1);
01260 
01261   int pos = l->length() - col;
01262 
01263   if (pos < 0)
01264     pos = 0;
01265 
01266   editAddUndo (KateUndoGroup::editWrapLine, line, col, pos, (!nextLine || newLine) ? "1" : "0");
01267 
01268   if (!nextLine || newLine)
01269   {
01270     KateTextLine::Ptr textLine = new KateTextLine();
01271 
01272     textLine->insertText (0, pos, l->text()+col, l->attributes()+col);
01273     l->truncate(col);
01274 
01275     m_buffer->insertLine (line+1, textLine);
01276     m_buffer->changeLine(line);
01277 
01278     QPtrList<KTextEditor::Mark> list;
01279     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
01280     {
01281       if( it.current()->line >= line )
01282       {
01283         if ((col == 0) || (it.current()->line > line))
01284           list.append( it.current() );
01285       }
01286     }
01287 
01288     for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
01289     {
01290       KTextEditor::Mark* mark = m_marks.take( it.current()->line );
01291       mark->line++;
01292       m_marks.insert( mark->line, mark );
01293     }
01294 
01295     if( !list.isEmpty() )
01296       emit marksChanged();
01297 
01298     // yes, we added a new line !
01299     if (newLineAdded)
01300       (*newLineAdded) = true;
01301   }
01302   else
01303   {
01304     nextLine->insertText (0, pos, l->text()+col, l->attributes()+col);
01305     l->truncate(col);
01306 
01307     m_buffer->changeLine(line);
01308     m_buffer->changeLine(line+1);
01309 
01310     // no, no new line added !
01311     if (newLineAdded)
01312       (*newLineAdded) = false;
01313   }
01314 
01315   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01316     it.current()->editLineWrapped (line, col, !nextLine || newLine);
01317 
01318   editEnd ();
01319 
01320   return true;
01321 }
01322 
01323 bool KateDocument::editUnWrapLine ( uint line, bool removeLine, uint length )
01324 {
01325   if (!isReadWrite())
01326     return false;
01327 
01328   KateTextLine::Ptr l = m_buffer->line(line);
01329   KateTextLine::Ptr nextLine = m_buffer->line(line+1);
01330 
01331   if (!l || !nextLine)
01332     return false;
01333 
01334   editStart ();
01335 
01336   uint col = l->length ();
01337 
01338   editAddUndo (KateUndoGroup::editUnWrapLine, line, col, length, removeLine ? "1" : "0");
01339 
01340   if (removeLine)
01341   {
01342     l->insertText (col, nextLine->length(), nextLine->text(), nextLine->attributes());
01343 
01344     m_buffer->changeLine(line);
01345     m_buffer->removeLine(line+1);
01346   }
01347   else
01348   {
01349     l->insertText (col, (nextLine->length() < length) ? nextLine->length() : length,
01350       nextLine->text(), nextLine->attributes());
01351     nextLine->removeText (0, (nextLine->length() < length) ? nextLine->length() : length);
01352 
01353     m_buffer->changeLine(line);
01354     m_buffer->changeLine(line+1);
01355   }
01356 
01357   QPtrList<KTextEditor::Mark> list;
01358   for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
01359   {
01360     if( it.current()->line >= line+1 )
01361       list.append( it.current() );
01362 
01363     if ( it.current()->line == line+1 )
01364     {
01365       KTextEditor::Mark* mark = m_marks.take( line );
01366 
01367       if (mark)
01368       {
01369         it.current()->type |= mark->type;
01370       }
01371     }
01372   }
01373 
01374   for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
01375   {
01376     KTextEditor::Mark* mark = m_marks.take( it.current()->line );
01377     mark->line--;
01378     m_marks.insert( mark->line, mark );
01379   }
01380 
01381   if( !list.isEmpty() )
01382     emit marksChanged();
01383 
01384   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01385     it.current()->editLineUnWrapped (line, col, removeLine, length);
01386 
01387   editEnd ();
01388 
01389   return true;
01390 }
01391 
01392 bool KateDocument::editInsertLine ( uint line, const QString &s )
01393 {
01394   if (!isReadWrite())
01395     return false;
01396 
01397   if ( line > numLines() )
01398     return false;
01399 
01400   editStart ();
01401 
01402   editAddUndo (KateUndoGroup::editInsertLine, line, 0, s.length(), s);
01403 
01404   removeTrailingSpace( line ); // old line
01405 
01406   KateTextLine::Ptr tl = new KateTextLine();
01407   tl->insertText (0, s.length(), s.unicode(), 0);
01408   m_buffer->insertLine(line, tl);
01409   m_buffer->changeLine(line);
01410 
01411   removeTrailingSpace( line ); // new line
01412 
01413   QPtrList<KTextEditor::Mark> list;
01414   for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
01415   {
01416     if( it.current()->line >= line )
01417       list.append( it.current() );
01418   }
01419 
01420   for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
01421   {
01422     KTextEditor::Mark* mark = m_marks.take( it.current()->line );
01423     mark->line++;
01424     m_marks.insert( mark->line, mark );
01425   }
01426 
01427   if( !list.isEmpty() )
01428     emit marksChanged();
01429 
01430   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01431     it.current()->editLineInserted (line);
01432 
01433   editEnd ();
01434 
01435   return true;
01436 }
01437 
01438 bool KateDocument::editRemoveLine ( uint line )
01439 {
01440   if (!isReadWrite())
01441     return false;
01442 
01443   if ( line > lastLine() )
01444     return false;
01445 
01446   if ( numLines() == 1 )
01447     return editRemoveText (0, 0, m_buffer->line(0)->length());
01448 
01449   editStart ();
01450 
01451   editAddUndo (KateUndoGroup::editRemoveLine, line, 0, lineLength(line), textLine(line));
01452 
01453   m_buffer->removeLine(line);
01454 
01455   QPtrList<KTextEditor::Mark> list;
01456   KTextEditor::Mark* rmark = 0;
01457   for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
01458   {
01459     if ( (it.current()->line > line) )
01460       list.append( it.current() );
01461     else if ( (it.current()->line == line) )
01462       rmark = it.current();
01463   }
01464 
01465   if (rmark)
01466     delete (m_marks.take (rmark->line));
01467 
01468   for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
01469   {
01470     KTextEditor::Mark* mark = m_marks.take( it.current()->line );
01471     mark->line--;
01472     m_marks.insert( mark->line, mark );
01473   }
01474 
01475   if( !list.isEmpty() )
01476     emit marksChanged();
01477 
01478   for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
01479     it.current()->editLineRemoved (line);
01480 
01481   editEnd();
01482 
01483   return true;
01484 }
01485 //END
01486 
01487 //BEGIN KTextEditor::UndoInterface stuff
01488 
01489 uint KateDocument::undoCount () const
01490 {
01491   return undoItems.count ();
01492 }
01493 
01494 uint KateDocument::redoCount () const
01495 {
01496   return redoItems.count ();
01497 }
01498 
01499 uint KateDocument::undoSteps () const
01500 {
01501   return m_config->undoSteps();
01502 }
01503 
01504 void KateDocument::setUndoSteps(uint steps)
01505 {
01506   m_config->setUndoSteps (steps);
01507 }
01508 
01509 void KateDocument::undo()
01510 {
01511   m_isInUndo = true;
01512   if ((undoItems.count() > 0) && undoItems.last())
01513   {
01514     clearSelection ();
01515 
01516     undoItems.last()->undo();
01517     redoItems.append (undoItems.last());
01518     undoItems.removeLast ();
01519     updateModified();
01520 
01521     emit undoChanged ();
01522   }
01523   m_isInUndo = false;
01524 }
01525 
01526 void KateDocument::redo()
01527 {
01528   m_isInUndo = true;
01529   if ((redoItems.count() > 0) && redoItems.last())
01530   {
01531     clearSelection ();
01532 
01533     redoItems.last()->redo();
01534     undoItems.append (redoItems.last());
01535     redoItems.removeLast ();
01536     updateModified();
01537 
01538     emit undoChanged ();
01539   }
01540   m_isInUndo = false;
01541 }
01542 
01543 void KateDocument::updateModified()
01544 {
01545   if ( ( lastUndoGroupWhenSaved &&
01546          !undoItems.isEmpty() &&
01547          undoItems.last() == lastUndoGroupWhenSaved )
01548        || ( undoItems.isEmpty() && docWasSavedWhenUndoWasEmpty ) )
01549   {
01550     setModified( false );
01551     kdDebug(13020) << k_funcinfo << "setting modified to false!" << endl;
01552   };
01553 }
01554 
01555 void KateDocument::clearUndo()
01556 {
01557   undoItems.setAutoDelete (true);
01558   undoItems.clear ();
01559   undoItems.setAutoDelete (false);
01560 
01561   lastUndoGroupWhenSaved = 0;
01562   docWasSavedWhenUndoWasEmpty = false;
01563 
01564   emit undoChanged ();
01565 }
01566 
01567 void KateDocument::clearRedo()
01568 {
01569   redoItems.setAutoDelete (true);
01570   redoItems.clear ();
01571   redoItems.setAutoDelete (false);
01572 
01573   emit undoChanged ();
01574 }
01575 
01576 QPtrList<KTextEditor::Cursor> KateDocument::cursors () const
01577 {
01578   return myCursors;
01579 }
01580 //END
01581 
01582 //BEGIN KTextEditor::SearchInterface stuff
01583 
01584 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)
01585 {
01586   if (text.isEmpty())
01587     return false;
01588 
01589   int line = startLine;
01590   int col = startCol;
01591 
01592   if (!backwards)
01593   {
01594     int searchEnd = lastLine();
01595 
01596     while (line <= searchEnd)
01597     {
01598       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01599 
01600       if (!textLine)
01601         return false;
01602 
01603       uint foundAt, myMatchLen;
01604       bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, false);
01605 
01606       if (found)
01607       {
01608         (*foundAtLine) = line;
01609         (*foundAtCol) = foundAt;
01610         (*matchLen) = myMatchLen;
01611         return true;
01612       }
01613 
01614       col = 0;
01615       line++;
01616     }
01617   }
01618   else
01619   {
01620     // backward search
01621     int searchEnd = 0;
01622 
01623     while (line >= searchEnd)
01624     {
01625       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01626 
01627       if (!textLine)
01628         return false;
01629 
01630       uint foundAt, myMatchLen;
01631       bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, true);
01632 
01633       if (found)
01634       {
01635        /* if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
01636             && line == selectStart.line() && foundAt == (uint) selectStart.col()
01637             && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
01638         {
01639           // To avoid getting stuck at one match we skip a match if it is already
01640           // selected (most likely because it has just been found).
01641           if (foundAt > 0)
01642             col = foundAt - 1;
01643           else {
01644             if (--line >= 0)
01645               col = lineLength(line);
01646           }
01647           continue;
01648       }*/
01649 
01650         (*foundAtLine) = line;
01651         (*foundAtCol) = foundAt;
01652         (*matchLen) = myMatchLen;
01653         return true;
01654       }
01655 
01656       if (line >= 1)
01657         col = lineLength(line-1);
01658 
01659       line--;
01660     }
01661   }
01662 
01663   return false;
01664 }
01665 
01666 bool KateDocument::searchText (unsigned int startLine, unsigned int startCol, const QRegExp &regexp, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool backwards)
01667 {
01668   kdDebug(13020)<<"KateDocument::searchText( "<<startLine<<", "<<startCol<<", "<<regexp.pattern()<<", "<<backwards<<" )"<<endl;
01669   if (regexp.isEmpty() || !regexp.isValid())
01670     return false;
01671 
01672   int line = startLine;
01673   int col = startCol;
01674 
01675   if (!backwards)
01676   {
01677     int searchEnd = lastLine();
01678 
01679     while (line <= searchEnd)
01680     {
01681       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01682 
01683       if (!textLine)
01684         return false;
01685 
01686       uint foundAt, myMatchLen;
01687       bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, false);
01688 
01689       if (found)
01690       {
01691         // A special case which can only occur when searching with a regular expression consisting
01692         // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{').
01693         if (myMatchLen == 0 && (uint) line == startLine && foundAt == (uint) col)
01694         {
01695           if (col < lineLength(line))
01696             col++;
01697           else {
01698             line++;
01699             col = 0;
01700           }
01701           continue;
01702         }
01703 
01704         (*foundAtLine) = line;
01705         (*foundAtCol) = foundAt;
01706         (*matchLen) = myMatchLen;
01707         return true;
01708       }
01709 
01710       col = 0;
01711       line++;
01712     }
01713   }
01714   else
01715   {
01716     // backward search
01717     int searchEnd = 0;
01718 
01719     while (line >= searchEnd)
01720     {
01721       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
01722 
01723       if (!textLine)
01724         return false;
01725 
01726       uint foundAt, myMatchLen;
01727       bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, true);
01728 
01729       if (found)
01730       {
01731         /*if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
01732             && line == selectStart.line() && foundAt == (uint) selectStart.col()
01733             && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
01734         {
01735           // To avoid getting stuck at one match we skip a match if it is already
01736           // selected (most likely because it has just been found).
01737           if (foundAt > 0)
01738             col = foundAt - 1;
01739           else {
01740             if (--line >= 0)
01741               col = lineLength(line);
01742           }
01743           continue;
01744       }*/
01745 
01746         (*foundAtLine) = line;
01747         (*foundAtCol) = foundAt;
01748         (*matchLen) = myMatchLen;
01749         return true;
01750       }
01751 
01752       if (line >= 1)
01753         col = lineLength(line-1);
01754 
01755       line--;
01756     }
01757   }
01758 
01759   return false;
01760 }
01761 //END
01762 
01763 //BEGIN KTextEditor::HighlightingInterface stuff
01764 
01765 uint KateDocument::hlMode ()
01766 {
01767   return KateHlManager::self()->findHl(highlight());
01768 }
01769 
01770 bool KateDocument::setHlMode (uint mode)
01771 {
01772   m_buffer->setHighlight (mode);
01773 
01774   if (true)
01775   {
01776     setDontChangeHlOnSave();
01777     return true;
01778   }
01779 
01780   return false;
01781 }
01782 
01783 void KateDocument::bufferHlChanged ()
01784 {
01785   // update all views
01786   makeAttribs(false);
01787 
01788   emit hlChanged();
01789 }
01790 
01791 uint KateDocument::hlModeCount ()
01792 {
01793   return KateHlManager::self()->highlights();
01794 }
01795 
01796 QString KateDocument::hlModeName (uint mode)
01797 {
01798   return KateHlManager::self()->hlName (mode);
01799 }
01800 
01801 QString KateDocument::hlModeSectionName (uint mode)
01802 {
01803   return KateHlManager::self()->hlSection (mode);
01804 }
01805 
01806 void KateDocument::setDontChangeHlOnSave()
01807 {
01808   hlSetByUser = true;
01809 }
01810 //END
01811 
01812 //BEGIN KTextEditor::ConfigInterface stuff
01813 void KateDocument::readConfig(KConfig *config)
01814 {
01815   config->setGroup("Kate Document Defaults");
01816 
01817   // read max loadable blocks, more blocks will be swapped out
01818   KateBuffer::setMaxLoadedBlocks (config->readNumEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks()));
01819 
01820   KateDocumentConfig::global()->readConfig (config);
01821 
01822   config->setGroup("Kate View Defaults");
01823   KateViewConfig::global()->readConfig (config);
01824 
01825   config->setGroup("Kate Renderer Defaults");
01826   KateRendererConfig::global()->readConfig (config);
01827 }
01828 
01829 void KateDocument::writeConfig(KConfig *config)
01830 {
01831   config->setGroup("Kate Document Defaults");
01832 
01833   // write max loadable blocks, more blocks will be swapped out
01834   config->writeEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks());
01835 
01836   KateDocumentConfig::global()->writeConfig (config);
01837 
01838   config->setGroup("Kate View Defaults");
01839   KateViewConfig::global()->writeConfig (config);
01840 
01841   config->setGroup("Kate Renderer Defaults");
01842   KateRendererConfig::global()->writeConfig (config);
01843 }
01844 
01845 void KateDocument::readConfig()
01846 {
01847   KConfig *config = kapp->config();
01848   readConfig (config);
01849 }
01850 
01851 void KateDocument::writeConfig()
01852 {
01853   KConfig *config = kapp->config();
01854   writeConfig (config);
01855   config->sync();
01856 }
01857 
01858 void KateDocument::readSessionConfig(KConfig *kconfig)
01859 {
01860   // restore the url
01861   KURL url (kconfig->readEntry("URL"));
01862 
01863   // get the encoding
01864   QString tmpenc=kconfig->readEntry("Encoding");
01865   if (!tmpenc.isEmpty() && (tmpenc != encoding()))
01866     setEncoding(tmpenc);
01867 
01868   // open the file if url valid
01869   if (!url.isEmpty() && url.isValid())
01870     openURL (url);
01871 
01872   // restore the hl stuff
01873   m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig->readEntry("Highlighting")));
01874 
01875   if (hlMode() > 0)
01876     hlSetByUser = true;
01877 
01878   // indent mode
01879   config()->setIndentationMode( (uint)kconfig->readNumEntry("Indentation Mode", config()->indentationMode() ) );
01880 
01881   // Restore Bookmarks
01882   QValueList<int> marks = kconfig->readIntListEntry("Bookmarks");
01883   for( uint i = 0; i < marks.count(); i++ )
01884     addMark( marks[i], KateDocument::markType01 );
01885 }
01886 
01887 void KateDocument::writeSessionConfig(KConfig *kconfig)
01888 {
01889   // save url
01890   kconfig->writeEntry("URL", m_url.prettyURL() );
01891 
01892   // save encoding
01893   kconfig->writeEntry("Encoding",encoding());
01894 
01895   // save hl
01896   kconfig->writeEntry("Highlighting", highlight()->name());
01897 
01898   kconfig->writeEntry("Indentation Mode", config()->indentationMode() );
01899 
01900   // Save Bookmarks
01901   QValueList<int> marks;
01902   for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
01903        it.current() && it.current()->type & KTextEditor::MarkInterface::markType01;
01904        ++it )
01905      marks << it.current()->line;
01906 
01907   kconfig->writeEntry( "Bookmarks", marks );
01908 }
01909 
01910 void KateDocument::configDialog()
01911 {
01912   KDialogBase *kd = new KDialogBase ( KDialogBase::IconList,
01913                                       i18n("Configure"),
01914                                       KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help,
01915                                       KDialogBase::Ok,
01916                                       kapp->mainWidget() );
01917 
01918 #ifndef Q_WS_WIN //TODO: reenable
01919   KWin::setIcons( kd->winId(), kapp->icon(), kapp->miniIcon() );
01920 #endif
01921 
01922   QPtrList<KTextEditor::ConfigPage> editorPages;
01923 
01924   for (uint i = 0; i < KTextEditor::configInterfaceExtension (this)->configPages (); i++)
01925   {
01926     QStringList path;
01927     path.clear();
01928     path << KTextEditor::configInterfaceExtension (this)->configPageName (i);
01929     QVBox *page = kd->addVBoxPage(path, KTextEditor::configInterfaceExtension (this)->configPageFullName (i),
01930                               KTextEditor::configInterfaceExtension (this)->configPagePixmap(i, KIcon::SizeMedium) );
01931 
01932     editorPages.append (KTextEditor::configInterfaceExtension (this)->configPage(i, page));
01933   }
01934 
01935   if (kd->exec())
01936   {
01937     KateDocumentConfig::global()->configStart ();
01938     KateViewConfig::global()->configStart ();
01939     KateRendererConfig::global()->configStart ();
01940 
01941     for (uint i=0; i<editorPages.count(); i++)
01942     {
01943       editorPages.at(i)->apply();
01944     }
01945 
01946     KateDocumentConfig::global()->configEnd ();
01947     KateViewConfig::global()->configEnd ();
01948     KateRendererConfig::global()->configEnd ();
01949 
01950     writeConfig ();
01951   }
01952 
01953   delete kd;
01954 }
01955 
01956 uint KateDocument::mark( uint line )
01957 {
01958   if( !m_marks[line] )
01959     return 0;
01960   return m_marks[line]->type;
01961 }
01962 
01963 void KateDocument::setMark( uint line, uint markType )
01964 {
01965   clearMark( line );
01966   addMark( line, markType );
01967 }
01968 
01969 void KateDocument::clearMark( uint line )
01970 {
01971   if( line > lastLine() )
01972     return;
01973 
01974   if( !m_marks[line] )
01975     return;
01976 
01977   KTextEditor::Mark* mark = m_marks.take( line );
01978   emit markChanged( *mark, MarkRemoved );
01979   emit marksChanged();
01980   delete mark;
01981   tagLines( line, line );
01982   repaintViews(true);
01983 }
01984 
01985 void KateDocument::addMark( uint line, uint markType )
01986 {
01987   if( line > lastLine())
01988     return;
01989 
01990   if( markType == 0 )
01991     return;
01992 
01993   if( m_marks[line] ) {
01994     KTextEditor::Mark* mark = m_marks[line];
01995 
01996     // Remove bits already set
01997     markType &= ~mark->type;
01998 
01999     if( markType == 0 )
02000       return;
02001 
02002     // Add bits
02003     mark->type |= markType;
02004   } else {
02005     KTextEditor::Mark *mark = new KTextEditor::Mark;
02006     mark->line = line;
02007     mark->type = markType;
02008     m_marks.insert( line, mark );
02009   }
02010 
02011   // Emit with a mark having only the types added.
02012   KTextEditor::Mark temp;
02013   temp.line = line;
02014   temp.type = markType;
02015   emit markChanged( temp, MarkAdded );
02016 
02017   emit marksChanged();
02018   tagLines( line, line );
02019   repaintViews(true);
02020 }
02021 
02022 void KateDocument::removeMark( uint line, uint markType )
02023 {
02024   if( line > lastLine() )
02025     return;
02026   if( !m_marks[line] )
02027     return;
02028 
02029   KTextEditor::Mark* mark = m_marks[line];
02030 
02031   // Remove bits not set
02032   markType &= mark->type;
02033 
02034   if( markType == 0 )
02035     return;
02036 
02037   // Subtract bits
02038   mark->type &= ~markType;
02039 
02040   // Emit with a mark having only the types removed.
02041   KTextEditor::Mark temp;
02042   temp.line = line;
02043   temp.type = markType;
02044   emit markChanged( temp, MarkRemoved );
02045 
02046   if( mark->type == 0 )
02047     m_marks.remove( line );
02048 
02049   emit marksChanged();
02050   tagLines( line, line );
02051   repaintViews(true);
02052 }
02053 
02054 QPtrList<KTextEditor::Mark> KateDocument::marks()
02055 {
02056   QPtrList<KTextEditor::Mark> list;
02057 
02058   for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
02059        it.current(); ++it ) {
02060     list.append( it.current() );
02061   }
02062 
02063   return list;
02064 }
02065 
02066 void KateDocument::clearMarks()
02067 {
02068   for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
02069        it.current(); ++it ) {
02070     KTextEditor::Mark* mark = it.current();
02071     emit markChanged( *mark, MarkRemoved );
02072     tagLines( mark->line, mark->line );
02073   }
02074 
02075   m_marks.clear();
02076 
02077   emit marksChanged();
02078   repaintViews(true);
02079 }
02080 
02081 void KateDocument::setPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
02082 {
02083   m_markPixmaps.replace( type, new QPixmap( pixmap ) );
02084 }
02085 
02086 void KateDocument::setDescription( MarkInterface::MarkTypes type, const QString& description )
02087 {
02088   m_markDescriptions.replace( type, new QString( description ) );
02089 }
02090 
02091 QPixmap *KateDocument::markPixmap( MarkInterface::MarkTypes type )
02092 {
02093   return m_markPixmaps[type];
02094 }
02095 
02096 QColor KateDocument::markColor( MarkInterface::MarkTypes type )
02097 {
02098   uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
02099   if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
02100     return KateRendererConfig::global()->lineMarkerColor(type);
02101   } else {
02102     return QColor();
02103   }
02104 }
02105 
02106 QString KateDocument::markDescription( MarkInterface::MarkTypes type )
02107 {
02108   if( m_markDescriptions[type] )
02109     return *m_markDescriptions[type];
02110   return QString::null;
02111 }
02112 
02113 void KateDocument::setMarksUserChangable( uint markMask )
02114 {
02115   m_editableMarks = markMask;
02116 }
02117 
02118 uint KateDocument::editableMarks()
02119 {
02120   return m_editableMarks;
02121 }
02122 //END
02123 
02124 //BEGIN KTextEditor::PrintInterface stuff
02125 bool KateDocument::printDialog ()
02126 {
02127   return KatePrinter::print (this);
02128 }
02129 
02130 bool KateDocument::print ()
02131 {
02132   return KatePrinter::print (this);
02133 }
02134 //END
02135 
02136 //BEGIN KTextEditor::DocumentInfoInterface (### unfinished)
02137 QString KateDocument::mimeType()
02138 {
02139   KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
02140 
02141   // if the document has a URL, try KMimeType::findByURL
02142   if ( ! m_url.isEmpty() )
02143     result = KMimeType::findByURL( m_url );
02144 
02145   else if ( m_url.isEmpty() || ! m_url.isLocalFile() )
02146     result = mimeTypeForContent();
02147 
02148   return result->name();
02149 }
02150 
02151 // TODO implement this -- how to calculate?
02152 long KateDocument::fileSize()
02153 {
02154   return 0;
02155 }
02156 
02157 // TODO implement this
02158 QString KateDocument::niceFileSize()
02159 {
02160   return "UNKNOWN";
02161 }
02162 
02163 KMimeType::Ptr KateDocument::mimeTypeForContent()
02164 {
02165   QByteArray buf (1024);
02166   uint bufpos = 0;
02167 
02168   for (uint i=0; i < numLines(); i++)
02169   {
02170     QString line = textLine( i );
02171     uint len = line.length() + 1;
02172 
02173     if (bufpos + len > 1024)
02174       len = 1024 - bufpos;
02175 
02176     memcpy(&buf[bufpos], (line + "\n").latin1(), len);
02177 
02178     bufpos += len;
02179 
02180     if (bufpos >= 1024)
02181       break;
02182   }
02183   buf.resize( bufpos );
02184 
02185   int accuracy = 0;
02186   return KMimeType::findByContent( buf, &accuracy );
02187 }
02188 //END KTextEditor::DocumentInfoInterface
02189 
02190 
02191 //BEGIN KParts::ReadWrite stuff
02192 
02193 bool KateDocument::openURL( const KURL &url )
02194 {
02195 //   kdDebug(13020)<<"KateDocument::openURL( "<<url.prettyURL()<<")"<<endl;
02196   // no valid URL
02197   if ( !url.isValid() )
02198     return false;
02199 
02200   // could not close old one
02201   if ( !closeURL() )
02202     return false;
02203 
02204   // set my url
02205   m_url = url;
02206 
02207   if ( m_url.isLocalFile() )
02208   {
02209     // local mode, just like in kpart
02210 
02211     m_file = m_url.path();
02212 
02213     emit started( 0 );
02214 
02215     if (openFile())
02216     {
02217       emit completed();
02218       emit setWindowCaption( m_url.prettyURL() );
02219 
02220       return true;
02221     }
02222 
02223     return false;
02224   }
02225   else
02226   {
02227     // remote mode
02228 
02229     m_bTemp = true;
02230 
02231     m_tempFile = new KTempFile ();
02232     m_file = m_tempFile->name();
02233 
02234     m_job = KIO::get ( url, false, isProgressInfoEnabled() );
02235 
02236     // connect to slots
02237     connect( m_job, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
02238            SLOT( slotDataKate( KIO::Job*, const QByteArray& ) ) );
02239 
02240     connect( m_job, SIGNAL( result( KIO::Job* ) ),
02241            SLOT( slotFinishedKate( KIO::Job* ) ) );
02242 
02243     QWidget *w = widget ();
02244     if (!w && !m_views.isEmpty ())
02245       w = m_views.first();
02246 
02247     if (w)
02248       m_job->setWindow (w->topLevelWidget());
02249 
02250     emit started( m_job );
02251 
02252     return true;
02253   }
02254 }
02255 
02256 void KateDocument::slotDataKate ( KIO::Job *, const QByteArray &data )
02257 {
02258 //   kdDebug(13020) << "KateDocument::slotData" << endl;
02259 
02260   if (!m_tempFile || !m_tempFile->file())
02261     return;
02262 
02263   m_tempFile->file()->writeBlock (data);
02264 }
02265 
02266 void KateDocument::slotFinishedKate ( KIO::Job * job )
02267 {
02268 //   kdDebug(13020) << "KateDocument::slotJobFinished" << endl;
02269 
02270   if (!m_tempFile)
02271     return;
02272 
02273   delete m_tempFile;
02274   m_tempFile = 0;
02275   m_job = 0;
02276 
02277   if (job->error())
02278     emit canceled( job->errorString() );
02279   else
02280   {
02281     if ( openFile(job) )
02282       emit setWindowCaption( m_url.prettyURL() );
02283     emit completed();
02284   }
02285 }
02286 
02287 void KateDocument::abortLoadKate()
02288 {
02289   if ( m_job )
02290   {
02291     kdDebug(13020) << "Aborting job " << m_job << endl;
02292     m_job->kill();
02293     m_job = 0;
02294   }
02295 
02296   delete m_tempFile;
02297   m_tempFile = 0;
02298 }
02299 
02300 bool KateDocument::openFile()
02301 {
02302   return openFile (0);
02303 }
02304 
02305 bool KateDocument::openFile(KIO::Job * job)
02306 {
02307   m_loading = true;
02308   // add new m_file to dirwatch
02309   activateDirWatch ();
02310 
02311   //
02312   // use metadata
02313   //
02314   if (job)
02315   {
02316     QString metaDataCharset = job->queryMetaData("charset");
02317 
02318     // only overwrite config if nothing set
02319     if (!metaDataCharset.isEmpty () && (!m_config->isSetEncoding() || m_config->encoding().isEmpty()))
02320       setEncoding (metaDataCharset);
02321   }
02322 
02323   //
02324   // service type magic to get encoding right
02325   //
02326   QString serviceType = m_extension->urlArgs().serviceType.simplifyWhiteSpace();
02327   int pos = serviceType.find(';');
02328   if (pos != -1)
02329     setEncoding (serviceType.mid(pos+1));
02330 
02331   // if the encoding is set here - on the command line/from the dialog/from KIO
02332   // we prevent file type and document variables from changing it
02333   bool encodingSticky = m_encodingSticky;
02334   m_encodingSticky = m_config->isSetEncoding();
02335 
02336   // Try getting the filetype here, so that variables does not have to be reset.
02337   int fileTypeFound = KateFactory::self()->fileTypeManager()->fileType (this);
02338   if ( fileTypeFound > -1 )
02339     updateFileType( fileTypeFound );
02340 
02341   // do we have success ?
02342   bool success = m_buffer->openFile (m_file);
02343   //
02344   // yeah, success
02345   //
02346   m_loading = false; // done reading file.
02347   if (success)
02348   {
02349     /*if (highlight() && !m_url.isLocalFile()) {
02350       // The buffer's highlighting gets nuked by KateBuffer::clear()
02351       m_buffer->setHighlight(m_highlight);
02352   }*/
02353 
02354     // update our hl type if needed
02355     if (!hlSetByUser)
02356     {
02357       int hl (KateHlManager::self()->detectHighlighting (this));
02358 
02359       if (hl >= 0)
02360         m_buffer->setHighlight(hl);
02361     }
02362 
02363     // update file type if we haven't allready done so.
02364     if ( fileTypeFound < 0 )
02365       updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
02366 
02367     // read dir config (if possible and wanted)
02368     readDirConfig ();
02369 
02370     // read vars
02371     readVariables();
02372 
02373     // update the md5 digest
02374     createDigest( m_digest );
02375   }
02376 
02377   //
02378   // update views
02379   //
02380   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
02381   {
02382     view->updateView(true);
02383   }
02384 
02385   //
02386   // emit the signal we need for example for kate app
02387   //
02388   emit fileNameChanged ();
02389 
02390   //
02391   // set doc name, dummy value as arg, don't need it
02392   //
02393   setDocName  (QString::null);
02394 
02395   //
02396   // to houston, we are not modified
02397   //
02398   if (m_modOnHd)
02399   {
02400     m_modOnHd = false;
02401     m_modOnHdReason = 0;
02402     emit modifiedOnDisc (this, m_modOnHd, 0);
02403   }
02404 
02405   //
02406   // display errors
02407   //
02408   if (s_openErrorDialogsActivated)
02409   {
02410     if (!success && m_buffer->loadingBorked())
02411       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()));
02412     else if (!success)
02413       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()));
02414   }
02415 
02416   // warn -> opened binary file!!!!!!!
02417   if (m_buffer->binary())
02418   {
02419     // this file can't be saved again without killing it
02420     setReadWrite( false );
02421 
02422     KMessageBox::information (widget()
02423       , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
02424       , i18n ("Binary File Opened")
02425       , "Binary File Opened Warning");
02426   }
02427 
02428   m_encodingSticky = encodingSticky;
02429 
02430   //
02431   // return the success
02432   //
02433   return success;
02434 }
02435 
02436 bool KateDocument::save()
02437 {
02438   bool l ( url().isLocalFile() );
02439 
02440   if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
02441        || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
02442   {
02443     KURL u( url() );
02444     u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
02445 
02446     kdDebug () << "backup src file name: " << url() << endl;
02447     kdDebug () << "backup dst file name: " << u << endl;
02448 
02449     // get the right permissions, start with safe default
02450     mode_t  perms = 0600;
02451     KIO::UDSEntry fentry;
02452     if (KIO::NetAccess::stat (url(), fentry, kapp->mainWidget()))
02453     {
02454       kdDebug () << "stating succesfull: " << url() << endl;
02455       KFileItem item (fentry, url());
02456       perms = item.permissions();
02457     }
02458 
02459     // first del existing file if any, than copy over the file we have
02460     // failure if a: the existing file could not be deleted, b: the file could not be copied
02461     if ( (!KIO::NetAccess::exists( u, false, kapp->mainWidget() ) || KIO::NetAccess::del( u, kapp->mainWidget() ))
02462           && KIO::NetAccess::file_copy( url(), u, perms, true, false, kapp->mainWidget() ) )
02463     {
02464       kdDebug(13020)<<"backing up successfull ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
02465     }
02466     else
02467     {
02468       kdDebug(13020)<<"backing up failed ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
02469       // FIXME: notify user for real ;)
02470     }
02471   }
02472 
02473   return KParts::ReadWritePart::save();
02474 }
02475 
02476 bool KateDocument::saveFile()
02477 {
02478   //
02479   // we really want to save this file ?
02480   //
02481   if (m_buffer->loadingBorked() && (KMessageBox::warningContinueCancel(widget(),
02482       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?"),i18n("Possible Data Loss"),i18n("Save Nevertheless")) != KMessageBox::Continue))
02483     return false;
02484 
02485   //
02486   // warn -> try to save binary file!!!!!!!
02487   //
02488   if (m_buffer->binary() && (KMessageBox::warningContinueCancel (widget()
02489         , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
02490         , i18n ("Trying to Save Binary File")
02491         , i18n("Save Nevertheless"), "Binary File Save Warning") != KMessageBox::Continue))
02492     return false;
02493 
02494   if ( !url().isEmpty() )
02495   {
02496     if (s_fileChangedDialogsActivated && m_modOnHd)
02497     {
02498       QString str = reasonedMOHString() + "\n\n";
02499 
02500       if (!isModified())
02501       {
02502         if (KMessageBox::warningContinueCancel(0,
02503                str + i18n("Do you really want to save this unmodified file? You could overwrite changed data in the file on disk."),i18n("Trying to Save Unmodified File"),i18n("Save Nevertheless")) != KMessageBox::Continue)
02504           return false;
02505       }
02506       else
02507       {
02508         if (KMessageBox::warningContinueCancel(0,
02509                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."),i18n("Possible Data Loss"),i18n("Save Nevertheless")) != KMessageBox::Continue)
02510           return false;
02511       }
02512     }
02513   }
02514 
02515   //
02516   // can we encode it if we want to save it ?
02517   //
02518   if (!m_buffer->canEncode ()
02519        && (KMessageBox::warningContinueCancel(0,
02520            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."),i18n("Possible Data Loss"),i18n("Save Nevertheless")) != KMessageBox::Continue))
02521   {
02522     return false;
02523   }
02524 
02525   // remove file from dirwatch
02526   deactivateDirWatch ();
02527 
02528   //
02529   // try to save
02530   //
02531   bool success = m_buffer->saveFile (m_file);
02532 
02533   // update the md5 digest
02534   createDigest( m_digest );
02535 
02536   // add m_file again to dirwatch
02537   activateDirWatch ();
02538 
02539   //
02540   // hurray, we had success, do stuff we need
02541   //
02542   if (success)
02543   {
02544     // update our hl type if needed
02545     if (!hlSetByUser)
02546     {
02547       int hl (KateHlManager::self()->detectHighlighting (this));
02548 
02549       if (hl >= 0)
02550         m_buffer->setHighlight(hl);
02551     }
02552 
02553     // read our vars
02554     readVariables();
02555   }
02556 
02557   //
02558   // we are not modified
02559   //
02560   if (success && m_modOnHd)
02561   {
02562     m_modOnHd = false;
02563     m_modOnHdReason = 0;
02564     emit modifiedOnDisc (this, m_modOnHd, 0);
02565   }
02566 
02567   //
02568   // display errors
02569   //
02570   if (!success)
02571     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()));
02572 
02573   //
02574   // return success
02575   //
02576   return success;
02577 }
02578 
02579 bool KateDocument::saveAs( const KURL &u )
02580 {
02581   QString oldDir = url().directory();
02582 
02583   if ( KParts::ReadWritePart::saveAs( u ) )
02584   {
02585     // null means base on filename
02586     setDocName( QString::null );
02587 
02588     if ( u.directory() != oldDir )
02589       readDirConfig();
02590 
02591     emit fileNameChanged();
02592     emit nameChanged((Kate::Document *) this);
02593 
02594     return true;
02595   }
02596 
02597   return false;
02598 }
02599 
02600 void KateDocument::readDirConfig ()
02601 {
02602   int depth = config()->searchDirConfigDepth ();
02603 
02604   if (m_url.isLocalFile() && (depth > -1))
02605   {
02606     QString currentDir = QFileInfo (m_file).dirPath();
02607 
02608     // only search as deep as specified or not at all ;)
02609     while (depth > -1)
02610     {
02611       kdDebug (13020) << "search for config file in path: " << currentDir << endl;
02612 
02613       // try to open config file in this dir
02614       QFile f (currentDir + "/.kateconfig");
02615 
02616       if (f.open (IO_ReadOnly))
02617       {
02618         QTextStream stream (&f);
02619 
02620         uint linesRead = 0;
02621         QString line = stream.readLine();
02622         while ((linesRead < 32) && !line.isNull())
02623         {
02624           readVariableLine( line );
02625 
02626           line = stream.readLine();
02627 
02628           linesRead++;
02629         }
02630 
02631         break;
02632       }
02633 
02634       QString newDir = QFileInfo (currentDir).dirPath();
02635 
02636       // bail out on looping (for example reached /)
02637       if (currentDir == newDir)
02638         break;
02639 
02640       currentDir = newDir;
02641       --depth;
02642     }
02643   }
02644 }
02645 
02646 void KateDocument::activateDirWatch ()
02647 {
02648   // same file as we are monitoring, return
02649   if (m_file == m_dirWatchFile)
02650     return;
02651 
02652   // remove the old watched file
02653   deactivateDirWatch ();
02654 
02655   // add new file if needed
02656   if (m_url.isLocalFile() && !m_file.isEmpty())
02657   {
02658     KateFactory::self()->dirWatch ()->addFile (m_file);
02659     m_dirWatchFile = m_file;
02660   }
02661 }
02662 
02663 void KateDocument::deactivateDirWatch ()
02664 {
02665   if (!m_dirWatchFile.isEmpty())
02666     KateFactory::self()->dirWatch ()->removeFile (m_dirWatchFile);
02667 
02668   m_dirWatchFile = QString::null;
02669 }
02670 
02671 bool KateDocument::closeURL()
02672 {
02673   abortLoadKate();
02674 
02675   //
02676   // file mod on hd
02677   //
02678   if ( !m_reloading && !url().isEmpty() )
02679   {
02680     if (s_fileChangedDialogsActivated && m_modOnHd)
02681     {
02682       if (!(KMessageBox::warningContinueCancel(
02683             widget(),
02684             reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
02685             i18n("Possible Data Loss"), i18n("Close Nevertheless"),
02686             QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
02687         return false;
02688     }
02689   }
02690 
02691   //
02692   // first call the normal kparts implementation
02693   //
02694   if (!KParts::ReadWritePart::closeURL ())
02695     return false;
02696 
02697   // remove file from dirwatch
02698   deactivateDirWatch ();
02699 
02700   //
02701   // empty url + filename
02702   //
02703   m_url = KURL ();
02704   m_file = QString::null;
02705 
02706   // we are not modified
02707   if (m_modOnHd)
02708   {
02709     m_modOnHd = false;
02710     m_modOnHdReason = 0;
02711     emit modifiedOnDisc (this, m_modOnHd, 0);
02712   }
02713 
02714   // clear the buffer
02715   m_buffer->clear();
02716 
02717   // remove all marks
02718   clearMarks ();
02719 
02720   // clear undo/redo history
02721   clearUndo();
02722   clearRedo();
02723 
02724   // no, we are no longer modified
02725   setModified(false);
02726 
02727   // we have no longer any hl
02728   m_buffer->setHighlight(0);
02729 
02730   // update all our views
02731   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
02732   {
02733     // Explicitly call the internal version because we don't want this to look like
02734     // an external request (and thus have the view not QWidget::scroll()ed.
02735     view->setCursorPositionInternal(0, 0, 1, false);
02736     view->clearSelection();
02737     view->updateView(true);
02738   }
02739 
02740   // uh, filename changed
02741   emit fileNameChanged ();
02742 
02743   // update doc name
02744   setDocName (QString::null);
02745 
02746   // success
02747   return true;
02748 }
02749 
02750 void KateDocument::setReadWrite( bool rw )
02751 {
02752   if (isReadWrite() != rw)
02753   {
02754     KParts::ReadWritePart::setReadWrite (rw);
02755 
02756     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02757     {
02758       view->slotUpdate();
02759       view->slotReadWriteChanged ();
02760     }
02761   }
02762 }
02763 
02764 void KateDocument::setModified(bool m) {
02765 
02766   if (isModified() != m) {
02767     KParts::ReadWritePart::setModified (m);
02768 
02769     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02770     {
02771       view->slotUpdate();
02772     }
02773 
02774     emit modifiedChanged ();
02775     emit modStateChanged ((Kate::Document *)this);
02776   }
02777   if ( m == false && ! undoItems.isEmpty() )
02778   {
02779     lastUndoGroupWhenSaved = undoItems.last();
02780   }
02781 
02782   if ( m == false ) docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
02783 }
02784 //END
02785 
02786 //BEGIN Kate specific stuff ;)
02787 
02788 void KateDocument::makeAttribs(bool needInvalidate)
02789 {
02790   for (uint z = 0; z < m_views.count(); z++)
02791     m_views.at(z)->renderer()->updateAttributes ();
02792 
02793   if (needInvalidate)
02794     m_buffer->invalidateHighlighting();
02795 
02796   tagAll ();
02797 }
02798 
02799 // the attributes of a hl have changed, update
02800 void KateDocument::internalHlChanged()
02801 {
02802   makeAttribs();
02803 }
02804 
02805 void KateDocument::addView(KTextEditor::View *view) {
02806   if (!view)
02807     return;
02808 
02809   m_views.append( (KateView *) view  );
02810   m_textEditViews.append( view );
02811 
02812   // apply the view & renderer vars from the file type
02813   const KateFileType *t = 0;
02814   if ((m_fileType > -1) && (t = KateFactory::self()->fileTypeManager()->fileType(m_fileType)))
02815     readVariableLine (t->varLine, true);
02816 
02817   // apply the view & renderer vars from the file
02818   readVariables (true);
02819 
02820   m_activeView = (KateView *) view;
02821 }
02822 
02823 void KateDocument::removeView(KTextEditor::View *view) {
02824   if (!view)
02825     return;
02826 
02827   if (m_activeView == view)
02828     m_activeView = 0L;
02829 
02830   m_views.removeRef( (KateView *) view );
02831   m_textEditViews.removeRef( view  );
02832 }
02833 
02834 void KateDocument::addSuperCursor(KateSuperCursor *cursor, bool privateC) {
02835   if (!cursor)
02836     return;
02837 
02838   m_superCursors.append( cursor );
02839 
02840   if (!privateC)
02841     myCursors.append( cursor );
02842 }
02843 
02844 void KateDocument::removeSuperCursor(KateSuperCursor *cursor, bool privateC) {
02845   if (!cursor)
02846     return;
02847 
02848   if (!privateC)
02849     myCursors.removeRef( cursor  );
02850 
02851   m_superCursors.removeRef( cursor  );
02852 }
02853 
02854 bool KateDocument::ownedView(KateView *view) {
02855   // do we own the given view?
02856   return (m_views.containsRef(view) > 0);
02857 }
02858 
02859 bool KateDocument::isLastView(int numViews) {
02860   return ((int) m_views.count() == numViews);
02861 }
02862 
02863 uint KateDocument::currentColumn( const KateTextCursor& cursor )
02864 {
02865   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
02866 
02867   if (textLine)
02868     return textLine->cursorX(cursor.col(), config()->tabWidth());
02869   else
02870     return 0;
02871 }
02872 
02873 bool KateDocument::typeChars ( KateView *view, const QString &chars )
02874 {
02875   KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorLine ());
02876 
02877   if (!textLine)
02878     return false;
02879 
02880   bool bracketInserted = false;
02881   QString buf;
02882   QChar c;
02883 
02884   for( uint z = 0; z < chars.length(); z++ )
02885   {
02886     QChar ch = c = chars[z];
02887     if (ch.isPrint() || ch == '\t')
02888     {
02889       buf.append (ch);
02890 
02891       if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets))
02892       {
02893         QChar end_ch;
02894         bool complete = true;
02895         QChar prevChar = textLine->getChar(view->cursorColumn()-1);
02896         QChar nextChar = textLine->getChar(view->cursorColumn());
02897         switch(ch) {
02898           case '(': end_ch = ')'; break;
02899           case '[': end_ch = ']'; break;
02900           case '{': end_ch = '}'; break;
02901           case '\'':end_ch = '\'';break;
02902           case '"': end_ch = '"'; break;
02903           default: complete = false;
02904         }
02905         if (complete)
02906         {
02907           if (view->hasSelection())
02908           { // there is a selection, enclose the selection
02909             buf.append (view->selection());
02910             buf.append (end_ch);
02911             bracketInserted = true;
02912           }
02913           else
02914           { // no selection, check whether we should better refuse to complete
02915             if ( ( (ch == '\'' || ch == '"') &&
02916                    (prevChar.isLetterOrNumber() || prevChar == ch) )
02917               || nextChar.isLetterOrNumber()
02918               || (nextChar == end_ch && prevChar != ch) )
02919             {
02920               kdDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
02921             }
02922             else
02923             {
02924               buf.append (end_ch);
02925               bracketInserted = true;
02926             }
02927           }
02928         }
02929       }
02930     }
02931   }
02932 
02933   if (buf.isEmpty())
02934     return false;
02935 
02936   editStart ();
02937 
02938   if (!view->config()->persistentSelection() && view->hasSelection() )
02939     view->removeSelectedText();
02940 
02941   int oldLine = view->cursorLine ();
02942   int oldCol = view->cursorColumnReal ();
02943 
02944 
02945   if (config()->configFlags()  & KateDocument::cfOvr)
02946     removeText (view->cursorLine(), view->cursorColumnReal(), view->cursorLine(), QMIN( view->cursorColumnReal()+buf.length(), textLine->length() ) );
02947 
02948   insertText (view->cursorLine(), view->cursorColumnReal(), buf);
02949   m_indenter->processChar(c);
02950 
02951   editEnd ();
02952 
02953   if (bracketInserted)
02954     view->setCursorPositionInternal (view->cursorLine(), view->cursorColumnReal()-1);
02955 
02956   emit charactersInteractivelyInserted (oldLine, oldCol, chars);
02957 
02958   return true;
02959 }
02960 
02961 void KateDocument::newLine( KateTextCursor& c, KateViewInternal *v )
02962 {
02963   editStart();
02964 
02965   if( !v->view()->config()->persistentSelection() && v->view()->hasSelection() )
02966     v->view()->removeSelectedText();
02967 
02968   // temporary hack to get the cursor pos right !!!!!!!!!
02969   c = v->getCursor ();
02970 
02971   if (c.line() > (int)lastLine())
02972    c.setLine(lastLine());
02973 
02974   if ( c.line() < 0 )
02975     c.setLine( 0 );
02976 
02977   uint ln = c.line();
02978 
02979   KateTextLine::Ptr textLine = kateTextLine(c.line());
02980 
02981   if (c.col() > (int)textLine->length())
02982     c.setCol(textLine->length());
02983 
02984   if (m_indenter->canProcessNewLine ())
02985   {
02986     int pos = textLine->firstChar();
02987 
02988     // length should do the job better
02989     if (pos < 0)
02990       pos = textLine->length();
02991 
02992     if (c.col() < pos)
02993       c.setCol(pos); // place cursor on first char if before
02994 
02995     editWrapLine (c.line(), c.col());
02996 
02997     KateDocCursor cursor (c.line() + 1, pos, this);
02998     m_indenter->processNewline(cursor, true);
02999 
03000     c.setPos(cursor);
03001   }
03002   else
03003   {
03004     editWrapLine (c.line(), c.col());
03005     c.setPos(c.line() + 1, 0);
03006   }
03007 
03008   removeTrailingSpace( ln );
03009 
03010   editEnd();
03011 }
03012 
03013 void KateDocument::transpose( const KateTextCursor& cursor)
03014 {
03015   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03016 
03017   if (!textLine || (textLine->length() < 2))
03018     return;
03019 
03020   uint col = cursor.col();
03021 
03022   if (col > 0)
03023     col--;
03024 
03025   if ((textLine->length() - col) < 2)
03026     return;
03027 
03028   uint line = cursor.line();
03029   QString s;
03030 
03031   //clever swap code if first character on the line swap right&left
03032   //otherwise left & right
03033   s.append (textLine->getChar(col+1));
03034   s.append (textLine->getChar(col));
03035   //do the swap
03036 
03037   // do it right, never ever manipulate a textline
03038   editStart ();
03039   editRemoveText (line, col, 2);
03040   editInsertText (line, col, s);
03041   editEnd ();
03042 }
03043 
03044 void KateDocument::backspace( KateView *view, const KateTextCursor& c )
03045 {
03046   if ( !view->config()->persistentSelection() && view->hasSelection() ) {
03047     view->removeSelectedText();
03048     return;
03049   }
03050 
03051   uint col = QMAX( c.col(), 0 );
03052   uint line = QMAX( c.line(), 0 );
03053 
03054   if ((col == 0) && (line == 0))
03055     return;
03056 
03057   int complement = 0;
03058   if (col > 0)
03059   {
03060     if (config()->configFlags() & KateDocument::cfAutoBrackets)
03061     {
03062       // if inside empty (), {}, [], '', "" delete both
03063       KateTextLine::Ptr tl = m_buffer->plainLine(line);
03064       if(!tl) return;
03065       QChar prevChar = tl->getChar(col-1);
03066       QChar nextChar = tl->getChar(col);
03067 
03068       if ( (prevChar == '"' && nextChar == '"') ||
03069            (prevChar == '\'' && nextChar == '\'') ||
03070            (prevChar == '(' && nextChar == ')') ||
03071            (prevChar == '[' && nextChar == ']') ||
03072            (prevChar == '{' && nextChar == '}') )
03073       {
03074         complement = 1;
03075       }
03076     }
03077     if (!(config()->configFlags() & KateDocument::cfBackspaceIndents))
03078     {
03079       // ordinary backspace
03080       //c.cursor.col--;
03081       removeText(line, col-1, line, col+complement);
03082     }
03083     else
03084     {
03085       // backspace indents: erase to next indent position
03086       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03087 
03088       // don't forget this check!!!! really!!!!
03089       if (!textLine)
03090         return;
03091 
03092       int colX = textLine->cursorX(col, config()->tabWidth());
03093       int pos = textLine->firstChar();
03094       if (pos > 0)
03095         pos = textLine->cursorX(pos, config()->tabWidth());
03096 
03097       if (pos < 0 || pos >= (int)colX)
03098       {
03099         // only spaces on left side of cursor
03100         // search a line with less spaces
03101         int y = line;
03102         while (--y >= 0)
03103         {
03104           // this is save, y <= line, and line was already success
03105           textLine = m_buffer->plainLine(y);
03106 
03107           pos = textLine->firstChar();
03108 
03109           if (pos >= 0)
03110           {
03111             pos = textLine->cursorX(pos, config()->tabWidth());
03112             if (pos < (int)colX)
03113             {
03114               replaceWithOptimizedSpace(line, col, pos, config()->configFlags());
03115               break;
03116             }
03117           }
03118         }
03119         if (y < 0) {
03120           // FIXME: what shoud we do in this case?
03121           removeText(line, 0, line, col+complement);
03122         }
03123       }
03124       else
03125         removeText(line, col-1, line, col+complement);
03126     }
03127   }
03128   else
03129   {
03130     // col == 0: wrap to previous line
03131     if (line >= 1)
03132     {
03133       KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
03134 
03135       // don't forget this check!!!! really!!!!
03136       if (!textLine)
03137         return;
03138 
03139       if (config()->wordWrap() && textLine->endingWith(QString::fromLatin1(" ")))
03140       {
03141         // gg: in hard wordwrap mode, backspace must also eat the trailing space
03142         removeText (line-1, textLine->length()-1, line, 0);
03143       }
03144       else
03145         removeText (line-1, textLine->length(), line, 0);
03146     }
03147   }
03148 
03149   emit backspacePressed();
03150 }
03151 
03152 void KateDocument::del( KateView *view, const KateTextCursor& c )
03153 {
03154   if ( !view->config()->persistentSelection() && view->hasSelection() ) {
03155     view->removeSelectedText();
03156     return;
03157   }
03158 
03159   if( c.col() < (int) m_buffer->plainLine(c.line())->length())
03160   {
03161     removeText(c.line(), c.col(), c.line(), c.col()+1);
03162   }
03163   else if ( (uint)c.line() < lastLine() )
03164   {
03165     removeText(c.line(), c.col(), c.line()+1, 0);
03166   }
03167 }
03168 
03169 void KateDocument::paste ( KateView* view )
03170 {
03171   QString s = QApplication::clipboard()->text();
03172 
03173   if (s.isEmpty())
03174     return;
03175 
03176   uint lines = s.contains (QChar ('\n'));
03177 
03178   m_undoDontMerge = true;
03179 
03180   editStart ();
03181 
03182   if (!view->config()->persistentSelection() && view->hasSelection() )
03183     view->removeSelectedText();
03184 
03185   uint line = view->cursorLine ();
03186   uint column = view->cursorColumnReal ();
03187 
03188   insertText ( line, column, s, view->blockSelectionMode() );
03189 
03190   editEnd();
03191 
03192   // move cursor right for block select, as the user is moved right internal
03193   // even in that case, but user expects other behavior in block selection
03194   // mode !
03195   if (view->blockSelectionMode())
03196     view->setCursorPositionInternal (line+lines, column);
03197 
03198   if (m_indenter->canProcessLine())
03199   {
03200     editStart();
03201 
03202     KateDocCursor begin(line, 0, this);
03203     KateDocCursor end(line + lines, 0, this);
03204 
03205     m_indenter->processSection (begin, end);
03206 
03207     editEnd();
03208   }
03209 
03210   if (!view->blockSelectionMode()) emit charactersSemiInteractivelyInserted (line, column, s);
03211   m_undoDontMerge = true;
03212 }
03213 
03214 void KateDocument::insertIndentChars ( KateView *view )
03215 {
03216   editStart ();
03217 
03218   QString s;
03219   if (config()->configFlags() & KateDocument::cfSpaceIndent)
03220   {
03221     int width = config()->indentationWidth();
03222     s.fill (' ', width - (view->cursorColumnReal() % width));
03223   }
03224   else
03225     s.append ('\t');
03226 
03227   insertText (view->cursorLine(), view->cursorColumnReal(), s);
03228 
03229   editEnd ();
03230 }
03231 
03232 void KateDocument::indent ( KateView *v, uint line, int change)
03233 {
03234   editStart ();
03235 
03236   if (!hasSelection())
03237   {
03238     // single line
03239     optimizeLeadingSpace(line, config()->configFlags(), change);
03240   }
03241   else
03242   {
03243     int sl = v->selStartLine();
03244     int el = v->selEndLine();
03245     int ec = v->selEndCol();
03246 
03247     if ((ec == 0) && ((el-1) >= 0))
03248     {
03249       el--; /* */
03250     }
03251 
03252     if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) {
03253       // unindent so that the existing indent profile doesn't get screwed
03254       // if any line we may unindent is already full left, don't do anything
03255       int adjustedChange = -change;
03256 
03257       for (line = sl; (int) line <= el && adjustedChange > 0; line++) {
03258         KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03259         int firstChar = textLine->firstChar();
03260         if (firstChar >= 0 && (v->lineSelected(line) || v->lineHasSelected(line))) {
03261           int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth();
03262           if (maxUnindent < adjustedChange)
03263             adjustedChange = maxUnindent;
03264         }
03265       }
03266 
03267       change = -adjustedChange;
03268     }
03269 
03270     for (line = sl; (int) line <= el; line++) {
03271       if (v->lineSelected(line) || v->lineHasSelected(line)) {
03272         optimizeLeadingSpace(line, config()->configFlags(), change);
03273       }
03274     }
03275   }
03276 
03277   editEnd ();
03278 }
03279 
03280 void KateDocument::align(KateView *view, uint line)
03281 {
03282   if (m_indenter->canProcessLine())
03283   {
03284     editStart ();
03285 
03286     if (!view->hasSelection())
03287     {
03288       KateDocCursor curLine(line, 0, this);
03289       m_indenter->processLine (curLine);
03290       editEnd ();
03291       activeView()->setCursorPosition (line, curLine.col());
03292     }
03293     else
03294     {
03295       m_indenter->processSection (view->selStart(), view->selEnd());
03296       editEnd ();
03297     }
03298   }
03299 }
03300 
03301 /*
03302   Optimize the leading whitespace for a single line.
03303   If change is > 0, it adds indentation units (indentationChars)
03304   if change is == 0, it only optimizes
03305   If change is < 0, it removes indentation units
03306   This will be used to indent, unindent, and optimal-fill a line.
03307   If excess space is removed depends on the flag cfKeepExtraSpaces
03308   which has to be set by the user
03309 */
03310 void KateDocument::optimizeLeadingSpace(uint line, int flags, int change)
03311 {
03312   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03313 
03314   int first_char = textline->firstChar();
03315 
03316   int w = 0;
03317   if (flags & KateDocument::cfSpaceIndent)
03318     w = config()->indentationWidth();
03319   else
03320     w = config()->tabWidth();
03321 
03322   if (first_char < 0)
03323     first_char = textline->length();
03324 
03325   int space =  textline->cursorX(first_char, config()->tabWidth()) + change * w;
03326   if (space < 0)
03327     space = 0;
03328 
03329   if (!(flags & KateDocument::cfKeepExtraSpaces))
03330   {
03331     uint extra = space % w;
03332 
03333     space -= extra;
03334     if (extra && change < 0) {
03335       // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide)
03336       space += w;
03337     }
03338   }
03339 
03340   //kdDebug(13020)  << "replace With Op: " << line << " " << first_char << " " << space << endl;
03341   replaceWithOptimizedSpace(line, first_char, space, flags);
03342 }
03343 
03344 void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags)
03345 {
03346   uint length;
03347   QString new_space;
03348 
03349   if (flags & KateDocument::cfSpaceIndent && ! (flags & KateDocumentConfig::cfMixedIndent) ) {
03350     length = space;
03351     new_space.fill(' ', length);
03352   }
03353   else {
03354     length = space / config()->tabWidth();
03355     new_space.fill('\t', length);
03356 
03357     QString extra_space;
03358     extra_space.fill(' ', space % config()->tabWidth());
03359     length += space % config()->tabWidth();
03360     new_space += extra_space;
03361   }
03362 
03363   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03364   uint change_from;
03365   for (change_from = 0; change_from < upto_column && change_from < length; change_from++) {
03366     if (textline->getChar(change_from) != new_space[change_from])
03367       break;
03368   }
03369 
03370   editStart();
03371 
03372   if (change_from < upto_column)
03373     removeText(line, change_from, line, upto_column);
03374 
03375   if (change_from < length)
03376     insertText(line, change_from, new_space.right(length - change_from));
03377 
03378   editEnd();
03379 }
03380 
03381 /*
03382   Remove a given string at the begining
03383   of the current line.
03384 */
03385 bool KateDocument::removeStringFromBegining(int line, QString &str)
03386 {
03387   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03388 
03389   int index = 0;
03390   bool there = false;
03391 
03392   if (textline->startingWith(str))
03393     there = true;
03394   else
03395   {
03396     index = textline->firstChar ();
03397 
03398     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03399       there = true;
03400   }
03401 
03402   if (there)
03403   {
03404     // Remove some chars
03405     removeText (line, index, line, index+str.length());
03406   }
03407 
03408   return there;
03409 }
03410 
03411 /*
03412   Remove a given string at the end
03413   of the current line.
03414 */
03415 bool KateDocument::removeStringFromEnd(int line, QString &str)
03416 {
03417   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03418 
03419   int index = 0;
03420   bool there = false;
03421 
03422   if(textline->endingWith(str))
03423   {
03424     index = textline->length() - str.length();
03425     there = true;
03426   }
03427   else
03428   {
03429     index = textline->lastChar ()-str.length()+1;
03430 
03431     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03432       there = true;
03433   }
03434 
03435   if (there)
03436   {
03437     // Remove some chars
03438     removeText (line, index, line, index+str.length());
03439   }
03440 
03441   return there;
03442 }
03443 
03444 /*
03445   Add to the current line a comment line mark at
03446   the begining.
03447 */
03448 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
03449 {
03450   if (highlight()->getCommentSingleLinePosition(attrib)==KateHighlighting::CSLPosColumn0)
03451   {
03452     QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03453     insertText (line, 0, commentLineMark);
03454   }
03455   else
03456   {
03457     QString commentLineMark=highlight()->getCommentSingleLineStart(attrib);
03458     KateTextLine::Ptr l = m_buffer->line(line);
03459     int pos=l->firstChar();
03460     if (pos >=0)
03461       insertText(line,pos,commentLineMark);
03462   }
03463 }
03464 
03465 /*
03466   Remove from the current line a comment line mark at
03467   the begining if there is one.
03468 */
03469 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
03470 {
03471   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
03472   QString longCommentMark = shortCommentMark + " ";
03473 
03474   editStart();
03475 
03476   // Try to remove the long comment mark first
03477   bool removed = (removeStringFromBegining(line, longCommentMark)
03478                   || removeStringFromBegining(line, shortCommentMark));
03479 
03480   editEnd();
03481 
03482   return removed;
03483 }
03484 
03485 /*
03486   Add to the current line a start comment mark at the
03487  begining and a stop comment mark at the end.
03488 */
03489 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
03490 {
03491   QString startCommentMark = highlight()->getCommentStart( attrib ) + " ";
03492   QString stopCommentMark = " " + highlight()->getCommentEnd( attrib );
03493 
03494   editStart();
03495 
03496   // Add the start comment mark
03497   insertText (line, 0, startCommentMark);
03498 
03499   // Go to the end of the line
03500   int col = m_buffer->plainLine(line)->length();
03501 
03502   // Add the stop comment mark
03503   insertText (line, col, stopCommentMark);
03504 
03505   editEnd();
03506 }
03507 
03508 /*
03509   Remove from the current line a start comment mark at
03510   the begining and a stop comment mark at the end.
03511 */
03512 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
03513 {
03514   QString shortStartCommentMark = highlight()->getCommentStart( attrib );
03515   QString longStartCommentMark = shortStartCommentMark + " ";
03516   QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
03517   QString longStopCommentMark = " " + shortStopCommentMark;
03518 
03519   editStart();
03520 
03521 #ifdef __GNUC__
03522 #warning "that's a bad idea, can lead to stray endings, FIXME"
03523 #endif
03524   // Try to remove the long start comment mark first
03525   bool removedStart = (removeStringFromBegining(line, longStartCommentMark)
03526                        || removeStringFromBegining(line, shortStartCommentMark));
03527 
03528   bool removedStop = false;
03529   if (removedStart)
03530   {
03531     // Try to remove the long stop comment mark first
03532     removedStop = (removeStringFromEnd(line, longStopCommentMark)
03533                       || removeStringFromEnd(line, shortStopCommentMark));
03534   }
03535 
03536   editEnd();
03537 
03538   return (removedStart || removedStop);
03539 }
03540 
03541 /*
03542   Add to the current selection a start comment
03543  mark at the begining and a stop comment mark
03544  at the end.
03545 */
03546 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
03547 {
03548   QString startComment = highlight()->getCommentStart( attrib );
03549   QString endComment = highlight()->getCommentEnd( attrib );
03550 
03551   int sl = view->selStartLine();
03552   int el = view->selEndLine();
03553   int sc = view->selStartCol();
03554   int ec = view->selEndCol();
03555 
03556   if ((ec == 0) && ((el-1) >= 0))
03557   {
03558     el--;
03559     ec = m_buffer->plainLine (el)->length();
03560   }
03561 
03562   editStart();
03563 
03564   insertText (el, ec, endComment);
03565   insertText (sl, sc, startComment);
03566 
03567   editEnd ();
03568 
03569   // Set the new selection
03570   ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 );
03571   view->setSelection(sl, sc, el, ec);
03572 }
03573 
03574 /*
03575   Add to the current selection a comment line
03576  mark at the begining of each line.
03577 */
03578 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
03579 {
03580   QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03581 
03582   int sl = view->selStartLine();
03583   int el = view->selEndLine();
03584 
03585   if ((view->selEndCol() == 0) && ((el-1) >= 0))
03586   {
03587     el--;
03588   }
03589 
03590   editStart();
03591 
03592   // For each line of the selection
03593   for (int z = el; z >= sl; z--) {
03594     //insertText (z, 0, commentLineMark);
03595     addStartLineCommentToSingleLine(z, attrib );
03596   }
03597 
03598   editEnd ();
03599 
03600   // Set the new selection
03601 
03602   KateDocCursor end (view->selEnd());
03603   end.setCol(view->selEndCol() + ((el == view->selEndLine()) ? commentLineMark.length() : 0) );
03604 
03605   view->setSelection(view->selStartLine(), 0, end.line(), end.col());
03606 }
03607 
03608 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
03609 {
03610   for(; line < (int)m_buffer->count(); line++) {
03611     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03612 
03613     if (!textLine)
03614       break;
03615 
03616     col = textLine->nextNonSpaceChar(col);
03617     if(col != -1)
03618       return true; // Next non-space char found
03619     col = 0;
03620   }
03621   // No non-space char found
03622   line = -1;
03623   col = -1;
03624   return false;
03625 }
03626 
03627 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
03628 {
03629   while(true)
03630   {
03631     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03632 
03633     if (!textLine)
03634       break;
03635 
03636     col = textLine->previousNonSpaceChar(col);
03637     if(col != -1) return true;
03638     if(line == 0) return false;
03639     --line;
03640     col = textLine->length();
03641 }
03642   // No non-space char found
03643   line = -1;
03644   col = -1;
03645   return false;
03646 }
03647 
03648 /*
03649   Remove from the selection a start comment mark at
03650   the begining and a stop comment mark at the end.
03651 */
03652 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
03653 {
03654   QString startComment = highlight()->getCommentStart( attrib );
03655   QString endComment = highlight()->getCommentEnd( attrib );
03656 
03657   int sl = kMax<int> (0, view->selStartLine());
03658   int el = kMin<int>  (view->selEndLine(), lastLine());
03659   int sc = view->selStartCol();
03660   int ec = view->selEndCol();
03661 
03662   // The selection ends on the char before selectEnd
03663   if (ec != 0) {
03664     ec--;
03665   } else {
03666     if (el > 0) {
03667       el--;
03668       ec = m_buffer->plainLine(el)->length() - 1;
03669     }
03670   }
03671 
03672   int startCommentLen = startComment.length();
03673   int endCommentLen = endComment.length();
03674 
03675   // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/
03676 
03677   bool remove = nextNonSpaceCharPos(sl, sc)
03678       && m_buffer->plainLine(sl)->stringAtPos(sc, startComment)
03679       && previousNonSpaceCharPos(el, ec)
03680       && ( (ec - endCommentLen + 1) >= 0 )
03681       && m_buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment);
03682 
03683   if (remove) {
03684     editStart();
03685 
03686     removeText (el, ec - endCommentLen + 1, el, ec + 1);
03687     removeText (sl, sc, sl, sc + startCommentLen);
03688 
03689     editEnd ();
03690 
03691     // Set the new selection
03692     ec -= endCommentLen + ( (el == sl) ? startCommentLen : 0 );
03693     view->setSelection(sl, sc, el, ec + 1);
03694   }
03695 
03696   return remove;
03697 }
03698 
03699 bool KateDocument::removeStartStopCommentFromRegion(const KateTextCursor &start,const KateTextCursor &end,int attrib)
03700 {
03701   QString startComment = highlight()->getCommentStart( attrib );
03702   QString endComment = highlight()->getCommentEnd( attrib );
03703   int startCommentLen = startComment.length();
03704   int endCommentLen = endComment.length();
03705 
03706     bool remove = m_buffer->plainLine(start.line())->stringAtPos(start.col(), startComment)
03707       && ( (end.col() - endCommentLen ) >= 0 )
03708       && m_buffer->plainLine(end.line())->stringAtPos(end.col() - endCommentLen , endComment);
03709       if (remove)  {
03710         editStart();
03711           removeText(end.line(),end.col()-endCommentLen,end.line(),end.col());
03712           removeText(start.line(),start.col(),start.line(),start.col()+startCommentLen);
03713         editEnd();
03714       }
03715       return remove;
03716 }
03717 
03718 /*
03719   Remove from the begining of each line of the
03720   selection a start comment line mark.
03721 */
03722 bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib )
03723 {
03724   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
03725   QString longCommentMark = shortCommentMark + " ";
03726 
03727   int sl = view->selStartLine();
03728   int el = view->selEndLine();
03729 
03730   if ((view->selEndCol() == 0) && ((el-1) >= 0))
03731   {
03732     el--;
03733   }
03734 
03735   // Find out how many char will be removed from the last line
03736   int removeLength = 0;
03737   if (m_buffer->plainLine(el)->startingWith(longCommentMark))
03738     removeLength = longCommentMark.length();
03739   else if (m_buffer->plainLine(el)->startingWith(shortCommentMark))
03740     removeLength = shortCommentMark.length();
03741 
03742   bool removed = false;
03743 
03744   editStart();
03745 
03746   // For each line of the selection
03747   for (int z = el; z >= sl; z--)
03748   {
03749     // Try to remove the long comment mark first
03750     removed = (removeStringFromBegining(z, longCommentMark)
03751                  || removeStringFromBegining(z, shortCommentMark)
03752                  || removed);
03753   }
03754 
03755   editEnd();
03756 
03757   if (removed)
03758   {
03759     // Set the new selection
03760     KateDocCursor end (view->selEnd());
03761     end.setCol(view->selEndCol() - ((el == view->selEndLine()) ? removeLength : 0) );
03762 
03763     setSelection(view->selStartLine(), view->selStartCol(), end.line(), end.col());
03764   }
03765 
03766   return removed;
03767 }
03768 
03769 /*
03770   Comment or uncomment the selection or the current
03771   line if there is no selection.
03772 */
03773 void KateDocument::comment( KateView *v, uint line,uint column, int change)
03774 {
03775   // We need to check that we can sanely comment the selectino or region.
03776   // It is if the attribute of the first and last character of the range to
03777   // comment belongs to the same language definition.
03778   // for lines with no text, we need the attribute for the lines context.
03779   bool hassel = v->hasSelection();
03780   int startAttrib, endAttrib;
03781   if ( hassel )
03782   {
03783     KateTextLine::Ptr ln = kateTextLine( v->selStartLine() );
03784     int l = v->selStartLine(), c = v->selStartCol();
03785     startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03786 
03787     ln = kateTextLine( v->selEndLine() );
03788     l = v->selEndLine(), c = v->selEndCol();
03789     endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03790   }
03791   else
03792   {
03793     KateTextLine::Ptr ln = kateTextLine( line );
03794     if ( ln->length() )
03795     {
03796       startAttrib = ln->attribute( ln->firstChar() );
03797       endAttrib = ln->attribute( ln->lastChar() );
03798     }
03799     else
03800     {
03801       int l = line, c = 0;
03802       if ( nextNonSpaceCharPos( l, c )  || previousNonSpaceCharPos( l, c ) )
03803         startAttrib = endAttrib = kateTextLine( l )->attribute( c );
03804       else
03805         startAttrib = endAttrib = 0;
03806     }
03807   }
03808 
03809   if ( ! highlight()->canComment( startAttrib, endAttrib ) )
03810   {
03811     kdDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!"<<endl;
03812     return;
03813   }
03814 
03815   bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
03816   bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
03817       && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
03818 
03819   bool removed = false;
03820 
03821   if (change > 0) // comment
03822   {
03823     if ( !hassel )
03824     {
03825       if ( hasStartLineCommentMark )
03826         addStartLineCommentToSingleLine( line, startAttrib );
03827       else if ( hasStartStopCommentMark )
03828         addStartStopCommentToSingleLine( line, startAttrib );
03829     }
03830     else
03831     {
03832       // anders: prefer single line comment to avoid nesting probs
03833       // If the selection starts after first char in the first line
03834       // or ends before the last char of the last line, we may use
03835       // multiline comment markers.
03836       // TODO We should try to detect nesting.
03837       //    - if selection ends at col 0, most likely she wanted that
03838       // line ignored
03839       if ( hasStartStopCommentMark &&
03840            ( !hasStartLineCommentMark || (
03841            ( v->selStartCol() > m_buffer->plainLine( v->selStartLine() )->firstChar() ) ||
03842            ( v->selEndCol() < ((int)m_buffer->plainLine( v->selEndLine() )->length()) )
03843          ) ) )
03844         addStartStopCommentToSelection( v, startAttrib );
03845       else if ( hasStartLineCommentMark )
03846         addStartLineCommentToSelection( v, startAttrib );
03847     }
03848   }
03849   else // uncomment
03850   {
03851     if ( !hassel )
03852     {
03853       removed = ( hasStartLineCommentMark
03854                   && removeStartLineCommentFromSingleLine( line, startAttrib ) )
03855         || ( hasStartStopCommentMark
03856              && removeStartStopCommentFromSingleLine( line, startAttrib ) );
03857       if ((!removed) && foldingTree()) {
03858         kdDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)"<<endl;
03859         int commentRegion=(highlight()->commentRegion(startAttrib));
03860         if (commentRegion){
03861            KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
03862            if (n) {
03863             KateTextCursor start,end;
03864              if ((n->nodeType()==commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
03865                 kdDebug(13020)<<"Enclosing region found:"<<start.col()<<"/"<<start.line()<<"-"<<end.col()<<"/"<<end.line()<<endl;
03866                 removeStartStopCommentFromRegion(start,end,startAttrib);
03867              } else {
03868                   kdDebug(13020)<<"Enclosing region found, but not valid"<<endl;
03869                   kdDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion<<endl;
03870              }
03871             //perhaps nested regions should be hadled here too...
03872           } else kdDebug(13020)<<"No enclosing region found"<<endl;
03873         } else kdDebug(13020)<<"No comment region specified for current hl"<<endl;
03874       }
03875     }
03876     else
03877     {
03878       // anders: this seems like it will work with above changes :)
03879       removed = ( hasStartLineCommentMark
03880           && removeStartLineCommentFromSelection( v, startAttrib ) )
03881         || ( hasStartStopCommentMark
03882           && removeStartStopCommentFromSelection( v, startAttrib ) );
03883     }
03884   }
03885 }
03886 
03887 void KateDocument::transform( KateView *v, const KateTextCursor &c,
03888                             KateDocument::TextTransform t )
03889 {
03890   editStart();
03891   uint cl( c.line() ), cc( c.col() );
03892   bool selectionRestored = false;
03893 
03894   if ( hasSelection() )
03895   {
03896     // cache the selection and cursor, so we can be sure to restore.
03897     KateTextCursor selstart = v->selStart();
03898     KateTextCursor selend = v->selEnd();
03899 
03900     int ln = v->selStartLine();
03901     while ( ln <= selend.line() )
03902     {
03903       uint start, end;
03904       start = (ln == selstart.line() || v->blockSelectionMode()) ?
03905           selstart.col() : 0;
03906       end = (ln == selend.line() || v->blockSelectionMode()) ?
03907           selend.col() : lineLength( ln );
03908       if ( start > end )
03909       {
03910         uint t = start;
03911         start = end;
03912         end = t;
03913       }
03914       QString s = text( ln, start, ln, end );
03915 
03916       if ( t == Uppercase )
03917         s = s.upper();
03918       else if ( t == Lowercase )
03919         s = s.lower();
03920       else // Capitalize
03921       {
03922         KateTextLine::Ptr l = m_buffer->plainLine( ln );
03923         uint p ( 0 );
03924         while( p < s.length() )
03925         {
03926           // If bol or the character before is not in a word, up this one:
03927           // 1. if both start and p is 0, upper char.
03928           // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
03929           // 3. if p-1 is not in a word, upper.
03930           if ( ( ! start && ! p ) ||
03931                    ( ( ln == selstart.line() || v->blockSelectionMode() ) &&
03932                    ! p && ! highlight()->isInWord( l->getChar( start - 1 )) ) ||
03933                    ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
03934              )
03935             s[p] = s.at(p).upper();
03936           p++;
03937         }
03938       }
03939 
03940       removeText( ln, start, ln, end );
03941       insertText( ln, start, s );
03942 
03943       ln++;
03944     }
03945 
03946     // restore selection
03947     v->setSelection( selstart, selend );
03948     selectionRestored = true;
03949 
03950   } else {  // no selection
03951     QString s;
03952     int n ( cc );
03953     switch ( t ) {
03954       case Uppercase:
03955       s = text( cl, cc, cl, cc + 1 ).upper();
03956       break;
03957       case Lowercase:
03958       s = text( cl, cc, cl, cc + 1 ).lower();
03959       break;
03960       case Capitalize:
03961       {
03962         KateTextLine::Ptr l = m_buffer->plainLine( cl );
03963         while ( n > 0 && highlight()->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) )
03964           n--;
03965         s = text( cl, n, cl, n + 1 ).upper();
03966       }
03967       break;
03968       default:
03969       break;
03970     }
03971     removeText( cl, n, cl, n+1 );
03972     insertText( cl, n, s );
03973   }
03974 
03975   if ( ! selectionRestored )
03976     v->setCursorPosition( cl, cc );
03977 
03978   editEnd();
03979 }
03980 
03981 void KateDocument::joinLines( uint first, uint last )
03982 {
03983 //   if ( first == last ) last += 1;
03984   editStart();
03985   int line( first );
03986   while ( first < last )
03987   {
03988     // Normalize the whitespace in the joined lines by making sure there's
03989     // always exactly one space between the joined lines
03990     // This cannot be done in editUnwrapLine, because we do NOT want this
03991     // behaviour when deleting from the start of a line, just when explicitly
03992     // calling the join command
03993     KateTextLine::Ptr l = m_buffer->line( line );
03994     KateTextLine::Ptr tl = m_buffer->line( line + 1 );
03995 
03996     if ( !l || !tl )
03997     {
03998       editEnd();
03999       return;
04000     }
04001 
04002     int pos = tl->firstChar();
04003     if ( pos >= 0 )
04004     {
04005       if (pos != 0)
04006         editRemoveText( line + 1, 0, pos );
04007       if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) )
04008         editInsertText( line + 1, 0, " " );
04009     }
04010     else
04011     {
04012       // Just remove the whitespace and let Kate handle the rest
04013       editRemoveText( line + 1, 0, tl->length() );
04014     }
04015 
04016     editUnWrapLine( line );
04017     first++;
04018   }
04019   editEnd();
04020 }
04021 
04022 QString KateDocument::getWord( const KateTextCursor& cursor ) {
04023   int start, end, len;
04024 
04025   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04026   len = textLine->length();
04027   start = end = cursor.col();
04028   if (start > len)        // Probably because of non-wrapping cursor mode.
04029     return QString("");
04030 
04031   while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
04032   while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(end))) end++;
04033   len = end - start;
04034   return QString(&textLine->text()[start], len);
04035 }
04036 
04037 void KateDocument::tagLines(int start, int end)
04038 {
04039   for (uint z = 0; z < m_views.count(); z++)
04040     m_views.at(z)->tagLines (start, end, true);
04041 }
04042 
04043 void KateDocument::tagLines(KateTextCursor start, KateTextCursor end)
04044 {
04045   // May need to switch start/end cols if in block selection mode
04046   if (blockSelectionMode() && start.col() > end.col()) {
04047     int sc = start.col();
04048     start.setCol(end.col());
04049     end.setCol(sc);
04050   }
04051 
04052   for (uint z = 0; z < m_views.count(); z++)
04053     m_views.at(z)->tagLines(start, end, true);
04054 }
04055 
04056 void KateDocument::repaintViews(bool paintOnlyDirty)
04057 {
04058   for (uint z = 0; z < m_views.count(); z++)
04059     m_views.at(z)->repaintText(paintOnlyDirty);
04060 }
04061 
04062 void KateDocument::tagAll()
04063 {
04064   for (uint z = 0; z < m_views.count(); z++)
04065   {
04066     m_views.at(z)->tagAll();
04067     m_views.at(z)->updateView (true);
04068   }
04069 }
04070 
04071 uint KateDocument::configFlags ()
04072 {
04073   return config()->configFlags();
04074 }
04075 
04076 void KateDocument::setConfigFlags (uint flags)
04077 {
04078   config()->setConfigFlags(flags);
04079 }
04080 
04081 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04082 inline bool isEndBracket  ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04083 inline bool isBracket     ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04084 
04085 /*
04086    Bracket matching uses the following algorithm:
04087    If in overwrite mode, match the bracket currently underneath the cursor.
04088    Otherwise, if the character to the right of the cursor is an starting bracket,
04089    match it. Otherwise if the character to the left of the cursor is a
04090    ending bracket, match it. Otherwise, if the the character to the left
04091    of the cursor is an starting bracket, match it. Otherwise, if the character
04092    to the right of the cursor is an ending bracket, match it. Otherwise, don't
04093    match anything.
04094 */
04095 void KateDocument::newBracketMark( const KateTextCursor& cursor, KateBracketRange& bm, int maxLines )
04096 {
04097   bm.setValid(false);
04098 
04099   bm.start() = cursor;
04100 
04101   if( !findMatchingBracket( bm.start(), bm.end(), maxLines ) )
04102     return;
04103 
04104   bm.setValid(true);
04105 
04106   const int tw = config()->tabWidth();
04107   const int indentStart = m_buffer->plainLine(bm.start().line())->indentDepth(tw);
04108   const int indentEnd = m_buffer->plainLine(bm.end().line())->indentDepth(tw);
04109   bm.setIndentMin(QMIN(indentStart, indentEnd));
04110 }
04111 
04112 bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines )
04113 {
04114   KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() );
04115   if( !textLine )
04116     return false;
04117 
04118   QChar right = textLine->getChar( start.col() );
04119   QChar left  = textLine->getChar( start.col() - 1 );
04120   QChar bracket;
04121 
04122   if ( config()->configFlags() & cfOvr ) {
04123     if( isBracket( right ) ) {
04124       bracket = right;
04125     } else {
04126       return false;
04127     }
04128   } else if ( isStartBracket( right ) ) {
04129     bracket = right;
04130   } else if ( isEndBracket( left ) ) {
04131     start.setCol(start.col() - 1);
04132     bracket = left;
04133   } else if ( isBracket( left ) ) {
04134     start.setCol(start.col() - 1);
04135     bracket = left;
04136   } else if ( isBracket( right ) ) {
04137     bracket = right;
04138   } else {
04139     return false;
04140   }
04141 
04142   QChar opposite;
04143 
04144   switch( bracket ) {
04145   case '{': opposite = '}'; break;
04146   case '}': opposite = '{'; break;
04147   case '[': opposite = ']'; break;
04148   case ']': opposite = '['; break;
04149   case '(': opposite = ')'; break;
04150   case ')': opposite = '('; break;
04151   default: return false;
04152   }
04153 
04154   bool forward = isStartBracket( bracket );
04155   int startAttr = textLine->attribute( start.col() );
04156   uint count = 0;
04157   int lines = 0;
04158   end = start;
04159 
04160   while( true ) {
04161     /* Increment or decrement, check base cases */
04162     if( forward ) {
04163       end.setCol(end.col() + 1);
04164       if( end.col() >= lineLength( end.line() ) ) {
04165         if( end.line() >= (int)lastLine() )
04166           return false;
04167         end.setPos(end.line() + 1, 0);
04168         textLine = m_buffer->plainLine( end.line() );
04169         lines++;
04170       }
04171     } else {
04172       end.setCol(end.col() - 1);
04173       if( end.col() < 0 ) {
04174         if( end.line() <= 0 )
04175           return false;
04176         end.setLine(end.line() - 1);
04177         end.setCol(lineLength( end.line() ) - 1);
04178         textLine = m_buffer->plainLine( end.line() );
04179         lines++;
04180       }
04181     }
04182 
04183     if ((maxLines != -1) && (lines > maxLines))
04184       return false;
04185 
04186     /* Easy way to skip comments */
04187     if( textLine->attribute( end.col() ) != startAttr )
04188       continue;
04189 
04190     /* Check for match */
04191     QChar c = textLine->getChar( end.col() );
04192     if( c == bracket ) {
04193       count++;
04194     } else if( c == opposite ) {
04195       if( count == 0 )
04196         return true;
04197       count--;
04198     }
04199 
04200   }
04201 }
04202 
04203 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
04204 {
04205   KParts::ReadWritePart::guiActivateEvent( ev );
04206   if ( ev->activated() )
04207     emit selectionChanged();
04208 }
04209 
04210 void KateDocument::setDocName (QString name )
04211 {
04212   if ( name == m_docName )
04213     return;
04214 
04215   if ( !name.isEmpty() )
04216   {
04217     // TODO check for similarly named documents
04218     m_docName = name;
04219     updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04220     emit nameChanged((Kate::Document *) this);
04221     return;
04222   }
04223 
04224   // if the name is set, and starts with FILENAME, it should not be changed!
04225   if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return;
04226 
04227   int count = -1;
04228 
04229   for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
04230   {
04231     if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) )
04232       if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count )
04233         count = KateFactory::self()->documents()->at(z)->m_docNameNumber;
04234   }
04235 
04236   m_docNameNumber = count + 1;
04237 
04238   m_docName = url().filename();
04239 
04240   if (m_docName.isEmpty())
04241     m_docName = i18n ("Untitled");
04242 
04243   if (m_docNameNumber > 0)
04244     m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
04245 
04246   updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04247   emit nameChanged ((Kate::Document *) this);
04248 }
04249 
04250 void KateDocument::slotModifiedOnDisk( Kate::View * /*v*/ )
04251 {
04252   if ( m_isasking < 0 )
04253   {
04254     m_isasking = 0;
04255     return;
04256   }
04257 
04258   if ( !s_fileChangedDialogsActivated || m_isasking )
04259     return;
04260 
04261   if (m_modOnHd && !url().isEmpty())
04262   {
04263     m_isasking = 1;
04264 
04265     KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), widget() );
04266     switch ( p.exec() )
04267     {
04268       case KateModOnHdPrompt::Save:
04269       {
04270         m_modOnHd = false;
04271         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
04272             url().url(),QString::null,widget(),i18n("Save File"));
04273 
04274         kdDebug(13020)<<"got "<<res.URLs.count()<<" URLs"<<endl;
04275         if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first() ) )
04276         {
04277           setEncoding( res.encoding );
04278 
04279           if( ! saveAs( res.URLs.first() ) )
04280           {
04281             KMessageBox::error( widget(), i18n("Save failed") );
04282             m_modOnHd = true;
04283           }
04284           else
04285             emit modifiedOnDisc( this, false, 0 );
04286         }
04287         else // the save as dialog was cancelled, we are still modified on disk
04288         {
04289           m_modOnHd = true;
04290         }
04291 
04292         m_isasking = 0;
04293         break;
04294       }
04295 
04296       case KateModOnHdPrompt::Reload:
04297         m_modOnHd = false;
04298         emit modifiedOnDisc( this, false, 0 );
04299         reloadFile();
04300         m_isasking = 0;
04301         break;
04302 
04303       case KateModOnHdPrompt::Ignore:
04304         m_modOnHd = false;
04305         emit modifiedOnDisc( this, false, 0 );
04306         m_isasking = 0;
04307         break;
04308 
04309       case KateModOnHdPrompt::Overwrite:
04310         m_modOnHd = false;
04311         emit modifiedOnDisc( this, false, 0 );
04312         m_isasking = 0;
04313         save();
04314         break;
04315 
04316       default:               // cancel: ignore next focus event
04317         m_isasking = -1;
04318     }
04319   }
04320 }
04321 
04322 void KateDocument::setModifiedOnDisk( int reason )
04323 {
04324   m_modOnHdReason = reason;
04325   m_modOnHd = (reason > 0);
04326   emit modifiedOnDisc( this, (reason > 0), reason );
04327 }
04328 
04329 class KateDocumentTmpMark
04330 {
04331   public:
04332     QString line;
04333     KTextEditor::Mark mark;
04334 };
04335 
04336 void KateDocument::reloadFile()
04337 {
04338   if ( !url().isEmpty() )
04339   {
04340     if (m_modOnHd && s_fileChangedDialogsActivated)
04341     {
04342       int i = KMessageBox::warningYesNoCancel
04343                 (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
04344                 i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes"));
04345 
04346       if ( i != KMessageBox::Yes)
04347       {
04348         if (i == KMessageBox::No)
04349         {
04350           m_modOnHd = false;
04351           m_modOnHdReason = 0;
04352           emit modifiedOnDisc (this, m_modOnHd, 0);
04353         }
04354 
04355         return;
04356       }
04357     }
04358 
04359     QValueList<KateDocumentTmpMark> tmp;
04360 
04361     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
04362     {
04363       KateDocumentTmpMark m;
04364 
04365       m.line = textLine (it.current()->line);
04366       m.mark = *it.current();
04367 
04368       tmp.append (m);
04369     }
04370 
04371     uint mode = hlMode ();
04372     bool byUser = hlSetByUser;
04373 
04374     m_storedVariables.clear();
04375 
04376     m_reloading = true;
04377 
04378     QValueList<int> lines, cols;
04379     for ( uint i=0; i < m_views.count(); i++ )
04380     {
04381       lines.append( m_views.at( i )->cursorLine() );
04382       cols.append( m_views.at( i )->cursorColumn() );
04383     }
04384 
04385     KateDocument::openURL( url() );
04386 
04387     for ( uint i=0; i < m_views.count(); i++ )
04388       m_views.at( i )->setCursorPositionInternal( lines[ i ], cols[ i ], m_config->tabWidth(), false );
04389 
04390     m_reloading = false;
04391 
04392     for (uint z=0; z < tmp.size(); z++)
04393     {
04394       if (z < numLines())
04395       {
04396         if (textLine(tmp[z].mark.line) == tmp[z].line)
04397           setMark (tmp[z].mark.line, tmp[z].mark.type);
04398       }
04399     }
04400 
04401     if (byUser)
04402       setHlMode (mode);
04403   }
04404 }
04405 
04406 void KateDocument::flush ()
04407 {
04408   closeURL ();
04409 }
04410 
04411 void KateDocument::setWordWrap (bool on)
04412 {
04413   config()->setWordWrap (on);
04414 }
04415 
04416 bool KateDocument::wordWrap ()
04417 {
04418   return config()->wordWrap ();
04419 }
04420 
04421 void KateDocument::setWordWrapAt (uint col)
04422 {
04423   config()->setWordWrapAt (col);
04424 }
04425 
04426 unsigned int KateDocument::wordWrapAt ()
04427 {
04428   return config()->wordWrapAt ();
04429 }
04430 
04431 void KateDocument::applyWordWrap ()
04432 {
04433   // dummy to make the API happy
04434 }
04435 
04436 void KateDocument::setPageUpDownMovesCursor (bool on)
04437 {
04438   config()->setPageUpDownMovesCursor (on);
04439 }
04440 
04441 bool KateDocument::pageUpDownMovesCursor ()
04442 {
04443   return config()->pageUpDownMovesCursor ();
04444 }
04445 
04446 void KateDocument::dumpRegionTree()
04447 {
04448   m_buffer->foldingTree()->debugDump();
04449 }
04450 //END
04451 
04452 //BEGIN KTextEditor::CursorInterface stuff
04453 
04454 KTextEditor::Cursor *KateDocument::createCursor ( )
04455 {
04456   return new KateSuperCursor (this, false, 0, 0, this);
04457 }
04458 
04459 void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range)
04460 {
04461   if (view)
04462     view->tagLines(range->start(), range->end());
04463   else
04464     tagLines(range->start(), range->end());
04465 }
04466 
04467 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
04468 {
04469   m_buffer->lineInfo(info,line);
04470 }
04471 
04472 KateCodeFoldingTree *KateDocument::foldingTree ()
04473 {
04474   return m_buffer->foldingTree();
04475 }
04476 
04477 void KateDocument::setEncoding (const QString &e)
04478 {
04479   if ( m_encodingSticky )
04480     return;
04481 
04482   QString ce = m_config->encoding().lower();
04483   if ( e.lower() == ce )
04484     return;
04485 
04486   m_config->setEncoding( e );
04487   if ( ! m_loading )
04488     reloadFile();
04489 }
04490 
04491 QString KateDocument::encoding() const
04492 {
04493   return m_config->encoding();
04494 }
04495 
04496 void KateDocument::updateConfig ()
04497 {
04498   emit undoChanged ();
04499   tagAll();
04500 
04501   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
04502   {
04503     view->updateDocumentConfig ();
04504   }
04505 
04506   // switch indenter if needed
04507   if (m_indenter->modeNumber() != m_config->indentationMode())
04508   {
04509     delete m_indenter;
04510     m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() );
04511   }
04512 
04513   m_indenter->updateConfig();
04514 
04515   m_buffer->setTabWidth (config()->tabWidth());
04516 
04517   // plugins
04518   for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
04519   {
04520     if (config()->plugin (i))
04521       loadPlugin (i);
04522     else
04523       unloadPlugin (i);
04524   }
04525 }
04526 
04527 //BEGIN Variable reader
04528 // "local variable" feature by anders, 2003
04529 /* TODO
04530       add config options (how many lines to read, on/off)
04531       add interface for plugins/apps to set/get variables
04532       add view stuff
04533 */
04534 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
04535 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
04536 
04537 void KateDocument::readVariables(bool onlyViewAndRenderer)
04538 {
04539   if (!onlyViewAndRenderer)
04540     m_config->configStart();
04541 
04542   // views!
04543   KateView *v;
04544   for (v = m_views.first(); v != 0L; v= m_views.next() )
04545   {
04546     v->config()->configStart();
04547     v->renderer()->config()->configStart();
04548   }
04549   // read a number of lines in the top/bottom of the document
04550   for (uint i=0; i < QMIN( 9, numLines() ); ++i )
04551   {
04552     readVariableLine( textLine( i ), onlyViewAndRenderer );
04553   }
04554   if ( numLines() > 10 )
04555   {
04556     for ( uint i = QMAX(10, numLines() - 10); i < numLines(); ++i )
04557     {
04558       readVariableLine( textLine( i ), onlyViewAndRenderer );
04559     }
04560   }
04561 
04562   if (!onlyViewAndRenderer)
04563     m_config->configEnd();
04564 
04565   for (v = m_views.first(); v != 0L; v= m_views.next() )
04566   {
04567     v->config()->configEnd();
04568     v->renderer()->config()->configEnd();
04569   }
04570 }
04571 
04572 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
04573 {
04574   if ( kvLine.search( t ) > -1 )
04575   {
04576     QStringList vvl; // view variable names
04577     vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
04578         << "line-numbers" << "icon-border" << "folding-markers"
04579         << "bookmark-sorting" << "auto-center-lines"
04580         << "icon-bar-color"
04581         // renderer
04582         << "background-color" << "selection-color"
04583         << "current-line-color" << "bracket-highlight-color"
04584         << "word-wrap-marker-color"
04585         << "font" << "font-size" << "scheme";
04586     int p( 0 );
04587     QString s = kvLine.cap(1);
04588     QString  var, val;
04589     while ( (p = kvVar.search( s, p )) > -1 )
04590     {
04591       p += kvVar.matchedLength();
04592       var = kvVar.cap( 1 );
04593       val = kvVar.cap( 2 ).stripWhiteSpace();
04594       bool state; // store booleans here
04595       int n; // store ints here
04596 
04597       // only apply view & renderer config stuff
04598       if (onlyViewAndRenderer)
04599       {
04600         if ( vvl.contains( var ) ) // FIXME define above
04601           setViewVariable( var, val );
04602       }
04603       else
04604       {
04605         // BOOL  SETTINGS
04606         if ( var == "word-wrap" && checkBoolValue( val, &state ) )
04607           setWordWrap( state ); // ??? FIXME CHECK
04608         else if ( var == "block-selection"  && checkBoolValue( val, &state ) )
04609           setBlockSelectionMode( state );
04610         // KateConfig::configFlags
04611         // FIXME should this be optimized to only a few calls? how?
04612         else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
04613           m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
04614         else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
04615           m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
04616         else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
04617           m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
04618         else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
04619           m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
04620         else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
04621           m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
04622         else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
04623           m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
04624         else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) )
04625           m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state );
04626         else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
04627           m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
04628         else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
04629           m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
04630         else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
04631           m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
04632         else if ( var == "space-indent" && checkBoolValue( val, &state ) )
04633           m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state );
04634         else if ( var == "smart-home" && checkBoolValue( val, &state ) )
04635           m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
04636         else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
04637           m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
04638         else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) )
04639           m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state);
04640         else if ( var == "mixed-indent" && checkBoolValue( val, &state ) )
04641           m_config->setConfigFlags( KateDocumentConfig::cfMixedIndent, state );
04642 
04643         // INTEGER SETTINGS
04644         else if ( var == "tab-width" && checkIntValue( val, &n ) )
04645           m_config->setTabWidth( n );
04646         else if ( var == "indent-width"  && checkIntValue( val, &n ) )
04647           m_config->setIndentationWidth( n );
04648         else if ( var == "indent-mode" )
04649         {
04650           if ( checkIntValue( val, &n ) )
04651             m_config->setIndentationMode( n );
04652           else
04653             m_config->setIndentationMode( KateAutoIndent::modeNumber( val) );
04654         }
04655         else if ( var == "word-wrap-column" && n > 0  && checkIntValue( val, &n ) ) // uint, but hard word wrap at 0 will be no fun ;)
04656           m_config->setWordWrapAt( n );
04657         else if ( var == "undo-steps"  && n >= 0  && checkIntValue( val, &n ) )
04658           setUndoSteps( n );
04659 
04660         // STRING SETTINGS
04661         else if ( var == "eol" || var == "end-of-line" )
04662         {
04663           QStringList l;
04664           l << "unix" << "dos" << "mac";
04665           if ( (n = l.findIndex( val.lower() )) != -1 )
04666             m_config->setEol( n );
04667         }
04668         else if ( var == "encoding" )
04669           m_config->setEncoding( val );
04670         else if ( var == "syntax" || var == "hl" )
04671         {
04672           for ( uint i=0; i < hlModeCount(); i++ )
04673           {
04674             if ( hlModeName( i ).lower() == val.lower() )
04675             {
04676               setHlMode( i );
04677               break;
04678             }
04679           }
04680         }
04681 
04682         // VIEW SETTINGS
04683         else if ( vvl.contains( var ) )
04684           setViewVariable( var, val );
04685         else
04686         {
04687           m_storedVariables.insert( var, val );
04688           emit variableChanged( var, val );
04689         }
04690       }
04691     }
04692   }
04693 }
04694 
04695 void KateDocument::setViewVariable( QString var, QString val )
04696 {
04697   KateView *v;
04698   bool state;
04699   int n;
04700   QColor c;
04701   for (v = m_views.first(); v != 0L; v= m_views.next() )
04702   {
04703     if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
04704       v->config()->setDynWordWrap( state );
04705     else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
04706       v->config()->setPersistentSelection( state );
04707     //else if ( var = "dynamic-word-wrap-indicators" )
04708     else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
04709       v->config()->setLineNumbers( state );
04710     else if (var == "icon-border" && checkBoolValue( val, &state ) )
04711       v->config()->setIconBar( state );
04712     else if (var == "folding-markers" && checkBoolValue( val, &state ) )
04713       v->config()->setFoldingBar( state );
04714     else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
04715       v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
04716     else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
04717       v->renderer()->config()->setIconBarColor( c );
04718     // RENDERER
04719     else if ( var == "background-color" && checkColorValue( val, c ) )
04720       v->renderer()->config()->setBackgroundColor( c );
04721     else if ( var == "selection-color" && checkColorValue( val, c ) )
04722       v->renderer()->config()->setSelectionColor( c );
04723     else if ( var == "current-line-color" && checkColorValue( val, c ) )
04724       v->renderer()->config()->setHighlightedLineColor( c );
04725     else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
04726       v->renderer()->config()->setHighlightedBracketColor( c );
04727     else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
04728       v->renderer()->config()->setWordWrapMarkerColor( c );
04729     else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
04730     {
04731       QFont _f( *v->renderer()->config()->font(  ) );
04732 
04733       if ( var == "font" )
04734       {
04735         _f.setFamily( val );
04736         _f.setFixedPitch( QFont( val ).fixedPitch() );
04737       }
04738       else
04739         _f.setPointSize( n );
04740 
04741       v->renderer()->config()->setFont( _f );
04742     }
04743     else if ( var == "scheme" )
04744     {
04745       v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) );
04746     }
04747   }
04748 }
04749 
04750 bool KateDocument::checkBoolValue( QString val, bool *result )
04751 {
04752   val = val.stripWhiteSpace().lower();
04753   QStringList l;
04754   l << "1" << "on" << "true";
04755   if ( l.contains( val ) )
04756   {
04757     *result = true;
04758     return true;
04759   }
04760   l.clear();
04761   l << "0" << "off" << "false";
04762   if ( l.contains( val ) )
04763   {
04764     *result = false;
04765     return true;
04766   }
04767   return false;
04768 }
04769 
04770 bool KateDocument::checkIntValue( QString val, int *result )
04771 {
04772   bool ret( false );
04773   *result = val.toInt( &ret );
04774   return ret;
04775 }
04776 
04777 bool KateDocument::checkColorValue( QString val, QColor &c )
04778 {
04779   c.setNamedColor( val );
04780   return c.isValid();
04781 }
04782 
04783 // KTextEditor::variable
04784 QString KateDocument::variable( const QString &name ) const
04785 {
04786   if ( m_storedVariables.contains( name ) )
04787     return m_storedVariables[ name ];
04788 
04789   return "";
04790 }
04791 
04792 //END
04793 
04794 void KateDocument::slotModOnHdDirty (const QString &path)
04795 {
04796   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1))
04797   {
04798     // compare md5 with the one we have (if we have one)
04799     if ( ! m_digest.isEmpty() )
04800     {
04801       QCString tmp;
04802       if ( createDigest( tmp ) && tmp == m_digest )
04803         return;
04804     }
04805 
04806     m_modOnHd = true;
04807     m_modOnHdReason = 1;
04808 
04809     // reenable dialog if not running atm
04810     if (m_isasking == -1)
04811       m_isasking = false;
04812 
04813     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04814   }
04815 }
04816 
04817 void KateDocument::slotModOnHdCreated (const QString &path)
04818 {
04819   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2))
04820   {
04821     m_modOnHd = true;
04822     m_modOnHdReason = 2;
04823 
04824     // reenable dialog if not running atm
04825     if (m_isasking == -1)
04826       m_isasking = false;
04827 
04828     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04829   }
04830 }
04831 
04832 void KateDocument::slotModOnHdDeleted (const QString &path)
04833 {
04834   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3))
04835   {
04836     m_modOnHd = true;
04837     m_modOnHdReason = 3;
04838 
04839     // reenable dialog if not running atm
04840     if (m_isasking == -1)
04841       m_isasking = false;
04842 
04843     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04844   }
04845 }
04846 
04847 bool KateDocument::createDigest( QCString &result )
04848 {
04849   bool ret = false;
04850   result = "";
04851   if ( url().isLocalFile() )
04852   {
04853     QFile f ( url().path() );
04854     if ( f.open( IO_ReadOnly) )
04855     {
04856       KMD5 md5;
04857       ret = md5.update( f );
04858       md5.hexDigest( result );
04859       f.close();
04860       ret = true;
04861     }
04862   }
04863   return ret;
04864 }
04865 
04866 QString KateDocument::reasonedMOHString() const
04867 {
04868   switch( m_modOnHdReason )
04869   {
04870     case 1:
04871       return i18n("The file '%1' was modified by another program.").arg( url().prettyURL() );
04872       break;
04873     case 2:
04874       return i18n("The file '%1' was created by another program.").arg( url().prettyURL() );
04875       break;
04876     case 3:
04877       return i18n("The file '%1' was deleted by another program.").arg( url().prettyURL() );
04878       break;
04879     default:
04880       return QString();
04881   }
04882 }
04883 
04884 void KateDocument::removeTrailingSpace( uint line )
04885 {
04886   // remove trailing spaces from left line if required
04887   if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn )
04888   {
04889     KateTextLine::Ptr ln = kateTextLine( line );
04890 
04891     if ( ! ln ) return;
04892 
04893     if ( line == activeView()->cursorLine()
04894          && activeView()->cursorColumnReal() >= (uint)QMAX(0,ln->lastChar()) )
04895       return;
04896 
04897     if ( ln->length() )
04898     {
04899       uint p = ln->lastChar() + 1;
04900       uint l = ln->length() - p;
04901       if ( l )
04902         editRemoveText( line, p, l);
04903     }
04904   }
04905 }
04906 
04907 void KateDocument::updateFileType (int newType, bool user)
04908 {
04909   if (user || !m_fileTypeSetByUser)
04910   {
04911     const KateFileType *t = 0;
04912     if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType)))
04913     {
04914       m_fileType = newType;
04915 
04916       if (t)
04917       {
04918         m_config->configStart();
04919         // views!
04920         KateView *v;
04921         for (v = m_views.first(); v != 0L; v= m_views.next() )
04922         {
04923           v->config()->configStart();
04924           v->renderer()->config()->configStart();
04925         }
04926 
04927         readVariableLine( t->varLine );
04928 
04929         m_config->configEnd();
04930         for (v = m_views.first(); v != 0L; v= m_views.next() )
04931         {
04932           v->config()->configEnd();
04933           v->renderer()->config()->configEnd();
04934         }
04935       }
04936     }
04937   }
04938 }
04939 
04940 uint KateDocument::documentNumber () const
04941 {
04942   return KTextEditor::Document::documentNumber ();
04943 }
04944 
04945 
04946 
04947 
04948 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
04949       *handled=true;
04950       *abortClosing=true;
04951       if (m_url.isEmpty())
04952       {
04953         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
04954                 QString::null,QString::null,0,i18n("Save File"));
04955 
04956         if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) {
04957                 *abortClosing=true;
04958                 return;
04959         }
04960         setEncoding( res.encoding );
04961           saveAs( res.URLs.first() );
04962         *abortClosing=false;
04963       }
04964       else
04965       {
04966           save();
04967           *abortClosing=false;
04968       }
04969 
04970 }
04971 
04972 bool KateDocument::checkOverwrite( KURL u )
04973 {
04974   if( !u.isLocalFile() )
04975     return true;
04976 
04977   QFileInfo info( u.path() );
04978   if( !info.exists() )
04979     return true;
04980 
04981   return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0,
04982     i18n( "A file named \"%1\" already exists. "
04983           "Are you sure you want to overwrite it?" ).arg( info.fileName() ),
04984     i18n( "Overwrite File?" ),
04985     i18n( "&Overwrite" ) );
04986 }
04987 
04988 void KateDocument::setDefaultEncoding (const QString &encoding)
04989 {
04990   s_defaultEncoding = encoding;
04991 }
04992 
04993 //BEGIN KTextEditor::TemplateInterface
04994 bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
04995       return (new KateTemplateHandler(this,line,column,templateString,initialValues))->initOk();
04996 }
04997 
04998 void KateDocument::testTemplateCode() {
04999   int col=activeView()->cursorColumn();
05000   int line=activeView()->cursorLine();
05001   insertTemplateText(line,col,"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05002 }
05003 
05004 bool KateDocument::invokeTabInterceptor(KKey key) {
05005   if (m_tabInterceptor) return (*m_tabInterceptor)(key);
05006   return false;
05007 }
05008 
05009 bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05010   if (m_tabInterceptor) return false;
05011   m_tabInterceptor=interceptor;
05012   return true;
05013 }
05014 
05015 bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05016   if (m_tabInterceptor!=interceptor) return false;
05017   m_tabInterceptor=0;
05018   return true;
05019 }
05020 //END KTextEditor::TemplateInterface
05021 
05022 //BEGIN DEPRECATED STUFF
05023  bool KateDocument::setSelection ( uint startLine, uint startCol, uint endLine, uint endCol )
05024 { if (m_activeView) return m_activeView->setSelection (startLine, startCol, endLine, endCol); return false; }
05025 
05026  bool KateDocument::clearSelection ()
05027  { if (m_activeView) return m_activeView->clearSelection(); return false; }
05028 
05029  bool KateDocument::hasSelection () const
05030  { if (m_activeView) return m_activeView->hasSelection (); return false; }
05031 
05032  QString KateDocument::selection () const
05033  { if (m_activeView) return m_activeView->selection (); return QString(""); }
05034 
05035  bool KateDocument::removeSelectedText ()
05036  { if (m_activeView) return m_activeView->removeSelectedText (); return false; }
05037 
05038  bool KateDocument::selectAll()
05039  { if (m_activeView) return m_activeView->selectAll (); return false; }
05040 
05041  int KateDocument::selStartLine()
05042  { if (m_activeView) return m_activeView->selStartLine (); return 0; }
05043 
05044  int KateDocument::selStartCol()
05045  { if (m_activeView) return m_activeView->selStartCol (); return 0; }
05046 
05047  int KateDocument::selEndLine()
05048  { if (m_activeView) return m_activeView->selEndLine (); return 0; }
05049 
05050  int KateDocument::selEndCol()
05051  { if (m_activeView) return m_activeView->selEndCol (); return 0; }
05052 
05053  bool KateDocument::blockSelectionMode ()
05054     { if (m_activeView) return m_activeView->blockSelectionMode (); return false; }
05055 
05056 bool KateDocument::setBlockSelectionMode (bool on)
05057     { if (m_activeView) return m_activeView->setBlockSelectionMode (on); return false; }
05058 
05059 bool KateDocument::toggleBlockSelectionMode ()
05060     { if (m_activeView) return m_activeView->toggleBlockSelectionMode (); return false; }
05061 //END DEPRECATED
05062 
05063 //END DEPRECATED STUFF
05064 
05065 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Home | KDE Accessibility Home | Description of Access Keys