kate Library API Documentation

katedocument.cpp

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