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 QWidget *w = widget (); 02274 if (!w && !m_views.isEmpty ()) 02275 w = m_views.first(); 02276 02277 if (w) 02278 m_job->setWindow (w->topLevelWidget()); 02279 02280 emit started( m_job ); 02281 02282 connect( m_job, SIGNAL( data( KIO::Job*, const QByteArray& ) ), 02283 SLOT( slotDataKate( KIO::Job*, const QByteArray& ) ) ); 02284 02285 connect( m_job, SIGNAL( result( KIO::Job* ) ), 02286 SLOT( slotFinishedKate( KIO::Job* ) ) ); 02287 02288 return true; 02289 } 02290 } 02291 02292 void KateDocument::slotDataKate ( KIO::Job *, const QByteArray &data ) 02293 { 02294 kdDebug(13020) << "KateDocument::slotData" << endl; 02295 02296 if (!m_tempFile || !m_tempFile->file()) 02297 return; 02298 02299 m_tempFile->file()->writeBlock (data); 02300 } 02301 02302 void KateDocument::slotFinishedKate ( KIO::Job * job ) 02303 { 02304 kdDebug(13020) << "KateDocument::slotJobFinished" << endl; 02305 02306 if (!m_tempFile) 02307 return; 02308 02309 delete m_tempFile; 02310 m_tempFile = 0; 02311 m_job = 0; 02312 02313 if (job->error()) 02314 emit canceled( job->errorString() ); 02315 else 02316 { 02317 if ( openFile(job) ) 02318 emit setWindowCaption( m_url.prettyURL() ); 02319 02320 emit completed(); 02321 } 02322 } 02323 02324 void KateDocument::abortLoadKate() 02325 { 02326 if ( m_job ) 02327 { 02328 kdDebug(13020) << "Aborting job " << m_job << endl; 02329 m_job->kill(); 02330 m_job = 0; 02331 } 02332 02333 delete m_tempFile; 02334 m_tempFile = 0; 02335 } 02336 02337 bool KateDocument::openFile() 02338 { 02339 return openFile (0); 02340 } 02341 02342 bool KateDocument::openFile(KIO::Job * job) 02343 { 02344 // add new m_file to dirwatch 02345 activateDirWatch (); 02346 02347 // 02348 // use metadata 02349 // 02350 if (job) 02351 { 02352 QString metaDataCharset = job->queryMetaData("charset"); 02353 02354 if (!metaDataCharset.isEmpty ()) 02355 setEncoding (metaDataCharset); 02356 } 02357 02358 // 02359 // service type magic to get encoding right 02360 // 02361 QString serviceType = m_extension->urlArgs().serviceType.simplifyWhiteSpace(); 02362 int pos = serviceType.find(';'); 02363 if (pos != -1) 02364 setEncoding (serviceType.mid(pos+1)); 02365 02366 // do we have success ? 02367 bool success = buffer->openFile (m_file); 02368 02369 // 02370 // yeah, success 02371 // 02372 if (success) 02373 { 02374 if (m_highlight && !m_url.isLocalFile()) { 02375 // The buffer's highlighting gets nuked by KateBuffer::clear() 02376 buffer->setHighlight(m_highlight); 02377 } 02378 02379 // update our hl type if needed 02380 if (!hlSetByUser) 02381 { 02382 int hl (HlManager::self()->detectHighlighting (this)); 02383 02384 if (hl >= 0) 02385 internalSetHlMode(hl); 02386 02387 } 02388 // update file type 02389 updateFileType (KateFactory::self()->fileTypeManager()->fileType (this)); 02390 02391 // read vars 02392 readVariables(); 02393 02394 // update the md5 digest 02395 createDigest( m_digest ); 02396 } 02397 02398 // 02399 // update views 02400 // 02401 updateViews(); 02402 02403 // 02404 // emit the signal we need for example for kate app 02405 // 02406 emit fileNameChanged (); 02407 02408 // 02409 // set doc name, dummy value as arg, don't need it 02410 // 02411 setDocName (QString::null); 02412 02413 // 02414 // to houston, we are not modified 02415 // 02416 if (m_modOnHd) 02417 { 02418 m_modOnHd = false; 02419 m_modOnHdReason = 0; 02420 emit modifiedOnDisc (this, m_modOnHd, 0); 02421 } 02422 02423 // 02424 // display errors 02425 // 02426 if (s_openErrorDialogsActivated) 02427 { 02428 if (!success && buffer->loadingBorked()) 02429 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())); 02430 else if (!success) 02431 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())); 02432 } 02433 02434 // 02435 // return the success 02436 // 02437 return success; 02438 } 02439 02440 bool KateDocument::save() 02441 { 02442 // FIXME reorder for efficiency, prompt user in case of failure 02443 bool l ( url().isLocalFile() ); 02444 if ( ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles ) || 02445 ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) ) 02446 && isModified() ) { 02447 KURL u( url().path() + config()->backupSuffix() ); 02448 if ( ! KIO::NetAccess::upload( url().path(), u, kapp->mainWidget() ) ) 02449 kdDebug(13020)<<"backing up failed ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl; 02450 } 02451 02452 return KParts::ReadWritePart::save(); 02453 } 02454 02455 bool KateDocument::saveFile() 02456 { 02457 // 02458 // we really want to save this file ? 02459 // 02460 bool reallySaveIt = !buffer->loadingBorked() || (KMessageBox::warningYesNo(widget(), 02461 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); 02462 02463 if ( !url().isEmpty() ) 02464 { 02465 if (s_fileChangedDialogsActivated && m_modOnHd) 02466 { 02467 QString str; 02468 02469 if (m_modOnHdReason == 1) 02470 str = i18n("The file %1 was changed (modified) on disc by another program!\n\n").arg(url().fileName()); 02471 else if (m_modOnHdReason == 2) 02472 str = i18n("The file %1 was changed (created) on disc by another program!\n\n").arg(url().fileName()); 02473 else if (m_modOnHdReason == 3) 02474 str = i18n("The file %1 was changed (deleted) on disc by another program!\n\n").arg(url().fileName()); 02475 02476 if (!isModified()) 02477 { 02478 if (!(KMessageBox::warningYesNo(0, 02479 str + i18n("Do you really want to save this unmodified file? You could overwrite changed data in the file on disk.")) == KMessageBox::Yes)) 02480 reallySaveIt = false; 02481 } 02482 else 02483 { 02484 if (!(KMessageBox::warningYesNo(0, 02485 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)) 02486 reallySaveIt = false; 02487 } 02488 } 02489 } 02490 02491 // 02492 // can we encode it if we want to save it ? 02493 // 02494 bool canEncode = true; 02495 02496 if (reallySaveIt) 02497 canEncode = buffer->canEncode (); 02498 02499 // 02500 // start with worst case, we had no success 02501 // 02502 bool success = false; 02503 02504 // remove file 02505 deactivateDirWatch (); 02506 02507 // 02508 // try to load it if needed 02509 // 02510 if (reallySaveIt && canEncode) 02511 success = buffer->saveFile (m_file); 02512 02513 // update the md5 digest 02514 createDigest( m_digest ); 02515 02516 // add file 02517 activateDirWatch (); 02518 02519 // 02520 // hurray, we had success, do stuff we need 02521 // 02522 if (success) 02523 { 02524 // update our hl type if needed 02525 if (!hlSetByUser) 02526 { 02527 int hl (HlManager::self()->detectHighlighting (this)); 02528 02529 if (hl >= 0) 02530 internalSetHlMode(hl); 02531 } 02532 02533 // update our file type 02534 updateFileType (KateFactory::self()->fileTypeManager()->fileType (this)); 02535 02536 // read our vars 02537 readVariables(); 02538 } 02539 02540 // 02541 // emit the signal we need for example for kate app 02542 // 02543 emit fileNameChanged (); 02544 02545 // 02546 // set doc name, dummy value as arg, don't need it 02547 // 02548 setDocName (QString::null); 02549 02550 // 02551 // we are not modified 02552 // 02553 if (success && m_modOnHd) 02554 { 02555 m_modOnHd = false; 02556 m_modOnHdReason = 0; 02557 emit modifiedOnDisc (this, m_modOnHd, 0); 02558 } 02559 02560 // 02561 // display errors 02562 // 02563 if (reallySaveIt && !canEncode) 02564 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.")); 02565 else if (reallySaveIt && !success) 02566 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())); 02567 02568 // 02569 // return success 02570 // 02571 return success; 02572 } 02573 02574 void KateDocument::activateDirWatch () 02575 { 02576 // same file as we are monitoring, return 02577 if (m_file == m_dirWatchFile) 02578 return; 02579 02580 // remove the old watched file 02581 deactivateDirWatch (); 02582 02583 // add new file if needed 02584 if (m_url.isLocalFile() && !m_file.isEmpty()) 02585 { 02586 KateFactory::self()->dirWatch ()->addFile (m_file); 02587 m_dirWatchFile = m_file; 02588 } 02589 } 02590 02591 void KateDocument::deactivateDirWatch () 02592 { 02593 if (!m_dirWatchFile.isEmpty()) 02594 KateFactory::self()->dirWatch ()->removeFile (m_dirWatchFile); 02595 02596 m_dirWatchFile = QString::null; 02597 } 02598 02599 bool KateDocument::closeURL() 02600 { 02601 abortLoadKate(); 02602 02603 // 02604 // file mod on hd 02605 // 02606 if ( !m_reloading && !url().isEmpty() ) 02607 { 02608 if (s_fileChangedDialogsActivated && m_modOnHd) 02609 { 02610 QString str; 02611 02612 if (m_modOnHdReason == 1) 02613 str = i18n("The file %1 was changed (modified) on disc by another program!\n\n").arg(url().fileName()); 02614 else if (m_modOnHdReason == 2) 02615 str = i18n("The file %1 was changed (created) on disc by another program!\n\n").arg(url().fileName()); 02616 else if (m_modOnHdReason == 3) 02617 str = i18n("The file %1 was changed (deleted) on disc by another program!\n\n").arg(url().fileName()); 02618 02619 if (!(KMessageBox::warningYesNo(0, 02620 str + i18n("Do you really want to continue to close this file? Data loss may occur.")) == KMessageBox::Yes)) 02621 return false; 02622 } 02623 } 02624 02625 // 02626 // first call the normal kparts implementation 02627 // 02628 if (!KParts::ReadWritePart::closeURL ()) 02629 return false; 02630 02631 // remove file 02632 deactivateDirWatch (); 02633 02634 // 02635 // empty url + filename 02636 // 02637 m_url = KURL (); 02638 m_file = QString::null; 02639 02640 // we are not modified 02641 if (m_modOnHd) 02642 { 02643 m_modOnHd = false; 02644 m_modOnHdReason = 0; 02645 emit modifiedOnDisc (this, m_modOnHd, 0); 02646 } 02647 02648 // clear the buffer 02649 buffer->clear(); 02650 02651 // remove all marks 02652 clearMarks (); 02653 02654 // clear undo/redo history 02655 clearUndo(); 02656 clearRedo(); 02657 02658 // no, we are no longer modified 02659 setModified(false); 02660 02661 // we have no longer any hl 02662 internalSetHlMode(0); 02663 02664 // update all our views 02665 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) 02666 { 02667 // Explicitly call the internal version because we don't want this to look like 02668 // an external request (and thus have the view not QWidget::scroll()ed. 02669 view->setCursorPositionInternal(0, 0, 1, false); 02670 view->updateView(true); 02671 } 02672 02673 // uh, filename changed 02674 emit fileNameChanged (); 02675 02676 // update doc name 02677 setDocName (QString::null); 02678 02679 // success 02680 return true; 02681 } 02682 02683 void KateDocument::setReadWrite( bool rw ) 02684 { 02685 if (isReadWrite() != rw) 02686 { 02687 KParts::ReadWritePart::setReadWrite (rw); 02688 02689 for( KateView* view = m_views.first(); view != 0L; view = m_views.next() ) 02690 { 02691 view->slotUpdate(); 02692 view->slotReadWriteChanged (); 02693 } 02694 } 02695 } 02696 02697 void KateDocument::setModified(bool m) { 02698 02699 if (isModified() != m) { 02700 KParts::ReadWritePart::setModified (m); 02701 02702 for( KateView* view = m_views.first(); view != 0L; view = m_views.next() ) 02703 { 02704 view->slotUpdate(); 02705 } 02706 02707 emit modifiedChanged (); 02708 emit modStateChanged ((Kate::Document *)this); 02709 } 02710 if ( m == false && ! undoItems.isEmpty() ) 02711 { 02712 lastUndoGroupWhenSaved = undoItems.last(); 02713 } 02714 02715 if ( m == false ) docWasSavedWhenUndoWasEmpty = undoItems.isEmpty(); 02716 } 02717 //END 02718 02719 //BEGIN Kate specific stuff ;) 02720 02721 void KateDocument::makeAttribs() 02722 { 02723 m_highlight->clearAttributeArrays (); 02724 02725 for (uint z = 0; z < m_views.count(); z++) 02726 m_views.at(z)->renderer()->updateAttributes (); 02727 02728 buffer->invalidateHighlighting(); 02729 02730 tagAll (); 02731 } 02732 02733 // the attributes of a hl have changed, update 02734 void KateDocument::internalHlChanged() 02735 { 02736 makeAttribs(); 02737 } 02738 02739 void KateDocument::addView(KTextEditor::View *view) { 02740 if (!view) 02741 return; 02742 02743 m_views.append( (KateView *) view ); 02744 m_textEditViews.append( view ); 02745 02746 // apply the view & renderer vars from the file type 02747 const KateFileType *t = 0; 02748 if ((m_fileType > -1) && (t = KateFactory::self()->fileTypeManager()->fileType(m_fileType))) 02749 readVariableLine (t->varLine, true); 02750 02751 // apply the view & renderer vars from the file 02752 readVariables (true); 02753 02754 m_activeView = (KateView *) view; 02755 } 02756 02757 void KateDocument::removeView(KTextEditor::View *view) { 02758 if (!view) 02759 return; 02760 02761 if (m_activeView == view) 02762 m_activeView = 0L; 02763 02764 m_views.removeRef( (KateView *) view ); 02765 m_textEditViews.removeRef( view ); 02766 } 02767 02768 void KateDocument::addSuperCursor(KateSuperCursor *cursor, bool privateC) { 02769 if (!cursor) 02770 return; 02771 02772 m_superCursors.append( cursor ); 02773 02774 if (!privateC) 02775 myCursors.append( cursor ); 02776 } 02777 02778 void KateDocument::removeSuperCursor(KateSuperCursor *cursor, bool privateC) { 02779 if (!cursor) 02780 return; 02781 02782 if (!privateC) 02783 myCursors.removeRef( cursor ); 02784 02785 m_superCursors.removeRef( cursor ); 02786 } 02787 02788 bool KateDocument::ownedView(KateView *view) { 02789 // do we own the given view? 02790 return (m_views.containsRef(view) > 0); 02791 } 02792 02793 bool KateDocument::isLastView(int numViews) { 02794 return ((int) m_views.count() == numViews); 02795 } 02796 02797 uint KateDocument::currentColumn( const KateTextCursor& cursor ) 02798 { 02799 TextLine::Ptr textLine = buffer->plainLine(cursor.line()); 02800 02801 if (textLine) 02802 return textLine->cursorX(cursor.col(), config()->tabWidth()); 02803 else 02804 return 0; 02805 } 02806 02807 bool KateDocument::typeChars ( KateView *view, const QString &chars ) 02808 { 02809 TextLine::Ptr textLine = buffer->plainLine(view->cursorLine ()); 02810 02811 if (!textLine) 02812 return false; 02813 02814 int oldLine = view->cursorLine (); 02815 int oldCol = view->cursorColumnReal (); 02816 02817 bool bracketInserted = false; 02818 QString buf; 02819 QChar c; 02820 for( uint z = 0; z < chars.length(); z++ ) 02821 { 02822 QChar ch = c = chars[z]; 02823 02824 if (ch.isPrint() || ch == '\t') 02825 { 02826 buf.append (ch); 02827 02828 if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets)) 02829 { 02830 if (ch == '(') { bracketInserted = true; buf.append (')'); } 02831 if (ch == '[') { bracketInserted = true; buf.append (']'); } 02832 if (ch == '{') { bracketInserted = true; buf.append ('}'); } 02833 } 02834 } 02835 } 02836 02837 if (buf.isEmpty()) 02838 return false; 02839 02840 editStart (); 02841 02842 if (!(config()->configFlags() & KateDocument::cfPersistent) && hasSelection() ) 02843 removeSelectedText(); 02844 02845 if (config()->configFlags() & KateDocument::cfOvr) 02846 removeText (view->cursorLine(), view->cursorColumnReal(), view->cursorLine(), QMIN( view->cursorColumnReal()+buf.length(), textLine->length() ) ); 02847 02848 insertText (view->cursorLine(), view->cursorColumnReal(), buf); 02849 m_indenter->processChar(c); 02850 02851 editEnd (); 02852 02853 if (bracketInserted) 02854 view->setCursorPositionInternal (view->cursorLine(), view->cursorColumnReal()-1); 02855 02856 emit charactersInteractivelyInserted (oldLine, oldCol, chars); 02857 02858 return true; 02859 } 02860 02861 void KateDocument::newLine( KateTextCursor& c, KateViewInternal *v ) 02862 { 02863 editStart(); 02864 02865 if( !(config()->configFlags() & cfPersistent) && hasSelection() ) 02866 removeSelectedText(); 02867 02868 // temporary hack to get the cursor pos right !!!!!!!!! 02869 c = v->getCursor (); 02870 02871 if (c.line() > (int)lastLine()) 02872 c.setLine(lastLine()); 02873 02874 TextLine::Ptr textLine = kateTextLine(c.line()); 02875 if (c.col() > (int)textLine->length()) 02876 c.setCol(textLine->length()); 02877 02878 if (!(config()->configFlags() & KateDocument::cfAutoIndent)) 02879 { 02880 insertText( c.line(), c.col(), "\n" ); 02881 c.setPos(c.line() + 1, 0); 02882 } 02883 else 02884 { 02885 int pos = textLine->firstChar(); 02886 if (c.col() < pos) 02887 c.setCol(pos); // place cursor on first char if before 02888 02889 insertText (c.line(), c.col(), "\n"); 02890 02891 KateDocCursor cursor (c.line() + 1, pos, this); 02892 m_indenter->processNewline(cursor, true); 02893 c.setPos(cursor); 02894 } 02895 02896 editEnd(); 02897 } 02898 02899 void KateDocument::transpose( const KateTextCursor& cursor) 02900 { 02901 TextLine::Ptr textLine = buffer->plainLine(cursor.line()); 02902 02903 if (!textLine || (textLine->length() < 2)) 02904 return; 02905 02906 uint col = cursor.col(); 02907 02908 if (col > 0) 02909 col--; 02910 02911 if ((textLine->length() - col) < 2) 02912 return; 02913 02914 uint line = cursor.line(); 02915 QString s; 02916 02917 //clever swap code if first character on the line swap right&left 02918 //otherwise left & right 02919 s.append (textLine->getChar(col+1)); 02920 s.append (textLine->getChar(col)); 02921 //do the swap 02922 02923 // do it right, never ever manipulate a textline 02924 editStart (); 02925 editRemoveText (line, col, 2); 02926 editInsertText (line, col, s); 02927 editEnd (); 02928 } 02929 02930 void KateDocument::backspace( const KateTextCursor& c ) 02931 { 02932 if( !(config()->configFlags() & cfPersistent) && hasSelection() ) { 02933 removeSelectedText(); 02934 return; 02935 } 02936 02937 uint col = QMAX( c.col(), 0 ); 02938 uint line = QMAX( c.line(), 0 ); 02939 02940 if ((col == 0) && (line == 0)) 02941 return; 02942 02943 if (col > 0) 02944 { 02945 if (!(config()->configFlags() & KateDocument::cfBackspaceIndents)) 02946 { 02947 // ordinary backspace 02948 //c.cursor.col--; 02949 removeText(line, col-1, line, col); 02950 } 02951 else 02952 { 02953 // backspace indents: erase to next indent position 02954 02955 TextLine::Ptr textLine = buffer->plainLine(line); 02956 int colX = textLine->cursorX(col, config()->tabWidth()); 02957 int pos = textLine->firstChar(); 02958 if (pos > 0) 02959 pos = textLine->cursorX(pos, config()->tabWidth()); 02960 02961 if (pos < 0 || pos >= (int)colX) 02962 { 02963 // only spaces on left side of cursor 02964 // search a line with less spaces 02965 int y = line; 02966 while (--y >= 0) 02967 { 02968 textLine = buffer->plainLine(y); 02969 pos = textLine->firstChar(); 02970 02971 if (pos >= 0) 02972 { 02973 pos = textLine->cursorX(pos, config()->tabWidth()); 02974 if (pos < (int)colX) 02975 { 02976 replaceWithOptimizedSpace(line, col, pos, config()->configFlags()); 02977 break; 02978 } 02979 } 02980 } 02981 if (y < 0) { 02982 // FIXME: what shoud we do in this case? 02983 removeText(line, 0, line, col); 02984 } 02985 } 02986 else 02987 removeText(line, col-1, line, col); 02988 } 02989 } 02990 else 02991 { 02992 // col == 0: wrap to previous line 02993 if (line >= 1) 02994 { 02995 TextLine::Ptr textLine = buffer->plainLine(line-1); 02996 if (config()->wordWrap() && textLine->endingWith(QString::fromLatin1(" "))) 02997 { 02998 // gg: in hard wordwrap mode, backspace must also eat the trailing space 02999 removeText (line-1, textLine->length()-1, line, 0); 03000 } 03001 else 03002 removeText (line-1, textLine->length(), line, 0); 03003 } 03004 } 03005 03006 emit backspacePressed(); 03007 } 03008 03009 void KateDocument::del( const KateTextCursor& c ) 03010 { 03011 if ( !(config()->configFlags() & cfPersistent) && hasSelection() ) { 03012 removeSelectedText(); 03013 return; 03014 } 03015 03016 if( c.col() < (int) buffer->plainLine(c.line())->length()) 03017 { 03018 removeText(c.line(), c.col(), c.line(), c.col()+1); 03019 } 03020 else 03021 { 03022 removeText(c.line(), c.col(), c.line()+1, 0); 03023 } 03024 } 03025 03026 void KateDocument::cut() 03027 { 03028 if (!hasSelection()) 03029 return; 03030 03031 copy(); 03032 removeSelectedText(); 03033 } 03034 03035 void KateDocument::copy() 03036 { 03037 if (!hasSelection()) 03038 return; 03039 03040 QApplication::clipboard()->setText(selection ()); 03041 } 03042 03043 void KateDocument::paste ( KateView* view ) 03044 { 03045 QString s = QApplication::clipboard()->text(); 03046 03047 if (s.isEmpty()) 03048 return; 03049 03050 m_undoDontMerge = true; 03051 03052 editStart (); 03053 03054 if (!(config()->configFlags() & KateDocument::cfPersistent) && hasSelection() ) 03055 removeSelectedText(); 03056 03057 uint line = view->cursorLine (); 03058 uint column = view->cursorColumnReal (); 03059 03060 insertText ( line, column, s, blockSelect ); 03061 03062 editEnd(); 03063 03064 // move cursor right for block select, as the user is moved right internal 03065 // even in that case, but user expects other behavior in block selection 03066 // mode ! 03067 if (blockSelect) 03068 { 03069 uint lines = s.contains (QChar ('\n')); 03070 view->setCursorPositionInternal (line+lines, column); 03071 } 03072 03073 m_undoDontMerge = true; 03074 } 03075 03076 void KateDocument::selectWord( const KateTextCursor& cursor ) 03077 { 03078 int start, end, len; 03079 03080 TextLine::Ptr textLine = buffer->plainLine(cursor.line()); 03081 len = textLine->length(); 03082 start = end = cursor.col(); 03083 while (start > 0 && m_highlight->isInWord(textLine->getChar(start - 1))) start--; 03084 while (end < len && m_highlight->isInWord(textLine->getChar(end))) end++; 03085 if (end <= start) return; 03086 03087 if (!(config()->configFlags() & KateDocument::cfKeepSelection)) 03088 clearSelection (); 03089 03090 setSelection (cursor.line(), start, cursor.line(), end); 03091 } 03092 03093 void KateDocument::selectLine( const KateTextCursor& cursor ) 03094 { 03095 if (!(config()->configFlags() & KateDocument::cfKeepSelection)) 03096 clearSelection (); 03097 03098 setSelection (cursor.line(), 0, cursor.line()/*+1, 0*/, buffer->plainLine(cursor.line())->length() ); 03099 } 03100 03101 void KateDocument::selectLength( const KateTextCursor& cursor, int length ) 03102 { 03103 int start, end; 03104 03105 TextLine::Ptr textLine = buffer->plainLine(cursor.line()); 03106 start = cursor.col(); 03107 end = start + length; 03108 if (end <= start) return; 03109 03110 if (!(config()->configFlags() & KateDocument::cfKeepSelection)) 03111 clearSelection (); 03112 setSelection (cursor.line(), start, cursor.line(), end); 03113 } 03114 03115 void KateDocument::insertIndentChars ( KateView *view ) 03116 { 03117 editStart (); 03118 03119 QString s; 03120 if (config()->configFlags() & KateDocument::cfSpaceIndent) 03121 s.fill (' ', config()->indentationWidth()); 03122 else 03123 s.append ('\t'); 03124 03125 insertText (view->cursorLine(), view->cursorColumnReal(), s); 03126 03127 editEnd (); 03128 } 03129 03130 void KateDocument::indent ( KateView *, uint line, int change) 03131 { 03132 editStart (); 03133 03134 if (!hasSelection()) 03135 { 03136 // single line 03137 optimizeLeadingSpace(line, config()->configFlags(), change); 03138 } 03139 else 03140 { 03141 int sl = selectStart.line(); 03142 int el = selectEnd.line(); 03143 int ec = selectEnd.col(); 03144 03145 if ((ec == 0) && ((el-1) >= 0)) 03146 { 03147 03148 /* */ 03149 el--; 03150 } 03151 03152 if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) { 03153 // unindent so that the existing indent profile doesn't get screwed 03154 // if any line we may unindent is already full left, don't do anything 03155 int adjustedChange = -change; 03156 03157 for (line = sl; (int) line <= el && adjustedChange > 0; line++) { 03158 TextLine::Ptr textLine = buffer->plainLine(line); 03159 int firstChar = textLine->firstChar(); 03160 if (firstChar >= 0 && (lineSelected(line) || lineHasSelected(line))) { 03161 int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth(); 03162 if (maxUnindent < adjustedChange) 03163 adjustedChange = maxUnindent; 03164 } 03165 } 03166 03167 change = -adjustedChange; 03168 } 03169 03170 for (line = sl; (int) line <= el; line++) { 03171 if (lineSelected(line) || lineHasSelected(line)) { 03172 optimizeLeadingSpace(line, config()->configFlags(), change); 03173 } 03174 } 03175 } 03176 03177 editEnd (); 03178 } 03179 03180 /* 03181 Optimize the leading whitespace for a single line. 03182 If change is > 0, it adds indentation units (indentationChars) 03183 if change is == 0, it only optimizes 03184 If change is < 0, it removes indentation units 03185 This will be used to indent, unindent, and optimal-fill a line. 03186 If excess space is removed depends on the flag cfKeepExtraSpaces 03187 which has to be set by the user 03188 */ 03189 void KateDocument::optimizeLeadingSpace(uint line, int flags, int change) 03190 { 03191 TextLine::Ptr textline = buffer->plainLine(line); 03192 03193 int first_char = textline->firstChar(); 03194 03195 int w = 0; 03196 if (flags & KateDocument::cfSpaceIndent) 03197 w = config()->indentationWidth(); 03198 else 03199 w = config()->tabWidth(); 03200 03201 if (first_char < 0) 03202 first_char = textline->length(); 03203 03204 int space = textline->cursorX(first_char, config()->tabWidth()) + change * w; 03205 if (space < 0) 03206 space = 0; 03207 03208 if (!(flags & KateDocument::cfKeepExtraSpaces)) 03209 { 03210 uint extra = space % w; 03211 03212 space -= extra; 03213 if (extra && change < 0) { 03214 // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide) 03215 space += w; 03216 } 03217 } 03218 03219 //kdDebug() << "replace With Op: " << line << " " << first_char << " " << space << endl; 03220 replaceWithOptimizedSpace(line, first_char, space, flags); 03221 } 03222 03223 void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags) 03224 { 03225 uint length; 03226 QString new_space; 03227 03228 if (flags & KateDocument::cfSpaceIndent) { 03229 length = space; 03230 new_space.fill(' ', length); 03231 } 03232 else { 03233 length = space / config()->tabWidth(); 03234 new_space.fill('\t', length); 03235 03236 QString extra_space; 03237 extra_space.fill(' ', space % config()->tabWidth()); 03238 length += space % config()->tabWidth(); 03239 new_space += extra_space; 03240 } 03241 03242 TextLine::Ptr textline = buffer->plainLine(line); 03243 uint change_from; 03244 for (change_from = 0; change_from < upto_column && change_from < length; change_from++) { 03245 if (textline->getChar(change_from) != new_space[change_from]) 03246 break; 03247 } 03248 03249 editStart(); 03250 03251 if (change_from < upto_column) 03252 removeText(line, change_from, line, upto_column); 03253 03254 if (change_from < length) 03255 insertText(line, change_from, new_space.right(length - change_from)); 03256 03257 editEnd(); 03258 } 03259 03260 /* 03261 Remove a given string at the begining 03262 of the current line. 03263 */ 03264 bool KateDocument::removeStringFromBegining(int line, QString &str) 03265 { 03266 TextLine::Ptr textline = buffer->plainLine(line); 03267 03268 int index = 0; 03269 bool there = false; 03270 03271 if (textline->startingWith(str)) 03272 there = true; 03273 else 03274 { 03275 index = textline->firstChar (); 03276 03277 if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str)) 03278 there = true; 03279 } 03280 03281 if (there) 03282 { 03283 // Remove some chars 03284 removeText (line, index, line, index+str.length()); 03285 } 03286 03287 return there; 03288 } 03289 03290 /* 03291 Remove a given string at the end 03292 of the current line. 03293 */ 03294 bool KateDocument::removeStringFromEnd(int line, QString &str) 03295 { 03296 TextLine::Ptr textline = buffer->plainLine(line); 03297 03298 int index = 0; 03299 bool there = false; 03300 03301 if(textline->endingWith(str)) 03302 { 03303 index = textline->length() - str.length(); 03304 there = true; 03305 } 03306 else 03307 { 03308 index = textline->lastChar ()-str.length()+1; 03309 03310 if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str)) 03311 there = true; 03312 } 03313 03314 if (there) 03315 { 03316 // Remove some chars 03317 removeText (line, index, line, index+str.length()); 03318 } 03319 03320 return there; 03321 } 03322 03323 /* 03324 Add to the current line a comment line mark at 03325 the begining. 03326 */ 03327 void KateDocument::addStartLineCommentToSingleLine(int line) 03328 { 03329 QString commentLineMark = m_highlight->getCommentSingleLineStart() + " "; 03330 insertText (line, 0, commentLineMark); 03331 } 03332 03333 /* 03334 Remove from the current line a comment line mark at 03335 the begining if there is one. 03336 */ 03337 bool KateDocument::removeStartLineCommentFromSingleLine(int line) 03338 { 03339 QString shortCommentMark = m_highlight->getCommentSingleLineStart(); 03340 QString longCommentMark = shortCommentMark + " "; 03341 03342 editStart(); 03343 03344 // Try to remove the long comment mark first 03345 bool removed = (removeStringFromBegining(line, longCommentMark) 03346 || removeStringFromBegining(line, shortCommentMark)); 03347 03348 editEnd(); 03349 03350 return removed; 03351 } 03352 03353 /* 03354 Add to the current line a start comment mark at the 03355 begining and a stop comment mark at the end. 03356 */ 03357 void KateDocument::addStartStopCommentToSingleLine(int line) 03358 { 03359 QString startCommentMark = m_highlight->getCommentStart() + " "; 03360 QString stopCommentMark = " " + m_highlight->getCommentEnd(); 03361 03362 editStart(); 03363 03364 // Add the start comment mark 03365 insertText (line, 0, startCommentMark); 03366 03367 // Go to the end of the line 03368 int col = buffer->plainLine(line)->length(); 03369 03370 // Add the stop comment mark 03371 insertText (line, col, stopCommentMark); 03372 03373 editEnd(); 03374 } 03375 03376 /* 03377 Remove from the current line a start comment mark at 03378 the begining and a stop comment mark at the end. 03379 */ 03380 bool KateDocument::removeStartStopCommentFromSingleLine(int line) 03381 { 03382 QString shortStartCommentMark = m_highlight->getCommentStart(); 03383 QString longStartCommentMark = shortStartCommentMark + " "; 03384 QString shortStopCommentMark = m_highlight->getCommentEnd(); 03385 QString longStopCommentMark = " " + shortStopCommentMark; 03386 03387 editStart(); 03388 03389 // Try to remove the long start comment mark first 03390 bool removedStart = (removeStringFromBegining(line, longStartCommentMark) 03391 || removeStringFromBegining(line, shortStartCommentMark)); 03392 03393 bool removedStop = false; 03394 if (removedStart) 03395 { 03396 // Try to remove the long stop comment mark first 03397 removedStop = (removeStringFromEnd(line, longStopCommentMark) 03398 || removeStringFromEnd(line, shortStopCommentMark)); 03399 } 03400 03401 editEnd(); 03402 03403 return (removedStart || removedStop); 03404 } 03405 03406 /* 03407 Add to the current selection a start comment 03408 mark at the begining and a stop comment mark 03409 at the end. 03410 */ 03411 void KateDocument::addStartStopCommentToSelection() 03412 { 03413 QString startComment = m_highlight->getCommentStart(); 03414 QString endComment = m_highlight->getCommentEnd(); 03415 03416 int sl = selectStart.line(); 03417 int el = selectEnd.line(); 03418 int sc = selectStart.col(); 03419 int ec = selectEnd.col(); 03420 03421 if ((ec == 0) && ((el-1) >= 0)) 03422 { 03423 el--; 03424 ec = buffer->plainLine (el)->length(); 03425 } 03426 03427 editStart(); 03428 03429 insertText (el, ec, endComment); 03430 insertText (sl, sc, startComment); 03431 03432 editEnd (); 03433 03434 // Set the new selection 03435 ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 ); 03436 setSelection(sl, sc, el, ec); 03437 } 03438 03439 /* 03440 Add to the current selection a comment line 03441 mark at the begining of each line. 03442 */ 03443 void KateDocument::addStartLineCommentToSelection() 03444 { 03445 QString commentLineMark = m_highlight->getCommentSingleLineStart() + " "; 03446 03447 int sl = selectStart.line(); 03448 int el = selectEnd.line(); 03449 03450 if ((selectEnd.col() == 0) && ((el-1) >= 0)) 03451 { 03452 el--; 03453 } 03454 03455 editStart(); 03456 03457 // For each line of the selection 03458 for (int z = el; z >= sl; z--) { 03459 insertText (z, 0, commentLineMark); 03460 } 03461 03462 editEnd (); 03463 03464 // Set the new selection 03465 selectEnd.setCol(selectEnd.col() + ((el == selectEnd.line()) ? commentLineMark.length() : 0) ); 03466 setSelection(selectStart.line(), 0, selectEnd.line(), selectEnd.col()); 03467 } 03468 03469 bool KateDocument::nextNonSpaceCharPos(int &line, int &col) 03470 { 03471 for(; line < (int)buffer->count(); line++) { 03472 col = buffer->plainLine(line)->nextNonSpaceChar(col); 03473 if(col != -1) 03474 return true; // Next non-space char found 03475 col = 0; 03476 } 03477 // No non-space char found 03478 line = -1; 03479 col = -1; 03480 return false; 03481 } 03482 03483 bool KateDocument::previousNonSpaceCharPos(int &line, int &col) 03484 { 03485 while(true) 03486 { 03487 col = buffer->plainLine(line)->previousNonSpaceChar(col); 03488 if(col != -1) return true; 03489 if(line == 0) return false; 03490 --line; 03491 col = buffer->plainLine(line)->length(); 03492 } 03493 // No non-space char found 03494 line = -1; 03495 col = -1; 03496 return false; 03497 } 03498 03499 /* 03500 Remove from the selection a start comment mark at 03501 the begining and a stop comment mark at the end. 03502 */ 03503 bool KateDocument::removeStartStopCommentFromSelection() 03504 { 03505 QString startComment = m_highlight->getCommentStart(); 03506 QString endComment = m_highlight->getCommentEnd(); 03507 03508 int sl = selectStart.line(); 03509 int el = selectEnd.line(); 03510 int sc = selectStart.col(); 03511 int ec = selectEnd.col(); 03512 03513 // The selection ends on the char before selectEnd 03514 if (ec != 0) { 03515 ec--; 03516 } else { 03517 if (el > 0) { 03518 el--; 03519 ec = buffer->plainLine(el)->length() - 1; 03520 } 03521 } 03522 03523 int startCommentLen = startComment.length(); 03524 int endCommentLen = endComment.length(); 03525 03526 // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/ 03527 03528 bool remove = nextNonSpaceCharPos(sl, sc) 03529 && buffer->plainLine(sl)->stringAtPos(sc, startComment) 03530 && previousNonSpaceCharPos(el, ec) 03531 && ( (ec - endCommentLen + 1) >= 0 ) 03532 && buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment); 03533 03534 if (remove) { 03535 editStart(); 03536 03537 removeText (el, ec - endCommentLen + 1, el, ec + 1); 03538 removeText (sl, sc, sl, sc + startCommentLen); 03539 03540 editEnd (); 03541 03542 // Set the new selection 03543 ec -= endCommentLen + ( (el == sl) ? startCommentLen : 0 ); 03544 setSelection(sl, sc, el, ec + 1); 03545 } 03546 03547 return remove; 03548 } 03549 03550 /* 03551 Remove from the begining of each line of the 03552 selection a start comment line mark. 03553 */ 03554 bool KateDocument::removeStartLineCommentFromSelection() 03555 { 03556 QString shortCommentMark = m_highlight->getCommentSingleLineStart(); 03557 QString longCommentMark = shortCommentMark + " "; 03558 03559 int sl = selectStart.line(); 03560 int el = selectEnd.line(); 03561 03562 if ((selectEnd.col() == 0) && ((el-1) >= 0)) 03563 { 03564 el--; 03565 } 03566 03567 // Find out how many char will be removed from the last line 03568 int removeLength = 0; 03569 if (buffer->plainLine(el)->startingWith(longCommentMark)) 03570 removeLength = longCommentMark.length(); 03571 else if (buffer->plainLine(el)->startingWith(shortCommentMark)) 03572 removeLength = shortCommentMark.length(); 03573 03574 bool removed = false; 03575 03576 editStart(); 03577 03578 // For each line of the selection 03579 for (int z = el; z >= sl; z--) 03580 { 03581 // Try to remove the long comment mark first 03582 removed = (removeStringFromBegining(z, longCommentMark) 03583 || removeStringFromBegining(z, shortCommentMark) 03584 || removed); 03585 } 03586 03587 editEnd(); 03588 03589 if(removed) { 03590 // Set the new selection 03591 selectEnd.setCol(selectEnd.col() - ((el == selectEnd.line()) ? removeLength : 0) ); 03592 setSelection(selectStart.line(), selectStart.col(), selectEnd.line(), selectEnd.col()); 03593 } 03594 03595 return removed; 03596 } 03597 03598 /* 03599 Comment or uncomment the selection or the current 03600 line if there is no selection. 03601 */ 03602 void KateDocument::comment( KateView *, uint line, int change) 03603 { 03604 bool hasStartLineCommentMark = !(m_highlight->getCommentSingleLineStart().isEmpty()); 03605 bool hasStartStopCommentMark = ( !(m_highlight->getCommentStart().isEmpty()) 03606 && !(m_highlight->getCommentEnd().isEmpty()) ); 03607 03608 bool removed = false; 03609 03610 if (change > 0) 03611 { 03612 if ( !hasSelection() ) 03613 { 03614 if ( hasStartLineCommentMark ) 03615 addStartLineCommentToSingleLine(line); 03616 else if ( hasStartStopCommentMark ) 03617 addStartStopCommentToSingleLine(line); 03618 } 03619 else 03620 { 03621 // anders: prefer single line comment to avoid nesting probs 03622 // If the selection starts after first char in the first line 03623 // or ends before the last char of the last line, we may use 03624 // multiline comment markers. 03625 // TODO We should try to detect nesting. 03626 // - if selection ends at col 0, most likely she wanted that 03627 // line ignored 03628 if ( hasStartStopCommentMark && 03629 ( !hasStartLineCommentMark || ( 03630 ( selectStart.col() > buffer->plainLine( selectStart.line() )->firstChar() ) || 03631 ( selectEnd.col() < ((int)buffer->plainLine( selectEnd.line() )->length()) ) 03632 ) ) ) 03633 addStartStopCommentToSelection(); 03634 else if ( hasStartLineCommentMark ) 03635 addStartLineCommentToSelection(); 03636 } 03637 } 03638 else 03639 { 03640 if ( !hasSelection() ) 03641 { 03642 removed = ( hasStartLineCommentMark 03643 && removeStartLineCommentFromSingleLine(line) ) 03644 || ( hasStartStopCommentMark 03645 && removeStartStopCommentFromSingleLine(line) ); 03646 } 03647 else 03648 { 03649 // anders: this seems like it will work with above changes :) 03650 removed = ( hasStartLineCommentMark 03651 && removeStartLineCommentFromSelection() ) 03652 || ( hasStartStopCommentMark 03653 && removeStartStopCommentFromSelection() ); 03654 } 03655 } 03656 } 03657 03658 void KateDocument::transform( KateView *, const KateTextCursor &c, 03659 KateDocument::TextTransform t ) 03660 { 03661 editStart(); 03662 if ( hasSelection() ) 03663 { 03664 int ln = selStartLine(); 03665 while ( ln <= selEndLine() ) 03666 { 03667 uint start, end; 03668 start = (ln == selStartLine() || blockSelectionMode()) ? 03669 selStartCol() : 0; 03670 end = (ln == selEndLine() || blockSelectionMode()) ? 03671 selEndCol() : lineLength( ln ); 03672 QString s = text( ln, start, ln, end ); 03673 03674 if ( t == Uppercase ) 03675 s = s.upper(); 03676 else if ( t == Lowercase ) 03677 s = s.lower(); 03678 else // Capitalize 03679 { 03680 TextLine::Ptr l = buffer->plainLine( ln ); 03681 uint p ( 0 ); 03682 while( p < s.length() ) 03683 { 03684 // If bol or the character before is not in a word, up this one: 03685 // 1. if both start and p is 0, upper char. 03686 // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper 03687 // 3. if p-1 is not in a word, upper. 03688 if ( ( ! start && ! p ) || 03689 ( ( ln == selStartLine() || blockSelectionMode() ) && 03690 ! p && ! m_highlight->isInWord( l->getChar( start - 1 ) ) ) || 03691 ( p && ! m_highlight->isInWord( s.at( p-1 ) ) ) 03692 ) 03693 s[p] = s.at(p).upper(); 03694 p++; 03695 } 03696 } 03697 03698 removeText( ln, start, ln, end ); 03699 insertText( ln, start, s ); 03700 03701 ln++; 03702 } 03703 } else { // no selection 03704 QString s; 03705 uint cline(c.line() ), ccol( c.col() ); 03706 int n ( ccol ); 03707 switch ( t ) { 03708 case Uppercase: 03709 s = text( cline, ccol, cline, ccol + 1 ).upper(); 03710 break; 03711 case Lowercase: 03712 s = text( cline, ccol, cline, ccol + 1 ).lower(); 03713 break; 03714 case Capitalize: // FIXME avoid/reset cursor jump!! 03715 { 03716 TextLine::Ptr l = buffer->plainLine( cline ); 03717 while ( n > 0 && m_highlight->isInWord( l->getChar( n-1 ) ) ) 03718 n--; 03719 s = text( cline, n, cline, n + 1 ).upper(); 03720 } 03721 break; 03722 default: 03723 break; 03724 } 03725 removeText( cline, n, cline, n+1 ); 03726 insertText( cline, n, s ); 03727 } 03728 editEnd(); 03729 } 03730 03731 void KateDocument::joinLines( uint first, uint last ) 03732 { 03733 // if ( first == last ) last += 1; 03734 editStart(); 03735 int l( first ); 03736 while ( first < last ) 03737 { 03738 editUnWrapLine( l ); 03739 first++; 03740 } 03741 editEnd(); 03742 } 03743 03744 QString KateDocument::getWord( const KateTextCursor& cursor ) { 03745 int start, end, len; 03746 03747 TextLine::Ptr textLine = buffer->plainLine(cursor.line()); 03748 len = textLine->length(); 03749 start = end = cursor.col(); 03750 if (start > len) // Probably because of non-wrapping cursor mode. 03751 return QString(""); 03752 03753 while (start > 0 && m_highlight->isInWord(textLine->getChar(start - 1))) start--; 03754 while (end < len && m_highlight->isInWord(textLine->getChar(end))) end++; 03755 len = end - start; 03756 return QString(&textLine->text()[start], len); 03757 } 03758 03759 void KateDocument::tagLines(int start, int end) 03760 { 03761 for (uint z = 0; z < m_views.count(); z++) 03762 m_views.at(z)->tagLines (start, end, true); 03763 } 03764 03765 void KateDocument::tagLines(KateTextCursor start, KateTextCursor end) 03766 { 03767 // May need to switch start/end cols if in block selection mode 03768 if (blockSelectionMode() && start.col() > end.col()) { 03769 int sc = start.col(); 03770 start.setCol(end.col()); 03771 end.setCol(sc); 03772 } 03773 03774 for (uint z = 0; z < m_views.count(); z++) 03775 m_views.at(z)->tagLines(start, end, true); 03776 } 03777 03778 void KateDocument::tagSelection(const KateTextCursor &oldSelectStart, const KateTextCursor &oldSelectEnd) 03779 { 03780 if (hasSelection()) { 03781 if (oldSelectStart.line() == -1) { 03782 // We have to tag the whole lot if 03783 // 1) we have a selection, and: 03784 // a) it's new; or 03785 tagLines(selectStart, selectEnd); 03786 03787 } else if (blockSelectionMode() && (oldSelectStart.col() != selectStart.col() || oldSelectEnd.col() != selectEnd.col())) { 03788 // b) we're in block selection mode and the columns have changed 03789 tagLines(selectStart, selectEnd); 03790 tagLines(oldSelectStart, oldSelectEnd); 03791 03792 } else { 03793 if (oldSelectStart != selectStart) { 03794 if (oldSelectStart < selectStart) 03795 tagLines(oldSelectStart, selectStart); 03796 else 03797 tagLines(selectStart, oldSelectStart); 03798 } 03799 03800 if (oldSelectEnd != selectEnd) { 03801 if (oldSelectEnd < selectEnd) 03802 tagLines(oldSelectEnd, selectEnd); 03803 else 03804 tagLines(selectEnd, oldSelectEnd); 03805 } 03806 } 03807 03808 } else { 03809 // No more selection, clean up 03810 tagLines(oldSelectStart, oldSelectEnd); 03811 } 03812 } 03813 03814 void KateDocument::repaintViews(bool paintOnlyDirty) 03815 { 03816 for (uint z = 0; z < m_views.count(); z++) 03817 m_views.at(z)->repaintText(paintOnlyDirty); 03818 } 03819 03820 void KateDocument::tagAll() 03821 { 03822 for (uint z = 0; z < m_views.count(); z++) 03823 { 03824 m_views.at(z)->tagAll(); 03825 m_views.at(z)->updateView (true); 03826 } 03827 } 03828 03829 void KateDocument::slotBufferChanged() 03830 { 03831 updateViews(); 03832 } 03833 03834 void KateDocument::updateViews() 03835 { 03836 if (noViewUpdates) 03837 return; 03838 03839 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) 03840 { 03841 view->updateView(true); 03842 } 03843 } 03844 03845 uint KateDocument::configFlags () 03846 { 03847 return config()->configFlags(); 03848 } 03849 03850 void KateDocument::setConfigFlags (uint flags) 03851 { 03852 config()->setConfigFlags(flags); 03853 } 03854 03855 bool KateDocument::lineColSelected (int line, int col) 03856 { 03857 if ( (!blockSelect) && (col < 0) ) 03858 col = 0; 03859 03860 KateTextCursor cursor(line, col); 03861 03862 if (blockSelect) 03863 return cursor.line() >= selectStart.line() && cursor.line() <= selectEnd.line() && cursor.col() >= selectStart.col() && cursor.col() < selectEnd.col(); 03864 else 03865 return (cursor >= selectStart) && (cursor < selectEnd); 03866 } 03867 03868 bool KateDocument::lineSelected (int line) 03869 { 03870 return (!blockSelect) 03871 && (selectStart <= KateTextCursor(line, 0)) 03872 && (line < selectEnd.line()); 03873 } 03874 03875 bool KateDocument::lineEndSelected (int line, int endCol) 03876 { 03877 return (!blockSelect) 03878 && (line > selectStart.line() || (line == selectStart.line() && (selectStart.col() < endCol || endCol == -1))) 03879 && (line < selectEnd.line() || (line == selectEnd.line() && (endCol <= selectEnd.col() && endCol != -1))); 03880 } 03881 03882 bool KateDocument::lineHasSelected (int line) 03883 { 03884 return (selectStart < selectEnd) 03885 && (line >= selectStart.line()) 03886 && (line <= selectEnd.line()); 03887 } 03888 03889 bool KateDocument::lineIsSelection (int line) 03890 { 03891 return (line == selectStart.line() && line == selectEnd.line()); 03892 } 03893 03894 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; } 03895 inline bool isEndBracket ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; } 03896 inline bool isBracket ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); } 03897 03898 /* 03899 Bracket matching uses the following algorithm: 03900 If in overwrite mode, match the bracket currently underneath the cursor. 03901 Otherwise, if the character to the left of the cursor is an ending bracket, 03902 match it. Otherwise if the character to the right of the cursor is a 03903 starting bracket, match it. Otherwise, if the the character to the left 03904 of the cursor is an starting bracket, match it. Otherwise, if the character 03905 to the right of the cursor is an ending bracket, match it. Otherwise, don't 03906 match anything. 03907 */ 03908 void KateDocument::newBracketMark( const KateTextCursor& cursor, KateTextRange& bm ) 03909 { 03910 bm.setValid(false); 03911 03912 bm.start() = cursor; 03913 03914 if( !findMatchingBracket( bm.start(), bm.end() ) ) 03915 return; 03916 03917 bm.setValid(true); 03918 } 03919 03920 bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end ) 03921 { 03922 TextLine::Ptr textLine = buffer->plainLine( start.line() ); 03923 if( !textLine ) 03924 return false; 03925 03926 QChar right = textLine->getChar( start.col() ); 03927 QChar left = textLine->getChar( start.col() - 1 ); 03928 QChar bracket; 03929 03930 if ( config()->configFlags() & cfOvr ) { 03931 if( isBracket( right ) ) { 03932 bracket = right; 03933 } else { 03934 return false; 03935 } 03936 } else if ( isEndBracket( left ) ) { 03937 start.setCol(start.col() - 1); 03938 bracket = left; 03939 } else if ( isStartBracket( right ) ) { 03940 bracket = right; 03941 } else if ( isBracket( left ) ) { 03942 start.setCol(start.col() - 1); 03943 bracket = left; 03944 } else if ( isBracket( right ) ) { 03945 bracket = right; 03946 } else { 03947 return false; 03948 } 03949 03950 QChar opposite; 03951 03952 switch( bracket ) { 03953 case '{': opposite = '}'; break; 03954 case '}': opposite = '{'; break; 03955 case '[': opposite = ']'; break; 03956 case ']': opposite = '['; break; 03957 case '(': opposite = ')'; break; 03958 case ')': opposite = '('; break; 03959 default: return false; 03960 } 03961 03962 bool forward = isStartBracket( bracket ); 03963 int startAttr = textLine->attribute( start.col() ); 03964 uint count = 0; 03965 end = start; 03966 03967 while( true ) { 03968 /* Increment or decrement, check base cases */ 03969 if( forward ) { 03970 end.setCol(end.col() + 1); 03971 if( end.col() >= lineLength( end.line() ) ) { 03972 if( end.line() >= (int)lastLine() ) 03973 return false; 03974 end.setPos(end.line() + 1, 0); 03975 textLine = buffer->plainLine( end.line() ); 03976 } 03977 } else { 03978 end.setCol(end.col() - 1); 03979 if( end.col() < 0 ) { 03980 if( end.line() <= 0 ) 03981 return false; 03982 end.setLine(end.line() - 1); 03983 end.setCol(lineLength( end.line() ) - 1); 03984 textLine = buffer->plainLine( end.line() ); 03985 } 03986 } 03987 03988 /* Easy way to skip comments */ 03989 if( textLine->attribute( end.col() ) != startAttr ) 03990 continue; 03991 03992 /* Check for match */ 03993 QChar c = textLine->getChar( end.col() ); 03994 if( c == bracket ) { 03995 count++; 03996 } else if( c == opposite ) { 03997 if( count == 0 ) 03998 return true; 03999 count--; 04000 } 04001 04002 } 04003 } 04004 04005 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev ) 04006 { 04007 KParts::ReadWritePart::guiActivateEvent( ev ); 04008 if ( ev->activated() ) 04009 emit selectionChanged(); 04010 } 04011 04012 void KateDocument::setDocName (QString name ) 04013 { 04014 if ( !name.isEmpty() ) 04015 { 04016 // TODO check for similarly named documents 04017 m_docName = name; 04018 emit nameChanged((Kate::Document *) this); 04019 return; 04020 } 04021 04022 // if the name is set, and starts with FILENAME, it should not be changed! 04023 if ( !m_docName.isEmpty() && m_docName.startsWith( url().filename() ) ) return; 04024 04025 int count = -1; 04026 04027 for (uint z=0; z < KateFactory::self()->documents()->count(); z++) 04028 { 04029 if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) ) 04030 if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count ) 04031 count = KateFactory::self()->documents()->at(z)->m_docNameNumber; 04032 } 04033 04034 m_docNameNumber = count + 1; 04035 04036 m_docName = url().filename(); 04037 04038 if (m_docName.isEmpty()) 04039 m_docName = i18n ("Untitled"); 04040 04041 if (m_docNameNumber > 0) 04042 m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1); 04043 04044 emit nameChanged ((Kate::Document *) this); 04045 } 04046 04047 void KateDocument::isModOnHD(bool ) 04048 { 04049 if (m_modOnHd && !url().isEmpty()) 04050 { 04051 reloadFile(); 04052 } 04053 } 04054 04055 class KateDocumentTmpMark 04056 { 04057 public: 04058 QString line; 04059 KTextEditor::Mark mark; 04060 }; 04061 04062 void KateDocument::reloadFile() 04063 { 04064 if ( !url().isEmpty() ) 04065 { 04066 if (m_modOnHd) 04067 { 04068 QString str; 04069 04070 if (m_modOnHdReason == 1) 04071 str = i18n("The file %1 was changed (modified) on disc by another program!\n\n").arg(url().fileName()); 04072 else if (m_modOnHdReason == 2) 04073 str = i18n("The file %1 was changed (created) on disc by another program!\n\n").arg(url().fileName()); 04074 else if (m_modOnHdReason == 3) 04075 str = i18n("The file %1 was changed (deleted) on disc by another program!\n\n").arg(url().fileName()); 04076 04077 int i = KMessageBox::warningYesNoCancel 04078 (0, str + i18n("Do you really want to reload the modified file? Data loss may occur.")); 04079 if ( i != KMessageBox::Yes) 04080 { 04081 if (i == KMessageBox::No) 04082 { 04083 m_modOnHd = false; 04084 m_modOnHdReason = 0; 04085 emit modifiedOnDisc (this, m_modOnHd, 0); 04086 } 04087 04088 return; 04089 } 04090 } 04091 QValueList<KateDocumentTmpMark> tmp; 04092 04093 for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it ) 04094 { 04095 KateDocumentTmpMark m; 04096 04097 m.line = buffer->textLine (it.current()->line); 04098 m.mark = *it.current(); 04099 04100 tmp.append (m); 04101 } 04102 04103 uint mode = hlMode (); 04104 bool byUser = hlSetByUser; 04105 04106 m_reloading = true; 04107 KateDocument::openURL( url() ); 04108 m_reloading = false; 04109 04110 for (uint z=0; z < tmp.size(); z++) 04111 { 04112 if (z < numLines()) 04113 { 04114 if (buffer->textLine(tmp[z].mark.line) == tmp[z].line) 04115 setMark (tmp[z].mark.line, tmp[z].mark.type); 04116 } 04117 } 04118 04119 if (byUser) 04120 setHlMode (mode); 04121 } 04122 } 04123 04124 void KateDocument::flush () 04125 { 04126 closeURL (); 04127 } 04128 04129 void KateDocument::setWordWrap (bool on) 04130 { 04131 config()->setWordWrap (on); 04132 } 04133 04134 bool KateDocument::wordWrap () 04135 { 04136 return config()->wordWrap (); 04137 } 04138 04139 void KateDocument::setWordWrapAt (uint col) 04140 { 04141 config()->setWordWrapAt (col); 04142 } 04143 04144 unsigned int KateDocument::wordWrapAt () 04145 { 04146 return config()->wordWrapAt (); 04147 } 04148 04149 void KateDocument::applyWordWrap () 04150 { 04151 if (hasSelection()) 04152 wrapText (selectStart.line(), selectEnd.line()); 04153 else 04154 wrapText (0, lastLine()); 04155 } 04156 04157 void KateDocument::setPageUpDownMovesCursor (bool on) 04158 { 04159 config()->setPageUpDownMovesCursor (on); 04160 } 04161 04162 bool KateDocument::pageUpDownMovesCursor () 04163 { 04164 return config()->pageUpDownMovesCursor (); 04165 } 04166 04167 void KateDocument::exportAs(const QString& filter) 04168 { 04169 if (filter=="kate_html_export") 04170 { 04171 QString filename=KFileDialog::getSaveFileName(QString::null,"text/html",0,i18n("Export File As")); 04172 if (filename.isEmpty()) 04173 { 04174 return; 04175 } 04176 KSaveFile *savefile=new KSaveFile(filename); 04177 if (!savefile->status()) 04178 { 04179 if (exportDocumentToHTML(savefile->textStream(),filename)) savefile->close(); 04180 else savefile->abort(); 04181 //if (!savefile->status()) --> Error 04182 } else {/*ERROR*/} 04183 delete savefile; 04184 } 04185 } 04186 04187 /* For now, this should become an plugin */ 04188 bool KateDocument::exportDocumentToHTML(QTextStream *outputStream,const QString &name) 04189 { 04190 outputStream->setEncoding(QTextStream::UnicodeUTF8); 04191 // let's write the HTML header : 04192 (*outputStream) << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl; 04193 (*outputStream) << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">" << endl; 04194 (*outputStream) << "<html xmlns=\"http://www.w3.org/1999/xhtml\">" << endl; 04195 (*outputStream) << "<head>" << endl; 04196 (*outputStream) << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />" << endl; 04197 (*outputStream) << "<meta name=\"Generator\" content=\"Kate, the KDE Advanced Text Editor\" />" << endl; 04198 // for the title, we write the name of the file (/usr/local/emmanuel/myfile.cpp -> myfile.cpp) 04199 (*outputStream) << "<title>" << name.right(name.length() - name.findRev('/') -1) << "</title>" << endl; 04200 (*outputStream) << "</head>" << endl; 04201 04202 (*outputStream) << "<body><pre>" << endl; 04203 // for each line : 04204 04205 // some variables : 04206 bool previousCharacterWasBold = false; 04207 bool previousCharacterWasItalic = false; 04208 // when entering a new color, we'll close all the <b> & <i> tags, 04209 // for HTML compliancy. that means right after that font tag, we'll 04210 // need to reinitialize the <b> and <i> tags. 04211 bool needToReinitializeTags = false; 04212 QColor previousCharacterColor(0,0,0); // default color of HTML characters is black 04213 (*outputStream) << "<span style='color: #000000'>"; 04214 04215 for (uint curLine=0;curLine<numLines();curLine++) 04216 { // html-export that line : 04217 TextLine::Ptr textLine = buffer->plainLine(curLine); 04218 //ASSERT(textLine != NULL); 04219 // for each character of the line : (curPos is the position in the line) 04220 for (uint curPos=0;curPos<textLine->length();curPos++) 04221 { 04222 // atm hardcode default schema, later add selector to the exportAs methode :) 04223 QMemArray<KateAttribute> *attributes = m_highlight->attributes (0); 04224 KateAttribute* charAttributes = 0; 04225 04226 if (textLine->attribute(curPos) < attributes->size()) 04227 charAttributes = &attributes->at(textLine->attribute(curPos)); 04228 else 04229 charAttributes = &attributes->at(0); 04230 04231 //ASSERT(charAttributes != NULL); 04232 // let's give the color for that character : 04233 if ( (charAttributes->textColor() != previousCharacterColor)) 04234 { // the new character has a different color : 04235 // if we were in a bold or italic section, close it 04236 if (previousCharacterWasBold) 04237 (*outputStream) << "</b>"; 04238 if (previousCharacterWasItalic) 04239 (*outputStream) << "</i>"; 04240 04241 // close the previous font tag : 04242 (*outputStream) << "</span>"; 04243 // let's read that color : 04244 int red, green, blue; 04245 // getting the red, green, blue values of the color : 04246 charAttributes->textColor().rgb(&red, &green, &blue); 04247 (*outputStream) << "<span style='color: #" 04248 << ( (red < 0x10)?"0":"") // need to put 0f, NOT f for instance. don't touch 1f. 04249 << QString::number(red, 16) // html wants the hex value here (hence the 16) 04250 << ( (green < 0x10)?"0":"") 04251 << QString::number(green, 16) 04252 << ( (blue < 0x10)?"0":"") 04253 << QString::number(blue, 16) 04254 << "'>"; 04255 // we need to reinitialize the bold/italic status, since we closed all the tags 04256 needToReinitializeTags = true; 04257 } 04258 // bold status : 04259 if ( (needToReinitializeTags && charAttributes->bold()) || 04260 (!previousCharacterWasBold && charAttributes->bold()) ) 04261 // we enter a bold section 04262 (*outputStream) << "<b>"; 04263 if ( !needToReinitializeTags && (previousCharacterWasBold && !charAttributes->bold()) ) 04264 // we leave a bold section 04265 (*outputStream) << "</b>"; 04266 04267 // italic status : 04268 if ( (needToReinitializeTags && charAttributes->italic()) || 04269 (!previousCharacterWasItalic && charAttributes->italic()) ) 04270 // we enter an italic section 04271 (*outputStream) << "<i>"; 04272 if ( !needToReinitializeTags && (previousCharacterWasItalic && !charAttributes->italic()) ) 04273 // we leave an italic section 04274 (*outputStream) << "</i>"; 04275 04276 // write the actual character : 04277 (*outputStream) << HTMLEncode(textLine->getChar(curPos)); 04278 04279 // save status for the next character : 04280 previousCharacterWasItalic = charAttributes->italic(); 04281 previousCharacterWasBold = charAttributes->bold(); 04282 previousCharacterColor = charAttributes->textColor(); 04283 needToReinitializeTags = false; 04284 } 04285 // finish the line : 04286 (*outputStream) << endl; 04287 } 04288 04289 // Be good citizens and close our tags 04290 if (previousCharacterWasBold) 04291 (*outputStream) << "</b>"; 04292 if (previousCharacterWasItalic) 04293 (*outputStream) << "</i>"; 04294 04295 // HTML document end : 04296 (*outputStream) << "</span>"; // i'm guaranteed a span is started (i started one at the beginning of the output). 04297 (*outputStream) << "</pre></body>"; 04298 (*outputStream) << "</html>"; 04299 // close the file : 04300 return true; 04301 } 04302 04303 QString KateDocument::HTMLEncode(QChar theChar) 04304 { 04305 switch (theChar.latin1()) 04306 { 04307 case '>': 04308 return QString("&gt;"); 04309 case '<': 04310 return QString("&lt;"); 04311 case '&': 04312 return QString("&amp;"); 04313 }; 04314 return theChar; 04315 } 04316 04317 Kate::ConfigPage *KateDocument::colorConfigPage (QWidget *p) 04318 { 04319 return (Kate::ConfigPage*) new KateSchemaConfigPage (p); 04320 } 04321 04322 Kate::ConfigPage *KateDocument::viewDefaultsConfigPage (QWidget *p) 04323 { 04324 return (Kate::ConfigPage*) new ViewDefaultsConfig(p); 04325 } 04326 04327 Kate::ConfigPage *KateDocument::fontConfigPage (QWidget *p) 04328 { 04329 return (Kate::ConfigPage*) new KateSchemaConfigPage (p); 04330 } 04331 04332 Kate::ConfigPage *KateDocument::indentConfigPage (QWidget *p) 04333 { 04334 return (Kate::ConfigPage*) new IndentConfigTab(p); 04335 } 04336 04337 Kate::ConfigPage *KateDocument::selectConfigPage (QWidget *p) 04338 { 04339 return (Kate::ConfigPage*) new SelectConfigTab(p); 04340 } 04341 04342 Kate::ConfigPage *KateDocument::editConfigPage (QWidget *p) 04343 { 04344 return (Kate::ConfigPage*) new EditConfigTab(p); 04345 } 04346 04347 Kate::ConfigPage *KateDocument::keysConfigPage (QWidget *p) 04348 { 04349 return (Kate::ConfigPage*) new EditKeyConfiguration(p, this); 04350 } 04351 04352 Kate::ConfigPage *KateDocument::hlConfigPage (QWidget *p) 04353 { 04354 return (Kate::ConfigPage*) new HlConfigPage (p); 04355 } 04356 04357 Kate::ConfigPage *KateDocument::saveConfigPage(QWidget *p) 04358 { 04359 return (Kate::ConfigPage*) new SaveConfigTab(p); 04360 } 04361 04362 Kate::ActionMenu *KateDocument::hlActionMenu (const QString& text, QObject* parent, const char* name) 04363 { 04364 KateViewHighlightAction *menu = new KateViewHighlightAction (text, parent, name); 04365 menu->setWhatsThis(i18n("Here you can choose how the current document should be highlighted.")); 04366 menu->updateMenu (this); 04367 04368 return (Kate::ActionMenu *)menu; 04369 } 04370 04371 Kate::ActionMenu *KateDocument::exportActionMenu (const QString& text, QObject* parent, const char* name) 04372 { 04373 KateExportAction *menu = new KateExportAction (text, parent, name); 04374 menu->updateMenu (this); 04375 menu->setWhatsThis(i18n("This command allows you to export the current document" 04376 " with all highlighting information into a markup document, e.g. HTML.")); 04377 return (Kate::ActionMenu *)menu; 04378 } 04379 04380 void KateDocument::dumpRegionTree() 04381 { 04382 buffer->dumpRegionTree(); 04383 } 04384 04385 unsigned int KateDocument::getRealLine(unsigned int virtualLine) 04386 { 04387 return buffer->lineNumber (virtualLine); 04388 } 04389 04390 unsigned int KateDocument::getVirtualLine(unsigned int realLine) 04391 { 04392 return buffer->lineVisibleNumber (realLine); 04393 } 04394 04395 unsigned int KateDocument::visibleLines () 04396 { 04397 return buffer->countVisible (); 04398 } 04399 04400 TextLine::Ptr KateDocument::kateTextLine(uint i) 04401 { 04402 return buffer->line (i); 04403 } 04404 04405 TextLine::Ptr KateDocument::plainKateTextLine(uint i) 04406 { 04407 return buffer->plainLine (i); 04408 } 04409 //END 04410 04411 //BEGIN KTextEditor::CursorInterface stuff 04412 04413 KTextEditor::Cursor *KateDocument::createCursor ( ) 04414 { 04415 return new KateSuperCursor (this, false, 0, 0, this); 04416 } 04417 04418 void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range) 04419 { 04420 if (view) 04421 view->tagLines(range->start(), range->end()); 04422 else 04423 tagLines(range->start(), range->end()); 04424 } 04425 04426 // 04427 // Spellchecking IN again 04428 // 04429 void KateDocument::spellcheck() 04430 { 04431 if( !isReadWrite() || text().isEmpty()) 04432 return; 04433 04434 m_kspell = new KSpell( 0, i18n("Spellcheck"), 04435 this, SLOT(ready(KSpell *)) ); 04436 04437 connect( m_kspell, SIGNAL(death()), 04438 this, SLOT(spellCleanDone()) ); 04439 04440 connect( m_kspell, SIGNAL(misspelling(const QString&, const QStringList&, unsigned int)), 04441 this, SLOT(misspelling(const QString&, const QStringList&, unsigned int)) ); 04442 connect( m_kspell, SIGNAL(corrected(const QString&, const QString&, unsigned int)), 04443 this, SLOT(corrected(const QString&, const QString&, unsigned int)) ); 04444 connect( m_kspell, SIGNAL(done(const QString&)), 04445 this, SLOT(spellResult(const QString&)) ); 04446 } 04447 04448 void KateDocument::ready(KSpell *) 04449 { 04450 m_mispellCount = 0; 04451 m_replaceCount = 0; 04452 04453 m_kspell->setProgressResolution( 1 ); 04454 04455 m_kspell->check( text() ); 04456 04457 kdDebug () << "SPELLING READY STATUS: " << m_kspell->status () << endl; 04458 } 04459 04460 void KateDocument::locatePosition( uint pos, uint& line, uint& col ) 04461 { 04462 uint cnt = 0; 04463 04464 line = col = 0; 04465 04466 // Find pos -- CHANGEME: store the last found pos's cursor 04467 // and do these searched relative to that to 04468 // (significantly) increase the speed of the spellcheck 04469 for( ; line < numLines() && cnt <= pos; line++ ) 04470 cnt += lineLength(line) + 1; 04471 04472 line--; 04473 col = pos - (cnt - lineLength(line)) + 1; 04474 } 04475 04476 void KateDocument::misspelling( const QString& origword, const QStringList&, unsigned int pos ) 04477 { 04478 m_mispellCount++; 04479 04480 uint line, col; 04481 04482 locatePosition( pos, line, col ); 04483 04484 if (activeView()) 04485 activeView()->setCursorPositionInternal (line, col, 1); 04486 04487 setSelection( line, col, line, col + origword.length() ); 04488 } 04489 04490 void KateDocument::corrected( const QString& originalword, const QString& newword, unsigned int pos ) 04491 { 04492 m_replaceCount++; 04493 04494 uint line, col; 04495 04496 locatePosition( pos, line, col ); 04497 04498 removeText( line, col, line, col + originalword.length() ); 04499 insertText( line, col, newword ); 04500 } 04501 04502 void KateDocument::spellResult( const QString& ) 04503 { 04504 clearSelection(); 04505 m_kspell->cleanUp(); 04506 } 04507 04508 void KateDocument::spellCleanDone() 04509 { 04510 KSpell::spellStatus status = m_kspell->status(); 04511 04512 if( status == KSpell::Error ) { 04513 KMessageBox::sorry( 0, 04514 i18n("ISpell could not be started. " 04515 "Please make sure you have ISpell " 04516 "properly configured and in your PATH.")); 04517 } else if( status == KSpell::Crashed ) { 04518 KMessageBox::sorry( 0, 04519 i18n("ISpell seems to have crashed.")); 04520 } 04521 04522 delete m_kspell; 04523 m_kspell = 0; 04524 04525 kdDebug () << "SPELLING END" << endl; 04526 } 04527 //END 04528 04529 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line) 04530 { 04531 buffer->lineInfo(info,line); 04532 } 04533 04534 KateCodeFoldingTree *KateDocument::foldingTree () 04535 { 04536 return buffer->foldingTree(); 04537 } 04538 04539 void KateDocument::setEncoding (const QString &e) 04540 { 04541 m_config->setEncoding(e); 04542 } 04543 04544 QString KateDocument::encoding() const 04545 { 04546 return m_config->encoding(); 04547 } 04548 04549 void KateDocument::updateConfig () 04550 { 04551 emit undoChanged (); 04552 tagAll(); 04553 04554 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) 04555 { 04556 view->updateDocumentConfig (); 04557 } 04558 04559 // switch indenter if needed 04560 if (m_indenter->modeNumber() != m_config->indentationMode()) 04561 { 04562 delete m_indenter; 04563 m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() ); 04564 } 04565 04566 m_indenter->updateConfig(); 04567 04568 buffer->setTabWidth (config()->tabWidth()); 04569 04570 // plugins 04571 for (uint i=0; i<KateFactory::self()->plugins().count(); i++) 04572 { 04573 if (config()->plugin (i)) 04574 loadPlugin (i); 04575 else 04576 unloadPlugin (i); 04577 } 04578 } 04579 04580 //BEGIN Variable reader 04581 // "local variable" feature by anders, 2003 04582 /* TODO 04583 add config options (how many lines to read, on/off) 04584 add interface for plugins/apps to set/get variables 04585 add view stuff 04586 */ 04587 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)"); 04588 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)"); 04589 04590 void KateDocument::readVariables(bool onlyViewAndRenderer) 04591 { 04592 if (!onlyViewAndRenderer) 04593 m_config->configStart(); 04594 04595 // views! 04596 KateView *v; 04597 for (v = m_views.first(); v != 0L; v= m_views.next() ) 04598 { 04599 v->config()->configStart(); 04600 v->renderer()->config()->configStart(); 04601 } 04602 // read a number of lines in the top/bottom of the document 04603 for (uint i=0; i < QMIN( 9, numLines() ); ++i ) 04604 { 04605 readVariableLine( textLine( i ), onlyViewAndRenderer ); 04606 } 04607 if ( numLines() > 10 ) 04608 { 04609 for ( uint i = QMAX(10, numLines() - 10); i < numLines(); ++i ) 04610 { 04611 readVariableLine( textLine( i ), onlyViewAndRenderer ); 04612 } 04613 } 04614 04615 if (!onlyViewAndRenderer) 04616 m_config->configEnd(); 04617 04618 for (v = m_views.first(); v != 0L; v= m_views.next() ) 04619 { 04620 v->config()->configEnd(); 04621 v->renderer()->config()->configEnd(); 04622 } 04623 } 04624 04625 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer ) 04626 { 04627 if ( kvLine.search( t ) > -1 ) 04628 { 04629 QStringList vvl; // view variable names 04630 vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators" 04631 << "line-numbers" << "icon-border" << "folding-markers" 04632 << "bookmark-sorting" << "auto-center-lines" 04633 << "icon-bar-color" 04634 // renderer 04635 << "background-color" << "selection-color" 04636 << "current-line-color" << "bracket-highlight-color" 04637 << "word-wrap-marker-color" 04638 << "font" << "font-size" << "scheme"; 04639 int p( 0 ); 04640 QString s = kvLine.cap(1); 04641 QString var, val; 04642 while ( (p = kvVar.search( s, p )) > -1 ) 04643 { 04644 p += kvVar.matchedLength(); 04645 var = kvVar.cap( 1 ); 04646 val = kvVar.cap( 2 ).stripWhiteSpace(); 04647 bool state; // store booleans here 04648 int n; // store ints here 04649 04650 // only apply view & renderer config stuff 04651 if (onlyViewAndRenderer) 04652 { 04653 if ( vvl.contains( var ) ) // FIXME define above 04654 setViewVariable( var, val ); 04655 } 04656 else 04657 { 04658 // BOOL SETTINGS 04659 if ( var == "word-wrap" && checkBoolValue( val, &state ) ) 04660 setWordWrap( state ); // ??? FIXME CHECK 04661 else if ( var == "block-selection" && checkBoolValue( val, &state ) ) 04662 setBlockSelectionMode( state ); 04663 // KateConfig::configFlags 04664 // FIXME should this be optimized to only a few calls? how? 04665 else if ( var == "auto-indent" && checkBoolValue( val, &state ) ) 04666 m_config->setConfigFlags( KateDocumentConfig::cfAutoIndent, state ); 04667 else if ( var == "backspace-indents" && checkBoolValue( val, &state ) ) 04668 m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state ); 04669 else if ( var == "replace-tabs" && checkBoolValue( val, &state ) ) 04670 m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabs, state ); 04671 else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) ) 04672 m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state ); 04673 else if ( var == "auto-brackets" && checkBoolValue( val, &state ) ) 04674 m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state ); 04675 else if ( var == "persistent-selection" && checkBoolValue( val, &state ) ) 04676 m_config->setConfigFlags( KateDocumentConfig::cfPersistent, state ); 04677 else if ( var == "keep-selection" && checkBoolValue( val, &state ) ) 04678 m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state ); 04679 else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) ) 04680 m_config->setConfigFlags( KateDocumentConfig::cfOvr, state ); 04681 else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) ) 04682 m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state ); 04683 else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) ) 04684 m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state ); 04685 else if ( var == "tab-indents" && checkBoolValue( val, &state ) ) 04686 m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state ); 04687 else if ( var == "show-tabs" && checkBoolValue( val, &state ) ) 04688 m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state ); 04689 else if ( var == "space-indent" && checkBoolValue( val, &state ) ) 04690 m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state ); 04691 else if ( var == "smart-home" && checkBoolValue( val, &state ) ) 04692 m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state ); 04693 04694 // INTEGER SETTINGS 04695 else if ( var == "tab-width" && checkIntValue( val, &n ) ) 04696 m_config->setTabWidth( n ); 04697 else if ( var == "indent-width" && checkIntValue( val, &n ) ) 04698 m_config->setIndentationWidth( n ); 04699 else if ( var == "indent-mode" ) 04700 { 04701 if ( checkIntValue( val, &n ) ) 04702 m_config->setIndentationMode( n ); 04703 else 04704 m_config->setIndentationMode( KateAutoIndent::modeNumber( val) ); 04705 } 04706 else if ( var == "word-wrap-column" && n > 0 && checkIntValue( val, &n ) ) // uint, but hard word wrap at 0 will be no fun ;) 04707 m_config->setWordWrapAt( n ); 04708 else if ( var == "undo-steps" && n >= 0 && checkIntValue( val, &n ) ) 04709 setUndoSteps( n ); 04710 04711 // STRING SETTINGS 04712 else if ( var == "eol" || var == "end-of-line" ) 04713 { 04714 QStringList l; 04715 l << "unix" << "dos" << "mac"; 04716 if ( (n = l.findIndex( val.lower() )) != -1 ) 04717 m_config->setEol( n ); 04718 } 04719 else if ( var == "encoding" ) 04720 m_config->setEncoding( val ); 04721 else if ( var == "syntax" || var == "hl" ) 04722 { 04723 for ( uint i=0; i < hlModeCount(); i++ ) 04724 { 04725 if ( hlModeName( i ) == val ) 04726 { 04727 setHlMode( i ); 04728 break; 04729 } 04730 } 04731 } 04732 04733 // VIEW SETTINGS 04734 else if ( vvl.contains( var ) ) // FIXME define above 04735 setViewVariable( var, val ); 04736 } 04737 } 04738 } 04739 } 04740 04741 void KateDocument::setViewVariable( QString var, QString val ) 04742 { 04743 //TODO 04744 KateView *v; 04745 bool state; 04746 int n; 04747 QColor c; 04748 for (v = m_views.first(); v != 0L; v= m_views.next() ) 04749 { 04750 if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) ) 04751 v->config()->setDynWordWrap( state ); 04752 //else if ( var = "dynamic-word-wrap-indicators" ) 04753 //TODO 04754 else if ( var == "line-numbers" && checkBoolValue( val, &state ) ) 04755 v->config()->setLineNumbers( state ); 04756 else if (var == "icon-border" && checkBoolValue( val, &state ) ) 04757 v->config()->setIconBar( state ); 04758 else if (var == "folding-markers" && checkBoolValue( val, &state ) ) 04759 v->config()->setFoldingBar( state ); 04760 else if ( var == "auto-center-lines" && checkIntValue( val, &n ) ) 04761 v->config()->setAutoCenterLines( n ); // FIXME uint, > N ?? 04762 else if ( var == "icon-bar-color" && checkColorValue( val, c ) ) 04763 v->renderer()->config()->setIconBarColor( c ); 04764 // RENDERER 04765 else if ( var == "background-color" && checkColorValue( val, c ) ) 04766 v->renderer()->config()->setBackgroundColor( c ); 04767 else if ( var == "selection-color" && checkColorValue( val, c ) ) 04768 v->renderer()->config()->setSelectionColor( c ); 04769 else if ( var == "current-line-color" && checkColorValue( val, c ) ) 04770 v->renderer()->config()->setHighlightedLineColor( c ); 04771 else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) ) 04772 v->renderer()->config()->setHighlightedBracketColor( c ); 04773 else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) ) 04774 v->renderer()->config()->setWordWrapMarkerColor( c ); 04775 else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) ) 04776 { 04777 QFont _f( *v->renderer()->config()->font( ) ); 04778 04779 if ( var == "font" ) 04780 { 04781 _f.setFamily( val ); 04782 _f.setFixedPitch( QFont( val ).fixedPitch() ); 04783 } 04784 else 04785 _f.setPointSize( n ); 04786 04787 v->renderer()->config()->setFont( _f ); 04788 } 04789 else if ( var == "scheme" ) 04790 { 04791 v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) ); 04792 } 04793 } 04794 } 04795 04796 bool KateDocument::checkBoolValue( QString val, bool *result ) 04797 { 04798 val = val.stripWhiteSpace().lower(); 04799 QStringList l; 04800 l << "1" << "on" << "true"; 04801 if ( l.contains( val ) ) 04802 { 04803 *result = true; 04804 return true; 04805 } 04806 l.clear(); 04807 l << "0" << "off" << "false"; 04808 if ( l.contains( val ) ) 04809 { 04810 *result = false; 04811 return true; 04812 } 04813 return false; 04814 } 04815 04816 bool KateDocument::checkIntValue( QString val, int *result ) 04817 { 04818 bool ret( false ); 04819 *result = val.toInt( &ret ); 04820 return ret; 04821 } 04822 04823 bool KateDocument::checkColorValue( QString val, QColor &c ) 04824 { 04825 c.setNamedColor( val ); 04826 return c.isValid(); 04827 } 04828 04829 //END 04830 04831 void KateDocument::slotModOnHdDirty (const QString &path) 04832 { 04833 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1)) 04834 { 04835 // compare md5 with the one we have (if we have one) 04836 if ( ! m_digest.isEmpty() ) 04837 { 04838 QCString tmp; 04839 if ( createDigest( tmp ) && tmp == m_digest ) 04840 return; 04841 } 04842 m_modOnHd = true; 04843 m_modOnHdReason = 1; 04844 emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason); 04845 } 04846 } 04847 04848 void KateDocument::slotModOnHdCreated (const QString &path) 04849 { 04850 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2)) 04851 { 04852 m_modOnHd = true; 04853 m_modOnHdReason = 2; 04854 emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason); 04855 } 04856 } 04857 04858 void KateDocument::slotModOnHdDeleted (const QString &path) 04859 { 04860 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3)) 04861 { 04862 m_modOnHd = true; 04863 m_modOnHdReason = 3; 04864 emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason); 04865 } 04866 } 04867 04868 bool KateDocument::createDigest( QCString &result ) 04869 { 04870 bool ret = false; 04871 result = ""; 04872 if ( url().isLocalFile() ) 04873 { 04874 QFile f ( url().path() ); 04875 if ( f.open( IO_ReadOnly) ) 04876 { 04877 KMD5 md5; 04878 ret = md5.update( f ); 04879 md5.hexDigest( result ); 04880 f.close(); 04881 } 04882 } 04883 return ret; 04884 } 04885 04886 bool KateDocument::wrapCursor () 04887 { 04888 return !blockSelect && (configFlags() & KateDocument::cfWrapCursor); 04889 } 04890 04891 void KateDocument::updateFileType (int newType, bool user) 04892 { 04893 if (user || !m_fileTypeSetByUser) 04894 { 04895 const KateFileType *t = 0; 04896 if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType))) 04897 { 04898 m_fileType = newType; 04899 04900 if (t) 04901 { 04902 m_config->configStart(); 04903 // views! 04904 KateView *v; 04905 for (v = m_views.first(); v != 0L; v= m_views.next() ) 04906 { 04907 v->config()->configStart(); 04908 v->renderer()->config()->configStart(); 04909 } 04910 04911 readVariableLine( t->varLine ); 04912 04913 m_config->configEnd(); 04914 for (v = m_views.first(); v != 0L; v= m_views.next() ) 04915 { 04916 v->config()->configEnd(); 04917 v->renderer()->config()->configEnd(); 04918 } 04919 } 04920 } 04921 } 04922 } 04923 04924 uint KateDocument::documentNumber () const 04925 { 04926 return KTextEditor::Document::documentNumber (); 04927 } 04928 04929 04930 04931 04932 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) { 04933 *handled=true; 04934 *abortClosing=true; 04935 if (m_url.isEmpty()) 04936 { 04937 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(), 04938 QString::null,QString::null,0,i18n("Save File")); 04939 04940 if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) { 04941 *abortClosing=true; 04942 return; 04943 } 04944 setEncoding( res.encoding ); 04945 saveAs( res.URLs.first() ); 04946 *abortClosing=false; 04947 } 04948 else 04949 { 04950 save(); 04951 *abortClosing=false; 04952 } 04953 04954 } 04955 04956 04957 bool KateDocument::checkOverwrite( KURL u ) 04958 { 04959 if( !u.isLocalFile() ) 04960 return true; 04961 04962 QFileInfo info( u.path() ); 04963 if( !info.exists() ) 04964 return true; 04965 04966 return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0, 04967 i18n( "A file named \"%1\" already exists. " 04968 "Are you sure you want to overwrite it?" ).arg( info.fileName() ), 04969 i18n( "Overwrite File?" ), 04970 i18n( "&Overwrite" ) ); 04971 } 04972 04973 void KateDocument::setDefaultEncoding (const QString &encoding) 04974 { 04975 s_defaultEncoding = encoding; 04976 } 04977 04978 void KateDocument::setIMSelectionValue( uint imStartLine, uint imStart, uint imEnd, 04979 uint imSelStart, uint imSelEnd, bool imComposeEvent ) 04980 { 04981 m_imStartLine = imStartLine; 04982 m_imStart = imStart; 04983 m_imEnd = imEnd; 04984 m_imSelStart = imSelStart; 04985 m_imSelEnd = imSelEnd; 04986 m_imComposeEvent = imComposeEvent; 04987 } 04988 04989 void KateDocument::getIMSelectionValue( uint *imStartLine, uint *imStart, uint *imEnd, 04990 uint *imSelStart, uint *imSelEnd ) 04991 { 04992 *imStartLine = m_imStartLine; 04993 *imStart = m_imStart; 04994 *imEnd = m_imEnd; 04995 *imSelStart = m_imSelStart; 04996 *imSelEnd = m_imSelEnd; 04997 } 04998 04999 // 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 Sat Jun 12 15:10:10 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003