• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kate
 

kate

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

kate

Skip menu "kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kate

Skip menu "kate"
  • arts
  • dcop
  • dnssd
  • interfaces
  •     interface
  •     library
  •   kspeech
  •   ktexteditor
  • kabc
  • kate
  • kcmshell
  • kdecore
  • kded
  • kdefx
  • kdeprint
  • kdesu
  • kdeui
  • kdoctools
  • khtml
  • kimgio
  • kinit
  • kio
  •   bookmarks
  •   httpfilter
  •   kfile
  •   kio
  •   kioexec
  •   kpasswdserver
  •   kssl
  • kioslave
  •   http
  • kjs
  • kmdi
  •   kmdi
  • knewstuff
  • kparts
  • krandr
  • kresources
  • kspell2
  • kunittest
  • kutils
  • kwallet
  • libkmid
  • libkscreensaver
Generated for kate by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |