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->updateView(true);
02737   }
02738 
02739   // uh, filename changed
02740   emit fileNameChanged ();
02741 
02742   // update doc name
02743   setDocName (QString::null);
02744 
02745   // success
02746   return true;
02747 }
02748 
02749 void KateDocument::setReadWrite( bool rw )
02750 {
02751   if (isReadWrite() != rw)
02752   {
02753     KParts::ReadWritePart::setReadWrite (rw);
02754 
02755     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02756     {
02757       view->slotUpdate();
02758       view->slotReadWriteChanged ();
02759     }
02760   }
02761 }
02762 
02763 void KateDocument::setModified(bool m) {
02764 
02765   if (isModified() != m) {
02766     KParts::ReadWritePart::setModified (m);
02767 
02768     for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
02769     {
02770       view->slotUpdate();
02771     }
02772 
02773     emit modifiedChanged ();
02774     emit modStateChanged ((Kate::Document *)this);
02775   }
02776   if ( m == false && ! undoItems.isEmpty() )
02777   {
02778     lastUndoGroupWhenSaved = undoItems.last();
02779   }
02780 
02781   if ( m == false ) docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
02782 }
02783 //END
02784 
02785 //BEGIN Kate specific stuff ;)
02786 
02787 void KateDocument::makeAttribs(bool needInvalidate)
02788 {
02789   for (uint z = 0; z < m_views.count(); z++)
02790     m_views.at(z)->renderer()->updateAttributes ();
02791 
02792   if (needInvalidate)
02793     m_buffer->invalidateHighlighting();
02794 
02795   tagAll ();
02796 }
02797 
02798 // the attributes of a hl have changed, update
02799 void KateDocument::internalHlChanged()
02800 {
02801   makeAttribs();
02802 }
02803 
02804 void KateDocument::addView(KTextEditor::View *view) {
02805   if (!view)
02806     return;
02807 
02808   m_views.append( (KateView *) view  );
02809   m_textEditViews.append( view );
02810 
02811   // apply the view & renderer vars from the file type
02812   const KateFileType *t = 0;
02813   if ((m_fileType > -1) && (t = KateFactory::self()->fileTypeManager()->fileType(m_fileType)))
02814     readVariableLine (t->varLine, true);
02815 
02816   // apply the view & renderer vars from the file
02817   readVariables (true);
02818 
02819   m_activeView = (KateView *) view;
02820 }
02821 
02822 void KateDocument::removeView(KTextEditor::View *view) {
02823   if (!view)
02824     return;
02825 
02826   if (m_activeView == view)
02827     m_activeView = 0L;
02828 
02829   m_views.removeRef( (KateView *) view );
02830   m_textEditViews.removeRef( view  );
02831 }
02832 
02833 void KateDocument::addSuperCursor(KateSuperCursor *cursor, bool privateC) {
02834   if (!cursor)
02835     return;
02836 
02837   m_superCursors.append( cursor );
02838 
02839   if (!privateC)
02840     myCursors.append( cursor );
02841 }
02842 
02843 void KateDocument::removeSuperCursor(KateSuperCursor *cursor, bool privateC) {
02844   if (!cursor)
02845     return;
02846 
02847   if (!privateC)
02848     myCursors.removeRef( cursor  );
02849 
02850   m_superCursors.removeRef( cursor  );
02851 }
02852 
02853 bool KateDocument::ownedView(KateView *view) {
02854   // do we own the given view?
02855   return (m_views.containsRef(view) > 0);
02856 }
02857 
02858 bool KateDocument::isLastView(int numViews) {
02859   return ((int) m_views.count() == numViews);
02860 }
02861 
02862 uint KateDocument::currentColumn( const KateTextCursor& cursor )
02863 {
02864   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
02865 
02866   if (textLine)
02867     return textLine->cursorX(cursor.col(), config()->tabWidth());
02868   else
02869     return 0;
02870 }
02871 
02872 bool KateDocument::typeChars ( KateView *view, const QString &chars )
02873 {
02874   KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorLine ());
02875 
02876   if (!textLine)
02877     return false;
02878 
02879   bool bracketInserted = false;
02880   QString buf;
02881   QChar c;
02882 
02883   for( uint z = 0; z < chars.length(); z++ )
02884   {
02885     QChar ch = c = chars[z];
02886     if (ch.isPrint() || ch == '\t')
02887     {
02888       buf.append (ch);
02889 
02890       if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets))
02891       {
02892         QChar end_ch;
02893         bool complete = true;
02894         QChar prevChar = textLine->getChar(view->cursorColumn()-1);
02895         QChar nextChar = textLine->getChar(view->cursorColumn());
02896         switch(ch) {
02897           case '(': end_ch = ')'; break;
02898           case '[': end_ch = ']'; break;
02899           case '{': end_ch = '}'; break;
02900           case '\'':end_ch = '\'';break;
02901           case '"': end_ch = '"'; break;
02902           default: complete = false;
02903         }
02904         if (complete)
02905         {
02906           if (view->hasSelection())
02907           { // there is a selection, enclose the selection
02908             buf.append (view->selection());
02909             buf.append (end_ch);
02910             bracketInserted = true;
02911           }
02912           else
02913           { // no selection, check whether we should better refuse to complete
02914             if ( ( (ch == '\'' || ch == '"') &&
02915                    (prevChar.isLetterOrNumber() || prevChar == ch) )
02916               || nextChar.isLetterOrNumber()
02917               || (nextChar == end_ch && prevChar != ch) )
02918             {
02919               kdDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
02920             }
02921             else
02922             {
02923               buf.append (end_ch);
02924               bracketInserted = true;
02925             }
02926           }
02927         }
02928       }
02929     }
02930   }
02931 
02932   if (buf.isEmpty())
02933     return false;
02934 
02935   editStart ();
02936 
02937   if (!view->config()->persistentSelection() && view->hasSelection() )
02938     view->removeSelectedText();
02939 
02940   int oldLine = view->cursorLine ();
02941   int oldCol = view->cursorColumnReal ();
02942 
02943 
02944   if (config()->configFlags()  & KateDocument::cfOvr)
02945     removeText (view->cursorLine(), view->cursorColumnReal(), view->cursorLine(), QMIN( view->cursorColumnReal()+buf.length(), textLine->length() ) );
02946 
02947   insertText (view->cursorLine(), view->cursorColumnReal(), buf);
02948   m_indenter->processChar(c);
02949 
02950   editEnd ();
02951 
02952   if (bracketInserted)
02953     view->setCursorPositionInternal (view->cursorLine(), view->cursorColumnReal()-1);
02954 
02955   emit charactersInteractivelyInserted (oldLine, oldCol, chars);
02956 
02957   return true;
02958 }
02959 
02960 void KateDocument::newLine( KateTextCursor& c, KateViewInternal *v )
02961 {
02962   editStart();
02963 
02964   if( !v->view()->config()->persistentSelection() && v->view()->hasSelection() )
02965     v->view()->removeSelectedText();
02966 
02967   // temporary hack to get the cursor pos right !!!!!!!!!
02968   c = v->getCursor ();
02969 
02970   if (c.line() > (int)lastLine())
02971    c.setLine(lastLine());
02972 
02973   if ( c.line() < 0 )
02974     c.setLine( 0 );
02975 
02976   uint ln = c.line();
02977 
02978   KateTextLine::Ptr textLine = kateTextLine(c.line());
02979 
02980   if (c.col() > (int)textLine->length())
02981     c.setCol(textLine->length());
02982 
02983   if (m_indenter->canProcessNewLine ())
02984   {
02985     int pos = textLine->firstChar();
02986 
02987     // length should do the job better
02988     if (pos < 0)
02989       pos = textLine->length();
02990 
02991     if (c.col() < pos)
02992       c.setCol(pos); // place cursor on first char if before
02993 
02994     editWrapLine (c.line(), c.col());
02995 
02996     KateDocCursor cursor (c.line() + 1, pos, this);
02997     m_indenter->processNewline(cursor, true);
02998 
02999     c.setPos(cursor);
03000   }
03001   else
03002   {
03003     editWrapLine (c.line(), c.col());
03004     c.setPos(c.line() + 1, 0);
03005   }
03006 
03007   removeTrailingSpace( ln );
03008 
03009   editEnd();
03010 }
03011 
03012 void KateDocument::transpose( const KateTextCursor& cursor)
03013 {
03014   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03015 
03016   if (!textLine || (textLine->length() < 2))
03017     return;
03018 
03019   uint col = cursor.col();
03020 
03021   if (col > 0)
03022     col--;
03023 
03024   if ((textLine->length() - col) < 2)
03025     return;
03026 
03027   uint line = cursor.line();
03028   QString s;
03029 
03030   //clever swap code if first character on the line swap right&left
03031   //otherwise left & right
03032   s.append (textLine->getChar(col+1));
03033   s.append (textLine->getChar(col));
03034   //do the swap
03035 
03036   // do it right, never ever manipulate a textline
03037   editStart ();
03038   editRemoveText (line, col, 2);
03039   editInsertText (line, col, s);
03040   editEnd ();
03041 }
03042 
03043 void KateDocument::backspace( KateView *view, const KateTextCursor& c )
03044 {
03045   if ( !view->config()->persistentSelection() && view->hasSelection() ) {
03046     view->removeSelectedText();
03047     return;
03048   }
03049 
03050   uint col = QMAX( c.col(), 0 );
03051   uint line = QMAX( c.line(), 0 );
03052 
03053   if ((col == 0) && (line == 0))
03054     return;
03055 
03056   int complement = 0;
03057   if (col > 0)
03058   {
03059     if (config()->configFlags() & KateDocument::cfAutoBrackets)
03060     {
03061       // if inside empty (), {}, [], '', "" delete both
03062       KateTextLine::Ptr tl = m_buffer->plainLine(line);
03063       if(!tl) return;
03064       QChar prevChar = tl->getChar(col-1);
03065       QChar nextChar = tl->getChar(col);
03066 
03067       if ( (prevChar == '"' && nextChar == '"') ||
03068            (prevChar == '\'' && nextChar == '\'') ||
03069            (prevChar == '(' && nextChar == ')') ||
03070            (prevChar == '[' && nextChar == ']') ||
03071            (prevChar == '{' && nextChar == '}') )
03072       {
03073         complement = 1;
03074       }
03075     }
03076     if (!(config()->configFlags() & KateDocument::cfBackspaceIndents))
03077     {
03078       // ordinary backspace
03079       //c.cursor.col--;
03080       removeText(line, col-1, line, col+complement);
03081     }
03082     else
03083     {
03084       // backspace indents: erase to next indent position
03085       KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03086 
03087       // don't forget this check!!!! really!!!!
03088       if (!textLine)
03089         return;
03090 
03091       int colX = textLine->cursorX(col, config()->tabWidth());
03092       int pos = textLine->firstChar();
03093       if (pos > 0)
03094         pos = textLine->cursorX(pos, config()->tabWidth());
03095 
03096       if (pos < 0 || pos >= (int)colX)
03097       {
03098         // only spaces on left side of cursor
03099         // search a line with less spaces
03100         int y = line;
03101         while (--y >= 0)
03102         {
03103           // this is save, y <= line, and line was already success
03104           textLine = m_buffer->plainLine(y);
03105 
03106           pos = textLine->firstChar();
03107 
03108           if (pos >= 0)
03109           {
03110             pos = textLine->cursorX(pos, config()->tabWidth());
03111             if (pos < (int)colX)
03112             {
03113               replaceWithOptimizedSpace(line, col, pos, config()->configFlags());
03114               break;
03115             }
03116           }
03117         }
03118         if (y < 0) {
03119           // FIXME: what shoud we do in this case?
03120           removeText(line, 0, line, col+complement);
03121         }
03122       }
03123       else
03124         removeText(line, col-1, line, col+complement);
03125     }
03126   }
03127   else
03128   {
03129     // col == 0: wrap to previous line
03130     if (line >= 1)
03131     {
03132       KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
03133 
03134       // don't forget this check!!!! really!!!!
03135       if (!textLine)
03136         return;
03137 
03138       if (config()->wordWrap() && textLine->endingWith(QString::fromLatin1(" ")))
03139       {
03140         // gg: in hard wordwrap mode, backspace must also eat the trailing space
03141         removeText (line-1, textLine->length()-1, line, 0);
03142       }
03143       else
03144         removeText (line-1, textLine->length(), line, 0);
03145     }
03146   }
03147 
03148   emit backspacePressed();
03149 }
03150 
03151 void KateDocument::del( KateView *view, const KateTextCursor& c )
03152 {
03153   if ( !view->config()->persistentSelection() && view->hasSelection() ) {
03154     view->removeSelectedText();
03155     return;
03156   }
03157 
03158   if( c.col() < (int) m_buffer->plainLine(c.line())->length())
03159   {
03160     removeText(c.line(), c.col(), c.line(), c.col()+1);
03161   }
03162   else if ( (uint)c.line() < lastLine() )
03163   {
03164     removeText(c.line(), c.col(), c.line()+1, 0);
03165   }
03166 }
03167 
03168 void KateDocument::paste ( KateView* view )
03169 {
03170   QString s = QApplication::clipboard()->text();
03171 
03172   if (s.isEmpty())
03173     return;
03174 
03175   uint lines = s.contains (QChar ('\n'));
03176 
03177   m_undoDontMerge = true;
03178 
03179   editStart ();
03180 
03181   if (!view->config()->persistentSelection() && view->hasSelection() )
03182     view->removeSelectedText();
03183 
03184   uint line = view->cursorLine ();
03185   uint column = view->cursorColumnReal ();
03186 
03187   insertText ( line, column, s, view->blockSelectionMode() );
03188 
03189   editEnd();
03190 
03191   // move cursor right for block select, as the user is moved right internal
03192   // even in that case, but user expects other behavior in block selection
03193   // mode !
03194   if (view->blockSelectionMode())
03195     view->setCursorPositionInternal (line+lines, column);
03196 
03197   if (m_indenter->canProcessLine())
03198   {
03199     editStart();
03200 
03201     KateDocCursor begin(line, 0, this);
03202     KateDocCursor end(line + lines, 0, this);
03203 
03204     m_indenter->processSection (begin, end);
03205 
03206     editEnd();
03207   }
03208 
03209   if (!view->blockSelectionMode()) emit charactersSemiInteractivelyInserted (line, column, s);
03210   m_undoDontMerge = true;
03211 }
03212 
03213 void KateDocument::insertIndentChars ( KateView *view )
03214 {
03215   editStart ();
03216 
03217   QString s;
03218   if (config()->configFlags() & KateDocument::cfSpaceIndent)
03219   {
03220     int width = config()->indentationWidth();
03221     s.fill (' ', width - (view->cursorColumnReal() % width));
03222   }
03223   else
03224     s.append ('\t');
03225 
03226   insertText (view->cursorLine(), view->cursorColumnReal(), s);
03227 
03228   editEnd ();
03229 }
03230 
03231 void KateDocument::indent ( KateView *v, uint line, int change)
03232 {
03233   editStart ();
03234 
03235   if (!hasSelection())
03236   {
03237     // single line
03238     optimizeLeadingSpace(line, config()->configFlags(), change);
03239   }
03240   else
03241   {
03242     int sl = v->selStartLine();
03243     int el = v->selEndLine();
03244     int ec = v->selEndCol();
03245 
03246     if ((ec == 0) && ((el-1) >= 0))
03247     {
03248       el--; /* */
03249     }
03250 
03251     if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) {
03252       // unindent so that the existing indent profile doesn't get screwed
03253       // if any line we may unindent is already full left, don't do anything
03254       int adjustedChange = -change;
03255 
03256       for (line = sl; (int) line <= el && adjustedChange > 0; line++) {
03257         KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03258         int firstChar = textLine->firstChar();
03259         if (firstChar >= 0 && (v->lineSelected(line) || v->lineHasSelected(line))) {
03260           int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth();
03261           if (maxUnindent < adjustedChange)
03262             adjustedChange = maxUnindent;
03263         }
03264       }
03265 
03266       change = -adjustedChange;
03267     }
03268 
03269     for (line = sl; (int) line <= el; line++) {
03270       if (v->lineSelected(line) || v->lineHasSelected(line)) {
03271         optimizeLeadingSpace(line, config()->configFlags(), change);
03272       }
03273     }
03274   }
03275 
03276   editEnd ();
03277 }
03278 
03279 void KateDocument::align(KateView *view, uint line)
03280 {
03281   if (m_indenter->canProcessLine())
03282   {
03283     editStart ();
03284 
03285     if (!view->hasSelection())
03286     {
03287       KateDocCursor curLine(line, 0, this);
03288       m_indenter->processLine (curLine);
03289       editEnd ();
03290       activeView()->setCursorPosition (line, curLine.col());
03291     }
03292     else
03293     {
03294       m_indenter->processSection (view->selStart(), view->selEnd());
03295       editEnd ();
03296     }
03297   }
03298 }
03299 
03300 /*
03301   Optimize the leading whitespace for a single line.
03302   If change is > 0, it adds indentation units (indentationChars)
03303   if change is == 0, it only optimizes
03304   If change is < 0, it removes indentation units
03305   This will be used to indent, unindent, and optimal-fill a line.
03306   If excess space is removed depends on the flag cfKeepExtraSpaces
03307   which has to be set by the user
03308 */
03309 void KateDocument::optimizeLeadingSpace(uint line, int flags, int change)
03310 {
03311   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03312 
03313   int first_char = textline->firstChar();
03314 
03315   int w = 0;
03316   if (flags & KateDocument::cfSpaceIndent)
03317     w = config()->indentationWidth();
03318   else
03319     w = config()->tabWidth();
03320 
03321   if (first_char < 0)
03322     first_char = textline->length();
03323 
03324   int space =  textline->cursorX(first_char, config()->tabWidth()) + change * w;
03325   if (space < 0)
03326     space = 0;
03327 
03328   if (!(flags & KateDocument::cfKeepExtraSpaces))
03329   {
03330     uint extra = space % w;
03331 
03332     space -= extra;
03333     if (extra && change < 0) {
03334       // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide)
03335       space += w;
03336     }
03337   }
03338 
03339   //kdDebug(13020)  << "replace With Op: " << line << " " << first_char << " " << space << endl;
03340   replaceWithOptimizedSpace(line, first_char, space, flags);
03341 }
03342 
03343 void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags)
03344 {
03345   uint length;
03346   QString new_space;
03347 
03348   if (flags & KateDocument::cfSpaceIndent && ! (flags & KateDocumentConfig::cfMixedIndent) ) {
03349     length = space;
03350     new_space.fill(' ', length);
03351   }
03352   else {
03353     length = space / config()->tabWidth();
03354     new_space.fill('\t', length);
03355 
03356     QString extra_space;
03357     extra_space.fill(' ', space % config()->tabWidth());
03358     length += space % config()->tabWidth();
03359     new_space += extra_space;
03360   }
03361 
03362   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03363   uint change_from;
03364   for (change_from = 0; change_from < upto_column && change_from < length; change_from++) {
03365     if (textline->getChar(change_from) != new_space[change_from])
03366       break;
03367   }
03368 
03369   editStart();
03370 
03371   if (change_from < upto_column)
03372     removeText(line, change_from, line, upto_column);
03373 
03374   if (change_from < length)
03375     insertText(line, change_from, new_space.right(length - change_from));
03376 
03377   editEnd();
03378 }
03379 
03380 /*
03381   Remove a given string at the begining
03382   of the current line.
03383 */
03384 bool KateDocument::removeStringFromBegining(int line, QString &str)
03385 {
03386   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03387 
03388   int index = 0;
03389   bool there = false;
03390 
03391   if (textline->startingWith(str))
03392     there = true;
03393   else
03394   {
03395     index = textline->firstChar ();
03396 
03397     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03398       there = true;
03399   }
03400 
03401   if (there)
03402   {
03403     // Remove some chars
03404     removeText (line, index, line, index+str.length());
03405   }
03406 
03407   return there;
03408 }
03409 
03410 /*
03411   Remove a given string at the end
03412   of the current line.
03413 */
03414 bool KateDocument::removeStringFromEnd(int line, QString &str)
03415 {
03416   KateTextLine::Ptr textline = m_buffer->plainLine(line);
03417 
03418   int index = 0;
03419   bool there = false;
03420 
03421   if(textline->endingWith(str))
03422   {
03423     index = textline->length() - str.length();
03424     there = true;
03425   }
03426   else
03427   {
03428     index = textline->lastChar ()-str.length()+1;
03429 
03430     if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
03431       there = true;
03432   }
03433 
03434   if (there)
03435   {
03436     // Remove some chars
03437     removeText (line, index, line, index+str.length());
03438   }
03439 
03440   return there;
03441 }
03442 
03443 /*
03444   Add to the current line a comment line mark at
03445   the begining.
03446 */
03447 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
03448 {
03449   if (highlight()->getCommentSingleLinePosition(attrib)==KateHighlighting::CSLPosColumn0)
03450   {
03451     QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03452     insertText (line, 0, commentLineMark);
03453   }
03454   else
03455   {
03456     QString commentLineMark=highlight()->getCommentSingleLineStart(attrib);
03457     KateTextLine::Ptr l = m_buffer->line(line);
03458     int pos=l->firstChar();
03459     if (pos >=0)
03460       insertText(line,pos,commentLineMark);
03461   }
03462 }
03463 
03464 /*
03465   Remove from the current line a comment line mark at
03466   the begining if there is one.
03467 */
03468 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
03469 {
03470   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
03471   QString longCommentMark = shortCommentMark + " ";
03472 
03473   editStart();
03474 
03475   // Try to remove the long comment mark first
03476   bool removed = (removeStringFromBegining(line, longCommentMark)
03477                   || removeStringFromBegining(line, shortCommentMark));
03478 
03479   editEnd();
03480 
03481   return removed;
03482 }
03483 
03484 /*
03485   Add to the current line a start comment mark at the
03486  begining and a stop comment mark at the end.
03487 */
03488 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
03489 {
03490   QString startCommentMark = highlight()->getCommentStart( attrib ) + " ";
03491   QString stopCommentMark = " " + highlight()->getCommentEnd( attrib );
03492 
03493   editStart();
03494 
03495   // Add the start comment mark
03496   insertText (line, 0, startCommentMark);
03497 
03498   // Go to the end of the line
03499   int col = m_buffer->plainLine(line)->length();
03500 
03501   // Add the stop comment mark
03502   insertText (line, col, stopCommentMark);
03503 
03504   editEnd();
03505 }
03506 
03507 /*
03508   Remove from the current line a start comment mark at
03509   the begining and a stop comment mark at the end.
03510 */
03511 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
03512 {
03513   QString shortStartCommentMark = highlight()->getCommentStart( attrib );
03514   QString longStartCommentMark = shortStartCommentMark + " ";
03515   QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
03516   QString longStopCommentMark = " " + shortStopCommentMark;
03517 
03518   editStart();
03519 
03520 #ifdef __GNUC__
03521 #warning "that's a bad idea, can lead to stray endings, FIXME"
03522 #endif
03523   // Try to remove the long start comment mark first
03524   bool removedStart = (removeStringFromBegining(line, longStartCommentMark)
03525                        || removeStringFromBegining(line, shortStartCommentMark));
03526 
03527   bool removedStop = false;
03528   if (removedStart)
03529   {
03530     // Try to remove the long stop comment mark first
03531     removedStop = (removeStringFromEnd(line, longStopCommentMark)
03532                       || removeStringFromEnd(line, shortStopCommentMark));
03533   }
03534 
03535   editEnd();
03536 
03537   return (removedStart || removedStop);
03538 }
03539 
03540 /*
03541   Add to the current selection a start comment
03542  mark at the begining and a stop comment mark
03543  at the end.
03544 */
03545 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
03546 {
03547   QString startComment = highlight()->getCommentStart( attrib );
03548   QString endComment = highlight()->getCommentEnd( attrib );
03549 
03550   int sl = view->selStartLine();
03551   int el = view->selEndLine();
03552   int sc = view->selStartCol();
03553   int ec = view->selEndCol();
03554 
03555   if ((ec == 0) && ((el-1) >= 0))
03556   {
03557     el--;
03558     ec = m_buffer->plainLine (el)->length();
03559   }
03560 
03561   editStart();
03562 
03563   insertText (el, ec, endComment);
03564   insertText (sl, sc, startComment);
03565 
03566   editEnd ();
03567 
03568   // Set the new selection
03569   ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 );
03570   view->setSelection(sl, sc, el, ec);
03571 }
03572 
03573 /*
03574   Add to the current selection a comment line
03575  mark at the begining of each line.
03576 */
03577 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
03578 {
03579   QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
03580 
03581   int sl = view->selStartLine();
03582   int el = view->selEndLine();
03583 
03584   if ((view->selEndCol() == 0) && ((el-1) >= 0))
03585   {
03586     el--;
03587   }
03588 
03589   editStart();
03590 
03591   // For each line of the selection
03592   for (int z = el; z >= sl; z--) {
03593     //insertText (z, 0, commentLineMark);
03594     addStartLineCommentToSingleLine(z, attrib );
03595   }
03596 
03597   editEnd ();
03598 
03599   // Set the new selection
03600 
03601   KateDocCursor end (view->selEnd());
03602   end.setCol(view->selEndCol() + ((el == view->selEndLine()) ? commentLineMark.length() : 0) );
03603 
03604   view->setSelection(view->selStartLine(), 0, end.line(), end.col());
03605 }
03606 
03607 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
03608 {
03609   for(; line < (int)m_buffer->count(); line++) {
03610     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03611 
03612     if (!textLine)
03613       break;
03614 
03615     col = textLine->nextNonSpaceChar(col);
03616     if(col != -1)
03617       return true; // Next non-space char found
03618     col = 0;
03619   }
03620   // No non-space char found
03621   line = -1;
03622   col = -1;
03623   return false;
03624 }
03625 
03626 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
03627 {
03628   while(true)
03629   {
03630     KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03631 
03632     if (!textLine)
03633       break;
03634 
03635     col = textLine->previousNonSpaceChar(col);
03636     if(col != -1) return true;
03637     if(line == 0) return false;
03638     --line;
03639     col = textLine->length();
03640 }
03641   // No non-space char found
03642   line = -1;
03643   col = -1;
03644   return false;
03645 }
03646 
03647 /*
03648   Remove from the selection a start comment mark at
03649   the begining and a stop comment mark at the end.
03650 */
03651 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
03652 {
03653   QString startComment = highlight()->getCommentStart( attrib );
03654   QString endComment = highlight()->getCommentEnd( attrib );
03655 
03656   int sl = kMax<int> (0, view->selStartLine());
03657   int el = kMin<int>  (view->selEndLine(), lastLine());
03658   int sc = view->selStartCol();
03659   int ec = view->selEndCol();
03660 
03661   // The selection ends on the char before selectEnd
03662   if (ec != 0) {
03663     ec--;
03664   } else {
03665     if (el > 0) {
03666       el--;
03667       ec = m_buffer->plainLine(el)->length() - 1;
03668     }
03669   }
03670 
03671   int startCommentLen = startComment.length();
03672   int endCommentLen = endComment.length();
03673 
03674   // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/
03675 
03676   bool remove = nextNonSpaceCharPos(sl, sc)
03677       && m_buffer->plainLine(sl)->stringAtPos(sc, startComment)
03678       && previousNonSpaceCharPos(el, ec)
03679       && ( (ec - endCommentLen + 1) >= 0 )
03680       && m_buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment);
03681 
03682   if (remove) {
03683     editStart();
03684 
03685     removeText (el, ec - endCommentLen + 1, el, ec + 1);
03686     removeText (sl, sc, sl, sc + startCommentLen);
03687 
03688     editEnd ();
03689 
03690     // Set the new selection
03691     ec -= endCommentLen + ( (el == sl) ? startCommentLen : 0 );
03692     view->setSelection(sl, sc, el, ec + 1);
03693   }
03694 
03695   return remove;
03696 }
03697 
03698 bool KateDocument::removeStartStopCommentFromRegion(const KateTextCursor &start,const KateTextCursor &end,int attrib)
03699 {
03700   QString startComment = highlight()->getCommentStart( attrib );
03701   QString endComment = highlight()->getCommentEnd( attrib );
03702   int startCommentLen = startComment.length();
03703   int endCommentLen = endComment.length();
03704 
03705     bool remove = m_buffer->plainLine(start.line())->stringAtPos(start.col(), startComment)
03706       && ( (end.col() - endCommentLen ) >= 0 )
03707       && m_buffer->plainLine(end.line())->stringAtPos(end.col() - endCommentLen , endComment);
03708       if (remove)  {
03709         editStart();
03710           removeText(end.line(),end.col()-endCommentLen,end.line(),end.col());
03711           removeText(start.line(),start.col(),start.line(),start.col()+startCommentLen);
03712         editEnd();
03713       }
03714       return remove;
03715 }
03716 
03717 /*
03718   Remove from the begining of each line of the
03719   selection a start comment line mark.
03720 */
03721 bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib )
03722 {
03723   QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
03724   QString longCommentMark = shortCommentMark + " ";
03725 
03726   int sl = view->selStartLine();
03727   int el = view->selEndLine();
03728 
03729   if ((view->selEndCol() == 0) && ((el-1) >= 0))
03730   {
03731     el--;
03732   }
03733 
03734   // Find out how many char will be removed from the last line
03735   int removeLength = 0;
03736   if (m_buffer->plainLine(el)->startingWith(longCommentMark))
03737     removeLength = longCommentMark.length();
03738   else if (m_buffer->plainLine(el)->startingWith(shortCommentMark))
03739     removeLength = shortCommentMark.length();
03740 
03741   bool removed = false;
03742 
03743   editStart();
03744 
03745   // For each line of the selection
03746   for (int z = el; z >= sl; z--)
03747   {
03748     // Try to remove the long comment mark first
03749     removed = (removeStringFromBegining(z, longCommentMark)
03750                  || removeStringFromBegining(z, shortCommentMark)
03751                  || removed);
03752   }
03753 
03754   editEnd();
03755 
03756   if (removed)
03757   {
03758     // Set the new selection
03759     KateDocCursor end (view->selEnd());
03760     end.setCol(view->selEndCol() - ((el == view->selEndLine()) ? removeLength : 0) );
03761 
03762     setSelection(view->selStartLine(), view->selStartCol(), end.line(), end.col());
03763   }
03764 
03765   return removed;
03766 }
03767 
03768 /*
03769   Comment or uncomment the selection or the current
03770   line if there is no selection.
03771 */
03772 void KateDocument::comment( KateView *v, uint line,uint column, int change)
03773 {
03774   // We need to check that we can sanely comment the selectino or region.
03775   // It is if the attribute of the first and last character of the range to
03776   // comment belongs to the same language definition.
03777   // for lines with no text, we need the attribute for the lines context.
03778   bool hassel = v->hasSelection();
03779   int startAttrib, endAttrib;
03780   if ( hassel )
03781   {
03782     KateTextLine::Ptr ln = kateTextLine( v->selStartLine() );
03783     int l = v->selStartLine(), c = v->selStartCol();
03784     startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03785 
03786     ln = kateTextLine( v->selEndLine() );
03787     l = v->selEndLine(), c = v->selEndCol();
03788     endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
03789   }
03790   else
03791   {
03792     KateTextLine::Ptr ln = kateTextLine( line );
03793     if ( ln->length() )
03794     {
03795       startAttrib = ln->attribute( ln->firstChar() );
03796       endAttrib = ln->attribute( ln->lastChar() );
03797     }
03798     else
03799     {
03800       int l = line, c = 0;
03801       if ( nextNonSpaceCharPos( l, c )  || previousNonSpaceCharPos( l, c ) )
03802         startAttrib = endAttrib = kateTextLine( l )->attribute( c );
03803       else
03804         startAttrib = endAttrib = 0;
03805     }
03806   }
03807 
03808   if ( ! highlight()->canComment( startAttrib, endAttrib ) )
03809   {
03810     kdDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!"<<endl;
03811     return;
03812   }
03813 
03814   bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
03815   bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
03816       && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
03817 
03818   bool removed = false;
03819 
03820   if (change > 0) // comment
03821   {
03822     if ( !hassel )
03823     {
03824       if ( hasStartLineCommentMark )
03825         addStartLineCommentToSingleLine( line, startAttrib );
03826       else if ( hasStartStopCommentMark )
03827         addStartStopCommentToSingleLine( line, startAttrib );
03828     }
03829     else
03830     {
03831       // anders: prefer single line comment to avoid nesting probs
03832       // If the selection starts after first char in the first line
03833       // or ends before the last char of the last line, we may use
03834       // multiline comment markers.
03835       // TODO We should try to detect nesting.
03836       //    - if selection ends at col 0, most likely she wanted that
03837       // line ignored
03838       if ( hasStartStopCommentMark &&
03839            ( !hasStartLineCommentMark || (
03840            ( v->selStartCol() > m_buffer->plainLine( v->selStartLine() )->firstChar() ) ||
03841            ( v->selEndCol() < ((int)m_buffer->plainLine( v->selEndLine() )->length()) )
03842          ) ) )
03843         addStartStopCommentToSelection( v, startAttrib );
03844       else if ( hasStartLineCommentMark )
03845         addStartLineCommentToSelection( v, startAttrib );
03846     }
03847   }
03848   else // uncomment
03849   {
03850     if ( !hassel )
03851     {
03852       removed = ( hasStartLineCommentMark
03853                   && removeStartLineCommentFromSingleLine( line, startAttrib ) )
03854         || ( hasStartStopCommentMark
03855              && removeStartStopCommentFromSingleLine( line, startAttrib ) );
03856       if ((!removed) && foldingTree()) {
03857         kdDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)"<<endl;
03858         int commentRegion=(highlight()->commentRegion(startAttrib));
03859         if (commentRegion){
03860            KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
03861            if (n) {
03862             KateTextCursor start,end;
03863              if ((n->nodeType()==commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
03864                 kdDebug(13020)<<"Enclosing region found:"<<start.col()<<"/"<<start.line()<<"-"<<end.col()<<"/"<<end.line()<<endl;
03865                 removeStartStopCommentFromRegion(start,end,startAttrib);
03866              } else {
03867                   kdDebug(13020)<<"Enclosing region found, but not valid"<<endl;
03868                   kdDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion<<endl;
03869              }
03870             //perhaps nested regions should be hadled here too...
03871           } else kdDebug(13020)<<"No enclosing region found"<<endl;
03872         } else kdDebug(13020)<<"No comment region specified for current hl"<<endl;
03873       }
03874     }
03875     else
03876     {
03877       // anders: this seems like it will work with above changes :)
03878       removed = ( hasStartLineCommentMark
03879           && removeStartLineCommentFromSelection( v, startAttrib ) )
03880         || ( hasStartStopCommentMark
03881           && removeStartStopCommentFromSelection( v, startAttrib ) );
03882     }
03883   }
03884 }
03885 
03886 void KateDocument::transform( KateView *v, const KateTextCursor &c,
03887                             KateDocument::TextTransform t )
03888 {
03889   editStart();
03890   uint cl( c.line() ), cc( c.col() );
03891   bool selectionRestored = false;
03892 
03893   if ( hasSelection() )
03894   {
03895     // cache the selection and cursor, so we can be sure to restore.
03896     KateTextCursor selstart = v->selStart();
03897     KateTextCursor selend = v->selEnd();
03898 
03899     int ln = v->selStartLine();
03900     while ( ln <= selend.line() )
03901     {
03902       uint start, end;
03903       start = (ln == selstart.line() || v->blockSelectionMode()) ?
03904           selstart.col() : 0;
03905       end = (ln == selend.line() || v->blockSelectionMode()) ?
03906           selend.col() : lineLength( ln );
03907       if ( start > end )
03908       {
03909         uint t = start;
03910         start = end;
03911         end = t;
03912       }
03913       QString s = text( ln, start, ln, end );
03914 
03915       if ( t == Uppercase )
03916         s = s.upper();
03917       else if ( t == Lowercase )
03918         s = s.lower();
03919       else // Capitalize
03920       {
03921         KateTextLine::Ptr l = m_buffer->plainLine( ln );
03922         uint p ( 0 );
03923         while( p < s.length() )
03924         {
03925           // If bol or the character before is not in a word, up this one:
03926           // 1. if both start and p is 0, upper char.
03927           // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
03928           // 3. if p-1 is not in a word, upper.
03929           if ( ( ! start && ! p ) ||
03930                    ( ( ln == selstart.line() || v->blockSelectionMode() ) &&
03931                    ! p && ! highlight()->isInWord( l->getChar( start - 1 )) ) ||
03932                    ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
03933              )
03934             s[p] = s.at(p).upper();
03935           p++;
03936         }
03937       }
03938 
03939       removeText( ln, start, ln, end );
03940       insertText( ln, start, s );
03941 
03942       ln++;
03943     }
03944 
03945     // restore selection
03946     v->setSelection( selstart, selend );
03947     selectionRestored = true;
03948 
03949   } else {  // no selection
03950     QString s;
03951     int n ( cc );
03952     switch ( t ) {
03953       case Uppercase:
03954       s = text( cl, cc, cl, cc + 1 ).upper();
03955       break;
03956       case Lowercase:
03957       s = text( cl, cc, cl, cc + 1 ).lower();
03958       break;
03959       case Capitalize:
03960       {
03961         KateTextLine::Ptr l = m_buffer->plainLine( cl );
03962         while ( n > 0 && highlight()->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) )
03963           n--;
03964         s = text( cl, n, cl, n + 1 ).upper();
03965       }
03966       break;
03967       default:
03968       break;
03969     }
03970     removeText( cl, n, cl, n+1 );
03971     insertText( cl, n, s );
03972   }
03973 
03974   if ( ! selectionRestored )
03975     v->setCursorPosition( cl, cc );
03976 
03977   editEnd();
03978 }
03979 
03980 void KateDocument::joinLines( uint first, uint last )
03981 {
03982 //   if ( first == last ) last += 1;
03983   editStart();
03984   int line( first );
03985   while ( first < last )
03986   {
03987     // Normalize the whitespace in the joined lines by making sure there's
03988     // always exactly one space between the joined lines
03989     // This cannot be done in editUnwrapLine, because we do NOT want this
03990     // behaviour when deleting from the start of a line, just when explicitly
03991     // calling the join command
03992     KateTextLine::Ptr l = m_buffer->line( line );
03993     KateTextLine::Ptr tl = m_buffer->line( line + 1 );
03994 
03995     if ( !l || !tl )
03996     {
03997       editEnd();
03998       return;
03999     }
04000 
04001     int pos = tl->firstChar();
04002     if ( pos >= 0 )
04003     {
04004       if (pos != 0)
04005         editRemoveText( line + 1, 0, pos );
04006       if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) )
04007         editInsertText( line + 1, 0, " " );
04008     }
04009     else
04010     {
04011       // Just remove the whitespace and let Kate handle the rest
04012       editRemoveText( line + 1, 0, tl->length() );
04013     }
04014 
04015     editUnWrapLine( line );
04016     first++;
04017   }
04018   editEnd();
04019 }
04020 
04021 QString KateDocument::getWord( const KateTextCursor& cursor ) {
04022   int start, end, len;
04023 
04024   KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04025   len = textLine->length();
04026   start = end = cursor.col();
04027   if (start > len)        // Probably because of non-wrapping cursor mode.
04028     return QString("");
04029 
04030   while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
04031   while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(end))) end++;
04032   len = end - start;
04033   return QString(&textLine->text()[start], len);
04034 }
04035 
04036 void KateDocument::tagLines(int start, int end)
04037 {
04038   for (uint z = 0; z < m_views.count(); z++)
04039     m_views.at(z)->tagLines (start, end, true);
04040 }
04041 
04042 void KateDocument::tagLines(KateTextCursor start, KateTextCursor end)
04043 {
04044   // May need to switch start/end cols if in block selection mode
04045   if (blockSelectionMode() && start.col() > end.col()) {
04046     int sc = start.col();
04047     start.setCol(end.col());
04048     end.setCol(sc);
04049   }
04050 
04051   for (uint z = 0; z < m_views.count(); z++)
04052     m_views.at(z)->tagLines(start, end, true);
04053 }
04054 
04055 void KateDocument::repaintViews(bool paintOnlyDirty)
04056 {
04057   for (uint z = 0; z < m_views.count(); z++)
04058     m_views.at(z)->repaintText(paintOnlyDirty);
04059 }
04060 
04061 void KateDocument::tagAll()
04062 {
04063   for (uint z = 0; z < m_views.count(); z++)
04064   {
04065     m_views.at(z)->tagAll();
04066     m_views.at(z)->updateView (true);
04067   }
04068 }
04069 
04070 uint KateDocument::configFlags ()
04071 {
04072   return config()->configFlags();
04073 }
04074 
04075 void KateDocument::setConfigFlags (uint flags)
04076 {
04077   config()->setConfigFlags(flags);
04078 }
04079 
04080 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04081 inline bool isEndBracket  ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04082 inline bool isBracket     ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04083 
04084 /*
04085    Bracket matching uses the following algorithm:
04086    If in overwrite mode, match the bracket currently underneath the cursor.
04087    Otherwise, if the character to the right of the cursor is an starting bracket,
04088    match it. Otherwise if the character to the left of the cursor is a
04089    ending bracket, match it. Otherwise, if the the character to the left
04090    of the cursor is an starting bracket, match it. Otherwise, if the character
04091    to the right of the cursor is an ending bracket, match it. Otherwise, don't
04092    match anything.
04093 */
04094 void KateDocument::newBracketMark( const KateTextCursor& cursor, KateBracketRange& bm, int maxLines )
04095 {
04096   bm.setValid(false);
04097 
04098   bm.start() = cursor;
04099 
04100   if( !findMatchingBracket( bm.start(), bm.end(), maxLines ) )
04101     return;
04102 
04103   bm.setValid(true);
04104 
04105   const int tw = config()->tabWidth();
04106   const int indentStart = m_buffer->plainLine(bm.start().line())->indentDepth(tw);
04107   const int indentEnd = m_buffer->plainLine(bm.end().line())->indentDepth(tw);
04108   bm.setIndentMin(QMIN(indentStart, indentEnd));
04109 }
04110 
04111 bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines )
04112 {
04113   KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() );
04114   if( !textLine )
04115     return false;
04116 
04117   QChar right = textLine->getChar( start.col() );
04118   QChar left  = textLine->getChar( start.col() - 1 );
04119   QChar bracket;
04120 
04121   if ( config()->configFlags() & cfOvr ) {
04122     if( isBracket( right ) ) {
04123       bracket = right;
04124     } else {
04125       return false;
04126     }
04127   } else if ( isStartBracket( right ) ) {
04128     bracket = right;
04129   } else if ( isEndBracket( left ) ) {
04130     start.setCol(start.col() - 1);
04131     bracket = left;
04132   } else if ( isBracket( left ) ) {
04133     start.setCol(start.col() - 1);
04134     bracket = left;
04135   } else if ( isBracket( right ) ) {
04136     bracket = right;
04137   } else {
04138     return false;
04139   }
04140 
04141   QChar opposite;
04142 
04143   switch( bracket ) {
04144   case '{': opposite = '}'; break;
04145   case '}': opposite = '{'; break;
04146   case '[': opposite = ']'; break;
04147   case ']': opposite = '['; break;
04148   case '(': opposite = ')'; break;
04149   case ')': opposite = '('; break;
04150   default: return false;
04151   }
04152 
04153   bool forward = isStartBracket( bracket );
04154   int startAttr = textLine->attribute( start.col() );
04155   uint count = 0;
04156   int lines = 0;
04157   end = start;
04158 
04159   while( true ) {
04160     /* Increment or decrement, check base cases */
04161     if( forward ) {
04162       end.setCol(end.col() + 1);
04163       if( end.col() >= lineLength( end.line() ) ) {
04164         if( end.line() >= (int)lastLine() )
04165           return false;
04166         end.setPos(end.line() + 1, 0);
04167         textLine = m_buffer->plainLine( end.line() );
04168         lines++;
04169       }
04170     } else {
04171       end.setCol(end.col() - 1);
04172       if( end.col() < 0 ) {
04173         if( end.line() <= 0 )
04174           return false;
04175         end.setLine(end.line() - 1);
04176         end.setCol(lineLength( end.line() ) - 1);
04177         textLine = m_buffer->plainLine( end.line() );
04178         lines++;
04179       }
04180     }
04181 
04182     if ((maxLines != -1) && (lines > maxLines))
04183       return false;
04184 
04185     /* Easy way to skip comments */
04186     if( textLine->attribute( end.col() ) != startAttr )
04187       continue;
04188 
04189     /* Check for match */
04190     QChar c = textLine->getChar( end.col() );
04191     if( c == bracket ) {
04192       count++;
04193     } else if( c == opposite ) {
04194       if( count == 0 )
04195         return true;
04196       count--;
04197     }
04198 
04199   }
04200 }
04201 
04202 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
04203 {
04204   KParts::ReadWritePart::guiActivateEvent( ev );
04205   if ( ev->activated() )
04206     emit selectionChanged();
04207 }
04208 
04209 void KateDocument::setDocName (QString name )
04210 {
04211   if ( name == m_docName )
04212     return;
04213 
04214   if ( !name.isEmpty() )
04215   {
04216     // TODO check for similarly named documents
04217     m_docName = name;
04218     updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04219     emit nameChanged((Kate::Document *) this);
04220     return;
04221   }
04222 
04223   // if the name is set, and starts with FILENAME, it should not be changed!
04224   if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return;
04225 
04226   int count = -1;
04227 
04228   for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
04229   {
04230     if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) )
04231       if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count )
04232         count = KateFactory::self()->documents()->at(z)->m_docNameNumber;
04233   }
04234 
04235   m_docNameNumber = count + 1;
04236 
04237   m_docName = url().filename();
04238 
04239   if (m_docName.isEmpty())
04240     m_docName = i18n ("Untitled");
04241 
04242   if (m_docNameNumber > 0)
04243     m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
04244 
04245   updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
04246   emit nameChanged ((Kate::Document *) this);
04247 }
04248 
04249 void KateDocument::slotModifiedOnDisk( Kate::View * /*v*/ )
04250 {
04251   if ( m_isasking < 0 )
04252   {
04253     m_isasking = 0;
04254     return;
04255   }
04256 
04257   if ( !s_fileChangedDialogsActivated || m_isasking )
04258     return;
04259 
04260   if (m_modOnHd && !url().isEmpty())
04261   {
04262     m_isasking = 1;
04263 
04264     KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), widget() );
04265     switch ( p.exec() )
04266     {
04267       case KateModOnHdPrompt::Save:
04268       {
04269         m_modOnHd = false;
04270         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
04271             url().url(),QString::null,widget(),i18n("Save File"));
04272 
04273         kdDebug(13020)<<"got "<<res.URLs.count()<<" URLs"<<endl;
04274         if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first() ) )
04275         {
04276           setEncoding( res.encoding );
04277 
04278           if( ! saveAs( res.URLs.first() ) )
04279           {
04280             KMessageBox::error( widget(), i18n("Save failed") );
04281             m_modOnHd = true;
04282           }
04283           else
04284             emit modifiedOnDisc( this, false, 0 );
04285         }
04286         else // the save as dialog was cancelled, we are still modified on disk
04287         {
04288           m_modOnHd = true;
04289         }
04290 
04291         m_isasking = 0;
04292         break;
04293       }
04294 
04295       case KateModOnHdPrompt::Reload:
04296         m_modOnHd = false;
04297         emit modifiedOnDisc( this, false, 0 );
04298         reloadFile();
04299         m_isasking = 0;
04300         break;
04301 
04302       case KateModOnHdPrompt::Ignore:
04303         m_modOnHd = false;
04304         emit modifiedOnDisc( this, false, 0 );
04305         m_isasking = 0;
04306         break;
04307 
04308       case KateModOnHdPrompt::Overwrite:
04309         m_modOnHd = false;
04310         emit modifiedOnDisc( this, false, 0 );
04311         m_isasking = 0;
04312         save();
04313         break;
04314 
04315       default:               // cancel: ignore next focus event
04316         m_isasking = -1;
04317     }
04318   }
04319 }
04320 
04321 void KateDocument::setModifiedOnDisk( int reason )
04322 {
04323   m_modOnHdReason = reason;
04324   m_modOnHd = (reason > 0);
04325   emit modifiedOnDisc( this, (reason > 0), reason );
04326 }
04327 
04328 class KateDocumentTmpMark
04329 {
04330   public:
04331     QString line;
04332     KTextEditor::Mark mark;
04333 };
04334 
04335 void KateDocument::reloadFile()
04336 {
04337   if ( !url().isEmpty() )
04338   {
04339     if (m_modOnHd && s_fileChangedDialogsActivated)
04340     {
04341       int i = KMessageBox::warningYesNoCancel
04342                 (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
04343                 i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes"));
04344 
04345       if ( i != KMessageBox::Yes)
04346       {
04347         if (i == KMessageBox::No)
04348         {
04349           m_modOnHd = false;
04350           m_modOnHdReason = 0;
04351           emit modifiedOnDisc (this, m_modOnHd, 0);
04352         }
04353 
04354         return;
04355       }
04356     }
04357 
04358     QValueList<KateDocumentTmpMark> tmp;
04359 
04360     for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
04361     {
04362       KateDocumentTmpMark m;
04363 
04364       m.line = textLine (it.current()->line);
04365       m.mark = *it.current();
04366 
04367       tmp.append (m);
04368     }
04369 
04370     uint mode = hlMode ();
04371     bool byUser = hlSetByUser;
04372 
04373     m_storedVariables.clear();
04374 
04375     m_reloading = true;
04376 
04377     QValueList<int> lines, cols;
04378     for ( uint i=0; i < m_views.count(); i++ )
04379     {
04380       lines.append( m_views.at( i )->cursorLine() );
04381       cols.append( m_views.at( i )->cursorColumn() );
04382     }
04383 
04384     KateDocument::openURL( url() );
04385 
04386     for ( uint i=0; i < m_views.count(); i++ )
04387       m_views.at( i )->setCursorPositionInternal( lines[ i ], cols[ i ], m_config->tabWidth(), false );
04388 
04389     m_reloading = false;
04390 
04391     for (uint z=0; z < tmp.size(); z++)
04392     {
04393       if (z < numLines())
04394       {
04395         if (textLine(tmp[z].mark.line) == tmp[z].line)
04396           setMark (tmp[z].mark.line, tmp[z].mark.type);
04397       }
04398     }
04399 
04400     if (byUser)
04401       setHlMode (mode);
04402   }
04403 }
04404 
04405 void KateDocument::flush ()
04406 {
04407   closeURL ();
04408 }
04409 
04410 void KateDocument::setWordWrap (bool on)
04411 {
04412   config()->setWordWrap (on);
04413 }
04414 
04415 bool KateDocument::wordWrap ()
04416 {
04417   return config()->wordWrap ();
04418 }
04419 
04420 void KateDocument::setWordWrapAt (uint col)
04421 {
04422   config()->setWordWrapAt (col);
04423 }
04424 
04425 unsigned int KateDocument::wordWrapAt ()
04426 {
04427   return config()->wordWrapAt ();
04428 }
04429 
04430 void KateDocument::applyWordWrap ()
04431 {
04432   // dummy to make the API happy
04433 }
04434 
04435 void KateDocument::setPageUpDownMovesCursor (bool on)
04436 {
04437   config()->setPageUpDownMovesCursor (on);
04438 }
04439 
04440 bool KateDocument::pageUpDownMovesCursor ()
04441 {
04442   return config()->pageUpDownMovesCursor ();
04443 }
04444 
04445 void KateDocument::dumpRegionTree()
04446 {
04447   m_buffer->foldingTree()->debugDump();
04448 }
04449 //END
04450 
04451 //BEGIN KTextEditor::CursorInterface stuff
04452 
04453 KTextEditor::Cursor *KateDocument::createCursor ( )
04454 {
04455   return new KateSuperCursor (this, false, 0, 0, this);
04456 }
04457 
04458 void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range)
04459 {
04460   if (view)
04461     view->tagLines(range->start(), range->end());
04462   else
04463     tagLines(range->start(), range->end());
04464 }
04465 
04466 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
04467 {
04468   m_buffer->lineInfo(info,line);
04469 }
04470 
04471 KateCodeFoldingTree *KateDocument::foldingTree ()
04472 {
04473   return m_buffer->foldingTree();
04474 }
04475 
04476 void KateDocument::setEncoding (const QString &e)
04477 {
04478   if ( m_encodingSticky )
04479     return;
04480 
04481   QString ce = m_config->encoding().lower();
04482   if ( e.lower() == ce )
04483     return;
04484 
04485   m_config->setEncoding( e );
04486   if ( ! m_loading )
04487     reloadFile();
04488 }
04489 
04490 QString KateDocument::encoding() const
04491 {
04492   return m_config->encoding();
04493 }
04494 
04495 void KateDocument::updateConfig ()
04496 {
04497   emit undoChanged ();
04498   tagAll();
04499 
04500   for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
04501   {
04502     view->updateDocumentConfig ();
04503   }
04504 
04505   // switch indenter if needed
04506   if (m_indenter->modeNumber() != m_config->indentationMode())
04507   {
04508     delete m_indenter;
04509     m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() );
04510   }
04511 
04512   m_indenter->updateConfig();
04513 
04514   m_buffer->setTabWidth (config()->tabWidth());
04515 
04516   // plugins
04517   for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
04518   {
04519     if (config()->plugin (i))
04520       loadPlugin (i);
04521     else
04522       unloadPlugin (i);
04523   }
04524 }
04525 
04526 //BEGIN Variable reader
04527 // "local variable" feature by anders, 2003
04528 /* TODO
04529       add config options (how many lines to read, on/off)
04530       add interface for plugins/apps to set/get variables
04531       add view stuff
04532 */
04533 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
04534 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
04535 
04536 void KateDocument::readVariables(bool onlyViewAndRenderer)
04537 {
04538   if (!onlyViewAndRenderer)
04539     m_config->configStart();
04540 
04541   // views!
04542   KateView *v;
04543   for (v = m_views.first(); v != 0L; v= m_views.next() )
04544   {
04545     v->config()->configStart();
04546     v->renderer()->config()->configStart();
04547   }
04548   // read a number of lines in the top/bottom of the document
04549   for (uint i=0; i < QMIN( 9, numLines() ); ++i )
04550   {
04551     readVariableLine( textLine( i ), onlyViewAndRenderer );
04552   }
04553   if ( numLines() > 10 )
04554   {
04555     for ( uint i = QMAX(10, numLines() - 10); i < numLines(); ++i )
04556     {
04557       readVariableLine( textLine( i ), onlyViewAndRenderer );
04558     }
04559   }
04560 
04561   if (!onlyViewAndRenderer)
04562     m_config->configEnd();
04563 
04564   for (v = m_views.first(); v != 0L; v= m_views.next() )
04565   {
04566     v->config()->configEnd();
04567     v->renderer()->config()->configEnd();
04568   }
04569 }
04570 
04571 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
04572 {
04573   if ( kvLine.search( t ) > -1 )
04574   {
04575     QStringList vvl; // view variable names
04576     vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
04577         << "line-numbers" << "icon-border" << "folding-markers"
04578         << "bookmark-sorting" << "auto-center-lines"
04579         << "icon-bar-color"
04580         // renderer
04581         << "background-color" << "selection-color"
04582         << "current-line-color" << "bracket-highlight-color"
04583         << "word-wrap-marker-color"
04584         << "font" << "font-size" << "scheme";
04585     int p( 0 );
04586     QString s = kvLine.cap(1);
04587     QString  var, val;
04588     while ( (p = kvVar.search( s, p )) > -1 )
04589     {
04590       p += kvVar.matchedLength();
04591       var = kvVar.cap( 1 );
04592       val = kvVar.cap( 2 ).stripWhiteSpace();
04593       bool state; // store booleans here
04594       int n; // store ints here
04595 
04596       // only apply view & renderer config stuff
04597       if (onlyViewAndRenderer)
04598       {
04599         if ( vvl.contains( var ) ) // FIXME define above
04600           setViewVariable( var, val );
04601       }
04602       else
04603       {
04604         // BOOL  SETTINGS
04605         if ( var == "word-wrap" && checkBoolValue( val, &state ) )
04606           setWordWrap( state ); // ??? FIXME CHECK
04607         else if ( var == "block-selection"  && checkBoolValue( val, &state ) )
04608           setBlockSelectionMode( state );
04609         // KateConfig::configFlags
04610         // FIXME should this be optimized to only a few calls? how?
04611         else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
04612           m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
04613         else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
04614           m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
04615         else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
04616           m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
04617         else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
04618           m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
04619         else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
04620           m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
04621         else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
04622           m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
04623         else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) )
04624           m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state );
04625         else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
04626           m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
04627         else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
04628           m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
04629         else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
04630           m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
04631         else if ( var == "space-indent" && checkBoolValue( val, &state ) )
04632           m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state );
04633         else if ( var == "smart-home" && checkBoolValue( val, &state ) )
04634           m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
04635         else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
04636           m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
04637         else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) )
04638           m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state);
04639         else if ( var == "mixed-indent" && checkBoolValue( val, &state ) )
04640           m_config->setConfigFlags( KateDocumentConfig::cfMixedIndent, state );
04641 
04642         // INTEGER SETTINGS
04643         else if ( var == "tab-width" && checkIntValue( val, &n ) )
04644           m_config->setTabWidth( n );
04645         else if ( var == "indent-width"  && checkIntValue( val, &n ) )
04646           m_config->setIndentationWidth( n );
04647         else if ( var == "indent-mode" )
04648         {
04649           if ( checkIntValue( val, &n ) )
04650             m_config->setIndentationMode( n );
04651           else
04652             m_config->setIndentationMode( KateAutoIndent::modeNumber( val) );
04653         }
04654         else if ( var == "word-wrap-column" && n > 0  && checkIntValue( val, &n ) ) // uint, but hard word wrap at 0 will be no fun ;)
04655           m_config->setWordWrapAt( n );
04656         else if ( var == "undo-steps"  && n >= 0  && checkIntValue( val, &n ) )
04657           setUndoSteps( n );
04658 
04659         // STRING SETTINGS
04660         else if ( var == "eol" || var == "end-of-line" )
04661         {
04662           QStringList l;
04663           l << "unix" << "dos" << "mac";
04664           if ( (n = l.findIndex( val.lower() )) != -1 )
04665             m_config->setEol( n );
04666         }
04667         else if ( var == "encoding" )
04668           m_config->setEncoding( val );
04669         else if ( var == "syntax" || var == "hl" )
04670         {
04671           for ( uint i=0; i < hlModeCount(); i++ )
04672           {
04673             if ( hlModeName( i ).lower() == val.lower() )
04674             {
04675               setHlMode( i );
04676               break;
04677             }
04678           }
04679         }
04680 
04681         // VIEW SETTINGS
04682         else if ( vvl.contains( var ) )
04683           setViewVariable( var, val );
04684         else
04685         {
04686           m_storedVariables.insert( var, val );
04687           emit variableChanged( var, val );
04688         }
04689       }
04690     }
04691   }
04692 }
04693 
04694 void KateDocument::setViewVariable( QString var, QString val )
04695 {
04696   KateView *v;
04697   bool state;
04698   int n;
04699   QColor c;
04700   for (v = m_views.first(); v != 0L; v= m_views.next() )
04701   {
04702     if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
04703       v->config()->setDynWordWrap( state );
04704     else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
04705       v->config()->setPersistentSelection( state );
04706     //else if ( var = "dynamic-word-wrap-indicators" )
04707     else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
04708       v->config()->setLineNumbers( state );
04709     else if (var == "icon-border" && checkBoolValue( val, &state ) )
04710       v->config()->setIconBar( state );
04711     else if (var == "folding-markers" && checkBoolValue( val, &state ) )
04712       v->config()->setFoldingBar( state );
04713     else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
04714       v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
04715     else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
04716       v->renderer()->config()->setIconBarColor( c );
04717     // RENDERER
04718     else if ( var == "background-color" && checkColorValue( val, c ) )
04719       v->renderer()->config()->setBackgroundColor( c );
04720     else if ( var == "selection-color" && checkColorValue( val, c ) )
04721       v->renderer()->config()->setSelectionColor( c );
04722     else if ( var == "current-line-color" && checkColorValue( val, c ) )
04723       v->renderer()->config()->setHighlightedLineColor( c );
04724     else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
04725       v->renderer()->config()->setHighlightedBracketColor( c );
04726     else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
04727       v->renderer()->config()->setWordWrapMarkerColor( c );
04728     else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
04729     {
04730       QFont _f( *v->renderer()->config()->font(  ) );
04731 
04732       if ( var == "font" )
04733       {
04734         _f.setFamily( val );
04735         _f.setFixedPitch( QFont( val ).fixedPitch() );
04736       }
04737       else
04738         _f.setPointSize( n );
04739 
04740       v->renderer()->config()->setFont( _f );
04741     }
04742     else if ( var == "scheme" )
04743     {
04744       v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) );
04745     }
04746   }
04747 }
04748 
04749 bool KateDocument::checkBoolValue( QString val, bool *result )
04750 {
04751   val = val.stripWhiteSpace().lower();
04752   QStringList l;
04753   l << "1" << "on" << "true";
04754   if ( l.contains( val ) )
04755   {
04756     *result = true;
04757     return true;
04758   }
04759   l.clear();
04760   l << "0" << "off" << "false";
04761   if ( l.contains( val ) )
04762   {
04763     *result = false;
04764     return true;
04765   }
04766   return false;
04767 }
04768 
04769 bool KateDocument::checkIntValue( QString val, int *result )
04770 {
04771   bool ret( false );
04772   *result = val.toInt( &ret );
04773   return ret;
04774 }
04775 
04776 bool KateDocument::checkColorValue( QString val, QColor &c )
04777 {
04778   c.setNamedColor( val );
04779   return c.isValid();
04780 }
04781 
04782 // KTextEditor::variable
04783 QString KateDocument::variable( const QString &name ) const
04784 {
04785   if ( m_storedVariables.contains( name ) )
04786     return m_storedVariables[ name ];
04787 
04788   return "";
04789 }
04790 
04791 //END
04792 
04793 void KateDocument::slotModOnHdDirty (const QString &path)
04794 {
04795   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1))
04796   {
04797     // compare md5 with the one we have (if we have one)
04798     if ( ! m_digest.isEmpty() )
04799     {
04800       QCString tmp;
04801       if ( createDigest( tmp ) && tmp == m_digest )
04802         return;
04803     }
04804 
04805     m_modOnHd = true;
04806     m_modOnHdReason = 1;
04807 
04808     // reenable dialog if not running atm
04809     if (m_isasking == -1)
04810       m_isasking = false;
04811 
04812     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04813   }
04814 }
04815 
04816 void KateDocument::slotModOnHdCreated (const QString &path)
04817 {
04818   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2))
04819   {
04820     m_modOnHd = true;
04821     m_modOnHdReason = 2;
04822 
04823     // reenable dialog if not running atm
04824     if (m_isasking == -1)
04825       m_isasking = false;
04826 
04827     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04828   }
04829 }
04830 
04831 void KateDocument::slotModOnHdDeleted (const QString &path)
04832 {
04833   if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3))
04834   {
04835     m_modOnHd = true;
04836     m_modOnHdReason = 3;
04837 
04838     // reenable dialog if not running atm
04839     if (m_isasking == -1)
04840       m_isasking = false;
04841 
04842     emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
04843   }
04844 }
04845 
04846 bool KateDocument::createDigest( QCString &result )
04847 {
04848   bool ret = false;
04849   result = "";
04850   if ( url().isLocalFile() )
04851   {
04852     QFile f ( url().path() );
04853     if ( f.open( IO_ReadOnly) )
04854     {
04855       KMD5 md5;
04856       ret = md5.update( f );
04857       md5.hexDigest( result );
04858       f.close();
04859       ret = true;
04860     }
04861   }
04862   return ret;
04863 }
04864 
04865 QString KateDocument::reasonedMOHString() const
04866 {
04867   switch( m_modOnHdReason )
04868   {
04869     case 1:
04870       return i18n("The file '%1' was modified by another program.").arg( url().prettyURL() );
04871       break;
04872     case 2:
04873       return i18n("The file '%1' was created by another program.").arg( url().prettyURL() );
04874       break;
04875     case 3:
04876       return i18n("The file '%1' was deleted by another program.").arg( url().prettyURL() );
04877       break;
04878     default:
04879       return QString();
04880   }
04881 }
04882 
04883 void KateDocument::removeTrailingSpace( uint line )
04884 {
04885   // remove trailing spaces from left line if required
04886   if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn )
04887   {
04888     KateTextLine::Ptr ln = kateTextLine( line );
04889 
04890     if ( ! ln ) return;
04891 
04892     if ( line == activeView()->cursorLine()
04893          && activeView()->cursorColumnReal() >= (uint)QMAX(0,ln->lastChar()) )
04894       return;
04895 
04896     if ( ln->length() )
04897     {
04898       uint p = ln->lastChar() + 1;
04899       uint l = ln->length() - p;
04900       if ( l )
04901         editRemoveText( line, p, l);
04902     }
04903   }
04904 }
04905 
04906 void KateDocument::updateFileType (int newType, bool user)
04907 {
04908   if (user || !m_fileTypeSetByUser)
04909   {
04910     const KateFileType *t = 0;
04911     if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType)))
04912     {
04913       m_fileType = newType;
04914 
04915       if (t)
04916       {
04917         m_config->configStart();
04918         // views!
04919         KateView *v;
04920         for (v = m_views.first(); v != 0L; v= m_views.next() )
04921         {
04922           v->config()->configStart();
04923           v->renderer()->config()->configStart();
04924         }
04925 
04926         readVariableLine( t->varLine );
04927 
04928         m_config->configEnd();
04929         for (v = m_views.first(); v != 0L; v= m_views.next() )
04930         {
04931           v->config()->configEnd();
04932           v->renderer()->config()->configEnd();
04933         }
04934       }
04935     }
04936   }
04937 }
04938 
04939 uint KateDocument::documentNumber () const
04940 {
04941   return KTextEditor::Document::documentNumber ();
04942 }
04943 
04944 
04945 
04946 
04947 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
04948       *handled=true;
04949       *abortClosing=true;
04950       if (m_url.isEmpty())
04951       {
04952         KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
04953                 QString::null,QString::null,0,i18n("Save File"));
04954 
04955         if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) {
04956                 *abortClosing=true;
04957                 return;
04958         }
04959         setEncoding( res.encoding );
04960           saveAs( res.URLs.first() );
04961         *abortClosing=false;
04962       }
04963       else
04964       {
04965           save();
04966           *abortClosing=false;
04967       }
04968 
04969 }
04970 
04971 bool KateDocument::checkOverwrite( KURL u )
04972 {
04973   if( !u.isLocalFile() )
04974     return true;
04975 
04976   QFileInfo info( u.path() );
04977   if( !info.exists() )
04978     return true;
04979 
04980   return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0,
04981     i18n( "A file named \"%1\" already exists. "
04982           "Are you sure you want to overwrite it?" ).arg( info.fileName() ),
04983     i18n( "Overwrite File?" ),
04984     i18n( "&Overwrite" ) );
04985 }
04986 
04987 void KateDocument::setDefaultEncoding (const QString &encoding)
04988 {
04989   s_defaultEncoding = encoding;
04990 }
04991 
04992 //BEGIN KTextEditor::TemplateInterface
04993 bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
04994       return (new KateTemplateHandler(this,line,column,templateString,initialValues))->initOk();
04995 }
04996 
04997 void KateDocument::testTemplateCode() {
04998   int col=activeView()->cursorColumn();
04999   int line=activeView()->cursorLine();
05000   insertTemplateText(line,col,"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05001 }
05002 
05003 bool KateDocument::invokeTabInterceptor(KKey key) {
05004   if (m_tabInterceptor) return (*m_tabInterceptor)(key);
05005   return false;
05006 }
05007 
05008 bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05009   if (m_tabInterceptor) return false;
05010   m_tabInterceptor=interceptor;
05011   return true;
05012 }
05013 
05014 bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05015   if (m_tabInterceptor!=interceptor) return false;
05016   m_tabInterceptor=0;
05017   return true;
05018 }
05019 //END KTextEditor::TemplateInterface
05020 
05021 //BEGIN DEPRECATED STUFF
05022  bool KateDocument::setSelection ( uint startLine, uint startCol, uint endLine, uint endCol )
05023 { if (m_activeView) return m_activeView->setSelection (startLine, startCol, endLine, endCol); return false; }
05024 
05025  bool KateDocument::clearSelection ()
05026  { if (m_activeView) return m_activeView->clearSelection(); return false; }
05027 
05028  bool KateDocument::hasSelection () const
05029  { if (m_activeView) return m_activeView->hasSelection (); return false; }
05030 
05031  QString KateDocument::selection () const
05032  { if (m_activeView) return m_activeView->selection (); return QString(""); }
05033 
05034  bool KateDocument::removeSelectedText ()
05035  { if (m_activeView) return m_activeView->removeSelectedText (); return false; }
05036 
05037  bool KateDocument::selectAll()
05038  { if (m_activeView) return m_activeView->selectAll (); return false; }
05039 
05040  int KateDocument::selStartLine()
05041  { if (m_activeView) return m_activeView->selStartLine (); return 0; }
05042 
05043  int KateDocument::selStartCol()
05044  { if (m_activeView) return m_activeView->selStartCol (); return 0; }
05045 
05046  int KateDocument::selEndLine()
05047  { if (m_activeView) return m_activeView->selEndLine (); return 0; }
05048 
05049  int KateDocument::selEndCol()
05050  { if (m_activeView) return m_activeView->selEndCol (); return 0; }
05051 
05052  bool KateDocument::blockSelectionMode ()
05053     { if (m_activeView) return m_activeView->blockSelectionMode (); return false; }
05054 
05055 bool KateDocument::setBlockSelectionMode (bool on)
05056     { if (m_activeView) return m_activeView->setBlockSelectionMode (on); return false; }
05057 
05058 bool KateDocument::toggleBlockSelectionMode ()
05059     { if (m_activeView) return m_activeView->toggleBlockSelectionMode (); return false; }
05060 //END DEPRECATED
05061 
05062 //END DEPRECATED STUFF
05063 
05064 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Home | KDE Accessibility Home | Description of Access Keys