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