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 KateTextLine::Ptr textLine = m_buffer->plainLine(j + numNeedleLines);
01645
01646 if (!textLine)
01647 return KTextEditor::Range::invalid();
01648
01649 hayLinesWindow[hayLinesZeroIndex] = textLine;
01650 FAST_DEBUG("searchText | filling slot " << hayLinesZeroIndex << " with line "
01651 << j + numNeedleLines << ": " << hayLinesWindow[hayLinesZeroIndex]->string());
01652 hayLinesZeroIndex = (hayLinesZeroIndex + 1) % numNeedleLines;
01653 }
01654 }
01655 }
01656
01657
01658 return KTextEditor::Range::invalid();
01659 }
01660 else
01661 {
01662
01663 const int minLeft = inputRange.start().column();
01664 const uint maxRight = inputRange.end().column();
01665 const int forMin = inputRange.start().line();
01666 const int forMax = inputRange.end().line();
01667 const int forInit = backwards ? forMax : forMin;
01668 const int forInc = backwards ? -1 : +1;
01669 FAST_DEBUG("searchText | single line " << (backwards ? forMax : forMin) << ".."
01670 << (backwards ? forMin : forMax));
01671 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01672 {
01673 KateTextLine::Ptr textLine = m_buffer->plainLine(j);
01674 if (!textLine)
01675 {
01676 FAST_DEBUG("searchText | line " << j << ": no");
01677 return KTextEditor::Range::invalid();
01678 }
01679
01680 const int offset = (j == forMin) ? minLeft : 0;
01681 const int line_end= (j==forMax) ? maxRight : textLine->length();
01682 uint foundAt, myMatchLen;
01683 FAST_DEBUG("searchText | searching in line line: " << j);
01684 const bool found = textLine->searchText (offset,line_end, text, &foundAt, &myMatchLen, casesensitive, backwards);
01685 if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
01686 {
01687 FAST_DEBUG("searchText | line " << j << ": yes");
01688 return KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
01689 }
01690 else
01691 {
01692 FAST_DEBUG("searchText | line " << j << ": no");
01693 }
01694 }
01695 }
01696 return KTextEditor::Range::invalid();
01697 }
01698
01699
01700
01701
01702 struct TwoViewCursor {
01703 int index;
01704 int openLine;
01705 int openCol;
01706 int closeLine;
01707 int closeCol;
01708
01709
01710
01711 };
01712
01713 struct IndexPair {
01714 int openIndex;
01715 int closeIndex;
01716 };
01717
01718
01719
01720 QVector<KTextEditor::Range> KateDocument::searchRegex(
01721 const KTextEditor::Range & inputRange,
01722 QRegExp ®exp,
01723 bool backwards)
01724 {
01725 FAST_DEBUG("KateDocument::searchRegex( " << inputRange.start().line() << ", "
01726 << inputRange.start().column() << ", " << regexp.pattern() << ", " << backwards << " )");
01727 if (regexp.isEmpty() || !regexp.isValid() || !inputRange.isValid() || (inputRange.start() == inputRange.end()))
01728 {
01729 QVector<KTextEditor::Range> result;
01730 result.append(KTextEditor::Range::invalid());
01731 return result;
01732 }
01733
01734
01735
01736 bool isMultiLine;
01737 QString multiLinePattern = regexp.pattern();
01738
01739
01740 const bool dotMatchesNewline = false;
01741 const int replacements = KateDocument::repairPattern(multiLinePattern, isMultiLine);
01742 if (dotMatchesNewline && (replacements > 0))
01743 {
01744 isMultiLine = true;
01745 }
01746
01747 const int firstLineIndex = inputRange.start().line();
01748 const int minColStart = inputRange.start().column();
01749
01750 if (isMultiLine)
01751 {
01752
01753 QString wholeDocument;
01754 const int inputLineCount = inputRange.end().line() - inputRange.start().line() + 1;
01755 FAST_DEBUG("multi line search (lines " << firstLineIndex << ".." << firstLineIndex + inputLineCount - 1 << ")");
01756
01757
01758 if (firstLineIndex >= m_buffer->count())
01759 {
01760 QVector<KTextEditor::Range> result;
01761 result.append(KTextEditor::Range::invalid());
01762 return result;
01763 }
01764
01765 QVector<int> lineLens (inputLineCount);
01766
01767
01768 KateTextLine::Ptr firstLine = m_buffer->plainLine(firstLineIndex);
01769 if (!firstLine)
01770 {
01771 QVector<KTextEditor::Range> result;
01772 result.append(KTextEditor::Range::invalid());
01773 return result;
01774 }
01775
01776 QString firstLineText = firstLine->string();
01777 const int firstLineLen = firstLineText.length() - minColStart;
01778 wholeDocument.append(firstLineText.right(firstLineLen));
01779 lineLens[0] = firstLineLen;
01780 FAST_DEBUG(" line" << 0 << "has length" << lineLens[0]);
01781
01782
01783 const QString sep("\n");
01784 for (int i = 1; i < inputLineCount; i++)
01785 {
01786 KateTextLine::Ptr textLine = m_buffer->plainLine(firstLineIndex + i);
01787 if (!textLine)
01788 {
01789 QVector<KTextEditor::Range> result;
01790 result.append(KTextEditor::Range::invalid());
01791 return result;
01792 }
01793
01794 QString text = textLine->string();
01795 lineLens[i] = text.length();
01796 wholeDocument.append(sep);
01797 wholeDocument.append(text);
01798 FAST_DEBUG(" line" << i << "has length" << lineLens[i]);
01799 }
01800
01801
01802 regexp.setPattern(multiLinePattern);
01803 const int pos = backwards
01804 ? KateDocument::fixedLastIndexIn(regexp, wholeDocument, -1, QRegExp::CaretAtZero)
01805 : regexp.indexIn(wholeDocument, 0, QRegExp::CaretAtZero);
01806 if (pos == -1)
01807 {
01808
01809 FAST_DEBUG("not found");
01810 {
01811 QVector<KTextEditor::Range> result;
01812 result.append(KTextEditor::Range::invalid());
01813 return result;
01814 }
01815 }
01816
01817 #ifdef FAST_DEBUG_ENABLE
01818 const int matchLen = regexp.matchedLength();
01819 FAST_DEBUG("found at relative pos " << pos << ", length " << matchLen);
01820 #endif
01821
01822
01823
01824 QMap<int, TwoViewCursor *> indicesToCursors;
01825 const int numCaptures = regexp.numCaptures();
01826 QVector<IndexPair> indexPairs(1 + numCaptures);
01827 for (int z = 0; z <= numCaptures; z++)
01828 {
01829 const int openIndex = regexp.pos(z);
01830 IndexPair & pair = indexPairs[z];
01831 if (openIndex == -1)
01832 {
01833
01834 pair.openIndex = -1;
01835 pair.closeIndex = -1;
01836 FAST_DEBUG("capture []");
01837 }
01838 else
01839 {
01840 const int closeIndex = openIndex + regexp.cap(z).length();
01841 pair.openIndex = openIndex;
01842 pair.closeIndex = closeIndex;
01843 FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]");
01844
01845
01846 if (!indicesToCursors.contains(openIndex))
01847 {
01848 TwoViewCursor * twoViewCursor = new TwoViewCursor;
01849 twoViewCursor->index = openIndex;
01850 indicesToCursors.insert(openIndex, twoViewCursor);
01851 FAST_DEBUG(" border index added: " << openIndex);
01852 }
01853 if (!indicesToCursors.contains(closeIndex))
01854 {
01855 TwoViewCursor * twoViewCursor = new TwoViewCursor;
01856 twoViewCursor->index = closeIndex;
01857 indicesToCursors.insert(closeIndex, twoViewCursor);
01858 FAST_DEBUG(" border index added: " << closeIndex);
01859 }
01860 }
01861 }
01862
01863
01864 int curRelLine = 0;
01865 int curRelCol = 0;
01866 int curRelIndex = 0;
01867 QMap<int, TwoViewCursor *>::const_iterator iter = indicesToCursors.constBegin();
01868 while (iter != indicesToCursors.constEnd())
01869 {
01870
01871 const int index = (*iter)->index;
01872 FAST_DEBUG("resolving position" << index);
01873 TwoViewCursor & twoViewCursor = *(*iter);
01874 while (curRelIndex <= index)
01875 {
01876 FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = "
01877 << curRelIndex << "relative, steps more to go" << index - curRelIndex);
01878 const int curRelLineLen = lineLens[curRelLine];
01879 const int curLineRemainder = curRelLineLen - curRelCol;
01880 const int lineFeedIndex = curRelIndex + curLineRemainder;
01881 if (index <= lineFeedIndex) {
01882 if (index == lineFeedIndex) {
01883
01884 FAST_DEBUG(" on line feed");
01885 const int absLine = curRelLine + firstLineIndex;
01886 twoViewCursor.openLine
01887 = twoViewCursor.closeLine
01888 = absLine;
01889 twoViewCursor.openCol
01890 = twoViewCursor.closeCol
01891 = ((curRelLine == 0) ? minColStart : 0) + curRelLineLen;
01892
01893
01894 const int advance = (index - curRelIndex) + 1;
01895 curRelLine++;
01896 curRelCol = 0;
01897 curRelIndex += advance;
01898 } else {
01899
01900 FAST_DEBUG(" before line feed");
01901 const int diff = (index - curRelIndex);
01902 const int absLine = curRelLine + firstLineIndex;
01903 const int absCol = ((curRelLine == 0) ? minColStart : 0) + curRelCol + diff;
01904 twoViewCursor.openLine
01905 = twoViewCursor.closeLine
01906 = absLine;
01907 twoViewCursor.openCol
01908 = twoViewCursor.closeCol
01909 = absCol;
01910
01911
01912 const int advance = diff + 1;
01913 curRelCol += advance;
01914 curRelIndex += advance;
01915 }
01916 FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol
01917 << ") close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")");
01918 }
01919 else
01920 {
01921
01922
01923 FAST_DEBUG(" not on this line");
01924 const int advance = curLineRemainder + 1;
01925 curRelLine++;
01926 curRelCol = 0;
01927 curRelIndex += advance;
01928 }
01929 }
01930
01931 ++iter;
01932 }
01933
01934
01935 QVector<KTextEditor::Range> result(1 + numCaptures);
01936 for (int y = 0; y <= numCaptures; y++)
01937 {
01938 IndexPair & pair = indexPairs[y];
01939 if ((pair.openIndex == -1) || (pair.closeIndex == -1))
01940 {
01941 result[y] = KTextEditor::Range::invalid();
01942 }
01943 else
01944 {
01945 const TwoViewCursor * const openCursors = indicesToCursors[pair.openIndex];
01946 const TwoViewCursor * const closeCursors = indicesToCursors[pair.closeIndex];
01947 const int startLine = openCursors->openLine;
01948 const int startCol = openCursors->openCol;
01949 const int endLine = closeCursors->closeLine;
01950 const int endCol = closeCursors->closeCol;
01951 FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")");
01952 result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol);
01953 }
01954 }
01955
01956
01957 iter = indicesToCursors.constBegin();
01958 while (iter != indicesToCursors.constEnd())
01959 {
01960 TwoViewCursor * const twoViewCursor = *iter;
01961 delete twoViewCursor;
01962 ++iter;
01963 }
01964 return result;
01965 }
01966 else
01967 {
01968
01969 const int minLeft = inputRange.start().column();
01970 const uint maxRight = inputRange.end().column();
01971 const int forMin = inputRange.start().line();
01972 const int forMax = inputRange.end().line();
01973 const int forInit = backwards ? forMax : forMin;
01974 const int forInc = backwards ? -1 : +1;
01975 FAST_DEBUG("single line " << (backwards ? forMax : forMin) << ".."
01976 << (backwards ? forMin : forMax));
01977 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc)
01978 {
01979 KateTextLine::Ptr textLine = m_buffer->plainLine(j);
01980 if (!textLine)
01981 {
01982 FAST_DEBUG("searchText | line " << j << ": no");
01983 QVector<KTextEditor::Range> result;
01984 result.append(KTextEditor::Range::invalid());
01985 return result;
01986 }
01987
01988
01989 const int first = (j == forMin) ? minLeft : 0;
01990 const int afterLast = (j == forMax) ? maxRight : textLine->length();
01991 const QString hay = textLine->string();
01992 bool found = true;
01993 int foundAt;
01994 uint myMatchLen;
01995 if (backwards) {
01996 const int lineLen = textLine->length();
01997 const int offset = afterLast - lineLen - 1;
01998 FAST_DEBUG("lastIndexIn(" << hay << "," << offset << ")");
01999 foundAt = KateDocument::fixedLastIndexIn(regexp, hay, offset);
02000 found = (foundAt != -1) && (foundAt >= first);
02001 } else {
02002 FAST_DEBUG("indexIn(" << hay << "," << first << ")");
02003 foundAt = regexp.indexIn(hay, first);
02004 found = (foundAt != -1);
02005 }
02006 myMatchLen = found ? regexp.matchedLength() : 0;
02007
02008
02009
02010
02011
02012
02013
02014
02015
02016
02017
02018
02019
02020
02021
02022
02023
02024
02025 if (found && !((j == forMax) && (static_cast<uint>(foundAt + myMatchLen) > maxRight)))
02026 {
02027 FAST_DEBUG("line " << j << ": yes");
02028
02029
02030 const int numCaptures = regexp.numCaptures();
02031 QVector<KTextEditor::Range> result(1 + numCaptures);
02032 result[0] = KTextEditor::Range(j, foundAt, j, foundAt + myMatchLen);
02033 FAST_DEBUG("result range " << 0 << ": (" << j << ", " << foundAt << ")..(" << j << ", " << foundAt + myMatchLen << ")");
02034 for (int y = 1; y <= numCaptures; y++)
02035 {
02036 const int openIndex = regexp.pos(y);
02037 if (openIndex == -1)
02038 {
02039 result[y] = KTextEditor::Range::invalid();
02040 FAST_DEBUG("capture []");
02041 }
02042 else
02043 {
02044 const int closeIndex = openIndex + regexp.cap(y).length();
02045 FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")");
02046 result[y] = KTextEditor::Range(j, openIndex, j, closeIndex);
02047 }
02048 }
02049 return result;
02050 }
02051 else
02052 {
02053 FAST_DEBUG("searchText | line " << j << ": no");
02054 }
02055 }
02056 }
02057
02058 QVector<KTextEditor::Range> result;
02059 result.append(KTextEditor::Range::invalid());
02060 return result;
02061 }
02062
02063 QWidget * KateDocument::dialogParent()
02064 {
02065 QWidget *w=widget();
02066
02067 if(!w)
02068 {
02069 w=activeView();
02070
02071 if(!w)
02072 w=QApplication::activeWindow();
02073 }
02074
02075 return w;
02076 }
02077
02078 QVector<KTextEditor::Range> KateDocument::searchText(
02079 const KTextEditor::Range & range,
02080 const QString & pattern,
02081 const KTextEditor::Search::SearchOptions options)
02082 {
02083
02084
02085
02086 QString workPattern(pattern);
02087
02088 KTextEditor::Search::SearchOptions finalOptions(options);
02089 const bool escapeSequences = finalOptions.testFlag(KTextEditor::Search::EscapeSequences);
02090
02091
02092 if (finalOptions.testFlag(KTextEditor::Search::WholeWords))
02093 {
02094
02095 if (escapeSequences)
02096 {
02097 KateDocument::escapePlaintext(workPattern);
02098 }
02099
02100
02101 workPattern = "\\b" + QRegExp::escape(workPattern) + "\\b";
02102
02103
02104 finalOptions |= KTextEditor::Search::Regex;
02105 finalOptions &= ~KTextEditor::Search::SearchOptions(KTextEditor::Search::WholeWords);
02106 }
02107
02108 const bool regexMode = finalOptions.testFlag(KTextEditor::Search::Regex);
02109 const bool caseSensitive = !finalOptions.testFlag(KTextEditor::Search::CaseInsensitive);
02110 const bool backwards = finalOptions.testFlag(KTextEditor::Search::Backwards);
02111
02112 if (regexMode)
02113 {
02114
02115 const Qt::CaseSensitivity caseSensitivity =
02116 caseSensitive
02117 ? Qt::CaseSensitive
02118 : Qt::CaseInsensitive;
02119
02120 QRegExp matcher(workPattern, caseSensitivity);
02121 if (matcher.isValid())
02122 {
02123
02124
02125 return searchRegex(range, matcher, backwards);
02126 }
02127 else
02128 {
02129
02130 QVector<KTextEditor::Range> result;
02131 result.append(KTextEditor::Range::invalid());
02132 return result;
02133 }
02134 }
02135 else
02136 {
02137
02138
02139
02140 if (escapeSequences)
02141 {
02142 KateDocument::escapePlaintext(workPattern);
02143 }
02144
02145
02146 KTextEditor::Range resultRange = searchText(range, workPattern, caseSensitive, backwards);
02147 QVector<KTextEditor::Range> result;
02148 result.append(resultRange);
02149 return result;
02150 }
02151 }
02152
02153
02154
02155 KTextEditor::Search::SearchOptions KateDocument::supportedSearchOptions() const
02156 {
02157 KTextEditor::Search::SearchOptions supported(KTextEditor::Search::Default);
02158 supported |= KTextEditor::Search::Regex;
02159 supported |= KTextEditor::Search::CaseInsensitive;
02160 supported |= KTextEditor::Search::Backwards;
02161
02162 supported |= KTextEditor::Search::EscapeSequences;
02163 supported |= KTextEditor::Search::WholeWords;
02164
02165 return supported;
02166 }
02167
02168
02169
02170 void KateDocument::escapePlaintext(QString & text, QList<ReplacementPart> * parts,
02171 bool replacementGoodies) {
02172
02173 const int inputLen = text.length();
02174 int input = 0;
02175
02176
02177 QString output;
02178 output.reserve(inputLen + 1);
02179
02180 while (input < inputLen)
02181 {
02182 switch (text[input].unicode())
02183 {
02184 case L'\n':
02185 output.append(text[input]);
02186 input++;
02187 break;
02188
02189 case L'\\':
02190 if (input + 1 >= inputLen)
02191 {
02192
02193 output.append(text[input]);
02194 input++;
02195 break;
02196 }
02197
02198 switch (text[input + 1].unicode())
02199 {
02200 case L'0':
02201 if (input + 4 >= inputLen)
02202 {
02203 if (parts == NULL)
02204 {
02205
02206 output.append(text[input + 1]);
02207 }
02208 else
02209 {
02210
02211 ReplacementPart curPart;
02212
02213
02214 if (!output.isEmpty())
02215 {
02216 curPart.type = ReplacementPart::Text;
02217 curPart.text = output;
02218 output.clear();
02219 parts->append(curPart);
02220 curPart.text.clear();
02221 }
02222
02223
02224 curPart.type = ReplacementPart::Reference;
02225 curPart.index = 0;
02226 parts->append(curPart);
02227 }
02228 input += 2;
02229 }
02230 else
02231 {
02232 bool stripAndSkip = false;
02233 const ushort text_2 = text[input + 2].unicode();
02234 if ((text_2 >= L'0') && (text_2 <= L'3'))
02235 {
02236 const ushort text_3 = text[input + 3].unicode();
02237 if ((text_3 >= L'0') && (text_3 <= L'7'))
02238 {
02239 const ushort text_4 = text[input + 4].unicode();
02240 if ((text_4 >= L'0') && (text_4 <= L'7'))
02241 {
02242 int digits[3];
02243 for (int i = 0; i < 3; i++)
02244 {
02245 digits[i] = 7 - (L'7' - text[input + 2 + i].unicode());
02246 }
02247 const int ch = 64 * digits[0] + 8 * digits[1] + digits[2];
02248 output.append(QChar(ch));
02249 input += 5;
02250 }
02251 else
02252 {
02253 stripAndSkip = true;
02254 }
02255 }
02256 else
02257 {
02258 stripAndSkip = true;
02259 }
02260 }
02261 else
02262 {
02263 stripAndSkip = true;
02264 }
02265
02266 if (stripAndSkip)
02267 {
02268 if (parts == NULL)
02269 {
02270
02271 output.append(text[input + 1]);
02272 }
02273 else
02274 {
02275
02276 ReplacementPart curPart;
02277
02278
02279 if (!output.isEmpty())
02280 {
02281 curPart.type = ReplacementPart::Text;
02282 curPart.text = output;
02283 output.clear();
02284 parts->append(curPart);
02285 curPart.text.clear();
02286 }
02287
02288
02289 curPart.type = ReplacementPart::Reference;
02290 curPart.index = 0;
02291 parts->append(curPart);
02292 }
02293 input += 2;
02294 }
02295 }
02296 break;
02297
02298 case L'1':
02299 case L'2':
02300 case L'3':
02301 case L'4':
02302 case L'5':
02303 case L'6':
02304 case L'7':
02305 case L'8':
02306 case L'9':
02307 if (parts == NULL)
02308 {
02309
02310 output.append(text[input + 1]);
02311 }
02312 else
02313 {
02314
02315 ReplacementPart curPart;
02316
02317
02318 if (!output.isEmpty())
02319 {
02320 curPart.type = ReplacementPart::Text;
02321 curPart.text = output;
02322 output.clear();
02323 parts->append(curPart);
02324 curPart.text.clear();
02325 }
02326
02327
02328 curPart.type = ReplacementPart::Reference;
02329 curPart.index = 9 - (L'9' - text[input + 1].unicode());
02330 parts->append(curPart);
02331 }
02332 input += 2;
02333 break;
02334
02335 case L'E':
02336 case L'L':
02337 case L'U':
02338 if ((parts == NULL) || !replacementGoodies) {
02339
02340 output.append(text[input + 1]);
02341 } else {
02342
02343 ReplacementPart curPart;
02344
02345
02346 if (!output.isEmpty())
02347 {
02348 curPart.type = ReplacementPart::Text;
02349 curPart.text = output;
02350 output.clear();
02351 parts->append(curPart);
02352 curPart.text.clear();
02353 }
02354
02355
02356 switch (text[input + 1].unicode()) {
02357 case L'L':
02358 curPart.type = ReplacementPart::LowerCase;
02359 break;
02360
02361 case L'U':
02362 curPart.type = ReplacementPart::UpperCase;
02363 break;
02364
02365 case L'E':
02366 default:
02367 curPart.type = ReplacementPart::KeepCase;
02368
02369 }
02370 parts->append(curPart);
02371 }
02372 input += 2;
02373 break;
02374
02375 case L'#':
02376 if ((parts == NULL) || !replacementGoodies) {
02377
02378 output.append(text[input + 1]);
02379 input += 2;
02380 } else {
02381
02382 ReplacementPart curPart;
02383
02384
02385 if (!output.isEmpty())
02386 {
02387 curPart.type = ReplacementPart::Text;
02388 curPart.text = output;
02389 output.clear();
02390 parts->append(curPart);
02391 curPart.text.clear();
02392 }
02393
02394
02395
02396 int count = 1;
02397 while ((input + count + 1 < inputLen) && (text[input + count + 1].unicode() == L'#')) {
02398 count++;
02399 }
02400 curPart.type = ReplacementPart::Counter;
02401 curPart.index = count;
02402 parts->append(curPart);
02403 input += 1 + count;
02404 }
02405 break;
02406
02407 case L'a':
02408 output.append(QChar(0x07));
02409 input += 2;
02410 break;
02411
02412 case L'f':
02413 output.append(QChar(0x0c));
02414 input += 2;
02415 break;
02416
02417 case L'n':
02418 output.append(QChar(0x0a));
02419 input += 2;
02420 break;
02421
02422 case L'r':
02423 output.append(QChar(0x0d));
02424 input += 2;
02425 break;
02426
02427 case L't':
02428 output.append(QChar(0x09));
02429 input += 2;
02430 break;
02431
02432 case L'v':
02433 output.append(QChar(0x0b));
02434 input += 2;
02435 break;
02436
02437 case L'x':
02438 if (input + 5 >= inputLen)
02439 {
02440
02441 output.append(text[input + 1]);
02442 input += 2;
02443 }
02444 else
02445 {
02446 bool stripAndSkip = false;
02447 const ushort text_2 = text[input + 2].unicode();
02448 if (((text_2 >= L'0') && (text_2 <= L'9'))
02449 || ((text_2 >= L'a') && (text_2 <= L'f'))
02450 || ((text_2 >= L'A') && (text_2 <= L'F')))
02451 {
02452 const ushort text_3 = text[input + 3].unicode();
02453 if (((text_3 >= L'0') && (text_3 <= L'9'))
02454 || ((text_3 >= L'a') && (text_3 <= L'f'))
02455 || ((text_3 >= L'A') && (text_3 <= L'F')))
02456 {
02457 const ushort text_4 = text[input + 4].unicode();
02458 if (((text_4 >= L'0') && (text_4 <= L'9'))
02459 || ((text_4 >= L'a') && (text_4 <= L'f'))
02460 || ((text_4 >= L'A') && (text_4 <= L'F')))
02461 {
02462 const ushort text_5 = text[input + 5].unicode();
02463 if (((text_5 >= L'0') && (text_5 <= L'9'))
02464 || ((text_5 >= L'a') && (text_5 <= L'f'))
02465 || ((text_5 >= L'A') && (text_5 <= L'F')))
02466 {
02467 int digits[4];
02468 for (int i = 0; i < 4; i++)
02469 {
02470 const ushort cur = text[input + 2 + i].unicode();
02471 if ((cur >= L'0') && (cur <= L'9'))
02472 {
02473 digits[i] = 9 - (L'9' - cur);
02474 }
02475 else if ((cur >= L'a') && (cur <= L'f'))
02476 {
02477 digits[i] = 15 - (L'f' - cur);
02478 }
02479 else
02480 {
02481 digits[i] = 15 - (L'F' - cur);
02482 }
02483 }
02484
02485 const int ch = 4096 * digits[0] + 256 * digits[1] + 16 * digits[2] + digits[3];
02486 output.append(QChar(ch));
02487 input += 6;
02488 }
02489 else
02490 {
02491 stripAndSkip = true;
02492 }
02493 }
02494 else
02495 {
02496 stripAndSkip = true;
02497 }
02498 }
02499 else
02500 {
02501 stripAndSkip = true;
02502 }
02503 }
02504
02505 if (stripAndSkip)
02506 {
02507
02508 output.append(text[input + 1]);
02509 input += 2;
02510 }
02511 }
02512 break;
02513
02514 default:
02515
02516 output.append(text[input + 1]);
02517 input += 2;
02518
02519 }
02520 break;
02521
02522 default:
02523 output.append(text[input]);
02524 input++;
02525
02526 }
02527 }
02528
02529 if (parts == NULL)
02530 {
02531
02532 text = output;
02533 }
02534 else
02535 {
02536
02537 if (!output.isEmpty())
02538 {
02539 ReplacementPart curPart;
02540 curPart.type = ReplacementPart::Text;
02541 curPart.text = output;
02542 parts->append(curPart);
02543 }
02544 }
02545 }
02546
02547
02548
02549
02550
02551
02552
02553
02554 int KateDocument::repairPattern(QString & pattern, bool & stillMultiLine)
02555 {
02556 const QString & text = pattern;
02557
02558
02559 const int inputLen = text.length();
02560 int input = 0;
02561
02562
02563 QString output;
02564 output.reserve(2 * inputLen + 1);
02565
02566
02567 stillMultiLine = false;
02568 int replaceCount = 0;
02569 bool insideClass = false;
02570
02571 while (input < inputLen)
02572 {
02573 if (insideClass)
02574 {
02575
02576 switch (text[input].unicode())
02577 {
02578 case L'\\':
02579 switch (text[input + 1].unicode())
02580 {
02581 case L'x':
02582 if (input + 5 < inputLen)
02583 {
02584
02585 output.append(text.mid(input, 6));
02586 input += 6;
02587 } else {
02588
02589 output.append(text.mid(input, 2));
02590 input += 2;
02591 }
02592 stillMultiLine = true;
02593 break;
02594
02595 case L'0':
02596 if (input + 4 < inputLen)
02597 {
02598
02599 output.append(text.mid(input, 5));
02600 input += 5;
02601 } else {
02602
02603 output.append(text.mid(input, 2));
02604 input += 2;
02605 }
02606 stillMultiLine = true;
02607 break;
02608
02609 case L's':
02610
02611 output.append("[ \\t]");
02612 input += 2;
02613 replaceCount++;
02614 break;
02615
02616 case L'n':
02617 stillMultiLine = true;
02618
02619
02620 default:
02621
02622 output.append(text.mid(input, 2));
02623 input += 2;
02624 }
02625 break;
02626
02627 case L']':
02628
02629 insideClass = false;
02630 output.append(text[input]);
02631 input++;
02632 break;
02633
02634 default:
02635
02636 output.append(text[input]);
02637 input++;
02638
02639 }
02640 }
02641 else
02642 {
02643
02644 switch (text[input].unicode())
02645 {
02646 case L'\\':
02647 switch (text[input + 1].unicode())
02648 {
02649 case L'x':
02650 if (input + 5 < inputLen)
02651 {
02652
02653 output.append(text.mid(input, 6));
02654 input += 6;
02655 } else {
02656
02657 output.append(text.mid(input, 2));
02658 input += 2;
02659 }
02660 stillMultiLine = true;
02661 break;
02662
02663 case L'0':
02664 if (input + 4 < inputLen)
02665 {
02666
02667 output.append(text.mid(input, 5));
02668 input += 5;
02669 } else {
02670
02671 output.append(text.mid(input, 2));
02672 input += 2;
02673 }
02674 stillMultiLine = true;
02675 break;
02676
02677 case L's':
02678
02679 output.append("[ \\t]");
02680 input += 2;
02681 replaceCount++;
02682 break;
02683
02684 case L'n':
02685 stillMultiLine = true;
02686
02687
02688 default:
02689
02690 output.append(text.mid(input, 2));
02691 input += 2;
02692 }
02693 break;
02694
02695 case L'.':
02696
02697 output.append("[^\\n]");
02698 input++;
02699 replaceCount++;
02700 break;
02701
02702 case L'[':
02703
02704 insideClass = true;
02705 output.append(text[input]);
02706 input++;
02707 break;
02708
02709 default:
02710
02711 output.append(text[input]);
02712 input++;
02713
02714 }
02715 }
02716 }
02717
02718
02719 pattern = output;
02720 return replaceCount;
02721 }
02722
02723
02724
02725 int KateDocument::fixedLastIndexIn(const QRegExp & matcher, const QString & str,
02726 int offset, QRegExp::CaretMode caretMode) {
02727 int prevPos = -1;
02728 int prevLen = 1;
02729 const int strLen = str.length();
02730 for (;;) {
02731 const int pos = matcher.indexIn(str, prevPos + prevLen, caretMode);
02732 if (pos == -1) {
02733
02734 break;
02735 } else {
02736 const int len = matcher.matchedLength();
02737 if (pos > strLen + offset + 1) {
02738
02739 break;
02740 }
02741
02742 if (pos + len > strLen + offset + 1) {
02743
02744 if (offset == -1) {
02745
02746 break;
02747 }
02748
02749 const QString str2 = str.mid(0, strLen + offset + 1);
02750 const int pos2 = matcher.indexIn(str2, pos, caretMode);
02751 if (pos2 != -1) {
02752
02753 return pos2;
02754 } else {
02755
02756 break;
02757 }
02758 }
02759
02760
02761 prevPos = pos;
02762 prevLen = (len == 0) ? 1 : len;
02763 }
02764 }
02765
02766
02767 if (prevPos != -1) {
02768
02769 matcher.indexIn(str, prevPos, caretMode);
02770 return prevPos;
02771 } else {
02772 return -1;
02773 }
02774 }
02775
02776
02777
02778 bool KateDocument::setMode (const QString &name)
02779 {
02780 updateFileType (name);
02781 return true;
02782 }
02783
02784 QString KateDocument::mode () const
02785 {
02786 return m_fileType;
02787 }
02788
02789 QStringList KateDocument::modes () const
02790 {
02791 QStringList m;
02792
02793 const QList<KateFileType *> &modeList = KateGlobal::self()->modeManager()->list();
02794 for (int i = 0; i < modeList.size(); ++i)
02795 m << modeList[i]->name;
02796
02797 return m;
02798 }
02799
02800 bool KateDocument::setHighlightingMode (const QString &name)
02801 {
02802 m_buffer->setHighlight (KateHlManager::self()->nameFind(name));
02803 return true;
02804 }
02805
02806 QString KateDocument::highlightingMode () const
02807 {
02808 return highlight()->name ();
02809 }
02810
02811 QStringList KateDocument::highlightingModes () const
02812 {
02813 QStringList hls;
02814
02815 for (int i = 0; i < KateHlManager::self()->highlights(); ++i)
02816 hls << KateHlManager::self()->hlName (i);
02817
02818 return hls;
02819 }
02820
02821 QString KateDocument::highlightingModeSection( int index ) const
02822 {
02823 return KateHlManager::self()->hlSection( index );
02824 }
02825
02826 QString KateDocument::modeSection( int index ) const
02827 {
02828 return KateGlobal::self()->modeManager()->list()[ index ]->section;
02829 }
02830
02831 void KateDocument::bufferHlChanged ()
02832 {
02833
02834 makeAttribs(false);
02835
02836
02837 m_indenter.checkRequiredStyle();
02838
02839 emit highlightingModeChanged(this);
02840 }
02841
02842 void KateDocument::setDontChangeHlOnSave()
02843 {
02844 hlSetByUser = true;
02845 }
02846
02847
02848
02849 void KateDocument::readSessionConfig(const KConfigGroup &kconfig)
02850 {
02851
02852 KUrl url (kconfig.readEntry("URL"));
02853
02854
02855 QString tmpenc=kconfig.readEntry("Encoding");
02856 if (!tmpenc.isEmpty() && (tmpenc != encoding()))
02857 setEncoding(tmpenc);
02858
02859
02860 if (!url.isEmpty() && url.isValid())
02861 openUrl (url);
02862 else completed();
02863
02864
02865 updateFileType (kconfig.readEntry("Mode", "Normal"));
02866
02867
02868 m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig.readEntry("Highlighting")));
02869
02870
02871
02872 setReadWrite(kconfig.readEntry("ReadWrite", true));
02873
02874
02875 config()->setIndentationMode( kconfig.readEntry("Indentation Mode", config()->indentationMode() ) );
02876
02877
02878 const QList<int> marks = kconfig.readEntry("Bookmarks", QList<int>());
02879 for( int i = 0; i < marks.count(); i++ )
02880 addMark( marks[i], KateDocument::markType01 );
02881 }
02882
02883 void KateDocument::writeSessionConfig(KConfigGroup &kconfig)
02884 {
02885 if ( this->url().isLocalFile() ) {
02886 const QString path = this->url().toLocalFile();
02887 if ( KGlobal::dirs()->relativeLocation( "tmp", path ) != path ) {
02888 return;
02889 }
02890 }
02891
02892 kconfig.writeEntry("URL", this->url().prettyUrl() );
02893
02894
02895 kconfig.writeEntry("Encoding",encoding());
02896
02897
02898 kconfig.writeEntry("Mode", m_fileType);
02899
02900
02901 kconfig.writeEntry("Highlighting", highlight()->name());
02902
02903
02904 kconfig.writeEntry("ReadWrite", isReadWrite());
02905
02906
02907 kconfig.writeEntry("Indentation Mode", config()->indentationMode() );
02908
02909
02910 QList<int> marks;
02911 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
02912 if (i.value()->type & KTextEditor::MarkInterface::markType01)
02913 marks << i.value()->line;
02914
02915 kconfig.writeEntry( "Bookmarks", marks );
02916 }
02917
02918 uint KateDocument::mark( int line )
02919 {
02920 if( !m_marks.value(line) )
02921 return 0;
02922
02923 return m_marks[line]->type;
02924 }
02925
02926 void KateDocument::setMark( int line, uint markType )
02927 {
02928 clearMark( line );
02929 addMark( line, markType );
02930 }
02931
02932 void KateDocument::clearMark( int line )
02933 {
02934 if( line > lastLine() )
02935 return;
02936
02937 if( !m_marks.value(line) )
02938 return;
02939
02940 KTextEditor::Mark* mark = m_marks.take( line );
02941 emit markChanged( this, *mark, MarkRemoved );
02942 emit marksChanged( this );
02943 delete mark;
02944 tagLines( line, line );
02945 repaintViews(true);
02946 }
02947
02948 void KateDocument::addMark( int line, uint markType )
02949 {
02950 if( line > lastLine())
02951 return;
02952
02953 if( markType == 0 )
02954 return;
02955
02956 if( m_marks.value(line) ) {
02957 KTextEditor::Mark* mark = m_marks[line];
02958
02959
02960 markType &= ~mark->type;
02961
02962 if( markType == 0 )
02963 return;
02964
02965
02966 mark->type |= markType;
02967 } else {
02968 KTextEditor::Mark *mark = new KTextEditor::Mark;
02969 mark->line = line;
02970 mark->type = markType;
02971 m_marks.insert( line, mark );
02972 }
02973
02974
02975 KTextEditor::Mark temp;
02976 temp.line = line;
02977 temp.type = markType;
02978 emit markChanged( this, temp, MarkAdded );
02979
02980 emit marksChanged( this );
02981 tagLines( line, line );
02982 repaintViews(true);
02983 }
02984
02985 void KateDocument::removeMark( int line, uint markType )
02986 {
02987 if( line > lastLine() )
02988 return;
02989
02990 if( !m_marks.value(line) )
02991 return;
02992
02993 KTextEditor::Mark* mark = m_marks[line];
02994
02995
02996 markType &= mark->type;
02997
02998 if( markType == 0 )
02999 return;
03000
03001
03002 mark->type &= ~markType;
03003
03004
03005 KTextEditor::Mark temp;
03006 temp.line = line;
03007 temp.type = markType;
03008 emit markChanged( this, temp, MarkRemoved );
03009
03010 if( mark->type == 0 )
03011 m_marks.remove( line );
03012
03013 emit marksChanged( this );
03014 tagLines( line, line );
03015 repaintViews(true);
03016 }
03017
03018 const QHash<int, KTextEditor::Mark*> &KateDocument::marks()
03019 {
03020 return m_marks;
03021 }
03022
03023 void KateDocument::clearMarks()
03024 {
03025 while (!m_marks.isEmpty())
03026 {
03027 QHash<int, KTextEditor::Mark*>::iterator it = m_marks.begin();
03028 KTextEditor::Mark mark = *it.value();
03029 delete it.value();
03030 m_marks.erase (it);
03031
03032 emit markChanged( this, mark, MarkRemoved );
03033 tagLines( mark.line, mark.line );
03034 }
03035
03036 m_marks.clear();
03037
03038 emit marksChanged( this );
03039 repaintViews(true);
03040 }
03041
03042 void KateDocument::setMarkPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
03043 {
03044 m_markPixmaps.insert( type, pixmap );
03045 }
03046
03047 void KateDocument::setMarkDescription( MarkInterface::MarkTypes type, const QString& description )
03048 {
03049 m_markDescriptions.insert( type, description );
03050 }
03051
03052 QPixmap KateDocument::markPixmap( MarkInterface::MarkTypes type ) const
03053 {
03054 return m_markPixmaps.contains(type) ?
03055 m_markPixmaps[type] : QPixmap();
03056 }
03057
03058 QColor KateDocument::markColor( MarkInterface::MarkTypes type ) const
03059 {
03060 uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
03061 if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
03062 return KateRendererConfig::global()->lineMarkerColor(type);
03063 } else {
03064 return QColor();
03065 }
03066 }
03067
03068 QString KateDocument::markDescription( MarkInterface::MarkTypes type ) const
03069 {
03070 return m_markDescriptions.contains(type) ?
03071 m_markDescriptions[type] : QString();
03072 }
03073
03074 void KateDocument::setEditableMarks( uint markMask )
03075 {
03076 m_editableMarks = markMask;
03077 }
03078
03079 uint KateDocument::editableMarks() const
03080 {
03081 return m_editableMarks;
03082 }
03083
03084
03085
03086 bool KateDocument::printDialog ()
03087 {
03088 return KatePrinter::print (this);
03089 }
03090
03091 bool KateDocument::print ()
03092 {
03093 return KatePrinter::print (this);
03094 }
03095
03096
03097
03098 QString KateDocument::mimeType()
03099 {
03100 KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
03101
03102
03103 if ( ! this->url().isEmpty() )
03104 result = KMimeType::findByUrl( this->url() );
03105
03106 else if ( this->url().isEmpty() || ! this->url().isLocalFile() )
03107 result = mimeTypeForContent();
03108
03109 return result->name();
03110 }
03111
03112 KMimeType::Ptr KateDocument::mimeTypeForContent()
03113 {
03114 QByteArray buf (1024,'\0');
03115 uint bufpos = 0;
03116
03117 for (int i=0; i < lines(); ++i)
03118 {
03119 QString line = this->line( i );
03120 uint len = line.length() + 1;
03121
03122 if (bufpos + len > 1024)
03123 len = 1024 - bufpos;
03124
03125 QString ld (line + QChar::fromAscii('\n'));
03126 buf.replace(bufpos,len,ld.toLatin1());
03127
03128 bufpos += len;
03129
03130 if (bufpos >= 1024)
03131 break;
03132 }
03133 buf.resize( bufpos );
03134
03135 int accuracy = 0;
03136 KMimeType::Ptr mt = KMimeType::findByContent(buf, &accuracy);
03137 return mt ? mt : KMimeType::defaultMimeTypePtr();
03138 }
03139
03140
03141
03142
03143 bool KateDocument::openFile()
03144 {
03145
03146 setOpeningError(false);
03147
03148
03149 activateDirWatch ();
03150
03151
03152
03153
03154 QString mimeType = arguments().mimeType();
03155 int pos = mimeType.indexOf(';');
03156 if (pos != -1)
03157 setEncoding (mimeType.mid(pos+1));
03158
03159
03160 emit KTextEditor::Document::textRemoved(this, documentRange());
03161 history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03162
03163 bool success = m_buffer->openFile (localFilePath());
03164
03165 emit KTextEditor::Document::textInserted(this, documentRange());
03166 history()->doEdit( new KateEditInfo(Kate::OpenFileEdit, KTextEditor::Range(0,0,0,0), QStringList(), documentRange(), QStringList()) );
03167
03168
03169
03170
03171 if (success)
03172 {
03173
03174 updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03175
03176
03177 readDirConfig ();
03178
03179
03180 readVariables();
03181
03182
03183 createDigest( m_digest );
03184
03185 if (!m_postLoadFilterChecks.isEmpty())
03186 {
03187 LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03188 foreach(const QString& checkplugin, m_postLoadFilterChecks)
03189 {
03190 lscps->postLoadFilter(checkplugin,this);
03191 }
03192 }
03193 }
03194
03195
03196 emit textChanged (this);
03197
03198
03199
03200
03201 foreach (KateView * view, m_views)
03202 {
03203
03204 view->setCursorPosition(KTextEditor::Cursor());
03205 view->updateView(true);
03206 }
03207
03208 if (!m_reloading)
03209 {
03210
03211
03212
03213 emit documentUrlChanged (this);
03214
03215
03216
03217
03218 setDocName (QString());
03219 }
03220
03221
03222
03223 if (m_modOnHd)
03224 {
03225 m_modOnHd = false;
03226 m_modOnHdReason = OnDiskUnmodified;
03227 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03228 }
03229
03230
03231
03232
03233 QWidget *parentWidget(dialogParent());
03234
03235 if (!suppressOpeningErrorDialogs())
03236 {
03237 if (!success)
03238 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()));
03239 }
03240
03241 if (!success) {
03242 setOpeningError(true);
03243 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()));
03244 }
03245
03246
03247 if (m_buffer->binary())
03248 {
03249
03250 setReadWrite( false );
03251
03252 if(!suppressOpeningErrorDialogs())
03253 KMessageBox::information (parentWidget
03254 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl())
03255 , i18n ("Binary File Opened")
03256 , "Binary File Opened Warning");
03257
03258 setOpeningError(true);
03259 setOpeningErrorMessage(i18n ("The file %1 is a binary, saving it will result in a corrupt file.", this->url().pathOrUrl()));
03260 }
03261
03262
03263
03264 else if (m_buffer->brokenUTF8())
03265 {
03266
03267 setReadWrite( false );
03268
03269 if (!suppressOpeningErrorDialogs())
03270 KMessageBox::information (parentWidget
03271 , i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03272 " It is set to read-only mode, as saving might destroy its content."
03273 " 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())
03274 , i18n ("Broken UTF-8 File Opened")
03275 , "Broken UTF-8 File Opened Warning");
03276 setOpeningError(true);
03277 setOpeningErrorMessage(i18n ("The file %1 was opened with UTF-8 encoding but contained invalid characters."
03278 " It is set to read-only mode, as saving might destroy its content."
03279 " 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()));
03280 }
03281
03282
03283
03284
03285 return success;
03286 }
03287
03288 bool KateDocument::saveFile()
03289 {
03290 QWidget *parentWidget(dialogParent());
03291
03292
03293
03294
03295 if (m_buffer->binary() && (KMessageBox::warningContinueCancel (parentWidget
03296 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.", url().pathOrUrl())
03297 , i18n ("Trying to Save Binary File")
03298 , KGuiItem(i18n("Save Nevertheless"))
03299 , KStandardGuiItem::cancel(), "Binary File Save Warning") != KMessageBox::Continue))
03300 return false;
03301
03302
03303 if ( !url().isEmpty() )
03304 {
03305 if (s_fileChangedDialogsActivated && m_modOnHd)
03306 {
03307 QString str = reasonedMOHString() + "\n\n";
03308
03309 if (!isModified())
03310 {
03311 if (KMessageBox::warningContinueCancel(parentWidget,
03312 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)
03313 return false;
03314 }
03315 else
03316 {
03317 if (KMessageBox::warningContinueCancel(parentWidget,
03318 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)
03319 return false;
03320 }
03321 }
03322 }
03323
03324
03325
03326
03327 if (!m_buffer->canEncode ()
03328 && (KMessageBox::warningContinueCancel(parentWidget,
03329 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))
03330 {
03331 return false;
03332 }
03333
03334
03335
03336
03337
03338
03339 bool l ( url().isLocalFile() );
03340
03341
03342 if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
03343 || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
03344 {
03345 KUrl u( url() );
03346 u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
03347
03348 kDebug( 13020 ) << "backup src file name: " << url();
03349 kDebug( 13020 ) << "backup dst file name: " << u;
03350
03351
03352 bool backupSuccess = false;
03353
03354
03355 if (u.isLocalFile ())
03356 {
03357 if (QFile::exists (url().toLocalFile ()))
03358 {
03359
03360 QFile backupFile (u.toLocalFile ());
03361 if (backupFile.exists()) backupFile.remove ();
03362
03363 backupSuccess = QFile::copy (url().toLocalFile (), u.toLocalFile ());
03364 }
03365 else
03366 backupSuccess = true;
03367 }
03368 else
03369 {
03370 QWidget *w = widget ();
03371 if (!w && !m_views.isEmpty ())
03372 w = m_views.first();
03373
03374
03375 mode_t perms = 0600;
03376 KIO::UDSEntry fentry;
03377 if (KIO::NetAccess::stat (url(), fentry, kapp->activeWindow()))
03378 {
03379 kDebug( 13020 ) << "stating succesfull: " << url();
03380 KFileItem item (fentry, url());
03381 perms = item.permissions();
03382
03383
03384 KIO::FileCopyJob *job = KIO::file_copy ( url(), u, -1, KIO::Overwrite );
03385 backupSuccess = KIO::NetAccess::synchronousRun(job, w);
03386 }
03387 else
03388 backupSuccess = true;
03389 }
03390
03391
03392 if (!backupSuccess && (KMessageBox::warningContinueCancel (parentWidget
03393 , i18n ("For file %1 no backup copy could be created before saving."
03394 " If an error occurs while saving, you might lose the data of this file."
03395 " 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())
03396 , i18n ("Failed to create backup copy.")
03397 , KGuiItem(i18n("Try to Save Nevertheless"))
03398 , KStandardGuiItem::cancel(), "Backup Failed Warning") != KMessageBox::Continue))
03399 {
03400 return false;
03401 }
03402 }
03403
03404
03405 updateFileType (KateGlobal::self()->modeManager()->fileType (this));
03406
03407 if (!m_preSavePostDialogFilterChecks.isEmpty())
03408 {
03409 LoadSaveFilterCheckPlugins *lscps=loadSaveFilterCheckPlugins();
03410 foreach(const QString& checkplugin, m_preSavePostDialogFilterChecks)
03411 {
03412 if (lscps->preSavePostDialogFilterCheck(checkplugin,this,parentWidget)==false)
03413 return false;
03414 }
03415 }
03416
03417
03418 QString oldPath = m_dirWatchFile;
03419
03420
03421 deactivateDirWatch ();
03422
03423
03424
03425
03426 if (!m_buffer->saveFile (localFilePath()))
03427 {
03428
03429 activateDirWatch (oldPath);
03430
03431 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()));
03432
03433 return false;
03434 }
03435
03436
03437 createDigest( m_digest );
03438
03439
03440 activateDirWatch ();
03441
03442
03443
03444
03445
03446 if ( url().isLocalFile())
03447 {
03448 QFileInfo fo (oldPath), fn (m_dirWatchFile);
03449
03450 if (fo.path() != fn.path())
03451 readDirConfig();
03452 }
03453
03454
03455 readVariables();
03456
03457
03458
03459
03460 if (m_modOnHd)
03461 {
03462 m_modOnHd = false;
03463 m_modOnHdReason = OnDiskUnmodified;
03464 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03465 }
03466
03467
03468 setDocName( QString() );
03469
03470
03471 emit documentUrlChanged (this);
03472
03473 m_savingToUrl=true;
03474
03475
03476
03477 setUndoDontMerge(true);
03478
03479
03480
03481
03482 return true;
03483 }
03484
03485 void KateDocument::readDirConfig ()
03486 {
03487 int depth = config()->searchDirConfigDepth ();
03488
03489 if (this->url().isLocalFile() && (depth > -1))
03490 {
03491 QString currentDir = QFileInfo (localFilePath()).absolutePath();
03492
03493
03494 while (depth > -1)
03495 {
03496
03497
03498
03499 QFile f (currentDir + "/.kateconfig");
03500
03501 if (f.open (QIODevice::ReadOnly))
03502 {
03503 QTextStream stream (&f);
03504
03505 uint linesRead = 0;
03506 QString line = stream.readLine();
03507 while ((linesRead < 32) && !line.isNull())
03508 {
03509 readVariableLine( line );
03510
03511 line = stream.readLine();
03512
03513 linesRead++;
03514 }
03515
03516 break;
03517 }
03518
03519 QString newDir = QFileInfo (currentDir).absolutePath();
03520
03521
03522 if (currentDir == newDir)
03523 break;
03524
03525 currentDir = newDir;
03526 --depth;
03527 }
03528 }
03529 }
03530
03531 void KateDocument::activateDirWatch (const QString &useFileName)
03532 {
03533 QString fileToUse = useFileName;
03534 if (fileToUse.isEmpty())
03535 fileToUse = localFilePath();
03536
03537
03538 if (fileToUse == m_dirWatchFile)
03539 return;
03540
03541
03542 deactivateDirWatch ();
03543
03544
03545 if (url().isLocalFile() && !fileToUse.isEmpty())
03546 {
03547 KateGlobal::self()->dirWatch ()->addFile (fileToUse);
03548 m_dirWatchFile = fileToUse;
03549 }
03550 }
03551
03552 void KateDocument::deactivateDirWatch ()
03553 {
03554 if (!m_dirWatchFile.isEmpty())
03555 KateGlobal::self()->dirWatch ()->removeFile (m_dirWatchFile);
03556
03557 m_dirWatchFile.clear();
03558 }
03559
03560 bool KateDocument::closeUrl()
03561 {
03562
03563
03564
03565 if ( !m_reloading && !url().isEmpty() )
03566 {
03567 if (s_fileChangedDialogsActivated && m_modOnHd)
03568 {
03569 QWidget *parentWidget(dialogParent());
03570
03571 if (!(KMessageBox::warningContinueCancel(
03572 parentWidget,
03573 reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
03574 i18n("Possible Data Loss"), KGuiItem(i18n("Close Nevertheless")), KStandardGuiItem::cancel(),
03575 QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
03576 return false;
03577 }
03578 }
03579
03580
03581
03582
03583 if (!KParts::ReadWritePart::closeUrl ())
03584 return false;
03585
03586
03587 if (!m_reloading)
03588 emit aboutToClose(this);
03589
03590
03591 deactivateDirWatch ();
03592
03593
03594
03595
03596 setUrl(KUrl());
03597 setLocalFilePath(QString());
03598
03599
03600 if (m_modOnHd)
03601 {
03602 m_modOnHd = false;
03603 m_modOnHdReason = OnDiskUnmodified;
03604 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
03605 }
03606
03607 emit KTextEditor::Document::textRemoved(this, documentRange());
03608
03609 {
03610 QMutexLocker l(smartMutex());
03611 history()->doEdit( new KateEditInfo(Kate::CloseFileEdit, documentRange(), QStringList(), KTextEditor::Range(0,0,0,0), QStringList()) );
03612
03613
03614 m_buffer->clear();
03615
03616
03617 clearMarks ();
03618
03619
03620 m_undoManager->clearUndo();
03621 m_undoManager->clearRedo();
03622 }
03623
03624
03625 setModified(false);
03626
03627
03628 m_buffer->setHighlight(0);
03629
03630
03631 foreach (KateView * view, m_views )
03632 {
03633 view->clearSelection();
03634 view->clear();
03635 }
03636
03637 if (!m_reloading)
03638 {
03639
03640
03641 emit documentUrlChanged (this);
03642
03643
03644 setDocName (QString());
03645 }
03646
03647 return true;
03648 }
03649
03650 void KateDocument::setReadWrite( bool rw )
03651 {
03652 if (isReadWrite() != rw)
03653 {
03654 KParts::ReadWritePart::setReadWrite (rw);
03655
03656 foreach( KateView* view, m_views)
03657 {
03658 view->slotUpdateUndo();
03659 view->slotReadWriteChanged ();
03660 }
03661 }
03662 }
03663
03664 void KateDocument::setModified(bool m) {
03665
03666 if (isModified() != m) {
03667 KParts::ReadWritePart::setModified (m);
03668
03669 foreach( KateView* view,m_views)
03670 {
03671 view->slotUpdateUndo();
03672 }
03673
03674 emit modifiedChanged (this);
03675 }
03676
03677 m_undoManager->setModified (m);
03678 }
03679
03680
03681
03682
03683 void KateDocument::makeAttribs(bool needInvalidate)
03684 {
03685 foreach(KateView *view,m_views)
03686 view->renderer()->updateAttributes ();
03687
03688 if (needInvalidate)
03689 m_buffer->invalidateHighlighting();
03690
03691 foreach(KateView *view,m_views)
03692 {
03693 view->tagAll();
03694 view->updateView (true);
03695 }
03696 }
03697
03698
03699 void KateDocument::internalHlChanged()
03700 {
03701 makeAttribs();
03702 }
03703
03704 void KateDocument::addView(KTextEditor::View *view) {
03705 if (!view)
03706 return;
03707
03708 m_views.append( static_cast<KateView*>(view) );
03709 m_textEditViews.append( view );
03710
03711 foreach(KTextEditor::SmartRange* highlight, m_documentHighlights) {
03712 Q_ASSERT(dynamic_cast<KateView*>(view));
03713 static_cast<KateView*>(view)->addExternalHighlight(highlight, m_documentDynamicHighlights.contains(highlight));
03714 }
03715
03716
03717 if (!m_fileType.isEmpty())
03718 readVariableLine(KateGlobal::self()->modeManager()->fileType(m_fileType).varLine, true);
03719
03720
03721 readVariables (true);
03722
03723 setActiveView(view);
03724 }
03725
03726 void KateDocument::removeView(KTextEditor::View *view) {
03727 if (!view)
03728 return;
03729
03730 if (activeView() == view)
03731 setActiveView(0L);
03732
03733 m_views.removeAll( (KateView *) view );
03734 m_textEditViews.removeAll( view );
03735 }
03736
03737 void KateDocument::setActiveView(KTextEditor::View* view)
03738 {
03739 if ( m_activeView == view ) return;
03740
03741 if (m_activeView) {
03742 disconnect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), this, SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03743 }
03744
03745 m_activeView = (KateView*)view;
03746
03747 if (m_activeView) {
03748 connect(m_activeView, SIGNAL(selectionChanged(KTextEditor::View*)), SIGNAL(activeViewSelectionChanged(KTextEditor::View*)));
03749 }
03750 }
03751
03752 bool KateDocument::ownedView(KateView *view) {
03753
03754 return (m_views.contains(view));
03755 }
03756
03757 uint KateDocument::toVirtualColumn( const KTextEditor::Cursor& cursor )
03758 {
03759 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03760
03761 if (textLine)
03762 return textLine->toVirtualColumn(cursor.column(), config()->tabWidth());
03763 else
03764 return 0;
03765 }
03766
03767 bool KateDocument::typeChars ( KateView *view, const QString &chars )
03768 {
03769
03770 QMutexLocker l(smartMutex());
03771
03772 KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorPosition().line ());
03773
03774 if (!textLine)
03775 return false;
03776
03777 bool bracketInserted = false;
03778 QString buf;
03779 QChar c;
03780 for( int z = 0; z < chars.length(); z++ )
03781 {
03782 QChar ch = c = chars[z];
03783
03784 if (ch.isPrint() || ch == QChar::fromAscii('\t'))
03785 {
03786 buf.append (ch);
03787
03788 if (!bracketInserted && (config()->configFlags() & KateDocumentConfig::cfAutoBrackets))
03789 {
03790 QChar end_ch;
03791 bool complete = true;
03792 QChar prevChar = textLine->at(view->cursorPosition().column()-1);
03793 QChar nextChar = textLine->at(view->cursorPosition().column());
03794 switch(ch.toAscii()) {
03795 case '(': end_ch = ')'; break;
03796 case '[': end_ch = ']'; break;
03797 case '{': end_ch = '}'; break;
03798 case '\'':end_ch = '\'';break;
03799 case '"': end_ch = '"'; break;
03800 default: complete = false;
03801 }
03802 if (complete)
03803 {
03804 if (view->selection())
03805 {
03806 buf.append (view->selectionText());
03807 buf.append (end_ch);
03808 bracketInserted = true;
03809 }
03810 else
03811 {
03812 if ( ( (ch == '\'' || ch == '"') &&
03813 (prevChar.isLetterOrNumber() || prevChar == ch) )
03814 || nextChar.isLetterOrNumber()
03815 || (nextChar == end_ch && prevChar != ch) )
03816 {
03817 kDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
03818 }
03819 else
03820 {
03821 buf.append (end_ch);
03822 bracketInserted = true;
03823 }
03824 }
03825 }
03826 }
03827 }
03828 }
03829
03830 if (buf.isEmpty())
03831 return false;
03832
03833 l.unlock();
03834
03835 editStart ();
03836
03837 if (!view->config()->persistentSelection() && view->selection() )
03838 view->removeSelectedText();
03839
03840 KTextEditor::Cursor oldCur (view->cursorPosition());
03841
03842 if (config()->configFlags() & KateDocumentConfig::cfOvr)
03843 removeText(KTextEditor::Range(view->cursorPosition(), qMin(buf.length(), textLine->length() - view->cursorPosition().column())));
03844
03845 insertText(view->cursorPosition(), buf);
03846 if (bracketInserted)
03847 view->setCursorPositionInternal (view->cursorPosition() - KTextEditor::Cursor(0,1));
03848
03849 KTextEditor::Cursor b(view->cursorPosition());
03850 m_indenter.userTypedChar (view, b, c);
03851
03852 editEnd ();
03853
03854 view->slotTextInserted (view, oldCur, chars);
03855 return true;
03856 }
03857
03858 void KateDocument::newLine( KateView *v )
03859 {
03860 editStart();
03861
03862 if( !v->config()->persistentSelection() && v->selection() )
03863 v->removeSelectedText();
03864
03865
03866 KTextEditor::Cursor c = v->cursorPosition();
03867
03868 if (c.line() > (int)lastLine())
03869 c.setLine(lastLine());
03870
03871 if (c.line() < 0)
03872 c.setLine(0);
03873
03874 uint ln = c.line();
03875
03876 KateTextLine::Ptr textLine = plainKateTextLine(ln);
03877
03878 if (c.column() > (int)textLine->length())
03879 c.setColumn(textLine->length());
03880
03881
03882 editWrapLine (c.line(), c.column());
03883
03884
03885 m_indenter.userTypedChar(v, v->cursorPosition(), '\n');
03886
03887 removeTrailingSpace( ln );
03888
03889 editEnd();
03890 }
03891
03892 void KateDocument::transpose( const KTextEditor::Cursor& cursor)
03893 {
03894 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
03895
03896 if (!textLine || (textLine->length() < 2))
03897 return;
03898
03899 uint col = cursor.column();
03900
03901 if (col > 0)
03902 col--;
03903
03904 if ((textLine->length() - col) < 2)
03905 return;
03906
03907 uint line = cursor.line();
03908 QString s;
03909
03910
03911
03912 s.append (textLine->at(col+1));
03913 s.append (textLine->at(col));
03914
03915
03916
03917 editStart ();
03918 editRemoveText (line, col, 2);
03919 editInsertText (line, col, s);
03920 editEnd ();
03921 }
03922
03923 void KateDocument::backspace( KateView *view, const KTextEditor::Cursor& c )
03924 {
03925 if ( !view->config()->persistentSelection() && view->selection() ) {
03926 view->removeSelectedText();
03927 return;
03928 }
03929
03930 uint col = qMax( c.column(), 0 );
03931 uint line = qMax( c.line(), 0 );
03932
03933 if ((col == 0) && (line == 0))
03934 return;
03935
03936 int complement = 0;
03937 if (col > 0)
03938 {
03939 if (config()->configFlags() & KateDocumentConfig::cfAutoBrackets)
03940 {
03941
03942 KateTextLine::Ptr tl = m_buffer->plainLine(line);
03943 if(!tl) return;
03944 QChar prevChar = tl->at(col-1);
03945 QChar nextChar = tl->at(col);
03946
03947 if ( (prevChar == '"' && nextChar == '"') ||
03948 (prevChar == '\'' && nextChar == '\'') ||
03949 (prevChar == '(' && nextChar == ')') ||
03950 (prevChar == '[' && nextChar == ']') ||
03951 (prevChar == '{' && nextChar == '}') )
03952 {
03953 complement = 1;
03954 }
03955 }
03956 if (!(config()->configFlags() & KateDocumentConfig::cfBackspaceIndents))
03957 {
03958
03959
03960 removeText(KTextEditor::Range(line, col-1, line, col+complement));
03961 }
03962 else
03963 {
03964
03965 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
03966
03967
03968 if (!textLine)
03969 return;
03970
03971 int colX = textLine->toVirtualColumn(col, config()->tabWidth());
03972 int pos = textLine->firstChar();
03973 if (pos > 0)
03974 pos = textLine->toVirtualColumn(pos, config()->tabWidth());
03975
03976 if (pos < 0 || pos >= (int)colX)
03977 {
03978
03979 indent( view, line, -1);
03980 }
03981 else
03982 removeText(KTextEditor::Range(line, col-1, line, col+complement));
03983 }
03984 }
03985 else
03986 {
03987
03988 if (line >= 1)
03989 {
03990 KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
03991
03992
03993 if (!textLine)
03994 return;
03995
03996 if (config()->wordWrap() && textLine->endsWith(QLatin1String(" ")))
03997 {
03998
03999 removeText (KTextEditor::Range(line-1, textLine->length()-1, line, 0));
04000 }
04001 else
04002 removeText (KTextEditor::Range(line-1, textLine->length(), line, 0));
04003 }
04004 }
04005 }
04006
04007 void KateDocument::del( KateView *view, const KTextEditor::Cursor& c )
04008 {
04009 if ( !view->config()->persistentSelection() && view->selection() ) {
04010 view->removeSelectedText();
04011 return;
04012 }
04013
04014 if( c.column() < (int) m_buffer->plainLine(c.line())->length())
04015 {
04016 removeText(KTextEditor::Range(c, 1));
04017 }
04018 else if ( c.line() < lastLine() )
04019 {
04020 removeText(KTextEditor::Range(c.line(), c.column(), c.line()+1, 0));
04021 }
04022 }
04023
04024 void KateDocument::paste ( KateView* view, QClipboard::Mode mode )
04025 {
04026 QString s = QApplication::clipboard()->text(mode);
04027
04028 if (s.isEmpty())
04029 return;
04030
04031 int lines = s.count (QChar::fromAscii ('\n'));
04032
04033 m_undoManager->setUndoDontMerge (true);
04034
04035 editStart (true, Kate::CutCopyPasteEdit);
04036
04037 if (!view->config()->persistentSelection() && view->selection() )
04038 view->removeSelectedText();
04039
04040 KTextEditor::Cursor pos = view->cursorPosition();
04041
04042 blockRemoveTrailingSpaces(true);
04043 insertText(pos, s, view->blockSelectionMode());
04044 blockRemoveTrailingSpaces(false);
04045
04046 for (int i = pos.line(); i < pos.line() + lines; ++i)
04047 removeTrailingSpace(i);
04048
04049 editEnd();
04050
04051
04052
04053
04054 if (view->blockSelectionMode())
04055 view->setCursorPositionInternal(pos + KTextEditor::Cursor(lines, 0));
04056
04057 if (config()->configFlags() & KateDocumentConfig::cfIndentPastedText)
04058 {
04059 KTextEditor::Range range = KTextEditor::Range(KTextEditor::Cursor(pos.line(), 0),
04060 KTextEditor::Cursor(pos.line() + lines, 0));
04061
04062 int start = view->selectionRange().start().line();
04063 const int end = view->selectionRange().end().line();
04064
04065 editStart();
04066
04067 blockRemoveTrailingSpaces(true);
04068 m_indenter.indent(view, range);
04069 blockRemoveTrailingSpaces(false);
04070
04071 for (; start <= end; ++start)
04072 removeTrailingSpace(start);
04073
04074 editEnd();
04075 }
04076
04077 if (!view->blockSelectionMode())
04078 emit charactersSemiInteractivelyInserted (pos, s);
04079 m_undoManager->setUndoDontMerge (true);
04080 }
04081
04082 void KateDocument::indent ( KateView *v, uint line, int change)
04083 {
04084
04085
04086 const bool hasSelection = v->selection();
04087 int start = v->selectionRange().start().line();
04088 const int end = v->selectionRange().end().line();
04089
04090 KTextEditor::Range range = hasSelection ? v->selectionRange() : KTextEditor::Range (KTextEditor::Cursor (line,0), KTextEditor::Cursor (line,0));
04091
04092 editStart();
04093 blockRemoveTrailingSpaces(true);
04094 m_indenter.changeIndent(range, change);
04095 blockRemoveTrailingSpaces(false);
04096
04097 if (hasSelection) {
04098 for (; start <= end; ++start)
04099 removeTrailingSpace(start);
04100 }
04101 editEnd();
04102 }
04103
04104 void KateDocument::align(KateView *view, const KTextEditor::Range &range)
04105 {
04106 editStart();
04107
04108 blockRemoveTrailingSpaces(true);
04109 m_indenter.indent(view, range);
04110 blockRemoveTrailingSpaces(false);
04111
04112 for (int start = range.start().line(); start <= range.end().line(); ++start) {
04113 removeTrailingSpace(start);
04114 }
04115
04116 editEnd();
04117 }
04118
04119
04120
04121
04122
04123 bool KateDocument::removeStringFromBeginning(int line, const QString &str)
04124 {
04125 KateTextLine::Ptr textline = m_buffer->plainLine(line);
04126
04127 KTextEditor::Cursor cursor (line, 0);
04128 bool there = textline->startsWith(str);
04129
04130 if (!there)
04131 {
04132 cursor.setColumn(textline->firstChar());
04133 there = textline->matchesAt(cursor.column(), str);
04134 }
04135
04136 if (there)
04137 {
04138
04139 removeText (KTextEditor::Range(cursor, str.length()));
04140 }
04141
04142 return there;
04143 }
04144
04145
04146
04147
04148
04149 bool KateDocument::removeStringFromEnd(int line, const QString &str)
04150 {
04151 KateTextLine::Ptr textline = m_buffer->plainLine(line);
04152
04153 KTextEditor::Cursor cursor (line, 0);
04154 bool there = textline->endsWith(str);
04155
04156 if (there)
04157 {
04158 cursor.setColumn(textline->length() - str.length());
04159 }
04160 else
04161 {
04162 cursor.setColumn(textline->lastChar() - str.length() + 1);
04163 there = textline->matchesAt(cursor.column(), str);
04164 }
04165
04166 if (there)
04167 {
04168
04169 removeText (KTextEditor::Range(cursor, str.length()));
04170 }
04171
04172 return there;
04173 }
04174
04175
04176
04177
04178 void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
04179 {
04180 QString commentLineMark = highlight()->getCommentSingleLineStart(attrib);
04181 int pos = -1;
04182
04183 if (highlight()->getCommentSingleLinePosition(attrib) == KateHighlighting::CSLPosColumn0)
04184 {
04185 pos = 0;
04186 commentLineMark += ' ';
04187 } else {
04188 const KateTextLine::Ptr l = kateTextLine(line);
04189 pos = l->firstChar();
04190 }
04191
04192 if (pos >= 0)
04193 insertText (KTextEditor::Cursor(line, pos), commentLineMark);
04194 }
04195
04196
04197
04198
04199
04200 bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
04201 {
04202 const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
04203 const QString longCommentMark = shortCommentMark + ' ';
04204
04205 editStart();
04206
04207
04208 bool removed = (removeStringFromBeginning(line, longCommentMark)
04209 || removeStringFromBeginning(line, shortCommentMark));
04210
04211 editEnd();
04212
04213 return removed;
04214 }
04215
04216
04217
04218
04219
04220 void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
04221 {
04222 const QString startCommentMark = highlight()->getCommentStart( attrib ) + ' ';
04223 const QString stopCommentMark = ' ' + highlight()->getCommentEnd( attrib );
04224
04225 editStart();
04226
04227
04228 insertText (KTextEditor::Cursor(line, 0), startCommentMark);
04229
04230
04231 const int col = m_buffer->plainLine(line)->length();
04232
04233
04234 insertText (KTextEditor::Cursor(line, col), stopCommentMark);
04235
04236 editEnd();
04237 }
04238
04239
04240
04241
04242
04243 bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
04244 {
04245 QString shortStartCommentMark = highlight()->getCommentStart( attrib );
04246 QString longStartCommentMark = shortStartCommentMark + ' ';
04247 QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
04248 QString longStopCommentMark = ' ' + shortStopCommentMark;
04249
04250 editStart();
04251
04252 #ifdef __GNUC__
04253 #warning "that's a bad idea, can lead to stray endings, FIXME"
04254 #endif
04255
04256 bool removedStart = (removeStringFromBeginning(line, longStartCommentMark)
04257 || removeStringFromBeginning(line, shortStartCommentMark));
04258
04259 bool removedStop = false;
04260 if (removedStart)
04261 {
04262
04263 removedStop = (removeStringFromEnd(line, longStopCommentMark)
04264 || removeStringFromEnd(line, shortStopCommentMark));
04265 }
04266
04267 editEnd();
04268
04269 return (removedStart || removedStop);
04270 }
04271
04272
04273
04274
04275
04276 void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
04277 {
04278 const QString startComment = highlight()->getCommentStart( attrib );
04279 const QString endComment = highlight()->getCommentEnd( attrib );
04280
04281 KTextEditor::Range range = view->selectionRange();
04282
04283 if ((range.end().column() == 0) && (range.end().line() > 0))
04284 range.end().setPosition(range.end().line() - 1, lineLength(range.end().line() - 1));
04285
04286 editStart();
04287
04288 insertText (range.end(), endComment);
04289 insertText (range.start(), startComment);
04290
04291 editEnd ();
04292
04293 }
04294
04295
04296
04297
04298 void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
04299 {
04300 const QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + ' ';
04301
04302 int sl = view->selectionRange().start().line();
04303 int el = view->selectionRange().end().line();
04304
04305
04306 if ((view->selectionRange().end().column() == 0) && (el > 0))
04307 {
04308 el--;
04309 }
04310
04311 editStart();
04312
04313
04314 for (int z = el; z >= sl; z--) {
04315
04316 addStartLineCommentToSingleLine(z, attrib );
04317 }
04318
04319 editEnd ();
04320
04321 }
04322
04323 bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
04324 {
04325 for(; line < (int)m_buffer->count(); line++) {
04326 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
04327
04328 if (!textLine)
04329 break;
04330
04331 col = textLine->nextNonSpaceChar(col);
04332 if(col != -1)
04333 return true;
04334 col = 0;
04335 }
04336
04337 line = -1;
04338 col = -1;
04339 return false;
04340 }
04341
04342 bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
04343 {
04344 while(true)
04345 {
04346 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
04347
04348 if (!textLine)
04349 break;
04350
04351 col = textLine->previousNonSpaceChar(col);
04352 if(col != -1) return true;
04353 if(line == 0) return false;
04354 --line;
04355 col = textLine->length();
04356 }
04357
04358 line = -1;
04359 col = -1;
04360 return false;
04361 }
04362
04363
04364
04365
04366
04367 bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
04368 {
04369 const QString startComment = highlight()->getCommentStart( attrib );
04370 const QString endComment = highlight()->getCommentEnd( attrib );
04371
04372 int sl = qMax<int> (0, view->selectionRange().start().line());
04373 int el = qMin<int> (view->selectionRange().end().line(), lastLine());
04374 int sc = view->selectionRange().start().column();
04375 int ec = view->selectionRange().end().column();
04376
04377
04378 if (ec != 0) {
04379 --ec;
04380 } else if (el > 0) {
04381 --el;
04382 ec = m_buffer->plainLine(el)->length() - 1;
04383 }
04384
04385 const int startCommentLen = startComment.length();
04386 const int endCommentLen = endComment.length();
04387
04388
04389
04390 bool remove = nextNonSpaceCharPos(sl, sc)
04391 && m_buffer->plainLine(sl)->matchesAt(sc, startComment)
04392 && previousNonSpaceCharPos(el, ec)
04393 && ( (ec - endCommentLen + 1) >= 0 )
04394 && m_buffer->plainLine(el)->matchesAt(ec - endCommentLen + 1, endComment);
04395
04396 if (remove) {
04397 editStart();
04398
04399 removeText (KTextEditor::Range(el, ec - endCommentLen + 1, el, ec + 1));
04400 removeText (KTextEditor::Range(sl, sc, sl, sc + startCommentLen));
04401
04402 editEnd ();
04403
04404 }
04405
04406 return remove;
04407 }
04408
04409 bool KateDocument::removeStartStopCommentFromRegion(const KTextEditor::Cursor &start,const KTextEditor::Cursor &end,int attrib)
04410 {
04411 const QString startComment = highlight()->getCommentStart( attrib );
04412 const QString endComment = highlight()->getCommentEnd( attrib );
04413 const int startCommentLen = startComment.length();
04414 const int endCommentLen = endComment.length();
04415
04416 const bool remove = m_buffer->plainLine(start.line())->matchesAt(start.column(), startComment)
04417 && m_buffer->plainLine(end.line())->matchesAt(end.column() - endCommentLen , endComment);
04418 if (remove) {
04419 editStart();
04420 removeText(KTextEditor::Range(end.line(), end.column() - endCommentLen, end.line(), end.column()));
04421 removeText(KTextEditor::Range(start, startCommentLen));
04422 editEnd();
04423 }
04424 return remove;
04425 }
04426
04427
04428
04429
04430
04431 bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib )
04432 {
04433 const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
04434 const QString longCommentMark = shortCommentMark + ' ';
04435
04436 int sl = view->selectionRange().start().line();
04437 int el = view->selectionRange().end().line();
04438
04439 if ((view->selectionRange().end().column() == 0) && (el > 0))
04440 {
04441 el--;
04442 }
04443
04444 bool removed = false;
04445
04446 editStart();
04447
04448
04449 for (int z = el; z >= sl; z--)
04450 {
04451
04452 removed = (removeStringFromBeginning(z, longCommentMark)
04453 || removeStringFromBeginning(z, shortCommentMark)
04454 || removed);
04455 }
04456
04457 editEnd();
04458
04459
04460 return removed;
04461 }
04462
04463
04464
04465
04466
04467 void KateDocument::comment( KateView *v, uint line,uint column, int change)
04468 {
04469
04470
04471
04472
04473 bool hassel = v->selection();
04474 int startAttrib, endAttrib;
04475 if ( hassel )
04476 {
04477 KateTextLine::Ptr ln = kateTextLine( v->selectionRange().start().line() );
04478 int l = v->selectionRange().start().line(), c = v->selectionRange().start().column();
04479 startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
04480
04481 ln = kateTextLine( v->selectionRange().end().line() );
04482 l = v->selectionRange().end().line(), c = v->selectionRange().end().column();
04483 endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
04484 }
04485 else
04486 {
04487 KateTextLine::Ptr ln = kateTextLine( line );
04488 if ( ln->length() )
04489 {
04490 startAttrib = ln->attribute( ln->firstChar() );
04491 endAttrib = ln->attribute( ln->lastChar() );
04492 }
04493 else
04494 {
04495 int l = line, c = 0;
04496 if ( nextNonSpaceCharPos( l, c ) || previousNonSpaceCharPos( l, c ) )
04497 startAttrib = endAttrib = kateTextLine( l )->attribute( c );
04498 else
04499 startAttrib = endAttrib = 0;
04500 }
04501 }
04502
04503 if ( ! highlight()->canComment( startAttrib, endAttrib ) )
04504 {
04505 kDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!";
04506 return;
04507 }
04508
04509 bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
04510 bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
04511 && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
04512
04513 bool removed = false;
04514
04515 if (change > 0)
04516 {
04517 if ( !hassel )
04518 {
04519 if ( hasStartLineCommentMark )
04520 addStartLineCommentToSingleLine( line, startAttrib );
04521 else if ( hasStartStopCommentMark )
04522 addStartStopCommentToSingleLine( line, startAttrib );
04523 }
04524 else
04525 {
04526
04527
04528
04529
04530
04531
04532
04533 if ( hasStartStopCommentMark &&
04534 ( !hasStartLineCommentMark || (
04535 ( v->selectionRange().start().column() > m_buffer->plainLine( v->selectionRange().start().line() )->firstChar() ) ||
04536 ( v->selectionRange().end().column() < ((int)m_buffer->plainLine( v->selectionRange().end().line() )->length()) )
04537 ) ) )
04538 addStartStopCommentToSelection( v, startAttrib );
04539 else if ( hasStartLineCommentMark )
04540 addStartLineCommentToSelection( v, startAttrib );
04541 }
04542 }
04543 else
04544 {
04545 if ( !hassel )
04546 {
04547 removed = ( hasStartLineCommentMark
04548 && removeStartLineCommentFromSingleLine( line, startAttrib ) )
04549 || ( hasStartStopCommentMark
04550 && removeStartStopCommentFromSingleLine( line, startAttrib ) );
04551 if ((!removed) && foldingTree()) {
04552 kDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)";
04553 int commentRegion=(highlight()->commentRegion(startAttrib));
04554 if (commentRegion){
04555 KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
04556 if (n) {
04557 KTextEditor::Cursor start,end;
04558 if ((n->nodeType()==(int)commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
04559 kDebug(13020)<<"Enclosing region found:"<<start.column()<<"/"<<start.line()<<"-"<<end.column()<<"/"<<end.line();
04560 removeStartStopCommentFromRegion(start,end,startAttrib);
04561 } else {
04562 kDebug(13020)<<"Enclosing region found, but not valid";
04563 kDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion;
04564 }
04565
04566 } else kDebug(13020)<<"No enclosing region found";
04567 } else kDebug(13020)<<"No comment region specified for current hl";
04568 }
04569 }
04570 else
04571 {
04572
04573 removed = ( hasStartLineCommentMark
04574 && removeStartLineCommentFromSelection( v, startAttrib ) )
04575 || ( hasStartStopCommentMark
04576 && removeStartStopCommentFromSelection( v, startAttrib ) );
04577 }
04578 }
04579 }
04580
04581 void KateDocument::transform( KateView *v, const KTextEditor::Cursor &c,
04582 KateDocument::TextTransform t )
04583 {
04584 editStart();
04585 KTextEditor::Cursor cursor = c;
04586
04587 if ( v->selection() )
04588 {
04589
04590 KTextEditor::Range selection = v->selectionRange();
04591
04592 KTextEditor::Range range(selection.start(), 0);
04593 while ( range.start().line() <= selection.end().line() )
04594 {
04595 int start = 0;
04596 int end = lineLength( range.start().line() );
04597
04598 if (range.start().line() == selection.start().line() || v->blockSelectionMode())
04599 start = selection.start().column();
04600
04601 if (range.start().line() == selection.end().line() || v->blockSelectionMode())
04602 end = selection.end().column();
04603
04604 if ( start > end )
04605 {
04606 int swapCol = start;
04607 start = end;
04608 end = swapCol;
04609 }
04610 range.start().setColumn( start );
04611 range.end().setColumn( end );
04612
04613 QString s = text( range );
04614 QString old = s;
04615
04616 if ( t == Uppercase )
04617 s = s.toUpper();
04618 else if ( t == Lowercase )
04619 s = s.toLower();
04620 else
04621 {
04622 KateTextLine::Ptr l = m_buffer->plainLine( range.start().line() );
04623 int p ( 0 );
04624 while( p < s.length() )
04625 {
04626
04627
04628
04629
04630 if ( ( ! range.start().column() && ! p ) ||
04631 ( ( range.start().line() == selection.start().line() || v->blockSelectionMode() ) &&
04632 ! p && ! highlight()->isInWord( l->at( range.start().column() - 1 )) ) ||
04633 ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
04634 )
04635 s[p] = s.at(p).toUpper();
04636 p++;
04637 }
04638 }
04639
04640 if ( s != old )
04641 {
04642 removeText( range );
04643 insertText( range.start(), s );
04644 }
04645
04646 range.setBothLines(range.start().line() + 1);
04647 }
04648
04649
04650 v->setSelection( selection );
04651
04652 } else {
04653 QString old = text( KTextEditor::Range(cursor, 1) );
04654 QString s;
04655 switch ( t ) {
04656 case Uppercase:
04657 s = old.toUpper();
04658 break;
04659 case Lowercase:
04660 s = old.toLower();
04661 break;
04662 case Capitalize:
04663 {
04664 KateTextLine::Ptr l = m_buffer->plainLine( cursor.line() );
04665 while ( cursor.column() > 0 && highlight()->isInWord( l->at( cursor.column() - 1 ), l->attribute( cursor.column() - 1 ) ) )
04666 cursor.setColumn(cursor.column() - 1);
04667 old = text( KTextEditor::Range(cursor, 1) );
04668 s = old.toUpper();
04669 }
04670 break;
04671 default:
04672 break;
04673 }
04674 if ( s != old )
04675 {
04676 removeText( KTextEditor::Range(cursor, 1) );
04677 insertText( cursor, s );
04678 }
04679 }
04680
04681 editEnd();
04682
04683 v->setCursorPosition( c );
04684 }
04685
04686 void KateDocument::joinLines( uint first, uint last )
04687 {
04688
04689 editStart();
04690 int line( first );
04691 while ( first < last )
04692 {
04693
04694
04695
04696
04697
04698 KateTextLine::Ptr l = kateTextLine( line );
04699 KateTextLine::Ptr tl = kateTextLine( line + 1 );
04700
04701 if ( !l || !tl )
04702 {
04703 editEnd();
04704 return;
04705 }
04706
04707 int pos = tl->firstChar();
04708 if ( pos >= 0 )
04709 {
04710 if (pos != 0)
04711 editRemoveText( line + 1, 0, pos );
04712 if ( !( l->length() == 0 || l->at( l->length() - 1 ).isSpace() ) )
04713 editInsertText( line + 1, 0, " " );
04714 }
04715 else
04716 {
04717
04718 editRemoveText( line + 1, 0, tl->length() );
04719 }
04720
04721 editUnWrapLine( line );
04722 first++;
04723 }
04724 editEnd();
04725 }
04726
04727 QString KateDocument::getWord( const KTextEditor::Cursor& cursor )
04728 {
04729 int start, end, len;
04730
04731 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
04732 len = textLine->length();
04733 start = end = cursor.column();
04734 if (start > len)
04735 return QString("");
04736
04737 while (start > 0 && highlight()->isInWord(textLine->at(start - 1), textLine->attribute(start - 1))) start--;
04738 while (end < len && highlight()->isInWord(textLine->at(end), textLine->attribute(end))) end++;
04739 len = end - start;
04740 return textLine->string().mid(start, len);
04741 }
04742
04743 void KateDocument::tagLines(int start, int end)
04744 {
04745 foreach(KateView *view,m_views)
04746 view->tagLines (start, end, true);
04747 }
04748
04749 void KateDocument::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end)
04750 {
04751
04752
04753
04754
04755
04756
04757
04758 foreach (KateView* view, m_views)
04759 view->tagLines(start, end, true);
04760 }
04761
04762 void KateDocument::repaintViews(bool paintOnlyDirty)
04763 {
04764 foreach(KateView *view,m_views)
04765 view->repaintText(paintOnlyDirty);
04766 }
04767
04768 inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
04769 inline bool isEndBracket ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
04770 inline bool isBracket ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
04771
04772
04773
04774
04775
04776
04777
04778
04779 void KateDocument::newBracketMark( const KTextEditor::Cursor& cursor, KTextEditor::Range& bm, int maxLines )
04780 {
04781 bm.start() = cursor;
04782
04783 if( findMatchingBracket( bm, maxLines ) )
04784 return;
04785
04786 bm = KTextEditor::Range::invalid();
04787
04788
04789
04790
04791
04792 }
04793
04794 bool KateDocument::findMatchingBracket( KTextEditor::Range& range, int maxLines )
04795 {
04796 KateTextLine::Ptr textLine = m_buffer->plainLine( range.start().line() );
04797 if( !textLine )
04798 return false;
04799
04800 QChar right = textLine->at( range.start().column() );
04801 QChar left = textLine->at( range.start().column() - 1 );
04802 QChar bracket;
04803
04804 if ( config()->configFlags() & KateDocumentConfig::cfOvr ) {
04805 if( isBracket( right ) ) {
04806 bracket = right;
04807 } else {
04808 return false;
04809 }
04810 } else if ( isBracket( left ) ) {
04811 range.start().setColumn(range.start().column() - 1);
04812 bracket = left;
04813 } else if ( isBracket( right ) ) {
04814 bracket = right;
04815 } else {
04816 return false;
04817 }
04818
04819 QChar opposite;
04820
04821 switch( bracket.toAscii() ) {
04822 case '{': opposite = '}'; break;
04823 case '}': opposite = '{'; break;
04824 case '[': opposite = ']'; break;
04825 case ']': opposite = '['; break;
04826 case '(': opposite = ')'; break;
04827 case ')': opposite = '('; break;
04828 default: return false;
04829 }
04830
04831 bool forward = isStartBracket( bracket );
04832 uint nesting = 0;
04833
04834 int minLine = qMax( range.start().line() - maxLines, 0 );
04835 int maxLine = qMin( range.start().line() + maxLines, documentEnd().line() );
04836
04837 range.end() = range.start();
04838 KateDocCursor cursor(range.start(), this);
04839 uchar validAttr = cursor.currentAttrib();
04840
04841 while( cursor.line() >= minLine && cursor.line() <= maxLine ) {
04842
04843 if( forward )
04844 cursor.moveForward(1);
04845 else
04846 cursor.moveBackward(1);
04847
04848 if( !cursor.validPosition() )
04849 return false;
04850
04851 if( cursor.currentAttrib() == validAttr )
04852 {
04853
04854 QChar c = cursor.currentChar();
04855 if( c == bracket ) {
04856 nesting++;
04857 } else if( c == opposite ) {
04858 if( nesting == 0 ) {
04859 if( forward )
04860 range.end() = cursor;
04861 else
04862 range.start() = cursor;
04863 return true;
04864 }
04865 nesting--;
04866 }
04867 }
04868
04869 if(cursor == KTextEditor::Cursor(0,0) || cursor >= documentEnd())
04870 return false;
04871 }
04872
04873 return false;
04874 }
04875
04876 void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
04877 {
04878 KParts::ReadWritePart::guiActivateEvent( ev );
04879
04880
04881 }
04882
04883 void KateDocument::setDocName (QString name )
04884 {
04885 if ( name == m_docName )
04886 return;
04887
04888 if ( !name.isEmpty() )
04889 {
04890
04891 m_docName = name;
04892 emit documentNameChanged (this);
04893 return;
04894 }
04895
04896
04897 if ( ! url().isEmpty()
04898 && (m_docName == url().fileName() || m_docName.startsWith (url().fileName() + " (") ) ) return;
04899
04900 int count = -1;
04901
04902 for (int z=0; z < KateGlobal::self()->kateDocuments().size(); ++z)
04903 {
04904 KateDocument *doc = (KateGlobal::self()->kateDocuments())[z];
04905
04906 if ( (doc != this) && (doc->url().fileName() == url().fileName()) )
04907 if ( doc->m_docNameNumber > count )
04908 count = doc->m_docNameNumber;
04909 }
04910
04911 m_docNameNumber = count + 1;
04912
04913 m_docName = url().fileName();
04914
04915 if (m_docName.isEmpty())
04916 m_docName = i18n ("Untitled");
04917
04918 if (m_docNameNumber > 0)
04919 m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
04920
04921 emit documentNameChanged (this);
04922 }
04923
04924 void KateDocument::slotModifiedOnDisk( KTextEditor::View * )
04925 {
04926 if ( m_isasking < 0 )
04927 {
04928 m_isasking = 0;
04929 return;
04930 }
04931
04932 if ( !s_fileChangedDialogsActivated || m_isasking )
04933 return;
04934
04935 if (m_modOnHd && !url().isEmpty())
04936 {
04937 m_isasking = 1;
04938
04939 QWidget *parentWidget(dialogParent());
04940
04941 KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), parentWidget );
04942 switch ( p.exec() )
04943 {
04944 case KateModOnHdPrompt::Save:
04945 {
04946 m_modOnHd = false;
04947 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(),
04948 url().url(),QString(),parentWidget,i18n("Save File"));
04949
04950 kDebug(13020)<<"got "<<res.URLs.count()<<" URLs";
04951 if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first(), parentWidget ) )
04952 {
04953 setEncoding( res.encoding );
04954
04955 if( ! saveAs( res.URLs.first() ) )
04956 {
04957 KMessageBox::error( parentWidget, i18n("Save failed") );
04958 m_modOnHd = true;
04959 }
04960 else
04961 emit modifiedOnDisk( this, false, OnDiskUnmodified );
04962 }
04963 else
04964 {
04965 m_modOnHd = true;
04966 }
04967
04968 m_isasking = 0;
04969 break;
04970 }
04971
04972 case KateModOnHdPrompt::Reload:
04973 m_modOnHd = false;
04974 emit modifiedOnDisk( this, false, OnDiskUnmodified );
04975 documentReload();
04976 m_isasking = 0;
04977 break;
04978
04979 case KateModOnHdPrompt::Ignore:
04980 m_modOnHd = false;
04981 emit modifiedOnDisk( this, false, OnDiskUnmodified );
04982 m_isasking = 0;
04983 break;
04984
04985 case KateModOnHdPrompt::Overwrite:
04986 m_modOnHd = false;
04987 emit modifiedOnDisk( this, false, OnDiskUnmodified );
04988 m_isasking = 0;
04989 save();
04990 break;
04991
04992 default:
04993 m_isasking = -1;
04994 }
04995 }
04996 }
04997
04998 void KateDocument::setModifiedOnDisk( ModifiedOnDiskReason reason )
04999 {
05000 m_modOnHdReason = reason;
05001 m_modOnHd = (reason != OnDiskUnmodified);
05002 emit modifiedOnDisk( this, (reason != OnDiskUnmodified), reason );
05003 }
05004
05005 class KateDocumentTmpMark
05006 {
05007 public:
05008 QString line;
05009 KTextEditor::Mark mark;
05010 };
05011
05012 void KateDocument::setModifiedOnDiskWarning (bool on)
05013 {
05014 s_fileChangedDialogsActivated = on;
05015 }
05016
05017 bool KateDocument::documentReload()
05018 {
05019 if ( !url().isEmpty() )
05020 {
05021 if (m_modOnHd && s_fileChangedDialogsActivated)
05022 {
05023 QWidget *parentWidget(dialogParent());
05024
05025 int i = KMessageBox::warningYesNoCancel
05026 (parentWidget, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
05027 i18n("File Was Changed on Disk"), KGuiItem(i18n("&Reload File")), KGuiItem(i18n("&Ignore Changes")));
05028
05029 if ( i != KMessageBox::Yes)
05030 {
05031 if (i == KMessageBox::No)
05032 {
05033 m_modOnHd = false;
05034 m_modOnHdReason = OnDiskUnmodified;
05035 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05036 }
05037
05038 return false;
05039 }
05040 }
05041
05042 emit aboutToReload(this);
05043
05044 if (clearOnDocumentReload())
05045 m_smartManager->clear(false);
05046
05047 QList<KateDocumentTmpMark> tmp;
05048
05049 for (QHash<int, KTextEditor::Mark*>::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i)
05050 {
05051 KateDocumentTmpMark m;
05052
05053 m.line = line (i.value()->line);
05054 m.mark = *i.value();
05055
05056 tmp.append (m);
05057 }
05058
05059 QString oldMode = mode ();
05060 bool byUser = m_fileTypeSetByUser;
05061
05062 m_storedVariables.clear();
05063
05064
05065 QVector<KTextEditor::Cursor> cursorPositions;
05066 cursorPositions.reserve(m_views.size());
05067 foreach (KateView *v, m_views)
05068 cursorPositions.append( v->cursorPosition() );
05069
05070 m_reloading = true;
05071 KateDocument::openUrl( url() );
05072 m_reloading = false;
05073
05074
05075
05076 QLinkedList<KateView*>::iterator it = m_views.begin();
05077 for(int i = 0; i < m_views.size(); ++i, ++it) {
05078 (*it)->setCursorPositionInternal( cursorPositions[i], m_config->tabWidth(), false );
05079 if ((*it)->isVisible()) {
05080 (*it)->repaintText(false);
05081 }
05082 }
05083
05084 for (int z=0; z < tmp.size(); z++)
05085 {
05086 if (z < (int)lines())
05087 {
05088 if (line(tmp[z].mark.line) == tmp[z].line)
05089 setMark (tmp[z].mark.line, tmp[z].mark.type);
05090 }
05091 }
05092
05093 if (byUser)
05094 setMode (oldMode);
05095
05096 return true;
05097 }
05098
05099 return false;
05100 }
05101
05102 bool KateDocument::documentSave()
05103 {
05104 if( !url().isValid() || !isReadWrite() )
05105 return documentSaveAs();
05106
05107 return save();
05108 }
05109
05110 bool KateDocument::documentSaveAs()
05111 {
05112 QWidget *parentWidget(dialogParent());
05113
05114 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(),
05115 url().url(),QString(),parentWidget,i18n("Save File"));
05116
05117 if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) )
05118 return false;
05119
05120 setEncoding( res.encoding );
05121
05122 return saveAs( res.URLs.first() );
05123 }
05124
05125 void KateDocument::setWordWrap (bool on)
05126 {
05127 config()->setWordWrap (on);
05128 }
05129
05130 bool KateDocument::wordWrap () const
05131 {
05132 return config()->wordWrap ();
05133 }
05134
05135 void KateDocument::setWordWrapAt (uint col)
05136 {
05137 config()->setWordWrapAt (col);
05138 }
05139
05140 unsigned int KateDocument::wordWrapAt () const
05141 {
05142 return config()->wordWrapAt ();
05143 }
05144
05145 void KateDocument::setPageUpDownMovesCursor (bool on)
05146 {
05147 config()->setPageUpDownMovesCursor (on);
05148 }
05149
05150 bool KateDocument::pageUpDownMovesCursor () const
05151 {
05152 return config()->pageUpDownMovesCursor ();
05153 }
05154
05155 void KateDocument::dumpRegionTree()
05156 {
05157 m_buffer->foldingTree()->debugDump();
05158 }
05159
05160
05161 void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
05162 {
05163 m_buffer->lineInfo(info,line);
05164 }
05165
05166 KateCodeFoldingTree *KateDocument::foldingTree ()
05167 {
05168 return m_buffer->foldingTree();
05169 }
05170
05171 bool KateDocument::setEncoding (const QString &e)
05172 {
05173 setProberTypeForEncodingAutoDetection(KEncodingProber::None);
05174 return m_config->setEncoding(e);
05175 }
05176
05177 const QString &KateDocument::encoding() const
05178 {
05179 return m_config->encoding();
05180 }
05181
05182 void KateDocument::setProberTypeForEncodingAutoDetection (KEncodingProber::ProberType proberType)
05183 {
05184 m_config->setEncodingProberType(proberType);
05185 }
05186
05187 KEncodingProber::ProberType KateDocument::proberTypeForEncodingAutoDetection() const
05188 {
05189 return m_config->encodingProberType();
05190 }
05191
05192 void KateDocument::updateConfig ()
05193 {
05194 m_undoManager->updateConfig ();
05195
05196
05197 m_indenter.setMode (m_config->indentationMode());
05198 m_indenter.updateConfig();
05199
05200
05201 m_buffer->setTabWidth (config()->tabWidth());
05202
05203
05204 foreach (KateView * view, m_views)
05205 view->updateDocumentConfig ();
05206 }
05207
05208
05209
05210
05211
05212
05213
05214
05215 QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
05216 QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)");
05217 QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)");
05218 QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
05219
05220 void KateDocument::readVariables(bool onlyViewAndRenderer)
05221 {
05222 if (!onlyViewAndRenderer)
05223 m_config->configStart();
05224
05225
05226 KateView *v;
05227 foreach (v,m_views)
05228 {
05229 v->config()->configStart();
05230 v->renderer()->config()->configStart();
05231 }
05232
05233 for (int i=0; i < qMin( 9, lines() ); ++i )
05234 {
05235 readVariableLine( line( i ), onlyViewAndRenderer );
05236 }
05237 if ( lines() > 10 )
05238 {
05239 for ( int i = qMax( 10, lines() - 10); i < lines(); i++ )
05240 {
05241 readVariableLine( line( i ), onlyViewAndRenderer );
05242 }
05243 }
05244
05245 if (!onlyViewAndRenderer)
05246 m_config->configEnd();
05247
05248 foreach (v,m_views)
05249 {
05250 v->config()->configEnd();
05251 v->renderer()->config()->configEnd();
05252 }
05253 }
05254
05255 void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
05256 {
05257
05258
05259 if (!t.contains("kate"))
05260 return;
05261
05262
05263 QString s;
05264
05265
05266 if ( kvLine.indexIn( t ) > -1 )
05267 {
05268 s = kvLine.cap(1);
05269
05270 kDebug (13020) << "normal variable line kate: matched: " << s;
05271 }
05272 else if (kvLineWildcard.indexIn( t ) > -1)
05273 {
05274 QStringList wildcards (kvLineWildcard.cap(1).split (';', QString::SkipEmptyParts));
05275 QString nameOfFile = url().fileName();
05276
05277 bool found = false;
05278 for (int i = 0; !found && i < wildcards.size(); ++i)
05279 {
05280 QRegExp wildcard (wildcards[i], Qt::CaseSensitive, QRegExp::Wildcard);
05281
05282 found = wildcard.exactMatch (nameOfFile);
05283 }
05284
05285
05286 if (!found)
05287 return;
05288
05289 s = kvLineWildcard.cap(2);
05290
05291 kDebug (13020) << "guarded variable line kate-wildcard: matched: " << s;
05292 }
05293 else if (kvLineMime.indexIn( t ) > -1)
05294 {
05295 QStringList types (kvLineMime.cap(1).split (';', QString::SkipEmptyParts));
05296
05297
05298 if (!types.contains (mimeType ()))
05299 return;
05300
05301 s = kvLineMime.cap(2);
05302
05303 kDebug (13020) << "guarded variable line kate-mimetype: matched: " << s;
05304 }
05305 else
05306 {
05307 return;
05308 }
05309
05310 QStringList vvl;
05311 vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
05312 << "line-numbers" << "icon-border" << "folding-markers"
05313 << "bookmark-sorting" << "auto-center-lines"
05314 << "icon-bar-color"
05315
05316 << "background-color" << "selection-color"
05317 << "current-line-color" << "bracket-highlight-color"
05318 << "word-wrap-marker-color"
05319 << "font" << "font-size" << "scheme";
05320 int spaceIndent = -1;
05321 bool replaceTabsSet = false;
05322 int p( 0 );
05323
05324 QString var, val;
05325 while ( (p = kvVar.indexIn( s, p )) > -1 )
05326 {
05327 p += kvVar.matchedLength();
05328 var = kvVar.cap( 1 );
05329 val = kvVar.cap( 2 ).trimmed();
05330 bool state;
05331 int n;
05332
05333
05334 if (onlyViewAndRenderer)
05335 {
05336 if ( vvl.contains( var ) )
05337 setViewVariable( var, val );
05338 }
05339 else
05340 {
05341
05342 if ( var == "word-wrap" && checkBoolValue( val, &state ) )
05343 setWordWrap( state );
05344
05345
05346 else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
05347 m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
05348 else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
05349 {
05350 m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
05351 replaceTabsSet = true;
05352 }
05353 else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
05354 m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
05355 else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
05356 m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
05357 else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
05358 m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
05359 else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
05360 m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
05361 else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
05362 m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
05363 else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
05364 m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
05365 else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
05366 m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
05367 else if ( var == "show-trailing-spaces" && checkBoolValue( val, &state ) )
05368 m_config->setConfigFlags( KateDocumentConfig::cfShowSpaces, state );
05369 else if ( var == "space-indent" && checkBoolValue( val, &state ) )
05370 {
05371
05372 spaceIndent = state;
05373 }
05374 else if ( var == "smart-home" && checkBoolValue( val, &state ) )
05375 m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
05376 else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
05377 m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
05378
05379
05380 else if ( var == "tab-width" && checkIntValue( val, &n ) )
05381 m_config->setTabWidth( n );
05382 else if ( var == "indent-width" && checkIntValue( val, &n ) )
05383 m_config->setIndentationWidth( n );
05384 else if ( var == "indent-mode" )
05385 {
05386 m_config->setIndentationMode( val );
05387 }
05388 else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 )
05389 m_config->setWordWrapAt( n );
05390
05391
05392 else if ( var == "eol" || var == "end-of-line" )
05393 {
05394 QStringList l;
05395 l << "unix" << "dos" << "mac";
05396 if ( (n = l.indexOf( val.toLower() )) != -1 )
05397 m_config->setEol( n );
05398 }
05399 else if ( var == "encoding" )
05400 m_config->setEncoding( val );
05401 else if (var == "presave-postdialog")
05402 setPreSavePostDialogFilterChecks(val.split(','));
05403 else if (var == "postsave")
05404 setPostSaveFilterChecks(val.split(','));
05405 else if (var == "postload")
05406 setPostLoadFilterChecks(val.split(','));
05407 else if ( var == "syntax" || var == "hl" )
05408 {
05409 setHighlightingMode( val );
05410 }
05411 else if ( var == "mode" )
05412 {
05413 setMode( val );
05414 }
05415
05416
05417 else if ( vvl.contains( var ) )
05418 setViewVariable( var, val );
05419 else
05420 {
05421 m_storedVariables.insert( var, val );
05422 emit variableChanged( this, var, val );
05423 }
05424 }
05425 }
05426
05427
05428
05429
05430
05431
05432
05433
05434 if ( !replaceTabsSet && spaceIndent >= 0 )
05435 {
05436 m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, spaceIndent > 0 );
05437 }
05438 }
05439
05440 void KateDocument::setViewVariable( QString var, QString val )
05441 {
05442 KateView *v;
05443 bool state;
05444 int n;
05445 QColor c;
05446 foreach (v,m_views)
05447 {
05448 if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
05449 v->config()->setDynWordWrap( state );
05450 else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
05451 v->config()->setPersistentSelection( state );
05452 else if ( var == "block-selection" && checkBoolValue( val, &state ) )
05453 v->setBlockSelectionMode( state );
05454
05455 else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
05456 v->config()->setLineNumbers( state );
05457 else if (var == "icon-border" && checkBoolValue( val, &state ) )
05458 v->config()->setIconBar( state );
05459 else if (var == "folding-markers" && checkBoolValue( val, &state ) )
05460 v->config()->setFoldingBar( state );
05461 else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
05462 v->config()->setAutoCenterLines( n );
05463 else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
05464 v->renderer()->config()->setIconBarColor( c );
05465
05466 else if ( var == "background-color" && checkColorValue( val, c ) )
05467 v->renderer()->config()->setBackgroundColor( c );
05468 else if ( var == "selection-color" && checkColorValue( val, c ) )
05469 v->renderer()->config()->setSelectionColor( c );
05470 else if ( var == "current-line-color" && checkColorValue( val, c ) )
05471 v->renderer()->config()->setHighlightedLineColor( c );
05472 else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
05473 v->renderer()->config()->setHighlightedBracketColor( c );
05474 else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
05475 v->renderer()->config()->setWordWrapMarkerColor( c );
05476 else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
05477 {
05478 QFont _f( v->renderer()->config()->font() );
05479
05480 if ( var == "font" )
05481 {
05482 _f.setFamily( val );
05483 _f.setFixedPitch( QFont( val ).fixedPitch() );
05484 }
05485 else
05486 _f.setPointSize( n );
05487
05488 v->renderer()->config()->setFont( _f );
05489 }
05490 else if ( var == "scheme" )
05491 {
05492 v->renderer()->config()->setSchema( val );
05493 }
05494 }
05495 }
05496
05497 bool KateDocument::checkBoolValue( QString val, bool *result )
05498 {
05499 val = val.trimmed().toLower();
05500 QStringList l;
05501 l << "1" << "on" << "true";
05502 if ( l.contains( val ) )
05503 {
05504 *result = true;
05505 return true;
05506 }
05507 l.clear();
05508 l << "0" << "off" << "false";
05509 if ( l.contains( val ) )
05510 {
05511 *result = false;
05512 return true;
05513 }
05514 return false;
05515 }
05516
05517 bool KateDocument::checkIntValue( QString val, int *result )
05518 {
05519 bool ret( false );
05520 *result = val.toInt( &ret );
05521 return ret;
05522 }
05523
05524 bool KateDocument::checkColorValue( QString val, QColor &c )
05525 {
05526 c.setNamedColor( val );
05527 return c.isValid();
05528 }
05529
05530
05531 QString KateDocument::variable( const QString &name ) const
05532 {
05533 if ( m_storedVariables.contains( name ) )
05534 return m_storedVariables[ name ];
05535
05536 return "";
05537 }
05538
05539
05540
05541 void KateDocument::slotModOnHdDirty (const QString &path)
05542 {
05543 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskModified))
05544 {
05545
05546 if ( ! m_digest.isEmpty() )
05547 {
05548 QByteArray tmp;
05549 if ( createDigest( tmp ) && tmp == m_digest )
05550 return;
05551 }
05552
05553 m_modOnHd = true;
05554 m_modOnHdReason = OnDiskModified;
05555
05556
05557 if (m_isasking == -1)
05558 m_isasking = false;
05559
05560 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05561 }
05562 }
05563
05564 void KateDocument::slotModOnHdCreated (const QString &path)
05565 {
05566 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskCreated))
05567 {
05568 m_modOnHd = true;
05569 m_modOnHdReason = OnDiskCreated;
05570
05571
05572 if (m_isasking == -1)
05573 m_isasking = false;
05574
05575 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05576 }
05577 }
05578
05579 void KateDocument::slotModOnHdDeleted (const QString &path)
05580 {
05581 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskDeleted))
05582 {
05583 m_modOnHd = true;
05584 m_modOnHdReason = OnDiskDeleted;
05585
05586
05587 if (m_isasking == -1)
05588 m_isasking = false;
05589
05590 emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason);
05591 }
05592 }
05593
05594 bool KateDocument::createDigest( QByteArray &result )
05595 {
05596 bool ret = false;
05597 result = "";
05598 if ( url().isLocalFile() )
05599 {
05600 QFile f ( url().toLocalFile() );
05601 if ( f.open( QIODevice::ReadOnly) )
05602 {
05603 KMD5 md5;
05604 ret = md5.update( f );
05605 md5.hexDigest( result );
05606 f.close();
05607 ret = true;
05608 }
05609 }
05610 return ret;
05611 }
05612
05613 QString KateDocument::reasonedMOHString() const
05614 {
05615
05616 QString str = KStringHandler::csqueeze(url().pathOrUrl());
05617
05618 switch( m_modOnHdReason )
05619 {
05620 case OnDiskModified:
05621 return i18n("The file '%1' was modified by another program.", str );
05622 break;
05623 case OnDiskCreated:
05624 return i18n("The file '%1' was created by another program.", str );
05625 break;
05626 case OnDiskDeleted:
05627 return i18n("The file '%1' was deleted by another program.", str );
05628 break;
05629 default:
05630 return QString();
05631 }
05632 return QString();
05633 }
05634
05635 void KateDocument::removeTrailingSpace(int line)
05636 {
05637
05638 if (m_blockRemoveTrailingSpaces
05639 || !(config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn))
05640 return;
05641
05642 KateTextLine::Ptr ln = plainKateTextLine(line);
05643
05644 if (!ln || ln->length() == 0)
05645 return;
05646
05647 if (line == activeView()->cursorPosition().line()
05648 && activeView()->cursorPosition().column() >= qMax(0, ln->lastChar()))
05649 return;
05650
05651 const int p = ln->lastChar() + 1;
05652 const int l = ln->length() - p;
05653 if (l > 0) {
05654 m_blockRemoveTrailingSpaces = true;
05655 editRemoveText(line, p, l);
05656 m_blockRemoveTrailingSpaces = false;
05657 }
05658 }
05659
05660 void KateDocument::updateFileType (const QString &newType, bool user)
05661 {
05662 if (user || !m_fileTypeSetByUser)
05663 {
05664 if (!newType.isEmpty())
05665 {
05666 m_fileType = newType;
05667
05668 m_config->configStart();
05669
05670 if (!hlSetByUser && !KateGlobal::self()->modeManager()->fileType(newType).hl.isEmpty())
05671 {
05672 int hl (KateHlManager::self()->nameFind (KateGlobal::self()->modeManager()->fileType(newType).hl));
05673
05674 if (hl >= 0)
05675 m_buffer->setHighlight(hl);
05676 }
05677
05681 if (!KateGlobal::self()->modeManager()->fileType(newType).indenter.isEmpty())
05682 config()->setIndentationMode (KateGlobal::self()->modeManager()->fileType(newType).indenter);
05683
05684
05685 KateView *v;
05686 foreach (v,m_views)
05687 {
05688 v->config()->configStart();
05689 v->renderer()->config()->configStart();
05690 }
05691
05692 readVariableLine( KateGlobal::self()->modeManager()->fileType(newType).varLine );
05693
05694 m_config->configEnd();
05695 foreach (v,m_views)
05696 {
05697 v->config()->configEnd();
05698 v->renderer()->config()->configEnd();
05699 }
05700 }
05701 }
05702
05703
05704 emit modeChanged (this);
05705 }
05706
05707 void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
05708 *handled=true;
05709 *abortClosing=true;
05710 if (this->url().isEmpty())
05711 {
05712 QWidget *parentWidget(dialogParent());
05713
05714 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(),
05715 QString(),QString(),parentWidget,i18n("Save File"));
05716
05717 if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) ) {
05718 *abortClosing=true;
05719 return;
05720 }
05721 setEncoding( res.encoding );
05722 saveAs( res.URLs.first() );
05723 *abortClosing=false;
05724 }
05725 else
05726 {
05727 save();
05728 *abortClosing=false;
05729 }
05730
05731 }
05732
05733 bool KateDocument::checkOverwrite( KUrl u, QWidget *parent )
05734 {
05735 if( !u.isLocalFile() )
05736 return true;
05737
05738 QFileInfo info( u.path() );
05739 if( !info.exists() )
05740 return true;
05741
05742 return KMessageBox::Cancel != KMessageBox::warningContinueCancel( parent,
05743 i18n( "A file named \"%1\" already exists. "
05744 "Are you sure you want to overwrite it?" , info.fileName() ),
05745 i18n( "Overwrite File?" ), KStandardGuiItem::overwrite(),
05746 KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous );
05747 }
05748
05749
05750
05751 bool KateDocument::insertTemplateTextImplementation ( const KTextEditor::Cursor &c, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
05752 if (m_templateHandler != 0)
05753 return false;
05754
05755 m_templateHandler = new KateTemplateHandler(this,c,templateString,initialValues);
05756
05757 connect(m_templateHandler, SIGNAL(destroyed(QObject *)), this, SLOT(templateHandlerDestroyed()));
05758
05759 return m_templateHandler->initOk();
05760 }
05761
05762 void KateDocument::testTemplateCode() {
05763
05764 qobject_cast<KTextEditor::TemplateInterface*>(activeView())->insertTemplateText(activeView()->cursorPosition(),"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
05765 }
05766
05767
05768 bool KateDocument::invokeTemplateHandler(int key) {
05769 if (m_templateHandler) return (*m_templateHandler)(key);
05770 return false;
05771 }
05772
05773 void KateDocument::templateHandlerDestroyed() {
05774 m_templateHandler=0;
05775 }
05776
05777 KateView * KateDocument::activeKateView( ) const
05778 {
05779 return static_cast<KateView*>(m_activeView);
05780 }
05781
05782 KTextEditor::Cursor KateDocument::documentEnd( ) const
05783 {
05784 return KTextEditor::Cursor(lastLine(), lineLength(lastLine()));
05785 }
05786
05787
05788
05789 int KateDocument::currentRevision() const
05790 {
05791 return m_smartManager->currentRevision();
05792 }
05793
05794 void KateDocument::releaseRevision(int revision) const
05795 {
05796 m_smartManager->releaseRevision(revision);
05797 }
05798
05799 void KateDocument::useRevision(int revision)
05800 {
05801 m_smartManager->useRevision(revision);
05802 }
05803
05804 KTextEditor::Cursor KateDocument::translateFromRevision(const KTextEditor::Cursor& cursor, KTextEditor::SmartCursor::InsertBehavior insertBehavior) const
05805 {
05806 return m_smartManager->translateFromRevision(cursor, insertBehavior);
05807 }
05808
05809 KTextEditor::Range KateDocument::translateFromRevision(const KTextEditor::Range& range, KTextEditor::SmartRange::InsertBehaviors insertBehavior) const
05810 {
05811 return m_smartManager->translateFromRevision(range, insertBehavior);
05812 }
05813
05814 KTextEditor::SmartCursor* KateDocument::newSmartCursor( const KTextEditor::Cursor & position, KTextEditor::SmartCursor::InsertBehavior insertBehavior )
05815 {
05816 return m_smartManager->newSmartCursor(position, insertBehavior, false);
05817 }
05818
05819 KTextEditor::SmartRange * KateDocument::newSmartRange( const KTextEditor::Range & range, KTextEditor::SmartRange * parent, KTextEditor::SmartRange::InsertBehaviors insertBehavior )
05820 {
05821 return m_smartManager->newSmartRange( range, parent, insertBehavior, false );
05822 }
05823
05824 KTextEditor::SmartRange * KateDocument::newSmartRange( KTextEditor::SmartCursor * start, KTextEditor::SmartCursor * end, KTextEditor::SmartRange * parent, KTextEditor::SmartRange::InsertBehaviors insertBehavior )
05825 {
05826 KateSmartCursor* kstart = dynamic_cast<KateSmartCursor*>(start);
05827 KateSmartCursor* kend = dynamic_cast<KateSmartCursor*>(end);
05828 if (!kstart || !kend)
05829 return 0L;
05830 if (kstart->range() || kend->range())
05831 return 0L;
05832 return m_smartManager->newSmartRange(kstart, kend, parent, insertBehavior, false);
05833 }
05834
05835 void KateDocument::unbindSmartRange( KTextEditor::SmartRange * range )
05836 {
05837 m_smartManager->unbindSmartRange(range);
05838 }
05839
05840 bool KateDocument::replaceText( const KTextEditor::Range & range, const QString & s, bool block )
05841 {
05842
05843 editStart();
05844 bool changed = removeText(range, block);
05845 changed |= insertText(range.start(), s, block);
05846 editEnd();
05847 return changed;
05848 }
05849
05850 void KateDocument::addHighlightToDocument( KTextEditor::SmartRange * topRange, bool supportDynamic )
05851 {
05852 if (m_documentHighlights.contains(topRange))
05853 return;
05854
05855 m_documentHighlights.append(topRange);
05856
05857
05858 topRange->addWatcher(this);
05859
05860 if (supportDynamic) {
05861 m_documentDynamicHighlights.append(topRange);
05862 emit dynamicHighlightAdded(static_cast<KateSmartRange*>(topRange));
05863 }
05864
05865 foreach (KateView * view, m_views)
05866 view->addExternalHighlight(topRange, supportDynamic);
05867 }
05868
05869 void KateDocument::removeHighlightFromDocument( KTextEditor::SmartRange * topRange )
05870 {
05871 if (!m_documentHighlights.contains(topRange))
05872 return;
05873
05874 foreach (KateView * view, m_views)
05875 view->removeExternalHighlight(topRange);
05876
05877 m_documentHighlights.removeAll(topRange);
05878 topRange->removeWatcher(this);
05879
05880 if (m_documentDynamicHighlights.contains(topRange)) {
05881 m_documentDynamicHighlights.removeAll(topRange);
05882 emit dynamicHighlightRemoved(static_cast<KateSmartRange*>(topRange));
05883 }
05884 }
05885
05886 const QList< KTextEditor::SmartRange * > KateDocument::documentHighlights( ) const
05887 {
05888 return m_documentHighlights;
05889 }
05890
05891 void KateDocument::addHighlightToView( KTextEditor::View * view, KTextEditor::SmartRange * topRange, bool supportDynamic )
05892 {
05893 static_cast<KateView*>(view)->addExternalHighlight(topRange, supportDynamic);
05894 }
05895
05896 void KateDocument::removeHighlightFromView( KTextEditor::View * view, KTextEditor::SmartRange * topRange )
05897 {
05898 static_cast<KateView*>(view)->removeExternalHighlight(topRange);
05899 }
05900
05901 const QList< KTextEditor::SmartRange * > KateDocument::viewHighlights( KTextEditor::View * view ) const
05902 {
05903 return static_cast<KateView*>(view)->externalHighlights();
05904 }
05905
05906 void KateDocument::addActionsToDocument( KTextEditor::SmartRange * topRange )
05907 {
05908 if (m_documentActions.contains(topRange))
05909 return;
05910
05911 m_documentActions.append(topRange);
05912
05913
05914 topRange->addWatcher(this);
05915 }
05916
05917 void KateDocument::removeActionsFromDocument( KTextEditor::SmartRange * topRange )
05918 {
05919 if (!m_documentActions.contains(topRange))
05920 return;
05921
05922 m_documentActions.removeAll(topRange);
05923 topRange->removeWatcher(this);
05924 }
05925
05926 const QList< KTextEditor::SmartRange * > KateDocument::documentActions( ) const
05927 {
05928 return m_documentActions;
05929 }
05930
05931 void KateDocument::addActionsToView( KTextEditor::View * view, KTextEditor::SmartRange * topRange )
05932 {
05933 static_cast<KateView*>(view)->addActions(topRange);
05934 }
05935
05936 void KateDocument::removeActionsFromView( KTextEditor::View * view, KTextEditor::SmartRange * topRange )
05937 {
05938 static_cast<KateView*>(view)->removeActions(topRange);
05939 }
05940
05941 const QList< KTextEditor::SmartRange * > KateDocument::viewActions( KTextEditor::View * view ) const
05942 {
05943 return static_cast<KateView*>(view)->actions();
05944 }
05945
05946 void KateDocument::attributeDynamic( KTextEditor::Attribute::Ptr )
05947 {
05948
05949 }
05950
05951 void KateDocument::attributeNotDynamic( KTextEditor::Attribute::Ptr )
05952 {
05953
05954 }
05955
05956 void KateDocument::clearSmartInterface( )
05957 {
05958 clearDocumentHighlights();
05959 foreach (KateView* view, m_views)
05960 clearViewHighlights(view);
05961
05962 clearDocumentActions();
05963
05964 m_smartManager->clear(false);
05965 }
05966
05967 void KateDocument::deleteCursors( )
05968 {
05969 m_smartManager->deleteCursors(false);
05970 }
05971
05972 void KateDocument::deleteRanges( )
05973 {
05974 m_smartManager->deleteRanges(false);
05975 }
05976
05977 void KateDocument::clearDocumentHighlights( )
05978 {
05979 m_documentHighlights.clear();
05980 }
05981
05982 void KateDocument::clearViewHighlights( KTextEditor::View * view )
05983 {
05984 static_cast<KateView*>(view)->clearExternalHighlights();
05985 }
05986
05987 void KateDocument::clearDocumentActions( )
05988 {
05989 m_documentActions.clear();
05990 }
05991
05992 void KateDocument::clearViewActions( KTextEditor::View * view )
05993 {
05994 static_cast<KateView*>(view)->clearActions();
05995 }
05996
05997 void KateDocument::ignoreModifiedOnDiskOnce( )
05998 {
05999 m_isasking = -1;
06000 }
06001
06002 KateHighlighting * KateDocument::highlight( ) const
06003 {
06004 return m_buffer->highlight();
06005 }
06006
06007 uint KateDocument::getRealLine( unsigned int virtualLine )
06008 {
06009 return m_buffer->lineNumber (virtualLine);
06010 }
06011
06012 uint KateDocument::getVirtualLine( unsigned int realLine )
06013 {
06014 return m_buffer->lineVisibleNumber (realLine);
06015 }
06016
06017 uint KateDocument::visibleLines( )
06018 {
06019 return m_buffer->countVisible ();
06020 }
06021
06022 KateTextLine::Ptr KateDocument::kateTextLine( uint i )
06023 {
06024 m_buffer->ensureHighlighted (i);
06025 return m_buffer->plainLine (i);
06026 }
06027
06028 KateTextLine::Ptr KateDocument::plainKateTextLine( uint i )
06029 {
06030 return m_buffer->plainLine (i);
06031 }
06032
06033 bool KateDocument::undoDontMerge( ) const
06034 {
06035 return m_undoManager->undoDontMerge ();
06036 }
06037
06038 void KateDocument::setUndoAllowComplexMerge(bool allow)
06039 {
06040 m_undoManager->setAllowComplexMerge (allow);
06041 }
06042
06043 bool KateDocument::undoAllowComplexMerge( ) const
06044 {
06045 return m_undoManager->allowComplexMerge ();
06046 }
06047
06048 void KateDocument::setUndoDontMerge(bool dontMerge)
06049 {
06050 m_undoManager->setUndoDontMerge (dontMerge);
06051 }
06052
06053 bool KateDocument::isEditRunning() const
06054 {
06055 return editIsRunning;
06056 }
06057
06058 bool KateDocument::isWithUndo() const
06059 {
06060 Q_ASSERT(isEditRunning());
06061
06062 return editWithUndo;
06063 }
06064
06065 void KateDocument::setMergeAllEdits(bool merge)
06066 {
06067 setUndoDontMerge(true);
06068 setUndoAllowComplexMerge(merge);
06069 }
06070
06071 void KateDocument::rangeDeleted( KTextEditor::SmartRange * range )
06072 {
06073 removeHighlightFromDocument(range);
06074 removeActionsFromDocument(range);
06075 }
06076
06077
06078
06079
06080
06081 bool KateDocument::simpleMode ()
06082 {
06083 return KateGlobal::self()->simpleMode () && KateGlobal::self()->documentConfig()->allowSimpleMode ();
06084 }
06085
06086 KateDocument::LoadSaveFilterCheckPlugins* KateDocument::loadSaveFilterCheckPlugins()
06087 {
06088 K_GLOBAL_STATIC(KateDocument::LoadSaveFilterCheckPlugins, s_loadSaveFilterCheckPlugins)
06089 return s_loadSaveFilterCheckPlugins;
06090 }
06091
06092
06093 void KateDocument::setAnnotationModel( KTextEditor::AnnotationModel* model )
06094 {
06095 KTextEditor::AnnotationModel* oldmodel = m_annotationModel;
06096 m_annotationModel = model;
06097 emit annotationModelChanged(oldmodel, m_annotationModel);
06098 }
06099
06100 KTextEditor::AnnotationModel* KateDocument::annotationModel() const
06101 {
06102 return m_annotationModel;
06103 }
06104
06105
06106
06107 bool KateDocument::queryClose()
06108 {
06109 if ( !isReadWrite() || !isModified() )
06110 return true;
06111
06112 QString docName = documentName();
06113
06114 QWidget *parentWidget=widget();
06115 if(!parentWidget) parentWidget=QApplication::activeWindow();
06116
06117 int res = KMessageBox::warningYesNoCancel( parentWidget,
06118 i18n( "The document \"%1\" has been modified.\n"
06119 "Do you want to save your changes or discard them?" , docName ),
06120 i18n( "Close Document" ), KStandardGuiItem::save(), KStandardGuiItem::discard() );
06121
06122 bool abortClose=false;
06123 bool handled=false;
06124
06125 switch(res) {
06126 case KMessageBox::Yes :
06127 sigQueryClose(&handled,&abortClose);
06128 if (!handled)
06129 {
06130 if (url().isEmpty())
06131 {
06132 KUrl url = KFileDialog::getSaveUrl(KUrl(), QString(), parentWidget);
06133 if (url.isEmpty())
06134 return false;
06135
06136 saveAs( url );
06137 }
06138 else
06139 {
06140 save();
06141 }
06142 } else if (abortClose) return false;
06143 return waitSaveComplete();
06144 case KMessageBox::No :
06145 return true;
06146 default :
06147 return false;
06148 }
06149 }
06150
06151
06152 void KateDocument::slotCanceled() {
06153 m_savingToUrl=false;
06154 m_saveAs=false;
06155 }
06156
06157 void KateDocument::slotCompleted() {
06158 if (m_savingToUrl) {
06159 if (!m_postSaveFilterChecks.isEmpty())
06160 {
06161 LoadSaveFilterCheckPlugins *lscps1=loadSaveFilterCheckPlugins();
06162 foreach(const QString& checkplugin, m_postSaveFilterChecks)
06163 {
06164 if (lscps1->postSaveFilterCheck(checkplugin,this,m_saveAs)==false)
06165 break;
06166 }
06167 }
06168 emit documentSavedOrUploaded(this,m_saveAs);
06169 }
06170 m_savingToUrl=false;
06171 m_saveAs=false;
06172 }
06173
06174 bool KateDocument::save() {
06175 m_saveAs = false;
06176 return KTextEditor::Document::save();
06177 }
06178
06179 bool KateDocument::saveAs( const KUrl &url ) {
06180 m_saveAs = true;
06181 return KTextEditor::Document::saveAs(url);
06182 }
06183
06184
06185 #ifdef FAST_DEBUG_ENABLE
06186 # undef FAST_DEBUG_ENABLE
06187 #endif
06188 #undef FAST_DEBUG
06189
06190