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 <qtimer.h> 00074 #include <qfile.h> 00075 #include <qclipboard.h> 00076 #include <qtextstream.h> 00077 #include <qtextcodec.h> 00078 #include <qmap.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, QWidget *parentWidget, 00095 const char *widgetName, QObject *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 QTimer(this); 00174 connect(m_undoMergeTimer, SIGNAL(timeout()), 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, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int))); 00193 connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated())); 00194 00195 // if the user changes the highlight with the dialog, notify the doc 00196 connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged())); 00197 00198 // signal for the arbitrary HL 00199 connect(m_arbitraryHL, SIGNAL(tagLines(KateView*, KateSuperRange*)), SLOT(tagArbitraryLines(KateView*, KateSuperRange*))); 00200 00201 // signals for mod on hd 00202 connect( KateFactory::self()->dirWatch(), SIGNAL(dirty (const QString &)), 00203 this, SLOT(slotModOnHdDirty (const QString &)) ); 00204 00205 connect( KateFactory::self()->dirWatch(), SIGNAL(created (const QString &)), 00206 this, SLOT(slotModOnHdCreated (const QString &)) ); 00207 00208 connect( KateFactory::self()->dirWatch(), SIGNAL(deleted (const QString &)), 00209 this, SLOT(slotModOnHdDeleted (const QString &)) ); 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,SIGNAL(sigQueryClose(bool *, bool*)),this,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 (QFile::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( QWidget *parent, const char *name ) 00357 { 00358 KateView* newView = new KateView( this, parent, name); 00359 connect(newView, SIGNAL(cursorPositionChanged()), SLOT(undoCancel())); 00360 if ( s_fileChangedDialogsActivated ) 00361 connect( newView, SIGNAL(gotFocus( Kate::View * )), this, SLOT(slotModifiedOnDisk()) ); 00362 return newView; 00363 } 00364 00365 QPtrList<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, QWidget *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 QString 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 QString (""); 00462 } 00463 00464 return QString (""); 00465 } 00466 00467 QString 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 QString (""); 00503 } 00504 00505 return QString (""); 00506 } 00507 00508 QPixmap 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 QString KateDocument::text() const 00553 { 00554 QString 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 QString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol ) const 00573 { 00574 return text(startLine, startCol, endLine, endCol, false); 00575 } 00576 00577 QString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise) const 00578 { 00579 if ( blockwise && (startCol > endCol) ) 00580 return QString (); 00581 00582 QString s; 00583 00584 if (startLine == endLine) 00585 { 00586 if (startCol > endCol) 00587 return QString (); 00588 00589 KateTextLine::Ptr textLine = m_buffer->plainLine(startLine); 00590 00591 if ( !textLine ) 00592 return QString (); 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 QString KateDocument::textLine( uint line ) const 00626 { 00627 KateTextLine::Ptr l = m_buffer->plainLine(line); 00628 00629 if (!l) 00630 return QString(); 00631 00632 return l->string(); 00633 } 00634 00635 bool KateDocument::setText(const QString &s) 00636 { 00637 if (!isReadWrite()) 00638 return false; 00639 00640 QPtrList<KTextEditor::Mark> m = marks (); 00641 QValueList<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 QString &s) 00679 { 00680 return insertText (line, col, s, false); 00681 } 00682 00683 bool KateDocument::insertText( uint line, uint col, const QString &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 QString 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 QChar 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 QString &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 QChar *text = l->text(); 01068 uint eolPosition = l->length()-1; 01069 01070 // take tabs into account here, too 01071 uint x = 0; 01072 const QString & t = l->string(); 01073 uint z2 = 0; 01074 for ( ; z2 < l->length(); z2++) 01075 { 01076 if (t[z2] == QChar('\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, QString (" ")); 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 QString &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 QString &str ) 01163 { 01164 if (!isReadWrite()) 01165 return false; 01166 01167 QString 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, QString().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( QPtrListIterator<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( QPtrListIterator<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, QString::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 QPtrList<KTextEditor::Mark> list; 01285 for( QIntDictIterator<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( QPtrListIterator<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( QPtrListIterator<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 QPtrList<KTextEditor::Mark> list; 01364 for( QIntDictIterator<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( QPtrListIterator<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( QPtrListIterator<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 QString &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 QPtrList<KTextEditor::Mark> list; 01420 for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it ) 01421 { 01422 if( it.current()->line >= line ) 01423 list.append( it.current() ); 01424 } 01425 01426 for( QPtrListIterator<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( QPtrListIterator<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 QPtrList<KTextEditor::Mark> list; 01462 KTextEditor::Mark* rmark = 0; 01463 for( QIntDictIterator<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( QPtrListIterator<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( QPtrListIterator<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 QPtrList<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 QString &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 QRegExp ®exp, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool backwards) 01736 { 01737 kdDebug(13020)<<"KateDocument::searchText( "<<startLine<<", "<<startCol<<", "<<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 QString KateDocument::hlModeName (uint mode) 01866 { 01867 return KateHlManager::self()->hlName (mode); 01868 } 01869 01870 QString 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 QString 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 QValueList<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 QValueList<int> marks; 01973 for( QIntDictIterator<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 QPtrList<KTextEditor::ConfigPage> editorPages; 01994 01995 for (uint i = 0; i < KTextEditor::configInterfaceExtension (this)->configPages (); i++) 01996 { 01997 QStringList path; 01998 path.clear(); 01999 path << KTextEditor::configInterfaceExtension (this)->configPageName (i); 02000 QVBox *page = kd->addVBoxPage(path, KTextEditor::configInterfaceExtension (this)->configPageFullName (i), 02001 KTextEditor::configInterfaceExtension (this)->configPagePixmap(i, KIcon::SizeMedium) ); 02002 02003 editorPages.append (KTextEditor::configInterfaceExtension (this)->configPage(i, page)); 02004 } 02005 02006 if (kd->exec()) 02007 { 02008 KateDocumentConfig::global()->configStart (); 02009 KateViewConfig::global()->configStart (); 02010 KateRendererConfig::global()->configStart (); 02011 02012 for (uint i=0; i<editorPages.count(); i++) 02013 { 02014 editorPages.at(i)->apply(); 02015 } 02016 02017 KateDocumentConfig::global()->configEnd (); 02018 KateViewConfig::global()->configEnd (); 02019 KateRendererConfig::global()->configEnd (); 02020 02021 writeConfig (); 02022 } 02023 02024 delete kd; 02025 } 02026 02027 uint KateDocument::mark( uint line ) 02028 { 02029 if( !m_marks[line] ) 02030 return 0; 02031 return m_marks[line]->type; 02032 } 02033 02034 void KateDocument::setMark( uint line, uint markType ) 02035 { 02036 clearMark( line ); 02037 addMark( line, markType ); 02038 } 02039 02040 void KateDocument::clearMark( uint line ) 02041 { 02042 if( line > lastLine() ) 02043 return; 02044 02045 if( !m_marks[line] ) 02046 return; 02047 02048 KTextEditor::Mark* mark = m_marks.take( line ); 02049 emit markChanged( *mark, MarkRemoved ); 02050 emit marksChanged(); 02051 delete mark; 02052 tagLines( line, line ); 02053 repaintViews(true); 02054 } 02055 02056 void KateDocument::addMark( uint line, uint markType ) 02057 { 02058 if( line > lastLine()) 02059 return; 02060 02061 if( markType == 0 ) 02062 return; 02063 02064 if( m_marks[line] ) { 02065 KTextEditor::Mark* mark = m_marks[line]; 02066 02067 // Remove bits already set 02068 markType &= ~mark->type; 02069 02070 if( markType == 0 ) 02071 return; 02072 02073 // Add bits 02074 mark->type |= markType; 02075 } else { 02076 KTextEditor::Mark *mark = new KTextEditor::Mark; 02077 mark->line = line; 02078 mark->type = markType; 02079 m_marks.insert( line, mark ); 02080 } 02081 02082 // Emit with a mark having only the types added. 02083 KTextEditor::Mark temp; 02084 temp.line = line; 02085 temp.type = markType; 02086 emit markChanged( temp, MarkAdded ); 02087 02088 emit marksChanged(); 02089 tagLines( line, line ); 02090 repaintViews(true); 02091 } 02092 02093 void KateDocument::removeMark( uint line, uint markType ) 02094 { 02095 if( line > lastLine() ) 02096 return; 02097 if( !m_marks[line] ) 02098 return; 02099 02100 KTextEditor::Mark* mark = m_marks[line]; 02101 02102 // Remove bits not set 02103 markType &= mark->type; 02104 02105 if( markType == 0 ) 02106 return; 02107 02108 // Subtract bits 02109 mark->type &= ~markType; 02110 02111 // Emit with a mark having only the types removed. 02112 KTextEditor::Mark temp; 02113 temp.line = line; 02114 temp.type = markType; 02115 emit markChanged( temp, MarkRemoved ); 02116 02117 if( mark->type == 0 ) 02118 m_marks.remove( line ); 02119 02120 emit marksChanged(); 02121 tagLines( line, line ); 02122 repaintViews(true); 02123 } 02124 02125 QPtrList<KTextEditor::Mark> KateDocument::marks() 02126 { 02127 QPtrList<KTextEditor::Mark> list; 02128 02129 for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); 02130 it.current(); ++it ) { 02131 list.append( it.current() ); 02132 } 02133 02134 return list; 02135 } 02136 02137 void KateDocument::clearMarks() 02138 { 02139 for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); 02140 it.current(); ++it ) { 02141 KTextEditor::Mark* mark = it.current(); 02142 emit markChanged( *mark, MarkRemoved ); 02143 tagLines( mark->line, mark->line ); 02144 } 02145 02146 m_marks.clear(); 02147 02148 emit marksChanged(); 02149 repaintViews(true); 02150 } 02151 02152 void KateDocument::setPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap ) 02153 { 02154 m_markPixmaps.replace( type, new QPixmap( pixmap ) ); 02155 } 02156 02157 void KateDocument::setDescription( MarkInterface::MarkTypes type, const QString& description ) 02158 { 02159 m_markDescriptions.replace( type, new QString( description ) ); 02160 } 02161 02162 QPixmap *KateDocument::markPixmap( MarkInterface::MarkTypes type ) 02163 { 02164 return m_markPixmaps[type]; 02165 } 02166 02167 QColor KateDocument::markColor( MarkInterface::MarkTypes type ) 02168 { 02169 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 QColor(); 02174 } 02175 } 02176 02177 QString KateDocument::markDescription( MarkInterface::MarkTypes type ) 02178 { 02179 if( m_markDescriptions[type] ) 02180 return *m_markDescriptions[type]; 02181 return QString::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 QString 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 QString KateDocument::niceFileSize() 02230 { 02231 return "UNKNOWN"; 02232 } 02233 02234 KMimeType::Ptr KateDocument::mimeTypeForContent() 02235 { 02236 QByteArray buf (1024); 02237 uint bufpos = 0; 02238 02239 for (uint i=0; i < numLines(); i++) 02240 { 02241 QString 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, SIGNAL( data( KIO::Job*, const QByteArray& ) ), 02309 SLOT( slotDataKate( KIO::Job*, const QByteArray& ) ) ); 02310 02311 connect( m_job, SIGNAL( result( KIO::Job* ) ), 02312 SLOT( slotFinishedKate( KIO::Job* ) ) ); 02313 02314 QWidget *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 QByteArray &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 QString 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 QString 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 // do we have success ? 02413 bool success = m_buffer->openFile (m_file); 02414 // 02415 // yeah, success 02416 // 02417 m_loading = false; // done reading file. 02418 if (success) 02419 { 02420 /*if (highlight() && !m_url.isLocalFile()) { 02421 // The buffer's highlighting gets nuked by KateBuffer::clear() 02422 m_buffer->setHighlight(m_highlight); 02423 }*/ 02424 02425 // update our hl type if needed 02426 if (!hlSetByUser) 02427 { 02428 int hl (KateHlManager::self()->detectHighlighting (this)); 02429 02430 if (hl >= 0) 02431 m_buffer->setHighlight(hl); 02432 } 02433 02434 // update file type if we haven't allready done so. 02435 if ( fileTypeFound < 0 ) 02436 updateFileType (KateFactory::self()->fileTypeManager()->fileType (this)); 02437 02438 // read dir config (if possible and wanted) 02439 readDirConfig (); 02440 02441 // read vars 02442 readVariables(); 02443 02444 // update the md5 digest 02445 createDigest( m_digest ); 02446 } 02447 02448 // 02449 // update views 02450 // 02451 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) 02452 { 02453 view->updateView(true); 02454 } 02455 02456 // 02457 // emit the signal we need for example for kate app 02458 // 02459 emit fileNameChanged (); 02460 02461 // 02462 // set doc name, dummy value as arg, don't need it 02463 // 02464 setDocName (QString::null); 02465 02466 // 02467 // to houston, we are not modified 02468 // 02469 if (m_modOnHd) 02470 { 02471 m_modOnHd = false; 02472 m_modOnHdReason = 0; 02473 emit modifiedOnDisc (this, m_modOnHd, 0); 02474 } 02475 02476 // 02477 // display errors 02478 // 02479 if (s_openErrorDialogsActivated) 02480 { 02481 if (!success && m_buffer->loadingBorked()) 02482 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())); 02483 else if (!success) 02484 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())); 02485 } 02486 02487 // warn -> opened binary file!!!!!!! 02488 if (m_buffer->binary()) 02489 { 02490 // this file can't be saved again without killing it 02491 setReadWrite( false ); 02492 02493 KMessageBox::information (widget() 02494 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url()) 02495 , i18n ("Binary File Opened") 02496 , "Binary File Opened Warning"); 02497 } 02498 02499 m_encodingSticky = encodingSticky; 02500 02501 // 02502 // return the success 02503 // 02504 return success; 02505 } 02506 02507 bool KateDocument::save() 02508 { 02509 bool l ( url().isLocalFile() ); 02510 02511 if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles ) 02512 || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) ) 02513 { 02514 KURL u( url() ); 02515 u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() ); 02516 02517 kdDebug () << "backup src file name: " << url() << endl; 02518 kdDebug () << "backup dst file name: " << u << endl; 02519 02520 // get the right permissions, start with safe default 02521 mode_t perms = 0600; 02522 KIO::UDSEntry fentry; 02523 if (KIO::NetAccess::stat (url(), fentry, kapp->mainWidget())) 02524 { 02525 kdDebug () << "stating succesfull: " << url() << endl; 02526 KFileItem item (fentry, url()); 02527 perms = item.permissions(); 02528 } 02529 02530 // first del existing file if any, than copy over the file we have 02531 // failure if a: the existing file could not be deleted, b: the file could not be copied 02532 if ( (!KIO::NetAccess::exists( u, false, kapp->mainWidget() ) || KIO::NetAccess::del( u, kapp->mainWidget() )) 02533 && KIO::NetAccess::file_copy( url(), u, perms, true, false, kapp->mainWidget() ) ) 02534 { 02535 kdDebug(13020)<<"backing up successfull ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl; 02536 } 02537 else 02538 { 02539 kdDebug(13020)<<"backing up failed ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl; 02540 // FIXME: notify user for real ;) 02541 } 02542 } 02543 02544 return KParts::ReadWritePart::save(); 02545 } 02546 02547 bool KateDocument::saveFile() 02548 { 02549 // 02550 // we really want to save this file ? 02551 // 02552 if (m_buffer->loadingBorked() && (KMessageBox::warningContinueCancel(widget(), 02553 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)) 02554 return false; 02555 02556 // 02557 // warn -> try to save binary file!!!!!!! 02558 // 02559 if (m_buffer->binary() && (KMessageBox::warningContinueCancel (widget() 02560 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url()) 02561 , i18n ("Trying to Save Binary File") 02562 , i18n("Save Nevertheless"), "Binary File Save Warning") != KMessageBox::Continue)) 02563 return false; 02564 02565 if ( !url().isEmpty() ) 02566 { 02567 if (s_fileChangedDialogsActivated && m_modOnHd) 02568 { 02569 QString str = reasonedMOHString() + "\n\n"; 02570 02571 if (!isModified()) 02572 { 02573 if (KMessageBox::warningContinueCancel(0, 02574 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) 02575 return false; 02576 } 02577 else 02578 { 02579 if (KMessageBox::warningContinueCancel(0, 02580 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) 02581 return false; 02582 } 02583 } 02584 } 02585 02586 // 02587 // can we encode it if we want to save it ? 02588 // 02589 if (!m_buffer->canEncode () 02590 && (KMessageBox::warningContinueCancel(0, 02591 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)) 02592 { 02593 return false; 02594 } 02595 02596 // remove file from dirwatch 02597 deactivateDirWatch (); 02598 02599 // 02600 // try to save 02601 // 02602 bool success = m_buffer->saveFile (m_file); 02603 02604 // update the md5 digest 02605 createDigest( m_digest ); 02606 02607 // add m_file again to dirwatch 02608 activateDirWatch (); 02609 02610 // 02611 // hurray, we had success, do stuff we need 02612 // 02613 if (success) 02614 { 02615 // update our hl type if needed 02616 if (!hlSetByUser) 02617 { 02618 int hl (KateHlManager::self()->detectHighlighting (this)); 02619 02620 if (hl >= 0) 02621 m_buffer->setHighlight(hl); 02622 } 02623 02624 // read our vars 02625 readVariables(); 02626 } 02627 02628 // 02629 // we are not modified 02630 // 02631 if (success && m_modOnHd) 02632 { 02633 m_modOnHd = false; 02634 m_modOnHdReason = 0; 02635 emit modifiedOnDisc (this, m_modOnHd, 0); 02636 } 02637 02638 // 02639 // display errors 02640 // 02641 if (!success) 02642 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())); 02643 02644 // 02645 // return success 02646 // 02647 return success; 02648 } 02649 02650 bool KateDocument::saveAs( const KURL &u ) 02651 { 02652 QString oldDir = url().directory(); 02653 02654 if ( KParts::ReadWritePart::saveAs( u ) ) 02655 { 02656 // null means base on filename 02657 setDocName( QString::null ); 02658 02659 if ( u.directory() != oldDir ) 02660 readDirConfig(); 02661 02662 emit fileNameChanged(); 02663 emit nameChanged((Kate::Document *) this); 02664 02665 return true; 02666 } 02667 02668 return false; 02669 } 02670 02671 void KateDocument::readDirConfig () 02672 { 02673 int depth = config()->searchDirConfigDepth (); 02674 02675 if (m_url.isLocalFile() && (depth > -1)) 02676 { 02677 QString currentDir = QFileInfo (m_file).dirPath(); 02678 02679 // only search as deep as specified or not at all ;) 02680 while (depth > -1) 02681 { 02682 kdDebug (13020) << "search for config file in path: " << currentDir << endl; 02683 02684 // try to open config file in this dir 02685 QFile f (currentDir + "/.kateconfig"); 02686 02687 if (f.open (IO_ReadOnly)) 02688 { 02689 QTextStream stream (&f); 02690 02691 uint linesRead = 0; 02692 QString line = stream.readLine(); 02693 while ((linesRead < 32) && !line.isNull()) 02694 { 02695 readVariableLine( line ); 02696 02697 line = stream.readLine(); 02698 02699 linesRead++; 02700 } 02701 02702 break; 02703 } 02704 02705 QString newDir = QFileInfo (currentDir).dirPath(); 02706 02707 // bail out on looping (for example reached /) 02708 if (currentDir == newDir) 02709 break; 02710 02711 currentDir = newDir; 02712 --depth; 02713 } 02714 } 02715 } 02716 02717 void KateDocument::activateDirWatch () 02718 { 02719 // same file as we are monitoring, return 02720 if (m_file == m_dirWatchFile) 02721 return; 02722 02723 // remove the old watched file 02724 deactivateDirWatch (); 02725 02726 // add new file if needed 02727 if (m_url.isLocalFile() && !m_file.isEmpty()) 02728 { 02729 KateFactory::self()->dirWatch ()->addFile (m_file); 02730 m_dirWatchFile = m_file; 02731 } 02732 } 02733 02734 void KateDocument::deactivateDirWatch () 02735 { 02736 if (!m_dirWatchFile.isEmpty()) 02737 KateFactory::self()->dirWatch ()->removeFile (m_dirWatchFile); 02738 02739 m_dirWatchFile = QString::null; 02740 } 02741 02742 bool KateDocument::closeURL() 02743 { 02744 abortLoadKate(); 02745 02746 // 02747 // file mod on hd 02748 // 02749 if ( !m_reloading && !url().isEmpty() ) 02750 { 02751 if (s_fileChangedDialogsActivated && m_modOnHd) 02752 { 02753 if (!(KMessageBox::warningContinueCancel( 02754 widget(), 02755 reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."), 02756 i18n("Possible Data Loss"), i18n("Close Nevertheless"), 02757 QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue)) 02758 return false; 02759 } 02760 } 02761 02762 // 02763 // first call the normal kparts implementation 02764 // 02765 if (!KParts::ReadWritePart::closeURL ()) 02766 return false; 02767 02768 // remove file from dirwatch 02769 deactivateDirWatch (); 02770 02771 // 02772 // empty url + filename 02773 // 02774 m_url = KURL (); 02775 m_file = QString::null; 02776 02777 // we are not modified 02778 if (m_modOnHd) 02779 { 02780 m_modOnHd = false; 02781 m_modOnHdReason = 0; 02782 emit modifiedOnDisc (this, m_modOnHd, 0); 02783 } 02784 02785 // clear the buffer 02786 m_buffer->clear(); 02787 02788 // remove all marks 02789 clearMarks (); 02790 02791 // clear undo/redo history 02792 clearUndo(); 02793 clearRedo(); 02794 02795 // no, we are no longer modified 02796 setModified(false); 02797 02798 // we have no longer any hl 02799 m_buffer->setHighlight(0); 02800 02801 // update all our views 02802 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) 02803 { 02804 // Explicitly call the internal version because we don't want this to look like 02805 // an external request (and thus have the view not QWidget::scroll()ed. 02806 view->setCursorPositionInternal(0, 0, 1, false); 02807 view->clearSelection(); 02808 view->updateView(true); 02809 } 02810 02811 // uh, filename changed 02812 emit fileNameChanged (); 02813 02814 // update doc name 02815 setDocName (QString::null); 02816 02817 // success 02818 return true; 02819 } 02820 02821 void KateDocument::setReadWrite( bool rw ) 02822 { 02823 if (isReadWrite() != rw) 02824 { 02825 KParts::ReadWritePart::setReadWrite (rw); 02826 02827 for( KateView* view = m_views.first(); view != 0L; view = m_views.next() ) 02828 { 02829 view->slotUpdate(); 02830 view->slotReadWriteChanged (); 02831 } 02832 } 02833 } 02834 02835 void KateDocument::setModified(bool m) { 02836 02837 if (isModified() != m) { 02838 KParts::ReadWritePart::setModified (m); 02839 02840 for( KateView* view = m_views.first(); view != 0L; view = m_views.next() ) 02841 { 02842 view->slotUpdate(); 02843 } 02844 02845 emit modifiedChanged (); 02846 emit modStateChanged ((Kate::Document *)this); 02847 } 02848 if ( m == false ) 02849 { 02850 if ( ! undoItems.isEmpty() ) 02851 { 02852 lastUndoGroupWhenSaved = undoItems.last(); 02853 } 02854 02855 if ( ! redoItems.isEmpty() ) 02856 { 02857 lastRedoGroupWhenSaved = redoItems.last(); 02858 } 02859 02860 docWasSavedWhenUndoWasEmpty = undoItems.isEmpty(); 02861 docWasSavedWhenRedoWasEmpty = redoItems.isEmpty(); 02862 } 02863 } 02864 //END 02865 02866 //BEGIN Kate specific stuff ;) 02867 02868 void KateDocument::makeAttribs(bool needInvalidate) 02869 { 02870 for (uint z = 0; z < m_views.count(); z++) 02871 m_views.at(z)->renderer()->updateAttributes (); 02872 02873 if (needInvalidate) 02874 m_buffer->invalidateHighlighting(); 02875 02876 tagAll (); 02877 } 02878 02879 // the attributes of a hl have changed, update 02880 void KateDocument::internalHlChanged() 02881 { 02882 makeAttribs(); 02883 } 02884 02885 void KateDocument::addView(KTextEditor::View *view) { 02886 if (!view) 02887 return; 02888 02889 m_views.append( (KateView *) view ); 02890 m_textEditViews.append( view ); 02891 02892 // apply the view & renderer vars from the file type 02893 const KateFileType *t = 0; 02894 if ((m_fileType > -1) && (t = KateFactory::self()->fileTypeManager()->fileType(m_fileType))) 02895 readVariableLine (t->varLine, true); 02896 02897 // apply the view & renderer vars from the file 02898 readVariables (true); 02899 02900 m_activeView = (KateView *) view; 02901 } 02902 02903 void KateDocument::removeView(KTextEditor::View *view) { 02904 if (!view) 02905 return; 02906 02907 if (m_activeView == view) 02908 m_activeView = 0L; 02909 02910 m_views.removeRef( (KateView *) view ); 02911 m_textEditViews.removeRef( view ); 02912 } 02913 02914 void KateDocument::addSuperCursor(KateSuperCursor *cursor, bool privateC) { 02915 if (!cursor) 02916 return; 02917 02918 m_superCursors.append( cursor ); 02919 02920 if (!privateC) 02921 myCursors.append( cursor ); 02922 } 02923 02924 void KateDocument::removeSuperCursor(KateSuperCursor *cursor, bool privateC) { 02925 if (!cursor) 02926 return; 02927 02928 if (!privateC) 02929 myCursors.removeRef( cursor ); 02930 02931 m_superCursors.removeRef( cursor ); 02932 } 02933 02934 bool KateDocument::ownedView(KateView *view) { 02935 // do we own the given view? 02936 return (m_views.containsRef(view) > 0); 02937 } 02938 02939 bool KateDocument::isLastView(int numViews) { 02940 return ((int) m_views.count() == numViews); 02941 } 02942 02943 uint KateDocument::currentColumn( const KateTextCursor& cursor ) 02944 { 02945 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line()); 02946 02947 if (textLine) 02948 return textLine->cursorX(cursor.col(), config()->tabWidth()); 02949 else 02950 return 0; 02951 } 02952 02953 bool KateDocument::typeChars ( KateView *view, const QString &chars ) 02954 { 02955 KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorLine ()); 02956 02957 if (!textLine) 02958 return false; 02959 02960 bool bracketInserted = false; 02961 QString buf; 02962 QChar c; 02963 02964 for( uint z = 0; z < chars.length(); z++ ) 02965 { 02966 QChar ch = c = chars[z]; 02967 if (ch.isPrint() || ch == '\t') 02968 { 02969 buf.append (ch); 02970 02971 if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets)) 02972 { 02973 QChar end_ch; 02974 bool complete = true; 02975 QChar prevChar = textLine->getChar(view->cursorColumnReal()-1); 02976 QChar nextChar = textLine->getChar(view->cursorColumnReal()); 02977 switch(ch) { 02978 case '(': end_ch = ')'; break; 02979 case '[': end_ch = ']'; break; 02980 case '{': end_ch = '}'; break; 02981 case '\'':end_ch = '\'';break; 02982 case '"': end_ch = '"'; break; 02983 default: complete = false; 02984 } 02985 if (complete) 02986 { 02987 if (view->hasSelection()) 02988 { // there is a selection, enclose the selection 02989 buf.append (view->selection()); 02990 buf.append (end_ch); 02991 bracketInserted = true; 02992 } 02993 else 02994 { // no selection, check whether we should better refuse to complete 02995 if ( ( (ch == '\'' || ch == '"') && 02996 (prevChar.isLetterOrNumber() || prevChar == ch) ) 02997 || nextChar.isLetterOrNumber() 02998 || (nextChar == end_ch && prevChar != ch) ) 02999 { 03000 kdDebug(13020) << "AutoBracket refused before: " << nextChar << "\n"; 03001 } 03002 else 03003 { 03004 buf.append (end_ch); 03005 bracketInserted = true; 03006 } 03007 } 03008 } 03009 } 03010 } 03011 } 03012 03013 if (buf.isEmpty()) 03014 return false; 03015 03016 editStart (); 03017 03018 if (!view->config()->persistentSelection() && view->hasSelection() ) 03019 view->removeSelectedText(); 03020 03021 int oldLine = view->cursorLine (); 03022 int oldCol = view->cursorColumnReal (); 03023 03024 03025 if (config()->configFlags() & KateDocument::cfOvr) 03026 removeText (view->cursorLine(), view->cursorColumnReal(), view->cursorLine(), kMin( view->cursorColumnReal()+buf.length(), textLine->length() ) ); 03027 03028 insertText (view->cursorLine(), view->cursorColumnReal(), buf); 03029 m_indenter->processChar(c); 03030 03031 editEnd (); 03032 03033 if (bracketInserted) 03034 view->setCursorPositionInternal (view->cursorLine(), view->cursorColumnReal()-1); 03035 03036 emit charactersInteractivelyInserted (oldLine, oldCol, chars); 03037 03038 return true; 03039 } 03040 03041 void KateDocument::newLine( KateTextCursor& c, KateViewInternal *v ) 03042 { 03043 editStart(); 03044 03045 if( !v->view()->config()->persistentSelection() && v->view()->hasSelection() ) 03046 v->view()->removeSelectedText(); 03047 03048 // temporary hack to get the cursor pos right !!!!!!!!! 03049 c = v->getCursor (); 03050 03051 if (c.line() > (int)lastLine()) 03052 c.setLine(lastLine()); 03053 03054 if ( c.line() < 0 ) 03055 c.setLine( 0 ); 03056 03057 uint ln = c.line(); 03058 03059 KateTextLine::Ptr textLine = kateTextLine(c.line()); 03060 03061 if (c.col() > (int)textLine->length()) 03062 c.setCol(textLine->length()); 03063 03064 if (m_indenter->canProcessNewLine ()) 03065 { 03066 int pos = textLine->firstChar(); 03067 03068 // length should do the job better 03069 if (pos < 0) 03070 pos = textLine->length(); 03071 03072 if (c.col() < pos) 03073 c.setCol(pos); // place cursor on first char if before 03074 03075 editWrapLine (c.line(), c.col()); 03076 03077 KateDocCursor cursor (c.line() + 1, pos, this); 03078 m_indenter->processNewline(cursor, true); 03079 03080 c.setPos(cursor); 03081 } 03082 else 03083 { 03084 editWrapLine (c.line(), c.col()); 03085 c.setPos(c.line() + 1, 0); 03086 } 03087 03088 removeTrailingSpace( ln ); 03089 03090 editEnd(); 03091 } 03092 03093 void KateDocument::transpose( const KateTextCursor& cursor) 03094 { 03095 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line()); 03096 03097 if (!textLine || (textLine->length() < 2)) 03098 return; 03099 03100 uint col = cursor.col(); 03101 03102 if (col > 0) 03103 col--; 03104 03105 if ((textLine->length() - col) < 2) 03106 return; 03107 03108 uint line = cursor.line(); 03109 QString s; 03110 03111 //clever swap code if first character on the line swap right&left 03112 //otherwise left & right 03113 s.append (textLine->getChar(col+1)); 03114 s.append (textLine->getChar(col)); 03115 //do the swap 03116 03117 // do it right, never ever manipulate a textline 03118 editStart (); 03119 editRemoveText (line, col, 2); 03120 editInsertText (line, col, s); 03121 editEnd (); 03122 } 03123 03124 void KateDocument::backspace( KateView *view, const KateTextCursor& c ) 03125 { 03126 if ( !view->config()->persistentSelection() && view->hasSelection() ) { 03127 view->removeSelectedText(); 03128 return; 03129 } 03130 03131 uint col = kMax( c.col(), 0 ); 03132 uint line = kMax( c.line(), 0 ); 03133 03134 if ((col == 0) && (line == 0)) 03135 return; 03136 03137 int complement = 0; 03138 if (col > 0) 03139 { 03140 if (config()->configFlags() & KateDocument::cfAutoBrackets) 03141 { 03142 // if inside empty (), {}, [], '', "" delete both 03143 KateTextLine::Ptr tl = m_buffer->plainLine(line); 03144 if(!tl) return; 03145 QChar prevChar = tl->getChar(col-1); 03146 QChar nextChar = tl->getChar(col); 03147 03148 if ( (prevChar == '"' && nextChar == '"') || 03149 (prevChar == '\'' && nextChar == '\'') || 03150 (prevChar == '(' && nextChar == ')') || 03151 (prevChar == '[' && nextChar == ']') || 03152 (prevChar == '{' && nextChar == '}') ) 03153 { 03154 complement = 1; 03155 } 03156 } 03157 if (!(config()->configFlags() & KateDocument::cfBackspaceIndents)) 03158 { 03159 // ordinary backspace 03160 //c.cursor.col--; 03161 removeText(line, col-1, line, col+complement); 03162 } 03163 else 03164 { 03165 // backspace indents: erase to next indent position 03166 KateTextLine::Ptr textLine = m_buffer->plainLine(line); 03167 03168 // don't forget this check!!!! really!!!! 03169 if (!textLine) 03170 return; 03171 03172 int colX = textLine->cursorX(col, config()->tabWidth()); 03173 int pos = textLine->firstChar(); 03174 if (pos > 0) 03175 pos = textLine->cursorX(pos, config()->tabWidth()); 03176 03177 if (pos < 0 || pos >= (int)colX) 03178 { 03179 // only spaces on left side of cursor 03180 indent( view, line, -1); 03181 } 03182 else 03183 removeText(line, col-1, line, col+complement); 03184 } 03185 } 03186 else 03187 { 03188 // col == 0: wrap to previous line 03189 if (line >= 1) 03190 { 03191 KateTextLine::Ptr textLine = m_buffer->plainLine(line-1); 03192 03193 // don't forget this check!!!! really!!!! 03194 if (!textLine) 03195 return; 03196 03197 if (config()->wordWrap() && textLine->endingWith(QString::fromLatin1(" "))) 03198 { 03199 // gg: in hard wordwrap mode, backspace must also eat the trailing space 03200 removeText (line-1, textLine->length()-1, line, 0); 03201 } 03202 else 03203 removeText (line-1, textLine->length(), line, 0); 03204 } 03205 } 03206 03207 emit backspacePressed(); 03208 } 03209 03210 void KateDocument::del( KateView *view, const KateTextCursor& c ) 03211 { 03212 if ( !view->config()->persistentSelection() && view->hasSelection() ) { 03213 view->removeSelectedText(); 03214 return; 03215 } 03216 03217 if( c.col() < (int) m_buffer->plainLine(c.line())->length()) 03218 { 03219 removeText(c.line(), c.col(), c.line(), c.col()+1); 03220 } 03221 else if ( (uint)c.line() < lastLine() ) 03222 { 03223 removeText(c.line(), c.col(), c.line()+1, 0); 03224 } 03225 } 03226 03227 void KateDocument::paste ( KateView* view ) 03228 { 03229 QString s = QApplication::clipboard()->text(); 03230 03231 if (s.isEmpty()) 03232 return; 03233 03234 uint lines = s.contains (QChar ('\n')); 03235 03236 m_undoDontMerge = true; 03237 03238 editStart (); 03239 03240 if (!view->config()->persistentSelection() && view->hasSelection() ) 03241 view->removeSelectedText(); 03242 03243 uint line = view->cursorLine (); 03244 uint column = view->cursorColumnReal (); 03245 03246 insertText ( line, column, s, view->blockSelectionMode() ); 03247 03248 editEnd(); 03249 03250 // move cursor right for block select, as the user is moved right internal 03251 // even in that case, but user expects other behavior in block selection 03252 // mode ! 03253 if (view->blockSelectionMode()) 03254 view->setCursorPositionInternal (line+lines, column); 03255 03256 if (m_indenter->canProcessLine() 03257 && config()->configFlags() & KateDocumentConfig::cfIndentPastedText) 03258 { 03259 editStart(); 03260 03261 KateDocCursor begin(line, 0, this); 03262 KateDocCursor end(line + lines, 0, this); 03263 03264 m_indenter->processSection (begin, end); 03265 03266 editEnd(); 03267 } 03268 03269 if (!view->blockSelectionMode()) emit charactersSemiInteractivelyInserted (line, column, s); 03270 m_undoDontMerge = true; 03271 } 03272 03273 void KateDocument::insertIndentChars ( KateView *view ) 03274 { 03275 editStart (); 03276 03277 QString s; 03278 if (config()->configFlags() & KateDocument::cfSpaceIndent) 03279 { 03280 int width = config()->indentationWidth(); 03281 s.fill (' ', width - (view->cursorColumnReal() % width)); 03282 } 03283 else 03284 s.append ('\t'); 03285 03286 insertText (view->cursorLine(), view->cursorColumnReal(), s); 03287 03288 editEnd (); 03289 } 03290 03291 void KateDocument::indent ( KateView *v, uint line, int change) 03292 { 03293 editStart (); 03294 03295 if (!hasSelection()) 03296 { 03297 // single line 03298 optimizeLeadingSpace(line, config()->configFlags(), change); 03299 } 03300 else 03301 { 03302 int sl = v->selStartLine(); 03303 int el = v->selEndLine(); 03304 int ec = v->selEndCol(); 03305 03306 if ((ec == 0) && ((el-1) >= 0)) 03307 { 03308 el--; /* */ 03309 } 03310 03311 if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) { 03312 // unindent so that the existing indent profile doesn't get screwed 03313 // if any line we may unindent is already full left, don't do anything 03314 int adjustedChange = -change; 03315 03316 for (line = sl; (int) line <= el && adjustedChange > 0; line++) { 03317 KateTextLine::Ptr textLine = m_buffer->plainLine(line); 03318 int firstChar = textLine->firstChar(); 03319 if (firstChar >= 0 && (v->lineSelected(line) || v->lineHasSelected(line))) { 03320 int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth(); 03321 if (maxUnindent < adjustedChange) 03322 adjustedChange = maxUnindent; 03323 } 03324 } 03325 03326 change = -adjustedChange; 03327 } 03328 03329 const bool rts = config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn; 03330 for (line = sl; (int) line <= el; line++) { 03331 if ((v->lineSelected(line) || v->lineHasSelected(line)) 03332 && (!rts || lineLength(line) > 0)) { 03333 optimizeLeadingSpace(line, config()->configFlags(), change); 03334 } 03335 } 03336 } 03337 03338 editEnd (); 03339 } 03340 03341 void KateDocument::align(KateView *view, uint line) 03342 { 03343 if (m_indenter->canProcessLine()) 03344 { 03345 editStart (); 03346 03347 if (!view->hasSelection()) 03348 { 03349 KateDocCursor curLine(line, 0, this); 03350 m_indenter->processLine (curLine); 03351 editEnd (); 03352 activeView()->setCursorPosition (line, curLine.col()); 03353 } 03354 else 03355 { 03356 m_indenter->processSection (view->selStart(), view->selEnd()); 03357 editEnd (); 03358 } 03359 } 03360 } 03361 03362 /* 03363 Optimize the leading whitespace for a single line. 03364 If change is > 0, it adds indentation units (indentationChars) 03365 if change is == 0, it only optimizes 03366 If change is < 0, it removes indentation units 03367 This will be used to indent, unindent, and optimal-fill a line. 03368 If excess space is removed depends on the flag cfKeepExtraSpaces 03369 which has to be set by the user 03370 */ 03371 void KateDocument::optimizeLeadingSpace(uint line, int flags, int change) 03372 { 03373 KateTextLine::Ptr textline = m_buffer->plainLine(line); 03374 03375 int first_char = textline->firstChar(); 03376 03377 int w = 0; 03378 if (flags & KateDocument::cfSpaceIndent) 03379 w = config()->indentationWidth(); 03380 else 03381 w = config()->tabWidth(); 03382 03383 if (first_char < 0) 03384 first_char = textline->length(); 03385 03386 int space = textline->cursorX(first_char, config()->tabWidth()) + change * w; 03387 if (space < 0) 03388 space = 0; 03389 03390 if (!(flags & KateDocument::cfKeepExtraSpaces)) 03391 { 03392 uint extra = space % w; 03393 03394 space -= extra; 03395 if (extra && change < 0) { 03396 // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide) 03397 space += w; 03398 } 03399 } 03400 03401 //kdDebug(13020) << "replace With Op: " << line << " " << first_char << " " << space << endl; 03402 replaceWithOptimizedSpace(line, first_char, space, flags); 03403 } 03404 03405 void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags) 03406 { 03407 uint length; 03408 QString new_space; 03409 03410 if (flags & KateDocument::cfSpaceIndent && ! (flags & KateDocumentConfig::cfMixedIndent) ) { 03411 length = space; 03412 new_space.fill(' ', length); 03413 } 03414 else { 03415 length = space / config()->tabWidth(); 03416 new_space.fill('\t', length); 03417 03418 QString extra_space; 03419 extra_space.fill(' ', space % config()->tabWidth()); 03420 length += space % config()->tabWidth(); 03421 new_space += extra_space; 03422 } 03423 03424 KateTextLine::Ptr textline = m_buffer->plainLine(line); 03425 uint change_from; 03426 for (change_from = 0; change_from < upto_column && change_from < length; change_from++) { 03427 if (textline->getChar(change_from) != new_space[change_from]) 03428 break; 03429 } 03430 03431 editStart(); 03432 03433 if (change_from < upto_column) 03434 removeText(line, change_from, line, upto_column); 03435 03436 if (change_from < length) 03437 insertText(line, change_from, new_space.right(length - change_from)); 03438 03439 editEnd(); 03440 } 03441 03442 /* 03443 Remove a given string at the begining 03444 of the current line. 03445 */ 03446 bool KateDocument::removeStringFromBegining(int line, QString &str) 03447 { 03448 KateTextLine::Ptr textline = m_buffer->plainLine(line); 03449 03450 int index = 0; 03451 bool there = false; 03452 03453 if (textline->startingWith(str)) 03454 there = true; 03455 else 03456 { 03457 index = textline->firstChar (); 03458 03459 if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str)) 03460 there = true; 03461 } 03462 03463 if (there) 03464 { 03465 // Remove some chars 03466 removeText (line, index, line, index+str.length()); 03467 } 03468 03469 return there; 03470 } 03471 03472 /* 03473 Remove a given string at the end 03474 of the current line. 03475 */ 03476 bool KateDocument::removeStringFromEnd(int line, QString &str) 03477 { 03478 KateTextLine::Ptr textline = m_buffer->plainLine(line); 03479 03480 int index = 0; 03481 bool there = false; 03482 03483 if(textline->endingWith(str)) 03484 { 03485 index = textline->length() - str.length(); 03486 there = true; 03487 } 03488 else 03489 { 03490 index = textline->lastChar ()-str.length()+1; 03491 03492 if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str)) 03493 there = true; 03494 } 03495 03496 if (there) 03497 { 03498 // Remove some chars 03499 removeText (line, index, line, index+str.length()); 03500 } 03501 03502 return there; 03503 } 03504 03505 /* 03506 Add to the current line a comment line mark at 03507 the begining. 03508 */ 03509 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib ) 03510 { 03511 if (highlight()->getCommentSingleLinePosition(attrib)==KateHighlighting::CSLPosColumn0) 03512 { 03513 QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " "; 03514 insertText (line, 0, commentLineMark); 03515 } 03516 else 03517 { 03518 QString commentLineMark=highlight()->getCommentSingleLineStart(attrib); 03519 KateTextLine::Ptr l = m_buffer->line(line); 03520 int pos=l->firstChar(); 03521 if (pos >=0) 03522 insertText(line,pos,commentLineMark); 03523 } 03524 } 03525 03526 /* 03527 Remove from the current line a comment line mark at 03528 the begining if there is one. 03529 */ 03530 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib ) 03531 { 03532 QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib ); 03533 QString longCommentMark = shortCommentMark + " "; 03534 03535 editStart(); 03536 03537 // Try to remove the long comment mark first 03538 bool removed = (removeStringFromBegining(line, longCommentMark) 03539 || removeStringFromBegining(line, shortCommentMark)); 03540 03541 editEnd(); 03542 03543 return removed; 03544 } 03545 03546 /* 03547 Add to the current line a start comment mark at the 03548 begining and a stop comment mark at the end. 03549 */ 03550 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib ) 03551 { 03552 QString startCommentMark = highlight()->getCommentStart( attrib ) + " "; 03553 QString stopCommentMark = " " + highlight()->getCommentEnd( attrib ); 03554 03555 editStart(); 03556 03557 // Add the start comment mark 03558 insertText (line, 0, startCommentMark); 03559 03560 // Go to the end of the line 03561 int col = m_buffer->plainLine(line)->length(); 03562 03563 // Add the stop comment mark 03564 insertText (line, col, stopCommentMark); 03565 03566 editEnd(); 03567 } 03568 03569 /* 03570 Remove from the current line a start comment mark at 03571 the begining and a stop comment mark at the end. 03572 */ 03573 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib ) 03574 { 03575 QString shortStartCommentMark = highlight()->getCommentStart( attrib ); 03576 QString longStartCommentMark = shortStartCommentMark + " "; 03577 QString shortStopCommentMark = highlight()->getCommentEnd( attrib ); 03578 QString longStopCommentMark = " " + shortStopCommentMark; 03579 03580 editStart(); 03581 03582 #ifdef __GNUC__ 03583 #warning "that's a bad idea, can lead to stray endings, FIXME" 03584 #endif 03585 // Try to remove the long start comment mark first 03586 bool removedStart = (removeStringFromBegining(line, longStartCommentMark) 03587 || removeStringFromBegining(line, shortStartCommentMark)); 03588 03589 bool removedStop = false; 03590 if (removedStart) 03591 { 03592 // Try to remove the long stop comment mark first 03593 removedStop = (removeStringFromEnd(line, longStopCommentMark) 03594 || removeStringFromEnd(line, shortStopCommentMark)); 03595 } 03596 03597 editEnd(); 03598 03599 return (removedStart || removedStop); 03600 } 03601 03602 /* 03603 Add to the current selection a start comment 03604 mark at the begining and a stop comment mark 03605 at the end. 03606 */ 03607 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib ) 03608 { 03609 QString startComment = highlight()->getCommentStart( attrib ); 03610 QString endComment = highlight()->getCommentEnd( attrib ); 03611 03612 int sl = view->selStartLine(); 03613 int el = view->selEndLine(); 03614 int sc = view->selStartCol(); 03615 int ec = view->selEndCol(); 03616 03617 if ((ec == 0) && ((el-1) >= 0)) 03618 { 03619 el--; 03620 ec = m_buffer->plainLine (el)->length(); 03621 } 03622 03623 editStart(); 03624 03625 insertText (el, ec, endComment); 03626 insertText (sl, sc, startComment); 03627 03628 editEnd (); 03629 03630 // Set the new selection 03631 ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 ); 03632 view->setSelection(sl, sc, el, ec); 03633 } 03634 03635 /* 03636 Add to the current selection a comment line 03637 mark at the begining of each line. 03638 */ 03639 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib ) 03640 { 03641 QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " "; 03642 03643 int sl = view->selStartLine(); 03644 int el = view->selEndLine(); 03645 03646 if ((view->selEndCol() == 0) && ((el-1) >= 0)) 03647 { 03648 el--; 03649 } 03650 03651 editStart(); 03652 03653 // For each line of the selection 03654 for (int z = el; z >= sl; z--) { 03655 //insertText (z, 0, commentLineMark); 03656 addStartLineCommentToSingleLine(z, attrib ); 03657 } 03658 03659 editEnd (); 03660 03661 // Set the new selection 03662 03663 KateDocCursor end (view->selEnd()); 03664 end.setCol(view->selEndCol() + ((el == view->selEndLine()) ? commentLineMark.length() : 0) ); 03665 03666 view->setSelection(view->selStartLine(), 0, end.line(), end.col()); 03667 } 03668 03669 bool KateDocument::nextNonSpaceCharPos(int &line, int &col) 03670 { 03671 for(; line < (int)m_buffer->count(); line++) { 03672 KateTextLine::Ptr textLine = m_buffer->plainLine(line); 03673 03674 if (!textLine) 03675 break; 03676 03677 col = textLine->nextNonSpaceChar(col); 03678 if(col != -1) 03679 return true; // Next non-space char found 03680 col = 0; 03681 } 03682 // No non-space char found 03683 line = -1; 03684 col = -1; 03685 return false; 03686 } 03687 03688 bool KateDocument::previousNonSpaceCharPos(int &line, int &col) 03689 { 03690 while(true) 03691 { 03692 KateTextLine::Ptr textLine = m_buffer->plainLine(line); 03693 03694 if (!textLine) 03695 break; 03696 03697 col = textLine->previousNonSpaceChar(col); 03698 if(col != -1) return true; 03699 if(line == 0) return false; 03700 --line; 03701 col = textLine->length(); 03702 } 03703 // No non-space char found 03704 line = -1; 03705 col = -1; 03706 return false; 03707 } 03708 03709 /* 03710 Remove from the selection a start comment mark at 03711 the begining and a stop comment mark at the end. 03712 */ 03713 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib ) 03714 { 03715 QString startComment = highlight()->getCommentStart( attrib ); 03716 QString endComment = highlight()->getCommentEnd( attrib ); 03717 03718 int sl = kMax<int> (0, view->selStartLine()); 03719 int el = kMin<int> (view->selEndLine(), lastLine()); 03720 int sc = view->selStartCol(); 03721 int ec = view->selEndCol(); 03722 03723 // The selection ends on the char before selectEnd 03724 if (ec != 0) { 03725 ec--; 03726 } else { 03727 if (el > 0) { 03728 el--; 03729 ec = m_buffer->plainLine(el)->length() - 1; 03730 } 03731 } 03732 03733 int startCommentLen = startComment.length(); 03734 int endCommentLen = endComment.length(); 03735 03736 // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/ 03737 03738 bool remove = nextNonSpaceCharPos(sl, sc) 03739 && m_buffer->plainLine(sl)->stringAtPos(sc, startComment) 03740 && previousNonSpaceCharPos(el, ec) 03741 && ( (ec - endCommentLen + 1) >= 0 ) 03742 && m_buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment); 03743 03744 if (remove) { 03745 editStart(); 03746 03747 removeText (el, ec - endCommentLen + 1, el, ec + 1); 03748 removeText (sl, sc, sl, sc + startCommentLen); 03749 03750 editEnd (); 03751 // set new selection not necessary, as the selection cursors are KateSuperCursors 03752 } 03753 03754 return remove; 03755 } 03756 03757 bool KateDocument::removeStartStopCommentFromRegion(const KateTextCursor &start,const KateTextCursor &end,int attrib) 03758 { 03759 QString startComment = highlight()->getCommentStart( attrib ); 03760 QString endComment = highlight()->getCommentEnd( attrib ); 03761 int startCommentLen = startComment.length(); 03762 int endCommentLen = endComment.length(); 03763 03764 bool remove = m_buffer->plainLine(start.line())->stringAtPos(start.col(), startComment) 03765 && ( (end.col() - endCommentLen ) >= 0 ) 03766 && m_buffer->plainLine(end.line())->stringAtPos(end.col() - endCommentLen , endComment); 03767 if (remove) { 03768 editStart(); 03769 removeText(end.line(),end.col()-endCommentLen,end.line(),end.col()); 03770 removeText(start.line(),start.col(),start.line(),start.col()+startCommentLen); 03771 editEnd(); 03772 } 03773 return remove; 03774 } 03775 03776 /* 03777 Remove from the begining of each line of the 03778 selection a start comment line mark. 03779 */ 03780 bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib ) 03781 { 03782 QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib ); 03783 QString longCommentMark = shortCommentMark + " "; 03784 03785 int sl = view->selStartLine(); 03786 int el = view->selEndLine(); 03787 03788 if ((view->selEndCol() == 0) && ((el-1) >= 0)) 03789 { 03790 el--; 03791 } 03792 03793 // Find out how many char will be removed from the last line 03794 int removeLength = 0; 03795 if (m_buffer->plainLine(el)->startingWith(longCommentMark)) 03796 removeLength = longCommentMark.length(); 03797 else if (m_buffer->plainLine(el)->startingWith(shortCommentMark)) 03798 removeLength = shortCommentMark.length(); 03799 03800 bool removed = false; 03801 03802 editStart(); 03803 03804 // For each line of the selection 03805 for (int z = el; z >= sl; z--) 03806 { 03807 // Try to remove the long comment mark first 03808 removed = (removeStringFromBegining(z, longCommentMark) 03809 || removeStringFromBegining(z, shortCommentMark) 03810 || removed); 03811 } 03812 03813 editEnd(); 03814 // updating selection already done by the KateSuperCursors 03815 return removed; 03816 } 03817 03818 /* 03819 Comment or uncomment the selection or the current 03820 line if there is no selection. 03821 */ 03822 void KateDocument::comment( KateView *v, uint line,uint column, int change) 03823 { 03824 // We need to check that we can sanely comment the selectino or region. 03825 // It is if the attribute of the first and last character of the range to 03826 // comment belongs to the same language definition. 03827 // for lines with no text, we need the attribute for the lines context. 03828 bool hassel = v->hasSelection(); 03829 int startAttrib, endAttrib; 03830 if ( hassel ) 03831 { 03832 KateTextLine::Ptr ln = kateTextLine( v->selStartLine() ); 03833 int l = v->selStartLine(), c = v->selStartCol(); 03834 startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0; 03835 03836 ln = kateTextLine( v->selEndLine() ); 03837 l = v->selEndLine(), c = v->selEndCol(); 03838 endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0; 03839 } 03840 else 03841 { 03842 KateTextLine::Ptr ln = kateTextLine( line ); 03843 if ( ln->length() ) 03844 { 03845 startAttrib = ln->attribute( ln->firstChar() ); 03846 endAttrib = ln->attribute( ln->lastChar() ); 03847 } 03848 else 03849 { 03850 int l = line, c = 0; 03851 if ( nextNonSpaceCharPos( l, c ) || previousNonSpaceCharPos( l, c ) ) 03852 startAttrib = endAttrib = kateTextLine( l )->attribute( c ); 03853 else 03854 startAttrib = endAttrib = 0; 03855 } 03856 } 03857 03858 if ( ! highlight()->canComment( startAttrib, endAttrib ) ) 03859 { 03860 kdDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!"<<endl; 03861 return; 03862 } 03863 03864 bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty()); 03865 bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty()) 03866 && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) ); 03867 03868 bool removed = false; 03869 03870 if (change > 0) // comment 03871 { 03872 if ( !hassel ) 03873 { 03874 if ( hasStartLineCommentMark ) 03875 addStartLineCommentToSingleLine( line, startAttrib ); 03876 else if ( hasStartStopCommentMark ) 03877 addStartStopCommentToSingleLine( line, startAttrib ); 03878 } 03879 else 03880 { 03881 // anders: prefer single line comment to avoid nesting probs 03882 // If the selection starts after first char in the first line 03883 // or ends before the last char of the last line, we may use 03884 // multiline comment markers. 03885 // TODO We should try to detect nesting. 03886 // - if selection ends at col 0, most likely she wanted that 03887 // line ignored 03888 if ( hasStartStopCommentMark && 03889 ( !hasStartLineCommentMark || ( 03890 ( v->selStartCol() > m_buffer->plainLine( v->selStartLine() )->firstChar() ) || 03891 ( v->selEndCol() < ((int)m_buffer->plainLine( v->selEndLine() )->length()) ) 03892 ) ) ) 03893 addStartStopCommentToSelection( v, startAttrib ); 03894 else if ( hasStartLineCommentMark ) 03895 addStartLineCommentToSelection( v, startAttrib ); 03896 } 03897 } 03898 else // uncomment 03899 { 03900 if ( !hassel ) 03901 { 03902 removed = ( hasStartLineCommentMark 03903 && removeStartLineCommentFromSingleLine( line, startAttrib ) ) 03904 || ( hasStartStopCommentMark 03905 && removeStartStopCommentFromSingleLine( line, startAttrib ) ); 03906 if ((!removed) && foldingTree()) { 03907 kdDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)"<<endl; 03908 int commentRegion=(highlight()->commentRegion(startAttrib)); 03909 if (commentRegion){ 03910 KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column); 03911 if (n) { 03912 KateTextCursor start,end; 03913 if ((n->nodeType()==commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) { 03914 kdDebug(13020)<<"Enclosing region found:"<<start.col()<<"/"<<start.line()<<"-"<<end.col()<<"/"<<end.line()<<endl; 03915 removeStartStopCommentFromRegion(start,end,startAttrib); 03916 } else { 03917 kdDebug(13020)<<"Enclosing region found, but not valid"<<endl; 03918 kdDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion<<endl; 03919 } 03920 //perhaps nested regions should be hadled here too... 03921 } else kdDebug(13020)<<"No enclosing region found"<<endl; 03922 } else kdDebug(13020)<<"No comment region specified for current hl"<<endl; 03923 } 03924 } 03925 else 03926 { 03927 // anders: this seems like it will work with above changes :) 03928 removed = ( hasStartLineCommentMark 03929 && removeStartLineCommentFromSelection( v, startAttrib ) ) 03930 || ( hasStartStopCommentMark 03931 && removeStartStopCommentFromSelection( v, startAttrib ) ); 03932 } 03933 } 03934 } 03935 03936 void KateDocument::transform( KateView *v, const KateTextCursor &c, 03937 KateDocument::TextTransform t ) 03938 { 03939 editStart(); 03940 uint cl( c.line() ), cc( c.col() ); 03941 bool selectionRestored = false; 03942 03943 if ( hasSelection() ) 03944 { 03945 // cache the selection and cursor, so we can be sure to restore. 03946 KateTextCursor selstart = v->selStart(); 03947 KateTextCursor selend = v->selEnd(); 03948 03949 int ln = v->selStartLine(); 03950 while ( ln <= selend.line() ) 03951 { 03952 uint start, end; 03953 start = (ln == selstart.line() || v->blockSelectionMode()) ? 03954 selstart.col() : 0; 03955 end = (ln == selend.line() || v->blockSelectionMode()) ? 03956 selend.col() : lineLength( ln ); 03957 if ( start > end ) 03958 { 03959 uint t = start; 03960 start = end; 03961 end = t; 03962 } 03963 QString s = text( ln, start, ln, end ); 03964 QString o = s; 03965 03966 if ( t == Uppercase ) 03967 s = s.upper(); 03968 else if ( t == Lowercase ) 03969 s = s.lower(); 03970 else // Capitalize 03971 { 03972 KateTextLine::Ptr l = m_buffer->plainLine( ln ); 03973 uint p ( 0 ); 03974 while( p < s.length() ) 03975 { 03976 // If bol or the character before is not in a word, up this one: 03977 // 1. if both start and p is 0, upper char. 03978 // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper 03979 // 3. if p-1 is not in a word, upper. 03980 if ( ( ! start && ! p ) || 03981 ( ( ln == selstart.line() || v->blockSelectionMode() ) && 03982 ! p && ! highlight()->isInWord( l->getChar( start - 1 )) ) || 03983 ( p && ! highlight()->isInWord( s.at( p-1 ) ) ) 03984 ) 03985 s[p] = s.at(p).upper(); 03986 p++; 03987 } 03988 } 03989 03990 if ( o != s ) 03991 { 03992 removeText( ln, start, ln, end ); 03993 insertText( ln, start, s ); 03994 } 03995 03996 ln++; 03997 } 03998 03999 // restore selection 04000 v->setSelection( selstart, selend ); 04001 selectionRestored = true; 04002 04003 } else { // no selection 04004 QString o = text( cl, cc, cl, cc + 1 ); 04005 QString s; 04006 int n ( cc ); 04007 switch ( t ) { 04008 case Uppercase: 04009 s = o.upper(); 04010 break; 04011 case Lowercase: 04012 s = o.lower(); 04013 break; 04014 case Capitalize: 04015 { 04016 KateTextLine::Ptr l = m_buffer->plainLine( cl ); 04017 while ( n > 0 && highlight()->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) ) 04018 n--; 04019 o = text( cl, n, cl, n + 1 ); 04020 s = o.upper(); 04021 } 04022 break; 04023 default: 04024 break; 04025 } 04026 04027 if ( s != o ) 04028 { 04029 removeText( cl, n, cl, n+1 ); 04030 insertText( cl, n, s ); 04031 } 04032 } 04033 editEnd(); 04034 04035 if ( ! selectionRestored ) 04036 v->setCursorPosition( cl, cc ); 04037 } 04038 04039 void KateDocument::joinLines( uint first, uint last ) 04040 { 04041 // if ( first == last ) last += 1; 04042 editStart(); 04043 int line( first ); 04044 while ( first < last ) 04045 { 04046 // Normalize the whitespace in the joined lines by making sure there's 04047 // always exactly one space between the joined lines 04048 // This cannot be done in editUnwrapLine, because we do NOT want this 04049 // behaviour when deleting from the start of a line, just when explicitly 04050 // calling the join command 04051 KateTextLine::Ptr l = m_buffer->line( line ); 04052 KateTextLine::Ptr tl = m_buffer->line( line + 1 ); 04053 04054 if ( !l || !tl ) 04055 { 04056 editEnd(); 04057 return; 04058 } 04059 04060 int pos = tl->firstChar(); 04061 if ( pos >= 0 ) 04062 { 04063 if (pos != 0) 04064 editRemoveText( line + 1, 0, pos ); 04065 if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) ) 04066 editInsertText( line + 1, 0, " " ); 04067 } 04068 else 04069 { 04070 // Just remove the whitespace and let Kate handle the rest 04071 editRemoveText( line + 1, 0, tl->length() ); 04072 } 04073 04074 editUnWrapLine( line ); 04075 first++; 04076 } 04077 editEnd(); 04078 } 04079 04080 QString KateDocument::getWord( const KateTextCursor& cursor ) { 04081 int start, end, len; 04082 04083 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line()); 04084 len = textLine->length(); 04085 start = end = cursor.col(); 04086 if (start > len) // Probably because of non-wrapping cursor mode. 04087 return QString(""); 04088 04089 while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--; 04090 while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(end))) end++; 04091 len = end - start; 04092 return QString(&textLine->text()[start], len); 04093 } 04094 04095 void KateDocument::tagLines(int start, int end) 04096 { 04097 for (uint z = 0; z < m_views.count(); z++) 04098 m_views.at(z)->tagLines (start, end, true); 04099 } 04100 04101 void KateDocument::tagLines(KateTextCursor start, KateTextCursor end) 04102 { 04103 // May need to switch start/end cols if in block selection mode 04104 if (blockSelectionMode() && start.col() > end.col()) { 04105 int sc = start.col(); 04106 start.setCol(end.col()); 04107 end.setCol(sc); 04108 } 04109 04110 for (uint z = 0; z < m_views.count(); z++) 04111 m_views.at(z)->tagLines(start, end, true); 04112 } 04113 04114 void KateDocument::repaintViews(bool paintOnlyDirty) 04115 { 04116 for (uint z = 0; z < m_views.count(); z++) 04117 m_views.at(z)->repaintText(paintOnlyDirty); 04118 } 04119 04120 void KateDocument::tagAll() 04121 { 04122 for (uint z = 0; z < m_views.count(); z++) 04123 { 04124 m_views.at(z)->tagAll(); 04125 m_views.at(z)->updateView (true); 04126 } 04127 } 04128 04129 uint KateDocument::configFlags () 04130 { 04131 return config()->configFlags(); 04132 } 04133 04134 void KateDocument::setConfigFlags (uint flags) 04135 { 04136 config()->setConfigFlags(flags); 04137 } 04138 04139 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; } 04140 inline bool isEndBracket ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; } 04141 inline bool isBracket ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); } 04142 04143 /* 04144 Bracket matching uses the following algorithm: 04145 If in overwrite mode, match the bracket currently underneath the cursor. 04146 Otherwise, if the character to the right of the cursor is an starting bracket, 04147 match it. Otherwise if the character to the left of the cursor is a 04148 ending bracket, match it. Otherwise, if the the character to the left 04149 of the cursor is an starting bracket, match it. Otherwise, if the character 04150 to the right of the cursor is an ending bracket, match it. Otherwise, don't 04151 match anything. 04152 */ 04153 void KateDocument::newBracketMark( const KateTextCursor& cursor, KateBracketRange& bm, int maxLines ) 04154 { 04155 bm.setValid(false); 04156 04157 bm.start() = cursor; 04158 04159 if( !findMatchingBracket( bm.start(), bm.end(), maxLines ) ) 04160 return; 04161 04162 bm.setValid(true); 04163 04164 const int tw = config()->tabWidth(); 04165 const int indentStart = m_buffer->plainLine(bm.start().line())->indentDepth(tw); 04166 const int indentEnd = m_buffer->plainLine(bm.end().line())->indentDepth(tw); 04167 bm.setIndentMin(kMin(indentStart, indentEnd)); 04168 } 04169 04170 bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines ) 04171 { 04172 KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() ); 04173 if( !textLine ) 04174 return false; 04175 04176 QChar right = textLine->getChar( start.col() ); 04177 QChar left = textLine->getChar( start.col() - 1 ); 04178 QChar bracket; 04179 04180 if ( config()->configFlags() & cfOvr ) { 04181 if( isBracket( right ) ) { 04182 bracket = right; 04183 } else { 04184 return false; 04185 } 04186 } else if ( isStartBracket( right ) ) { 04187 bracket = right; 04188 } else if ( isEndBracket( left ) ) { 04189 start.setCol(start.col() - 1); 04190 bracket = left; 04191 } else if ( isBracket( left ) ) { 04192 start.setCol(start.col() - 1); 04193 bracket = left; 04194 } else if ( isBracket( right ) ) { 04195 bracket = right; 04196 } else { 04197 return false; 04198 } 04199 04200 QChar opposite; 04201 04202 switch( bracket ) { 04203 case '{': opposite = '}'; break; 04204 case '}': opposite = '{'; break; 04205 case '[': opposite = ']'; break; 04206 case ']': opposite = '['; break; 04207 case '(': opposite = ')'; break; 04208 case ')': opposite = '('; break; 04209 default: return false; 04210 } 04211 04212 bool forward = isStartBracket( bracket ); 04213 int startAttr = textLine->attribute( start.col() ); 04214 uint count = 0; 04215 int lines = 0; 04216 end = start; 04217 04218 while( true ) { 04219 /* Increment or decrement, check base cases */ 04220 if( forward ) { 04221 end.setCol(end.col() + 1); 04222 if( end.col() >= lineLength( end.line() ) ) { 04223 if( end.line() >= (int)lastLine() ) 04224 return false; 04225 end.setPos(end.line() + 1, 0); 04226 textLine = m_buffer->plainLine( end.line() ); 04227 lines++; 04228 } 04229 } else { 04230 end.setCol(end.col() - 1); 04231 if( end.col() < 0 ) { 04232 if( end.line() <= 0 ) 04233 return false; 04234 end.setLine(end.line() - 1); 04235 end.setCol(lineLength( end.line() ) - 1); 04236 textLine = m_buffer->plainLine( end.line() ); 04237 lines++; 04238 } 04239 } 04240 04241 if ((maxLines != -1) && (lines > maxLines)) 04242 return false; 04243 04244 /* Easy way to skip comments */ 04245 if( textLine->attribute( end.col() ) != startAttr ) 04246 continue; 04247 04248 /* Check for match */ 04249 QChar c = textLine->getChar( end.col() ); 04250 if( c == bracket ) { 04251 count++; 04252 } else if( c == opposite ) { 04253 if( count == 0 ) 04254 return true; 04255 count--; 04256 } 04257 04258 } 04259 } 04260 04261 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev ) 04262 { 04263 KParts::ReadWritePart::guiActivateEvent( ev ); 04264 if ( ev->activated() ) 04265 emit selectionChanged(); 04266 } 04267 04268 void KateDocument::setDocName (QString name ) 04269 { 04270 if ( name == m_docName ) 04271 return; 04272 04273 if ( !name.isEmpty() ) 04274 { 04275 // TODO check for similarly named documents 04276 m_docName = name; 04277 updateFileType (KateFactory::self()->fileTypeManager()->fileType (this)); 04278 emit nameChanged((Kate::Document *) this); 04279 return; 04280 } 04281 04282 // if the name is set, and starts with FILENAME, it should not be changed! 04283 if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return; 04284 04285 int count = -1; 04286 04287 for (uint z=0; z < KateFactory::self()->documents()->count(); z++) 04288 { 04289 if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) ) 04290 if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count ) 04291 count = KateFactory::self()->documents()->at(z)->m_docNameNumber; 04292 } 04293 04294 m_docNameNumber = count + 1; 04295 04296 m_docName = url().filename(); 04297 04298 if (m_docName.isEmpty()) 04299 m_docName = i18n ("Untitled"); 04300 04301 if (m_docNameNumber > 0) 04302 m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1); 04303 04304 updateFileType (KateFactory::self()->fileTypeManager()->fileType (this)); 04305 emit nameChanged ((Kate::Document *) this); 04306 } 04307 04308 void KateDocument::slotModifiedOnDisk( Kate::View * /*v*/ ) 04309 { 04310 if ( m_isasking < 0 ) 04311 { 04312 m_isasking = 0; 04313 return; 04314 } 04315 04316 if ( !s_fileChangedDialogsActivated || m_isasking ) 04317 return; 04318 04319 if (m_modOnHd && !url().isEmpty()) 04320 { 04321 m_isasking = 1; 04322 04323 KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), widget() ); 04324 switch ( p.exec() ) 04325 { 04326 case KateModOnHdPrompt::Save: 04327 { 04328 m_modOnHd = false; 04329 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(), 04330 url().url(),QString::null,widget(),i18n("Save File")); 04331 04332 kdDebug(13020)<<"got "<<res.URLs.count()<<" URLs"<<endl; 04333 if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first() ) ) 04334 { 04335 setEncoding( res.encoding ); 04336 04337 if( ! saveAs( res.URLs.first() ) ) 04338 { 04339 KMessageBox::error( widget(), i18n("Save failed") ); 04340 m_modOnHd = true; 04341 } 04342 else 04343 emit modifiedOnDisc( this, false, 0 ); 04344 } 04345 else // the save as dialog was cancelled, we are still modified on disk 04346 { 04347 m_modOnHd = true; 04348 } 04349 04350 m_isasking = 0; 04351 break; 04352 } 04353 04354 case KateModOnHdPrompt::Reload: 04355 m_modOnHd = false; 04356 emit modifiedOnDisc( this, false, 0 ); 04357 reloadFile(); 04358 m_isasking = 0; 04359 break; 04360 04361 case KateModOnHdPrompt::Ignore: 04362 m_modOnHd = false; 04363 emit modifiedOnDisc( this, false, 0 ); 04364 m_isasking = 0; 04365 break; 04366 04367 case KateModOnHdPrompt::Overwrite: 04368 m_modOnHd = false; 04369 emit modifiedOnDisc( this, false, 0 ); 04370 m_isasking = 0; 04371 save(); 04372 break; 04373 04374 default: // cancel: ignore next focus event 04375 m_isasking = -1; 04376 } 04377 } 04378 } 04379 04380 void KateDocument::setModifiedOnDisk( int reason ) 04381 { 04382 m_modOnHdReason = reason; 04383 m_modOnHd = (reason > 0); 04384 emit modifiedOnDisc( this, (reason > 0), reason ); 04385 } 04386 04387 class KateDocumentTmpMark 04388 { 04389 public: 04390 QString line; 04391 KTextEditor::Mark mark; 04392 }; 04393 04394 void KateDocument::reloadFile() 04395 { 04396 if ( !url().isEmpty() ) 04397 { 04398 if (m_modOnHd && s_fileChangedDialogsActivated) 04399 { 04400 int i = KMessageBox::warningYesNoCancel 04401 (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"), 04402 i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes")); 04403 04404 if ( i != KMessageBox::Yes) 04405 { 04406 if (i == KMessageBox::No) 04407 { 04408 m_modOnHd = false; 04409 m_modOnHdReason = 0; 04410 emit modifiedOnDisc (this, m_modOnHd, 0); 04411 } 04412 04413 return; 04414 } 04415 } 04416 04417 QValueList<KateDocumentTmpMark> tmp; 04418 04419 for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it ) 04420 { 04421 KateDocumentTmpMark m; 04422 04423 m.line = textLine (it.current()->line); 04424 m.mark = *it.current(); 04425 04426 tmp.append (m); 04427 } 04428 04429 uint mode = hlMode (); 04430 bool byUser = hlSetByUser; 04431 04432 m_storedVariables.clear(); 04433 04434 m_reloading = true; 04435 04436 QValueList<int> lines, cols; 04437 for ( uint i=0; i < m_views.count(); i++ ) 04438 { 04439 lines.append( m_views.at( i )->cursorLine() ); 04440 cols.append( m_views.at( i )->cursorColumn() ); 04441 } 04442 04443 KateDocument::openURL( url() ); 04444 04445 for ( uint i=0; i < m_views.count(); i++ ) 04446 m_views.at( i )->setCursorPositionInternal( lines[ i ], cols[ i ], m_config->tabWidth(), false ); 04447 04448 m_reloading = false; 04449 04450 for ( QValueList<int>::size_type z=0; z < tmp.size(); z++ ) 04451 { 04452 if (z < numLines()) 04453 { 04454 if (textLine(tmp[z].mark.line) == tmp[z].line) 04455 setMark (tmp[z].mark.line, tmp[z].mark.type); 04456 } 04457 } 04458 04459 if (byUser) 04460 setHlMode (mode); 04461 } 04462 } 04463 04464 void KateDocument::flush () 04465 { 04466 closeURL (); 04467 } 04468 04469 void KateDocument::setWordWrap (bool on) 04470 { 04471 config()->setWordWrap (on); 04472 } 04473 04474 bool KateDocument::wordWrap () 04475 { 04476 return config()->wordWrap (); 04477 } 04478 04479 void KateDocument::setWordWrapAt (uint col) 04480 { 04481 config()->setWordWrapAt (col); 04482 } 04483 04484 unsigned int KateDocument::wordWrapAt () 04485 { 04486 return config()->wordWrapAt (); 04487 } 04488 04489 void KateDocument::applyWordWrap () 04490 { 04491 // dummy to make the API happy 04492 } 04493 04494 void KateDocument::setPageUpDownMovesCursor (bool on) 04495 { 04496 config()->setPageUpDownMovesCursor (on); 04497 } 04498 04499 bool KateDocument::pageUpDownMovesCursor () 04500 { 04501 return config()->pageUpDownMovesCursor (); 04502 } 04503 04504 void KateDocument::dumpRegionTree() 04505 { 04506 m_buffer->foldingTree()->debugDump(); 04507 } 04508 //END 04509 04510 //BEGIN KTextEditor::CursorInterface stuff 04511 04512 KTextEditor::Cursor *KateDocument::createCursor ( ) 04513 { 04514 return new KateSuperCursor (this, false, 0, 0, this); 04515 } 04516 04517 void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range) 04518 { 04519 if (view) 04520 view->tagLines(range->start(), range->end()); 04521 else 04522 tagLines(range->start(), range->end()); 04523 } 04524 04525 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line) 04526 { 04527 m_buffer->lineInfo(info,line); 04528 } 04529 04530 KateCodeFoldingTree *KateDocument::foldingTree () 04531 { 04532 return m_buffer->foldingTree(); 04533 } 04534 04535 void KateDocument::setEncoding (const QString &e) 04536 { 04537 if ( m_encodingSticky ) 04538 return; 04539 04540 QString ce = m_config->encoding().lower(); 04541 if ( e.lower() == ce ) 04542 return; 04543 04544 m_config->setEncoding( e ); 04545 if ( ! m_loading ) 04546 reloadFile(); 04547 } 04548 04549 QString KateDocument::encoding() const 04550 { 04551 return m_config->encoding(); 04552 } 04553 04554 void KateDocument::updateConfig () 04555 { 04556 emit undoChanged (); 04557 tagAll(); 04558 04559 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) 04560 { 04561 view->updateDocumentConfig (); 04562 } 04563 04564 // switch indenter if needed 04565 if (m_indenter->modeNumber() != m_config->indentationMode()) 04566 { 04567 delete m_indenter; 04568 m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() ); 04569 } 04570 04571 m_indenter->updateConfig(); 04572 04573 m_buffer->setTabWidth (config()->tabWidth()); 04574 04575 // plugins 04576 for (uint i=0; i<KateFactory::self()->plugins().count(); i++) 04577 { 04578 if (config()->plugin (i)) 04579 loadPlugin (i); 04580 else 04581 unloadPlugin (i); 04582 } 04583 } 04584 04585 //BEGIN Variable reader 04586 // "local variable" feature by anders, 2003 04587 /* TODO 04588 add config options (how many lines to read, on/off) 04589 add interface for plugins/apps to set/get variables 04590 add view stuff 04591 */ 04592 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)"); 04593 QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)"); 04594 QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)"); 04595 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)"); 04596 04597 void KateDocument::readVariables(bool onlyViewAndRenderer) 04598 { 04599 if (!onlyViewAndRenderer) 04600 m_config->configStart(); 04601 04602 // views! 04603 KateView *v; 04604 for (v = m_views.first(); v != 0L; v= m_views.next() ) 04605 { 04606 v->config()->configStart(); 04607 v->renderer()->config()->configStart(); 04608 } 04609 // read a number of lines in the top/bottom of the document 04610 for (uint i=0; i < kMin( 9U, numLines() ); ++i ) 04611 { 04612 readVariableLine( textLine( i ), onlyViewAndRenderer ); 04613 } 04614 if ( numLines() > 10 ) 04615 { 04616 for ( uint i = kMax(10U, numLines() - 10); i < numLines(); ++i ) 04617 { 04618 readVariableLine( textLine( i ), onlyViewAndRenderer ); 04619 } 04620 } 04621 04622 if (!onlyViewAndRenderer) 04623 m_config->configEnd(); 04624 04625 for (v = m_views.first(); v != 0L; v= m_views.next() ) 04626 { 04627 v->config()->configEnd(); 04628 v->renderer()->config()->configEnd(); 04629 } 04630 } 04631 04632 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer ) 04633 { 04634 // simple check first, no regex 04635 // no kate inside, no vars, simple... 04636 if (t.find("kate") < 0) 04637 return; 04638 04639 // found vars, if any 04640 QString s; 04641 04642 if ( kvLine.search( t ) > -1 ) 04643 { 04644 s = kvLine.cap(1); 04645 04646 kdDebug (13020) << "normal variable line kate: matched: " << s << endl; 04647 } 04648 else if (kvLineWildcard.search( t ) > -1) // regex given 04649 { 04650 QStringList wildcards (QStringList::split(';', kvLineWildcard.cap(1))); 04651 QString nameOfFile = url().fileName(); 04652 04653 bool found = false; 04654 for (QStringList::size_type i = 0; !found && i < wildcards.size(); ++i) 04655 { 04656 QRegExp wildcard (wildcards[i], true/*Qt::CaseSensitive*/, true/*QRegExp::Wildcard*/); 04657 04658 found = wildcard.exactMatch (nameOfFile); 04659 } 04660 04661 // nothing usable found... 04662 if (!found) 04663 return; 04664 04665 s = kvLineWildcard.cap(2); 04666 04667 kdDebug (13020) << "guarded variable line kate-wildcard: matched: " << s << endl; 04668 } 04669 else if (kvLineMime.search( t ) > -1) // mime-type given 04670 { 04671 QStringList types (QStringList::split(';', kvLineMime.cap(1))); 04672 04673 // no matching type found 04674 if (!types.contains (mimeType ())) 04675 return; 04676 04677 s = kvLineMime.cap(2); 04678 04679 kdDebug (13020) << "guarded variable line kate-mimetype: matched: " << s << endl; 04680 } 04681 else // nothing found 04682 { 04683 return; 04684 } 04685 04686 QStringList vvl; // view variable names 04687 vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators" 04688 << "line-numbers" << "icon-border" << "folding-markers" 04689 << "bookmark-sorting" << "auto-center-lines" 04690 << "icon-bar-color" 04691 // renderer 04692 << "background-color" << "selection-color" 04693 << "current-line-color" << "bracket-highlight-color" 04694 << "word-wrap-marker-color" 04695 << "font" << "font-size" << "scheme"; 04696 int p( 0 ); 04697 04698 QString var, val; 04699 while ( (p = kvVar.search( s, p )) > -1 ) 04700 { 04701 p += kvVar.matchedLength(); 04702 var = kvVar.cap( 1 ); 04703 val = kvVar.cap( 2 ).stripWhiteSpace(); 04704 bool state; // store booleans here 04705 int n; // store ints here 04706 04707 // only apply view & renderer config stuff 04708 if (onlyViewAndRenderer) 04709 { 04710 if ( vvl.contains( var ) ) // FIXME define above 04711 setViewVariable( var, val ); 04712 } 04713 else 04714 { 04715 // BOOL SETTINGS 04716 if ( var == "word-wrap" && checkBoolValue( val, &state ) ) 04717 setWordWrap( state ); // ??? FIXME CHECK 04718 else if ( var == "block-selection" && checkBoolValue( val, &state ) ) 04719 setBlockSelectionMode( state ); 04720 // KateConfig::configFlags 04721 // FIXME should this be optimized to only a few calls? how? 04722 else if ( var == "backspace-indents" && checkBoolValue( val, &state ) ) 04723 m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state ); 04724 else if ( var == "replace-tabs" && checkBoolValue( val, &state ) ) 04725 m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state ); 04726 else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) ) 04727 m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state ); 04728 else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) ) 04729 m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state ); 04730 else if ( var == "auto-brackets" && checkBoolValue( val, &state ) ) 04731 m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state ); 04732 else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) ) 04733 m_config->setConfigFlags( KateDocumentConfig::cfOvr, state ); 04734 else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) ) 04735 m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state ); 04736 else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) ) 04737 m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state ); 04738 else if ( var == "tab-indents" && checkBoolValue( val, &state ) ) 04739 m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state ); 04740 else if ( var == "show-tabs" && checkBoolValue( val, &state ) ) 04741 m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state ); 04742 else if ( var == "space-indent" && checkBoolValue( val, &state ) ) 04743 m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state ); 04744 else if ( var == "smart-home" && checkBoolValue( val, &state ) ) 04745 m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state ); 04746 else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) ) 04747 m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state ); 04748 else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) ) 04749 m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state); 04750 else if ( var == "mixed-indent" && checkBoolValue( val, &state ) ) 04751 m_config->setConfigFlags( KateDocumentConfig::cfMixedIndent, state ); 04752 04753 // INTEGER SETTINGS 04754 else if ( var == "tab-width" && checkIntValue( val, &n ) ) 04755 m_config->setTabWidth( n ); 04756 else if ( var == "indent-width" && checkIntValue( val, &n ) ) 04757 m_config->setIndentationWidth( n ); 04758 else if ( var == "indent-mode" ) 04759 { 04760 if ( checkIntValue( val, &n ) ) 04761 m_config->setIndentationMode( n ); 04762 else 04763 m_config->setIndentationMode( KateAutoIndent::modeNumber( val) ); 04764 } 04765 else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 ) // uint, but hard word wrap at 0 will be no fun ;) 04766 m_config->setWordWrapAt( n ); 04767 else if ( var == "undo-steps" && checkIntValue( val, &n ) && n >= 0 ) 04768 setUndoSteps( n ); 04769 04770 // STRING SETTINGS 04771 else if ( var == "eol" || var == "end-of-line" ) 04772 { 04773 QStringList l; 04774 l << "unix" << "dos" << "mac"; 04775 if ( (n = l.findIndex( val.lower() )) != -1 ) 04776 m_config->setEol( n ); 04777 } 04778 else if ( var == "encoding" ) 04779 m_config->setEncoding( val ); 04780 else if ( var == "syntax" || var == "hl" ) 04781 { 04782 for ( uint i=0; i < hlModeCount(); i++ ) 04783 { 04784 if ( hlModeName( i ).lower() == val.lower() ) 04785 { 04786 setHlMode( i ); 04787 break; 04788 } 04789 } 04790 } 04791 04792 // VIEW SETTINGS 04793 else if ( vvl.contains( var ) ) 04794 setViewVariable( var, val ); 04795 else 04796 { 04797 m_storedVariables.insert( var, val ); 04798 emit variableChanged( var, val ); 04799 } 04800 } 04801 } 04802 } 04803 04804 void KateDocument::setViewVariable( QString var, QString val ) 04805 { 04806 KateView *v; 04807 bool state; 04808 int n; 04809 QColor c; 04810 for (v = m_views.first(); v != 0L; v= m_views.next() ) 04811 { 04812 if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) ) 04813 v->config()->setDynWordWrap( state ); 04814 else if ( var == "persistent-selection" && checkBoolValue( val, &state ) ) 04815 v->config()->setPersistentSelection( state ); 04816 //else if ( var = "dynamic-word-wrap-indicators" ) 04817 else if ( var == "line-numbers" && checkBoolValue( val, &state ) ) 04818 v->config()->setLineNumbers( state ); 04819 else if (var == "icon-border" && checkBoolValue( val, &state ) ) 04820 v->config()->setIconBar( state ); 04821 else if (var == "folding-markers" && checkBoolValue( val, &state ) ) 04822 v->config()->setFoldingBar( state ); 04823 else if ( var == "auto-center-lines" && checkIntValue( val, &n ) ) 04824 v->config()->setAutoCenterLines( n ); // FIXME uint, > N ?? 04825 else if ( var == "icon-bar-color" && checkColorValue( val, c ) ) 04826 v->renderer()->config()->setIconBarColor( c ); 04827 // RENDERER 04828 else if ( var == "background-color" && checkColorValue( val, c ) ) 04829 v->renderer()->config()->setBackgroundColor( c ); 04830 else if ( var == "selection-color" && checkColorValue( val, c ) ) 04831 v->renderer()->config()->setSelectionColor( c ); 04832 else if ( var == "current-line-color" && checkColorValue( val, c ) ) 04833 v->renderer()->config()->setHighlightedLineColor( c ); 04834 else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) ) 04835 v->renderer()->config()->setHighlightedBracketColor( c ); 04836 else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) ) 04837 v->renderer()->config()->setWordWrapMarkerColor( c ); 04838 else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) ) 04839 { 04840 QFont _f( *v->renderer()->config()->font( ) ); 04841 04842 if ( var == "font" ) 04843 { 04844 _f.setFamily( val ); 04845 _f.setFixedPitch( QFont( val ).fixedPitch() ); 04846 } 04847 else 04848 _f.setPointSize( n ); 04849 04850 v->renderer()->config()->setFont( _f ); 04851 } 04852 else if ( var == "scheme" ) 04853 { 04854 v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) ); 04855 } 04856 } 04857 } 04858 04859 bool KateDocument::checkBoolValue( QString val, bool *result ) 04860 { 04861 val = val.stripWhiteSpace().lower(); 04862 QStringList l; 04863 l << "1" << "on" << "true"; 04864 if ( l.contains( val ) ) 04865 { 04866 *result = true; 04867 return true; 04868 } 04869 l.clear(); 04870 l << "0" << "off" << "false"; 04871 if ( l.contains( val ) ) 04872 { 04873 *result = false; 04874 return true; 04875 } 04876 return false; 04877 } 04878 04879 bool KateDocument::checkIntValue( QString val, int *result ) 04880 { 04881 bool ret( false ); 04882 *result = val.toInt( &ret ); 04883 return ret; 04884 } 04885 04886 bool KateDocument::checkColorValue( QString val, QColor &c ) 04887 { 04888 c.setNamedColor( val ); 04889 return c.isValid(); 04890 } 04891 04892 // KTextEditor::variable 04893 QString KateDocument::variable( const QString &name ) const 04894 { 04895 if ( m_storedVariables.contains( name ) ) 04896 return m_storedVariables[ name ]; 04897 04898 return ""; 04899 } 04900 04901 //END 04902 04903 void KateDocument::slotModOnHdDirty (const QString &path) 04904 { 04905 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1)) 04906 { 04907 // compare md5 with the one we have (if we have one) 04908 if ( ! m_digest.isEmpty() ) 04909 { 04910 QCString tmp; 04911 if ( createDigest( tmp ) && tmp == m_digest ) 04912 return; 04913 } 04914 04915 m_modOnHd = true; 04916 m_modOnHdReason = 1; 04917 04918 // reenable dialog if not running atm 04919 if (m_isasking == -1) 04920 m_isasking = false; 04921 04922 emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason); 04923 } 04924 } 04925 04926 void KateDocument::slotModOnHdCreated (const QString &path) 04927 { 04928 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2)) 04929 { 04930 m_modOnHd = true; 04931 m_modOnHdReason = 2; 04932 04933 // reenable dialog if not running atm 04934 if (m_isasking == -1) 04935 m_isasking = false; 04936 04937 emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason); 04938 } 04939 } 04940 04941 void KateDocument::slotModOnHdDeleted (const QString &path) 04942 { 04943 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3)) 04944 { 04945 m_modOnHd = true; 04946 m_modOnHdReason = 3; 04947 04948 // reenable dialog if not running atm 04949 if (m_isasking == -1) 04950 m_isasking = false; 04951 04952 emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason); 04953 } 04954 } 04955 04956 bool KateDocument::createDigest( QCString &result ) 04957 { 04958 bool ret = false; 04959 result = ""; 04960 if ( url().isLocalFile() ) 04961 { 04962 QFile f ( url().path() ); 04963 if ( f.open( IO_ReadOnly) ) 04964 { 04965 KMD5 md5; 04966 ret = md5.update( f ); 04967 md5.hexDigest( result ); 04968 f.close(); 04969 ret = true; 04970 } 04971 } 04972 return ret; 04973 } 04974 04975 QString KateDocument::reasonedMOHString() const 04976 { 04977 switch( m_modOnHdReason ) 04978 { 04979 case 1: 04980 return i18n("The file '%1' was modified by another program.").arg( url().prettyURL() ); 04981 break; 04982 case 2: 04983 return i18n("The file '%1' was created by another program.").arg( url().prettyURL() ); 04984 break; 04985 case 3: 04986 return i18n("The file '%1' was deleted by another program.").arg( url().prettyURL() ); 04987 break; 04988 default: 04989 return QString(); 04990 } 04991 } 04992 04993 void KateDocument::removeTrailingSpace( uint line ) 04994 { 04995 // remove trailing spaces from left line if required 04996 if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn ) 04997 { 04998 KateTextLine::Ptr ln = kateTextLine( line ); 04999 05000 if ( ! ln ) return; 05001 05002 if ( line == activeView()->cursorLine() 05003 && activeView()->cursorColumnReal() >= (uint)kMax(0,ln->lastChar()) ) 05004 return; 05005 05006 if ( ln->length() ) 05007 { 05008 uint p = ln->lastChar() + 1; 05009 uint l = ln->length() - p; 05010 if ( l ) 05011 editRemoveText( line, p, l); 05012 } 05013 } 05014 } 05015 05016 void KateDocument::updateFileType (int newType, bool user) 05017 { 05018 if (user || !m_fileTypeSetByUser) 05019 { 05020 const KateFileType *t = 0; 05021 if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType))) 05022 { 05023 m_fileType = newType; 05024 05025 if (t) 05026 { 05027 m_config->configStart(); 05028 // views! 05029 KateView *v; 05030 for (v = m_views.first(); v != 0L; v= m_views.next() ) 05031 { 05032 v->config()->configStart(); 05033 v->renderer()->config()->configStart(); 05034 } 05035 05036 readVariableLine( t->varLine ); 05037 05038 m_config->configEnd(); 05039 for (v = m_views.first(); v != 0L; v= m_views.next() ) 05040 { 05041 v->config()->configEnd(); 05042 v->renderer()->config()->configEnd(); 05043 } 05044 } 05045 } 05046 } 05047 } 05048 05049 uint KateDocument::documentNumber () const 05050 { 05051 return KTextEditor::Document::documentNumber (); 05052 } 05053 05054 05055 05056 05057 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) { 05058 *handled=true; 05059 *abortClosing=true; 05060 if (m_url.isEmpty()) 05061 { 05062 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(), 05063 QString::null,QString::null,0,i18n("Save File")); 05064 05065 if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) { 05066 *abortClosing=true; 05067 return; 05068 } 05069 setEncoding( res.encoding ); 05070 saveAs( res.URLs.first() ); 05071 *abortClosing=false; 05072 } 05073 else 05074 { 05075 save(); 05076 *abortClosing=false; 05077 } 05078 05079 } 05080 05081 bool KateDocument::checkOverwrite( KURL u ) 05082 { 05083 if( !u.isLocalFile() ) 05084 return true; 05085 05086 QFileInfo info( u.path() ); 05087 if( !info.exists() ) 05088 return true; 05089 05090 return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0, 05091 i18n( "A file named \"%1\" already exists. " 05092 "Are you sure you want to overwrite it?" ).arg( info.fileName() ), 05093 i18n( "Overwrite File?" ), 05094 i18n( "&Overwrite" ) ); 05095 } 05096 05097 void KateDocument::setDefaultEncoding (const QString &encoding) 05098 { 05099 s_defaultEncoding = encoding; 05100 } 05101 05102 //BEGIN KTextEditor::TemplateInterface 05103 bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) { 05104 return (new KateTemplateHandler(this,line,column,templateString,initialValues))->initOk(); 05105 } 05106 05107 void KateDocument::testTemplateCode() { 05108 int col=activeView()->cursorColumn(); 05109 int line=activeView()->cursorLine(); 05110 insertTemplateText(line,col,"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>()); 05111 } 05112 05113 bool KateDocument::invokeTabInterceptor(KKey key) { 05114 if (m_tabInterceptor) return (*m_tabInterceptor)(key); 05115 return false; 05116 } 05117 05118 bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) { 05119 if (m_tabInterceptor) return false; 05120 m_tabInterceptor=interceptor; 05121 return true; 05122 } 05123 05124 bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) { 05125 if (m_tabInterceptor!=interceptor) return false; 05126 m_tabInterceptor=0; 05127 return true; 05128 } 05129 //END KTextEditor::TemplateInterface 05130 05131 //BEGIN DEPRECATED STUFF 05132 bool KateDocument::setSelection ( uint startLine, uint startCol, uint endLine, uint endCol ) 05133 { if (m_activeView) return m_activeView->setSelection (startLine, startCol, endLine, endCol); return false; } 05134 05135 bool KateDocument::clearSelection () 05136 { if (m_activeView) return m_activeView->clearSelection(); return false; } 05137 05138 bool KateDocument::hasSelection () const 05139 { if (m_activeView) return m_activeView->hasSelection (); return false; } 05140 05141 QString KateDocument::selection () const 05142 { if (m_activeView) return m_activeView->selection (); return QString(""); } 05143 05144 bool KateDocument::removeSelectedText () 05145 { if (m_activeView) return m_activeView->removeSelectedText (); return false; } 05146 05147 bool KateDocument::selectAll() 05148 { if (m_activeView) return m_activeView->selectAll (); return false; } 05149 05150 int KateDocument::selStartLine() 05151 { if (m_activeView) return m_activeView->selStartLine (); return 0; } 05152 05153 int KateDocument::selStartCol() 05154 { if (m_activeView) return m_activeView->selStartCol (); return 0; } 05155 05156 int KateDocument::selEndLine() 05157 { if (m_activeView) return m_activeView->selEndLine (); return 0; } 05158 05159 int KateDocument::selEndCol() 05160 { if (m_activeView) return m_activeView->selEndCol (); return 0; } 05161 05162 bool KateDocument::blockSelectionMode () 05163 { if (m_activeView) return m_activeView->blockSelectionMode (); return false; } 05164 05165 bool KateDocument::setBlockSelectionMode (bool on) 05166 { if (m_activeView) return m_activeView->setBlockSelectionMode (on); return false; } 05167 05168 bool KateDocument::toggleBlockSelectionMode () 05169 { if (m_activeView) return m_activeView->toggleBlockSelectionMode (); return false; } 05170 //END DEPRECATED 05171 05172 //END DEPRECATED STUFF 05173 05174 // kate: space-indent on; indent-width 2; replace-tabs on;