katedocument.cpp

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