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