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