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 "kateglobal.h"
00027 #include "katedialogs.h"
00028 #include "katehighlight.h"
00029 #include "kateview.h"
00030 #include "kateautoindent.h"
00031 #include "katetextline.h"
00032 #include "katedocumenthelpers.h"
00033 #include "kateprinter.h"
00034 #include "katesmartcursor.h"
00035 #include "katerenderer.h"
00036 #include <ktexteditor/attribute.h>
00037 #include "kateconfig.h"
00038 #include "katemodemanager.h"
00039 #include "kateschema.h"
00040 #include "katetemplatehandler.h"
00041 #include "katesmartmanager.h"
00042 #include <ktexteditor/plugin.h>
00043 #include <ktexteditor/loadsavefiltercheckplugin.h>
00044 #include "kateedit.h"
00045 #include "katebuffer.h"
00046 #include "kateundomanager.h"
00047 #include "katepartpluginmanager.h"
00048
00049 #include <kio/job.h>
00050 #include <kio/jobuidelegate.h>
00051 #include <kio/netaccess.h>
00052 #include <kfileitem.h>
00053
00054 #include <kparts/event.h>
00055
00056 #include <klocale.h>
00057 #include <kglobal.h>
00058 #include <kapplication.h>
00059 #include <kmenu.h>
00060 #include <kconfig.h>
00061 #include <kfiledialog.h>
00062 #include <kmessagebox.h>
00063 #include <kstandardaction.h>
00064 #include <kxmlguifactory.h>
00065 #include <kdebug.h>
00066 #include <kglobalsettings.h>
00067 #include <klibloader.h>
00068 #include <kdirwatch.h>
00069 #include <kencodingfiledialog.h>
00070 #include <ktemporaryfile.h>
00071 #include <kcodecs.h>
00072 #include <kstandarddirs.h>
00073 #include <kstringhandler.h>
00074
00075 #include <kservicetypetrader.h>
00076
00077 #include <QtDBus/QtDBus>
00078 #include <QtCore/QTimer>
00079 #include <QtCore/QFile>
00080 #include <QtGui/QClipboard>
00081 #include <QtCore/QTextStream>
00082 #include <QtCore/QTextCodec>
00083 #include <QtCore/QMap>
00084 #include <QtCore/QMutex>
00085
00086
00087
00088
00089
00090
00091
00092 #ifdef FAST_DEBUG_ENABLE
00093 # define FAST_DEBUG(x) (kDebug( 13020 ) << x)
00094 #else
00095 # define FAST_DEBUG(x)
00096 #endif
00097
00098
00099
00100
00101 class KatePartPluginItem
00102 {
00103 public:
00104 KTextEditor::Plugin *plugin;
00105 };
00106
00107
00108 static int dummy = 0;
00109
00110
00111 class KateDocument::LoadSaveFilterCheckPlugins
00112 {
00113 public:
00114 LoadSaveFilterCheckPlugins() {
00115 KService::List traderList = KServiceTypeTrader::self()->query("KTextEditor/LoadSaveFilterCheckPlugin");
00116
00117 foreach(const KService::Ptr &ptr, traderList)
00118 {
00119 QString libname;
00120 libname=ptr->library();
00121 libname=libname.right(libname.length()-12);
00122 m_plugins[libname]=0;
00123 }
00124
00125 }
00126 ~LoadSaveFilterCheckPlugins() {
00127 if ( m_plugins.count()==0) return;
00128 QHashIterator<QString,KTextEditor::LoadSaveFilterCheckPlugin*>i(m_plugins);
00129 while (i.hasNext())
00130 i.next();
00131 delete i.value();
00132 m_plugins.clear();
00133 }
00134 bool preSavePostDialogFilterCheck(const QString& pluginName,KateDocument *document,QWidget *parentWidget) {
00135 KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00136 if (!plug) {
00137 if (KMessageBox::warningContinueCancel (parentWidget
00138 , i18n ("The filter/check plugin '%1' could not be found, still continue saving of %2", pluginName,document->url().pathOrUrl())
00139 , i18n ("Saving problems")
00140 , KGuiItem(i18n("Save Nevertheless"))
00141 , KStandardGuiItem::cancel()) != KMessageBox::Continue)
00142 return false;
00143 else
00144 return true;
00145 }
00146 return plug->preSavePostDialogFilterCheck(document);
00147 }
00148 void postLoadFilter(const QString& pluginName,KateDocument *document) {
00149 KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00150 if (!plug) return;
00151 plug->postLoadFilter(document);
00152 }
00153 bool postSaveFilterCheck(const QString& pluginName,KateDocument *document,bool saveas) {
00154 KTextEditor::LoadSaveFilterCheckPlugin *plug=getPlugin(pluginName);
00155 if (!plug) return false;
00156 return plug->postSaveFilterCheck(document,saveas);
00157 }
00158 private:
00159 KTextEditor::LoadSaveFilterCheckPlugin *getPlugin(const QString & pluginName)
00160 {
00161 if (!m_plugins.contains(pluginName)) return 0;
00162 if (!m_plugins[pluginName]) {
00163 m_plugins[pluginName]=KLibLoader::createInstance<KTextEditor::LoadSaveFilterCheckPlugin>( "ktexteditor_"+pluginName);
00164 }
00165 return m_plugins[pluginName];
00166 }
00167 QHash <QString,KTextEditor::LoadSaveFilterCheckPlugin*> m_plugins;
00168 };
00169
00170
00171
00172
00173
00174 KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView,
00175 bool bReadOnly, QWidget *parentWidget,
00176 QObject *parent)
00177 : KTextEditor::Document (parent),
00178 m_activeView(0L),
00179 m_undoManager(new KateUndoManager(this)),
00180 m_annotationModel( 0 ),
00181 m_saveAs(false),
00182 m_indenter(this),
00183 m_modOnHd (false),
00184 m_modOnHdReason (OnDiskUnmodified),
00185 s_fileChangedDialogsActivated (false),
00186 m_templateHandler(0),
00187 m_savingToUrl(false)
00188 {
00189 setComponentData ( KateGlobal::self()->componentData () );
00190
00191 QString pathName ("/Kate/Document/%1");
00192 pathName = pathName.arg (++dummy);
00193
00194
00195 QDBusConnection::sessionBus().registerObject (pathName, this);
00196
00197
00198 KateGlobal::self()->registerDocument(this);
00199
00200 m_reloading = false;
00201
00202 m_editHistory = new KateEditHistory(this);
00203 m_smartManager = new KateSmartManager(this);
00204 m_buffer = new KateBuffer(this);
00205
00206
00207
00208 m_config = new KateDocumentConfig(this);
00209
00210
00211 setActiveView(0L);
00212
00213 hlSetByUser = false;
00214 m_fileTypeSetByUser = false;
00215
00216 editSessionNumber = 0;
00217 editIsRunning = false;
00218 editWithUndo = false;
00219
00220 m_docNameNumber = 0;
00221 m_docName = "need init";
00222
00223 m_bSingleViewMode = bSingleViewMode;
00224 m_bBrowserView = bBrowserView;
00225 m_bReadOnly = bReadOnly;
00226
00227 setEditableMarks( markType01 );
00228
00229 clearMarks ();
00230 setModified (false);
00231
00232
00233 m_buffer->setHighlight (0);
00234
00235 m_blockRemoveTrailingSpaces = false;
00236 m_extension = new KateBrowserExtension( this );
00237
00238
00239 m_indenter.updateConfig ();
00240
00241
00242 connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int)));
00243 connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated()));
00244
00245
00246 connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged()));
00247
00248
00249 connect( KateGlobal::self()->dirWatch(), SIGNAL(dirty (const QString &)),
00250 this, SLOT(slotModOnHdDirty (const QString &)) );
00251
00252 connect( KateGlobal::self()->dirWatch(), SIGNAL(created (const QString &)),
00253 this, SLOT(slotModOnHdCreated (const QString &)) );
00254
00255 connect( KateGlobal::self()->dirWatch(), SIGNAL(deleted (const QString &)),
00256 this, SLOT(slotModOnHdDeleted (const QString &)) );
00257
00258 connect (this,SIGNAL(completed()),this,SLOT(slotCompleted()));
00259 connect (this,SIGNAL(canceled(const QString&)),this,SLOT(slotCanceled()));
00260
00261 setDocName ("");
00262
00263
00264
00265
00266 if ( m_bSingleViewMode && parentWidget )
00267 {
00268 KTextEditor::View *view = (KTextEditor::View*)createView( parentWidget );
00269 insertChildClient( view );
00270 view->show();
00271 setWidget( view );
00272 }
00273
00274 connect(this,SIGNAL(sigQueryClose(bool *, bool*)),this,SLOT(slotQueryClose_save(bool *, bool*)));
00275
00276 m_isasking = 0;
00277
00278
00279 KatePartPluginManager::self()->addDocument(this);
00280 }
00281
00282
00283
00284
00285 KateDocument::~KateDocument()
00286 {
00287
00288
00289
00290 emit aboutToClose(this);
00291
00292
00293 deactivateDirWatch ();
00294
00295
00296 setAutoDeleteWidget(false);
00297 setAutoDeletePart(false);
00298
00299
00300 while (!m_views.isEmpty()) {
00301 delete m_views.takeFirst();
00302 }
00303
00304
00305 KatePartPluginManager::self()->removeDocument(this);
00306
00307
00308 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
00309 delete i.value();
00310 m_marks.clear();
00311
00312 delete m_config;
00313 KateGlobal::self()->deregisterDocument (this);
00314 }
00315
00316
00317
00318 QWidget *KateDocument::widget()
00319 {
00320
00321 if (!singleViewMode())
00322 return 0;
00323
00324
00325 if (KTextEditor::Document::widget())
00326 return KTextEditor::Document::widget();
00327
00328
00329 KTextEditor::View *view = (KTextEditor::View*)createView(0);
00330 insertChildClient( view );
00331 setWidget( view );
00332 return view;
00333 }
00334
00335
00336
00337 KTextEditor::View *KateDocument::createView( QWidget *parent )
00338 {
00339 KateView* newView = new KateView( this, parent);
00340 if ( s_fileChangedDialogsActivated )
00341 connect( newView, SIGNAL(focusIn( KTextEditor::View * )), this, SLOT(slotModifiedOnDisk()) );
00342
00343 emit viewCreated (this, newView);
00344
00345 return newView;
00346 }
00347
00348 const QList<KTextEditor::View*> &KateDocument::views () const
00349 {
00350 return m_textEditViews;
00351 }
00352
00353 KTextEditor::Editor *KateDocument::editor ()
00354 {
00355 return KateGlobal::self();
00356 }
00357
00358
00359
00360 QString KateDocument::text() const
00361 {
00362 QString s;
00363
00364 for (int i = 0; i < m_buffer->count(); i++)
00365 {
00366 KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00367
00368 if (textLine)
00369 {
00370 s.append (textLine->string());
00371
00372 if ((i+1) < m_buffer->count())
00373 s.append(QChar::fromAscii('\n'));
00374 }
00375 }
00376
00377 return s;
00378 }
00379
00380 QString KateDocument::text( const KTextEditor::Range& range, bool blockwise ) const
00381 {
00382 if (!range.isValid()) {
00383 kWarning() << k_funcinfo << "Text requested for invalid range" << range;
00384 return QString();
00385 }
00386
00387 if ( blockwise && (range.start().column() > range.end().column()) )
00388 return QString ();
00389
00390 QString s;
00391
00392 if (range.start().line() == range.end().line())
00393 {
00394 if (range.start().column() > range.end().column())
00395 return QString ();
00396
00397 KateTextLine::Ptr textLine = m_buffer->plainLine(range.start().line());
00398
00399 if ( !textLine )
00400 return QString ();
00401
00402 return textLine->string(range.start().column(), range.end().column()-range.start().column());
00403 }
00404 else
00405 {
00406
00407 for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
00408 {
00409 KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00410
00411 if ( !blockwise )
00412 {
00413 if (i == range.start().line())
00414 s.append (textLine->string(range.start().column(), textLine->length()-range.start().column()));
00415 else if (i == range.end().line())
00416 s.append (textLine->string(0, range.end().column()));
00417 else
00418 s.append (textLine->string());
00419 }
00420 else
00421 {
00422 s.append( textLine->string( range.start().column(), range.end().column()-range.start().column()));
00423 }
00424
00425 if ( i < range.end().line() )
00426 s.append(QChar::fromAscii('\n'));
00427 }
00428 }
00429
00430 return s;
00431 }
00432
00433 QChar KateDocument::character( const KTextEditor::Cursor & position ) const
00434 {
00435 KateTextLine::Ptr textLine = m_buffer->plainLine(position.line());
00436
00437 if ( !textLine )
00438 return QChar();
00439
00440 if (position.column() >= 0 && position.column() < textLine->string().length())
00441 return textLine->string().at(position.column());
00442
00443 return QChar();
00444 }
00445
00446 QStringList KateDocument::textLines( const KTextEditor::Range & range, bool blockwise ) const
00447 {
00448 QStringList ret;
00449
00450 if (!range.isValid()) {
00451 kWarning() << k_funcinfo << "Text requested for invalid range" << range;
00452 return ret;
00453 }
00454
00455 if ( blockwise && (range.start().column() > range.end().column()) )
00456 return ret;
00457
00458 if (range.start().line() == range.end().line())
00459 {
00460 Q_ASSERT(range.start() <= range.end());
00461
00462 KateTextLine::Ptr textLine = m_buffer->plainLine(range.start().line());
00463
00464 if ( !textLine )
00465 return ret;
00466
00467 ret << textLine->string(range.start().column(), range.end().column() - range.start().column());
00468 }
00469 else
00470 {
00471 for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i)
00472 {
00473 KateTextLine::Ptr textLine = m_buffer->plainLine(i);
00474
00475 if ( !blockwise )
00476 {
00477 if (i == range.start().line())
00478 ret << textLine->string(range.start().column(), textLine->length() - range.start().column());
00479 else if (i == range.end().line())
00480 ret << textLine->string(0, range.end().column());
00481 else
00482 ret << textLine->string();
00483 }
00484 else
00485 {
00486 ret << textLine->string(range.start().column(), range.end().column() - range.start().column());
00487 }
00488 }
00489 }
00490
00491 return ret;
00492 }
00493
00494 QString KateDocument::line( int line ) const
00495 {
00496 KateTextLine::Ptr l = m_buffer->plainLine(line);
00497
00498 if (!l)
00499 return QString();
00500
00501 return l->string();
00502 }
00503
00504 bool KateDocument::setText(const QString &s)
00505 {
00506 if (!isReadWrite())
00507 return false;
00508
00509 QList<KTextEditor::Mark> msave;
00510
00511 foreach (KTextEditor::Mark* mark, m_marks)
00512 msave.append(*mark);
00513
00514 editStart ();
00515
00516
00517 clear();
00518
00519
00520 insertText (KTextEditor::Cursor(), s);
00521
00522 editEnd ();
00523
00524 foreach (const KTextEditor::Mark& mark, msave)
00525 setMark (mark.line, mark.type);
00526
00527 return true;
00528 }
00529
00530 bool KateDocument::setText( const QStringList & text )
00531 {
00532 if (!isReadWrite())
00533 return false;
00534
00535 QList<KTextEditor::Mark> msave;
00536
00537 foreach (KTextEditor::Mark* mark, m_marks)
00538 msave.append(*mark);
00539
00540 editStart ();
00541
00542
00543 clear();
00544
00545
00546 insertText (KTextEditor::Cursor::start(), text);
00547
00548 editEnd ();
00549
00550 foreach (const KTextEditor::Mark& mark, msave)
00551 setMark (mark.line, mark.type);
00552
00553 return true;
00554 }
00555
00556 bool KateDocument::clear()
00557 {
00558 if (!isReadWrite())
00559 return false;
00560
00561 foreach (KateView *view, m_views) {
00562 view->clear();
00563 view->tagAll();
00564 view->update();
00565 }
00566
00567 clearMarks ();
00568
00569 return removeText (KTextEditor::Range(KTextEditor::Cursor(), KTextEditor::Cursor(lastLine()+1, 0)));
00570 }
00571
00572 bool KateDocument::insertText( const KTextEditor::Cursor& position, const QString& text, bool block )
00573 {
00574 if (!isReadWrite())
00575 return false;
00576
00577 if (text.isEmpty())
00578 return true;
00579
00580 editStart();
00581
00582 int currentLine = position.line();
00583 int currentLineStart = 0;
00584 int totalLength = text.length();
00585 int insertColumn = position.column();
00586
00587 if (position.line() > lines())
00588 {
00589 int line = lines();
00590 while (line != position.line() + totalLength + 1)
00591 {
00592 editInsertLine(line,"");
00593 line++;
00594 }
00595 }
00596
00597 bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn );
00598 int tabWidth = config()->tabWidth();
00599
00600 static const QChar newLineChar('\n');
00601 static const QChar tabChar('\t');
00602 static const QChar spaceChar(' ');
00603
00604 int insertColumnExpanded = insertColumn;
00605 KateTextLine::Ptr l = kateTextLine( currentLine );
00606 if (l)
00607 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00608
00609 int pos = 0;
00610 for (; pos < totalLength; pos++)
00611 {
00612 const QChar& ch = text.at(pos);
00613
00614 if (ch == newLineChar)
00615 {
00616
00617 if (currentLineStart < pos)
00618 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00619
00620 if ( !block )
00621 {
00622 editWrapLine(currentLine, insertColumn + pos - currentLineStart);
00623 insertColumn = 0;
00624 }
00625 else
00626 {
00627 if ( currentLine == lastLine() )
00628 editWrapLine(currentLine , insertColumn + pos - currentLineStart);
00629 insertColumn = position.column();
00630 }
00631
00632 currentLine++;
00633 currentLineStart = pos + 1;
00634 l = kateTextLine( currentLine );
00635 if (l)
00636 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00637 }
00638 else
00639 {
00640 if ( replacetabs && ch == tabChar )
00641 {
00642 int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth );
00643 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar));
00644
00645 insertColumn += pos - currentLineStart + spacesRequired;
00646 currentLineStart = pos + 1;
00647 l = kateTextLine( currentLine );
00648 if (l)
00649 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00650 }
00651 }
00652 }
00653
00654
00655 if (currentLineStart < pos)
00656 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00657
00658 editEnd();
00659 return true;
00660 }
00661
00662 bool KateDocument::insertText( const KTextEditor::Cursor & position, const QStringList & textLines, bool block )
00663 {
00664 if (!isReadWrite())
00665 return false;
00666
00667 if (textLines.isEmpty() || (textLines.count() == 1 && textLines.first().isEmpty()))
00668 return true;
00669
00670
00671 if (position.line() > lines())
00672 return false;
00673
00674 editStart();
00675
00676 if (position.line() > lines())
00677 editInsertLine(position.line(),"");
00678
00679 int currentLine = position.line();
00680 int currentLineStart = 0;
00681 int insertColumn = position.column();
00682
00683 bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn );
00684 int tabWidth = config()->tabWidth();
00685
00686 static const QChar newLineChar('\n');
00687 static const QChar tabChar('\t');
00688 static const QChar spaceChar(' ');
00689
00690 int insertColumnExpanded = insertColumn;
00691 KateTextLine::Ptr l = kateTextLine( currentLine );
00692 if (l)
00693 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00694
00695 foreach (const QString &text, textLines)
00696 {
00697 int pos = 0;
00698 for (; pos < text.length(); pos++)
00699 {
00700 const QChar& ch = text.at(pos);
00701
00702 if (ch == newLineChar)
00703 {
00704
00705 if (currentLineStart < pos)
00706 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00707
00708 if ( !block )
00709 {
00710 editWrapLine(currentLine, pos + insertColumn);
00711 insertColumn = 0;
00712 }
00713 else
00714 {
00715 if ( currentLine == lastLine() )
00716 editWrapLine(currentLine , insertColumn + pos - currentLineStart);
00717 insertColumn = position.column();
00718 }
00719
00720 currentLine++;
00721 currentLineStart = pos + 1;
00722 l = kateTextLine( currentLine );
00723 if (l)
00724 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00725 }
00726 else
00727 {
00728 if ( replacetabs && ch == tabChar )
00729 {
00730 int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth );
00731 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar));
00732
00733 insertColumn += pos - currentLineStart + spacesRequired;
00734 l = kateTextLine( currentLine );
00735 if (l)
00736 insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth );
00737 currentLineStart = pos + 1;
00738 }
00739 }
00740 }
00741
00742
00743 if (currentLineStart < pos - currentLineStart)
00744 editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart));
00745 }
00746
00747 editEnd();
00748 return true;
00749 }
00750
00751
00752 bool KateDocument::removeText ( const KTextEditor::Range &_range, bool block )
00753 {
00754 KTextEditor::Range range = _range;
00755
00756 if (!isReadWrite())
00757 return false;
00758
00759 if ( block && (range.start().column() > range.end().column()) )
00760 return false;
00761
00762
00763 Q_ASSERT( range.start().line() <= range.end().line() );
00764
00765 if ( range.start().line() > lastLine() )
00766 return false;
00767
00768 if (!block)
00769 emit aboutToRemoveText(range);
00770
00771 editStart();
00772
00773 if ( !block )
00774 {
00775 if ( range.end().line() > lastLine() )
00776 {
00777 range.end().setPosition(lastLine()+1, 0);
00778 }
00779
00780 if (range.onSingleLine())
00781 {
00782 editRemoveText(range.start().line(), range.start().column(), range.columnWidth());
00783 }
00784 else if (range.start().line() + 1 == range.end().line())
00785 {
00786 if ( (m_buffer->plainLine(range.start().line())->length() - range.start().column()) > 0 )
00787 editRemoveText(range.start().line(), range.start().column(), m_buffer->plainLine(range.start().line())->length() - range.start().column());
00788
00789 if (range.end().column())
00790 editRemoveText (range.start().line() + 1, 0, range.end().column());
00791
00792 editUnWrapLine (range.start().line());
00793 }
00794 else
00795 {
00796 for (int line = range.end().line(); line >= range.start().line(); --line)
00797 {
00798 if ((line > range.start().line()) && (line < range.end().line())) {
00799 editRemoveLine(line);
00800
00801 } else if (line == range.end().line()) {
00802 if ( range.end().line() <= lastLine() )
00803 editRemoveText(line, 0, range.end().column());
00804
00805 } else {
00806 if ( (m_buffer->plainLine(line)->length() - range.start().column()) > 0 )
00807 editRemoveText(line, range.start().column(), m_buffer->plainLine(line)->length() - range.start().column());
00808
00809 editUnWrapLine(range.start().line());
00810 }
00811
00812 if ( line == 0 )
00813 break;
00814 }
00815 }
00816 }
00817 else
00818 {
00819 int startLine = qMax(0, range.start().line());
00820 for (int line = qMin(range.end().line(), lastLine()); line >= startLine; --line)
00821 editRemoveText(line, range.start().column(), range.end().column() - range.start().column());
00822 }
00823
00824 editEnd ();
00825 emit textRemoved();
00826 return true;
00827 }
00828
00829 bool KateDocument::insertLine( int l, const QString &str )
00830 {
00831 if (!isReadWrite())
00832 return false;
00833
00834 if (l < 0 || l > lines())
00835 return false;
00836
00837 return editInsertLine (l, str);
00838 }
00839
00840 bool KateDocument::insertLines( int line, const QStringList & text )
00841 {
00842 if (!isReadWrite())
00843 return false;
00844
00845 if (line < 0 || line > lines())
00846 return false;
00847
00848 bool success = true;
00849 foreach (const QString &string, text)
00850
00851 success &= editInsertLine (line++, string);
00852
00853 return success;
00854 }
00855
00856 bool KateDocument::removeLine( int line )
00857 {
00858 if (!isReadWrite())
00859 return false;
00860
00861 if (line < 0 || line > lastLine())
00862 return false;
00863
00864 return editRemoveLine (line);
00865 }
00866
00867 int KateDocument::totalCharacters() const
00868 {
00869 int l = 0;
00870
00871 for (int i = 0; i < m_buffer->count(); ++i)
00872 {
00873 KateTextLine::Ptr line = m_buffer->plainLine(i);
00874
00875 if (line)
00876 l += line->length();
00877 }
00878
00879 return l;
00880 }
00881
00882 int KateDocument::lines() const
00883 {
00884 return m_buffer->count();
00885 }
00886
00887 int KateDocument::numVisLines() const
00888 {
00889 return m_buffer->countVisible ();
00890 }
00891
00892 int KateDocument::lineLength ( int line ) const
00893 {
00894 if (line < 0 || line > lastLine())
00895 return -1;
00896
00897 KateTextLine::Ptr l = m_buffer->plainLine(line);
00898
00899 if (!l)
00900 return -1;
00901
00902 return l->length();
00903 }
00904
00905
00906
00907
00908
00909
00910 void KateDocument::editStart (bool withUndo, Kate::EditSource editSource)
00911 {
00912 editSessionNumber++;
00913
00914 if (editSource == Kate::NoEditSource)
00915 m_editSources.push(m_editSources.isEmpty() ? Kate::UserInputEdit : m_editSources.top());
00916 else
00917 m_editSources.push(editSource);
00918
00919
00920 smartMutex()->lock();
00921
00922 if (editSessionNumber > 1)
00923 return;
00924
00925 editIsRunning = true;
00926 editWithUndo = withUndo;
00927
00928 if (editWithUndo)
00929 m_undoManager->editStart();
00930
00931 foreach(KateView *view,m_views)
00932 {
00933 view->editStart ();
00934 }
00935
00936 m_buffer->editStart ();
00937 }
00938
00939 void KateDocument::undoSafePoint() {
00940 m_undoManager->undoSafePoint();
00941 }
00942
00943
00944
00945
00946 void KateDocument::editEnd ()
00947 {
00948 if (editSessionNumber == 0) {
00949
00950
00951 Q_ASSERT(0);
00952 return;
00953 }
00954
00955
00956 if (m_buffer->editChanged() && (editSessionNumber == 1))
00957 if (editWithUndo && config()->wordWrap())
00958 wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
00959
00960 editSessionNumber--;
00961
00962 m_editSources.pop();
00963
00964
00965 smartMutex()->unlock();
00966
00967 if (editSessionNumber > 0)
00968 return;
00969
00970
00971
00972 m_buffer->editEnd ();
00973
00974 if (editWithUndo)
00975 m_undoManager->editEnd();
00976
00977
00978 foreach(KateView *view, m_views)
00979 view->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
00980
00981 if (m_buffer->editChanged())
00982 {
00983 setModified(true);
00984 emit textChanged (this);
00985 }
00986
00987 editIsRunning = false;
00988 }
00989
00990 void KateDocument::pushEditState ()
00991 {
00992 editStateStack.push(editSessionNumber);
00993 }
00994
00995 void KateDocument::popEditState ()
00996 {
00997 if (editStateStack.isEmpty()) return;
00998
00999 int count = editStateStack.pop() - editSessionNumber;
01000 while (count < 0) { ++count; editEnd(); }
01001 while (count > 0) { --count; editStart(); }
01002 }
01003
01004 void KateDocument::inputMethodStart()
01005 {
01006 editStart(false);
01007 }
01008
01009 void KateDocument::inputMethodEnd()
01010 {
01011 editEnd();
01012 }
01013
01014 bool KateDocument::wrapText(int startLine, int endLine)
01015 {
01016 if (startLine < 0 || endLine < 0)
01017 return false;
01018
01019 if (!isReadWrite())
01020 return false;
01021
01022 int col = config()->wordWrapAt();
01023
01024 if (col == 0)
01025 return false;
01026
01027 editStart ();
01028
01029 for (int line = startLine; (line <= endLine) && (line < lines()); line++)
01030 {
01031 KateTextLine::Ptr l = kateTextLine(line);
01032
01033 if (!l)
01034 return false;
01035
01036 kDebug (13020) << "try wrap line: " << line;
01037
01038 if (l->virtualLength(m_buffer->tabWidth()) > col)
01039 {
01040 KateTextLine::Ptr nextl = kateTextLine(line+1);
01041
01042 kDebug (13020) << "do wrap line: " << line;
01043
01044 int eolPosition = l->length()-1;
01045
01046
01047 int x = 0;
01048 const QString & t = l->string();
01049 int z2 = 0;
01050 for ( ; z2 < l->length(); z2++)
01051 {
01052 static const QChar tabChar('\t');
01053 if (t.at(z2) == tabChar)
01054 x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
01055 else
01056 x++;
01057
01058 if (x > col)
01059 break;
01060 }
01061
01062 int searchStart = qMin (z2, l->length()-1);
01063
01064
01065
01066 if (searchStart == eolPosition && t.at(searchStart).isSpace())
01067 searchStart--;
01068
01069
01070
01071
01072
01073
01074
01075 int z = 0;
01076 int nw = 0;
01077 for (z=searchStart; z > 0; z--)
01078 {
01079 if (t.at(z).isSpace()) break;
01080 if ( ! nw && highlight()->canBreakAt( t.at(z) , l->attribute(z) ) )
01081 nw = z;
01082 }
01083
01084 bool removeTrailingSpace = false;
01085 if (z > 0)
01086 {
01087
01088
01089
01090
01091
01092 z++;
01093 removeTrailingSpace = true;
01094 }
01095 else
01096 {
01097
01098
01099
01100 if ( nw && nw < col ) nw++;
01101 z = nw ? nw : col;
01102 }
01103
01104 if (nextl && !nextl->isAutoWrapped())
01105 {
01106 editWrapLine (line, z, true);
01107 editMarkLineAutoWrapped (line+1, true);
01108
01109 endLine++;
01110 }
01111 else
01112 {
01113 if (nextl && (nextl->length() > 0) && !nextl->at(0).isSpace() && ((l->length() < 1) || !l->at(l->length()-1).isSpace()))
01114 editInsertText (line+1, 0, QString (" "));
01115
01116 bool newLineAdded = false;
01117 editWrapLine (line, z, false, &newLineAdded);
01118
01119 editMarkLineAutoWrapped (line+1, true);
01120
01121 endLine++;
01122 }
01123
01124 if (removeTrailingSpace) {
01125
01126 editRemoveText (line, z - 1, 1);
01127 }
01128 }
01129 }
01130
01131 editEnd ();
01132
01133 return true;
01134 }
01135
01136 bool KateDocument::editInsertText ( int line, int col, const QString &s, Kate::EditSource editSource )
01137 {
01138 if (line < 0 || col < 0)
01139 return false;
01140
01141 if (!isReadWrite())
01142 return false;
01143
01144 KateTextLine::Ptr l = kateTextLine(line);
01145
01146 if (!l)
01147 return false;
01148
01149 editStart (true, editSource);
01150
01151 m_undoManager->slotTextInserted(line, col, s);
01152
01153 l->insertText (col, s);
01154
01155 m_buffer->changeLine(line);
01156
01157 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col), QStringList(), KTextEditor::Range(line, col, line, col + s.length()), QStringList(s)) );
01158 emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line, col + s.length()));
01159
01160 editEnd();
01161
01162 return true;
01163 }
01164
01165 bool KateDocument::editRemoveText ( int line, int col, int len, Kate::EditSource editSource )
01166 {
01167 if (line < 0 || col < 0 || len < 0)
01168 return false;
01169
01170 if (!isReadWrite())
01171 return false;
01172
01173 KateTextLine::Ptr l = kateTextLine(line);
01174
01175 if (!l)
01176 return false;
01177
01178 editStart (true, editSource);
01179
01180 m_undoManager->slotTextRemoved(line, col, l->string().mid(col, len));
01181
01182 l->removeText (col, len);
01183 removeTrailingSpace( line );
01184
01185 m_buffer->changeLine(line);
01186
01187 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()) );
01188 emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len));
01189
01190 editEnd ();
01191
01192 return true;
01193 }
01194
01195 bool KateDocument::editMarkLineAutoWrapped ( int line, bool autowrapped )
01196 {
01197 if (line < 0)
01198 return false;
01199
01200 if (!isReadWrite())
01201 return false;
01202
01203 KateTextLine::Ptr l = kateTextLine(line);
01204
01205 if (!l)
01206 return false;
01207
01208 editStart ();
01209
01210 m_undoManager->slotMarkLineAutoWrapped(line, autowrapped);
01211
01212 l->setAutoWrapped (autowrapped);
01213
01214 m_buffer->changeLine(line);
01215
01216 editEnd ();
01217
01218 return true;
01219 }
01220
01221 bool KateDocument::editWrapLine ( int line, int col, bool newLine, bool *newLineAdded)
01222 {
01223 if (line < 0 || col < 0)
01224 return false;
01225
01226 if (!isReadWrite())
01227 return false;
01228
01229 KateTextLine::Ptr l = kateTextLine(line);
01230
01231 if (!l)
01232 return false;
01233
01234 editStart ();
01235
01236 KateTextLine::Ptr nextLine = kateTextLine(line+1);
01237
01238 int pos = l->length() - col;
01239
01240 if (pos < 0)
01241 pos = 0;
01242
01243 m_undoManager->slotLineWrapped(line, col, pos, (!nextLine || newLine));
01244
01245 if (!nextLine || newLine)
01246 {
01247 KateTextLine::Ptr textLine(new KateTextLine());
01248
01249 textLine->insertText (0, l->string().mid(col, pos));
01250 l->truncate(col);
01251
01252 m_buffer->insertLine (line+1, textLine);
01253 m_buffer->changeLine(line);
01254
01255 QList<KTextEditor::Mark*> list;
01256 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01257 {
01258 if( i.value()->line >= line )
01259 {
01260 if ((col == 0) || (i.value()->line > line))
01261 list.append( i.value() );
01262 }
01263 }
01264
01265 for( int i=0; i < list.size(); ++i )
01266 m_marks.take( list[i]->line );
01267
01268 for( int i=0; i < list.size(); ++i )
01269 {
01270 list[i]->line++;
01271 m_marks.insert( list[i]->line, list[i] );
01272 }
01273
01274 if( !list.isEmpty() )
01275 emit marksChanged( this );
01276
01277
01278 if (newLineAdded)
01279 (*newLineAdded) = true;
01280
01281 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line, col), QStringList(), KTextEditor::Range(line, col, line+1, 0), QStringList()) );
01282 }
01283 else
01284 {
01285 nextLine->insertText (0, l->string().mid(col, pos));
01286 l->truncate(col);
01287
01288 m_buffer->changeLine(line);
01289 m_buffer->changeLine(line+1);
01290
01291
01292 if (newLineAdded)
01293 (*newLineAdded) = false;
01294
01295 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line+1, 0), QStringList(), KTextEditor::Range(line, col, line+1, pos), QStringList()) );
01296 }
01297
01298 emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line+1, pos));
01299
01300 editEnd ();
01301
01302 return true;
01303 }
01304
01305 bool KateDocument::editUnWrapLine ( int line, bool removeLine, int length )
01306 {
01307 if (line < 0 || length < 0)
01308 return false;
01309
01310 if (!isReadWrite())
01311 return false;
01312
01313 KateTextLine::Ptr l = kateTextLine(line);
01314 KateTextLine::Ptr nextLine = kateTextLine(line+1);
01315
01316 if (!l || !nextLine)
01317 return false;
01318
01319 editStart ();
01320
01321 int col = l->length ();
01322
01323 m_undoManager->slotLineUnWrapped(line, col, length, removeLine);
01324
01325 if (removeLine)
01326 {
01327 l->insertText (col, nextLine->string());
01328
01329 m_buffer->changeLine(line);
01330 m_buffer->removeLine(line+1);
01331 }
01332 else
01333 {
01334 l->insertText (col, nextLine->string().left((nextLine->length() < length) ? nextLine->length() : length));
01335 nextLine->removeText (0, (nextLine->length() < length) ? nextLine->length() : length);
01336
01337 m_buffer->changeLine(line);
01338 m_buffer->changeLine(line+1);
01339 }
01340
01341 QList<KTextEditor::Mark*> list;
01342 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01343 {
01344 if( i.value()->line >= line+1 )
01345 list.append( i.value() );
01346
01347 if ( i.value()->line == line+1 )
01348 {
01349 KTextEditor::Mark* mark = m_marks.take( line );
01350
01351 if (mark)
01352 {
01353 i.value()->type |= mark->type;
01354 }
01355 }
01356 }
01357
01358 for( int i=0; i < list.size(); ++i )
01359 m_marks.take( list[i]->line );
01360
01361 for( int i=0; i < list.size(); ++i )
01362 {
01363 list[i]->line--;
01364 m_marks.insert( list[i]->line, list[i] );
01365 }
01366
01367 if( !list.isEmpty() )
01368 emit marksChanged( this );
01369
01370 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(line, col, line+1, 0), QStringList(QString()), KTextEditor::Range(line, col, line, col), QStringList()) );
01371 emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line+1, 0));
01372
01373 editEnd ();
01374
01375 return true;
01376 }
01377
01378 bool KateDocument::editInsertLine ( int line, const QString &s, Kate::EditSource editSource )
01379 {
01380 if (line < 0)
01381 return false;
01382
01383 if (!isReadWrite())
01384 return false;
01385
01386 if ( line > lines() )
01387 return false;
01388
01389 editStart (true, editSource);
01390
01391 m_undoManager->slotLineInserted(line, s);
01392
01393 removeTrailingSpace( line );
01394
01395 KateTextLine::Ptr tl(new KateTextLine());
01396 tl->insertText (0, s);
01397 m_buffer->insertLine(line, tl);
01398 m_buffer->changeLine(line);
01399
01400 removeTrailingSpace( line );
01401
01402 QList<KTextEditor::Mark*> list;
01403 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01404 {
01405 if( i.value()->line >= line )
01406 list.append( i.value() );
01407 }
01408
01409 for( int i=0; i < list.size(); ++i )
01410 m_marks.take( list[i]->line );
01411
01412 for( int i=0; i < list.size(); ++i )
01413 {
01414 list[i]->line++;
01415 m_marks.insert( list[i]->line, list[i] );
01416 }
01417
01418 if( !list.isEmpty() )
01419 emit marksChanged( this );
01420
01421 KTextEditor::Range rangeInserted(line, 0, line, tl->length());
01422
01423 if (line) {
01424 KateTextLine::Ptr prevLine = plainKateTextLine(line - 1);
01425 rangeInserted.start().setPosition(line - 1, prevLine->length());
01426 } else {
01427 rangeInserted.end().setPosition(line + 1, 0);
01428 }
01429
01430 history()->doEdit( new KateEditInfo(m_editSources.top(), KTextEditor::Range(rangeInserted.start(), rangeInserted.start()), QStringList(), rangeInserted, QStringList(s)) );
01431 emit KTextEditor::Document::textInserted(this, rangeInserted);
01432
01433 editEnd ();
01434
01435 return true;
01436 }
01437
01438 bool KateDocument::editRemoveLine ( int line, Kate::EditSource editSource )
01439 {
01440 if (line < 0)
01441 return false;
01442
01443 if (!isReadWrite())
01444 return false;
01445
01446 if ( line > lastLine() )
01447 return false;
01448
01449 if ( lines() == 1 )
01450 return editRemoveText (0, 0, kateTextLine(0)->length());
01451
01452 KateLineInfo info;;
01453 lineInfo (&info, line);
01454 if (info.startsInVisibleBlock)
01455 foldingTree()->toggleRegionVisibility(line);
01456
01457 editStart (true, editSource);
01458
01459 QString oldText = this->line(line);
01460
01461 m_undoManager->slotLineRemoved(line, this->line(line));
01462
01463 KTextEditor::Range rangeRemoved(line, 0, line, oldText.length());
01464
01465 if (line < lastLine()) {
01466 rangeRemoved.end().setPosition(line + 1, 0);
01467 } else if (line) {
01468 KateTextLine::Ptr prevLine = plainKateTextLine(line - 1);
01469 rangeRemoved.start().setPosition(line - 1, prevLine->length());
01470 }
01471
01472 m_buffer->removeLine(line);
01473
01474 KTextEditor::Mark* rmark = 0;
01475 QList<KTextEditor::Mark*> list;
01476 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
01477 {
01478 if ( (i.value()->line > line) )
01479 list.append( i.value() );
01480 else if ( (i.value()->line == line) )
01481 rmark = i.value();
01482 }
01483
01484 if (rmark)
01485 delete (m_marks.take (rmark->line));
01486
01487 for( int i=0; i < list.size(); ++i )
01488 {
01489 KTextEditor::Mark* mark = m_marks.take( list[i]->line );
01490 mark->line--;
01491 m_marks.insert( mark->line, mark );
01492 }
01493
01494 if( !list.isEmpty() )
01495 emit marksChanged( this );
01496
01497 history()->doEdit( new KateEditInfo(m_editSources.top(), rangeRemoved, QStringList(QString(oldText)), KTextEditor::Range(rangeRemoved.start(), rangeRemoved.start()), QStringList()) );
01498 emit KTextEditor::Document::textRemoved(this, rangeRemoved);
01499
01500 editEnd();
01501
01502 return true;
01503 }
01504
01505
01506
01507
01508 uint KateDocument::undoCount () const
01509 {
01510 return m_undoManager->undoCount ();
01511 }
01512
01513 uint KateDocument::redoCount () const
01514 {
01515 return m_undoManager->redoCount ();
01516 }
01517
01518 void KateDocument::undo()
01519 {
01520 m_undoManager->undo();
01521 }
01522
01523 void KateDocument::redo()
01524 {
01525 m_undoManager->redo();
01526 }
01527
01528
01529
01530
01531 KTextEditor::Range KateDocument::searchText (const KTextEditor::Range & inputRange, const QString &text, bool casesensitive, bool backwards)
01532 {
01533 FAST_DEBUG("KateDocument::searchText( " << inputRange.start().line() << ", "
01534 << inputRange.start().column() << ", " << text << ", " << backwards << " )");
01535 if (text.isEmpty() || !inputRange.isValid() || (inputRange.start() == inputRange.end()))
01536 {
01537 return KTextEditor::Range::invalid();
01538 }
01539
01540
01541 const QString sep("\n");
01542 const QStringList needleLines = text.split(sep);
01543 const int numNeedleLines = needleLines.count();
01544 FAST_DEBUG("searchText | needle has " << numNeedleLines << " lines");
01545
01546 if (numNeedleLines > 1)
01547 {
01548
01549 const int lastLine = inputRange.end().line();
01550
01551 const int forMin = inputRange.start().line();
01552 const int forMax = lastLine + 1 - numNeedleLines;
01553 const int forInit = backwards ? forMax : forMin;
01554 const int forInc = backwards ? -1 : +1;
01555
01556 const int minLeft = inputRange.start().column();
01557 const uint maxRight = inputRange.end().column();
01558
01559
01560 int hayLinesZeroIndex = 0;
01561 QVector<KateTextLine::Ptr> hayLinesWindow;
01562 for (int i = 0; i < numNeedleLines; i++) {
01563 KateTextLine::Ptr textLine = m_buffer->plainLine((backwards ? forMax : forMin) + i);
01564
01565 if (!textLine)
01566 return KTextEditor::Range::invalid();
01567
01568 hayLinesWindow.append (textLine);
01569 FAST_DEBUG("searchText | hayLinesWindow[" << i << "] = \"" << hayLinesWindow[i]->string() << "\"");
01570 }
01571
01572 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01573 {
01574
01575 uint startCol = 0;
01576 for (int k = 0; k < numNeedleLines; k++)
01577 {
01578
01579 const QString & needleLine = needleLines[k];
01580 KateTextLine::Ptr & hayLine = hayLinesWindow[(k + hayLinesZeroIndex) % numNeedleLines];
01581 FAST_DEBUG("searchText | hayLine = \"" << hayLine->string() << "\"");
01582
01583
01584 if (k == 0) {
01585
01586 if (needleLine.length() == 0)
01587 {
01588 startCol = hayLine->length();
01589 }
01590 else
01591 {
01592 uint myMatchLen;
01593 const uint colOffset = (j > forMin) ? 0 : minLeft;
01594 const bool matches = hayLine->searchText(colOffset, hayLine->length(),needleLine, &startCol,
01595 &myMatchLen, casesensitive, false);
01596 if (!matches || (startCol + myMatchLen != static_cast<uint>(hayLine->length()))) {
01597 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01598 break;
01599 }
01600 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01601 }
01602 } else if (k == numNeedleLines - 1) {
01603
01604 uint foundAt, myMatchLen;
01605 const bool matches = hayLine->searchText(0,hayLine->length(), needleLine, &foundAt, &myMatchLen, casesensitive, false);
01606 if (matches && (foundAt == 0) && !((k == lastLine)
01607 && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
01608 {
01609 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01610 return KTextEditor::Range(j, startCol, j + k, needleLine.length());
01611 }
01612 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01613 } else {
01614
01615 uint foundAt, myMatchLen;
01616 const bool matches = hayLine->searchText(0, hayLine->length(),needleLine, &foundAt, &myMatchLen, casesensitive, false);
01617 if (!matches || (foundAt != 0) || (myMatchLen != static_cast<uint>(needleLine.length()))) {
01618 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": no");
01619 break;
01620 }
01621 FAST_DEBUG("searchText | [" << j << " + " << k << "] line " << j + k << ": yes");
01622 }
01623 }
01624
01625
01626 if ((backwards && (j > forMin)) || (!backwards && (j < forMax)))
01627 {
01628 if (backwards)
01629 {
01630 hayLinesZeroIndex = (hayLinesZeroIndex + numNeedleLines - 1) % numNeedleLines;
01631
01632 KateTextLine::Ptr textLine = m_buffer->plainLine(j - 1);
01633
01634 if (!textLine)
01635 return KTextEditor::Range::invalid();
01636
01637 hayLinesWindow[hayLinesZeroIndex] = textLine;
01638
01639 FAST_DEBUG("searchText | filling slot " << hayLinesZeroIndex << " with line "
01640 << j - 1 << ": " << hayLinesWindow[hayLinesZeroIndex]->string());
01641 }
01642 else
01643 {
01644 hayLinesWindow[hayLinesZeroIndex] = m_buffer->plainLine(j + numNeedleLines);
01645 FAST_DEBUG("searchText | filling slot " << hayLinesZeroIndex << " with line "
01646 << j + numNeedleLines << ": " << hayLinesWindow[hayLinesZeroIndex]->string());
01647 hayLinesZeroIndex = (hayLinesZeroIndex + 1) % numNeedleLines;
01648 }
01649 }
01650 }
01651
01652
01653 return KTextEditor::Range::invalid();
01654 }
01655 else
01656 {
01657
01658 const int minLeft = inputRange.start().column();
01659 const uint maxRight = inputRange.end().column();
01660 const int forMin = inputRange.start().line();
01661 const int forMax = inputRange.end().line();
01662 const int forInit = backwards ? forMax : forMin;
01663 const int forInc = backwards ? -1 : +1;
01664 FAST_DEBUG("searchText | single line " << (backwards ? forMax : forMin) << ".."
01665 << (backwards ? forMin : forMax));
01666 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01667 {
01668 KateTextLine::Ptr textLine = m_buffer->plainLine(j);
01669 if (!textLine)
01670 {
01671 FAST_DEBUG("searchText | line " << j << ": no");
01672 return KTextEditor::Range::invalid();
01673 }
01674
01675 const int offset = (j == forMin) ? minLeft : 0;
01676 const int line_end= (j==forMax) ? maxRight : textLine->length();
01677 uint foundAt, myMatchLen;
01678 FAST_DEBUG("searchText | searching in line line: " << j);
01679 const bool found = textLine->searchText (offset,line_end, text, &foundAt, &myMatchLen, casesensitive, backwards);
01680 if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
01681 {
01682 FAST_DEBUG("searchText | line " << j << ": yes");
01683 return KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
01684 }
01685 else
01686 {
01687 FAST_DEBUG("searchText | line " << j << ": no");
01688 }
01689 }
01690 }
01691 return KTextEditor::Range::invalid();
01692 }
01693
01694
01695
01696
01697 struct TwoViewCursor {
01698 int index;
01699 int openLine;
01700 int openCol;
01701 int closeLine;
01702 int closeCol;
01703
01704
01705
01706 };
01707
01708 struct IndexPair {
01709 int openIndex;
01710 int closeIndex;
01711 };
01712
01713
01714
01715 QVector<KTextEditor::Range> KateDocument::searchRegex(
01716 const KTextEditor::Range & inputRange,
01717 QRegExp ®exp,
01718 bool backwards)
01719 {
01720 FAST_DEBUG("KateDocument::searchRegex( " << inputRange.start().line() << ", "
01721 << inputRange.start().column() << ", " << regexp.pattern() << ", " << backwards << " )");
01722 if (regexp.isEmpty() || !regexp.isValid() || !inputRange.isValid() || (inputRange.start() == inputRange.end()))
01723 {
01724 QVector<KTextEditor::Range> result;
01725 result.append(KTextEditor::Range::invalid());
01726 return result;
01727 }
01728
01729
01730
01731 bool isMultiLine;
01732 QString multiLinePattern = regexp.pattern();
01733
01734
01735 const bool dotMatchesNewline = false;
01736 const int replacements = KateDocument::repairPattern(multiLinePattern, isMultiLine);
01737 if (dotMatchesNewline && (replacements > 0))
01738 {
01739 isMultiLine = true;
01740 }
01741
01742 const int firstLineIndex = inputRange.start().line();
01743 const int minColStart = inputRange.start().column();
01744
01745 if (isMultiLine)
01746 {
01747
01748 QString wholeDocument;
01749 const int inputLineCount = inputRange.end().line() - inputRange.start().line() + 1;
01750 FAST_DEBUG("multi line search (lines " << firstLineIndex << ".." << firstLineIndex + inputLineCount - 1 << ")");
01751
01752
01753 if (firstLineIndex >= m_buffer->count())
01754 {
01755 QVector<KTextEditor::Range> result;
01756 result.append(KTextEditor::Range::invalid());
01757 return result;
01758 }
01759
01760 QVector<int> lineLens (inputLineCount);
01761
01762
01763 KateTextLine::Ptr firstLine = m_buffer->plainLine(firstLineIndex);
01764 if (!firstLine)
01765 {
01766 QVector<KTextEditor::Range> result;
01767 result.append(KTextEditor::Range::invalid());
01768 return result;
01769 }
01770
01771 QString firstLineText = firstLine->string();
01772 const int firstLineLen = firstLineText.length() - minColStart;
01773 wholeDocument.append(firstLineText.right(firstLineLen));
01774 lineLens[0] = firstLineLen;
01775 FAST_DEBUG(" line" << 0 << "has length" << lineLens[0]);
01776
01777
01778 const QString sep("\n");
01779 for (int i = 1; i < inputLineCount; i++)
01780 {
01781 KateTextLine::Ptr textLine = m_buffer->plainLine(firstLineIndex + i);
01782 if (!textLine)
01783 {
01784 QVector<KTextEditor::Range> result;
01785 result.append(KTextEditor::Range::invalid());
01786 return result;
01787 }
01788
01789 QString text = textLine->string();
01790 lineLens[i] = text.length();
01791 wholeDocument.append(sep);
01792 wholeDocument.append(text);
01793 FAST_DEBUG(" line" << i << "has length" << lineLens[i]);
01794 }
01795
01796
01797 regexp.setPattern(multiLinePattern);
01798 const int pos = backwards
01799 ? KateDocument::fixedLastIndexIn(regexp, wholeDocument, -1, QRegExp::CaretAtZero)
01800 : regexp.indexIn(wholeDocument, 0, QRegExp::CaretAtZero);
01801 if (pos == -1)
01802 {
01803
01804 FAST_DEBUG("not found");
01805 {
01806 QVector<KTextEditor::Range> result;
01807 result.append(KTextEditor::Range::invalid());
01808 return result;
01809 }
01810 }
01811
01812 #ifdef FAST_DEBUG_ENABLE
01813 const int matchLen = regexp.matchedLength();
01814 FAST_DEBUG("found at relative pos " << pos << ", length " << matchLen);
01815 #endif
01816
01817
01818
01819 QMap<int, TwoViewCursor *> indicesToCursors;
01820 const int numCaptures = regexp.numCaptures();
01821 QVector<IndexPair> indexPairs(1 + numCaptures);
01822 for (int z = 0; z <= numCaptures; z++)
01823 {
01824 const int openIndex = regexp.pos(z);
01825 IndexPair & pair = indexPairs[z];
01826 if (openIndex == -1)
01827 {
01828
01829 pair.openIndex = -1;
01830 pair.closeIndex = -1;
01831 FAST_DEBUG("capture []");
01832 }
01833 else
01834 {
01835 const int closeIndex = openIndex + regexp.cap(z).length();
01836 pair.openIndex = openIndex;
01837 pair.closeIndex = closeIndex;
01838 FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]");
01839
01840
01841 if (!indicesToCursors.contains(openIndex))
01842 {
01843 TwoViewCursor * twoViewCursor = new TwoViewCursor;
01844 twoViewCursor->index = openIndex;
01845 indicesToCursors.insert(openIndex, twoViewCursor);
01846 FAST_DEBUG(" border index added: " << openIndex);
01847 }
01848 if (!indicesToCursors.contains(closeIndex))
01849 {
01850 TwoViewCursor * twoViewCursor = new TwoViewCursor;
01851 twoViewCursor->index = closeIndex;
01852 indicesToCursors.insert(closeIndex, twoViewCursor);
01853 FAST_DEBUG(" border index added: " << closeIndex);
01854 }
01855 }
01856 }
01857
01858
01859 int curRelLine = 0;
01860 int curRelCol = 0;
01861 int curRelIndex = 0;
01862 QMap<int, TwoViewCursor *>::const_iterator iter = indicesToCursors.constBegin();
01863 while (iter != indicesToCursors.constEnd())
01864 {
01865
01866 const int index = (*iter)->index;
01867 FAST_DEBUG("resolving position" << index);
01868 TwoViewCursor & twoViewCursor = *(*iter);
01869 while (curRelIndex <= index)
01870 {
01871 FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = "
01872 << curRelIndex << "relative, steps more to go" << index - curRelIndex);
01873 const int curRelLineLen = lineLens[curRelLine];
01874 const int curLineRemainder = curRelLineLen - curRelCol;
01875 const int lineFeedIndex = curRelIndex + curLineRemainder;
01876 if (index <= lineFeedIndex) {
01877 if (index == lineFeedIndex) {
01878
01879 FAST_DEBUG(" on line feed");
01880 const int absLine = curRelLine + firstLineIndex;
01881 twoViewCursor.openLine
01882 = twoViewCursor.closeLine
01883 = absLine;
01884 twoViewCursor.openCol
01885 = twoViewCursor.closeCol
01886 = ((curRelLine == 0) ? minColStart : 0) + curRelLineLen;
01887
01888
01889 const int advance = (index - curRelIndex) + 1;
01890 curRelLine++;
01891 curRelCol = 0;
01892 curRelIndex += advance;
01893 } else {
01894
01895 FAST_DEBUG(" before line feed");
01896 const int diff = (index - curRelIndex);
01897 const int absLine = curRelLine + firstLineIndex;
01898 const int absCol = ((curRelLine == 0) ? minColStart : 0) + curRelCol + diff;
01899 twoViewCursor.openLine
01900 = twoViewCursor.closeLine
01901 = absLine;
01902 twoViewCursor.openCol
01903 = twoViewCursor.closeCol
01904 = absCol;
01905
01906
01907 const int advance = diff + 1;
01908 curRelCol += advance;
01909 curRelIndex += advance;
01910 }
01911 FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol
01912 << ") close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")");
01913 }
01914 else
01915 {
01916
01917
01918 FAST_DEBUG(" not on this line");
01919 const int advance = curLineRemainder + 1;
01920 curRelLine++;
01921 curRelCol = 0;
01922 curRelIndex += advance;
01923 }
01924 }
01925
01926 ++iter;
01927 }
01928
01929
01930 QVector<KTextEditor::Range> result(1 + numCaptures);
01931 for (int y = 0; y <= numCaptures; y++)
01932 {
01933 IndexPair & pair = indexPairs[y];
01934 if ((pair.openIndex == -1) || (pair.closeIndex == -1))
01935 {
01936 result[y] = KTextEditor::Range::invalid();
01937 }
01938 else
01939 {
01940 const TwoViewCursor * const openCursors = indicesToCursors[pair.openIndex];
01941 const TwoViewCursor * const closeCursors = indicesToCursors[pair.closeIndex];
01942 const int startLine = openCursors->openLine;
01943 const int startCol = openCursors->openCol;
01944 const int endLine = closeCursors->closeLine;
01945 const int endCol = closeCursors->closeCol;
01946 FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")");
01947 result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol);
01948 }
01949 }
01950
01951
01952 iter = indicesToCursors.constBegin();
01953 while (iter != indicesToCursors.constEnd())
01954 {
01955 TwoViewCursor * const twoViewCursor = *iter;
01956 delete twoViewCursor;
01957 ++iter;
01958 }
01959 return result;
01960 }
01961 else
01962 {
01963
01964 const int minLeft = inputRange.start().column();
01965 const uint maxRight = inputRange.end().column();
01966 const int forMin = inputRange.start().line();
01967 const int forMax = inputRange.end().line();
01968 const int forInit = backwards ? forMax : forMin;
01969 const int forInc = backwards ? -1 : +1;
01970 FAST_DEBUG("single line " << (backwards ? forMax : forMin) << ".."
01971 << (backwards ? forMin : forMax));
01972 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01973 {
01974 KateTextLine::Ptr textLine = m_buffer->plainLine(j);
01975 if (!textLine)
01976 {
01977 FAST_DEBUG("searchText | line " << j << ": no");
01978 QVector<KTextEditor::Range> result;
01979 result.append(KTextEditor::Range::invalid());
01980 return result;
01981 }
01982
01983
01984 const int first = (j == forMin) ? minLeft : 0;
01985 const int afterLast = (j == forMax) ? maxRight : textLine->length();
01986 const QString hay = textLine->string();
01987 bool found = true;
01988 int foundAt;
01989 uint myMatchLen;
01990 if (backwards) {
01991 const int lineLen = textLine->length();
01992 const int offset = afterLast - lineLen - 1;
01993 FAST_DEBUG("lastIndexIn(" << hay << "," << offset << ")");
01994 foundAt = KateDocument::fixedLastIndexIn(regexp, hay, offset);
01995 found = (foundAt != -1) && (foundAt >= first);
01996 } else {
01997 FAST_DEBUG("indexIn(" << hay << "," << first << ")");
01998 foundAt = regexp.indexIn(hay, first);
01999 found = (foundAt != -1);
02000 }
02001 myMatchLen = found ? regexp.matchedLength() : 0;
02002
02003
02004
02005
02006
02007
02008
02009
02010
02011
02012
02013
02014
02015
02016
02017
02018
02019
02020 if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
02021 {
02022 FAST_DEBUG("line " << j << ": yes");
02023
02024
02025 const int numCaptures = regexp.numCaptures();
02026 QVector<KTextEditor::Range> result(1 + numCaptures);
02027 result[0] = KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
02028 FAST_DEBUG("result range " << 0 << ": (" << j << ", " << foundAt << ")..(" << j << ", " << foundAt + myMatchLen << ")");
02029 for (int y = 1; y <= numCaptures; y++)
02030 {
02031 const int openIndex = regexp.pos(y);
02032 if (openIndex == -1)
02033 {
02034 result[y] = KTextEditor::Range::invalid();
02035 FAST_DEBUG("capture []");
02036 }
02037 else
02038 {
02039 const int closeIndex = openIndex + regexp.cap(y).length();
02040 FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")");
02041 result[y] = KTextEditor::Range(j, openIndex, j, closeIndex);
02042 }
02043 }
02044 return result;
02045 }
02046 else
02047 {
02048 FAST_DEBUG("searchText | line " << j << ": no");
02049 }
02050 }
02051 }
02052
02053 QVector<KTextEditor::Range> result;
02054 result.append(KTextEditor::Range::invalid());
02055 return result;
02056 }
02057
02058 QWidget * KateDocument::dialogParent()
02059 {
02060 QWidget *w=widget();
02061
02062 if(!w)
02063 {
02064 w=activeView();
02065
02066 if(!w)
02067 w=QApplication::activeWindow();
02068 }
02069
02070 return w;
02071 }
02072
02073 QVector<KTextEditor::Range> KateDocument::searchText(
02074 const KTextEditor::Range & range,
02075 const QString & pattern,
02076 const KTextEditor::Search::SearchOptions options)
02077 {
02078
02079
02080
02081 QString workPattern(pattern);
02082
02083 KTextEditor::Search::SearchOptions finalOptions(options);
02084 const bool escapeSequences = finalOptions.testFlag(KTextEditor::Search::EscapeSequences);
02085
02086
02087 if (finalOptions.testFlag(KTextEditor::Search::WholeWords))
02088 {
02089
02090 if (escapeSequences)
02091 {
02092 KateDocument::escapePlaintext(workPattern);
02093 }
02094
02095
02096 workPattern = "\\b" + QRegExp::escape(workPattern) + "\\b";
02097
02098
02099 finalOptions |= KTextEditor::Search::Regex;
02100 finalOptions &= ~KTextEditor::Search::SearchOptions(KTextEditor::Search::WholeWords);
02101 }
02102
02103 const bool regexMode = finalOptions.testFlag(KTextEditor::Search::Regex);
02104 const bool caseSensitive = !finalOptions.testFlag(KTextEditor::Search::CaseInsensitive);
02105 const bool backwards = finalOptions.testFlag(KTextEditor::Search::Backwards);
02106
02107 if (regexMode)
02108 {
02109
02110 const Qt::CaseSensitivity caseSensitivity =
02111 caseSensitive
02112 ? Qt::CaseSensitive
02113 : Qt::CaseInsensitive;
02114
02115 QRegExp matcher(workPattern, caseSensitivity);
02116 if (matcher.isValid())
02117 {
02118
02119
02120 return searchRegex(range, matcher, backwards);
02121 }
02122 else
02123 {
02124
02125 QVector<KTextEditor::Range> result;
02126 result.append(KTextEditor::Range::invalid());
02127 return result;
02128 }
02129 }
02130 else
02131 {
02132
02133
02134
02135 if (escapeSequences)
02136 {
02137 KateDocument::escapePlaintext(workPattern);
02138 }
02139
02140
02141 KTextEditor::Range resultRange = searchText(range, workPattern, caseSensitive, backwards);
02142 QVector<KTextEditor::Range> result;
02143 result.append(resultRange);
02144 return result;
02145 }
02146 }
02147
02148
02149
02150 KTextEditor::Search::SearchOptions KateDocument::supportedSearchOptions() const
02151 {
02152 KTextEditor::Search::SearchOptions supported(KTextEditor::Search::Default);
02153 supported |= KTextEditor::Search::Regex;
02154 supported |= KTextEditor::Search::CaseInsensitive;
02155 supported |= KTextEditor::Search::Backwards;
02156
02157 supported |= KTextEditor::Search::EscapeSequences;
02158 supported |= KTextEditor::Search::WholeWords;
02159
02160 return supported;
02161 }
02162
02163
02164
02165 void KateDocument::escapePlaintext(QString & text, QList<ReplacementPart> * parts,
02166 bool replacementGoodies) {
02167
02168 const int inputLen = text.length();
02169 int input = 0;
02170
02171
02172 QString output;
02173 output.reserve(inputLen + 1);
02174
02175 while (input < inputLen)
02176 {
02177 switch (text[input].unicode())
02178 {
02179 case L'\n':
02180 output.append(text[input]);
02181 input++;
02182 break;
02183
02184 case L'\\':
02185 if (input + 1 >= inputLen)
02186 {
02187
02188 output.append(text[input]);
02189 input++;
02190 break;
02191 }
02192
02193 switch (text[input + 1].unicode())
02194 {
02195 case L'0':
02196 if (input + 4 >= inputLen)
02197 {
02198 if (parts == NULL)
02199 {
02200
02201 output.append(text[input + 1]);
02202 }
02203 else
02204 {
02205
02206 ReplacementPart curPart;
02207
02208
02209 if (!output.isEmpty())
02210 {
02211 curPart.type = ReplacementPart::Text;
02212 curPart.text = output;
02213 output.clear();
02214 parts->append(curPart);
02215 curPart.text.clear();
02216 }
02217
02218
02219 curPart.type = ReplacementPart::Reference;
02220 curPart.index = 0;
02221 parts->append(curPart);
02222 }
02223 input += 2;
02224 }
02225 else
02226 {
02227 bool stripAndSkip = false;
02228 const ushort text_2 = text[input + 2].unicode();
02229 if ((text_2 >= L'0') && (text_2 <= L'3'))
02230 {
02231 const ushort text_3 = text[input + 3].unicode();
02232 if ((text_3 >= L'0') && (text_3 <= L'7'))
02233 {
02234 const ushort text_4 = text[input + 4].unicode();
02235 if ((text_4 >= L'0') && (text_4 <= L'7'))
02236 {
02237 int digits[3];
02238 for (int i = 0; i < 3; i++)
02239 {
02240 digits[i] = 7 - (L'7' - text[input + 2 + i].unicode());
02241 }
02242 const int ch = 64 * digits[0] + 8 * digits[1] + digits[2];
02243 output.append(QChar(ch));
02244 input += 5;
02245 }
02246 else
02247 {
02248 stripAndSkip = true;
02249 }
02250 }
02251 else
02252 {
02253 stripAndSkip = true;
02254 }
02255 }
02256 else
02257 {
02258 stripAndSkip = true;
02259 }
02260
02261 if (stripAndSkip)
02262 {
02263 if (parts == NULL)
02264 {
02265
02266 output.append(text[input + 1]);
02267 }
02268 else
02269 {
02270
02271 ReplacementPart curPart;
02272
02273
02274 if (!output.isEmpty())
02275 {
02276 curPart.type = ReplacementPart::Text;
02277 curPart.text = output;
02278 output.clear();
02279 parts->append(curPart);
02280 curPart.text.clear();
02281 }
02282
02283
02284 curPart.type = ReplacementPart::Reference;
02285 curPart.index = 0;
02286 parts->append(curPart);
02287 }
02288 input += 2;
02289 }
02290 }
02291 break;
02292
02293 case L'1':
02294 case L'2':
02295 case L'3':
02296 case L'4':
02297 case L'5':
02298 case L'6':
02299 case L'7':
02300 case L'8':
02301 case L'9':
02302 if (parts == NULL)
02303 {
02304
02305 output.append(text[input + 1]);
02306 }
02307 else
02308 {
02309
02310 ReplacementPart curPart;
02311
02312
02313 if (!output.isEmpty())
02314 {
02315 curPart.type = ReplacementPart::Text;
02316 curPart.text = output;
02317 output.clear();
02318 parts->append(curPart);
02319 curPart.text.clear();
02320 }
02321
02322
02323 curPart.type = ReplacementPart::Reference;
02324 curPart.index = 9 - (L'9' - text[input + 1].unicode());
02325 parts->append(curPart);
02326 }
02327 input += 2;
02328 break;
02329
02330 case L'E':
02331 case L'L':
02332 case L'U':
02333 if ((parts == NULL) || !replacementGoodies) {
02334
02335 output.append(text[input + 1]);
02336 } else {
02337
02338 ReplacementPart curPart;
02339
02340
02341 if (!output.isEmpty())
02342 {
02343 curPart.type = ReplacementPart::Text;
02344 curPart.text = output;
02345 output.clear();
02346 parts->append(curPart);
02347 curPart.text.clear();
02348 }
02349
02350
02351 switch (text[input + 1].unicode()) {
02352 case L'L':
02353 curPart.type = ReplacementPart::LowerCase;
02354 break;
02355
02356 case L'U':
02357 curPart.type = ReplacementPart::UpperCase;
02358 break;
02359
02360 case L'E':
02361 default:
02362 curPart.type = ReplacementPart::KeepCase;
02363
02364 }
02365 parts->append(curPart);
02366 }
02367 input += 2;
02368 break;
02369
02370 case L'#':
02371 if ((parts == NULL) || !replacementGoodies) {
02372
02373 output.append(text[input + 1]);
02374 input += 2;
02375 } else {
02376
02377 ReplacementPart curPart;
02378
02379
02380 if (!output.isEmpty())
02381 {
02382 curPart.type = ReplacementPart::Text;
02383 curPart.text = output;
02384 output.clear();
02385 parts->append(curPart);
02386 curPart.text.clear();
02387 }
02388
02389
02390
02391 int count = 1;
02392 while ((input + count + 1 < inputLen) && (text[input + count + 1].unicode() == L'#')) {
02393 count++;
02394 }
02395 curPart.type = ReplacementPart::Counter;
02396 curPart.index = count;
02397 parts->append(curPart);
02398 input += 1 + count;
02399 }
02400 break;
02401
02402 case L'a':
02403 output.append(QChar(0x07));
02404 input += 2;
02405 break;
02406
02407 case L'f':
02408 output.append(QChar(0x0c));
02409 input += 2;
02410 break;
02411
02412 case L'n':
02413 output.append(QChar(0x0a));
02414 input += 2;
02415 break;
02416
02417 case L'r':
02418 output.append(QChar(0x0d));
02419 input += 2;
02420 break;
02421
02422 case L't':
02423 output.append(QChar(0x09));
02424 input += 2;
02425 break;
02426
02427 case L'v':
02428 output.append(QChar(0x0b));
02429 input += 2;
02430 break;
02431
02432 case L'x':
02433 if (input + 5 >= inputLen)
02434 {
02435
02436 output.append(text[input + 1]);
02437 input += 2;
02438 }
02439 else
02440 {
02441 bool stripAndSkip = false;
02442 const ushort text_2 = text[input + 2].unicode();
02443 if (((text_2 >= L'0') && (text_2 <= L'9'))
02444 || ((text_2 >= L'a') && (text_2 <= L'f'))
02445 || ((text_2 >= L'A') && (text_2 <= L'F')))
02446 {
02447 const ushort text_3 = text[input + 3].unicode();
02448 if (((text_3 >= L'0') && (text_3 <= L'9'))
02449 || ((text_3 >= L'a') && (text_3 <= L'f'))
02450 || ((text_3 >= L'A') && (text_3 <= L'F')))
02451 {
02452 const ushort text_4 = text[input + 4].unicode();
02453 if (((text_4 >= L'0') && (text_4 <= L'9'))
02454 || ((text_4 >= L'a') && (text_4 <= L'f'))
02455 || ((text_4 >= L'A') && (text_4 <= L'F')))
02456 {
02457 const ushort text_5 = text[input + 5].unicode();
02458 if (((text_5 >= L'0') && (text_5 <= L'9'))
02459 || ((text_5 >= L'a') && (text_5 <= L'f'))
02460 || ((text_5 >= L'A') && (text_5 <= L'F')))
02461 {
02462 int digits[4];
02463 for (int i = 0; i < 4; i++)
02464 {
02465 const ushort cur = text[input + 2 + i].unicode();
02466 if ((cur >= L'0') && (cur <= L'9'))
02467 {
02468 digits[i] = 9 - (L'9' - cur);
02469 }
02470 else if ((cur >= L'a') && (cur <= L'f'))
02471 {
02472 digits[i] = 15 - (L'f' - cur);
02473 }
02474 else
02475 {
02476 digits[i] = 15 - (L'F' - cur);
02477 }
02478 }
02479
02480 const int ch = 4096 * digits[0] + 256 * digits[1] + 16 * digits[2] + digits[3];
02481 output.append(QChar(ch));
02482 input += 6;
02483 }
02484 else
02485 {
02486 stripAndSkip = true;
02487 }
02488 }
02489 else
02490 {
02491 stripAndSkip = true;
02492 }
02493 }
02494 else
02495 {
02496 stripAndSkip = true;
02497 }
02498 }
02499
02500 if (stripAndSkip)
02501 {
02502
02503 output.append(text[input + 1]);
02504 input += 2;
02505 }
02506 }
02507 break;
02508
02509 default:
02510
02511 output.append(text[input + 1]);
02512 input += 2;
02513
02514 }
02515 break;
02516
02517 default:
02518 output.append(text[input]);
02519 input++;
02520
02521 }
02522 }
02523
02524 if (parts == NULL)
02525 {
02526
02527 text = output;
02528 }
02529 else
02530 {
02531
02532 if (!output.isEmpty())
02533 {
02534 ReplacementPart curPart;
02535 curPart.type = ReplacementPart::Text;
02536 curPart.text = output;
02537 parts->append(curPart);
02538 }
02539 }
02540 }
02541
02542
02543
02544
02545
02546
02547
02548
02549 int KateDocument::repairPattern(QString & pattern, bool & stillMultiLine)
02550 {
02551 const QString & text = pattern;
02552
02553
02554 const int inputLen = text.length();
02555 int input = 0;
02556
02557
02558 QString output;
02559 output.reserve(2 * inputLen + 1);
02560
02561
02562 stillMultiLine = false;
02563 int replaceCount = 0;
02564 bool insideClass = false;
02565
02566 while (input < inputLen)
02567 {
02568 if (insideClass)
02569 {
02570
02571 switch (text[input].unicode())
02572 {
02573 case L'\\':
02574 switch (text[input + 1].unicode())
02575 {
02576 case L'x':
02577 if (input + 5 < inputLen)
02578 {
02579
02580 output.append(text.mid(input, 6));
02581 input += 6;
02582 } else {
02583
02584 output.append(text.mid(input, 2));
02585 input += 2;
02586 }
02587 stillMultiLine = true;
02588 break;
02589
02590 case L'0':
02591 if (input + 4 < inputLen)
02592 {
02593
02594 output.append(text.mid(input, 5));
02595 input += 5;
02596 } else {
02597
02598 output.append(text.mid(input, 2));
02599 input += 2;
02600 }
02601 stillMultiLine = true;
02602 break;
02603
02604 case L's':
02605
02606 output.append("[ \\t]");
02607 input += 2;
02608 replaceCount++;
02609 break;
02610
02611 case L'n':
02612 stillMultiLine = true;
02613
02614
02615 default:
02616
02617 output.append(text.mid(input, 2));
02618 input += 2;
02619 }
02620 break;
02621
02622 case L']':
02623
02624 insideClass = false;
02625 output.append(text[input]);
02626 input++;
02627 break;
02628
02629 default:
02630
02631 output.append(text[input]);
02632 input++;
02633
02634 }
02635 }
02636 else
02637 {
02638
02639 switch (text[input].unicode())
02640 {
02641 case L'\\':
02642 switch (text[input + 1].unicode())
02643 {
02644 case L'x':
02645 if (input + 5 < inputLen)
02646 {
02647
02648 output.append(text.mid(input, 6));
02649 input += 6;
02650 } else {
02651
02652 output.append(text.mid(input, 2));
02653 input += 2;
02654 }
02655 stillMultiLine = true;
02656 break;
02657
02658 case L'0':
02659 if (input + 4 < inputLen)
02660 {
02661
02662 output.append(text.mid(input, 5));
02663 input += 5;
02664 } else {
02665
02666 output.append(text.mid(input, 2));
02667 input += 2;
02668 }
02669 stillMultiLine = true;
02670 break;
02671
02672 case L's':
02673
02674 output.append("[ \\t]");
02675 input += 2;
02676 replaceCount++;
02677 break;
02678
02679 case L'n':
02680 stillMultiLine = true;
02681
02682
02683 default:
02684
02685 output.append(text.mid(input, 2));
02686 input += 2;
02687 }
02688 break;
02689
02690 case L'.':
02691
02692 output.append("[^\\n]");
02693 input++;
02694 replaceCount++;
02695 break;
02696
02697 case L'[':
02698
02699 insideClass = true;
02700 output.append(text[input]);
02701 input++;
02702 break;
02703
02704 default:
02705
02706 output.append(text[input]);
02707 input++;
02708
02709 }
02710 }
02711 }
02712
02713
02714 pattern = output;
02715 return replaceCount;
02716 }
02717
02718
02719
02720 int KateDocument::fixedLastIndexIn(const QRegExp & matcher, const QString & str,
02721 int offset, QRegExp::CaretMode caretMode) {
02722 int prevPos = -1;
02723 int prevLen = 1;
02724 const int strLen = str.length();
02725 for (;;) {
02726 const int pos = matcher.indexIn(str, prevPos + prevLen, caretMode);
02727 if (pos == -1) {
02728
02729 break;
02730 } else {
02731 const int len = matcher.matchedLength();
02732 if (pos > strLen + offset + 1) {
02733
02734 break;
02735 }
02736
02737 if (pos + len > strLen + offset + 1) {
02738
02739 if (offset == -1) {
02740
02741 break;
02742 }
02743
02744 const QString str2 = str.mid(0, strLen + offset + 1);
02745 const int pos2 = matcher.indexIn(str2, pos, caretMode);
02746 if (pos2 != -1) {
02747
02748 return pos2;
02749 } else {
02750
02751 break;
02752 }
02753 }
02754
02755
02756 prevPos = pos;
02757 prevLen = (len == 0) ? 1 : len;
02758 }
02759 }
02760
02761
02762 if (prevPos != -1) {
02763
02764 matcher.indexIn(str, prevPos, caretMode);
02765 return prevPos;
02766 } else {
02767 return -1;
02768 }
02769 }
02770
02771
02772
02773 bool KateDocument::setMode (const QString &name)
02774 {
02775 updateFileType (name);
02776 return true;
02777 }
02778
02779 QString KateDocument::mode () const
02780 {
02781 return m_fileType;
02782 }
02783
02784 QStringList KateDocument::modes () const
02785 {
02786 QStringList m;
02787
02788 const QList<KateFileType *> &modeList = KateGlobal::self()->modeManager()->list();
02789 for (int i = 0; i < modeList.size(); ++i)
02790 m << modeList[i]->name;
02791
02792 return m;
02793 }
02794
02795 bool KateDocument::setHighlightingMode (const QString &name)
02796 {
02797 m_buffer->setHighlight (KateHlManager::self()->nameFind(name));
02798 return true;
02799 }
02800
02801 QString KateDocument::highlightingMode () const
02802 {
02803 return highlight()->name ();
02804 }
02805
02806 QStringList KateDocument::highlightingModes () const
02807 {
02808 QStringList hls;
02809
02810 for (int i = 0; i < KateHlManager::self()->highlights(); ++i)
02811 hls << KateHlManager::self()->hlName (i);
02812
02813 return hls;
02814 }
02815
02816 QString KateDocument::highlightingModeSection( int index ) const
02817 {
02818 return KateHlManager::self()->hlSection( index );
02819 }
02820
02821 QString KateDocument::modeSection( int index ) const
02822 {
02823 return KateGlobal::self()->modeManager()->list()[ index ]->section;
02824 }
02825
02826 void KateDocument::bufferHlChanged ()
02827 {
02828
02829 makeAttribs(false);
02830
02831
02832 m_indenter.checkRequiredStyle();
02833
02834 emit highlightingModeChanged(this);
02835 }
02836
02837 void KateDocument::setDontChangeHlOnSave()
02838 {
02839 hlSetByUser = true;
02840 }
02841
02842
02843
02844 void KateDocument::readSessionConfig(const KConfigGroup &kconfig)
02845 {
02846
02847 KUrl url (kconfig.readEntry("URL"));
02848
02849
02850 QString tmpenc=kconfig.readEntry("Encoding");
02851 if (!tmpenc.isEmpty() && (tmpenc != encoding()))
02852 setEncoding(tmpenc);
02853
02854
02855 if (!url.isEmpty() && url.isValid())
02856 openUrl (url);
02857 else completed();
02858
02859
02860 updateFileType (kconfig.readEntry("Mode", "Normal"));
02861
02862
02863 m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig.readEntry("Highlighting")));
02864
02865
02866 config()->setIndentationMode( kconfig.readEntry("Indentation Mode", config()->indentationMode() ) );
02867
02868
02869 QList<int> marks = kconfig.readEntry("Bookmarks", QList<int>());
02870 for( int i = 0; i < marks.count(); i++ )
02871 addMark( marks[i], KateDocument::markType01 );
02872 }
02873
02874 void KateDocument::writeSessionConfig(KConfigGroup &kconfig)
02875 {
02876 if ( this->url().isLocalFile() ) {
02877 const QString path = this->url().toLocalFile();
02878 if ( KGlobal::dirs()->relativeLocation( "tmp", path ) != path ) {
02879 return;
02880 }
02881 }
02882
02883 kconfig.writeEntry("URL", this->url().prettyUrl() );
02884
02885
02886 kconfig.writeEntry("Encoding",encoding());
02887
02888
02889 kconfig.writeEntry("Mode", m_fileType);
02890
02891
02892 kconfig.writeEntry("Highlighting", highlight()->name());
02893
02894
02895 kconfig.writeEntry("Indentation Mode", config()->indentationMode() );
02896
02897
02898 QList<int> marks;
02899 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
02900 if (i.value()->type & KTextEditor::MarkInterface::markType01)
02901 marks << i.value()->line;
02902
02903 kconfig.writeEntry( "Bookmarks", marks );
02904 }
02905
02906 uint KateDocument::mark( int line )
02907 {
02908 if( !m_marks.value(line) )
02909 return 0;
02910
02911 return m_marks[line]->type;
02912 }
02913
02914 void KateDocument::setMark( int line, uint markType )
02915 {
02916 clearMark( line );
02917 addMark( line, markType );
02918 }
02919
02920 void KateDocument::clearMark( int line )
02921 {
02922 if( line > lastLine() )
02923 return;
02924
02925 if( !m_marks.value(line) )
02926 return;
02927
02928 KTextEditor::Mark* mark = m_marks.take( line );
02929 emit markChanged( this, *mark, MarkRemoved );
02930 emit marksChanged( this );
02931 delete mark;
02932 tagLines( line, line );
02933 repaintViews(true);
02934 }
02935
02936 void KateDocument::addMark( int line, uint markType )
02937 {
02938 if( line > lastLine())
02939 return;
02940
02941 if( markType == 0 )
02942 return;
02943
02944 if( m_marks.value(line) ) {
02945 KTextEditor::Mark* mark = m_marks[line];
02946
02947
02948 markType &= ~mark->type;
02949
02950 if( markType == 0 )
02951 return;
02952
02953
02954 mark->type |= markType;
02955 } else {
02956 KTextEditor::Mark *mark = new KTextEditor::Mark;
02957 mark->line = line;
02958 mark->type = markType;
02959 m_marks.insert( line, mark );
02960 }
02961
02962
02963 KTextEditor::Mark temp;
02964 temp.line = line;
02965 temp.type = markType;
02966 emit markChanged( this, temp, MarkAdded );
02967
02968 emit marksChanged( this );
02969 tagLines( line, line );
02970 repaintViews(true);
02971 }
02972
02973 void KateDocument::removeMark( int line, uint markType )
02974 {
02975 if( line > lastLine() )
02976 return;
02977
02978 if( !m_marks.value(line) )
02979 return;
02980
02981 KTextEditor::Mark* mark = m_marks[line];
02982
02983
02984 markType &= mark->type;
02985
02986 if( markType == 0 )
02987 return;
02988
02989
02990 mark->type &= ~markType;
02991
02992
02993 KTextEditor::Mark temp;
02994 temp.line = line;
02995 temp.type = markType;
02996 emit markChanged( this, temp, MarkRemoved );
02997
02998 if( mark->type == 0 )
02999 m_marks.remove( line );
03000
03001 emit marksChanged( this );
03002 tagLines( line, line );
03003 repaintViews(true);
03004 }
03005
03006 const QHash<int, KTextEditor::Mark*> &KateDocument::marks()
03007 {
03008 return m_marks;
03009 }
03010
03011 void KateDocument::clearMarks()
03012 {
03013 while (!m_marks.isEmpty())
03014 {
03015 QHash<int, KTextEditor::Mark*>::iterator it = m_marks.begin();
03016 KTextEditor::Mark mark = *it.value();
03017 delete it.value();
03018 m_marks.erase (it);
03019
03020 emit markChanged( this, mark, MarkRemoved );
03021 tagLines( mark.line, mark.line );
03022 }
03023
03024 m_marks.clear();
03025
03026 emit marksChanged( this );
03027 repaintViews(true);
03028 }
03029
03030 void KateDocument::setMarkPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
03031 {
03032 m_markPixmaps.insert( type, pixmap );
03033 }
03034
03035 void KateDocument::setMarkDescription( MarkInterface::MarkTypes type, const QString& description )
03036 {
03037 m_markDescriptions.insert( type, description );
03038 }
03039
03040 QPixmap KateDocument::markPixmap( MarkInterface::MarkTypes type ) const
03041 {
03042 return m_markPixmaps.contains(type) ?
03043 m_markPixmaps[type] : QPixmap();
03044 }
03045
03046 QColor KateDocument::markColor( MarkInterface::MarkTypes type ) const
03047 {
03048 uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
03049 if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
03050 return KateRendererConfig::global()->lineMarkerColor(type);
03051 } else {
03052 return QColor();
03053 }
03054 }
03055
03056 QString KateDocument::markDescription( MarkInterface::MarkTypes type ) const
03057 {
03058 return m_markDescriptions.contains(type) ?
03059 m_markDescriptions[type] : QString();
03060 }
03061
03062 void KateDocument::setEditableMarks( uint markMask )
03063 {
03064 m_editableMarks = markMask;
03065 }
03066
03067 uint KateDocument::editableMarks() const
03068 {
03069 return m_editableMarks;
03070 }
03071
03072
03073
03074 bool KateDocument::printDialog ()
03075 {
03076 return KatePrinter::print (this);
03077 }
03078
03079 bool KateDocument::print ()
03080 {
03081 return KatePrinter::print (this);
03082 }
03083
03084
03085
03086 QString KateDocument::mimeType()
03087 {
03088 KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
03089
03090
03091 if ( ! this->url().isEmpty() )
03092 result = KMimeType::findByUrl( this->url() );
03093
03094 else if ( this->url().isEmpty() || ! this->url().isLocalFile() )
03095 result = mimeTypeForContent();
03096
03097 return result->name();
03098 }
03099
03100 KMimeType::Ptr KateDocument::mimeTypeForContent()
03101 {
03102 QByteArray buf (1024,'\0');
03103 uint bufpos = 0;
03104
03105 for (int i=0; i < lines(); ++i)
03106 {
03107 QString line = this->line( i );
03108 uint len = line.length() + 1;
03109
03110 if (bufpos + len > 1024)
03111 len = 1024 - bufpos;
03112
03113 QString ld (line + QChar::fromAscii('\n'));
03114 buf.replace(bufpos,len,ld.toLatin1());
03115
03116 bufpos += len;
03117
03118 if (bufpos >= 1024)
03119 break;
03120 }
03121 buf.resize( bufpos );
03122
03123 int accuracy = 0;
03124 KMimeType::Ptr mt = KMimeType::findByContent(buf, &accuracy);
03125 return mt ? mt : KMimeType::defaultMimeTypePtr();
03126 }
03127
03128
03129
03130
03131 bool KateDocument::openFile()
03132 {
03133
03134 setOpeningError(false);
03135
03136
03137 activateDirWatch ();
03138
03139
03140
03141
03142 QString mimeType = arguments().mimeType();
03143 int pos = mimeType.indexOf(';');
03144 if (pos != -1)
03145 setEncoding (mimeType.mid(pos+1));
03146
03147
03148 emit KTextEditor::Document::textRemoved(this, documentRange());
03149 history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03150
03151 bool success = m_buffer->openFile (localFilePath());
03152
03153 emit KTextEditor::Document::textInserted(this, documentRange());
03154 history()->doEdit( new KateEditInfo(Kate::OpenFileEdit, KTextEditor::Range(0,0,0,0), QStringList(), documentRange(), QStringList()) );
03155
03156
03157
03158
03159 if (success)
03160 {
03161
03162 updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03163
03164
03165 readDirConfig ();
03166
03167
03168 readVariables();
03169
03170
03171 createDigest( m_digest );
03172
03173 if (!m_postLoadFilterChecks.isEmpty())
03174 {
03175 LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03176 foreach(const QString& checkplugin, m_postLoadFilterChecks)
03177 {
03178 lscps->postLoadFilter(checkplugin,this);
03179 }
03180 }
03181 }
03182
03183
03184 emit textChanged (this);
03185
03186
03187
03188
03189 foreach (KateView * view, m_views)
03190 {
03191
03192 view->setCursorPosition(KTextEditor::Cursor());
03193 view->updateView(true);
03194 }
03195
03196 if (!m_reloading)
03197 {
03198
03199
03200
03201 emit documentUrlChanged (this);
03202
03203
03204
03205
03206 setDocName (QString());
03207 }
03208
03209
03210
03211 if (m_modOnHd)
03212 {
03213 m_modOnHd = false;
03214 m_modOnHdReason = OnDiskUnmodified;
03215 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03216 }
03217
03218
03219
03220
03221 QWidget *parentWidget(dialogParent());
03222
03223 if (!suppressOpeningErrorDialogs())
03224 {
03225 if (!success)
03226 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()));
03227 }
03228
03229 if (!success) {
03230 setOpeningError(true);
03231 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()));
03232 }
03233
03234
03235 if (m_buffer->binary())
03236 {
03237
03238 setReadWrite( false );
03239
03240 if(!suppressOpeningErrorDialogs())
03241 KMessageBox::information (parentWidget
03242 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl())
03243 , i18n ("Binary File Opened")
03244 , "Binary File Opened Warning");
03245
03246 setOpeningError(true);
03247 setOpeningErrorMessage(i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl()));
03248 }
03249
03250
03251
03252 else if (m_buffer->brokenUTF8())
03253 {
03254
03255 setReadWrite( false );
03256
03257 if (!suppressOpeningErrorDialogs())
03258 KMessageBox::information (parentWidget
03259 , i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03260 " It is set to read-only mode, as saving might destroy its content."
03261 " 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())
03262 , i18n ("Broken UTF-8 File Opened")
03263 , "Broken UTF-8 File Opened Warning");
03264 setOpeningError(true);
03265 setOpeningErrorMessage(i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03266 " It is set to read-only mode, as saving might destroy its content."
03267 " 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()));
03268 }
03269
03270
03271
03272
03273 return success;
03274 }
03275
03276 bool KateDocument::saveFile()
03277 {
03278 QWidget *parentWidget(dialogParent());
03279
03280
03281
03282
03283 if (m_buffer->binary() && (KMessageBox::warningContinueCancel (parentWidget
03284 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", url().pathOrUrl())
03285 , i18n ("Trying to Save Binary File")
03286 , KGuiItem(i18n("Save Nevertheless"))
03287 , KStandardGuiItem::cancel(), "Binary File Save Warning") != KMessageBox::Continue))
03288 return false;
03289
03290
03291 if ( !url().isEmpty() )
03292 {
03293 if (s_fileChangedDialogsActivated && m_modOnHd)
03294 {
03295 QString str = reasonedMOHString() + "\n\n";
03296
03297 if (!isModified())
03298 {
03299 if (KMessageBox::warningContinueCancel(parentWidget,
03300 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)
03301 return false;
03302 }
03303 else
03304 {
03305 if (KMessageBox::warningContinueCancel(parentWidget,
03306 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)
03307 return false;
03308 }
03309 }
03310 }
03311
03312
03313
03314
03315 if (!m_buffer->canEncode ()
03316 && (KMessageBox::warningContinueCancel(parentWidget,
03317 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))
03318 {
03319 return false;
03320 }
03321
03322
03323
03324
03325
03326
03327 bool l ( url().isLocalFile() );
03328
03329
03330 if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
03331 || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
03332 {
03333 KUrl u( url() );
03334 u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
03335
03336 kDebug( 13020 ) << "backup src file name: " << url();
03337 kDebug( 13020 ) << "backup dst file name: " << u;
03338
03339
03340 bool backupSuccess = false;
03341
03342
03343 if (u.isLocalFile ())
03344 {
03345 if (QFile::exists (url().toLocalFile ()))
03346 {
03347
03348 QFile backupFile (u.toLocalFile ());
03349 if (backupFile.exists()) backupFile.remove ();
03350
03351 backupSuccess = QFile::copy (url().toLocalFile (), u.toLocalFile ());
03352 }
03353 else
03354 backupSuccess = true;
03355 }
03356 else
03357 {
03358 QWidget *w = widget ();
03359 if (!w && !m_views.isEmpty ())
03360 w = m_views.first();
03361
03362
03363 mode_t perms = 0600;
03364 KIO::UDSEntry fentry;
03365 if (KIO::NetAccess::stat (url(), fentry, kapp->activeWindow()))
03366 {
03367 kDebug( 13020 ) << "stating succesfull: " << url();
03368 KFileItem item (fentry, url());
03369 perms = item.permissions();
03370
03371
03372 KIO::FileCopyJob *job = KIO::file_copy ( url(), u, -1, KIO::Overwrite );
03373 backupSuccess = KIO::NetAccess::synchronousRun(job, w);
03374 }
03375 else
03376 backupSuccess = true;
03377 }
03378
03379
03380 if (!backupSuccess && (KMessageBox::warningContinueCancel (parentWidget
03381 , i18n ("For file %1 no backup copy could be created before saving."
03382 " If an error occurs while saving, you might lose the data of this file."
03383 " 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())
03384 , i18n ("Failed to create backup copy.")
03385 , KGuiItem(i18n("Try to Save Nevertheless"))
03386 , KStandardGuiItem::cancel(), "Backup Failed Warning") != KMessageBox::Continue))
03387 {
03388 return false;
03389 }
03390 }
03391
03392
03393 updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03394
03395 if (!m_preSavePostDialogFilterChecks.isEmpty())
03396 {
03397 LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03398 foreach(const QString& checkplugin, m_preSavePostDialogFilterChecks)
03399 {
03400 if (lscps->preSavePostDialogFilterCheck(checkplugin,this,parentWidget)==false)
03401 return false;
03402 }
03403 }
03404
03405
03406 QString oldPath = m_dirWatchFile;
03407
03408
03409 deactivateDirWatch ();
03410
03411
03412
03413
03414 if (!m_buffer->saveFile (localFilePath()))
03415 {
03416
03417 activateDirWatch (oldPath);
03418
03419 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()));
03420
03421 return false;
03422 }
03423
03424
03425 createDigest( m_digest );
03426
03427
03428 activateDirWatch ();
03429
03430
03431
03432
03433
03434 if ( url().isLocalFile())
03435 {
03436 QFileInfo fo (oldPath), fn (m_dirWatchFile);
03437
03438 if (fo.path() != fn.path())
03439 readDirConfig();
03440 }
03441
03442
03443 readVariables();
03444
03445
03446
03447
03448 if (m_modOnHd)
03449 {
03450 m_modOnHd = false;
03451 m_modOnHdReason = OnDiskUnmodified;
03452 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03453 }
03454
03455
03456 setDocName( QString() );
03457
03458
03459 emit documentUrlChanged (this);
03460
03461 m_savingToUrl=true;
03462
03463
03464
03465 setUndoDontMerge(true);
03466
03467
03468
03469
03470 return true;
03471 }
03472
03473 void KateDocument::readDirConfig ()
03474 {
03475 int depth = config()->searchDirConfigDepth ();
03476
03477 if (this->url().isLocalFile() && (depth > -1))
03478 {
03479 QString currentDir = QFileInfo (localFilePath()).absolutePath();
03480
03481
03482 while (depth > -1)
03483 {
03484
03485
03486
03487 QFile f (currentDir + "/.kateconfig");
03488
03489 if (f.open (QIODevice::ReadOnly))
03490 {
03491 QTextStream stream (&f);
03492
03493 uint linesRead = 0;
03494 QString line = stream.readLine();
03495 while ((linesRead < 32) && !line.isNull())
03496 {
03497 readVariableLine( line );
03498
03499 line = stream.readLine();
03500
03501 linesRead++;
03502 }
03503
03504 break;
03505 }
03506
03507 QString newDir = QFileInfo (currentDir).absolutePath();
03508
03509
03510 if (currentDir == newDir)
03511 break;
03512
03513 currentDir = newDir;
03514 --depth;
03515 }
03516 }
03517 }
03518
03519 void KateDocument::activateDirWatch (const QString &useFileName)
03520 {
03521 QString fileToUse = useFileName;
03522 if (fileToUse.isEmpty())
03523 fileToUse = localFilePath();
03524
03525
03526 if (fileToUse == m_dirWatchFile)
03527 return;
03528
03529
03530 deactivateDirWatch ();
03531
03532
03533 if (url().isLocalFile() && !fileToUse.isEmpty())
03534 {
03535 KateGlobal::self()->dirWatch ()->addFile (fileToUse);
03536 m_dirWatchFile = fileToUse;
03537 }
03538 }
03539
03540 void KateDocument::deactivateDirWatch ()
03541 {
03542 if (!m_dirWatchFile.isEmpty())
03543 KateGlobal::self()->dirWatch ()->removeFile (m_dirWatchFile);
03544
03545 m_dirWatchFile.clear();
03546 }
03547
03548 bool KateDocument::closeUrl()
03549 {
03550
03551
03552
03553 if ( !m_reloading && !url().isEmpty() )
03554 {
03555 if (s_fileChangedDialogsActivated && m_modOnHd)
03556 {
03557 QWidget *parentWidget(dialogParent());
03558
03559 if (!(KMessageBox::warningContinueCancel(
03560 parentWidget,
03561 reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
03562 i18n("Possible Data Loss"), KGuiItem(i18n("Close Nevertheless")), KStandardGuiItem::cancel(),
03563 QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
03564 return false;
03565 }
03566 }
03567
03568
03569
03570
03571 if (!KParts::ReadWritePart::closeUrl ())
03572 return false;
03573
03574
03575 if (!m_reloading)
03576 emit aboutToClose(this);
03577
03578
03579 deactivateDirWatch ();
03580
03581
03582
03583
03584 setUrl(KUrl());
03585 setLocalFilePath(QString());
03586
03587
03588 if (m_modOnHd)
03589 {
03590 m_modOnHd = false;
03591 m_modOnHdReason = OnDiskUnmodified;
03592 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03593 }
03594
03595 emit KTextEditor::Document::textRemoved(this, documentRange());
03596
03597 {
03598 QMutexLocker l(smartMutex());
03599 history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03600
03601
03602 m_buffer->clear();
03603
03604
03605 clearMarks ();
03606
03607
03608 m_undoManager->clearUndo();
03609 m_undoManager->clearRedo();
03610 }
03611
03612
03613 setModified(false);
03614
03615
03616 m_buffer->setHighlight(0);
03617
03618
03619 foreach (KateView * view, m_views )
03620 {
03621 view->clearSelection();
03622 view->clear();
03623 }
03624
03625 if (!m_reloading)
03626 {
03627
03628
03629 emit documentUrlChanged (this);
03630
03631
03632 setDocName (QString());
03633 }
03634
03635 return true;
03636 }
03637
03638 void KateDocument::setReadWrite( bool rw )
03639 {
03640 if (isReadWrite() != rw)
03641 {
03642 KParts::ReadWritePart::setReadWrite (rw);
03643
03644 foreach( KateView* view, m_views)
03645 {
03646 view->slotUpdateUndo();
03647 view->slotReadWriteChanged ();
03648 }
03649 }
03650 }
03651
03652 void KateDocument::setModified(bool m) {
03653
03654 if (isModified() != m) {
03655 KParts::ReadWritePart::setModified (m);
03656
03657 foreach( KateView* view,m_views)
03658 {
03659 view->slotUpdateUndo();
03660 }
03661
03662 emit modifiedChanged (this);
03663 }
03664
03665 m_undoManager->setModified (m);
03666 }
03667
03668
03669
03670
03671 void KateDocument::makeAttribs(bool needInvalidate)
03672 {
03673 foreach(KateView *view,m_views)
03674 view->renderer()->updateAttributes ();
03675
03676 if (needInvalidate)
03677 m_buffer->invalidateHighlighting();
03678
03679 foreach(KateView *view,m_views)
03680 {
03681 view->tagAll();
03682 view->updateView (true);
03683 }
03684 }
03685
03686
03687 void KateDocument::internalHlChanged()
03688 {
03689 makeAttribs();
03690 }
03691
03692 void KateDocument::addView(KTextEditor::View *view) {
03693 if (!view)
03694 return;
03695
03696 m_views.append( static_cast<KateView*>(view) );
03697 m_textEditViews.append( view );
03698
03699 foreach(KTextEditor::SmartRange* highlight, m_documentHighlights) {
03700 Q_ASSERT(dynamic_cast<KateView*>(view));
03701 static_cast<KateView*>(view)->addExternalHighlight(highlight, m_documentDynamicHighlights.contains(highlight));
03702 }
03703
03704
03705 if (!m_fileType.isEmpty())
03706 readVariableLine(KateGlobal::self()->modeManager()->fileType(m_fileType).varLine, true);
03707
03708
03709 readVariables (true);
03710
03711 setActiveView(view);
03712 }
03713
03714 void KateDocument::removeView(KTextEditor::View *view) {
03715 if (!view)
03716 return;
03717
03718 if (activeView() == view)
03719 setActiveView(0L);
03720
03721 m_views.removeAll( (KateView *) view );
03722 m_textEditViews.removeAll( view );
03723 }
03724
03725 void KateDocument::setActiveView(KTextEditor::View* view)
03726 {
03727 if ( m_activeView == view ) return;
03728
03729 if (m_activeView) {
03730 disconnect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), this, SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03731 }
03732
03733 m_activeView = (KateView*)view;
03734
03735 if (m_activeView) {
03736 connect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03737 }
03738 }
03739
03740 bool KateDocument::ownedView(KateView *view) {
03741
03742 return (m_views.contains(view));
03743 }
03744
03745 uint KateDocument::toVirtualColumn( const KTextEditor::Cursor& cursor )
03746 {
03747 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03748
03749 if (textLine)
03750 return textLine->toVirtualColumn(cursor.column(), config()->tabWidth());
03751 else
03752 return 0;
03753 }
03754
03755 bool KateDocument::typeChars ( KateView *view, const QString &chars )
03756 {
03757
03758 QMutexLocker l(smartMutex());
03759
03760 KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorPosition().line ());
03761
03762 if (!textLine)
03763 return false;
03764
03765 bool bracketInserted = false;
03766 QString buf;
03767 QChar c;
03768 for( int z = 0; z < chars.length(); z++ )
03769 {
03770 QChar ch = c = chars[z];
03771
03772 if (ch.isPrint() || ch == QChar::fromAscii('\t'))
03773 {
03774 buf.append (ch);
03775
03776 if (!bracketInserted && (config()->configFlags() & KateDocumentConfig::cfAutoBrackets))
03777 {
03778 QChar end_ch;
03779 bool complete = true;
03780 QChar prevChar = textLine->at(view->cursorPosition().column()-1);
03781 QChar nextChar = textLine->at(view->cursorPosition().column());
03782 switch(ch.toAscii()) {
03783 case '(': end_ch = ')'; break;
03784 case '[': end_ch = ']'; break;
03785 case '{': end_ch = '}'; break;
03786 case '\'':end_ch = '\'';break;
03787 case '"': end_ch = '"'; break;
03788 default: complete = false;
03789 }
03790 if (complete)
03791 {
03792 if (view->selection())
03793 {
03794 buf.append (view->selectionText());
03795 buf.append (end_ch);
03796 bracketInserted = true;
03797 }
03798 else
03799 {
03800 if ( ( (ch == '\'' || ch == '"') &&
03801 (prevChar.isLetterOrNumber() || prevChar == ch) )
03802 || nextChar.isLetterOrNumber()
03803 || (nextChar == end_ch && prevChar != ch) )
03804 {
03805 kDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
03806 }
03807 else
03808 {
03809 buf.append (end_ch);
03810 bracketInserted = true;
03811 }
03812 }
03813 }
03814 }
03815 }
03816 }
03817
03818 if (buf.isEmpty())
03819 return false;
03820
03821 l.unlock();
03822
03823 editStart ();
03824
03825 if (!view->config()->persistentSelection() && view->selection() )
03826 view->removeSelectedText();
03827
03828 KTextEditor::Cursor oldCur (view->cursorPosition());
03829
03830 if (config()->configFlags() & KateDocumentConfig::cfOvr)
03831 removeText(KTextEditor::Range(view->cursorPosition(), qMin(buf.length(), textLine->length() - view->cursorPosition().column())));
03832
03833 insertText(view->cursorPosition(), buf);
03834 if (bracketInserted)
03835 view->