kate Library API Documentation

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