00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "katedocument.h"
00025 #include "katedocument.moc"
00026 #include "katekeyinterceptorfunctor.h"
00027 #include "kateglobal.h"
00028 #include "katedialogs.h"
00029 #include "katehighlight.h"
00030 #include "kateview.h"
00031 #include "kateautoindent.h"
00032 #include "katetextline.h"
00033 #include "katedocumenthelpers.h"
00034 #include "kateprinter.h"
00035 #include "katesmartcursor.h"
00036 #include "katerenderer.h"
00037 #include <ktexteditor/attribute.h>
00038 #include "kateconfig.h"
00039 #include "katemodemanager.h"
00040 #include "kateschema.h"
00041 #include "katetemplatehandler.h"
00042 #include "katesmartmanager.h"
00043 #include <ktexteditor/plugin.h>
00044 #include <ktexteditor/loadsavefiltercheckplugin.h>
00045 #include "kateedit.h"
00046 #include "katebuffer.h"
00047 #include "kateundo.h"
00048 #include "katepartpluginmanager.h"
00049
00050 #include <kio/job.h>
00051 #include <kio/jobuidelegate.h>
00052 #include <kio/netaccess.h>
00053 #include <kio/kfileitem.h>
00054
00055 #include <kparts/event.h>
00056
00057 #include <klocale.h>
00058 #include <kglobal.h>
00059 #include <kapplication.h>
00060 #include <kmenu.h>
00061 #include <kconfig.h>
00062 #include <kfiledialog.h>
00063 #include <kmessagebox.h>
00064 #include <kstandardaction.h>
00065 #include <kxmlguifactory.h>
00066 #include <kdebug.h>
00067 #include <kglobalsettings.h>
00068 #include <klibloader.h>
00069 #include <kdirwatch.h>
00070 #include <kencodingfiledialog.h>
00071 #include <ktemporaryfile.h>
00072 #include <kcodecs.h>
00073 #include <kstandarddirs.h>
00074 #include <kstringhandler.h>
00075
00076 #include <services/kservicetypetrader.h>
00077
00078 #include <QtDBus/QtDBus>
00079 #include <QtCore/QTimer>
00080 #include <QtCore/QFile>
00081 #include <QtGui/QClipboard>
00082 #include <QtCore/QTextStream>
00083 #include <QtCore/QTextCodec>
00084 #include <QtCore/QMap>
00085 #include <QtCore/QMutex>
00086
00087
00088
00089
00090
00091
00092
00093 #ifdef FAST_DEBUG_ENABLE
00094 # define FAST_DEBUG(x) (kDebug( 13020 ) << x)
00095 #else
00096 # define FAST_DEBUG(x)
00097 #endif
00098
00099
00100
00101
00102 class KatePartPluginItem
00103 {
00104 public:
00105 KTextEditor::Plugin *plugin;
00106 };
00107
00108
00109 static int dummy = 0;
00110
00111
00112 class KateDocument::LoadSaveFilterCheckPlugins
00113 {
00114 public:
00115 LoadSaveFilterCheckPlugins() {
00116 KService::List traderList = KServiceTypeTrader::self()->query("KTextEditor/LoadSaveFilterCheckPlugin");
00117
00118 foreach(const KService::Ptr &ptr, traderList)
00119 {
00120 QString libname;
00121 libname=ptr->library();
00122 libname=libname.right(libname.length()-12);
00123 m_plugins[libname]=0;
00124 }
00125
00126 }
00127 ~LoadSaveFilterCheckPlugins() {
00128 if ( m_plugins.count()==0) return;
00129 QHashIterator<QString,KTextEditor::LoadSaveFilterCheckPlugin*>i(m_plugins);
00130 while (i.hasNext())
00131 i.next();
00132 delete i.value();
00133 m_plugins.clear();
00134 }
00135 bool preSavePostDialogFilterCheck(const QString& pluginName,KateDocument *document,QWidget *parentWidget) {
00136 KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00137 if (!plug) {
00138 if (KMessageBox::warningContinueCancel (parentWidget
00139 , i18n ("The filter/check plugin '%1' could not be found, still continue saving of %2", pluginName,document->url().pathOrUrl())
00140 , i18n ("Saving problems")
00141 , KGuiItem(i18n("Save Nevertheless"))
00142 , KStandardGuiItem::cancel()) != KMessageBox::Continue)
00143 return false;
00144 else
00145 return true;
00146 }
00147 return plug->preSavePostDialogFilterCheck(document);
00148 }
00149 void postLoadFilter(const QString& pluginName,KateDocument *document) {
00150 KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00151 if (!plug) return;
00152 plug->postLoadFilter(document);
00153 }
00154 bool postSaveFilterCheck(const QString& pluginName,KateDocument *document,bool saveas) {
00155 KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00156 if (!plug) return false;
00157 return plug->postSaveFilterCheck(document,saveas);
00158 }
00159 private:
00160 KTextEditor::LoadSaveFilterCheckPlugin *getPlugin(const QString & pluginName)
00161 {
00162 if (!m_plugins.contains(pluginName)) return 0;
00163 if (!m_plugins[pluginName]) {
00164 m_plugins[pluginName]=KLibLoader::createInstance<KTextEditor::LoadSaveFilterCheckPlugin>( "ktexteditor_"+pluginName);
00165 }
00166 return m_plugins[pluginName];
00167 }
00168 QHash <QString,KTextEditor::LoadSaveFilterCheckPlugin*> m_plugins;
00169 };
00170
00171
00172
00173
00174
00175 KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView,
00176 bool bReadOnly, QWidget *parentWidget,
00177 QObject *parent)
00178 : KTextEditor::Document (parent),
00179 m_activeView(0L),
00180 m_undoDontMerge(false),
00181 m_undoIgnoreCancel(false),
00182 m_mergeAllEdits(false),
00183 m_firstMergeGroupSkipped(false),
00184 lastUndoGroupWhenSaved( 0 ),
00185 lastRedoGroupWhenSaved( 0 ),
00186 docWasSavedWhenUndoWasEmpty( true ),
00187 docWasSavedWhenRedoWasEmpty( true ),
00188 m_annotationModel( 0 ),
00189 m_saveAs(false),
00190 m_indenter(this),
00191 m_modOnHd (false),
00192 m_modOnHdReason (OnDiskUnmodified),
00193 s_fileChangedDialogsActivated (false),
00194 m_tabInterceptor(0),
00195 m_savingToUrl(false)
00196 {
00197 setComponentData ( KateGlobal::self()->componentData () );
00198
00199 m_undoComplexMerge=false;
00200
00201 QString pathName ("/Kate/Document/%1");
00202 pathName = pathName.arg (++dummy);
00203
00204
00205 QDBusConnection::sessionBus().registerObject (pathName, this);
00206
00207
00208 KateGlobal::self()->registerDocument(this);
00209
00210 m_reloading = false;
00211
00212 m_editHistory = new KateEditHistory(this);
00213 m_smartManager = new KateSmartManager(this);
00214 m_buffer = new KateBuffer(this);
00215
00216
00217
00218 m_config = new KateDocumentConfig(this);
00219
00220
00221 setActiveView(0L);
00222
00223 hlSetByUser = false;
00224 m_fileTypeSetByUser = false;
00225
00226 editSessionNumber = 0;
00227 editIsRunning = false;
00228 m_editCurrentUndo = 0L;
00229 editWithUndo = false;
00230
00231 m_docNameNumber = 0;
00232 m_docName = "need init";
00233
00234 m_bSingleViewMode = bSingleViewMode;
00235 m_bBrowserView = bBrowserView;
00236 m_bReadOnly = bReadOnly;
00237
00238 setEditableMarks( markType01 );
00239
00240 m_undoMergeTimer = new QTimer(this);
00241 m_undoMergeTimer->setSingleShot(true);
00242 connect(m_undoMergeTimer, SIGNAL(timeout()), SLOT(undoCancel()));
00243
00244 clearMarks ();
00245 clearUndo ();
00246 clearRedo ();
00247 setModified (false);
00248 docWasSavedWhenUndoWasEmpty = true;
00249
00250
00251 m_buffer->setHighlight (0);
00252
00253 m_blockRemoveTrailingSpaces = false;
00254 m_extension = new KateBrowserExtension( this );
00255
00256
00257 m_indenter.updateConfig ();
00258
00259
00260 connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int)));
00261 connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated()));
00262
00263
00264 connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged()));
00265
00266
00267 connect( KateGlobal::self()->dirWatch(), SIGNAL(dirty (const QString &)),
00268 this, SLOT(slotModOnHdDirty (const QString &)) );
00269
00270 connect( KateGlobal::self()->dirWatch(), SIGNAL(created (const QString &)),
00271 this, SLOT(slotModOnHdCreated (const QString &)) );
00272
00273 connect( KateGlobal::self()->dirWatch(), SIGNAL(deleted (const QString &)),
00274 this, SLOT(slotModOnHdDeleted (const QString &)) );
00275
00276 connect (this,SIGNAL(completed()),this,SLOT(slotCompleted()));
00277 connect (this,SIGNAL(canceled(const QString&)),this,SLOT(slotCanceled()));
00278
00279 setDocName ("");
00280
00281
00282
00283
00284 if ( m_bSingleViewMode && parentWidget )
00285 {
00286 KTextEditor::View *view = (KTextEditor::View*)createView( parentWidget );
00287 insertChildClient( view );
00288 view->show();
00289 setWidget( view );
00290 }
00291
00292 connect(this,SIGNAL(sigQueryClose(bool *, bool*)),this,SLOT(slotQueryClose_save(bool *, bool*)));
00293
00294 m_isasking = 0;
00295
00296
00297 KatePartPluginManager::self()->addDocument(this);
00298 }
00299
00300
00301
00302
00303 KateDocument::~KateDocument()
00304 {
00305
00306
00307
00308 emit aboutToClose(this);
00309
00310
00311 deactivateDirWatch ();
00312
00313
00314 setAutoDeleteWidget(false);
00315 setAutoDeletePart(false);
00316
00317
00318 while (!m_views.isEmpty()) {
00319 delete m_views.takeFirst();
00320 }
00321
00322 delete m_editCurrentUndo;
00323
00324
00325 qDeleteAll(undoItems);
00326 undoItems.clear();
00327 qDeleteAll(redoItems);
00328 redoItems.clear();
00329
00330
00331 KatePartPluginManager::self()->removeDocument(this);
00332
00333
00334 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
00335 delete i.value();
00336 m_marks.clear();
00337
00338 delete m_config;
00339 KateGlobal::self()->deregisterDocument (this);
00340 }
00341
00342
00343
00344 QWidget *KateDocument::widget()
00345 {
00346
00347 if (!singleViewMode())
00348 return 0;
00349
00350
00351 if (KTextEditor::Document::widget())
00352 return KTextEditor::Document::widget();
00353
00354
00355 KTextEditor::View *view = (KTextEditor::View*)createView(0);
00356 insertChildClient( view );
00357 setWidget( view );
00358 return view;
00359 }
00360
00361
00362
00363 KTextEditor::View *KateDocument::createView( QWidget *parent )
00364 {
00365 KateView* newView = new KateView( this, parent);
00366 connect(newView, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), SLOT(undoCancel()));
00367 if ( s_fileChangedDialogsActivated )
00368 connect( newView, SIGNAL(focusIn( KTextEditor::View * )), this, SLOT(slotModifiedOnDisk()) );
00369
00370 emit viewCreated (this, newView);
00371
00372 return newView;
00373 }
00374
00375 const QList<KTextEditor::View*> &KateDocument::views () const
00376 {
00377 return m_textEditViews;
00378 }
00379
00380 KTextEditor::Editor *KateDocument::editor ()
00381 {
00382 return KateGlobal::self();
00383 }
00384
00385
00386
00387 QString KateDocument::text() const
00388 {
00389 QString s;
00390
00391 for (int i = 0; i < m_buffer->count(); i++)
00392 {
00393 KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00394
00395 if (textLine)
00396 {
00397 s.append (textLine->string());
00398
00399 if ((i+1) < m_buffer->count())
00400 s.append(QChar::fromAscii('\n'));
00401 }
00402 }
00403
00404 return s;
00405 }
00406
00407 QString KateDocument::text( const KTextEditor::Range& range, bool blockwise ) const
00408 {
00409 if (!range.isValid()) {
00410 kWarning() << k_funcinfo << "Text requested for invalid range" << range;
00411 return QString();
00412 }
00413
00414 if ( blockwise && (range.start().column() > range.end().column()) )
00415 return QString ();
00416
00417 QString s;
00418
00419 if (range.start().line() == range.end().line())
00420 {
00421 if (range.start().column() > range.end().column())
00422 return QString ();
00423
00424 KateTextLine::Ptr textLine = m_buffer->plainLine(range.start().line());
00425
00426 if ( !textLine )
00427 return QString ();
00428
00429 return textLine->string(range.start().column(), range.end().column()-range.start().column());
00430 }
00431 else
00432 {
00433
00434 for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
00435 {
00436 KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00437
00438 if ( !blockwise )
00439 {
00440 if (i == range.start().line())
00441 s.append (textLine->string(range.start().column(), textLine->length()-range.start().column()));
00442 else if (i == range.end().line())
00443 s.append (textLine->string(0, range.end().column()));
00444 else
00445 s.append (textLine->string());
00446 }
00447 else
00448 {
00449 s.append( textLine->string( range.start().column(), range.end().column()-range.start().column()));
00450 }
00451
00452 if ( i < range.end().line() )
00453 s.append(QChar::fromAscii('\n'));
00454 }
00455 }
00456
00457 return s;
00458 }
00459
00460 QChar KateDocument::character( const KTextEditor::Cursor & position ) const
00461 {
00462 KateTextLine::Ptr textLine = m_buffer->plainLine(position.line());
00463
00464 if ( !textLine )
00465 return QChar();
00466
00467 if (position.column() >= 0 && position.column() < textLine->string().length())
00468 return textLine->string().at(position.column());
00469
00470 return QChar();
00471 }
00472
00473 QStringList KateDocument::textLines( const KTextEditor::Range & range, bool blockwise ) const
00474 {
00475 QStringList ret;
00476
00477 if (!range.isValid()) {
00478 kWarning() << k_funcinfo << "Text requested for invalid range" << range;
00479 return ret;
00480 }
00481
00482 if ( blockwise && (range.start().column() > range.end().column()) )
00483 return ret;
00484
00485 if (range.start().line() == range.end().line())
00486 {
00487 Q_ASSERT(range.start() <= range.end());
00488
00489 KateTextLine::Ptr textLine = m_buffer->plainLine(range.start().line());
00490
00491 if ( !textLine )
00492 return ret;
00493
00494 ret << textLine->string(range.start().column(), range.end().column() - range.start().column());
00495 }
00496 else
00497 {
00498 for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
00499 {
00500 KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00501
00502 if ( !blockwise )
00503 {
00504 if (i == range.start().line())
00505 ret << textLine->string(range.start().column(), textLine->length() - range.start().column());
00506 else if (i == range.end().line())
00507 ret << textLine->string(0, range.end().column());
00508 else
00509 ret << textLine->string();
00510 }
00511 else
00512 {
00513 ret << textLine->string(range.start().column(), range.end().column() - range.start().column());
00514 }
00515 }
00516 }
00517
00518 return ret;
00519 }
00520
00521 QString KateDocument::line( int line ) const
00522 {
00523 KateTextLine::Ptr l = m_buffer->plainLine(line);
00524
00525 if (!l)
00526 return QString();
00527
00528 return l->string();
00529 }
00530
00531 bool KateDocument::setText(const QString &s)
00532 {
00533 if (!isReadWrite())
00534 return false;
00535
00536 QList<KTextEditor::Mark> msave;
00537
00538 foreach (KTextEditor::Mark* mark, m_marks)
00539 msave.append(*mark);
00540
00541 editStart ();
00542
00543
00544 clear();
00545
00546
00547 insertText (KTextEditor::Cursor(), s);
00548
00549 editEnd ();
00550
00551 foreach (const KTextEditor::Mark& mark, msave)
00552 setMark (mark.line, mark.type);
00553
00554 return true;
00555 }
00556
00557 bool KateDocument::setText( const QStringList & text )
00558 {
00559 if (!isReadWrite())
00560 return false;
00561
00562 QList<KTextEditor::Mark> msave;
00563
00564 foreach (KTextEditor::Mark* mark, m_marks)
00565 msave.append(*mark);
00566
00567 editStart ();
00568
00569
00570 clear();
00571
00572
00573 insertText (KTextEditor::Cursor::start(), text);
00574
00575 editEnd ();
00576
00577 foreach (const KTextEditor::Mark& mark, msave)
00578 setMark (mark.line, mark.type);
00579
00580 return true;
00581 }
00582
00583 bool KateDocument::clear()
00584 {
00585 if (!isReadWrite())
00586 return false;
00587
00588 foreach (KateView *view, m_views) {
00589 view->clear();
00590 view->tagAll();
00591 view->update();
00592 }
00593
00594 clearMarks ();
00595
00596 return removeText (KTextEditor::Range(KTextEditor::Cursor(), KTextEditor::Cursor(lastLine()+1, 0)));
00597 }
00598
00599 bool KateDocument::insertText( const KTextEditor::Cursor& position, const QString& text, bool block )
00600 {
00601 if (!isReadWrite())
00602 return false;
00603
00604 if (text.isEmpty())
00605 return true;
00606
00607 editStart();
00608
00609 int currentLine = position.line();
00610 int currentLineStart = 0;
00611 int totalLength = text.length();
00612 int insertColumn = position.column();
00613
00614 if (position.line() > lines())
00615 {
00616 int line = lines();
00617 while (line != position.line() + totalLength + 1)
00618 {
00619 editInsertLine(line,"");
00620 line++;
00621 }
00622 }
00623
00624 bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn );
00625 int tabWidth = config()->tabWidth();
00626
00627 static const QChar newLineChar('\n');
00628 static const QChar tabChar('\t');
00629 static const QChar spaceChar(' ');
00630
00631 int insertColumnExpanded = insertColumn;
00632 KateTextLine::Ptr l = kateTextLine( currentLine );
00633 if (l)
00634 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00635
00636 int pos = 0;
00637 for (; pos < totalLength; pos++)
00638 {
00639 const QChar& ch = text.at(pos);
00640
00641 if (ch == newLineChar)
00642 {
00643
00644 if (currentLineStart < pos)
00645 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00646
00647 if ( !block )
00648 {
00649 editWrapLine(currentLine, insertColumn + pos - currentLineStart);
00650 insertColumn = 0;
00651 }
00652 else
00653 {
00654 if ( currentLine == lastLine() )
00655 editWrapLine(currentLine , insertColumn + pos - currentLineStart);
00656 insertColumn = position.column();
00657 }
00658
00659 currentLine++;
00660 currentLineStart = pos + 1;
00661 l = kateTextLine( currentLine );
00662 if (l)
00663 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00664 }
00665 else
00666 {
00667 if ( replacetabs && ch == tabChar )
00668 {
00669 int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth );
00670 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar));
00671
00672 insertColumn += pos - currentLineStart + spacesRequired;
00673 currentLineStart = pos + 1;
00674 l = kateTextLine( currentLine );
00675 if (l)
00676 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00677 }
00678 }
00679 }
00680
00681
00682 if (currentLineStart < pos)
00683 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00684
00685 editEnd();
00686 return true;
00687 }
00688
00689 bool KateDocument::insertText( const KTextEditor::Cursor & position, const QStringList & textLines, bool block )
00690 {
00691 if (!isReadWrite())
00692 return false;
00693
00694 if (textLines.isEmpty() || (textLines.count() == 1 && textLines.first().isEmpty()))
00695 return true;
00696
00697
00698 if (position.line() > lines())
00699 return false;
00700
00701 editStart();
00702
00703 if (position.line() > lines())
00704 editInsertLine(position.line(),"");
00705
00706 int currentLine = position.line();
00707 int currentLineStart = 0;
00708 int insertColumn = position.column();
00709
00710 bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn );
00711 int tabWidth = config()->tabWidth();
00712
00713 static const QChar newLineChar('\n');
00714 static const QChar tabChar('\t');
00715 static const QChar spaceChar(' ');
00716
00717 int insertColumnExpanded = insertColumn;
00718 KateTextLine::Ptr l = kateTextLine( currentLine );
00719 if (l)
00720 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00721
00722 foreach (const QString &text, textLines)
00723 {
00724 int pos = 0;
00725 for (; pos < text.length(); pos++)
00726 {
00727 const QChar& ch = text.at(pos);
00728
00729 if (ch == newLineChar)
00730 {
00731
00732 if (currentLineStart < pos)
00733 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00734
00735 if ( !block )
00736 {
00737 editWrapLine(currentLine, pos + insertColumn);
00738 insertColumn = 0;
00739 }
00740 else
00741 {
00742 if ( currentLine == lastLine() )
00743 editWrapLine(currentLine , insertColumn + pos - currentLineStart);
00744 insertColumn = position.column();
00745 }
00746
00747 currentLine++;
00748 currentLineStart = pos + 1;
00749 l = kateTextLine( currentLine );
00750 if (l)
00751 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00752 }
00753 else
00754 {
00755 if ( replacetabs && ch == tabChar )
00756 {
00757 int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth );
00758 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar));
00759
00760 insertColumn += pos - currentLineStart + spacesRequired;
00761 l = kateTextLine( currentLine );
00762 if (l)
00763 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00764 currentLineStart = pos + 1;
00765 }
00766 }
00767 }
00768
00769
00770 if (currentLineStart < pos - currentLineStart)
00771 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00772 }
00773
00774 editEnd();
00775 return true;
00776 }
00777
00778
00779 bool KateDocument::removeText ( const KTextEditor::Range &_range, bool block )
00780 {
00781 KTextEditor::Range range = _range;
00782
00783 if (!isReadWrite())
00784 return false;
00785
00786 if ( block && (range.start().column() > range.end().column()) )
00787 return false;
00788
00789
00790 Q_ASSERT( range.start().line() <= range.end().line() );
00791
00792 if ( range.start().line() > lastLine() )
00793 return false;
00794
00795 if (!block)
00796 emit aboutToRemoveText(range);
00797
00798 editStart();
00799
00800 if ( !block )
00801 {
00802 if ( range.end().line() > lastLine() )
00803 {
00804 range.end().setPosition(lastLine()+1, 0);
00805 }
00806
00807 if (range.onSingleLine())
00808 {
00809 editRemoveText(range.start().line(), range.start().column(), range.columnWidth());
00810 }
00811 else if (range.start().line() + 1 == range.end().line())
00812 {
00813 if ( (m_buffer->plainLine(range.start().line())->length() - range.start().column()) > 0 )
00814 editRemoveText(range.start().line(), range.start().column(), m_buffer->plainLine(range.start().line())->length() - range.start().column());
00815
00816 if (range.end().column())
00817 editRemoveText (range.start().line() + 1, 0, range.end().column());
00818
00819 editUnWrapLine (range.start().line());
00820 }
00821 else
00822 {
00823 for (int line = range.end().line(); line >= range.start().line(); --line)
00824 {
00825 if ((line > range.start().line()) && (line < range.end().line())) {
00826 editRemoveLine(line);
00827
00828 } else if (line == range.end().line()) {
00829 if ( range.end().line() <= lastLine() )
00830 editRemoveText(line, 0, range.end().column());
00831
00832 } else {
00833 if ( (m_buffer->plainLine(line)->length() - range.start().column()) > 0 )
00834 editRemoveText(line, range.start().column(), m_buffer->plainLine(line)->length() - range.start().column());
00835
00836 editUnWrapLine(range.start().line());
00837 }
00838
00839 if ( line == 0 )
00840 break;
00841 }
00842 }
00843 }
00844 else
00845 {
00846 int startLine = qMax(0, range.start().line());
00847 for (int line = qMin(range.end().line(), lastLine()); line >= startLine; --line)
00848 editRemoveText(line, range.start().column(), range.end().column() - range.start().column());
00849 }
00850
00851 editEnd ();
00852 emit textRemoved();
00853 return true;
00854 }
00855
00856 bool KateDocument::insertLine( int l, const QString &str )
00857 {
00858 if (!isReadWrite())
00859 return false;
00860
00861 if (l < 0 || l > lines())
00862 return false;
00863
00864 return editInsertLine (l, str);
00865 }
00866
00867 bool KateDocument::insertLines( int line, const QStringList & text )
00868 {
00869 if (!isReadWrite())
00870 return false;
00871
00872 if (line < 0 || line > lines())
00873 return false;
00874
00875 bool success = true;
00876 foreach (const QString &string, text)
00877
00878 success &= editInsertLine (line++, string);
00879
00880 return success;
00881 }
00882
00883 bool KateDocument::removeLine( int line )
00884 {
00885 if (!isReadWrite())
00886 return false;
00887
00888 if (line < 0 || line > lastLine())
00889 return false;
00890
00891 return editRemoveLine (line);
00892 }
00893
00894 int KateDocument::totalCharacters() const
00895 {
00896 int l = 0;
00897
00898 for (int i = 0; i < m_buffer->count(); ++i)
00899 {
00900 KateTextLine::Ptr line = m_buffer->plainLine(i);
00901
00902 if (line)
00903 l += line->length();
00904 }
00905
00906 return l;
00907 }
00908
00909 int KateDocument::lines() const
00910 {
00911 return m_buffer->count();
00912 }
00913
00914 int KateDocument::numVisLines() const
00915 {
00916 return m_buffer->countVisible ();
00917 }
00918
00919 int KateDocument::lineLength ( int line ) const
00920 {
00921 if (line < 0 || line > lastLine())
00922 return -1;
00923
00924 KateTextLine::Ptr l = m_buffer->plainLine(line);
00925
00926 if (!l)
00927 return -1;
00928
00929 return l->length();
00930 }
00931
00932
00933
00934
00935
00936
00937 void KateDocument::editStart (bool withUndo, Kate::EditSource editSource)
00938 {
00939 editSessionNumber++;
00940
00941 if (editSource == Kate::NoEditSource)
00942 m_editSources.push(m_editSources.isEmpty() ? Kate::UserInputEdit : m_editSources.top());
00943 else
00944 m_editSources.push(editSource);
00945
00946 if (editSessionNumber > 1)
00947 return;
00948
00949
00950 smartMutex()->lock();
00951
00952 editIsRunning = true;
00953 editWithUndo = withUndo;
00954
00955 if (editWithUndo)
00956 undoStart();
00957 else
00958 undoCancel();
00959
00960 foreach(KateView *view,m_views)
00961 {
00962 view->editStart ();
00963 }
00964
00965 m_buffer->editStart ();
00966 }
00967
00968 void KateDocument::undoStart()
00969 {
00970 if (m_editCurrentUndo || (m_activeView && activeKateView()->imComposeEvent())) return;
00971
00972
00973 m_editCurrentUndo = new KateUndoGroup(this);
00974 if (m_activeView) {
00975 m_editCurrentUndo->setUndoCursor(m_activeView->cursorPosition());
00976 m_editCurrentUndo->setUndoSelection(m_activeView->selectionRange());
00977 }
00978 }
00979
00980 void KateDocument::undoEnd()
00981 {
00982 if (m_activeView && activeKateView()->imComposeEvent())
00983 return;
00984
00985 if (m_editCurrentUndo)
00986 {
00987 bool changedUndo = false;
00988
00989 if (m_activeView) {
00990 m_editCurrentUndo->setRedoCursor(m_activeView->cursorPosition());
00991 m_editCurrentUndo->setRedoSelection(m_activeView->selectionRange());
00992 }
00993
00994 if (m_editCurrentUndo->isEmpty()) {
00995 delete m_editCurrentUndo;
00996 } else if (((m_mergeAllEdits && !m_firstMergeGroupSkipped) || !m_undoDontMerge)
00997 && !undoItems.isEmpty() && undoItems.last()
00998 && undoItems.last()->merge(m_editCurrentUndo, m_undoComplexMerge || m_mergeAllEdits)) {
00999 delete m_editCurrentUndo;
01000 m_firstMergeGroupSkipped = true;
01001 } else {
01002 undoItems.append(m_editCurrentUndo);
01003 changedUndo = true;
01004 }
01005
01006 m_undoDontMerge = false;
01007 m_undoIgnoreCancel = true;
01008
01009 m_editCurrentUndo = 0L;
01010
01011
01012
01013 m_undoMergeTimer->start(5000);
01014
01015 if (changedUndo)
01016 emit undoChanged();
01017 }
01018 }
01019
01020 void KateDocument::undoCancel()
01021 {
01022
01023 if (editIsRunning)
01024 return;
01025
01026 if (m_undoIgnoreCancel) {
01027 m_undoIgnoreCancel = false;
01028 return;
01029 }
01030
01031 m_undoDontMerge = true;
01032
01033 Q_ASSERT(!m_editCurrentUndo);
01034
01035
01036 delete m_editCurrentUndo;
01037 m_editCurrentUndo = 0L;
01038 }
01039
01040 void KateDocument::undoSafePoint() {
01041 Q_ASSERT(m_editCurrentUndo);
01042 if (!m_editCurrentUndo) return;
01043 m_editCurrentUndo->safePoint();
01044 }
01045
01046
01047
01048
01049 void KateDocument::editEnd ()
01050 {
01051 if (editSessionNumber == 0)
01052 return;
01053
01054
01055 if (m_buffer->editChanged() && (editSessionNumber == 1))
01056 if (editWithUndo && config()->wordWrap())
01057 wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
01058
01059 editSessionNumber--;
01060
01061 m_editSources.pop();
01062
01063 if (editSessionNumber > 0)
01064 return;
01065
01066
01067
01068 m_buffer->editEnd ();
01069
01070 if (editWithUndo)
01071 undoEnd();
01072
01073
01074 foreach(KateView *view, m_views)
01075 view->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
01076
01077
01078 smartMutex()->unlock();
01079
01080 if (m_buffer->editChanged())
01081 {
01082 setModified(true);
01083 emit textChanged (this);
01084 }
01085
01086 editIsRunning = false;
01087 }
01088
01089 void KateDocument::pushEditState ()
01090 {
01091 editStateStack.push(editSessionNumber);
01092 }
01093
01094 void KateDocument::popEditState ()
01095 {
01096 if (editStateStack.isEmpty()) return;
01097
01098 int count = editStateStack.pop() - editSessionNumber;
01099 while (count < 0) { ++count; editEnd(); }
01100 while (count > 0) { --count; editStart(); }
01101 }
01102
01103 bool KateDocument::wrapText(int startLine, int endLine)
01104 {
01105 if (startLine < 0 || endLine < 0)
01106 return false;
01107
01108 if (!isReadWrite())
01109 return false;
01110
01111 int col = config()->wordWrapAt();
01112
01113 if (col == 0)
01114 return false;
01115
01116 editStart ();
01117
01118 for (int line = startLine; (line <= endLine) && (line < lines()); line++)
01119 {
01120 KateTextLine::Ptr l = kateTextLine(line);
01121
01122 if (!l)
01123 return false;
01124
01125 kDebug (13020) << "try wrap line: " << line;
01126
01127 if (l->virtualLength(m_buffer->tabWidth()) > col)
01128 {
01129 KateTextLine::Ptr nextl = kateTextLine(line+1);
01130
01131 kDebug (13020) << "do wrap line: " << line;
01132
01133 int eolPosition = l->length()-1;
01134
01135
01136 int x = 0;
01137 const QString & t = l->string();
01138 int z2 = 0;
01139 for ( ; z2 < l->length(); z2++)
01140 {
01141 static const QChar tabChar('\t');
01142 if (t.at(z2) == tabChar)
01143 x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
01144 else
01145 x++;
01146
01147 if (x > col)
01148 break;
01149 }
01150
01151 int searchStart = qMin (z2, l->length()-1);
01152
01153
01154
01155 if (searchStart == eolPosition && t.at(searchStart).isSpace())
01156 searchStart--;
01157
01158
01159
01160
01161
01162
01163
01164 int z = 0;
01165 int nw = 0;
01166 for (z=searchStart; z > 0; z--)
01167 {
01168 if (t.at(z).isSpace()) break;
01169 if ( ! nw && highlight()->canBreakAt( t.at(z) , l->attribute(z) ) )
01170 nw = z;
01171 }
01172
01173 bool removeTrailingSpace = false;
01174 if (z > 0)
01175 {
01176
01177
01178
01179
01180
01181 z++;
01182 removeTrailingSpace = true;
01183 }
01184 else
01185 {
01186
01187
01188
01189 if ( nw && nw < col ) nw++;
01190 z = nw ? nw : col;
01191 }
01192
01193 if (nextl && !nextl->isAutoWrapped())
01194 {
01195 editWrapLine (line, z, true);
01196 editMarkLineAutoWrapped (line+1, true);
01197
01198 endLine++;
01199 }
01200 else
01201 {
01202 if (nextl && (nextl->length() > 0) && !nextl->at(0).isSpace() && ((l->length() < 1) || !l->at(l->length()-1).isSpace()))
01203 editInsertText (line+1, 0, QString (" "));
01204
01205 bool newLineAdded = false;
01206 editWrapLine (line, z, false, &newLineAdded);
01207
01208 editMarkLineAutoWrapped (line+1, true);
01209
01210 endLine++;
01211 }
01212
01213 if (removeTrailingSpace) {
01214
01215 editRemoveText (line, z - 1, 1);
01216 }
01217 }
01218 }
01219
01220 editEnd ();
01221
01222 return true;
01223 }
01224
01225 void KateDocument::editAddUndo (int type, uint line, uint col, uint len, const QString &text)
01226 {
01227 if (editIsRunning && editWithUndo && m_editCurrentUndo) {
01228 m_editCurrentUndo->addItem(static_cast<KateUndoGroup::UndoType>(type), line, col, len, text);
01229
01230
01231 if (redoItems.count()) {
01232 qDeleteAll(redoItems);
01233 redoItems.clear();
01234 }
01235 }
01236 }
01237
01238 bool KateDocument::editInsertText ( int line, int col, const QString &s, Kate::EditSource editSource )
01239 {
01240 if (line < 0 || col < 0)
01241 return false;
01242
01243 if (!isReadWrite())
01244 return false;
01245
01246 KateTextLine::Ptr l = kateTextLine(line);
01247
01248 if (!l)
01249 return false;
01250
01251 editStart (true, editSource);
01252
01253 editAddUndo (KateUndoGroup::editInsertText, line, col, s.length(), s);
01254
01255 l->insertText (col, s);
01256
01257 m_buffer->changeLine(line);
01258
01259 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col), QStringList(), KTextEditor::Range(line, col, line, col + s.length()), QStringList(s)) );
01260 emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line, col + s.length()));
01261
01262 editEnd();
01263
01264 return true;
01265 }
01266
01267 bool KateDocument::editRemoveText ( int line, int col, int len, Kate::EditSource editSource )
01268 {
01269 if (line < 0 || col < 0 || len < 0)
01270 return false;
01271
01272 if (!isReadWrite())
01273 return false;
01274
01275 KateTextLine::Ptr l = kateTextLine(line);
01276
01277 if (!l)
01278 return false;
01279
01280 editStart (true, editSource);
01281
01282 editAddUndo (KateUndoGroup::editRemoveText, line, col, len, l->string().mid(col, len));
01283
01284 l->removeText (col, len);
01285 removeTrailingSpace( line );
01286
01287 m_buffer->changeLine(line);
01288
01289 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col + len), QStringList(l->string().mid(col, len)), KTextEditor::Range(line, col, line, col), QStringList()) );
01290 emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len));
01291
01292 editEnd ();
01293
01294 return true;
01295 }
01296
01297 bool KateDocument::editMarkLineAutoWrapped ( int line, bool autowrapped )
01298 {
01299 if (line < 0)
01300 return false;
01301
01302 if (!isReadWrite())
01303 return false;
01304
01305 KateTextLine::Ptr l = kateTextLine(line);
01306
01307 if (!l)
01308 return false;
01309
01310 editStart ();
01311
01312 editAddUndo (KateUndoGroup::editMarkLineAutoWrapped, line, autowrapped ? 1 : 0, 0, QString());
01313
01314 l->setAutoWrapped (autowrapped);
01315
01316 m_buffer->changeLine(line);
01317
01318 editEnd ();
01319
01320 return true;
01321 }
01322
01323 bool KateDocument::editWrapLine ( int line, int col, bool newLine, bool *newLineAdded)
01324 {
01325 if (line < 0 || col < 0)
01326 return false;
01327
01328 if (!isReadWrite())
01329 return false;
01330
01331 KateTextLine::Ptr l = kateTextLine(line);
01332
01333 if (!l)
01334 return false;
01335
01336 editStart ();
01337
01338 KateTextLine::Ptr nextLine = kateTextLine(line+1);
01339
01340 int pos = l->length() - col;
01341
01342 if (pos < 0)
01343 pos = 0;
01344
01345 editAddUndo (KateUndoGroup::editWrapLine, line, col, pos, (!nextLine || newLine) ? "1" : "0");
01346
01347 if (!nextLine || newLine)
01348 {
01349 KateTextLine::Ptr textLine(new KateTextLine());
01350
01351 textLine->insertText (0, l->string().mid(col, pos));
01352 l->truncate(col);
01353
01354 m_buffer->insertLine (line+1, textLine);
01355 m_buffer->changeLine(line);
01356
01357 QList<KTextEditor::Mark*> list;
01358 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01359 {
01360 if( i.value()->line >= line )
01361 {
01362 if ((col == 0) || (i.value()->line > line))
01363 list.append( i.value() );
01364 }
01365 }
01366
01367 for( int i=0; i < list.size(); ++i )
01368 m_marks.take( list[i]->line );
01369
01370 for( int i=0; i < list.size(); ++i )
01371 {
01372 list[i]->line++;
01373 m_marks.insert( list[i]->line, list[i] );
01374 }
01375
01376 if( !list.isEmpty() )
01377 emit marksChanged( this );
01378
01379
01380 if (newLineAdded)
01381 (*newLineAdded) = true;
01382
01383 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col), QStringList(), KTextEditor::Range(line, col, line+1, 0), QStringList()) );
01384 }
01385 else
01386 {
01387 nextLine->insertText (0, l->string().mid(col, pos));
01388 l->truncate(col);
01389
01390 m_buffer->changeLine(line);
01391 m_buffer->changeLine(line+1);
01392
01393
01394 if (newLineAdded)
01395 (*newLineAdded) = false;
01396
01397 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line+1, 0), QStringList(), KTextEditor::Range(line, col, line+1, pos), QStringList()) );
01398 }
01399
01400 emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line+1, pos));
01401
01402 editEnd ();
01403
01404 return true;
01405 }
01406
01407 bool KateDocument::editUnWrapLine ( int line, bool removeLine, int length )
01408 {
01409 if (line < 0 || length < 0)
01410 return false;
01411
01412 if (!isReadWrite())
01413 return false;
01414
01415 KateTextLine::Ptr l = kateTextLine(line);
01416 KateTextLine::Ptr nextLine = kateTextLine(line+1);
01417
01418 if (!l || !nextLine)
01419 return false;
01420
01421 editStart ();
01422
01423 int col = l->length ();
01424
01425 editAddUndo (KateUndoGroup::editUnWrapLine, line, col, length, removeLine ? "1" : "0");
01426
01427 if (removeLine)
01428 {
01429 l->insertText (col, nextLine->string());
01430
01431 m_buffer->changeLine(line);
01432 m_buffer->removeLine(line+1);
01433 }
01434 else
01435 {
01436 l->insertText (col, nextLine->string().left((nextLine->length() < length) ? nextLine->length() : length));
01437 nextLine->removeText (0, (nextLine->length() < length) ? nextLine->length() : length);
01438
01439 m_buffer->changeLine(line);
01440 m_buffer->changeLine(line+1);
01441 }
01442
01443 QList<KTextEditor::Mark*> list;
01444 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01445 {
01446 if( i.value()->line >= line+1 )
01447 list.append( i.value() );
01448
01449 if ( i.value()->line == line+1 )
01450 {
01451 KTextEditor::Mark* mark = m_marks.take( line );
01452
01453 if (mark)
01454 {
01455 i.value()->type |= mark->type;
01456 }
01457 }
01458 }
01459
01460 for( int i=0; i < list.size(); ++i )
01461 m_marks.take( list[i]->line );
01462
01463 for( int i=0; i < list.size(); ++i )
01464 {
01465 list[i]->line--;
01466 m_marks.insert( list[i]->line, list[i] );
01467 }
01468
01469 if( !list.isEmpty() )
01470 emit marksChanged( this );
01471
01472 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line+1, 0), QStringList(QString()), KTextEditor::Range(line, col, line, col), QStringList()) );
01473 emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line+1, 0));
01474
01475 editEnd ();
01476
01477 return true;
01478 }
01479
01480 bool KateDocument::editInsertLine ( int line, const QString &s, Kate::EditSource editSource )
01481 {
01482 if (line < 0)
01483 return false;
01484
01485 if (!isReadWrite())
01486 return false;
01487
01488 if ( line > lines() )
01489 return false;
01490
01491 editStart (true, editSource);
01492
01493 editAddUndo (KateUndoGroup::editInsertLine, line, 0, s.length(), s);
01494
01495 removeTrailingSpace( line );
01496
01497 KateTextLine::Ptr tl(new KateTextLine());
01498 tl->insertText (0, s);
01499 m_buffer->insertLine(line, tl);
01500 m_buffer->changeLine(line);
01501
01502 removeTrailingSpace( line );
01503
01504 QList<KTextEditor::Mark*> list;
01505 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01506 {
01507 if( i.value()->line >= line )
01508 list.append( i.value() );
01509 }
01510
01511 for( int i=0; i < list.size(); ++i )
01512 m_marks.take( list[i]->line );
01513
01514 for( int i=0; i < list.size(); ++i )
01515 {
01516 list[i]->line++;
01517 m_marks.insert( list[i]->line, list[i] );
01518 }
01519
01520 if( !list.isEmpty() )
01521 emit marksChanged( this );
01522
01523 KTextEditor::Range rangeInserted(line, 0, line, tl->length());
01524
01525 if (line) {
01526 KateTextLine::Ptr prevLine = plainKateTextLine(line - 1);
01527 rangeInserted.start().setPosition(line - 1, prevLine->length());
01528 } else {
01529 rangeInserted.end().setPosition(line + 1, 0);
01530 }
01531
01532 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(rangeInserted.start(), rangeInserted.start()), QStringList(), rangeInserted, QStringList(s)) );
01533 emit KTextEditor::Document::textInserted(this, rangeInserted);
01534
01535 editEnd ();
01536
01537 return true;
01538 }
01539
01540 bool KateDocument::editRemoveLine ( int line, Kate::EditSource editSource )
01541 {
01542 if (line < 0)
01543 return false;
01544
01545 if (!isReadWrite())
01546 return false;
01547
01548 if ( line > lastLine() )
01549 return false;
01550
01551 if ( lines() == 1 )
01552 return editRemoveText (0, 0, kateTextLine(0)->length());
01553
01554 KateLineInfo info;;
01555 lineInfo (&info, line);
01556 if (info.startsInVisibleBlock)
01557 foldingTree()->toggleRegionVisibility(line);
01558
01559 editStart (true, editSource);
01560
01561 QString oldText = this->line(line);
01562
01563 editAddUndo (KateUndoGroup::editRemoveLine, line, 0, lineLength(line), this->line(line));
01564
01565 KTextEditor::Range rangeRemoved(line, 0, line, oldText.length());
01566
01567 if (line < lastLine()) {
01568 rangeRemoved.end().setPosition(line + 1, 0);
01569 } else if (line) {
01570 KateTextLine::Ptr prevLine = plainKateTextLine(line - 1);
01571 rangeRemoved.start().setPosition(line - 1, prevLine->length());
01572 }
01573
01574 m_buffer->removeLine(line);
01575
01576 KTextEditor::Mark* rmark = 0;
01577 QList<KTextEditor::Mark*> list;
01578 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01579 {
01580 if ( (i.value()->line > line) )
01581 list.append( i.value() );
01582 else if ( (i.value()->line == line) )
01583 rmark = i.value();
01584 }
01585
01586 if (rmark)
01587 delete (m_marks.take (rmark->line));
01588
01589 for( int i=0; i < list.size(); ++i )
01590 {
01591 KTextEditor::Mark* mark = m_marks.take( list[i]->line );
01592 mark->line--;
01593 m_marks.insert( mark->line, mark );
01594 }
01595
01596 if( !list.isEmpty() )
01597 emit marksChanged( this );
01598
01599 history()->doEdit( new KateEditInfo(m_editSources.top(), rangeRemoved, QStringList(QString(oldText)), KTextEditor::Range(rangeRemoved.start(), rangeRemoved.start()), QStringList()) );
01600 emit KTextEditor::Document::textRemoved(this, rangeRemoved);
01601
01602 editEnd();
01603
01604 return true;
01605 }
01606
01607
01608
01609
01610 uint KateDocument::undoCount () const
01611 {
01612 return undoItems.count ();
01613 }
01614
01615 uint KateDocument::redoCount () const
01616 {
01617 return redoItems.count ();
01618 }
01619
01620 void KateDocument::undo()
01621 {
01622 if ((undoItems.count() > 0) && undoItems.last())
01623 {
01624
01625
01626 undoItems.last()->undo();
01627 redoItems.append (undoItems.last());
01628 undoItems.removeLast ();
01629 updateModified();
01630
01631 emit undoChanged ();
01632 }
01633 }
01634
01635 void KateDocument::redo()
01636 {
01637 if ((redoItems.count() > 0) && redoItems.last())
01638 {
01639
01640
01641 redoItems.last()->redo();
01642 undoItems.append (redoItems.last());
01643 redoItems.removeLast ();
01644 updateModified();
01645
01646 emit undoChanged ();
01647 }
01648 }
01649
01650 void KateDocument::updateModified()
01651 {
01652
01653
01654
01655
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675 unsigned char currentPattern = 0;
01676 const unsigned char patterns[] = {5,16,21,24,26,88,90,93,133,144,149,165};
01677 const unsigned char patternCount = sizeof(patterns);
01678 KateUndoGroup* undoLast = 0;
01679 KateUndoGroup* redoLast = 0;
01680
01681 if (undoItems.isEmpty())
01682 {
01683 currentPattern |= 1;
01684 }
01685 else
01686 {
01687 undoLast = undoItems.last();
01688 }
01689
01690 if (redoItems.isEmpty())
01691 {
01692 currentPattern |= 2;
01693 }
01694 else
01695 {
01696 redoLast = redoItems.last();
01697 }
01698
01699 if (docWasSavedWhenUndoWasEmpty) currentPattern |= 4;
01700 if (docWasSavedWhenRedoWasEmpty) currentPattern |= 8;
01701 if (lastUndoGroupWhenSaved == undoLast) currentPattern |= 16;
01702 if (lastUndoGroupWhenSaved == redoLast) currentPattern |= 32;
01703 if (lastRedoGroupWhenSaved == undoLast) currentPattern |= 64;
01704 if (lastRedoGroupWhenSaved == redoLast) currentPattern |= 128;
01705
01706
01707
01708 kDebug(13020) << "Pattern:" << static_cast<unsigned int>(currentPattern);
01709
01710 for (uint patternIndex = 0; patternIndex < patternCount; ++patternIndex)
01711 {
01712 if ( currentPattern == patterns[patternIndex] )
01713 {
01714 setModified( false );
01715
01716
01717 setUndoDontMerge(true);
01718 kDebug(13020) << "setting modified to false!";
01719 break;
01720 }
01721 }
01722 }
01723
01724 void KateDocument::clearUndo()
01725 {
01726 qDeleteAll(undoItems);
01727 undoItems.clear ();
01728
01729 lastUndoGroupWhenSaved = 0;
01730 docWasSavedWhenUndoWasEmpty = false;
01731
01732 emit undoChanged ();
01733 }
01734
01735 void KateDocument::clearRedo()
01736 {
01737 qDeleteAll(redoItems);
01738 redoItems.clear ();
01739
01740 lastRedoGroupWhenSaved = 0;
01741 docWasSavedWhenRedoWasEmpty = false;
01742
01743 emit undoChanged ();
01744 }
01745
01746
01747
01748
01749 KTextEditor::Range KateDocument::searchText (const KTextEditor::Range & inputRange, const QString &text, bool casesensitive, bool backwards)
01750 {
01751 FAST_DEBUG("KateDocument::searchText( " << inputRange.start().line() << ", "
01752 << inputRange.start().column() << ", " << text << ", " << backwards << " )");
01753 if (text.isEmpty() || !inputRange.isValid() || (inputRange.start() == inputRange.end()))
01754 {
01755 return KTextEditor::Range::invalid();
01756 }
01757
01758
01759 const QString sep("\n");
01760 const QStringList needleLines = text.split(sep);
01761 const int numNeedleLines = needleLines.count();
01762 FAST_DEBUG("searchText | needle has " << numNeedleLines << " lines");
01763
01764 if (numNeedleLines > 1)
01765 {
01766
01767 const int lastLine = inputRange.end().line();
01768
01769 const int forMin = inputRange.start().line();
01770 const int forMax = lastLine + 1 - numNeedleLines;
01771 const int forInit = backwards ? forMax : forMin;
01772 const int forInc = backwards ? -1 : +1;
01773
01774 const int minLeft = inputRange.start().column();
01775 const uint maxRight = inputRange.end().column();
01776
01777
01778 int hayLinesZeroIndex = 0;
01779 QVector<KateTextLine::Ptr> hayLinesWindow;
01780 for (int i = 0; i < numNeedleLines; i++) {
01781 KateTextLine::Ptr textLine = m_buffer->plainLine((backwards ? forMax : forMin) + i);
01782
01783 if (!textLine)
01784 return KTextEditor::Range::invalid();
01785
01786 hayLinesWindow.append (textLine);
01787 FAST_DEBUG("searchText | hayLinesWindow[" << i << "] = \"" << hayLinesWindow[i]->string() << "\"");
01788 }
01789
01790 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01791 {
01792
01793 uint startCol = 0;
01794 for (int k = 0; k < numNeedleLines; k++)
01795 {
01796
01797 const QString & needleLine = needleLines[k];
01798 KateTextLine::Ptr & hayLine = hayLinesWindow[(k + hayLinesZeroIndex) % numNeedleLines];
01799 FAST_DEBUG("searchText | hayLine = \"" << hayLine->string() << "\"");
01800
01801
01802 if (k == 0) {
01803
01804 if (needleLine.length() == 0)
01805 {
01806 startCol = hayLine->length();
01807 }
01808 else
01809 {
01810 uint myMatchLen;
01811 const uint colOffset = (j > forMin) ? 0 : minLeft;
01812 const bool matches = hayLine->searchText(colOffset, hayLine->length(),needleLine, &startCol,
01813 &myMatchLen, casesensitive, false);
01814 if (!matches || (startCol + myMatchLen != static_cast<uint>(hayLine->length()))) {
01815 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01816 break;
01817 }
01818 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01819 }
01820 } else if (k == numNeedleLines - 1) {
01821
01822 uint foundAt, myMatchLen;
01823 const bool matches = hayLine->searchText(0,hayLine->length(), needleLine, &foundAt, &myMatchLen, casesensitive, false);
01824 if (matches && (foundAt == 0) && !((k == lastLine)
01825 && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
01826 {
01827 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01828 return KTextEditor::Range(j, startCol, j + k, needleLine.length());
01829 }
01830 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01831 } else {
01832
01833 uint foundAt, myMatchLen;
01834 const bool matches = hayLine->searchText(0, hayLine->length(),needleLine, &foundAt, &myMatchLen, casesensitive, false);
01835 if (!matches || (foundAt != 0) || (myMatchLen != static_cast<uint>(needleLine.length()))) {
01836 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01837 break;
01838 }
01839 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01840 }
01841 }
01842
01843
01844 if ((backwards && (j > forMin)) || (!backwards && (j < forMax)))
01845 {
01846 if (backwards)
01847 {
01848 hayLinesZeroIndex = (hayLinesZeroIndex + numNeedleLines - 1) % numNeedleLines;
01849
01850 KateTextLine::Ptr textLine = m_buffer->plainLine(j - 1);
01851
01852 if (!textLine)
01853 return KTextEditor::Range::invalid();
01854
01855 hayLinesWindow[hayLinesZeroIndex] = textLine;
01856
01857 FAST_DEBUG("searchText | filling slot " << hayLinesZeroIndex << " with line "
01858 << j - 1 << ": " << hayLinesWindow[hayLinesZeroIndex]->string());
01859 }
01860 else
01861 {
01862 hayLinesWindow[hayLinesZeroIndex] = m_buffer->plainLine(j + numNeedleLines);
01863 FAST_DEBUG("searchText | filling slot " << hayLinesZeroIndex << " with line "
01864 << j + numNeedleLines << ": " << hayLinesWindow[hayLinesZeroIndex]->string());
01865 hayLinesZeroIndex = (hayLinesZeroIndex + 1) % numNeedleLines;
01866 }
01867 }
01868 }
01869
01870
01871 return KTextEditor::Range::invalid();
01872 }
01873 else
01874 {
01875
01876 const int minLeft = inputRange.start().column();
01877 const uint maxRight = inputRange.end().column();
01878 const int forMin = inputRange.start().line();
01879 const int forMax = inputRange.end().line();
01880 const int forInit = backwards ? forMax : forMin;
01881 const int forInc = backwards ? -1 : +1;
01882 FAST_DEBUG("searchText | single line " << (backwards ? forMax : forMin) << ".."
01883 << (backwards ? forMin : forMax));
01884 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01885 {
01886 KateTextLine::Ptr textLine = m_buffer->plainLine(j);
01887 if (!textLine)
01888 {
01889 FAST_DEBUG("searchText | line " << j << ": no");
01890 return KTextEditor::Range::invalid();
01891 }
01892
01893 const int offset = (j == forMin) ? minLeft : 0;
01894 const int line_end= (j==forMax) ? maxRight : textLine->length();
01895 uint foundAt, myMatchLen;
01896 FAST_DEBUG("searchText | searching in line line: " << j);
01897 const bool found = textLine->searchText (offset,line_end, text, &foundAt, &myMatchLen, casesensitive, backwards);
01898 if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
01899 {
01900 FAST_DEBUG("searchText | line " << j << ": yes");
01901 return KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
01902 }
01903 else
01904 {
01905 FAST_DEBUG("searchText | line " << j << ": no");
01906 }
01907 }
01908 }
01909 return KTextEditor::Range::invalid();
01910 }
01911
01912
01913
01914
01915 struct TwoViewCursor {
01916 int index;
01917 int openLine;
01918 int openCol;
01919 int closeLine;
01920 int closeCol;
01921
01922
01923
01924 };
01925
01926 struct IndexPair {
01927 int openIndex;
01928 int closeIndex;
01929 };
01930
01931
01932
01933 QVector<KTextEditor::Range> KateDocument::searchRegex(
01934 const KTextEditor::Range & inputRange,
01935 QRegExp ®exp,
01936 bool backwards)
01937 {
01938 FAST_DEBUG("KateDocument::searchRegex( " << inputRange.start().line() << ", "
01939 << inputRange.start().column() << ", " << regexp.pattern() << ", " << backwards << " )");
01940 if (regexp.isEmpty() || !regexp.isValid() || !inputRange.isValid() || (inputRange.start() == inputRange.end()))
01941 {
01942 QVector<KTextEditor::Range> result;
01943 result.append(KTextEditor::Range::invalid());
01944 return result;
01945 }
01946
01947
01948
01949 bool isMultiLine;
01950 QString multiLinePattern = regexp.pattern();
01951
01952
01953 const bool dotMatchesNewline = false;
01954 const int replacements = KateDocument::repairPattern(multiLinePattern, isMultiLine);
01955 if (dotMatchesNewline && (replacements > 0))
01956 {
01957 isMultiLine = true;
01958 }
01959
01960 const int firstLineIndex = inputRange.start().line();
01961 const int minColStart = inputRange.start().column();
01962
01963 if (isMultiLine)
01964 {
01965
01966 QString wholeDocument;
01967 const int inputLineCount = inputRange.end().line() - inputRange.start().line() + 1;
01968 FAST_DEBUG("multi line search (lines " << firstLineIndex << ".." << firstLineIndex + inputLineCount - 1 << ")");
01969
01970
01971 if (firstLineIndex >= m_buffer->count())
01972 {
01973 QVector<KTextEditor::Range> result;
01974 result.append(KTextEditor::Range::invalid());
01975 return result;
01976 }
01977
01978 QVector<int> lineLens (inputLineCount);
01979
01980
01981 KateTextLine::Ptr firstLine = m_buffer->plainLine(firstLineIndex);
01982 if (!firstLine)
01983 {
01984 QVector<KTextEditor::Range> result;
01985 result.append(KTextEditor::Range::invalid());
01986 return result;
01987 }
01988
01989 QString firstLineText = firstLine->string();
01990 const int firstLineLen = firstLineText.length() - minColStart;
01991 wholeDocument.append(firstLineText.right(firstLineLen));
01992 lineLens[0] = firstLineLen;
01993 FAST_DEBUG(" line" << 0 << "has length" << lineLens[0]);
01994
01995
01996 const QString sep("\n");
01997 for (int i = 1; i < inputLineCount; i++)
01998 {
01999 KateTextLine::Ptr textLine = m_buffer->plainLine(firstLineIndex + i);
02000 if (!textLine)
02001 {
02002 QVector<KTextEditor::Range> result;
02003 result.append(KTextEditor::Range::invalid());
02004 return result;
02005 }
02006
02007 QString text = textLine->string();
02008 lineLens[i] = text.length();
02009 wholeDocument.append(sep);
02010 wholeDocument.append(text);
02011 FAST_DEBUG(" line" << i << "has length" << lineLens[i]);
02012 }
02013
02014
02015 regexp.setPattern(multiLinePattern);
02016 const int pos = backwards
02017 ? KateDocument::fixedLastIndexIn(regexp, wholeDocument, -1, QRegExp::CaretAtZero)
02018 : regexp.indexIn(wholeDocument, 0, QRegExp::CaretAtZero);
02019 if (pos == -1)
02020 {
02021
02022 FAST_DEBUG("not found");
02023 {
02024 QVector<KTextEditor::Range> result;
02025 result.append(KTextEditor::Range::invalid());
02026 return result;
02027 }
02028 }
02029
02030 #ifdef FAST_DEBUG_ENABLE
02031 const int matchLen = regexp.matchedLength();
02032 FAST_DEBUG("found at relative pos " << pos << ", length " << matchLen);
02033 #endif
02034
02035
02036
02037 QMap<int, TwoViewCursor *> indicesToCursors;
02038 const int numCaptures = regexp.numCaptures();
02039 QVector<IndexPair> indexPairs(1 + numCaptures);
02040 for (int z = 0; z <= numCaptures; z++)
02041 {
02042 const int openIndex = regexp.pos(z);
02043 IndexPair & pair = indexPairs[z];
02044 if (openIndex == -1)
02045 {
02046
02047 pair.openIndex = -1;
02048 pair.closeIndex = -1;
02049 FAST_DEBUG("capture []");
02050 }
02051 else
02052 {
02053 const int closeIndex = openIndex + regexp.cap(z).length();
02054 pair.openIndex = openIndex;
02055 pair.closeIndex = closeIndex;
02056 FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]");
02057
02058
02059 if (!indicesToCursors.contains(openIndex))
02060 {
02061 TwoViewCursor * twoViewCursor = new TwoViewCursor;
02062 twoViewCursor->index = openIndex;
02063 indicesToCursors.insert(openIndex, twoViewCursor);
02064 FAST_DEBUG(" border index added: " << openIndex);
02065 }
02066 if (!indicesToCursors.contains(closeIndex))
02067 {
02068 TwoViewCursor * twoViewCursor = new TwoViewCursor;
02069 twoViewCursor->index = closeIndex;
02070 indicesToCursors.insert(closeIndex, twoViewCursor);
02071 FAST_DEBUG(" border index added: " << closeIndex);
02072 }
02073 }
02074 }
02075
02076
02077 int curRelLine = 0;
02078 int curRelCol = 0;
02079 int curRelIndex = 0;
02080 QMap<int, TwoViewCursor *>::const_iterator iter = indicesToCursors.constBegin();
02081 while (iter != indicesToCursors.constEnd())
02082 {
02083
02084 const int index = (*iter)->index;
02085 FAST_DEBUG("resolving position" << index);
02086 TwoViewCursor & twoViewCursor = *(*iter);
02087 while (curRelIndex <= index)
02088 {
02089 FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = "
02090 << curRelIndex << "relative, steps more to go" << index - curRelIndex);
02091 const int curRelLineLen = lineLens[curRelLine];
02092 const int curLineRemainder = curRelLineLen - curRelCol;
02093 const int lineFeedIndex = curRelIndex + curLineRemainder;
02094 if (index <= lineFeedIndex) {
02095 if (index == lineFeedIndex) {
02096
02097 FAST_DEBUG(" on line feed");
02098 const int absLine = curRelLine + firstLineIndex;
02099 twoViewCursor.openLine
02100 = twoViewCursor.closeLine
02101 = absLine;
02102 twoViewCursor.openCol
02103 = twoViewCursor.closeCol
02104 = ((curRelLine == 0) ? minColStart : 0) + curRelLineLen;
02105
02106
02107 const int advance = (index - curRelIndex) + 1;
02108 curRelLine++;
02109 curRelCol = 0;
02110 curRelIndex += advance;
02111 } else {
02112
02113 FAST_DEBUG(" before line feed");
02114 const int diff = (index - curRelIndex);
02115 const int absLine = curRelLine + firstLineIndex;
02116 const int absCol = ((curRelLine == 0) ? minColStart : 0) + curRelCol + diff;
02117 twoViewCursor.openLine
02118 = twoViewCursor.closeLine
02119 = absLine;
02120 twoViewCursor.openCol
02121 = twoViewCursor.closeCol
02122 = absCol;
02123
02124
02125 const int advance = diff + 1;
02126 curRelCol += advance;
02127 curRelIndex += advance;
02128 }
02129 FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol
02130 << ") close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")");
02131 }
02132 else
02133 {
02134
02135
02136 FAST_DEBUG(" not on this line");
02137 const int advance = curLineRemainder + 1;
02138 curRelLine++;
02139 curRelCol = 0;
02140 curRelIndex += advance;
02141 }
02142 }
02143
02144 ++iter;
02145 }
02146
02147
02148 QVector<KTextEditor::Range> result(1 + numCaptures);
02149 for (int y = 0; y <= numCaptures; y++)
02150 {
02151 IndexPair & pair = indexPairs[y];
02152 if ((pair.openIndex == -1) || (pair.closeIndex == -1))
02153 {
02154 result[y] = KTextEditor::Range::invalid();
02155 }
02156 else
02157 {
02158 const TwoViewCursor * const openCursors = indicesToCursors[pair.openIndex];
02159 const TwoViewCursor * const closeCursors = indicesToCursors[pair.closeIndex];
02160 const int startLine = openCursors->openLine;
02161 const int startCol = openCursors->openCol;
02162 const int endLine = closeCursors->closeLine;
02163 const int endCol = closeCursors->closeCol;
02164 FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")");
02165 result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol);
02166 }
02167 }
02168
02169
02170 iter = indicesToCursors.constBegin();
02171 while (iter != indicesToCursors.constEnd())
02172 {
02173 TwoViewCursor * const twoViewCursor = *iter;
02174 delete twoViewCursor;
02175 ++iter;
02176 }
02177 return result;
02178 }
02179 else
02180 {
02181
02182 const int minLeft = inputRange.start().column();
02183 const uint maxRight = inputRange.end().column();
02184 const int forMin = inputRange.start().line();
02185 const int forMax = inputRange.end().line();
02186 const int forInit = backwards ? forMax : forMin;
02187 const int forInc = backwards ? -1 : +1;
02188 FAST_DEBUG("single line " << (backwards ? forMax : forMin) << ".."
02189 << (backwards ? forMin : forMax));
02190 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
02191 {
02192 KateTextLine::Ptr textLine = m_buffer->plainLine(j);
02193 if (!textLine)
02194 {
02195 FAST_DEBUG("searchText | line " << j << ": no");
02196 QVector<KTextEditor::Range> result;
02197 result.append(KTextEditor::Range::invalid());
02198 return result;
02199 }
02200
02201
02202 const int first = (j == forMin) ? minLeft : 0;
02203 const int afterLast = (j == forMax) ? maxRight : textLine->length();
02204 const QString hay = textLine->string();
02205 bool found = true;
02206 int foundAt;
02207 uint myMatchLen;
02208 if (backwards) {
02209 const int lineLen = textLine->length();
02210 const int offset = afterLast - lineLen - 1;
02211 FAST_DEBUG("lastIndexIn(" << hay << "," << offset << ")");
02212 foundAt = KateDocument::fixedLastIndexIn(regexp, hay, offset);
02213 found = (foundAt != -1) && (foundAt >= first);
02214 } else {
02215 FAST_DEBUG("indexIn(" << hay << "," << first << ")");
02216 foundAt = regexp.indexIn(hay, first);
02217 found = (foundAt != -1);
02218 }
02219 myMatchLen = found ? regexp.matchedLength() : 0;
02220
02221
02222
02223
02224
02225
02226
02227
02228
02229
02230
02231
02232
02233
02234
02235
02236
02237
02238 if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
02239 {
02240 FAST_DEBUG("line " << j << ": yes");
02241
02242
02243 const int numCaptures = regexp.numCaptures();
02244 QVector<KTextEditor::Range> result(1 + numCaptures);
02245 result[0] = KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
02246 FAST_DEBUG("result range " << 0 << ": (" << j << ", " << foundAt << ")..(" << j << ", " << foundAt + myMatchLen << ")");
02247 for (int y = 1; y <= numCaptures; y++)
02248 {
02249 const int openIndex = regexp.pos(y);
02250 if (openIndex == -1)
02251 {
02252 result[y] = KTextEditor::Range::invalid();
02253 FAST_DEBUG("capture []");
02254 }
02255 else
02256 {
02257 const int closeIndex = openIndex + regexp.cap(y).length();
02258 FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")");
02259 result[y] = KTextEditor::Range(j, openIndex, j, closeIndex);
02260 }
02261 }
02262 return result;
02263 }
02264 else
02265 {
02266 FAST_DEBUG("searchText | line " << j << ": no");
02267 }
02268 }
02269 }
02270
02271 QVector<KTextEditor::Range> result;
02272 result.append(KTextEditor::Range::invalid());
02273 return result;
02274 }
02275
02276 QWidget * KateDocument::dialogParent()
02277 {
02278 QWidget *w=widget();
02279
02280 if(!w)
02281 {
02282 w=activeView();
02283
02284 if(!w)
02285 w=QApplication::activeWindow();
02286 }
02287
02288 return w;
02289 }
02290
02291 QVector<KTextEditor::Range> KateDocument::searchText(
02292 const KTextEditor::Range & range,
02293 const QString & pattern,
02294 const KTextEditor::Search::SearchOptions options)
02295 {
02296
02297
02298
02299 QString workPattern(pattern);
02300
02301 KTextEditor::Search::SearchOptions finalOptions(options);
02302 const bool escapeSequences = finalOptions.testFlag(KTextEditor::Search::EscapeSequences);
02303
02304
02305 if (finalOptions.testFlag(KTextEditor::Search::WholeWords))
02306 {
02307
02308 if (escapeSequences)
02309 {
02310 KateDocument::escapePlaintext(workPattern);
02311 }
02312
02313
02314 workPattern = "\\b" + QRegExp::escape(workPattern) + "\\b";
02315
02316
02317 finalOptions |= KTextEditor::Search::Regex;
02318 finalOptions &= ~KTextEditor::Search::SearchOptions(KTextEditor::Search::WholeWords);
02319 }
02320
02321 const bool regexMode = finalOptions.testFlag(KTextEditor::Search::Regex);
02322 const bool caseSensitive = !finalOptions.testFlag(KTextEditor::Search::CaseInsensitive);
02323 const bool backwards = finalOptions.testFlag(KTextEditor::Search::Backwards);
02324
02325 if (regexMode)
02326 {
02327
02328 const Qt::CaseSensitivity caseSensitivity =
02329 caseSensitive
02330 ? Qt::CaseSensitive
02331 : Qt::CaseInsensitive;
02332
02333 QRegExp matcher(workPattern, caseSensitivity);
02334 if (matcher.isValid())
02335 {
02336
02337
02338 return searchRegex(range, matcher, backwards);
02339 }
02340 else
02341 {
02342
02343 QVector<KTextEditor::Range> result;
02344 result.append(KTextEditor::Range::invalid());
02345 return result;
02346 }
02347 }
02348 else
02349 {
02350
02351
02352
02353 if (escapeSequences)
02354 {
02355 KateDocument::escapePlaintext(workPattern);
02356 }
02357
02358
02359 KTextEditor::Range resultRange = searchText(range, workPattern, caseSensitive, backwards);
02360 QVector<KTextEditor::Range> result;
02361 result.append(resultRange);
02362 return result;
02363 }
02364 }
02365
02366
02367
02368 KTextEditor::Search::SearchOptions KateDocument::supportedSearchOptions() const
02369 {
02370 KTextEditor::Search::SearchOptions supported(KTextEditor::Search::Default);
02371 supported |= KTextEditor::Search::Regex;
02372 supported |= KTextEditor::Search::CaseInsensitive;
02373 supported |= KTextEditor::Search::Backwards;
02374
02375 supported |= KTextEditor::Search::EscapeSequences;
02376 supported |= KTextEditor::Search::WholeWords;
02377
02378 return supported;
02379 }
02380
02381
02382
02383 void KateDocument::escapePlaintext(QString & text, QList<ReplacementPart> * parts,
02384 bool replacementGoodies) {
02385
02386 const int inputLen = text.length();
02387 int input = 0;
02388
02389
02390 QString output;
02391 output.reserve(inputLen + 1);
02392
02393 while (input < inputLen)
02394 {
02395 switch (text[input].unicode())
02396 {
02397 case L'\n':
02398 output.append(text[input]);
02399 input++;
02400 break;
02401
02402 case L'\\':
02403 if (input + 1 >= inputLen)
02404 {
02405
02406 output.append(text[input]);
02407 input++;
02408 break;
02409 }
02410
02411 switch (text[input + 1].unicode())
02412 {
02413 case L'0':
02414 if (input + 4 >= inputLen)
02415 {
02416 if (parts == NULL)
02417 {
02418
02419 output.append(text[input + 1]);
02420 }
02421 else
02422 {
02423
02424 ReplacementPart curPart;
02425
02426
02427 if (!output.isEmpty())
02428 {
02429 curPart.type = ReplacementPart::Text;
02430 curPart.text = output;
02431 output.clear();
02432 parts->append(curPart);
02433 curPart.text.clear();
02434 }
02435
02436
02437 curPart.type = ReplacementPart::Reference;
02438 curPart.index = 0;
02439 parts->append(curPart);
02440 }
02441 input += 2;
02442 }
02443 else
02444 {
02445 bool stripAndSkip = false;
02446 const ushort text_2 = text[input + 2].unicode();
02447 if ((text_2 >= L'0') && (text_2 <= L'3'))
02448 {
02449 const ushort text_3 = text[input + 3].unicode();
02450 if ((text_3 >= L'0') && (text_3 <= L'7'))
02451 {
02452 const ushort text_4 = text[input + 4].unicode();
02453 if ((text_4 >= L'0') && (text_4 <= L'7'))
02454 {
02455 int digits[3];
02456 for (int i = 0; i < 3; i++)
02457 {
02458 digits[i] = 7 - (L'7' - text[input + 2 + i].unicode());
02459 }
02460 const int ch = 64 * digits[0] + 8 * digits[1] + digits[2];
02461 output.append(QChar(ch));
02462 input += 5;
02463 }
02464 else
02465 {
02466 stripAndSkip = true;
02467 }
02468 }
02469 else
02470 {
02471 stripAndSkip = true;
02472 }
02473 }
02474 else
02475 {
02476 stripAndSkip = true;
02477 }
02478
02479 if (stripAndSkip)
02480 {
02481 if (parts == NULL)
02482 {
02483
02484 output.append(text[input + 1]);
02485 }
02486 else
02487 {
02488
02489 ReplacementPart curPart;
02490
02491
02492 if (!output.isEmpty())
02493 {
02494 curPart.type = ReplacementPart::Text;
02495 curPart.text = output;
02496 output.clear();
02497 parts->append(curPart);
02498 curPart.text.clear();
02499 }
02500
02501
02502 curPart.type = ReplacementPart::Reference;
02503 curPart.index = 0;
02504 parts->append(curPart);
02505 }
02506 input += 2;
02507 }
02508 }
02509 break;
02510
02511 case L'1':
02512 case L'2':
02513 case L'3':
02514 case L'4':
02515 case L'5':
02516 case L'6':
02517 case L'7':
02518 case L'8':
02519 case L'9':
02520 if (parts == NULL)
02521 {
02522
02523 output.append(text[input + 1]);
02524 }
02525 else
02526 {
02527
02528 ReplacementPart curPart;
02529
02530
02531 if (!output.isEmpty())
02532 {
02533 curPart.type = ReplacementPart::Text;
02534 curPart.text = output;
02535 output.clear();
02536 parts->append(curPart);
02537 curPart.text.clear();
02538 }
02539
02540
02541 curPart.type = ReplacementPart::Reference;
02542 curPart.index = 9 - (L'9' - text[input + 1].unicode());
02543 parts->append(curPart);
02544 }
02545 input += 2;
02546 break;
02547
02548 case L'E':
02549 case L'L':
02550 case L'U':
02551 if ((parts == NULL) || !replacementGoodies) {
02552
02553 output.append(text[input + 1]);
02554 } else {
02555
02556 ReplacementPart curPart;
02557
02558
02559 if (!output.isEmpty())
02560 {
02561 curPart.type = ReplacementPart::Text;
02562 curPart.text = output;
02563 output.clear();
02564 parts->append(curPart);
02565 curPart.text.clear();
02566 }
02567
02568
02569 switch (text[input + 1].unicode()) {
02570 case L'L':
02571 curPart.type = ReplacementPart::LowerCase;
02572 break;
02573
02574 case L'U':
02575 curPart.type = ReplacementPart::UpperCase;
02576 break;
02577
02578 case L'E':
02579 default:
02580 curPart.type = ReplacementPart::KeepCase;
02581
02582 }
02583 parts->append(curPart);
02584 }
02585 input += 2;
02586 break;
02587
02588 case L'#':
02589 if ((parts == NULL) || !replacementGoodies) {
02590
02591 output.append(text[input + 1]);
02592 input += 2;
02593 } else {
02594
02595 ReplacementPart curPart;
02596
02597
02598 if (!output.isEmpty())
02599 {
02600 curPart.type = ReplacementPart::Text;
02601 curPart.text = output;
02602 output.clear();
02603 parts->append(curPart);
02604 curPart.text.clear();
02605 }
02606
02607
02608
02609 int count = 1;
02610 while ((input + count + 1 < inputLen) && (text[input + count + 1].unicode() == L'#')) {
02611 count++;
02612 }
02613 curPart.type = ReplacementPart::Counter;
02614 curPart.index = count;
02615 parts->append(curPart);
02616 input += 1 + count;
02617 }
02618 break;
02619
02620 case L'a':
02621 output.append(QChar(0x07));
02622 input += 2;
02623 break;
02624
02625 case L'f':
02626 output.append(QChar(0x0c));
02627 input += 2;
02628 break;
02629
02630 case L'n':
02631 output.append(QChar(0x0a));
02632 input += 2;
02633 break;
02634
02635 case L'r':
02636 output.append(QChar(0x0d));
02637 input += 2;
02638 break;
02639
02640 case L't':
02641 output.append(QChar(0x09));
02642 input += 2;
02643 break;
02644
02645 case L'v':
02646 output.append(QChar(0x0b));
02647 input += 2;
02648 break;
02649
02650 case L'x':
02651 if (input + 5 >= inputLen)
02652 {
02653
02654 output.append(text[input + 1]);
02655 input += 2;
02656 }
02657 else
02658 {
02659 bool stripAndSkip = false;
02660 const ushort text_2 = text[input + 2].unicode();
02661 if (((text_2 >= L'0') && (text_2 <= L'9'))
02662 || ((text_2 >= L'a') && (text_2 <= L'f'))
02663 || ((text_2 >= L'A') && (text_2 <= L'F')))
02664 {
02665 const ushort text_3 = text[input + 3].unicode();
02666 if (((text_3 >= L'0') && (text_3 <= L'9'))
02667 || ((text_3 >= L'a') && (text_3 <= L'f'))
02668 || ((text_3 >= L'A') && (text_3 <= L'F')))
02669 {
02670 const ushort text_4 = text[input + 4].unicode();
02671 if (((text_4 >= L'0') && (text_4 <= L'9'))
02672 || ((text_4 >= L'a') && (text_4 <= L'f'))
02673 || ((text_4 >= L'A') && (text_4 <= L'F')))
02674 {
02675 const ushort text_5 = text[input + 5].unicode();
02676 if (((text_5 >= L'0') && (text_5 <= L'9'))
02677 || ((text_5 >= L'a') && (text_5 <= L'f'))
02678 || ((text_5 >= L'A') && (text_5 <= L'F')))
02679 {
02680 int digits[4];
02681 for (int i = 0; i < 4; i++)
02682 {
02683 const ushort cur = text[input + 2 + i].unicode();
02684 if ((cur >= L'0') && (cur <= L'9'))
02685 {
02686 digits[i] = 9 - (L'9' - cur);
02687 }
02688 else if ((cur >= L'a') && (cur <= L'f'))
02689 {
02690 digits[i] = 15 - (L'f' - cur);
02691 }
02692 else
02693 {
02694 digits[i] = 15 - (L'F' - cur);
02695 }
02696 }
02697
02698 const int ch = 4096 * digits[0] + 256 * digits[1] + 16 * digits[2] + digits[3];
02699 output.append(QChar(ch));
02700 input += 6;
02701 }
02702 else
02703 {
02704 stripAndSkip = true;
02705 }
02706 }
02707 else
02708 {
02709 stripAndSkip = true;
02710 }
02711 }
02712 else
02713 {
02714 stripAndSkip = true;
02715 }
02716 }
02717
02718 if (stripAndSkip)
02719 {
02720
02721 output.append(text[input + 1]);
02722 input += 2;
02723 }
02724 }
02725 break;
02726
02727 default:
02728
02729 output.append(text[input + 1]);
02730 input += 2;
02731
02732 }
02733 break;
02734
02735 default:
02736 output.append(text[input]);
02737 input++;
02738
02739 }
02740 }
02741
02742 if (parts == NULL)
02743 {
02744
02745 text = output;
02746 }
02747 else
02748 {
02749
02750 if (!output.isEmpty())
02751 {
02752 ReplacementPart curPart;
02753 curPart.type = ReplacementPart::Text;
02754 curPart.text = output;
02755 parts->append(curPart);
02756 }
02757 }
02758 }
02759
02760
02761
02762
02763
02764
02765
02766
02767 int KateDocument::repairPattern(QString & pattern, bool & stillMultiLine)
02768 {
02769 const QString & text = pattern;
02770
02771
02772 const int inputLen = text.length();
02773 int input = 0;
02774
02775
02776 QString output;
02777 output.reserve(2 * inputLen + 1);
02778
02779
02780 stillMultiLine = false;
02781 int replaceCount = 0;
02782 bool insideClass = false;
02783
02784 while (input < inputLen)
02785 {
02786 if (insideClass)
02787 {
02788
02789 switch (text[input].unicode())
02790 {
02791 case L'\\':
02792 switch (text[input + 1].unicode())
02793 {
02794 case L'x':
02795 if (input + 5 < inputLen)
02796 {
02797
02798 output.append(text.mid(input, 6));
02799 input += 6;
02800 } else {
02801
02802 output.append(text.mid(input, 2));
02803 input += 2;
02804 }
02805 stillMultiLine = true;
02806 break;
02807
02808 case L'0':
02809 if (input + 4 < inputLen)
02810 {
02811
02812 output.append(text.mid(input, 5));
02813 input += 5;
02814 } else {
02815
02816 output.append(text.mid(input, 2));
02817 input += 2;
02818 }
02819 stillMultiLine = true;
02820 break;
02821
02822 case L's':
02823
02824 output.append("[ \\t]");
02825 input += 2;
02826 replaceCount++;
02827 break;
02828
02829 case L'n':
02830 stillMultiLine = true;
02831
02832
02833 default:
02834
02835 output.append(text.mid(input, 2));
02836 input += 2;
02837 }
02838 break;
02839
02840 case L']':
02841
02842 insideClass = false;
02843 output.append(text[input]);
02844 input++;
02845 break;
02846
02847 default:
02848
02849 output.append(text[input]);
02850 input++;
02851
02852 }
02853 }
02854 else
02855 {
02856
02857 switch (text[input].unicode())
02858 {
02859 case L'\\':
02860 switch (text[input + 1].unicode())
02861 {
02862 case L'x':
02863 if (input + 5 < inputLen)
02864 {
02865
02866 output.append(text.mid(input, 6));
02867 input += 6;
02868 } else {
02869
02870 output.append(text.mid(input, 2));
02871 input += 2;
02872 }
02873 stillMultiLine = true;
02874 break;
02875
02876 case L'0':
02877 if (input + 4 < inputLen)
02878 {
02879
02880 output.append(text.mid(input, 5));
02881 input += 5;
02882 } else {
02883
02884 output.append(text.mid(input, 2));
02885 input += 2;
02886 }
02887 stillMultiLine = true;
02888 break;
02889
02890 case L's':
02891
02892 output.append("[ \\t]");
02893 input += 2;
02894 replaceCount++;
02895 break;
02896
02897 case L'n':
02898 stillMultiLine = true;
02899
02900
02901 default:
02902
02903 output.append(text.mid(input, 2));
02904 input += 2;
02905 }
02906 break;
02907
02908 case L'.':
02909
02910 output.append("[^\\n]");
02911 input++;
02912 replaceCount++;
02913 break;
02914
02915 case L'[':
02916
02917 insideClass = true;
02918 output.append(text[input]);
02919 input++;
02920 break;
02921
02922 default:
02923
02924 output.append(text[input]);
02925 input++;
02926
02927 }
02928 }
02929 }
02930
02931
02932 pattern = output;
02933 return replaceCount;
02934 }
02935
02936
02937
02938 int KateDocument::fixedLastIndexIn(const QRegExp & matcher, const QString & str,
02939 int offset, QRegExp::CaretMode caretMode) {
02940 int prevPos = -1;
02941 int prevLen = 1;
02942 const int strLen = str.length();
02943 for (;;) {
02944 const int pos = matcher.indexIn(str, prevPos + prevLen, caretMode);
02945 if (pos == -1) {
02946
02947 break;
02948 } else {
02949 const int len = matcher.matchedLength();
02950 if (pos > strLen + offset + 1) {
02951
02952 break;
02953 }
02954
02955 if (pos + len > strLen + offset + 1) {
02956
02957 if (offset == -1) {
02958
02959 break;
02960 }
02961
02962 const QString str2 = str.mid(0, strLen + offset + 1);
02963 const int pos2 = matcher.indexIn(str2, pos, caretMode);
02964 if (pos2 != -1) {
02965
02966 return pos2;
02967 } else {
02968
02969 break;
02970 }
02971 }
02972
02973
02974 prevPos = pos;
02975 prevLen = (len == 0) ? 1 : len;
02976 }
02977 }
02978
02979
02980 if (prevPos != -1) {
02981
02982 matcher.indexIn(str, prevPos, caretMode);
02983 return prevPos;
02984 } else {
02985 return -1;
02986 }
02987 }
02988
02989
02990
02991 bool KateDocument::setMode (const QString &name)
02992 {
02993 updateFileType (name);
02994 return true;
02995 }
02996
02997 QString KateDocument::mode () const
02998 {
02999 return m_fileType;
03000 }
03001
03002 QStringList KateDocument::modes () const
03003 {
03004 QStringList m;
03005
03006 const QList<KateFileType *> &modeList = KateGlobal::self()->modeManager()->list();
03007 for (int i = 0; i < modeList.size(); ++i)
03008 m << modeList[i]->name;
03009
03010 return m;
03011 }
03012
03013 bool KateDocument::setHighlightingMode (const QString &name)
03014 {
03015 m_buffer->setHighlight (KateHlManager::self()->nameFind(name));
03016
03017 if (true)
03018 {
03019 setDontChangeHlOnSave();
03020 return true;
03021 }
03022
03023 return false;
03024 }
03025
03026 QString KateDocument::highlightingMode () const
03027 {
03028 return highlight()->name ();
03029 }
03030
03031 QStringList KateDocument::highlightingModes () const
03032 {
03033 QStringList hls;
03034
03035 for (int i = 0; i < KateHlManager::self()->highlights(); ++i)
03036 hls << KateHlManager::self()->hlName (i);
03037
03038 return hls;
03039 }
03040
03041 QString KateDocument::highlightingModeSection( int index ) const
03042 {
03043 return KateHlManager::self()->hlSection( index );
03044 }
03045
03046 QString KateDocument::modeSection( int index ) const
03047 {
03048 return KateGlobal::self()->modeManager()->list()[ index ]->section;
03049 }
03050
03051 void KateDocument::bufferHlChanged ()
03052 {
03053
03054 makeAttribs(false);
03055
03056
03057 m_indenter.checkRequiredStyle();
03058
03059 emit highlightingModeChanged(this);
03060 }
03061
03062 void KateDocument::setDontChangeHlOnSave()
03063 {
03064 hlSetByUser = true;
03065 }
03066
03067
03068
03069 void KateDocument::readSessionConfig(const KConfigGroup &kconfig)
03070 {
03071
03072 KUrl url (kconfig.readEntry("URL"));
03073
03074
03075 QString tmpenc=kconfig.readEntry("Encoding");
03076 if (!tmpenc.isEmpty() && (tmpenc != encoding()))
03077 setEncoding(tmpenc);
03078
03079
03080 if (!url.isEmpty() && url.isValid())
03081 openUrl (url);
03082 else completed();
03083
03084
03085 updateFileType (kconfig.readEntry("Mode", "Normal"));
03086
03087
03088 m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig.readEntry("Highlighting")));
03089
03090
03091 config()->setIndentationMode( kconfig.readEntry("Indentation Mode", config()->indentationMode() ) );
03092
03093
03094 QList<int> marks = kconfig.readEntry("Bookmarks", QList<int>());
03095 for( int i = 0; i < marks.count(); i++ )
03096 addMark( marks[i], KateDocument::markType01 );
03097 }
03098
03099 void KateDocument::writeSessionConfig(KConfigGroup &kconfig)
03100 {
03101 if ( this->url().isLocalFile() ) {
03102 const QString path = this->url().path();
03103 if ( KGlobal::dirs()->relativeLocation( "tmp", path ) != path ) {
03104 return;
03105 }
03106 }
03107
03108 kconfig.writeEntry("URL", this->url().prettyUrl() );
03109
03110
03111 kconfig.writeEntry("Encoding",encoding());
03112
03113
03114 kconfig.writeEntry("Mode", m_fileType);
03115
03116
03117 kconfig.writeEntry("Highlighting", highlight()->name());
03118
03119
03120 kconfig.writeEntry("Indentation Mode", config()->indentationMode() );
03121
03122
03123 QList<int> marks;
03124 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
03125 if (i.value()->type & KTextEditor::MarkInterface::markType01)
03126 marks << i.value()->line;
03127
03128 kconfig.writeEntry( "Bookmarks", marks );
03129 }
03130
03131 uint KateDocument::mark( int line )
03132 {
03133 if( !m_marks.value(line) )
03134 return 0;
03135
03136 return m_marks[line]->type;
03137 }
03138
03139 void KateDocument::setMark( int line, uint markType )
03140 {
03141 clearMark( line );
03142 addMark( line, markType );
03143 }
03144
03145 void KateDocument::clearMark( int line )
03146 {
03147 if( line > lastLine() )
03148 return;
03149
03150 if( !m_marks.value(line) )
03151 return;
03152
03153 KTextEditor::Mark* mark = m_marks.take( line );
03154 emit markChanged( this, *mark, MarkRemoved );
03155 emit marksChanged( this );
03156 delete mark;
03157 tagLines( line, line );
03158 repaintViews(true);
03159 }
03160
03161 void KateDocument::addMark( int line, uint markType )
03162 {
03163 if( line > lastLine())
03164 return;
03165
03166 if( markType == 0 )
03167 return;
03168
03169 if( m_marks.value(line) ) {
03170 KTextEditor::Mark* mark = m_marks[line];
03171
03172
03173 markType &= ~mark->type;
03174
03175 if( markType == 0 )
03176 return;
03177
03178
03179 mark->type |= markType;
03180 } else {
03181 KTextEditor::Mark *mark = new KTextEditor::Mark;
03182 mark->line = line;
03183 mark->type = markType;
03184 m_marks.insert( line, mark );
03185 }
03186
03187
03188 KTextEditor::Mark temp;
03189 temp.line = line;
03190 temp.type = markType;
03191 emit markChanged( this, temp, MarkAdded );
03192
03193 emit marksChanged( this );
03194 tagLines( line, line );
03195 repaintViews(true);
03196 }
03197
03198 void KateDocument::removeMark( int line, uint markType )
03199 {
03200 if( line > lastLine() )
03201 return;
03202
03203 if( !m_marks.value(line) )
03204 return;
03205
03206 KTextEditor::Mark* mark = m_marks[line];
03207
03208
03209 markType &= mark->type;
03210
03211 if( markType == 0 )
03212 return;
03213
03214
03215 mark->type &= ~markType;
03216
03217
03218 KTextEditor::Mark temp;
03219 temp.line = line;
03220 temp.type = markType;
03221 emit markChanged( this, temp, MarkRemoved );
03222
03223 if( mark->type == 0 )
03224 m_marks.remove( line );
03225
03226 emit marksChanged( this );
03227 tagLines( line, line );
03228 repaintViews(true);
03229 }
03230
03231 const QHash<int, KTextEditor::Mark*> &KateDocument::marks()
03232 {
03233 return m_marks;
03234 }
03235
03236 void KateDocument::clearMarks()
03237 {
03238 while (!m_marks.isEmpty())
03239 {
03240 QHash<int, KTextEditor::Mark*>::iterator it = m_marks.begin();
03241 KTextEditor::Mark mark = *it.value();
03242 delete it.value();
03243 m_marks.erase (it);
03244
03245 emit markChanged( this, mark, MarkRemoved );
03246 tagLines( mark.line, mark.line );
03247 }
03248
03249 m_marks.clear();
03250
03251 emit marksChanged( this );
03252 repaintViews(true);
03253 }
03254
03255 void KateDocument::setMarkPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
03256 {
03257 m_markPixmaps.insert( type, pixmap );
03258 }
03259
03260 void KateDocument::setMarkDescription( MarkInterface::MarkTypes type, const QString& description )
03261 {
03262 m_markDescriptions.insert( type, description );
03263 }
03264
03265 QPixmap KateDocument::markPixmap( MarkInterface::MarkTypes type ) const
03266 {
03267 return m_markPixmaps.contains(type) ?
03268 m_markPixmaps[type] : QPixmap();
03269 }
03270
03271 QColor KateDocument::markColor( MarkInterface::MarkTypes type ) const
03272 {
03273 uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
03274 if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
03275 return KateRendererConfig::global()->lineMarkerColor(type);
03276 } else {
03277 return QColor();
03278 }
03279 }
03280
03281 QString KateDocument::markDescription( MarkInterface::MarkTypes type ) const
03282 {
03283 return m_markDescriptions.contains(type) ?
03284 m_markDescriptions[type] : QString();
03285 }
03286
03287 void KateDocument::setEditableMarks( uint markMask )
03288 {
03289 m_editableMarks = markMask;
03290 }
03291
03292 uint KateDocument::editableMarks() const
03293 {
03294 return m_editableMarks;
03295 }
03296
03297
03298
03299 bool KateDocument::printDialog ()
03300 {
03301 return KatePrinter::print (this);
03302 }
03303
03304 bool KateDocument::print ()
03305 {
03306 return KatePrinter::print (this);
03307 }
03308
03309
03310
03311 QString KateDocument::mimeType()
03312 {
03313 KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
03314
03315
03316 if ( ! this->url().isEmpty() )
03317 result = KMimeType::findByUrl( this->url() );
03318
03319 else if ( this->url().isEmpty() || ! this->url().isLocalFile() )
03320 result = mimeTypeForContent();
03321
03322 return result->name();
03323 }
03324
03325 KMimeType::Ptr KateDocument::mimeTypeForContent()
03326 {
03327 QByteArray buf (1024,'\0');
03328 uint bufpos = 0;
03329
03330 for (int i=0; i < lines(); ++i)
03331 {
03332 QString line = this->line( i );
03333 uint len = line.length() + 1;
03334
03335 if (bufpos + len > 1024)
03336 len = 1024 - bufpos;
03337
03338 QString ld (line + QChar::fromAscii('\n'));
03339 buf.replace(bufpos,len,ld.toLatin1());
03340
03341 bufpos += len;
03342
03343 if (bufpos >= 1024)
03344 break;
03345 }
03346 buf.resize( bufpos );
03347
03348 int accuracy = 0;
03349 KMimeType::Ptr mt = KMimeType::findByContent(buf, &accuracy);
03350 return mt ? mt : KMimeType::defaultMimeTypePtr();
03351 }
03352
03353
03354
03355
03356 bool KateDocument::openFile()
03357 {
03358
03359 setOpeningError(false);
03360
03361
03362 activateDirWatch ();
03363
03364
03365
03366
03367 QString mimeType = arguments().mimeType();
03368 int pos = mimeType.indexOf(';');
03369 if (pos != -1)
03370 setEncoding (mimeType.mid(pos+1));
03371
03372
03373 emit KTextEditor::Document::textRemoved(this, documentRange());
03374 history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03375
03376 bool success = m_buffer->openFile (localFilePath());
03377
03378 emit KTextEditor::Document::textInserted(this, documentRange());
03379 history()->doEdit( new KateEditInfo(Kate::OpenFileEdit, KTextEditor::Range(0,0,0,0), QStringList(), documentRange(), QStringList()) );
03380
03381
03382
03383
03384 if (success)
03385 {
03386
03387 updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03388
03389
03390 readDirConfig ();
03391
03392
03393 readVariables();
03394
03395
03396 createDigest( m_digest );
03397
03398 if (!m_postLoadFilterChecks.isEmpty())
03399 {
03400 LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03401 foreach(const QString& checkplugin, m_postLoadFilterChecks)
03402 {
03403 lscps->postLoadFilter(checkplugin,this);
03404 }
03405 }
03406 }
03407
03408
03409 emit textChanged (this);
03410
03411
03412
03413
03414 foreach (KateView * view, m_views)
03415 {
03416
03417 view->setCursorPosition(KTextEditor::Cursor());
03418 view->updateView(true);
03419 }
03420
03421 if (!m_reloading)
03422 {
03423
03424
03425
03426 emit documentUrlChanged (this);
03427
03428
03429
03430
03431 setDocName (QString());
03432 }
03433
03434
03435
03436 if (m_modOnHd)
03437 {
03438 m_modOnHd = false;
03439 m_modOnHdReason = OnDiskUnmodified;
03440 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03441 }
03442
03443
03444
03445
03446 QWidget *parentWidget(dialogParent());
03447
03448 if (!suppressOpeningErrorDialogs())
03449 {
03450 if (!success)
03451 KMessageBox::error (parentWidget, 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.", this->url().pathOrUrl()));
03452 }
03453
03454 if (!success) {
03455 setOpeningError(true);
03456 setOpeningErrorMessage(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.",this->url().pathOrUrl()));
03457 }
03458
03459
03460 if (m_buffer->binary())
03461 {
03462
03463 setReadWrite( false );
03464
03465 if(!suppressOpeningErrorDialogs())
03466 KMessageBox::information (parentWidget
03467 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl())
03468 , i18n ("Binary File Opened")
03469 , "Binary File Opened Warning");
03470
03471 setOpeningError(true);
03472 setOpeningErrorMessage(i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl()));
03473 }
03474
03475
03476
03477 else if (m_buffer->brokenUTF8())
03478 {
03479
03480 setReadWrite( false );
03481
03482 if (!suppressOpeningErrorDialogs())
03483 KMessageBox::information (parentWidget
03484 , i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03485 " It is set to read-only mode, as saving might destroy its content."
03486 " Either reopen the file with the correct encoding chosen or enable the read-write mode again in the menu to be able to edit it.", this->url().pathOrUrl())
03487 , i18n ("Broken UTF-8 File Opened")
03488 , "Broken UTF-8 File Opened Warning");
03489 setOpeningError(true);
03490 setOpeningErrorMessage(i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03491 " It is set to read-only mode, as saving might destroy its content."
03492 " Either reopen the file with the correct encoding chosen or enable the read-write mode again in the menu to be able to edit it.", this->url().pathOrUrl()));
03493 }
03494
03495
03496
03497
03498 return success;
03499 }
03500
03501 bool KateDocument::saveFile()
03502 {
03503 QWidget *parentWidget(dialogParent());
03504
03505
03506
03507
03508 if (m_buffer->binary() && (KMessageBox::warningContinueCancel (parentWidget
03509 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", url().pathOrUrl())
03510 , i18n ("Trying to Save Binary File")
03511 , KGuiItem(i18n("Save Nevertheless"))
03512 , KStandardGuiItem::cancel(), "Binary File Save Warning") != KMessageBox::Continue))
03513 return false;
03514
03515
03516 if ( !url().isEmpty() )
03517 {
03518 if (s_fileChangedDialogsActivated && m_modOnHd)
03519 {
03520 QString str = reasonedMOHString() + "\n\n";
03521
03522 if (!isModified())
03523 {
03524 if (KMessageBox::warningContinueCancel(parentWidget,
03525 str + i18n("Do you really want to save this unmodified file? You could overwrite changed data in the file on disk."),i18n("Trying to Save Unmodified File"),KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue)
03526 return false;
03527 }
03528 else
03529 {
03530 if (KMessageBox::warningContinueCancel(parentWidget,
03531 str + i18n("Do you really want to save this file? Both your open file and the file on disk were changed. There could be some data lost."),i18n("Possible Data Loss"),KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue)
03532 return false;
03533 }
03534 }
03535 }
03536
03537
03538
03539
03540 if (!m_buffer->canEncode ()
03541 && (KMessageBox::warningContinueCancel(parentWidget,
03542 i18n("The selected encoding cannot encode every unicode character in this document. Do you really want to save it? There could be some data lost."),i18n("Possible Data Loss"),KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue))
03543 {
03544 return false;
03545 }
03546
03547
03548
03549
03550
03551
03552 bool l ( url().isLocalFile() );
03553
03554
03555 if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
03556 || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
03557 {
03558 KUrl u( url() );
03559 u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
03560
03561 kDebug( 13020 ) << "backup src file name: " << url();
03562 kDebug( 13020 ) << "backup dst file name: " << u;
03563
03564
03565 bool backupSuccess = false;
03566
03567
03568 if (u.isLocalFile ())
03569 {
03570 if (QFile::exists (url().toLocalFile ()))
03571 {
03572
03573 QFile backupFile (u.toLocalFile ());
03574 if (backupFile.exists()) backupFile.remove ();
03575
03576 backupSuccess = QFile::copy (url().toLocalFile (), u.toLocalFile ());
03577 }
03578 else
03579 backupSuccess = true;
03580 }
03581 else
03582 {
03583 QWidget *w = widget ();
03584 if (!w && !m_views.isEmpty ())
03585 w = m_views.first();
03586
03587
03588 mode_t perms = 0600;
03589 KIO::UDSEntry fentry;
03590 if (KIO::NetAccess::stat (url(), fentry, kapp->activeWindow()))
03591 {
03592 kDebug( 13020 ) << "stating succesfull: " << url();
03593 KFileItem item (fentry, url());
03594 perms = item.permissions();
03595
03596
03597 KIO::FileCopyJob *job = KIO::file_copy ( url(), u, -1, KIO::Overwrite );
03598 backupSuccess = KIO::NetAccess::synchronousRun(job, w);
03599 }
03600 else
03601 backupSuccess = true;
03602 }
03603
03604
03605 if (!backupSuccess && (KMessageBox::warningContinueCancel (parentWidget
03606 , i18n ("For file %1 no backup copy could be created before saving."
03607 " If an error occurs while saving, you might lose the data of this file."
03608 " A reason could be that the media you write to is full or the directory of the file is read-only for you.", url().pathOrUrl())
03609 , i18n ("Failed to create backup copy.")
03610 , KGuiItem(i18n("Try to Save Nevertheless"))
03611 , KStandardGuiItem::cancel(), "Backup Failed Warning") != KMessageBox::Continue))
03612 {
03613 return false;
03614 }
03615 }
03616
03617
03618 updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03619
03620 if (!m_preSavePostDialogFilterChecks.isEmpty())
03621 {
03622 LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03623 foreach(const QString& checkplugin, m_preSavePostDialogFilterChecks)
03624 {
03625 if (lscps->preSavePostDialogFilterCheck(checkplugin,this,parentWidget)==false)
03626 return false;
03627 }
03628 }
03629
03630
03631 QString oldPath = m_dirWatchFile;
03632
03633
03634 deactivateDirWatch ();
03635
03636
03637
03638
03639 if (!m_buffer->saveFile (localFilePath()))
03640 {
03641
03642 activateDirWatch (oldPath);
03643
03644 KMessageBox::error (parentWidget, 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.", this->url().pathOrUrl()));
03645
03646 return false;
03647 }
03648
03649
03650 createDigest( m_digest );
03651
03652
03653 activateDirWatch ();
03654
03655
03656
03657
03658
03659 if ( url().isLocalFile())
03660 {
03661 QFileInfo fo (oldPath), fn (m_dirWatchFile);
03662
03663 if (fo.path() != fn.path())
03664 readDirConfig();
03665 }
03666
03667
03668 readVariables();
03669
03670
03671
03672
03673 if (m_modOnHd)
03674 {
03675 m_modOnHd = false;
03676 m_modOnHdReason = OnDiskUnmodified;
03677 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03678 }
03679
03680
03681 setDocName( QString() );
03682
03683
03684 emit documentUrlChanged (this);
03685
03686 m_savingToUrl=true;
03687
03688
03689
03690 setUndoDontMerge(true);
03691
03692
03693
03694
03695 return true;
03696 }
03697
03698 void KateDocument::readDirConfig ()
03699 {
03700 int depth = config()->searchDirConfigDepth ();
03701
03702 if (this->url().isLocalFile() && (depth > -1))
03703 {
03704 QString currentDir = QFileInfo (localFilePath()).absolutePath();
03705
03706
03707 while (depth > -1)
03708 {
03709
03710
03711
03712 QFile f (currentDir + "/.kateconfig");
03713
03714 if (f.open (QIODevice::ReadOnly))
03715 {
03716 QTextStream stream (&f);
03717
03718 uint linesRead = 0;
03719 QString line = stream.readLine();
03720 while ((linesRead < 32) && !line.isNull())
03721 {
03722 readVariableLine( line );
03723
03724 line = stream.readLine();
03725
03726 linesRead++;
03727 }
03728
03729 break;
03730 }
03731
03732 QString newDir = QFileInfo (currentDir).absolutePath();
03733
03734
03735 if (currentDir == newDir)
03736 break;
03737
03738 currentDir = newDir;
03739 --depth;
03740 }
03741 }
03742 }
03743
03744 void KateDocument::activateDirWatch (const QString &useFileName)
03745 {
03746 QString fileToUse = useFileName;
03747 if (fileToUse.isEmpty())
03748 fileToUse = localFilePath();
03749
03750
03751 if (fileToUse == m_dirWatchFile)
03752 return;
03753
03754
03755 deactivateDirWatch ();
03756
03757
03758 if (url().isLocalFile() && !fileToUse.isEmpty())
03759 {
03760 KateGlobal::self()->dirWatch ()->addFile (fileToUse);
03761 m_dirWatchFile = fileToUse;
03762 }
03763 }
03764
03765 void KateDocument::deactivateDirWatch ()
03766 {
03767 if (!m_dirWatchFile.isEmpty())
03768 KateGlobal::self()->dirWatch ()->removeFile (m_dirWatchFile);
03769
03770 m_dirWatchFile.clear();
03771 }
03772
03773 bool KateDocument::closeUrl()
03774 {
03775
03776
03777
03778 if ( !m_reloading && !url().isEmpty() )
03779 {
03780 if (s_fileChangedDialogsActivated && m_modOnHd)
03781 {
03782 QWidget *parentWidget(dialogParent());
03783
03784 if (!(KMessageBox::warningContinueCancel(
03785 parentWidget,
03786 reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
03787 i18n("Possible Data Loss"), KGuiItem(i18n("Close Nevertheless")), KStandardGuiItem::cancel(),
03788 QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
03789 return false;
03790 }
03791 }
03792
03793
03794
03795
03796 if (!KParts::ReadWritePart::closeUrl ())
03797 return false;
03798
03799
03800 if (!m_reloading)
03801 emit aboutToClose(this);
03802
03803
03804 deactivateDirWatch ();
03805
03806
03807
03808
03809 setUrl(KUrl());
03810 setLocalFilePath(QString());
03811
03812
03813 if (m_modOnHd)
03814 {
03815 m_modOnHd = false;
03816 m_modOnHdReason = OnDiskUnmodified;
03817 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03818 }
03819
03820 emit KTextEditor::Document::textRemoved(this, documentRange());
03821 history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03822
03823
03824 m_buffer->clear();
03825
03826
03827 clearMarks ();
03828
03829
03830 clearUndo();
03831 clearRedo();
03832
03833
03834 setModified(false);
03835
03836
03837 m_buffer->setHighlight(0);
03838
03839
03840 foreach (KateView * view, m_views )
03841 {
03842 view->clearSelection();
03843 view->clear();
03844 }
03845
03846 if (!m_reloading)
03847 {
03848
03849
03850 emit documentUrlChanged (this);
03851
03852
03853 setDocName (QString());
03854 }
03855
03856 return true;
03857 }
03858
03859 void KateDocument::setReadWrite( bool rw )
03860 {
03861 if (isReadWrite() != rw)
03862 {
03863 KParts::ReadWritePart::setReadWrite (rw);
03864
03865 foreach( KateView* view, m_views)
03866 {
03867 view->slotUpdate();
03868 view->slotReadWriteChanged ();
03869 }
03870 }
03871 }
03872
03873 void KateDocument::setModified(bool m) {
03874
03875 if (isModified() != m) {
03876 KParts::ReadWritePart::setModified (m);
03877
03878 foreach( KateView* view,m_views)
03879 {
03880 view->slotUpdate();
03881 }
03882
03883 emit modifiedChanged (this);
03884 }
03885 if ( m == false )
03886 {
03887 if ( ! undoItems.isEmpty() )
03888 {
03889 lastUndoGroupWhenSaved = undoItems.last();
03890 }
03891
03892 if ( ! redoItems.isEmpty() )
03893 {
03894 lastRedoGroupWhenSaved = redoItems.last();
03895 }
03896
03897 docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
03898 docWasSavedWhenRedoWasEmpty = redoItems.isEmpty();
03899 }
03900 }
03901
03902
03903
03904
03905 void KateDocument::makeAttribs(bool needInvalidate)
03906 {
03907 foreach(KateView *view,m_views)
03908 view->renderer()->updateAttributes ();
03909
03910 if (needInvalidate)
03911 m_buffer->invalidateHighlighting();
03912
03913 tagAll ();
03914 }
03915
03916
03917 void KateDocument::internalHlChanged()
03918 {
03919 makeAttribs();
03920 }
03921
03922 void KateDocument::addView(KTextEditor::View *view) {
03923 if (!view)
03924 return;
03925
03926 m_views.append( static_cast<KateView*>(view) );
03927 m_textEditViews.append( view );
03928
03929 foreach(KTextEditor::SmartRange* highlight, m_documentHighlights) {
03930 Q_ASSERT(dynamic_cast<KateView*>(view));
03931 static_cast<KateView*>(view)->addExternalHighlight(highlight, m_documentDynamicHighlights.contains(highlight));
03932 }
03933
03934
03935 if (!m_fileType.isEmpty())
03936 readVariableLine(KateGlobal::self()->modeManager()->fileType(m_fileType).varLine, true);
03937
03938
03939 readVariables (true);
03940
03941 setActiveView(view);
03942 }
03943
03944 void KateDocument::removeView(KTextEditor::View *view) {
03945 if (!view)
03946 return;
03947
03948 if (activeView() == view)
03949 setActiveView(0L);
03950
03951 m_views.removeAll( (KateView *) view );
03952 m_textEditViews.removeAll( view );
03953 }
03954
03955 void KateDocument::setActiveView(KTextEditor::View* view)
03956 {
03957 if ( m_activeView == view ) return;
03958
03959 if (m_activeView) {
03960 disconnect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), this, SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03961 }
03962
03963 m_activeView = (KateView*)view;
03964
03965 if (m_activeView) {
03966 connect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03967 }
03968 }
03969
03970 bool KateDocument::ownedView(KateView *view) {
03971
03972 return (m_views.contains(view));
03973 }
03974
03975 uint KateDocument::toVirtualColumn( const KTextEditor::Cursor& cursor )
03976 {
03977 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03978
03979 if (textLine)
03980 return textLine->toVirtualColumn(cursor.column(), config()->tabWidth());
03981 else
03982 return 0;
03983 }
03984
03985 bool KateDocument::typeChars ( KateView *view, const QString &chars )
03986 {
03987
03988 QMutexLocker l(smartMutex());
03989
03990 KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorPosition().line ());
03991
03992 if (!textLine)
03993 return false;
03994
03995 bool bracketInserted = false;
03996 QString buf;
03997 QChar c;
03998 for( int z = 0; z < chars.length(); z++ )
03999 {
04000 QChar ch = c = chars[z];
04001
04002 if (ch.isPrint() || ch == QChar::fromAscii('\t'))
04003 {
04004 buf.append (ch);
04005
04006 if (!bracketInserted && (config()->configFlags() & KateDocumentConfig::cfAutoBrackets))
04007 {
04008 QChar end_ch;
04009 bool complete = true;
04010 QChar prevChar = textLine->at(view->cursorPosition().column()-1);
04011 QChar nextChar = textLine->at(view->cursorPosition().column());
04012 switch(ch.toAscii()) {
04013 case '(': end_ch = ')'; break;
04014 case '[': end_ch = ']'; break;
04015 case '{': end_ch = '}'; break;
04016 case '\'':end_ch = '\'';break;
04017 case '"': end_ch = '"'; break;
04018 default: complete = false;
04019 }
04020 if (complete)
04021 {
04022 if (view->selection())
04023 {
04024 buf.append (view->selectionText());
04025 buf.append (end_ch);
04026 bracketInserted = true;
04027 }
04028 else
04029 {
04030 if ( ( (ch == '\'' || ch == '"') &&
04031 (prevChar.isLetterOrNumber() || prevChar == ch) )
04032 || nextChar.isLetterOrNumber()
04033 || (nextChar == end_ch && prevChar != ch) )
04034 {
04035 kDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
04036 }
04037 else
04038 {
04039 buf.append (end_ch);
04040 bracketInserted = true;
04041 }
04042 }
04043 }
04044 }
04045 }
04046 }
04047
04048 if (buf.isEmpty())
04049 return false;
04050
04051 l.unlock();
04052
04053 editStart ();
04054
04055 if (!view->config()->persistentSelection() && view->selection() )
04056 view->removeSelectedText();
04057
04058 KTextEditor::Cursor oldCur (view->cursorPosition());
04059
04060 if (config()->configFlags() & KateDocumentConfig::cfOvr)
04061 removeText(KTextEditor::Range(view->cursorPosition(), qMin(buf.length(), textLine->length() - view->cursorPosition().column())));
04062
04063 insertText(view->cursorPosition(), buf);
04064 KTextEditor::Cursor b(view->cursorPosition());
04065 m_indenter.userTypedChar (view, b, c);
04066
04067 editEnd ();
04068
04069 if (bracketInserted)
04070 view->setCursorPositionInternal (view->cursorPosition() - KTextEditor::Cursor(0,1));
04071
04072 view->slotTextInserted (view, oldCur, chars);
04073 return true;
04074 }
04075
04076 void KateDocument::newLine( KateView *v )
04077 {
04078 editStart();
04079
04080 if( !v->config()->persistentSelection() && v->selection() )
04081 v->removeSelectedText();
04082
04083
04084 KTextEditor::Cursor c = v->cursorPosition();
04085
04086 if (c.line() > (int)lastLine())
04087 c.setLine(lastLine());
04088
04089 if (c.line() < 0)
04090 c.setLine(0);
04091
04092 uint ln = c.line();
04093
04094 KateTextLine::Ptr textLine = plainKateTextLine(ln);
04095
04096 if (c.column() > (int)textLine->length())
04097 c.setColumn(textLine->length());
04098
04099
04100 editWrapLine (c.line(), c.column());
04101
04102
04103 m_indenter.userTypedChar(v, v->cursorPosition(), '\n');
04104
04105 removeTrailingSpace( ln );
04106
04107 editEnd();
04108 }
04109
04110 void KateDocument::transpose( const KTextEditor::Cursor& cursor)
04111 {
04112 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04113
04114 if (!textLine || (textLine->length() < 2))
04115 return;
04116
04117 uint col = cursor.column();
04118
04119 if (col > 0)
04120 col--;
04121
04122 if ((textLine->length() - col) < 2)
04123 return;
04124
04125 uint line = cursor.line();
04126 QString s;
04127
04128
04129
04130 s.append (textLine->at(col+1));
04131 s.append (textLine->at(col));
04132
04133
04134
04135 editStart ();
04136 editRemoveText (line, col, 2);
04137 editInsertText (line, col, s);
04138 editEnd ();
04139 }
04140
04141 void KateDocument::backspace( KateView *view, const KTextEditor::Cursor& c )
04142 {
04143 if ( !view->config()->persistentSelection() && view->selection() ) {
04144 view->removeSelectedText();
04145 return;
04146 }
04147
04148 uint col = qMax( c.column(), 0 );
04149 uint line = qMax( c.line(), 0 );
04150
04151 if ((col == 0) && (line == 0))
04152 return;
04153
04154 int complement = 0;
04155 if (col > 0)
04156 {
04157 if (config()->configFlags() & KateDocumentConfig::cfAutoBrackets)
04158 {
04159
04160 KateTextLine::Ptr tl = m_buffer->plainLine(line);
04161 if(!tl) return;
04162 QChar prevChar = tl->at(col-1);
04163 QChar nextChar = tl->at(col);
04164
04165 if ( (prevChar == '"' && nextChar == '"') ||
04166 (prevChar == '\'' && nextChar == '\'') ||
04167 (prevChar == '(' && nextChar == ')') ||
04168 (prevChar == '[' && nextChar == ']') ||
04169 (prevChar == '{' && nextChar == '}') )
04170 {
04171 complement = 1;
04172 }
04173 }
04174 if (!(config()->configFlags() & KateDocumentConfig::cfBackspaceIndents))
04175 {
04176
04177
04178 removeText(KTextEditor::Range(line, col-1, line, col+complement));
04179 }
04180 else
04181 {
04182
04183 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
04184
04185
04186 if (!textLine)
04187 return;
04188
04189 int colX = textLine->toVirtualColumn(col, config()->tabWidth());
04190 int pos = textLine->firstChar();
04191 if (pos > 0)
04192 pos = textLine->toVirtualColumn(pos, config()->tabWidth());
04193
04194 if (pos < 0 || pos >= (int)colX)
04195 {
04196
04197 indent( view, line, -1);
04198 }
04199 else
04200 removeText(KTextEditor::Range(line, col-1, line, col+complement));
04201 }
04202 }
04203 else
04204 {
04205
04206 if (line >= 1)
04207 {
04208 KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
04209
04210
04211 if (!textLine)
04212 return;
04213
04214 if (config()->wordWrap() && textLine->endsWith(QLatin1String(" ")))
04215 {
04216
04217 removeText (KTextEditor::Range(line-1, textLine->length()-1, line, 0));
04218 }
04219 else
04220 removeText (KTextEditor::Range(line-1, textLine->length(), line, 0));
04221 }
04222 }
04223 }
04224
04225 void KateDocument::del( KateView *view, const KTextEditor::Cursor& c )
04226 {
04227 if ( !view->config()->persistentSelection() && view->selection() ) {
04228 view->removeSelectedText();
04229 return;
04230 }
04231
04232 if( c.column() < (int) m_buffer->plainLine(c.line())->length())
04233 {
04234 removeText(KTextEditor::Range(c, 1));
04235 }
04236 else if ( c.line() < lastLine() )
04237 {
04238 removeText(KTextEditor::Range(c.line(), c.column(), c.line()+1, 0));
04239 }
04240 }
04241
04242 void KateDocument::paste ( KateView* view, QClipboard::Mode mode )
04243 {
04244 QString s = QApplication::clipboard()->text(mode);
04245
04246 if (s.isEmpty())
04247 return;
04248
04249 int lines = s.count (QChar::fromAscii ('\n'));
04250
04251 m_undoDontMerge = true;
04252
04253 editStart (true, Kate::CutCopyPasteEdit);
04254
04255 if (!view->config()->persistentSelection() && view->selection() )
04256 view->removeSelectedText();
04257
04258 KTextEditor::Cursor pos = view->cursorPosition();
04259
04260 blockRemoveTrailingSpaces(true);
04261 insertText(pos, s, view->blockSelectionMode());
04262 blockRemoveTrailingSpaces(false);
04263
04264 for (int i = pos.line(); i < pos.line() + lines; ++i)
04265 removeTrailingSpace(i);
04266
04267 editEnd();
04268
04269
04270
04271
04272 if (view->blockSelectionMode())
04273 view->setCursorPositionInternal(pos + KTextEditor::Cursor(lines, 0));
04274
04275 if (config()->configFlags() & KateDocumentConfig::cfIndentPastedText)
04276 {
04277 KTextEditor::Range range = KTextEditor::Range(KTextEditor::Cursor(pos.line(), 0),
04278 KTextEditor::Cursor(pos.line() + lines, 0));
04279
04280 int start = view->selectionRange().start().line();
04281 const int end = view->selectionRange().end().line();
04282
04283 editStart();
04284
04285 blockRemoveTrailingSpaces(true);
04286 m_indenter.indent(view, range);
04287 blockRemoveTrailingSpaces(false);
04288
04289 for (; start <= end; ++start)
04290 removeTrailingSpace(start);
04291
04292 editEnd();
04293 }
04294
04295 if (!view->blockSelectionMode()) emit charactersSemiInteractivelyInserted (pos, s);
04296 m_undoDontMerge = true;
04297 }
04298
04299 void KateDocument::indent ( KateView *v, uint line, int change)
04300 {
04301
04302
04303 const bool hasSelection = v->selection();
04304 int start = v->selectionRange().start().line();
04305 const int end = v->selectionRange().end().line();
04306
04307 KTextEditor::Range range = hasSelection ? v->selectionRange() : KTextEditor::Range (KTextEditor::Cursor (line,0), KTextEditor::Cursor (line,0));
04308
04309 editStart();
04310 blockRemoveTrailingSpaces(true);
04311 m_indenter.changeIndent(v, range, change);
04312 blockRemoveTrailingSpaces(false);
04313
04314 if (hasSelection) {
04315 for (; start <= end; ++start)
04316 removeTrailingSpace(start);
04317 }
04318 editEnd();
04319 }
04320
04321 void KateDocument::align(KateView *view, const KTextEditor::Range &range)
04322 {
04323 editStart();
04324
04325 blockRemoveTrailingSpaces(true);
04326 m_indenter.indent(view, range);
04327 blockRemoveTrailingSpaces(false);
04328
04329 for (int start = range.start().line(); start <= range.end().line(); ++start) {
04330 removeTrailingSpace(start);
04331 }
04332
04333 editEnd();
04334 }
04335
04336
04337
04338
04339
04340 bool KateDocument::removeStringFromBeginning(int line, const QString &str)
04341 {
04342 KateTextLine::Ptr textline = m_buffer->plainLine(line);
04343
04344 KTextEditor::Cursor cursor (line, 0);
04345 bool there = textline->startsWith(str);
04346
04347 if (!there)
04348 {
04349 cursor.setColumn(textline->firstChar());
04350 there = textline->matchesAt(cursor.column(), str);
04351 }
04352
04353 if (there)
04354 {
04355
04356 removeText (KTextEditor::Range(cursor, str.length()));
04357 }
04358
04359 return there;
04360 }
04361
04362
04363
04364
04365
04366 bool KateDocument::removeStringFromEnd(int line, const QString &str)
04367 {
04368 KateTextLine::Ptr textline = m_buffer->plainLine(line);
04369
04370 KTextEditor::Cursor cursor (line, 0);
04371 bool there = textline->endsWith(str);
04372
04373 if (there)
04374 {
04375 cursor.setColumn(textline->length() - str.length());
04376 }
04377 else
04378 {
04379 cursor.setColumn(textline->lastChar() - str.length() + 1);
04380 there = textline->matchesAt(cursor.column(), str);
04381 }
04382
04383 if (there)
04384 {
04385
04386 removeText (KTextEditor::Range(cursor, str.length()));
04387 }
04388
04389 return there;
04390 }
04391
04392
04393
04394
04395 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
04396 {
04397 QString commentLineMark = highlight()->getCommentSingleLineStart(attrib);
04398 int pos = -1;
04399
04400 if (highlight()->getCommentSingleLinePosition(attrib) == KateHighlighting::CSLPosColumn0)
04401 {
04402 pos = 0;
04403 commentLineMark += ' ';
04404 } else {
04405 const KateTextLine::Ptr l = kateTextLine(line);
04406 pos = l->firstChar();
04407 }
04408
04409 if (pos >= 0)
04410 insertText (KTextEditor::Cursor(line, pos), commentLineMark);
04411 }
04412
04413
04414
04415
04416
04417 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
04418 {
04419 const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
04420 const QString longCommentMark = shortCommentMark + ' ';
04421
04422 editStart();
04423
04424
04425 bool removed = (removeStringFromBeginning(line, longCommentMark)
04426 || removeStringFromBeginning(line, shortCommentMark));
04427
04428 editEnd();
04429
04430 return removed;
04431 }
04432
04433
04434
04435
04436
04437 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
04438 {
04439 const QString startCommentMark = highlight()->getCommentStart( attrib ) + ' ';
04440 const QString stopCommentMark = ' ' + highlight()->getCommentEnd( attrib );
04441
04442 editStart();
04443
04444
04445 insertText (KTextEditor::Cursor(line, 0), startCommentMark);
04446
04447
04448 const int col = m_buffer->plainLine(line)->length();
04449
04450
04451 insertText (KTextEditor::Cursor(line, col), stopCommentMark);
04452
04453 editEnd();
04454 }
04455
04456
04457
04458
04459
04460 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
04461 {
04462 QString shortStartCommentMark = highlight()->getCommentStart( attrib );
04463 QString longStartCommentMark = shortStartCommentMark + ' ';
04464 QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
04465 QString longStopCommentMark = ' ' + shortStopCommentMark;
04466
04467 editStart();
04468
04469 #ifdef __GNUC__
04470 #warning "that's a bad idea, can lead to stray endings, FIXME"
04471 #endif
04472
04473 bool removedStart = (removeStringFromBeginning(line, longStartCommentMark)
04474 || removeStringFromBeginning(line, shortStartCommentMark));
04475
04476 bool removedStop = false;
04477 if (removedStart)
04478 {
04479
04480 removedStop = (removeStringFromEnd(line, longStopCommentMark)
04481 || removeStringFromEnd(line, shortStopCommentMark));
04482 }
04483
04484 editEnd();
04485
04486 return (removedStart || removedStop);
04487 }
04488
04489
04490
04491
04492
04493 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
04494 {
04495 const QString startComment = highlight()->getCommentStart( attrib );
04496 const QString endComment = highlight()->getCommentEnd( attrib );
04497
04498 KTextEditor::Range range = view->selectionRange();
04499
04500 if ((range.end().column() == 0) && (range.end().line() > 0))
04501 range.end().setPosition(range.end().line() - 1, lineLength(range.end().line() - 1));
04502
04503 editStart();
04504
04505 insertText (range.end(), endComment);
04506 insertText (range.start(), startComment);
04507
04508 editEnd ();
04509
04510 }
04511
04512
04513
04514
04515 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
04516 {
04517 const QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + ' ';
04518
04519 int sl = view->selectionRange().start().line();
04520 int el = view->selectionRange().end().line();
04521
04522
04523 if ((view->selectionRange().end().column() == 0) && (el > 0))
04524 {
04525 el--;
04526 }
04527
04528 editStart();
04529
04530
04531 for (int z = el; z >= sl; z--) {
04532
04533 addStartLineCommentToSingleLine(z, attrib );
04534 }
04535
04536 editEnd ();
04537
04538 }
04539
04540 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
04541 {
04542 for(; line < (int)m_buffer->count(); line++) {
04543 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
04544
04545 if (!textLine)
04546 break;
04547
04548 col = textLine->nextNonSpaceChar(col);
04549 if(col != -1)
04550 return true;
04551 col = 0;
04552 }
04553
04554 line = -1;
04555 col = -1;
04556 return false;
04557 }
04558
04559 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
04560 {
04561 while(true)
04562 {
04563 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
04564
04565 if (!textLine)
04566 break;
04567
04568 col = textLine->previousNonSpaceChar(col);
04569 if(col != -1) return true;
04570 if(line == 0) return false;
04571 --line;
04572 col = textLine->length();
04573 }
04574
04575 line = -1;
04576 col = -1;
04577 return false;
04578 }
04579
04580
04581
04582
04583
04584 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
04585 {
04586 const QString startComment = highlight()->getCommentStart( attrib );
04587 const QString endComment = highlight()->getCommentEnd( attrib );
04588
04589 int sl = qMax<int> (0, view->selectionRange().start().line());
04590 int el = qMin<int> (view->selectionRange().end().line(), lastLine());
04591 int sc = view->selectionRange().start().column();
04592 int ec = view->selectionRange().end().column();
04593
04594
04595 if (ec != 0) {
04596 --ec;
04597 } else if (el > 0) {
04598 --el;
04599 ec = m_buffer->plainLine(el)->length() - 1;
04600 }
04601
04602 const int startCommentLen = startComment.length();
04603 const int endCommentLen = endComment.length();
04604
04605
04606
04607 bool remove = nextNonSpaceCharPos(sl, sc)
04608 && m_buffer->plainLine(sl)->matchesAt(sc, startComment)
04609 && previousNonSpaceCharPos(el, ec)
04610 && ( (ec - endCommentLen + 1) >= 0 )
04611 && m_buffer->plainLine(el)->matchesAt(ec - endCommentLen + 1, endComment);
04612
04613 if (remove) {
04614 editStart();
04615
04616 removeText (KTextEditor::Range(el, ec - endCommentLen + 1, el, ec + 1));
04617 removeText (KTextEditor::Range(sl, sc, sl, sc + startCommentLen));
04618
04619 editEnd ();
04620
04621 }
04622
04623 return remove;
04624 }
04625
04626 bool KateDocument::removeStartStopCommentFromRegion(const KTextEditor::Cursor &start,const KTextEditor::Cursor &end,int attrib)
04627 {
04628 const QString startComment = highlight()->getCommentStart( attrib );
04629 const QString endComment = highlight()->getCommentEnd( attrib );
04630 const int startCommentLen = startComment.length();
04631 const int endCommentLen = endComment.length();
04632
04633 const bool remove = m_buffer->plainLine(start.line())->matchesAt(start.column(), startComment)
04634 && m_buffer->plainLine(end.line())->matchesAt(end.column() - endCommentLen , endComment);
04635 if (remove) {
04636 editStart();
04637 removeText(KTextEditor::Range(end.line(), end.column() - endCommentLen, end.line(), end.column()));
04638 removeText(KTextEditor::Range(start, startCommentLen));
04639 editEnd();
04640 }
04641 return remove;
04642 }
04643
04644
04645
04646
04647
04648 bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib )
04649 {
04650 const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
04651 const QString longCommentMark = shortCommentMark + ' ';
04652
04653 int sl = view->selectionRange().start().line();
04654 int el = view->selectionRange().end().line();
04655
04656 if ((view->selectionRange().end().column() == 0) && (el > 0))
04657 {
04658 el--;
04659 }
04660
04661 bool removed = false;
04662
04663 editStart();
04664
04665
04666 for (int z = el; z >= sl; z--)
04667 {
04668
04669 removed = (removeStringFromBeginning(z, longCommentMark)
04670 || removeStringFromBeginning(z, shortCommentMark)
04671 || removed);
04672 }
04673
04674 editEnd();
04675
04676
04677 return removed;
04678 }
04679
04680
04681
04682
04683
04684 void KateDocument::comment( KateView *v, uint line,uint column, int change)
04685 {
04686
04687
04688
04689
04690 bool hassel = v->selection();
04691 int startAttrib, endAttrib;
04692 if ( hassel )
04693 {
04694 KateTextLine::Ptr ln = kateTextLine( v->selectionRange().start().line() );
04695 int l = v->selectionRange().start().line(), c = v->selectionRange().start().column();
04696 startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
04697
04698 ln = kateTextLine( v->selectionRange().end().line() );
04699 l = v->selectionRange().end().line(), c = v->selectionRange().end().column();
04700 endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
04701 }
04702 else
04703 {
04704 KateTextLine::Ptr ln = kateTextLine( line );
04705 if ( ln->length() )
04706 {
04707 startAttrib = ln->attribute( ln->firstChar() );
04708 endAttrib = ln->attribute( ln->lastChar() );
04709 }
04710 else
04711 {
04712 int l = line, c = 0;
04713 if ( nextNonSpaceCharPos( l, c ) || previousNonSpaceCharPos( l, c ) )
04714 startAttrib = endAttrib = kateTextLine( l )->attribute( c );
04715 else
04716 startAttrib = endAttrib = 0;
04717 }
04718 }
04719
04720 if ( ! highlight()->canComment( startAttrib, endAttrib ) )
04721 {
04722 kDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!";
04723 return;
04724 }
04725
04726 bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
04727 bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
04728 && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
04729
04730 bool removed = false;
04731
04732 if (change > 0)
04733 {
04734 if ( !hassel )
04735 {
04736 if ( hasStartLineCommentMark )
04737 addStartLineCommentToSingleLine( line, startAttrib );
04738 else if ( hasStartStopCommentMark )
04739 addStartStopCommentToSingleLine( line, startAttrib );
04740 }
04741 else
04742 {
04743
04744
04745
04746
04747
04748
04749
04750 if ( hasStartStopCommentMark &&
04751 ( !hasStartLineCommentMark || (
04752 ( v->selectionRange().start().column() > m_buffer->plainLine( v->selectionRange().start().line() )->firstChar() ) ||
04753 ( v->selectionRange().end().column() < ((int)m_buffer->plainLine( v->selectionRange().end().line() )->length()) )
04754 ) ) )
04755 addStartStopCommentToSelection( v, startAttrib );
04756 else if ( hasStartLineCommentMark )
04757 addStartLineCommentToSelection( v, startAttrib );
04758 }
04759 }
04760 else
04761 {
04762 if ( !hassel )
04763 {
04764 removed = ( hasStartLineCommentMark
04765 && removeStartLineCommentFromSingleLine( line, startAttrib ) )
04766 || ( hasStartStopCommentMark
04767 && removeStartStopCommentFromSingleLine( line, startAttrib ) );
04768 if ((!removed) && foldingTree()) {
04769 kDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)";
04770 int commentRegion=(highlight()->commentRegion(startAttrib));
04771 if (commentRegion){
04772 KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
04773 if (n) {
04774 KTextEditor::Cursor start,end;
04775 if ((n->nodeType()==(int)commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
04776 kDebug(13020)<<"Enclosing region found:"<<start.column()<<"/"<<start.line()<<"-"<<end.column()<<"/"<<end.line();
04777 removeStartStopCommentFromRegion(start,end,startAttrib);
04778 } else {
04779 kDebug(13020)<<"Enclosing region found, but not valid";
04780 kDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion;
04781 }
04782
04783 } else kDebug(13020)<<"No enclosing region found";
04784 } else kDebug(13020)<<"No comment region specified for current hl";
04785 }
04786 }
04787 else
04788 {
04789
04790 removed = ( hasStartLineCommentMark
04791 && removeStartLineCommentFromSelection( v, startAttrib ) )
04792 || ( hasStartStopCommentMark
04793 && removeStartStopCommentFromSelection( v, startAttrib ) );
04794 }
04795 }
04796 }
04797
04798 void KateDocument::transform( KateView *v, const KTextEditor::Cursor &c,
04799 KateDocument::TextTransform t )
04800 {
04801 editStart();
04802 KTextEditor::Cursor cursor = c;
04803
04804 if ( v->selection() )
04805 {
04806
04807 KTextEditor::Range selection = v->selectionRange();
04808
04809 KTextEditor::Range range(selection.start(), 0);
04810 while ( range.start().line() <= selection.end().line() )
04811 {
04812 int start = 0;
04813 int end = lineLength( range.start().line() );
04814
04815 if (range.start().line() == selection.start().line() || v->blockSelectionMode())
04816 start = selection.start().column();
04817
04818 if (range.start().line() == selection.end().line() || v->blockSelectionMode())
04819 end = selection.end().column();
04820
04821 if ( start > end )
04822 {
04823 int swapCol = start;
04824 start = end;
04825 end = swapCol;
04826 }
04827 range.start().setColumn( start );
04828 range.end().setColumn( end );
04829
04830 QString s = text( range );
04831 QString old = s;
04832
04833 if ( t == Uppercase )
04834 s = s.toUpper();
04835 else if ( t == Lowercase )
04836 s = s.toLower();
04837 else
04838 {
04839 KateTextLine::Ptr l = m_buffer->plainLine( range.start().line() );
04840 int p ( 0 );
04841 while( p < s.length() )
04842 {
04843
04844
04845
04846
04847 if ( ( ! range.start().column() && ! p ) ||
04848 ( ( range.start().line() == selection.start().line() || v->blockSelectionMode() ) &&
04849 ! p && ! highlight()->isInWord( l->at( range.start().column() - 1 )) ) ||
04850 ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
04851 )
04852 s[p] = s.at(p).toUpper();
04853 p++;
04854 }
04855 }
04856
04857 if ( s != old )
04858 {
04859 removeText( range );
04860 insertText( range.start(), s );
04861 }
04862
04863 range.setBothLines(range.start().line() + 1);
04864 }
04865
04866
04867 v->setSelection( selection );
04868
04869 } else {
04870 QString old = text( KTextEditor::Range(cursor, 1) );
04871 QString s;
04872 switch ( t ) {
04873 case Uppercase:
04874 s = old.toUpper();
04875 break;
04876 case Lowercase:
04877 s = old.toLower();
04878 break;
04879 case Capitalize:
04880 {
04881 KateTextLine::Ptr l = m_buffer->plainLine( cursor.line() );
04882 while ( cursor.column() > 0 && highlight()->isInWord( l->at( cursor.column() - 1 ), l->attribute( cursor.column() - 1 ) ) )
04883 cursor.setColumn(cursor.column() - 1);
04884 old = text( KTextEditor::Range(cursor, 1) );
04885 s = old.toUpper();
04886 }
04887 break;
04888 default:
04889 break;
04890 }
04891 if ( s != old )
04892 {
04893 removeText( KTextEditor::Range(cursor, 1) );
04894 insertText( cursor, s );
04895 }
04896 }
04897
04898 editEnd();
04899
04900 v->setCursorPosition( c );
04901 }
04902
04903 void KateDocument::joinLines( uint first, uint last )
04904 {
04905
04906 editStart();
04907 int line( first );
04908 while ( first < last )
04909 {
04910
04911
04912
04913
04914
04915 KateTextLine::Ptr l = kateTextLine( line );
04916 KateTextLine::Ptr tl = kateTextLine( line + 1 );
04917
04918 if ( !l || !tl )
04919 {
04920 editEnd();
04921 return;
04922 }
04923
04924 int pos = tl->firstChar();
04925 if ( pos >= 0 )
04926 {
04927 if (pos != 0)
04928 editRemoveText( line + 1, 0, pos );
04929 if ( !( l->length() == 0 || l->at( l->length() - 1 ).isSpace() ) )
04930 editInsertText( line + 1, 0, " " );
04931 }
04932 else
04933 {
04934
04935 editRemoveText( line + 1, 0, tl->length() );
04936 }
04937
04938 editUnWrapLine( line );
04939 first++;
04940 }
04941 editEnd();
04942 }
04943
04944 QString KateDocument::getWord( const KTextEditor::Cursor& cursor )
04945 {
04946 int start, end, len;
04947
04948 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04949 len = textLine->length();
04950 start = end = cursor.column();
04951 if (start > len)
04952 return QString("");
04953
04954 while (start > 0 && highlight()->isInWord(textLine->at(start - 1), textLine->attribute(start - 1))) start--;
04955 while (end < len && highlight()->isInWord(textLine->at(end), textLine->attribute(end))) end++;
04956 len = end - start;
04957 return textLine->string().mid(start, len);
04958 }
04959
04960 void KateDocument::tagLines(int start, int end)
04961 {
04962 foreach(KateView *view,m_views)
04963 view->tagLines (start, end, true);
04964 }
04965
04966 void KateDocument::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end)
04967 {
04968
04969
04970
04971
04972
04973
04974
04975 foreach (KateView* view, m_views)
04976 view->tagLines(start, end, true);
04977 }
04978
04979 void KateDocument::repaintViews(bool paintOnlyDirty)
04980 {
04981 foreach(KateView *view,m_views)
04982 view->repaintText(paintOnlyDirty);
04983 }
04984
04985 void KateDocument::tagAll()
04986 {
04987 foreach(KateView *view,m_views)
04988 {
04989 view->tagAll();
04990 view->updateView (true);
04991 }
04992 }
04993
04994 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04995 inline bool isEndBracket ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04996 inline bool isBracket ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04997
04998
04999
05000
05001
05002
05003
05004
05005 void KateDocument::newBracketMark( const KTextEditor::Cursor& cursor, KTextEditor::Range& bm, int maxLines )
05006 {
05007 bm.start() = cursor;
05008
05009 if( findMatchingBracket( bm, maxLines ) )
05010 return;
05011
05012 bm = KTextEditor::Range::invalid();
05013
05014
05015
05016
05017
05018 }
05019
05020 bool KateDocument::findMatchingBracket( KTextEditor::Range& range, int maxLines )
05021 {
05022 KateTextLine::Ptr textLine = m_buffer->plainLine( range.start().line() );
05023 if( !textLine )
05024 return false;
05025
05026 QChar right = textLine->at( range.start().column() );
05027 QChar left = textLine->at( range.start().column() - 1 );
05028 QChar bracket;
05029
05030 if ( config()->configFlags() & KateDocumentConfig::cfOvr ) {
05031 if( isBracket( right ) ) {
05032 bracket = right;
05033 } else {
05034 return false;
05035 }
05036 } else if ( isBracket( left ) ) {
05037 range.start().setColumn(range.start().column() - 1);
05038 bracket = left;
05039 } else if ( isBracket( right ) ) {
05040 bracket = right;
05041 } else {
05042 return false;
05043 }
05044
05045 QChar opposite;
05046
05047 switch( bracket.toAscii() ) {
05048 case '{': opposite = '}'; break;
05049 case '}': opposite = '{'; break;
05050 case '[': opposite = ']'; break;
05051 case ']': opposite = '['; break;
05052 case '(': opposite = ')'; break;
05053 case ')': opposite = '('; break;
05054 default: return false;
05055 }
05056
05057 bool forward = isStartBracket( bracket );
05058 uint nesting = 0;
05059
05060 int minLine = qMax( range.start().line() - maxLines, 0 );
05061 int maxLine = qMin( range.start().line() + maxLines, documentEnd().line() );
05062
05063 range.end() = range.start();
05064 KateDocCursor cursor(range.start(), this);
05065 uchar validAttr = cursor.currentAttrib();
05066
05067 while( cursor.line() >= minLine && cursor.line() <= maxLine ) {
05068
05069 if( forward )
05070 cursor.moveForward(1);
05071 else
05072 cursor.moveBackward(1);
05073
05074 if( !cursor.validPosition() )
05075 return false;
05076
05077 if( cursor.currentAttrib() == validAttr )
05078 {
05079
05080 QChar c = cursor.currentChar();
05081 if( c == bracket ) {
05082 nesting++;
05083 } else if( c == opposite ) {
05084 if( nesting == 0 ) {
05085 if( forward )
05086 range.end() = cursor;
05087 else
05088 range.start() = cursor;
05089 return true;
05090 }
05091 nesting--;
05092 }
05093 }
05094
05095 if(cursor == KTextEditor::Cursor(0,0) || cursor >= documentEnd())
05096 return false;
05097 }
05098
05099 return false;
05100 }
05101
05102 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
05103 {
05104 KParts::ReadWritePart::guiActivateEvent( ev );
05105
05106
05107 }
05108
05109 void KateDocument::setDocName (QString name )
05110 {
05111 if ( name == m_docName )
05112 return;
05113
05114 if ( !name.isEmpty() )
05115 {
05116
05117 m_docName = name;
05118 emit documentNameChanged (this);
05119 return;
05120 }
05121
05122
05123 if ( ! url().isEmpty()
05124 && (m_docName == url().fileName() || m_docName.startsWith (url().fileName() + " (") ) ) return;
05125
05126 int count = -1;
05127
05128 for (int z=0; z < KateGlobal::self()->kateDocuments().size(); ++z)
05129 {
05130 KateDocument *doc = (KateGlobal::self()->kateDocuments())[z];
05131
05132 if ( (doc != this) && (doc->url().fileName() == url().fileName()) )
05133 if ( doc->m_docNameNumber > count )
05134 count = doc->m_docNameNumber;
05135 }
05136
05137 m_docNameNumber = count + 1;
05138
05139 m_docName = url().fileName();
05140
05141 if (m_docName.isEmpty())
05142 m_docName = i18n ("Untitled");
05143
05144 if (m_docNameNumber > 0)
05145 m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
05146
05147 emit documentNameChanged (this);
05148 }
05149
05150 void KateDocument::slotModifiedOnDisk( KTextEditor::View * )
05151 {
05152 if ( m_isasking < 0 )
05153 {
05154 m_isasking = 0;
05155 return;
05156 }
05157
05158 if ( !s_fileChangedDialogsActivated || m_isasking )
05159 return;
05160
05161 if (m_modOnHd && !url().isEmpty())
05162 {
05163 m_isasking = 1;
05164
05165 QWidget *parentWidget(dialogParent());
05166
05167 KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), parentWidget );
05168 switch ( p.exec() )
05169 {
05170 case KateModOnHdPrompt::Save:
05171 {
05172 m_modOnHd = false;
05173 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(),
05174 url().url(),QString(),parentWidget,i18n("Save File"));
05175
05176 kDebug(13020)<<"got "<<res.URLs.count()<<" URLs";
05177 if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first(), parentWidget ) )
05178 {
05179 setEncoding( res.encoding );
05180
05181 if( ! saveAs( res.URLs.first() ) )
05182 {
05183 KMessageBox::error( parentWidget, i18n("Save failed") );
05184 m_modOnHd = true;
05185 }
05186 else
05187 emit modifiedOnDisk( this, false, OnDiskUnmodified );
05188 }
05189 else
05190 {
05191 m_modOnHd = true;
05192 }
05193
05194 m_isasking = 0;
05195 break;
05196 }
05197
05198 case KateModOnHdPrompt::Reload:
05199 m_modOnHd = false;
05200 emit modifiedOnDisk( this, false, OnDiskUnmodified );
05201 documentReload();
05202 m_isasking = 0;
05203 break;
05204
05205 case KateModOnHdPrompt::Ignore:
05206 m_modOnHd = false;
05207 emit modifiedOnDisk( this, false, OnDiskUnmodified );
05208 m_isasking = 0;
05209 break;
05210
05211 case KateModOnHdPrompt::Overwrite:
05212 m_modOnHd = false;
05213 emit modifiedOnDisk( this, false, OnDiskUnmodified );
05214 m_isasking = 0;
05215 save();
05216 break;
05217
05218 default:
05219 m_isasking = -1;
05220 }
05221 }
05222 }
05223
05224 void KateDocument::setModifiedOnDisk( ModifiedOnDiskReason reason )
05225 {
05226 m_modOnHdReason = reason;
05227 m_modOnHd = (reason != OnDiskUnmodified);
05228 emit modifiedOnDisk( this, (reason != OnDiskUnmodified), reason );
05229 }
05230
05231 class KateDocumentTmpMark
05232 {
05233 public:
05234 QString line;
05235 KTextEditor::Mark mark;
05236 };
05237
05238 void KateDocument::setModifiedOnDiskWarning (bool on)
05239 {
05240 s_fileChangedDialogsActivated = on;
05241 }
05242
05243 bool KateDocument::documentReload()
05244 {
05245 if ( !url().isEmpty() )
05246 {
05247 if (m_modOnHd && s_fileChangedDialogsActivated)
05248 {
05249 QWidget *parentWidget(dialogParent());
05250
05251 int i = KMessageBox::warningYesNoCancel
05252 (parentWidget, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
05253 i18n("File Was Changed on Disk"), KGuiItem(i18n("&Reload File")), KGuiItem(i18n("&Ignore Changes")));
05254
05255 if ( i != KMessageBox::Yes)
05256 {
05257 if (i == KMessageBox::No)
05258 {
05259 m_modOnHd = false;
05260 m_modOnHdReason = OnDiskUnmodified;
05261 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05262 }
05263
05264 return false;
05265 }
05266 }
05267
05268 emit aboutToReload(this);
05269
05270 if (clearOnDocumentReload())
05271 m_smartManager->clear(false);
05272
05273 QList<KateDocumentTmpMark> tmp;
05274
05275 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
05276 {
05277 KateDocumentTmpMark m;
05278
05279 m.line = line (i.value()->line);
05280 m.mark = *i.value();
05281
05282 tmp.append (m);
05283 }
05284
05285 QString oldMode = mode ();
05286 bool byUser = m_fileTypeSetByUser;
05287
05288 m_storedVariables.clear();
05289
05290
05291 QVector<KTextEditor::Cursor> cursorPositions;
05292 cursorPositions.reserve(m_views.size());
05293 foreach (KateView *v, m_views)
05294 cursorPositions.append( v->cursorPosition() );
05295
05296 m_reloading = true;
05297 KateDocument::openUrl( url() );
05298 m_reloading = false;
05299
05300
05301
05302 QLinkedList<KateView*>::iterator it = m_views.begin();
05303 for(int i = 0; i < m_views.size(); ++i, ++it) {
05304 (*it)->setCursorPositionInternal( cursorPositions[i], m_config->tabWidth(), false );
05305 if ((*it)->isVisible()) {
05306 (*it)->repaintText(false);
05307 }
05308 }
05309
05310 for (int z=0; z < tmp.size(); z++)
05311 {
05312 if (z < (int)lines())
05313 {
05314 if (line(tmp[z].mark.line) == tmp[z].line)
05315 setMark (tmp[z].mark.line, tmp[z].mark.type);
05316 }
05317 }
05318
05319 if (byUser)
05320 setMode (oldMode);
05321
05322 return true;
05323 }
05324
05325 return false;
05326 }
05327
05328 bool KateDocument::documentSave()
05329 {
05330 if( !url().isValid() || !isReadWrite() )
05331 return documentSaveAs();
05332
05333 return save();
05334 }
05335
05336 bool KateDocument::documentSaveAs()
05337 {
05338 QWidget *parentWidget(dialogParent());
05339
05340 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(),
05341 url().url(),QString(),parentWidget,i18n("Save File"));
05342
05343 if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) )
05344 return false;
05345
05346 setEncoding( res.encoding );
05347
05348 return saveAs( res.URLs.first() );
05349 }
05350
05351 void KateDocument::setWordWrap (bool on)
05352 {
05353 config()->setWordWrap (on);
05354 }
05355
05356 bool KateDocument::wordWrap () const
05357 {
05358 return config()->wordWrap ();
05359 }
05360
05361 void KateDocument::setWordWrapAt (uint col)
05362 {
05363 config()->setWordWrapAt (col);
05364 }
05365
05366 unsigned int KateDocument::wordWrapAt () const
05367 {
05368 return config()->wordWrapAt ();
05369 }
05370
05371 void KateDocument::setPageUpDownMovesCursor (bool on)
05372 {
05373 config()->setPageUpDownMovesCursor (on);
05374 }
05375
05376 bool KateDocument::pageUpDownMovesCursor () const
05377 {
05378 return config()->pageUpDownMovesCursor ();
05379 }
05380
05381 void KateDocument::dumpRegionTree()
05382 {
05383 m_buffer->foldingTree()->debugDump();
05384 }
05385
05386
05387 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
05388 {
05389 m_buffer->lineInfo(info,line);
05390 }
05391
05392 KateCodeFoldingTree *KateDocument::foldingTree ()
05393 {
05394 return m_buffer->foldingTree();
05395 }
05396
05397 bool KateDocument::setEncoding (const QString &e)
05398 {
05399 return m_config->setEncoding(e);
05400 }
05401
05402 const QString &KateDocument::encoding() const
05403 {
05404 return m_config->encoding();
05405 }
05406
05407 void KateDocument::setProberTypeForEncodingAutoDetection (KEncodingProber::ProberType proberType)
05408 {
05409 m_config->setEncodingProberType(proberType);
05410 }
05411
05412 KEncodingProber::ProberType KateDocument::proberTypeForEncodingAutoDetection() const
05413 {
05414 return m_config->encodingProberType();
05415 }
05416
05417 void KateDocument::updateConfig ()
05418 {
05419 emit undoChanged ();
05420 tagAll();
05421
05422 foreach (KateView * view,m_views)
05423 {
05424 view->updateDocumentConfig ();
05425 }
05426
05427
05428 m_indenter.setMode (m_config->indentationMode());
05429 m_indenter.updateConfig();
05430
05431 m_buffer->setTabWidth (config()->tabWidth());
05432 }
05433
05434
05435
05436
05437
05438
05439
05440
05441 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
05442 QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)");
05443 QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)");
05444 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
05445
05446 void KateDocument::readVariables(bool onlyViewAndRenderer)
05447 {
05448 if (!onlyViewAndRenderer)
05449 m_config->configStart();
05450
05451
05452 KateView *v;
05453 foreach (v,m_views)
05454 {
05455 v->config()->configStart();
05456 v->renderer()->config()->configStart();
05457 }
05458
05459 for (int i=0; i < qMin( 9, lines() ); ++i )
05460 {
05461 readVariableLine( line( i ), onlyViewAndRenderer );
05462 }
05463 if ( lines() > 10 )
05464 {
05465 for ( int i = qMax( 10, lines() - 10); i < lines(); i++ )
05466 {
05467 readVariableLine( line( i ), onlyViewAndRenderer );
05468 }
05469 }
05470
05471 if (!onlyViewAndRenderer)
05472 m_config->configEnd();
05473
05474 foreach (v,m_views)
05475 {
05476 v->config()->configEnd();
05477 v->renderer()->config()->configEnd();
05478 }
05479 }
05480
05481 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
05482 {
05483
05484
05485 if (!t.contains("kate"))
05486 return;
05487
05488
05489 QString s;
05490
05491
05492 if ( kvLine.indexIn( t ) > -1 )
05493 {
05494 s = kvLine.cap(1);
05495
05496 kDebug (13020) << "normal variable line kate: matched: " << s;
05497 }
05498 else if (kvLineWildcard.indexIn( t ) > -1)
05499 {
05500 QStringList wildcards (kvLineWildcard.cap(1).split (';', QString::SkipEmptyParts));
05501 QString nameOfFile = url().fileName();
05502
05503 bool found = false;
05504 for (int i = 0; !found && i < wildcards.size(); ++i)
05505 {
05506 QRegExp wildcard (wildcards[i], Qt::CaseSensitive, QRegExp::Wildcard);
05507
05508 found = wildcard.exactMatch (nameOfFile);
05509 }
05510
05511
05512 if (!found)
05513 return;
05514
05515 s = kvLineWildcard.cap(2);
05516
05517 kDebug (13020) << "guarded variable line kate-wildcard: matched: " << s;
05518 }
05519 else if (kvLineMime.indexIn( t ) > -1)
05520 {
05521 QStringList types (kvLineMime.cap(1).split (';', QString::SkipEmptyParts));
05522
05523
05524 if (!types.contains (mimeType ()))
05525 return;
05526
05527 s = kvLineMime.cap(2);
05528
05529 kDebug (13020) << "guarded variable line kate-mimetype: matched: " << s;
05530 }
05531 else
05532 {
05533 return;
05534 }
05535
05536 QStringList vvl;
05537 vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
05538 << "line-numbers" << "icon-border" << "folding-markers"
05539 << "bookmark-sorting" << "auto-center-lines"
05540 << "icon-bar-color"
05541
05542 << "background-color" << "selection-color"
05543 << "current-line-color" << "bracket-highlight-color"
05544 << "word-wrap-marker-color"
05545 << "font" << "font-size" << "scheme";
05546 int spaceIndent = -1;
05547 bool replaceTabsSet = false;
05548 int p( 0 );
05549
05550 QString var, val;
05551 while ( (p = kvVar.indexIn( s, p )) > -1 )
05552 {
05553 p += kvVar.matchedLength();
05554 var = kvVar.cap( 1 );
05555 val = kvVar.cap( 2 ).trimmed();
05556 bool state;
05557 int n;
05558
05559
05560 if (onlyViewAndRenderer)
05561 {
05562 if ( vvl.contains( var ) )
05563 setViewVariable( var, val );
05564 }
05565 else
05566 {
05567
05568 if ( var == "word-wrap" && checkBoolValue( val, &state ) )
05569 setWordWrap( state );
05570
05571
05572 else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
05573 m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
05574 else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
05575 {
05576 m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
05577 replaceTabsSet = true;
05578 }
05579 else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
05580 m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
05581 else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
05582 m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
05583 else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
05584 m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
05585 else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
05586 m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
05587 else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
05588 m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
05589 else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
05590 m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
05591 else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
05592 m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
05593 else if ( var == "show-trailing-spaces" && checkBoolValue( val, &state ) )
05594 m_config->setConfigFlags( KateDocumentConfig::cfShowSpaces, state );
05595 else if ( var == "space-indent" && checkBoolValue( val, &state ) )
05596 {
05597
05598 spaceIndent = state;
05599 }
05600 else if ( var == "smart-home" && checkBoolValue( val, &state ) )
05601 m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
05602 else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
05603 m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
05604
05605
05606 else if ( var == "tab-width" && checkIntValue( val, &n ) )
05607 m_config->setTabWidth( n );
05608 else if ( var == "indent-width" && checkIntValue( val, &n ) )
05609 m_config->setIndentationWidth( n );
05610 else if ( var == "indent-mode" )
05611 {
05612 m_config->setIndentationMode( val );
05613 }
05614 else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 )
05615 m_config->setWordWrapAt( n );
05616
05617
05618 else if ( var == "eol" || var == "end-of-line" )
05619 {
05620 QStringList l;
05621 l << "unix" << "dos" << "mac";
05622 if ( (n = l.indexOf( val.toLower() )) != -1 )
05623 m_config->setEol( n );
05624 }
05625 else if ( var == "encoding" )
05626 m_config->setEncoding( val );
05627 else if (var == "presave-postdialog")
05628 setPreSavePostDialogFilterChecks(val.split(','));
05629 else if (var == "postsave")
05630 setPostSaveFilterChecks(val.split(','));
05631 else if (var == "postload")
05632 setPostLoadFilterChecks(val.split(','));
05633 else if ( var == "syntax" || var == "hl" )
05634 {
05635 setHighlightingMode( val );
05636 }
05637 else if ( var == "mode" )
05638 {
05639 setMode( val );
05640 }
05641
05642
05643 else if ( vvl.contains( var ) )
05644 setViewVariable( var, val );
05645 else
05646 {
05647 m_storedVariables.insert( var, val );
05648 emit variableChanged( this, var, val );
05649 }
05650 }
05651 }
05652
05653
05654
05655
05656
05657
05658
05659
05660 if ( !replaceTabsSet && spaceIndent >= 0 )
05661 {
05662 m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, spaceIndent > 0 );
05663 }
05664 }
05665
05666 void KateDocument::setViewVariable( QString var, QString val )
05667 {
05668 KateView *v;
05669 bool state;
05670 int n;
05671 QColor c;
05672 foreach (v,m_views)
05673 {
05674 if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
05675 v->config()->setDynWordWrap( state );
05676 else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
05677 v->config()->setPersistentSelection( state );
05678 else if ( var == "block-selection" && checkBoolValue( val, &state ) )
05679 v->setBlockSelectionMode( state );
05680
05681 else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
05682 v->config()->setLineNumbers( state );
05683 else if (var == "icon-border" && checkBoolValue( val, &state ) )
05684 v->config()->setIconBar( state );
05685 else if (var == "folding-markers" && checkBoolValue( val, &state ) )
05686 v->config()->setFoldingBar( state );
05687 else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
05688 v->config()->setAutoCenterLines( n );
05689 else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
05690 v->renderer()->config()->setIconBarColor( c );
05691
05692 else if ( var == "background-color" && checkColorValue( val, c ) )
05693 v->renderer()->config()->setBackgroundColor( c );
05694 else if ( var == "selection-color" && checkColorValue( val, c ) )
05695 v->renderer()->config()->setSelectionColor( c );
05696 else if ( var == "current-line-color" && checkColorValue( val, c ) )
05697 v->renderer()->config()->setHighlightedLineColor( c );
05698 else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
05699 v->renderer()->config()->setHighlightedBracketColor( c );
05700 else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
05701 v->renderer()->config()->setWordWrapMarkerColor( c );
05702 else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
05703 {
05704 QFont _f( v->renderer()->config()->font() );
05705
05706 if ( var == "font" )
05707 {
05708 _f.setFamily( val );
05709 _f.setFixedPitch( QFont( val ).fixedPitch() );
05710 }
05711 else
05712 _f.setPointSize( n );
05713
05714 v->renderer()->config()->setFont( _f );
05715 }
05716 else if ( var == "scheme" )
05717 {
05718 v->renderer()->config()->setSchema( val );
05719 }
05720 }
05721 }
05722
05723 bool KateDocument::checkBoolValue( QString val, bool *result )
05724 {
05725 val = val.trimmed().toLower();
05726 QStringList l;
05727 l << "1" << "on" << "true";
05728 if ( l.contains( val ) )
05729 {
05730 *result = true;
05731 return true;
05732 }
05733 l.clear();
05734 l << "0" << "off" << "false";
05735 if ( l.contains( val ) )
05736 {
05737 *result = false;
05738 return true;
05739 }
05740 return false;
05741 }
05742
05743 bool KateDocument::checkIntValue( QString val, int *result )
05744 {
05745 bool ret( false );
05746 *result = val.toInt( &ret );
05747 return ret;
05748 }
05749
05750 bool KateDocument::checkColorValue( QString val, QColor &c )
05751 {
05752 c.setNamedColor( val );
05753 return c.isValid();
05754 }
05755
05756
05757 QString KateDocument::variable( const QString &name ) const
05758 {
05759 if ( m_storedVariables.contains( name ) )
05760 return m_storedVariables[ name ];
05761
05762 return "";
05763 }
05764
05765
05766
05767 void KateDocument::slotModOnHdDirty (const QString &path)
05768 {
05769 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskModified))
05770 {
05771
05772 if ( ! m_digest.isEmpty() )
05773 {
05774 QByteArray tmp;
05775 if ( createDigest( tmp ) && tmp == m_digest )
05776 return;
05777 }
05778
05779 m_modOnHd = true;
05780 m_modOnHdReason = OnDiskModified;
05781
05782
05783 if (m_isasking == -1)
05784 m_isasking = false;
05785
05786 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05787 }
05788 }
05789
05790 void KateDocument::slotModOnHdCreated (const QString &path)
05791 {
05792 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskCreated))
05793 {
05794 m_modOnHd = true;
05795 m_modOnHdReason = OnDiskCreated;
05796
05797
05798 if (m_isasking == -1)
05799 m_isasking = false;
05800
05801 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05802 }
05803 }
05804
05805 void KateDocument::slotModOnHdDeleted (const QString &path)
05806 {
05807 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskDeleted))
05808 {
05809 m_modOnHd = true;
05810 m_modOnHdReason = OnDiskDeleted;
05811
05812
05813 if (m_isasking == -1)
05814 m_isasking = false;
05815
05816 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05817 }
05818 }
05819
05820 bool KateDocument::createDigest( QByteArray &result )
05821 {
05822 bool ret = false;
05823 result = "";
05824 if ( url().isLocalFile() )
05825 {
05826 QFile f ( url().path() );
05827 if ( f.open( QIODevice::ReadOnly) )
05828 {
05829 KMD5 md5;
05830 ret = md5.update( f );
05831 md5.hexDigest( result );
05832 f.close();
05833 ret = true;
05834 }
05835 }
05836 return ret;
05837 }
05838
05839 QString KateDocument::reasonedMOHString() const
05840 {
05841
05842 QString str = KStringHandler::csqueeze(url().pathOrUrl());
05843
05844 switch( m_modOnHdReason )
05845 {
05846 case OnDiskModified:
05847 return i18n("The file '%1' was modified by another program.", str );
05848 break;
05849 case OnDiskCreated:
05850 return i18n("The file '%1' was created by another program.", str );
05851 break;
05852 case OnDiskDeleted:
05853 return i18n("The file '%1' was deleted by another program.", str );
05854 break;
05855 default:
05856 return QString();
05857 }
05858 return QString();
05859 }
05860
05861 void KateDocument::removeTrailingSpace(int line)
05862 {
05863
05864 if (m_blockRemoveTrailingSpaces
05865 || !(config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn))
05866 return;
05867
05868 KateTextLine::Ptr ln = plainKateTextLine(line);
05869
05870 if (!ln || ln->length() == 0)
05871 return;
05872
05873 if (line == activeView()->cursorPosition().line()
05874 && activeView()->cursorPosition().column() >= qMax(0, ln->lastChar()))
05875 return;
05876
05877 const int p = ln->lastChar() + 1;
05878 const int l = ln->length() - p;
05879 if (l > 0) {
05880 m_blockRemoveTrailingSpaces = true;
05881 editRemoveText(line, p, l);
05882 m_blockRemoveTrailingSpaces = false;
05883 }
05884 }
05885
05886 void KateDocument::updateFileType (const QString &newType, bool user)
05887 {
05888 if (user || !m_fileTypeSetByUser)
05889 {
05890 if (!newType.isEmpty())
05891 {
05892 m_fileType = newType;
05893
05894 m_config->configStart();
05895
05896 if (!hlSetByUser && !KateGlobal::self()->modeManager()->fileType(newType).hl.isEmpty())
05897 {
05898 int hl (KateHlManager::self()->nameFind (KateGlobal::self()->modeManager()->fileType(newType).hl));
05899
05900 if (hl >= 0)
05901 m_buffer->setHighlight(hl);
05902 }
05903
05904
05905
05906 KateView *v;
05907 foreach (v,m_views)
05908 {
05909 v->config()->configStart();
05910 v->renderer()->config()->configStart();
05911 }
05912
05913 readVariableLine( KateGlobal::self()->modeManager()->fileType(newType).varLine );
05914
05915 m_config->configEnd();
05916 foreach (v,m_views)
05917 {
05918 v->config()->configEnd();
05919 v->renderer()->config()->configEnd();
05920 }
05921 }
05922 }
05923
05924
05925 emit modeChanged (this);
05926 }
05927
05928 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
05929 *handled=true;
05930 *abortClosing=true;
05931 if (this->url().isEmpty())
05932 {
05933 QWidget *parentWidget(dialogParent());
05934
05935 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(),
05936 QString(),QString(),parentWidget,i18n("Save File"));
05937
05938 if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) ) {
05939 *abortClosing=true;
05940 return;
05941 }
05942 setEncoding( res.encoding );
05943 saveAs( res.URLs.first() );
05944 *abortClosing=false;
05945 }
05946 else
05947 {
05948 save();
05949 *abortClosing=false;
05950 }
05951
05952 }
05953
05954 bool KateDocument::checkOverwrite( KUrl u, QWidget *parent )
05955 {
05956 if( !u.isLocalFile() )
05957 return true;
05958
05959 QFileInfo info( u.path() );
05960 if( !info.exists() )
05961 return true;
05962
05963 return KMessageBox::Cancel != KMessageBox::warningContinueCancel( parent,
05964 i18n( "A file named \"%1\" already exists. "
05965 "Are you sure you want to overwrite it?" , info.fileName() ),
05966 i18n( "Overwrite File?" ),
05967 KGuiItem(i18n( "&Overwrite" )) );
05968 }
05969
05970
05971
05972 bool KateDocument::insertTemplateTextImplementation ( const KTextEditor::Cursor &c, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
05973 return (new KateTemplateHandler(this,c,templateString,initialValues))->initOk();
05974 }
05975
05976 void KateDocument::testTemplateCode() {
05977
05978 qobject_cast<KTextEditor::TemplateInterface*>(activeView())->insertTemplateText(activeView()->cursorPosition(),"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05979 }
05980
05981
05982 bool KateDocument::invokeTabInterceptor(int key) {
05983 if (m_tabInterceptor) return (*m_tabInterceptor)(key);
05984 return false;
05985 }
05986
05987 bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05988 if (m_tabInterceptor) return false;
05989 m_tabInterceptor=interceptor;
05990 return true;
05991 }
05992
05993 bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
05994 if (m_tabInterceptor!=interceptor) return false;
05995 m_tabInterceptor=0;
05996 return true;
05997 }
05998
05999 KateView * KateDocument::activeKateView( ) const
06000 {
06001 return static_cast<KateView*>(m_activeView);
06002 }
06003
06004 KTextEditor::Cursor KateDocument::documentEnd( ) const
06005 {
06006 return KTextEditor::Cursor(lastLine(), lineLength(lastLine()));
06007 }
06008
06009
06010
06011 int KateDocument::currentRevision() const
06012 {
06013 return m_smartManager->currentRevision();
06014 }
06015
06016 void KateDocument::releaseRevision(int revision) const
06017 {
06018 m_smartManager->releaseRevision(revision);
06019 }
06020
06021 void KateDocument::useRevision(int revision)
06022 {
06023 m_smartManager->useRevision(revision);
06024 }
06025
06026 KTextEditor::Cursor KateDocument::translateFromRevision(const KTextEditor::Cursor& cursor, KTextEditor::SmartCursor::InsertBehavior insertBehavior) const
06027 {
06028 return m_smartManager->translateFromRevision(cursor, insertBehavior);
06029 }
06030
06031 KTextEditor::Range KateDocument::translateFromRevision(const KTextEditor::Range& range, KTextEditor::SmartRange::InsertBehaviors insertBehavior) const
06032 {
06033 return m_smartManager->translateFromRevision(range, insertBehavior);
06034 }
06035
06036 KTextEditor::SmartCursor* KateDocument::newSmartCursor( const KTextEditor::Cursor & position, KTextEditor::SmartCursor::InsertBehavior insertBehavior )
06037 {
06038 return m_smartManager->newSmartCursor(position, insertBehavior, false);
06039 }
06040
06041 KTextEditor::SmartRange * KateDocument::newSmartRange( const KTextEditor::Range & range, KTextEditor::SmartRange * parent, KTextEditor::SmartRange::InsertBehaviors insertBehavior )
06042 {
06043 return m_smartManager->newSmartRange( range, parent, insertBehavior, false );
06044 }
06045
06046 KTextEditor::SmartRange * KateDocument::newSmartRange( KTextEditor::SmartCursor * start, KTextEditor::SmartCursor * end, KTextEditor::SmartRange * parent, KTextEditor::SmartRange::InsertBehaviors insertBehavior )
06047 {
06048 KateSmartCursor* kstart = dynamic_cast<KateSmartCursor*>(start);
06049 KateSmartCursor* kend = dynamic_cast<KateSmartCursor*>(end);
06050 if (!kstart || !kend)
06051 return 0L;
06052 if (kstart->range() || kend->range())
06053 return 0L;
06054 return m_smartManager->newSmartRange(kstart, kend, parent, insertBehavior, false);
06055 }
06056
06057 void KateDocument::unbindSmartRange( KTextEditor::SmartRange * range )
06058 {
06059 m_smartManager->unbindSmartRange(range);
06060 }
06061
06062 bool KateDocument::replaceText( const KTextEditor::Range & range, const QString & s, bool block )
06063 {
06064
06065 editStart();
06066 bool changed = removeText(range, block);
06067 changed |= insertText(range.start(), s, block);
06068 editEnd();
06069 return changed;
06070 }
06071
06072 void KateDocument::addHighlightToDocument( KTextEditor::SmartRange * topRange, bool supportDynamic )
06073 {
06074 if (m_documentHighlights.contains(topRange))
06075 return;
06076
06077 m_documentHighlights.append(topRange);
06078
06079
06080 topRange->addWatcher(this);
06081
06082 if (supportDynamic) {
06083 m_documentDynamicHighlights.append(topRange);
06084 emit dynamicHighlightAdded(static_cast<KateSmartRange*>(topRange));
06085 }
06086
06087 foreach (KateView * view, m_views)
06088 view->addExternalHighlight(topRange, supportDynamic);
06089 }
06090
06091 void KateDocument::removeHighlightFromDocument( KTextEditor::SmartRange * topRange )
06092 {
06093 if (!m_documentHighlights.contains(topRange))
06094 return;
06095
06096 foreach (KateView * view, m_views)
06097 view->removeExternalHighlight(topRange);
06098
06099 m_documentHighlights.removeAll(topRange);
06100 topRange->removeWatcher(this);
06101
06102 if (m_documentDynamicHighlights.contains(topRange)) {
06103 m_documentDynamicHighlights.removeAll(topRange);
06104 emit dynamicHighlightRemoved(static_cast<KateSmartRange*>(topRange));
06105 }
06106 }
06107
06108 const QList< KTextEditor::SmartRange * > KateDocument::documentHighlights( ) const
06109 {
06110 return m_documentHighlights;
06111 }
06112
06113 void KateDocument::addHighlightToView( KTextEditor::View * view, KTextEditor::SmartRange * topRange, bool supportDynamic )
06114 {
06115 static_cast<KateView*>(view)->addExternalHighlight(topRange, supportDynamic);
06116 }
06117
06118 void KateDocument::removeHighlightFromView( KTextEditor::View * view, KTextEditor::SmartRange * topRange )
06119 {
06120 static_cast<KateView*>(view)->removeExternalHighlight(topRange);
06121 }
06122
06123 const QList< KTextEditor::SmartRange * > KateDocument::viewHighlights( KTextEditor::View * view ) const
06124 {
06125 return static_cast<KateView*>(view)->externalHighlights();
06126 }
06127
06128 void KateDocument::addActionsToDocument( KTextEditor::SmartRange * topRange )
06129 {
06130 if (m_documentActions.contains(topRange))
06131 return;
06132
06133 m_documentActions.append(topRange);
06134
06135
06136 topRange->addWatcher(this);
06137 }
06138
06139 void KateDocument::removeActionsFromDocument( KTextEditor::SmartRange * topRange )
06140 {
06141 if (!m_documentActions.contains(topRange))
06142 return;
06143
06144 m_documentActions.removeAll(topRange);
06145 topRange->removeWatcher(this);
06146 }
06147
06148 const QList< KTextEditor::SmartRange * > KateDocument::documentActions( ) const
06149 {
06150 return m_documentActions;
06151 }
06152
06153 void KateDocument::addActionsToView( KTextEditor::View * view, KTextEditor::SmartRange * topRange )
06154 {
06155 static_cast<KateView*>(view)->addActions(topRange);
06156 }
06157
06158 void KateDocument::removeActionsFromView( KTextEditor::View * view, KTextEditor::SmartRange * topRange )
06159 {
06160 static_cast<KateView*>(view)->removeActions(topRange);
06161 }
06162
06163 const QList< KTextEditor::SmartRange * > KateDocument::viewActions( KTextEditor::View * view ) const
06164 {
06165 return static_cast<KateView*>(view)->actions();
06166 }
06167
06168 void KateDocument::attributeDynamic( KTextEditor::Attribute::Ptr )
06169 {
06170
06171 }
06172
06173 void KateDocument::attributeNotDynamic( KTextEditor::Attribute::Ptr )
06174 {
06175
06176 }
06177
06178 void KateDocument::clearSmartInterface( )
06179 {
06180 clearDocumentHighlights();
06181 foreach (KateView* view, m_views)
06182 clearViewHighlights(view);
06183
06184 clearDocumentActions();
06185
06186 m_smartManager->clear(false);
06187 }
06188
06189 void KateDocument::deleteCursors( )
06190 {
06191 m_smartManager->deleteCursors(false);
06192 }
06193
06194 void KateDocument::deleteRanges( )
06195 {
06196 m_smartManager->deleteRanges(false);
06197 }
06198
06199 void KateDocument::clearDocumentHighlights( )
06200 {
06201 m_documentHighlights.clear();
06202 }
06203
06204 void KateDocument::clearViewHighlights( KTextEditor::View * view )
06205 {
06206 static_cast<KateView*>(view)->clearExternalHighlights();
06207 }
06208
06209 void KateDocument::clearDocumentActions( )
06210 {
06211 m_documentActions.clear();
06212 }
06213
06214 void KateDocument::clearViewActions( KTextEditor::View * view )
06215 {
06216 static_cast<KateView*>(view)->clearActions();
06217 }
06218
06219 void KateDocument::ignoreModifiedOnDiskOnce( )
06220 {
06221 m_isasking = -1;
06222 }
06223
06224 KateHighlighting * KateDocument::highlight( ) const
06225 {
06226 return m_buffer->highlight();
06227 }
06228
06229 uint KateDocument::getRealLine( unsigned int virtualLine )
06230 {
06231 return m_buffer->lineNumber (virtualLine);
06232 }
06233
06234 uint KateDocument::getVirtualLine( unsigned int realLine )
06235 {
06236 return m_buffer->lineVisibleNumber (realLine);
06237 }
06238
06239 uint KateDocument::visibleLines( )
06240 {
06241 return m_buffer->countVisible ();
06242 }
06243
06244 KateTextLine::Ptr KateDocument::kateTextLine( uint i )
06245 {
06246 m_buffer->ensureHighlighted (i);
06247 return m_buffer->plainLine (i);
06248 }
06249
06250 KateTextLine::Ptr KateDocument::plainKateTextLine( uint i )
06251 {
06252 return m_buffer->plainLine (i);
06253 }
06254
06255 bool KateDocument::undoDontMerge( ) const
06256 {
06257 return m_undoDontMerge;
06258 }
06259
06260 void KateDocument::setUndoDontMergeComplex(bool dontMerge)
06261 {
06262 m_undoComplexMerge = dontMerge;
06263 }
06264
06265 bool KateDocument::undoDontMergeComplex( ) const
06266 {
06267 return m_undoComplexMerge;
06268 }
06269
06270 void KateDocument::setUndoDontMerge(bool dontMerge)
06271 {
06272 m_undoDontMerge = dontMerge;
06273 }
06274
06275 bool KateDocument::isEditRunning() const
06276 {
06277 return editIsRunning;
06278 }
06279
06280 void KateDocument::rangeDeleted( KTextEditor::SmartRange * range )
06281 {
06282 removeHighlightFromDocument(range);
06283 removeActionsFromDocument(range);
06284 }
06285
06286
06287
06288
06289
06290 bool KateDocument::simpleMode ()
06291 {
06292 return KateGlobal::self()->simpleMode () && KateGlobal::self()->documentConfig()->allowSimpleMode ();
06293 }
06294
06295 KateDocument::LoadSaveFilterCheckPlugins* KateDocument::loadSaveFilterCheckPlugins()
06296 {
06297 K_GLOBAL_STATIC(KateDocument::LoadSaveFilterCheckPlugins, s_loadSaveFilterCheckPlugins)
06298 return s_loadSaveFilterCheckPlugins;
06299 }
06300
06301
06302 void KateDocument::setAnnotationModel( KTextEditor::AnnotationModel* model )
06303 {
06304 KTextEditor::AnnotationModel* oldmodel = m_annotationModel;
06305 m_annotationModel = model;
06306 emit annotationModelChanged(oldmodel, m_annotationModel);
06307 }
06308
06309 KTextEditor::AnnotationModel* KateDocument::annotationModel() const
06310 {
06311 return m_annotationModel;
06312 }
06313
06314
06315
06316 bool KateDocument::queryClose()
06317 {
06318 if ( !isReadWrite() || !isModified() )
06319 return true;
06320
06321 QString docName = documentName();
06322
06323 QWidget *parentWidget=widget();
06324 if(!parentWidget) parentWidget=QApplication::activeWindow();
06325
06326 int res = KMessageBox::warningYesNoCancel( parentWidget,
06327 i18n( "The document \"%1\" has been modified.\n"
06328 "Do you want to save your changes or discard them?" , docName ),
06329 i18n( "Close Document" ), KStandardGuiItem::save(), KStandardGuiItem::discard() );
06330
06331 bool abortClose=false;
06332 bool handled=false;
06333
06334 switch(res) {
06335 case KMessageBox::Yes :
06336 sigQueryClose(&handled,&abortClose);
06337 if (!handled)
06338 {
06339 if (url().isEmpty())
06340 {
06341 KUrl url = KFileDialog::getSaveUrl(KUrl(), QString(), parentWidget);
06342 if (url.isEmpty())
06343 return false;
06344
06345 saveAs( url );
06346 }
06347 else
06348 {
06349 save();
06350 }
06351 } else if (abortClose) return false;
06352 return waitSaveComplete();
06353 case KMessageBox::No :
06354 return true;
06355 default :
06356 return false;
06357 }
06358 }
06359
06360
06361 void KateDocument::slotCanceled() {
06362 m_savingToUrl=false;
06363 m_saveAs=false;
06364 }
06365
06366 void KateDocument::slotCompleted() {
06367 if (m_savingToUrl) {
06368 if (!m_postSaveFilterChecks.isEmpty())
06369 {
06370 LoadSaveFilterCheckPlugins *lscps1=loadSaveFilterCheckPlugins();
06371 foreach(const QString& checkplugin, m_postSaveFilterChecks)
06372 {
06373 if (lscps1->postSaveFilterCheck(checkplugin,this,m_saveAs)==false)
06374 break;
06375 }
06376 }
06377 emit documentSavedOrUploaded(this,m_saveAs);
06378 }
06379 m_savingToUrl=false;
06380 m_saveAs=false;
06381 }
06382
06383 bool KateDocument::save() {
06384 m_saveAs = false;
06385 return KTextEditor::Document::save();
06386 }
06387
06388 bool KateDocument::saveAs( const KUrl &url ) {
06389 m_saveAs = true;
06390 return KTextEditor::Document::saveAs(url);
06391 }
06392
06393
06394 #ifdef FAST_DEBUG_ENABLE
06395 # undef FAST_DEBUG_ENABLE
06396 #endif
06397 #undef FAST_DEBUG
06398
06399