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