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