kate Library API Documentation

katedocument.cpp

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