summaryrefslogtreecommitdiff
path: root/noncore/apps/tinykate/libkate/document/katedocument.cpp
authorjowenn <jowenn>2002-11-10 21:08:01 (UTC)
committer jowenn <jowenn>2002-11-10 21:08:01 (UTC)
commite97a6da57804aa14907dec327fbae71bff9b383e (patch) (side-by-side diff)
tree15f6ee292dba24bdda72f5c72f6d2224c3516763 /noncore/apps/tinykate/libkate/document/katedocument.cpp
parent7c012ee8cd16d8befacc6f6750711443fac0fd5e (diff)
downloadopie-e97a6da57804aa14907dec327fbae71bff9b383e.zip
opie-e97a6da57804aa14907dec327fbae71bff9b383e.tar.gz
opie-e97a6da57804aa14907dec327fbae71bff9b383e.tar.bz2
import of tiny kate. (saving not possible yet)
Diffstat (limited to 'noncore/apps/tinykate/libkate/document/katedocument.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/apps/tinykate/libkate/document/katedocument.cpp3169
1 files changed, 3169 insertions, 0 deletions
diff --git a/noncore/apps/tinykate/libkate/document/katedocument.cpp b/noncore/apps/tinykate/libkate/document/katedocument.cpp
new file mode 100644
index 0000000..0d84bcf
--- a/dev/null
+++ b/noncore/apps/tinykate/libkate/document/katedocument.cpp
@@ -0,0 +1,3169 @@
+/***************************************************************************
+ katedocument.cpp - description
+ -------------------
+ begin : Mon Jan 15 2001
+ copyright : (C) 2001 by Christoph "Crossfire" Cullmann
+ (C) 2002 by Joseph Wenninger
+ email : crossfire@babylon2k.de
+ jowenn@kde.org
+
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+/*
+ Copyright (C) 1998, 1999 Jochen Wilhelmy
+ digisnap@cs.tu-berlin.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "katedocument.h"
+
+
+#include <qfileinfo.h>
+#include <qdatetime.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <qpe/config.h>
+#include <qstring.h>
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <stdio.h>
+
+#include <qtimer.h>
+#include <qobject.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qfont.h>
+#include <qpainter.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qtextcodec.h>
+#include <kglobal.h>
+
+#include <klocale.h>
+//#include <kcharsets.h>
+#include <kdebug.h>
+//#include <kinstance.h>
+
+#include <kglobalsettings.h>
+//#include <kaction.h>
+//#include <kstdaction.h>
+
+#include "../view/kateview.h"
+#include "katebuffer.h"
+#include "katetextline.h"
+
+#include "katecmd.h"
+
+KateAction::KateAction(Action a, PointStruc &cursor, int len, const QString &text)
+ : action(a), cursor(cursor), len(len), text(text) {
+}
+
+KateActionGroup::KateActionGroup(PointStruc &aStart, int type)
+ : start(aStart), action(0L), undoType(type) {
+}
+
+KateActionGroup::~KateActionGroup() {
+ KateAction *current, *next;
+
+ current = action;
+ while (current) {
+ next = current->next;
+ delete current;
+ current = next;
+ }
+}
+
+void KateActionGroup::insertAction(KateAction *a) {
+ a->next = action;
+ action = a;
+}
+
+const char * KateActionGroup::typeName(int type)
+{
+ // return a short text description of the given undo group type suitable for a menu
+ // not the lack of i18n's, the caller is expected to handle translation
+ switch (type) {
+ case ugPaste : return "Paste Text";
+ case ugDelBlock : return "Selection Overwrite";
+ case ugIndent : return "Indent";
+ case ugUnindent : return "Unindent";
+ case ugComment : return "Comment";
+ case ugUncomment : return "Uncomment";
+ case ugReplace : return "Text Replace";
+ case ugSpell : return "Spell Check";
+ case ugInsChar : return "Typing";
+ case ugDelChar : return "Delete Text";
+ case ugInsLine : return "New Line";
+ case ugDelLine : return "Delete Line";
+ }
+ return "";
+}
+
+const int KateDocument::maxAttribs = 32;
+
+QStringList KateDocument::searchForList = QStringList();
+QStringList KateDocument::replaceWithList = QStringList();
+
+uint KateDocument::uniqueID = 0;
+
+QPtrDict<KateDocument::KateDocPrivate>* KateDocument::d_ptr = 0;
+
+
+KateDocument::KateDocument(bool bSingleViewMode, bool bBrowserView,
+ QWidget *parentWidget, const char *widgetName,
+ QObject *, const char *)
+ : Kate::Document (),
+ myFont(KGlobalSettings::generalFont()), myFontBold(KGlobalSettings::generalFont()), myFontItalic(KGlobalSettings::generalFont()), myFontBI(KGlobalSettings::generalFont()),
+ myFontMetrics (myFont), myFontMetricsBold (myFontBold), myFontMetricsItalic (myFontItalic), myFontMetricsBI (myFontBI),
+ hlManager(HlManager::self ())
+{
+
+ d(this)->hlSetByUser = false;
+ PreHighlightedTill=0;
+ RequestPreHighlightTill=0;
+
+ m_bSingleViewMode=bSingleViewMode;
+ m_bBrowserView = bBrowserView;
+
+ m_url = QString::null;
+
+ // NOTE: QFont::CharSet doesn't provide all the charsets KDE supports
+ // (esp. it doesn't distinguish between UTF-8 and iso10646-1)
+
+ myEncoding = QString::fromLatin1(QTextCodec::codecForLocale()->name());
+
+ maxLength = -1;
+
+ setFont (KGlobalSettings::generalFont());
+
+ myDocID = uniqueID;
+ uniqueID++;
+
+ myDocName = QString ("");
+ fileInfo = new QFileInfo ();
+
+ myCmd = new KateCmd (this);
+
+ connect(this,SIGNAL(modifiedChanged ()),this,SLOT(slotModChanged ()));
+
+ buffer = new KWBuffer;
+ connect(buffer, SIGNAL(linesChanged(int)), this, SLOT(slotBufferChanged()));
+// connect(buffer, SIGNAL(textChanged()), this, SIGNAL(textChanged()));
+ connect(buffer, SIGNAL(needHighlight(long,long)),this,SLOT(slotBufferHighlight(long,long)));
+
+ colors[0] = KGlobalSettings::baseColor();
+ colors[1] = KGlobalSettings::highlightColor();
+
+ m_attribs = new Attribute[maxAttribs];
+
+ m_highlight = 0L;
+ tabChars = 8;
+
+ m_singleSelection = false;
+
+ newDocGeometry = false;
+ readOnly = false;
+ newDoc = false;
+
+ modified = false;
+
+ undoList.setAutoDelete(true);
+ undoState = 0;
+ undoSteps = 50;
+
+ pseudoModal = 0L;
+ clear();
+
+ setHighlight(0); //calls updateFontData()
+ // if the user changes the highlight with the dialog, notify the doc
+ connect(hlManager,SIGNAL(changed()),SLOT(hlChanged()));
+
+ newDocGeometry = false;
+
+ readConfig();
+
+ setReadOnly(false);
+}
+
+void KateDocument::setDontChangeHlOnSave()
+{
+ d(this)->hlSetByUser = true;
+}
+
+void KateDocument::setFont (QFont font)
+{
+ kdDebug()<<"Kate:: setFont"<<endl;
+ int oldwidth=myFontMetrics.width('W'); //Quick & Dirty Hack (by JoWenn) //Remove in KDE 3.0
+ myFont = font;
+ myFontBold = QFont (font);
+ myFontBold.setBold (true);
+
+ myFontItalic = QFont (font);
+ myFontItalic.setItalic (true);
+
+ myFontBI = QFont (font);
+ myFontBI.setBold (true);
+ myFontBI.setItalic (true);
+
+ myFontMetrics = CachedFontMetrics (myFont);
+ myFontMetricsBold = CachedFontMetrics (myFontBold);
+ myFontMetricsItalic = CachedFontMetrics (myFontItalic);
+ myFontMetricsBI = CachedFontMetrics (myFontBI);
+ int newwidth=myFontMetrics.width('W'); //Quick & Dirty Hack (by JoWenn) //Remove in KDE 3.0
+ maxLength=maxLength*(float)newwidth/(float)oldwidth; //Quick & Dirty Hack (by JoWenn) //Remove in KDE 3.0
+
+ updateFontData();
+ updateViews(); //Quick & Dirty Hack (by JoWenn) //Remove in KDE 3.0
+
+}
+
+long KateDocument::needPreHighlight(long till)
+{
+ int max=numLines()-1;
+ if (till>max)
+ {
+ till=max;
+ }
+ if (PreHighlightedTill>=till) return -1;
+
+ long tmp=RequestPreHighlightTill;
+ if (RequestPreHighlightTill<till)
+ {
+ RequestPreHighlightTill=till;
+ if (tmp<=PreHighlightedTill) QTimer::singleShot(10,this,SLOT(doPreHighlight()));
+ }
+ return RequestPreHighlightTill;
+}
+
+void KateDocument::doPreHighlight()
+{
+ int from = PreHighlightedTill;
+ int till = PreHighlightedTill+200;
+ int max = numLines()-1;
+ if (till > max)
+ {
+ till = max;
+ }
+ PreHighlightedTill = till;
+ updateLines(from,till);
+ emit preHighlightChanged(PreHighlightedTill);
+ if (PreHighlightedTill<RequestPreHighlightTill)
+ QTimer::singleShot(10,this,SLOT(doPreHighlight()));
+}
+
+KateDocument::~KateDocument()
+{
+ m_highlight->release();
+
+ if ( !m_bSingleViewMode )
+ {
+ m_views.setAutoDelete( true );
+ m_views.clear();
+ m_views.setAutoDelete( false );
+ }
+ delete_d(this);
+}
+
+void KateDocument::openURL(const QString &filename)
+{
+
+ m_file=filename;
+ fileInfo->setFile (m_file);
+ setMTime();
+
+ if (!fileInfo->exists() || !fileInfo->isReadable())
+ {
+ qDebug("File doesn't exit or couldn't be read");
+ return false;
+ }
+
+ buffer->clear();
+#warning fixme
+// buffer->insertFile(0, m_file, KGlobal::charsets()->codecForName(myEncoding));
+ qDebug("Telling buffer to open file");
+ buffer->insertFile(0, m_file, QTextCodec::codecForLocale());
+
+ setMTime();
+
+ if (myWordWrap)
+ wrapText (myWordWrapAt);
+
+ int hl = hlManager->wildcardFind( m_file );
+
+ setHighlight(hl);
+
+ updateLines();
+ updateViews();
+
+ emit fileNameChanged();
+
+ return true;
+}
+
+bool KateDocument::saveFile()
+{
+ QFile f( m_file );
+ if ( !f.open( IO_WriteOnly ) )
+ return false; // Error
+
+ QTextStream stream(&f);
+
+ stream.setEncoding(QTextStream::RawUnicode); // disable Unicode headers
+#warning fixme
+// stream.setCodec(KGlobal::charsets()->codecForName(myEncoding));
+ stream.setCodec(QTextCodec::codecForLocale()); // this line sets the mapper to the correct codec
+
+ int maxLine = numLines();
+ int line = 0;
+ while(true)
+ {
+ stream << getTextLine(line)->getString();
+ line++;
+ if (line >= maxLine) break;
+
+ if (eolMode == KateDocument::eolUnix) stream << "\n";
+ else if (eolMode == KateDocument::eolDos) stream << "\r\n";
+ else if (eolMode == KateDocument::eolMacintosh) stream << '\r';
+ };
+ f.close();
+
+ fileInfo->setFile (m_file);
+ setMTime();
+
+ if (!(d(this)->hlSetByUser))
+ {
+ int hl = hlManager->wildcardFind( m_file );
+
+ setHighlight(hl);
+ }
+ emit fileNameChanged ();
+
+ return (f.status() == IO_Ok);
+}
+
+KTextEditor::View *KateDocument::createView( QWidget *parent, const char *name )
+{
+ return new KateView( this, parent, name);
+}
+
+QString KateDocument::textLine( int line ) const
+{
+ TextLine::Ptr l = getTextLine( line );
+ if ( !l )
+ return QString();
+
+ return l->getString();
+}
+
+void KateDocument::replaceLine(const QString& s,int line)
+{
+ remove_Line(line,false);
+ insert_Line(s,line,true);
+}
+
+void KateDocument::insertLine( const QString &str, int l ) {
+ insert_Line(str,l,true);
+}
+
+void KateDocument::insert_Line(const QString& s,int line, bool update)
+{
+ kdDebug(13020)<<"KateDocument::insertLine "<<s<<QString(" %1").arg(line)<<endl;
+ TextLine::Ptr TL=new TextLine();
+ TL->append(s.unicode(),s.length());
+ buffer->insertLine(line,TL);
+ if (update)
+ {
+ newDocGeometry=true;
+ updateLines(line);
+ updateViews();
+ }
+}
+
+void KateDocument::insertAt( const QString &s, int line, int col, bool )
+{
+ VConfig c;
+ c.view = 0; // ### FIXME
+ c.cursor.x = col;
+ c.cursor.y = line;
+ c.cXPos = 0; // ### FIXME
+ c.flags = 0; // ### FIXME
+ insert( c, s );
+}
+
+void KateDocument::removeLine( int line ) {
+ remove_Line(line,true);
+}
+
+void KateDocument::remove_Line(int line,bool update)
+{
+ kdDebug(13020)<<"KateDocument::removeLine "<<QString("%1").arg(line)<<endl;
+ buffer->removeLine(line);
+// newDocGeometry=true;
+// if line==0)
+ if (update)
+ {
+ updateLines(line);
+ updateViews();
+ }
+}
+
+int KateDocument::length() const
+{
+ return text().length();
+}
+
+void KateDocument::setSelection( int , int , int , int )
+{
+}
+
+bool KateDocument::hasSelection() const
+{
+ return (selectEnd >= selectStart);
+}
+
+QString KateDocument::selection() const
+{
+ uint flags = 0;
+ TextLine::Ptr textLine;
+ int len, z, start, end, i;
+
+ len = 1;
+ if (!(flags & KateView::cfVerticalSelect)) {
+ for (z = selectStart; z <= selectEnd; z++) {
+ textLine = getTextLine(z);
+ len += textLine->numSelected();
+ if (textLine->isSelected()) len++;
+ }
+ QString s;
+ len = 0;
+ for (z = selectStart; z <= selectEnd; z++) {
+ textLine = getTextLine(z);
+ end = 0;
+ do {
+ start = textLine->findUnselected(end);
+ end = textLine->findSelected(start);
+ for (i = start; i < end; i++) {
+ s[len] = textLine->getChar(i);
+ len++;
+ }
+ } while (start < end);
+ if (textLine->isSelected()) {
+ s[len] = '\n';
+ len++;
+ }
+ }
+// s[len] = '\0';
+ return s;
+ } else {
+ for (z = selectStart; z <= selectEnd; z++) {
+ textLine = getTextLine(z);
+ len += textLine->numSelected() + 1;
+ }
+ QString s;
+ len = 0;
+ for (z = selectStart; z <= selectEnd; z++) {
+ textLine = getTextLine(z);
+ end = 0;
+ do {
+ start = textLine->findUnselected(end);
+ end = textLine->findSelected(start);
+ for (i = start; i < end; i++) {
+ s[len] = textLine->getChar(i);
+ len++;
+ }
+ } while (start < end);
+ s[len] = '\n';
+ len++;
+ }
+// s[len] = '\0'; // the final \0 is not counted in length()
+ return s;
+ }
+}
+
+int KateDocument::numLines() const
+{
+ return buffer->count();
+}
+
+
+TextLine::Ptr KateDocument::getTextLine(int line) const
+{
+ // This is a hack to get this stuff working.
+ return buffer->line(line);
+}
+
+int KateDocument::textLength(int line) {
+ TextLine::Ptr textLine = getTextLine(line);
+ if (!textLine) return 0;
+ return textLine->length();
+}
+
+void KateDocument::setTabWidth(int chars) {
+ if (tabChars == chars) return;
+ if (chars < 1) chars = 1;
+ if (chars > 16) chars = 16;
+ tabChars = chars;
+ updateFontData();
+
+ maxLength = -1;
+ for (int i=0; i < buffer->count(); i++)
+ {
+ TextLine::Ptr textLine = buffer->line(i);
+ int len = textWidth(textLine,textLine->length());
+ if (len > maxLength) {
+ maxLength = len;
+ longestLine = textLine;
+ }
+ }
+}
+
+void KateDocument::setReadOnly(bool m) {
+ KTextEditor::View *view;
+
+ if (m != readOnly) {
+ readOnly = m;
+// if (readOnly) recordReset();
+ for (view = m_views.first(); view != 0L; view = m_views.next() ) {
+ emit static_cast<KateView *>( view )->newStatus();
+ }
+ }
+}
+
+bool KateDocument::isReadOnly() const {
+ return readOnly;
+}
+
+void KateDocument::setNewDoc( bool m )
+{
+// KTextEditor::View *view;
+
+ if ( m != newDoc )
+ {
+ newDoc = m;
+//// if (readOnly) recordReset();
+// for (view = m_views.first(); view != 0L; view = m_views.next() ) {
+// emit static_cast<KateView *>( view )->newStatus();
+// }
+ }
+}
+
+bool KateDocument::isNewDoc() const {
+ return newDoc;
+}
+
+void KateDocument::setModified(bool m) {
+ KTextEditor::View *view;
+
+ if (m != modified) {
+ modified = m;
+ for (view = m_views.first(); view != 0L; view = m_views.next() ) {
+ emit static_cast<KateView *>( view )->newStatus();
+ }
+ emit modifiedChanged ();
+ }
+}
+
+bool KateDocument::isModified() const {
+ return modified;
+}
+
+void KateDocument::readConfig()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("Kate Document");
+
+ myWordWrap = config->readBoolEntry("Word Wrap On", false);
+ myWordWrapAt = config->readNumEntry("Word Wrap At", 80);
+ if (myWordWrap)
+ wrapText (myWordWrapAt);
+
+ setTabWidth(config->readNumEntry("TabWidth", 8));
+ setUndoSteps(config->readNumEntry("UndoSteps", 50));
+ m_singleSelection = config->readBoolEntry("SingleSelection", false);
+ myEncoding = config->readEntry("Encoding", QString::fromLatin1(QTextCodec::codecForLocale()->name()));
+ setFont (config->readFontEntry("Font", &myFont));
+
+ colors[0] = config->readColorEntry("Color Background", &colors[0]);
+ colors[1] = config->readColorEntry("Color Selected", &colors[1]);
+
+ config->sync();
+}
+
+void KateDocument::writeConfig()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("Kate Document");
+#if 0
+ cofig->writeEntry("Word Wrap On", myWordWrap);
+ config->writeEntry("Word Wrap At", myWordWrapAt);
+ config->writeEntry("TabWidth", tabChars);
+ config->writeEntry("UndoSteps", undoSteps);
+ config->writeEntry("SingleSelection", m_singleSelection);
+ config->writeEntry("Encoding", myEncoding);
+ config->writeEntry("Font", myFont);
+ config->writeEntry("Color Background", colors[0]);
+ config->writeEntry("Color Selected", colors[1]);
+#endif
+ config->sync();
+}
+
+void KateDocument::readSessionConfig(KConfig *config)
+{
+ m_url = config->readEntry("URL"); // ### doesn't this break the encoding? (Simon)
+ setHighlight(hlManager->nameFind(config->readEntry("Highlight")));
+ // anders: restore bookmarks if possible
+ QValueList<int> l = config->readIntListEntry("Bookmarks");
+ if ( l.count() ) {
+ for (uint i=0; i < l.count(); i++) {
+ if ( numLines() < l[i] ) break;
+ getTextLine( l[i] )->addMark( Bookmark );
+ }
+ }
+}
+
+void KateDocument::writeSessionConfig(KConfig *config)
+{
+#if 0
+ config->writeEntry("URL", m_url); // ### encoding?? (Simon)
+ config->writeEntry("Highlight", m_highlight->name());
+ // anders: save bookmarks
+ QList<Kate::Mark> l = marks();
+ QValueList<int> ml;
+ for (uint i=0; i < l.count(); i++) {
+ if ( l.at(i)->type == 1) // only save bookmarks
+ ml << l.at(i)->line;
+ }
+ if ( ml.count() )
+ config->writeEntry("Bookmarks", ml);
+#endif
+}
+
+
+void KateDocument::setHighlight(int n) {
+ Highlight *h;
+
+// hlNumber = n;
+
+ h = hlManager->getHl(n);
+ if (h == m_highlight) {
+ updateLines();
+ } else {
+ if (m_highlight != 0L) m_highlight->release();
+ h->use();
+ m_highlight = h;
+ makeAttribs();
+ }
+ PreHighlightedTill=0;
+ RequestPreHighlightTill=0;
+ emit(highlightChanged());
+}
+
+void KateDocument::makeAttribs() {
+ qDebug("KateDocument::makeAttribs()");
+ m_numAttribs = hlManager->makeAttribs(m_highlight, m_attribs, maxAttribs);
+ updateFontData();
+ updateLines();
+}
+
+void KateDocument::updateFontData() {
+ int maxAscent, maxDescent;
+ int tabWidth;
+ KateView *view;
+
+ maxAscent = myFontMetrics.ascent();
+ maxDescent = myFontMetrics.descent();
+ tabWidth = myFontMetrics.width(' ');
+
+ fontHeight = maxAscent + maxDescent + 1;
+ fontAscent = maxAscent;
+ m_tabWidth = tabChars*tabWidth;
+
+ for (view = views.first(); view != 0L; view = views.next() ) {
+ view->myViewInternal->drawBuffer->resize(view->width(),fontHeight);
+ view->tagAll();
+ view->updateCursor();
+ }
+}
+
+void KateDocument::hlChanged() { //slot
+ makeAttribs();
+ updateViews();
+}
+
+
+void KateDocument::addView(KTextEditor::View *view) {
+ views.append( static_cast<KateView *>( view ) );
+ KTextEditor::Document::addView( view );
+ connect( static_cast<KateView *>( view ), SIGNAL( destroyed() ), this, SLOT( slotViewDestroyed() ) );
+}
+
+void KateDocument::removeView(KTextEditor::View *view) {
+// if (undoView == view) recordReset();
+ disconnect( static_cast<KateView *>( view ), SIGNAL( destroyed() ), this, SLOT( slotViewDestroyed() ) );
+ views.removeRef( static_cast<KateView *>( view ) );
+ KTextEditor::Document::removeView( view );
+}
+
+void KateDocument::slotViewDestroyed()
+{
+ views.removeRef( static_cast<const KateView *>( sender() ) );
+}
+
+bool KateDocument::ownedView(KateView *view) {
+ // do we own the given view?
+ return (views.containsRef(view) > 0);
+}
+
+bool KateDocument::isLastView(int numViews) {
+ return ((int) views.count() == numViews);
+}
+
+int KateDocument::textWidth(const TextLine::Ptr &textLine, int cursorX) {
+ int x;
+ int z;
+ QChar ch;
+ Attribute *a;
+
+ x = 0;
+ for (z = 0; z < cursorX; z++) {
+ ch = textLine->getChar(z);
+ a = &m_attribs[textLine->getAttr(z)];
+
+ if (ch == '\t')
+ x += m_tabWidth - (x % m_tabWidth);
+ else if (a->bold && a->italic)
+ x += myFontMetricsBI.width(ch);
+ else if (a->bold)
+ x += myFontMetricsBold.width(ch);
+ else if (a->italic)
+ x += myFontMetricsItalic.width(ch);
+ else
+ x += myFontMetrics.width(ch);
+ }
+ return x;
+}
+
+int KateDocument::textWidth(PointStruc &cursor) {
+ if (cursor.x < 0)
+ cursor.x = 0;
+ if (cursor.y < 0)
+ cursor.y = 0;
+ if (cursor.y >= numLines())
+ cursor.y = lastLine();
+ return textWidth(getTextLine(cursor.y),cursor.x);
+}
+
+int KateDocument::textWidth(bool wrapCursor, PointStruc &cursor, int xPos) {
+ int len;
+ int x, oldX;
+ int z;
+ QChar ch;
+ Attribute *a;
+
+ if (cursor.y < 0) cursor.y = 0;
+ if (cursor.y > lastLine()) cursor.y = lastLine();
+ TextLine::Ptr textLine = getTextLine(cursor.y);
+ len = textLine->length();
+
+ x = oldX = z = 0;
+ while (x < xPos && (!wrapCursor || z < len)) {
+ oldX = x;
+ ch = textLine->getChar(z);
+ a = &m_attribs[textLine->getAttr(z)];
+
+ if (ch == '\t')
+ x += m_tabWidth - (x % m_tabWidth);
+ else if (a->bold && a->italic)
+ x += myFontMetricsBI.width(ch);
+ else if (a->bold)
+ x += myFontMetricsBold.width(ch);
+ else if (a->italic)
+ x += myFontMetricsItalic.width(ch);
+ else
+ x += myFontMetrics.width(ch);
+
+ z++;
+ }
+ if (xPos - oldX < x - xPos && z > 0) {
+ z--;
+ x = oldX;
+ }
+ cursor.x = z;
+ return x;
+}
+
+
+int KateDocument::textPos(const TextLine::Ptr &textLine, int xPos) {
+ int x, oldX;
+ int z;
+ QChar ch;
+ Attribute *a;
+
+ x = oldX = z = 0;
+ while (x < xPos) { // && z < len) {
+ oldX = x;
+ ch = textLine->getChar(z);
+ a = &m_attribs[textLine->getAttr(z)];
+
+ if (ch == '\t')
+ x += m_tabWidth - (x % m_tabWidth);
+ else if (a->bold && a->italic)
+ x += myFontMetricsBI.width(ch);
+ else if (a->bold)
+ x += myFontMetricsBold.width(ch);
+ else if (a->italic)
+ x += myFontMetricsItalic.width(ch);
+ else
+ x += myFontMetrics.width(ch);
+
+ z++;
+ }
+ if (xPos - oldX < x - xPos && z > 0) {
+ z--;
+ // newXPos = oldX;
+ }// else newXPos = x;
+ return z;
+}
+
+int KateDocument::textWidth() {
+ return int(maxLength + 8);
+}
+
+int KateDocument::textHeight() {
+ return numLines()*fontHeight;
+}
+
+void KateDocument::insert(VConfig &c, const QString &s) {
+ int pos;
+ QChar ch;
+ QString buf;
+
+ if (s.isEmpty()) return;
+
+ recordStart(c, KateActionGroup::ugPaste);
+
+ pos = 0;
+ if (!(c.flags & KateView::cfVerticalSelect)) {
+ do {
+ ch = s[pos];
+ if (ch.isPrint() || ch == '\t') {
+ buf += ch; // append char to buffer
+ } else if (ch == '\n') {
+ recordAction(KateAction::newLine, c.cursor); // wrap contents behind cursor to new line
+ recordInsert(c, buf); // append to old line
+// c.cursor.x += buf.length();
+ buf.truncate(0); // clear buffer
+ c.cursor.y++;
+ c.cursor.x = 0;
+ }
+ pos++;
+ } while (pos < (int) s.length());
+ } else {
+ int xPos;
+
+ xPos = textWidth(c.cursor);
+ do {
+ ch = s[pos];
+ if (ch.isPrint() || ch == '\t') {
+ buf += ch;
+ } else if (ch == '\n') {
+ recordInsert(c, buf);
+ c.cursor.x += buf.length();
+ buf.truncate(0);
+ c.cursor.y++;
+ if (c.cursor.y >= numLines())
+ recordAction(KateAction::insLine, c.cursor);
+ c.cursor.x = textPos(getTextLine(c.cursor.y), xPos);
+ }
+ pos++;
+ } while (pos < (int) s.length());
+ }
+ recordInsert(c, buf);
+ c.cursor.x += buf.length();
+ recordEnd(c);
+}
+
+void KateDocument::insertFile(VConfig &c, QIODevice &dev)
+{
+ recordStart(c, KateActionGroup::ugPaste);
+
+ QString buf;
+ QChar ch, last;
+
+ QTextStream stream( &dev );
+
+ while ( !stream.atEnd() ) {
+ stream >> ch;
+
+ if (ch.isPrint() || ch == '\t') {
+ buf += ch;
+ } else if (ch == '\n' || ch == '\r') {
+ if (last != '\r' || ch != '\n') {
+ recordAction(KateAction::newLine, c.cursor);
+ recordInsert(c, buf);
+ buf.truncate(0);
+ c.cursor.y++;
+ c.cursor.x = 0;
+ }
+ last = ch;
+ }
+ }
+
+ recordInsert(c, buf);
+ recordEnd(c);
+}
+
+int KateDocument::currentColumn(PointStruc &cursor) {
+ return getTextLine(cursor.y)->cursorX(cursor.x,tabChars);
+}
+
+bool KateDocument::insertChars(VConfig &c, const QString &chars) {
+ int z, pos, l;
+ bool onlySpaces;
+ QChar ch;
+ QString buf;
+
+ TextLine::Ptr textLine = getTextLine(c.cursor.y);
+
+ pos = 0;
+ onlySpaces = true;
+ for (z = 0; z < (int) chars.length(); z++) {
+ ch = chars[z];
+ if (ch == '\t' && c.flags & KateView::cfReplaceTabs) {
+ l = tabChars - (textLine->cursorX(c.cursor.x, tabChars) % tabChars);
+ while (l > 0) {
+ buf.insert(pos, ' ');
+ pos++;
+ l--;
+ }
+ } else if (ch.isPrint() || ch == '\t') {
+ buf.insert(pos, ch);
+ pos++;
+ if (ch != ' ') onlySpaces = false;
+ if (c.flags & KateView::cfAutoBrackets) {
+ if (ch == '(') buf.insert(pos, ')');
+ if (ch == '[') buf.insert(pos, ']');
+ if (ch == '{') buf.insert(pos, '}');
+ }
+ }
+ }
+ //pos = cursor increment
+
+ //return false if nothing has to be inserted
+ if (buf.isEmpty()) return false;
+
+ //auto deletion of the marked text occurs not very often and can therefore
+ // be recorded separately
+ if (c.flags &KateView:: cfDelOnInput) delMarkedText(c);
+
+ recordStart(c, KateActionGroup::ugInsChar);
+ recordReplace(c/*.cursor*/, (c.flags & KateView::cfOvr) ? buf.length() : 0, buf);
+ c.cursor.x += pos;
+
+ if (myWordWrap && myWordWrapAt > 0) {
+ int line;
+ const QChar *s;
+// int pos;
+ PointStruc actionCursor;
+
+ line = c.cursor.y;
+ do {
+ textLine = getTextLine(line);
+ s = textLine->getText();
+ l = textLine->length();
+ for (z = myWordWrapAt; z < l; z++) if (!s[z].isSpace()) break; //search for text to wrap
+ if (z >= l) break; // nothing more to wrap
+ pos = myWordWrapAt;
+ for (; z >= 0; z--) { //find wrap position
+ if (s[z].isSpace()) {
+ pos = z + 1;
+ break;
+ }
+ }
+ //pos = wrap position
+
+ if (line == c.cursor.y && pos <= c.cursor.x) {
+ //wrap cursor
+ c.cursor.y++;
+ c.cursor.x -= pos;
+ }
+
+ if (line == lastLine() || (getTextLine(line+1)->length() == 0) ) {
+ //at end of doc: create new line
+ actionCursor.x = pos;
+ actionCursor.y = line;
+ recordAction(KateAction::newLine,actionCursor);
+ } else {
+ //wrap
+ actionCursor.y = line + 1;
+ if (!s[l - 1].isSpace()) { //add space in next line if necessary
+ actionCursor.x = 0;
+ recordInsert(actionCursor, " ");
+ }
+ actionCursor.x = textLine->length() - pos;
+ recordAction(KateAction::wordWrap, actionCursor);
+ }
+ line++;
+ } while (true);
+ }
+ recordEnd(c);
+ return true;
+}
+
+QString tabString(int pos, int tabChars) {
+ QString s;
+ while (pos >= tabChars) {
+ s += '\t';
+ pos -= tabChars;
+ }
+ while (pos > 0) {
+ s += ' ';
+ pos--;
+ }
+ return s;
+}
+
+void KateDocument::newLine(VConfig &c) {
+
+ //auto deletion of marked text is done by the view to have a more
+ // "low level" KateDocument::newLine method
+ recordStart(c, KateActionGroup::ugInsLine);
+
+ if (!(c.flags & KateView::cfAutoIndent)) {
+ recordAction(KateAction::newLine,c.cursor);
+ c.cursor.y++;
+ c.cursor.x = 0;
+ } else {
+ TextLine::Ptr textLine = getTextLine(c.cursor.y);
+ int pos = textLine->firstChar();
+ if (c.cursor.x < pos) c.cursor.x = pos; // place cursor on first char if before
+
+ int y = c.cursor.y;
+ while ((y > 0) && (pos < 0)) { // search a not empty text line
+ textLine = getTextLine(--y);
+ pos = textLine->firstChar();
+ }
+ recordAction(KateAction::newLine, c.cursor);
+ c.cursor.y++;
+ c.cursor.x = 0;
+ if (pos > 0) {
+ pos = textLine->cursorX(pos, tabChars);
+// if (getTextLine(c.cursor.y)->length() > 0) {
+ QString s = tabString(pos, (c.flags & KateView::cfSpaceIndent) ? 0xffffff : tabChars);
+ recordInsert(c.cursor, s);
+ pos = s.length();
+// }
+// recordInsert(c.cursor, QString(textLine->getText(), pos));
+ c.cursor.x = pos;
+ }
+ }
+
+ recordEnd(c);
+}
+
+void KateDocument::killLine(VConfig &c) {
+
+ recordStart(c, KateActionGroup::ugDelLine);
+ c.cursor.x = 0;
+ recordDelete(c.cursor, 0xffffff);
+ if (c.cursor.y < lastLine()) {
+ recordAction(KateAction::killLine, c.cursor);
+ }
+ recordEnd(c);
+}
+
+void KateDocument::backspace(VConfig &c) {
+
+ if (c.cursor.x <= 0 && c.cursor.y <= 0) return;
+
+ if (c.cursor.x > 0) {
+ recordStart(c, KateActionGroup::ugDelChar);
+ if (!(c.flags & KateView::cfBackspaceIndents)) {
+ // ordinary backspace
+ c.cursor.x--;
+ recordDelete(c.cursor, 1);
+ } else {
+ // backspace indents: erase to next indent position
+ int l = 1; // del one char
+
+ TextLine::Ptr textLine = getTextLine(c.cursor.y);
+ int pos = textLine->firstChar();
+ if (pos < 0 || pos >= c.cursor.x) {
+ // only spaces on left side of cursor
+ // search a line with less spaces
+ int y = c.cursor.y;
+ while (y > 0) {
+ textLine = getTextLine(--y);
+ pos = textLine->firstChar();
+ if (pos >= 0 && pos < c.cursor.x) {
+ l = c.cursor.x - pos; // del more chars
+ break;
+ }
+ }
+ }
+ // break effectively jumps here
+ c.cursor.x -= l;
+ recordDelete(c.cursor, l);
+ }
+ } else {
+ // c.cursor.x == 0: wrap to previous line
+ recordStart(c, KateActionGroup::ugDelLine);
+ c.cursor.y--;
+ c.cursor.x = getTextLine(c.cursor.y)->length();
+ recordAction(KateAction::delLine,c.cursor);
+ }
+ recordEnd(c);
+}
+
+
+void KateDocument::del(VConfig &c) {
+ TextLine::Ptr textLine = getTextLine(c.cursor.y);
+ int len = (c.flags & KateView::cfRemoveSpaces) ? textLine->lastChar() : textLine->length();
+ if (c.cursor.x < len/*getTextLine(c.cursor.y)->length()*/) {
+ // delete one character
+ recordStart(c, KateActionGroup::ugDelChar);
+ recordDelete(c.cursor, 1);
+ recordEnd(c);
+ } else {
+ if (c.cursor.y < lastLine()) {
+ // wrap next line to this line
+ textLine->truncate(c.cursor.x); // truncate spaces
+ recordStart(c, KateActionGroup::ugDelLine);
+ recordAction(KateAction::delLine,c.cursor);
+ recordEnd(c);
+ }
+ }
+}
+
+void KateDocument::clear() {
+ PointStruc cursor;
+ KateView *view;
+
+ setPseudoModal(0L);
+ cursor.x = cursor.y = 0;
+ for (view = views.first(); view != 0L; view = views.next() ) {
+ view->updateCursor(cursor);
+ view->tagAll();
+ }
+
+ eolMode = KateDocument::eolUnix;
+
+ buffer->clear();
+ longestLine = buffer->line(0);
+
+ maxLength = 0;
+
+ select.x = -1;
+
+ selectStart = 0xffffff;
+ selectEnd = 0;
+ oldMarkState = false;
+
+ setModified(false);
+
+ undoList.clear();
+ currentUndo = 0;
+ newUndo();
+}
+
+void KateDocument::cut(VConfig &c) {
+
+ if (selectEnd < selectStart) return;
+
+ copy(c.flags);
+ delMarkedText(c);
+}
+
+void KateDocument::copy(int flags) {
+
+ if (selectEnd < selectStart) return;
+
+ QString s = markedText(flags);
+ if (!s.isEmpty()) {
+//#if defined(_WS_X11_)
+ if (m_singleSelection)
+ disconnect(QApplication::clipboard(), SIGNAL(dataChanged()), this, 0);
+//#endif
+ QApplication::clipboard()->setText(s);
+//#if defined(_WS_X11_)
+ if (m_singleSelection) {
+ connect(QApplication::clipboard(), SIGNAL(dataChanged()),
+ this, SLOT(clipboardChanged()));
+ }
+//#endif
+ }
+}
+
+void KateDocument::paste(VConfig &c) {
+ QString s = QApplication::clipboard()->text();
+ if (!s.isEmpty()) {
+ insert(c, s);
+ }
+}
+
+void KateDocument::toggleRect(int start, int end, int x1, int x2) {
+ int z, line;
+ bool t;
+
+ if (x1 > x2) {
+ z = x1;
+ x1 = x2;
+ x2 = z;
+ }
+ if (start > end) {
+ z = start;
+ start = end;
+ end = z;
+ }
+
+ t = false;
+ for (line = start; line < end; line++) {
+ int x, oldX, s, e, newX1, newX2;
+ QChar ch;
+ Attribute *a;
+
+ TextLine::Ptr textLine = getTextLine(line);
+
+ //--- speed optimization
+ //s = textPos(textLine, x1, newX1);
+ x = oldX = z = 0;
+ while (x < x1) { // && z < len) {
+ oldX = x;
+ ch = textLine->getChar(z);
+ a = &m_attribs[textLine->getAttr(z)];
+
+ if (ch == '\t')
+ x += m_tabWidth - (x % m_tabWidth);
+ else if (a->bold && a->italic)
+ x += myFontMetricsBI.width(ch);
+ else if (a->bold)
+ x += myFontMetricsBold.width(ch);
+ else if (a->italic)
+ x += myFontMetricsItalic.width(ch);
+ else
+ x += myFontMetrics.width(ch);
+
+ z++;
+ }
+ s = z;
+ if (x1 - oldX < x - x1 && z > 0) {
+ s--;
+ newX1 = oldX;
+ } else newX1 = x;
+ //e = textPos(textLine, x2, newX2);
+ while (x < x2) { // && z < len) {
+ oldX = x;
+ ch = textLine->getChar(z);
+ a = &m_attribs[textLine->getAttr(z)];
+
+ if (ch == '\t')
+ x += m_tabWidth - (x % m_tabWidth);
+ else if (a->bold && a->italic)
+ x += myFontMetricsBI.width(ch);
+ else if (a->bold)
+ x += myFontMetricsBold.width(ch);
+ else if (a->italic)
+ x += myFontMetricsItalic.width(ch);
+ else
+ x += myFontMetrics.width(ch);
+
+ z++;
+ }
+ e = z;
+ if (x2 - oldX < x - x2 && z > 0) {
+ e--;
+ newX2 = oldX;
+ } else newX2 = x;
+ //---
+
+ if (e > s) {
+ textLine->toggleSelect(s, e);
+ tagLineRange(line, newX1, newX2);
+ t = true;
+ }
+ }
+ if (t) {
+ end--;
+// tagLines(start, end);
+
+ if (start < selectStart) selectStart = start;
+ if (end > selectEnd) selectEnd = end;
+ emit selectionChanged();
+ }
+}
+
+void KateDocument::selectTo(VConfig &c, PointStruc &cursor, int cXPos) {
+ //c.cursor = old cursor position
+ //cursor = new cursor position
+
+ if (c.cursor.x != select.x || c.cursor.y != select.y) {
+ //new selection
+
+ if (!(c.flags & KateView::cfKeepSelection)) deselectAll();
+// else recordReset();
+
+ anchor = c.cursor;
+ aXPos = c.cXPos;
+ }
+
+ if (!(c.flags & KateView::cfVerticalSelect)) {
+ //horizontal selections
+ int x, y, sXPos;
+ int ex, ey, eXPos;
+ bool sel;
+
+ if (cursor.y > c.cursor.y || (cursor.y == c.cursor.y && cursor.x > c.cursor.x)) {
+ x = c.cursor.x;
+ y = c.cursor.y;
+ sXPos = c.cXPos;
+ ex = cursor.x;
+ ey = cursor.y;
+ eXPos = cXPos;
+ sel = true;
+ } else {
+ x = cursor.x;
+ y = cursor.y;
+ sXPos = cXPos;
+ ex = c.cursor.x;
+ ey = c.cursor.y;
+ eXPos = c.cXPos;
+ sel = false;
+ }
+
+// tagLines(y, ye);
+ if (y < ey) {
+ //tagLineRange(y, sXPos, 0xffffff);
+ tagLines(y, ey -1);
+ tagLineRange(ey, 0, eXPos);
+ } else tagLineRange(y, sXPos, eXPos);
+
+ if (y < selectStart) selectStart = y;
+ if (ey > selectEnd) selectEnd = ey;
+
+ TextLine::Ptr textLine = getTextLine(y);
+
+ if (c.flags & KateView::cfXorSelect) {
+ //xor selection with old selection
+ while (y < ey) {
+ textLine->toggleSelectEol(x);
+ x = 0;
+ y++;
+ textLine = getTextLine(y);
+ }
+ textLine->toggleSelect(x, ex);
+ } else {
+ //set selection over old selection
+
+ if (anchor.y > y || (anchor.y == y && anchor.x > x)) {
+ if (anchor.y < ey || (anchor.y == ey && anchor.x < ex)) {
+ sel = !sel;
+ while (y < anchor.y) {
+ textLine->selectEol(sel, x);
+ x = 0;
+ y++;
+ textLine = getTextLine(y);
+ }
+ textLine->select(sel, x, anchor.x);
+ x = anchor.x;
+ }
+ sel = !sel;
+ }
+ while (y < ey) {
+ textLine->selectEol(sel, x);
+ x = 0;
+ y++;
+ textLine = getTextLine(y);
+ }
+ textLine->select(sel, x, ex);
+ }
+ } else {
+ //vertical (block) selections
+// int ax, sx, ex;
+
+// ax = textWidth(anchor);
+// sx = textWidth(start);
+// ex = textWidth(end);
+
+ toggleRect(c.cursor.y + 1, cursor.y + 1, aXPos, c.cXPos);
+ toggleRect(anchor.y, cursor.y + 1, c.cXPos, cXPos);
+ }
+ select = cursor;
+ optimizeSelection();
+ emit selectionChanged();
+}
+
+
+void KateDocument::selectAll() {
+ int z;
+ TextLine::Ptr textLine;
+
+ select.x = -1;
+
+// if (selectStart != 0 || selectEnd != lastLine()) recordReset();
+
+ selectStart = 0;
+ selectEnd = lastLine();
+
+ tagLines(selectStart,selectEnd);
+
+ for (z = selectStart; z < selectEnd; z++) {
+ textLine = getTextLine(z);
+ textLine->selectEol(true,0);
+ }
+ textLine = getTextLine(z);
+ textLine->select(true,0,textLine->length());
+ emit selectionChanged();
+}
+
+void KateDocument::deselectAll() {
+ select.x = -1;
+ if (selectEnd < selectStart) return;
+
+// recordReset();
+
+ tagLines(selectStart,selectEnd);
+
+ for (int z = selectStart; z <= selectEnd; z++) {
+ TextLine::Ptr textLine = getTextLine(z);
+ textLine->selectEol(false,0);
+ }
+ selectStart = 0xffffff;
+ selectEnd = 0;
+ emit selectionChanged();
+}
+
+void KateDocument::invertSelection() {
+ TextLine::Ptr textLine;
+
+ select.x = -1;
+
+// if (selectStart != 0 || selectEnd != lastLine()) recordReset();
+
+ selectStart = 0;
+ selectEnd = lastLine();
+
+ tagLines(selectStart,selectEnd);
+
+ for (int z = selectStart; z < selectEnd; z++) {
+ textLine = getTextLine(z);
+ textLine->toggleSelectEol(0);
+ }
+ textLine = getTextLine(selectEnd);
+ textLine->toggleSelect(0,textLine->length());
+ optimizeSelection();
+ emit selectionChanged();
+}
+
+void KateDocument::selectWord(PointStruc &cursor, int flags) {
+ int start, end, len;
+
+ TextLine::Ptr textLine = getTextLine(cursor.y);
+ len = textLine->length();
+ start = end = cursor.x;
+ while (start > 0 && m_highlight->isInWord(textLine->getChar(start - 1))) start--;
+ while (end < len && m_highlight->isInWord(textLine->getChar(end))) end++;
+ if (end <= start) return;
+ if (!(flags & KateView::cfKeepSelection)) deselectAll();
+// else recordReset();
+
+ textLine->select(true, start, end);
+
+ anchor.x = start;
+ select.x = end;
+ anchor.y = select.y = cursor.y;
+ tagLines(cursor.y, cursor.y);
+ if (cursor.y < selectStart) selectStart = cursor.y;
+ if (cursor.y > selectEnd) selectEnd = cursor.y;
+ emit selectionChanged();
+}
+
+void KateDocument::selectLength(PointStruc &cursor, int length, int flags) {
+ int start, end;
+
+ TextLine::Ptr textLine = getTextLine(cursor.y);
+ start = cursor.x;
+ end = start + length;
+ if (end <= start) return;
+ if (!(flags & KateView::cfKeepSelection)) deselectAll();
+
+ textLine->select(true, start, end);
+
+ anchor.x = start;
+ select.x = end;
+ anchor.y = select.y = cursor.y;
+ tagLines(cursor.y, cursor.y);
+ if (cursor.y < selectStart) selectStart = cursor.y;
+ if (cursor.y > selectEnd) selectEnd = cursor.y;
+ emit selectionChanged();
+}
+
+void KateDocument::doIndent(VConfig &c, int change) {
+
+ c.cursor.x = 0;
+
+ recordStart(c, (change < 0) ? KateActionGroup::ugUnindent
+ : KateActionGroup::ugIndent);
+
+ if (selectEnd < selectStart) {
+ // single line
+ optimizeLeadingSpace(c.cursor.y, c.flags, change);
+ } else {
+ // entire selection
+ TextLine::Ptr textLine;
+ int line, z;
+ QChar ch;
+
+ if (c.flags & KateView::cfKeepIndentProfile && change < 0) {
+ // unindent so that the existing indent profile doesn´t get screwed
+ // if any line we may unindent is already full left, don't do anything
+ for (line = selectStart; line <= selectEnd; line++) {
+ textLine = getTextLine(line);
+ if (textLine->isSelected() || textLine->numSelected()) {
+ for (z = 0; z < tabChars; z++) {
+ ch = textLine->getChar(z);
+ if (ch == '\t') break;
+ if (ch != ' ') {
+ change = 0;
+ goto jumpOut;
+ }
+ }
+ }
+ }
+ jumpOut:;
+ }
+
+ for (line = selectStart; line <= selectEnd; line++) {
+ textLine = getTextLine(line);
+ if (textLine->isSelected() || textLine->numSelected()) {
+ optimizeLeadingSpace(line, c.flags, change);
+ }
+ }
+ }
+ // recordEnd now removes empty undo records
+ recordEnd(c.view, c.cursor, c.flags | KateView::cfPersistent);
+}
+
+/*
+ Optimize the leading whitespace for a single line.
+ If change is > 0, it adds indentation units (tabChars)
+ if change is == 0, it only optimizes
+ If change is < 0, it removes indentation units
+ This will be used to indent, unindent, and optimal-fill a line.
+ If excess space is removed depends on the flag cfKeepExtraSpaces
+ which has to be set by the user
+*/
+void KateDocument::optimizeLeadingSpace(int line, int flags, int change) {
+ int len;
+ int chars, space, okLen;
+ QChar ch;
+ int extra;
+ QString s;
+ PointStruc cursor;
+
+ TextLine::Ptr textLine = getTextLine(line);
+ len = textLine->length();
+ space = 0; // length of space at the beginning of the textline
+ okLen = 0; // length of space which does not have to be replaced
+ for (chars = 0; chars < len; chars++) {
+ ch = textLine->getChar(chars);
+ if (ch == ' ') {
+ space++;
+ if (flags & KateView::cfSpaceIndent && okLen == chars) okLen++;
+ } else if (ch == '\t') {
+ space += tabChars - space % tabChars;
+ if (!(flags & KateView::cfSpaceIndent) && okLen == chars) okLen++;
+ } else break;
+ }
+
+ space += change*tabChars; // modify space width
+ // if line contains only spaces it will be cleared
+ if (space < 0 || chars == len) space = 0;
+
+ extra = space % tabChars; // extra spaces which don´t fit the indentation pattern
+ if (flags & KateView::cfKeepExtraSpaces) chars -= extra;
+
+ if (flags & KateView::cfSpaceIndent) {
+ space -= extra;
+ ch = ' ';
+ } else {
+ space /= tabChars;
+ ch = '\t';
+ }
+
+ // don´t replace chars which are already ok
+ cursor.x = QMIN(okLen, QMIN(chars, space));
+ chars -= cursor.x;
+ space -= cursor.x;
+ if (chars == 0 && space == 0) return; //nothing to do
+
+ s.fill(ch, space);
+
+//printf("chars %d insert %d cursor.x %d\n", chars, insert, cursor.x);
+ cursor.y = line;
+ recordReplace(cursor, chars, s);
+}
+
+void KateDocument::doComment(VConfig &c, int change)
+{
+ c.flags |=KateView:: cfPersistent;
+
+ recordStart(c, (change < 0) ? KateActionGroup::ugUncomment
+ : KateActionGroup::ugComment);
+
+ QString startComment = m_highlight->getCommentStart();
+ QString startLineComment = m_highlight->getCommentSingleLineStart();
+ QString endComment = m_highlight->getCommentEnd();
+
+ int startCommentLen = startComment.length();
+ int startLineCommentLen = startLineComment.length();
+ int endCommentLen = endComment.length();
+
+ if (change > 0)
+ {
+ if ( !hasMarkedText() )
+ {
+ if (startLineComment != "")
+ {
+ // Add a start comment mark
+ c.cursor.x = 0;
+ recordReplace(c.cursor, 0, startLineComment);
+ }
+ else if ((startComment != "") && (endComment != ""))
+ {
+ // Add a start comment mark
+ c.cursor.x = 0;
+ recordReplace(c.cursor, 0, startComment);
+
+ // Add an end comment mark
+ TextLine* textline = getTextLine(c.cursor.y);
+ c.cursor.x = textline->length();
+ recordReplace(c.cursor, 0, endComment);
+ c.cursor.x = 0;
+ }
+ }
+ else if ((startComment != "") && (endComment != ""))
+ {
+ QString marked (c.view->markedText ());
+ int preDeleteLine = -1, preDeleteCol = -1;
+ c.view->getCursorPosition (&preDeleteLine, &preDeleteCol);
+
+ if (marked.length() > 0)
+ c.view->keyDelete ();
+
+ int line = -1, col = -1;
+ c.view->getCursorPosition (&line, &col);
+
+ c.view->insertText (startComment + marked + endComment);
+ }
+ }
+ else
+ {
+ if ( !hasMarkedText() )
+ {
+ TextLine* textline = getTextLine(c.cursor.y);
+
+ if(textline->startingWith(startLineComment))
+ {
+ // Remove start comment mark
+ c.cursor.x = 0;
+ recordReplace(c.cursor, startLineCommentLen, "");
+ }
+ else if (textline->startingWith(startComment) && textline->endingWith(endComment))
+ {
+ // Remove start comment mark
+ c.cursor.x = 0;
+ recordReplace(c.cursor, startCommentLen, "");
+
+ // Remove end comment mark
+ if(endComment != "")
+ {
+ c.cursor.x = textline->length() - endCommentLen;
+ recordReplace(c.cursor, endCommentLen, "");
+ c.cursor.x = 0;
+ }
+ }
+ }
+ else
+ {
+ QString marked (c.view->markedText ());
+ int preDeleteLine = -1, preDeleteCol = -1;
+ c.view->getCursorPosition (&preDeleteLine, &preDeleteCol);
+
+ int start = marked.find (startComment);
+ int end = marked.findRev (endComment);
+
+ if ((start > -1) && (end > -1))
+ {
+ marked.remove (start, startCommentLen);
+ marked.remove (end-startCommentLen, endCommentLen);
+
+ c.view->keyDelete ();
+
+ int line = -1, col = -1;
+ c.view->getCursorPosition (&line, &col);
+ c.view->insertText (marked);
+ }
+ }
+ }
+
+ recordEnd(c.view, c.cursor, c.flags | KateView::cfPersistent);
+}
+
+
+QString KateDocument::text() const
+{
+ QString s;
+
+ for (int i=0; i < buffer->count(); i++)
+ {
+ TextLine::Ptr textLine = buffer->line(i);
+ s.insert(s.length(), textLine->getText(), textLine->length());
+ if ( (i < (buffer->count()-1)) )
+ s.append('\n');
+ }
+
+ return s;
+}
+
+QString KateDocument::getWord(PointStruc &cursor) {
+ int start, end, len;
+
+ TextLine::Ptr textLine = getTextLine(cursor.y);
+ len = textLine->length();
+ start = end = cursor.x;
+ while (start > 0 && m_highlight->isInWord(textLine->getChar(start - 1))) start--;
+ while (end < len && m_highlight->isInWord(textLine->getChar(end))) end++;
+ len = end - start;
+ return QString(&textLine->getText()[start], len);
+}
+
+void KateDocument::setText(const QString &s) {
+ int pos;
+ QChar ch;
+
+ clear();
+
+ int line=1;
+
+ TextLine::Ptr textLine = buffer->line(0);
+ for (pos = 0; pos <= (int) s.length(); pos++) {
+ ch = s[pos];
+ if (ch.isPrint() || ch == '\t') {
+ textLine->append(&ch, 1);
+ } else if (ch == '\n')
+ {
+ textLine = new TextLine();
+ buffer->insertLine (line, textLine);
+ line++;
+ }
+ }
+ updateLines();
+}
+
+
+QString KateDocument::markedText(int flags) {
+ TextLine::Ptr textLine;
+ int len, z, start, end, i;
+
+ len = 1;
+ if (!(flags & KateView::cfVerticalSelect)) {
+ for (z = selectStart; z <= selectEnd; z++) {
+ textLine = getTextLine(z);
+ len += textLine->numSelected();
+ if (textLine->isSelected()) len++;
+ }
+ QString s;
+ len = 0;
+ for (z = selectStart; z <= selectEnd; z++) {
+ textLine = getTextLine(z);
+ end = 0;
+ do {
+ start = textLine->findUnselected(end);
+ end = textLine->findSelected(start);
+ for (i = start; i < end; i++) {
+ s[len] = textLine->getChar(i);
+ len++;
+ }
+ } while (start < end);
+ if (textLine->isSelected()) {
+ s[len] = '\n';
+ len++;
+ }
+ }
+// s[len] = '\0';
+ return s;
+ } else {
+ for (z = selectStart; z <= selectEnd; z++) {
+ textLine = getTextLine(z);
+ len += textLine->numSelected() + 1;
+ }
+ QString s;
+ len = 0;
+ for (z = selectStart; z <= selectEnd; z++) {
+ textLine = getTextLine(z);
+ end = 0;
+ do {
+ start = textLine->findUnselected(end);
+ end = textLine->findSelected(start);
+ for (i = start; i < end; i++) {
+ s[len] = textLine->getChar(i);
+ len++;
+ }
+ } while (start < end);
+ s[len] = '\n';
+ len++;
+ }
+// s[len] = '\0'; // the final \0 is not counted in length()
+ return s;
+ }
+}
+
+void KateDocument::delMarkedText(VConfig &c/*, bool undo*/) {
+ int end = 0;
+
+ if (selectEnd < selectStart) return;
+
+ // the caller may have already started an undo record for the current action
+// if (undo)
+
+ //auto deletion of the marked text occurs not very often and can therefore
+ // be recorded separately
+ recordStart(c, KateActionGroup::ugDelBlock);
+
+ for (c.cursor.y = selectEnd; c.cursor.y >= selectStart; c.cursor.y--) {
+ TextLine::Ptr textLine = getTextLine(c.cursor.y);
+
+ c.cursor.x = textLine->length();
+ do {
+ end = textLine->findRevUnselected(c.cursor.x);
+ if (end == 0) break;
+ c.cursor.x = textLine->findRevSelected(end);
+ recordDelete(c.cursor, end - c.cursor.x);
+ } while (true);
+ end = c.cursor.x;
+ c.cursor.x = textLine->length();
+ if (textLine->isSelected()) recordAction(KateAction::delLine,c.cursor);
+ }
+ c.cursor.y++;
+ /*if (end < c.cursor.x)*/ c.cursor.x = end;
+
+ selectEnd = -1;
+ select.x = -1;
+
+ /*if (undo)*/ recordEnd(c);
+}
+
+void KateDocument::tagLineRange(int line, int x1, int x2) {
+ int z;
+
+ for (z = 0; z < (int) views.count(); z++) {
+ views.at(z)->tagLines(line, line, x1, x2);
+ }
+}
+
+void KateDocument::tagLines(int start, int end) {
+ int z;
+
+ for (z = 0; z < (int) views.count(); z++) {
+ views.at(z)->tagLines(start, end, 0, 0xffffff);
+ }
+}
+
+void KateDocument::tagAll() {
+ int z;
+
+ for (z = 0; z < (int) views.count(); z++) {
+ views.at(z)->tagAll();
+ }
+}
+
+void KateDocument::updateLines(int startLine, int endLine, int flags, int cursorY) {
+ TextLine::Ptr textLine;
+ int line, last_line;
+ int ctxNum, endCtx;
+// kdDebug(13020)<<"******************KateDocument::updateLines Checkpoint 1"<<endl;
+ if (buffer->line(startLine)==0) {kdDebug(13020)<<"********************No buffer for line " << startLine << " found**************"<<endl; return;};
+// kdDebug(13020)<<"KateDocument::updateLines Checkpoint 2"<<endl;
+ last_line = lastLine();
+// if (endLine >= last_line) endLine = last_line;
+
+ line = startLine;
+ ctxNum = 0;
+ if (line > 0) ctxNum = getTextLine(line - 1)->getContext();
+ do {
+// kdDebug(13020)<<QString("**************Working on line: %1").arg(line)<<endl;
+ textLine = getTextLine(line);
+ if (textLine==0) kdDebug(13020)<<"****updateLines()>> error textLine==0"<<endl;
+ if (line <= endLine && line != cursorY) {
+ if (flags & KateView::cfRemoveSpaces) textLine->removeSpaces();
+ updateMaxLength(textLine);
+ }
+ endCtx = textLine->getContext();
+ qDebug("DOHIGHLIGHT");
+ ctxNum = m_highlight->doHighlight(ctxNum,textLine);
+ textLine->setContext(ctxNum);
+ line++;
+ } while ((buffer->line(line)!=0) && (line <= endLine || endCtx != ctxNum));
+// kdDebug(13020)<<"updateLines :: while loop left"<<endl;
+ tagLines(startLine, line - 1);
+}
+
+
+void KateDocument::updateMaxLength(TextLine::Ptr &textLine) {
+ int len;
+
+ len = textWidth(textLine,textLine->length());
+
+ if (len > maxLength) {
+ longestLine = textLine;
+ maxLength = len;
+ newDocGeometry = true;
+ } else {
+ if (!longestLine || (textLine == longestLine && len <= maxLength*3/4)) {
+ maxLength = -1;
+ for (int i = 0; i < numLines();i++) {
+ textLine = getTextLine(i);
+ len = textWidth(textLine,textLine->length());
+ if (len > maxLength) {
+ maxLength = len;
+ longestLine = textLine;
+ }
+ }
+ newDocGeometry = true;
+ }
+ }
+}
+
+void KateDocument::slotBufferChanged() {
+ newDocGeometry = true;
+ //updateLines();//JW
+ updateViews();
+}
+
+void KateDocument::slotBufferHighlight(long start,long stop) {
+ kdDebug(13020)<<"KateDocument::slotBufferHighlight"<<QString("%1-%2").arg(start).arg(stop)<<endl;
+ updateLines(start,stop);
+// buffer->startLoadTimer();
+}
+
+void KateDocument::updateViews(KateView *exclude) {
+ KateView *view;
+ int flags;
+ bool markState = hasMarkedText();
+
+ flags = (newDocGeometry) ? KateView::ufDocGeometry : 0;
+ for (view = views.first(); view != 0L; view = views.next() ) {
+ if (view != exclude) view->updateView(flags);
+
+ // notify every view about the changed mark state....
+ if (oldMarkState != markState) emit view->newMarkStatus();
+ }
+ oldMarkState = markState;
+ newDocGeometry = false;
+}
+
+QColor &KateDocument::cursorCol(int x, int y) {
+ int attr;
+ Attribute *a;
+
+ TextLine::Ptr textLine = getTextLine(y);
+ attr = textLine->getRawAttr(x);
+ a = &m_attribs[attr & taAttrMask];
+ if (attr & taSelected) return a->selCol; else return a->col;
+}
+
+void KateDocument::paintTextLine(QPainter &paint, int line, int xStart, int xEnd, bool showTabs)
+{
+ paintTextLine (paint, line, 0, xStart, xEnd, showTabs);
+}
+
+void KateDocument::paintTextLine(QPainter &paint, int line, int y, int xStart, int xEnd, bool showTabs)
+{
+ TextLine::Ptr textLine;
+ int len;
+ const QChar *s;
+ int z, x;
+ QChar ch;
+ Attribute *a = 0L;
+ int attr, nextAttr;
+ int xs;
+ int xc, zc;
+
+ if (line > lastLine()) {
+ paint.fillRect(0, y, xEnd - xStart,fontHeight, colors[0]);
+ return;
+ }
+
+ textLine = getTextLine(line);
+ len = textLine->length();
+ s = textLine->getText();
+
+ // skip to first visible character
+ x = 0;
+ z = 0;
+ do {
+ xc = x;
+ zc = z;
+ if (z == len) break;
+ ch = s[z];//textLine->getChar(z);
+ if (ch == '\t') {
+ x += m_tabWidth - (x % m_tabWidth);
+ } else {
+ a = &m_attribs[textLine->getAttr(z)];
+
+ if (a->bold && a->italic)
+ x += myFontMetricsBI.width(ch);
+ else if (a->bold)
+ x += myFontMetricsBold.width(ch);
+ else if (a->italic)
+ x += myFontMetricsItalic.width(ch);
+ else
+ x += myFontMetrics.width(ch);
+ }
+ z++;
+ } while (x <= xStart);
+
+ // draw background
+ xs = xStart;
+ attr = textLine->getRawAttr(zc);
+ while (x < xEnd)
+ {
+ nextAttr = textLine->getRawAttr(z);
+ if ((nextAttr ^ attr) & taSelected)
+ {
+ if (attr & taSelected)
+ paint.fillRect(xs - xStart, y, x - xs, fontHeight, colors[1]);
+ else
+ paint.fillRect(xs - xStart, y, x - xs, fontHeight, colors[0]);
+
+ xs = x;
+ attr = nextAttr;
+ }
+
+ if (z == len) break;
+
+ ch = s[z];//textLine->getChar(z);
+
+ if (ch == '\t')
+ x += m_tabWidth - (x % m_tabWidth);
+ else
+ {
+ a = &m_attribs[textLine->getAttr(z)];
+
+ if (a->bold && a->italic)
+ x += myFontMetricsBI.width(ch);
+ else if (a->bold)
+ x += myFontMetricsBold.width(ch);
+ else if (a->italic)
+ x += myFontMetricsItalic.width(ch);
+ else
+ x += myFontMetrics.width(ch);
+ }
+ z++;
+ }
+
+ if (attr & taSelected)
+ paint.fillRect(xs - xStart, y, xEnd - xs, fontHeight, colors[1]);
+ else
+ paint.fillRect(xs - xStart, y, xEnd - xs, fontHeight, colors[0]);
+
+ len = z; //reduce length to visible length
+
+ // draw text
+ x = xc;
+ z = zc;
+ y += fontAscent;// -1;
+ attr = -1;
+ while (z < len) {
+ ch = s[z];//textLine->getChar(z);
+ if (ch == '\t') {
+ if (z > zc) {
+ //this should cause no copy at all
+ QConstString str((QChar *) &s[zc], z - zc /*+1*/);
+ QString s = str.string();
+ paint.drawText(x - xStart, y, s);
+
+ if (a->bold && a->italic)
+ x += myFontMetricsBI.width(s);
+ else if (a->bold)
+ x += myFontMetricsBold.width(s);
+ else if (a->italic)
+ x += myFontMetricsItalic.width(s);
+ else
+ x += myFontMetrics.width(s);
+ }
+ zc = z +1;
+
+ if (showTabs) {
+ nextAttr = textLine->getRawAttr(z);
+ if (nextAttr != attr) {
+ attr = nextAttr;
+ a = &m_attribs[attr & taAttrMask];
+
+ if (attr & taSelected) paint.setPen(a->selCol);
+ else paint.setPen(a->col);
+
+ if (a->bold && a->italic)
+ paint.setFont(myFontBI);
+ else if (a->bold)
+ paint.setFont(myFontBold);
+ else if (a->italic)
+ paint.setFont(myFontItalic);
+ else
+ paint.setFont(myFont);
+ }
+
+// paint.drawLine(x - xStart, y -2, x - xStart, y);
+// paint.drawLine(x - xStart, y, x - xStart + 2, y);
+ paint.drawPoint(x - xStart, y);
+ paint.drawPoint(x - xStart +1, y);
+ paint.drawPoint(x - xStart, y -1);
+ }
+ x += m_tabWidth - (x % m_tabWidth);
+ } else {
+ nextAttr = textLine->getRawAttr(z);
+ if (nextAttr != attr) {
+ if (z > zc) {
+ QConstString str((QChar *) &s[zc], z - zc /*+1*/);
+ QString s = str.string();
+ paint.drawText(x - xStart, y, s);
+
+ if (a->bold && a->italic)
+ x += myFontMetricsBI.width(s);
+ else if (a->bold)
+ x += myFontMetricsBold.width(s);
+ else if (a->italic)
+ x += myFontMetricsItalic.width(s);
+ else
+ x += myFontMetrics.width(s);
+ zc = z;
+ }
+ attr = nextAttr;
+ a = &m_attribs[attr & taAttrMask];
+
+ if (attr & taSelected) paint.setPen(a->selCol);
+ else paint.setPen(a->col);
+
+ if (a->bold && a->italic)
+ paint.setFont(myFontBI);
+ else if (a->bold)
+ paint.setFont(myFontBold);
+ else if (a->italic)
+ paint.setFont(myFontItalic);
+ else
+ paint.setFont(myFont);
+ }
+ }
+ z++;
+ }
+ if (z > zc) {
+ QConstString str((QChar *) &s[zc], z - zc /*+1*/);
+ paint.drawText(x - xStart, y, str.string());
+ }
+}
+
+// Applies the search context, and returns whether a match was found. If one is,
+// the length of the string matched is also returned.
+bool KateDocument::doSearch(SConfig &sc, const QString &searchFor) {
+ int line, col;
+ int searchEnd;
+ int bufLen, tlen;
+ QChar *t;
+ TextLine::Ptr textLine;
+ int pos, newPos;
+
+ if (searchFor.isEmpty()) return false;
+
+ bufLen = 0;
+ t = 0L;
+
+ line = sc.cursor.y;
+ col = sc.cursor.x;
+ if (!(sc.flags & KateView::sfBackward)) {
+ //forward search
+ if (sc.flags & KateView::sfSelected) {
+ if (line < selectStart) {
+ line = selectStart;
+ col = 0;
+ }
+ searchEnd = selectEnd;
+ } else searchEnd = lastLine();
+
+ while (line <= searchEnd) {
+ textLine = getTextLine(line);
+ tlen = textLine->length();
+ if (tlen > bufLen) {
+ delete t;
+ bufLen = (tlen + 255) & (~255);
+ t = new QChar[bufLen];
+ }
+ memcpy(t, textLine->getText(), tlen*sizeof(QChar));
+ if (sc.flags & KateView::sfSelected) {
+ pos = 0;
+ do {
+ pos = textLine->findSelected(pos);
+ newPos = textLine->findUnselected(pos);
+ memset(&t[pos], 0, (newPos - pos)*sizeof(QChar));
+ pos = newPos;
+ } while (pos < tlen);
+ }
+
+ QString text(t, tlen);
+ if (sc.flags & KateView::sfWholeWords) {
+ // Until the end of the line...
+ while (col < tlen) {
+ // ...find the next match.
+ col = sc.search(text, col);
+ if (col != -1) {
+ // Is the match delimited correctly?
+ if (((col == 0) || (!m_highlight->isInWord(t[col]))) &&
+ ((col + sc.matchedLength == tlen) || (!m_highlight->isInWord(t[col + sc.matchedLength])))) {
+ goto found;
+ }
+ else {
+ // Start again from the next character.
+ col++;
+ }
+ }
+ else {
+ // No match.
+ break;
+ }
+ }
+ }
+ else {
+ // Non-whole-word search.
+ col = sc.search(text, col);
+ if (col != -1)
+ goto found;
+ }
+ col = 0;
+ line++;
+ }
+ } else {
+ // backward search
+ if (sc.flags & KateView::sfSelected) {
+ if (line > selectEnd) {
+ line = selectEnd;
+ col = -1;
+ }
+ searchEnd = selectStart;
+ } else searchEnd = 0;
+
+ while (line >= searchEnd) {
+ textLine = getTextLine(line);
+ tlen = textLine->length();
+ if (tlen > bufLen) {
+ delete t;
+ bufLen = (tlen + 255) & (~255);
+ t = new QChar[bufLen];
+ }
+ memcpy(t, textLine->getText(), tlen*sizeof(QChar));
+ if (sc.flags & KateView::sfSelected) {
+ pos = 0;
+ do {
+ pos = textLine->findSelected(pos);
+ newPos = textLine->findUnselected(pos);
+ memset(&t[pos], 0, (newPos - pos)*sizeof(QChar));
+ pos = newPos;
+ } while (pos < tlen);
+ }
+
+ if (col < 0 || col > tlen) col = tlen;
+
+ QString text(t, tlen);
+ if (sc.flags & KateView::sfWholeWords) {
+ // Until the beginning of the line...
+ while (col >= 0) {
+ // ...find the next match.
+ col = sc.search(text, col);
+ if (col != -1) {
+ // Is the match delimited correctly?
+ if (((col == 0) || (!m_highlight->isInWord(t[col]))) &&
+ ((col + sc.matchedLength == tlen) || (!m_highlight->isInWord(t[col + sc.matchedLength])))) {
+ goto found;
+ }
+ else {
+ // Start again from the previous character.
+ col--;
+ }
+ }
+ else {
+ // No match.
+ break;
+ }
+ }
+ }
+ else {
+ // Non-whole-word search.
+ col = sc.search(text, col);
+ if (col != -1)
+ goto found;
+ }
+ col = -1;
+ line--;
+ }
+ }
+ sc.flags |= KateView::sfWrapped;
+ return false;
+found:
+ if (sc.flags & KateView::sfWrapped) {
+ if ((line > sc.startCursor.y || (line == sc.startCursor.y && col >= sc.startCursor.x))
+ ^ ((sc.flags & KateView::sfBackward) != 0)) return false;
+ }
+ sc.cursor.x = col;
+ sc.cursor.y = line;
+ return true;
+}
+
+void KateDocument::tagLine(int line) {
+
+ if (tagStart > line) tagStart = line;
+ if (tagEnd < line) tagEnd = line;
+}
+
+void KateDocument::insLine(int line) {
+ KateView *view;
+
+ if (selectStart >= line) selectStart++;
+ if (selectEnd >= line) selectEnd++;
+ if (tagStart >= line) tagStart++;
+ if (tagEnd >= line) tagEnd++;
+
+ newDocGeometry = true;
+ for (view = views.first(); view != 0L; view = views.next() ) {
+ view->insLine(line);
+ }
+}
+
+void KateDocument::delLine(int line) {
+ KateView *view;
+
+ if (selectStart >= line && selectStart > 0) selectStart--;
+ if (selectEnd >= line) selectEnd--;
+ if (tagStart >= line && tagStart > 0) tagStart--;
+ if (tagEnd >= line) tagEnd--;
+
+ newDocGeometry = true;
+ for (view = views.first(); view != 0L; view = views.next() ) {
+ view->delLine(line);
+ }
+}
+
+void KateDocument::optimizeSelection() {
+ TextLine::Ptr textLine;
+
+ while (selectStart <= selectEnd) {
+ textLine = getTextLine(selectStart);
+ if (textLine->isSelected() || textLine->numSelected() > 0) break;
+ selectStart++;
+ }
+ while (selectEnd >= selectStart) {
+ textLine = getTextLine(selectEnd);
+ if (textLine->isSelected() || textLine->numSelected() > 0) break;
+ selectEnd--;
+ }
+ if (selectStart > selectEnd) {
+ selectStart = 0xffffff;
+ selectEnd = 0;
+ }
+}
+
+void KateDocument::doAction(KateAction *a) {
+
+ switch (a->action) {
+ case KateAction::replace:
+ doReplace(a);
+ break;
+ case KateAction::wordWrap:
+ doWordWrap(a);
+ break;
+ case KateAction::wordUnWrap:
+ doWordUnWrap(a);
+ break;
+ case KateAction::newLine:
+ doNewLine(a);
+ break;
+ case KateAction::delLine:
+ doDelLine(a);
+ break;
+ case KateAction::insLine:
+ doInsLine(a);
+ break;
+ case KateAction::killLine:
+ doKillLine(a);
+ break;
+/* case KateAction::doubleLine:
+ break;
+ case KateAction::removeLine:
+ break;*/
+ }
+}
+
+void KateDocument::doReplace(KateAction *a) {
+ TextLine::Ptr textLine;
+ int l;
+
+ //exchange current text with stored text in KateAction *a
+
+ textLine = getTextLine(a->cursor.y);
+ l = textLine->length() - a->cursor.x;
+ if (l > a->len) l = a->len;
+
+ QString oldText(&textLine->getText()[a->cursor.x], (l < 0) ? 0 : l);
+ textLine->replace(a->cursor.x, a->len, a->text.unicode(), a->text.length());
+
+ a->len = a->text.length();
+ a->text = oldText;
+
+ buffer->changeLine(a->cursor.y);
+
+ tagLine(a->cursor.y);
+}
+
+void KateDocument::doWordWrap(KateAction *a) {
+ TextLine::Ptr textLine;
+
+ textLine = getTextLine(a->cursor.y - 1);
+ a->len = textLine->length() - a->cursor.x;
+ textLine->wrap(getTextLine(a->cursor.y),a->len);
+
+ buffer->changeLine(a->cursor.y - 1);
+ buffer->changeLine(a->cursor.y);
+
+ tagLine(a->cursor.y - 1);
+ tagLine(a->cursor.y);
+ if (selectEnd == a->cursor.y - 1) selectEnd++;
+
+ a->action = KateAction::wordUnWrap;
+}
+
+void KateDocument::doWordUnWrap(KateAction *a) {
+ TextLine::Ptr textLine;
+
+ textLine = getTextLine(a->cursor.y - 1);
+// textLine->setLength(a->len);
+ textLine->unWrap(a->len, getTextLine(a->cursor.y),a->cursor.x);
+
+ buffer->changeLine(a->cursor.y - 1);
+ buffer->changeLine(a->cursor.y);
+
+ tagLine(a->cursor.y - 1);
+ tagLine(a->cursor.y);
+
+ a->action = KateAction::wordWrap;
+}
+
+void KateDocument::doNewLine(KateAction *a) {
+ TextLine::Ptr textLine, newLine;
+
+ textLine = getTextLine(a->cursor.y);
+ newLine = new TextLine(textLine->getRawAttr(), textLine->getContext());
+ textLine->wrap(newLine,a->cursor.x);
+
+ buffer->insertLine(a->cursor.y + 1, newLine);
+ buffer->changeLine(a->cursor.y);
+
+ insLine(a->cursor.y + 1);
+ tagLine(a->cursor.y);
+ tagLine(a->cursor.y + 1);
+ if (selectEnd == a->cursor.y) selectEnd++;//addSelection(a->cursor.y + 1);
+
+ a->action = KateAction::delLine;
+}
+
+void KateDocument::doDelLine(KateAction *a) {
+ TextLine::Ptr textLine, nextLine;
+
+ textLine = getTextLine(a->cursor.y);
+ nextLine = getTextLine(a->cursor.y+1);
+// textLine->setLength(a->cursor.x);
+ textLine->unWrap(a->cursor.x, nextLine,nextLine->length());
+ textLine->setContext(nextLine->getContext());
+ if (longestLine == nextLine) longestLine = 0L;
+
+ buffer->changeLine(a->cursor.y);
+ buffer->removeLine(a->cursor.y+1);
+
+ tagLine(a->cursor.y);
+ delLine(a->cursor.y + 1);
+
+ a->action = KateAction::newLine;
+}
+
+void KateDocument::doInsLine(KateAction *a) {
+
+ buffer->insertLine(a->cursor.y, new TextLine());
+
+ insLine(a->cursor.y);
+
+ a->action = KateAction::killLine;
+}
+
+void KateDocument::doKillLine(KateAction *a) {
+ TextLine::Ptr textLine = getTextLine(a->cursor.y);
+ if (longestLine == textLine) longestLine = 0L;
+
+ buffer->removeLine(a->cursor.y);
+
+ delLine(a->cursor.y);
+ tagLine(a->cursor.y);
+
+ a->action = KateAction::insLine;
+}
+
+void KateDocument::newUndo() {
+ KTextEditor::View *view;
+ int state;
+
+ state = 0;
+ if (currentUndo > 0) state |= 1;
+ if (currentUndo < (int) undoList.count()) state |= 2;
+ undoState = state;
+ for (view = m_views.first(); view != 0L; view = m_views.next() ) {
+ emit static_cast<KateView *>( view )->newUndo();
+ }
+}
+
+void KateDocument::recordStart(VConfig &c, int newUndoType) {
+ recordStart(c.view, c.cursor, c.flags, newUndoType);
+}
+
+void KateDocument::recordStart(KateView *, PointStruc &cursor, int flags,
+ int newUndoType, bool keepModal, bool mergeUndo) {
+
+ KateActionGroup *g;
+
+// if (newUndoType == KateActionGroup::ugNone) {
+ // only a bug would cause this
+//why should someone do this? we can't prevent all programming errors :) (jochen whilhelmy)
+// debug("KateDocument::recordStart() called with no undo group type!");
+// return;
+// }
+
+ if (!keepModal) setPseudoModal(0L);
+
+ //i optimized the group undo stuff a bit (jochen wilhelmy)
+ // recordReset() is not needed any more
+ g = undoList.getLast();
+ if (g != 0L && ((undoCount < 1024 && flags & KateView::cfGroupUndo
+ && g->end.x == cursor.x && g->end.y == cursor.y) || mergeUndo)) {
+
+ //undo grouping : same actions are put into one undo step
+ //precondition : new action starts where old stops or mergeUndo flag
+ if (g->undoType == newUndoType
+ || (g->undoType == KateActionGroup::ugInsChar
+ && newUndoType == KateActionGroup::ugInsLine)
+ || (g->undoType == KateActionGroup::ugDelChar
+ && newUndoType == KateActionGroup::ugDelLine)) {
+
+ undoCount++;
+ if (g->undoType != newUndoType) undoCount = 0xffffff;
+ return;
+ }
+ }
+ undoCount = 0;
+/*
+ if (undoView != view) {
+ // always kill the current undo group if the editing view changes
+ recordReset();
+ undoType = newUndoType;
+ } else if (newUndoType == undoType) {
+printf("bla!!!\n");
+ // same as current type, keep using it
+ return;
+ } else if ( (undoType == KateActionGroup::ugInsChar && newUndoType == KateActionGroup::ugInsLine) ||
+ (undoType == KateActionGroup::ugDelChar && newUndoType == KateActionGroup::ugDelLine) ) {
+ // some type combinations can run together...
+ undoType += 1000;
+ return;
+ } else {
+ recordReset();
+ undoType = newUndoType;
+ }
+
+ undoView = view;
+*/
+ while ((int) undoList.count() > currentUndo) undoList.removeLast();
+ while ((int) undoList.count() > undoSteps) {
+ undoList.removeFirst();
+ currentUndo--;
+ }
+
+ g = new KateActionGroup(cursor, newUndoType);
+ undoList.append(g);
+// currentUndo++;
+
+ tagEnd = 0;
+ tagStart = 0xffffff;
+}
+
+void KateDocument::recordAction(KateAction::Action action, PointStruc &cursor) {
+ KateAction *a;
+
+ a = new KateAction(action, cursor);
+ doAction(a);
+ undoList.getLast()->insertAction(a);
+}
+
+void KateDocument::recordInsert(VConfig &c, const QString &text) {
+ recordReplace(c, 0, text);
+}
+
+void KateDocument::recordReplace(VConfig &c, int len, const QString &text) {
+ if (c.cursor.x > 0 && !(c.flags & KateView::cfSpaceIndent)) {
+ TextLine::Ptr textLine = getTextLine(c.cursor.y);
+ if (textLine->length() == 0) {
+ QString s = tabString(c.cursor.x, tabChars);
+ int len = s.length();
+ s += text;
+ c.cursor.x = 0;
+ recordReplace(c.cursor, len, s);
+ c.cursor.x = len;
+ return;
+ }
+ }
+ recordReplace(c.cursor, len, text);
+}
+
+void KateDocument::recordInsert(PointStruc &cursor, const QString &text) {
+ recordReplace(cursor, 0, text);
+}
+
+void KateDocument::recordDelete(PointStruc &cursor, int len) {
+ recordReplace(cursor, len, QString::null);
+}
+
+void KateDocument::recordReplace(PointStruc &cursor, int len, const QString &text) {
+ KateAction *a;
+ TextLine::Ptr textLine;
+ int l;
+
+ if (len == 0 && text.isEmpty()) return;
+
+ //try to append to last replace action
+ a = undoList.getLast()->action;
+ if (a == 0L || a->action != KateAction::replace
+ || a->cursor.x + a->len != cursor.x || a->cursor.y != cursor.y) {
+
+//if (a != 0L) printf("new %d %d\n", a->cursor.x + a->len, cursor.x);
+ a = new KateAction(KateAction::replace, cursor);
+ undoList.getLast()->insertAction(a);
+ }
+
+ //replace
+ textLine = getTextLine(cursor.y);
+ l = textLine->length() - cursor.x;
+ if (l > len) l = len;
+ a->text.insert(a->text.length(), &textLine->getText()[cursor.x], (l < 0) ? 0 : l);
+ textLine->replace(cursor.x, len, text.unicode(), text.length());
+ a->len += text.length();
+
+ buffer->changeLine(a->cursor.y);
+ updateMaxLength(textLine);
+ tagLine(a->cursor.y);
+}
+
+void KateDocument::recordEnd(VConfig &c) {
+ recordEnd(c.view, c.cursor, c.flags);
+}
+
+void KateDocument::recordEnd(KateView *view, PointStruc &cursor, int flags) {
+ KateActionGroup *g;
+
+ // clear selection if option "persistent selections" is off
+// if (!(flags & cfPersistent)) deselectAll();
+
+ g = undoList.getLast();
+ if (g->action == 0L) {
+ // no action has been done: remove empty undo record
+ undoList.removeLast();
+ return;
+ }
+ // store end cursor position for redo
+ g->end = cursor;
+ currentUndo = undoList.count();
+
+ if (tagStart <= tagEnd) {
+ optimizeSelection();
+ updateLines(tagStart, tagEnd, flags, cursor.y);
+ setModified(true);
+ }
+
+ view->updateCursor(cursor, flags);
+
+// newUndo();
+/*
+ undoCount++;
+ // we limit the number of individual undo operations for sanity - is 1K reasonable?
+ // this is also where we handle non-group undo preference
+ // if the undo type is singlular, we always finish it now
+ if ( undoType == KateActionGroup::ugPaste ||
+ undoType == KateActionGroup::ugDelBlock ||
+ undoType > 1000 ||
+ undoCount > 1024 || !(flags & cfGroupUndo) ) {
+printf("recordend %d %d\n", undoType, undoCount);
+ recordReset();
+ }
+*/
+
+ // this should keep the flood of signals down a little...
+ if (undoCount == 0) newUndo();
+ emit textChanged();
+}
+/*
+void KateDocument::recordReset()
+{
+ if (pseudoModal)
+ return;
+
+ // forces the next call of recordStart() to begin a new undo group
+ // not used in normal editing, but used by markFound(), etc.
+ undoType = KateActionGroup::ugNone;
+ undoCount = 0;
+ undoView = NULL;
+ undoReported = false;
+printf("recordreset\n");
+}
+*/
+
+/*
+void KateDocument::recordDel(PointStruc &cursor, TextLine::Ptr &textLine, int l) {
+ int len;
+
+ len = textLine->length() - cursor.x;
+ if (len > l) len = l;
+ if (len > 0) {
+ insertUndo(new KateAction(KateAction::replace,cursor,&textLine->getText()[cursor.x],len));
+ }
+}
+*/
+
+
+void KateDocument::doActionGroup(KateActionGroup *g, int flags, bool undo) {
+ KateAction *a, *next;
+
+ setPseudoModal(0L);
+ if (!(flags & KateView::cfPersistent)) deselectAll();
+ tagEnd = 0;
+ tagStart = 0xffffff;
+
+ a = g->action;
+ g->action = 0L;
+ while (a) {
+ doAction(a);
+ next = a->next;
+ g->insertAction(a);
+ a = next;
+ }
+ optimizeSelection();
+ if (tagStart <= tagEnd) updateLines(tagStart, tagEnd, flags);
+
+ // the undo/redo functions set undo to true, all others should leave it
+ // alone (default)
+ if (!undo) {
+ setModified(true);
+ newUndo();
+ }
+}
+
+int KateDocument::nextUndoType()
+{
+ KateActionGroup *g;
+
+ if (currentUndo <= 0) return KateActionGroup::ugNone;
+ g = undoList.at(currentUndo - 1);
+ return g->undoType;
+}
+
+int KateDocument::nextRedoType()
+{
+ KateActionGroup *g;
+
+ if (currentUndo >= (int) undoList.count()) return KateActionGroup::ugNone;
+ g = undoList.at(currentUndo);
+// if (!g) return KateActionGroup::ugNone;
+ return g->undoType;
+}
+
+void KateDocument::undoTypeList(QValueList<int> &lst)
+{
+ lst.clear();
+ for (int i = currentUndo-1; i>=0 ;i--)
+ lst.append(undoList.at(i)->undoType);
+}
+
+void KateDocument::redoTypeList(QValueList<int> &lst)
+{
+ lst.clear();
+ for (int i = currentUndo+1; i<(int)undoList.count(); i++)
+ lst.append(undoList.at(i)->undoType);
+}
+
+void KateDocument::undo(VConfig &c, int count) {
+ KateActionGroup *g = 0L;
+ int num;
+ bool needUpdate = false; // don't update the cursor until completely done
+
+ if (count <= 0) return;
+
+ for (num = 0 ; num < count ; num++) {
+ if (currentUndo <= 0) break;
+ currentUndo--;
+ g = undoList.at(currentUndo);
+ doActionGroup(g, c.flags, true); // do not setModified() or newUndo()
+ needUpdate = true;
+
+// if (num == 0) recordReset();
+ }
+
+ if (needUpdate) {
+ // since we told doActionGroup() not to do this stuff, we need to do it now
+ c.view->updateCursor(g->start);
+ setModified(true);
+ newUndo();
+ }
+}
+
+void KateDocument::redo(VConfig &c, int count) {
+ KateActionGroup *g = 0L;
+ int num;
+ bool needUpdate = false; // don't update the cursor until completely done
+
+ if (count <= 0) return;
+
+ for (num = 0 ; num < count ; num++) {
+ if (currentUndo+1 > (int)undoList.count()) break;
+ g = undoList.at(currentUndo);
+ currentUndo++;
+ doActionGroup(g, c.flags, true); // do not setModified() or newUndo()
+ needUpdate = true;
+
+// if (num == 0) recordReset();
+ }
+
+ if (needUpdate) {
+ // since we told doActionGroup() not to do this stuff, we need to do it now
+ c.view->updateCursor(g->end);
+ setModified(true);
+ newUndo();
+ }
+}
+
+void KateDocument::clearRedo() {
+ // disable redos
+ // this was added as an assist to the spell checker
+ bool deleted = false;
+
+ while ((int) undoList.count() > currentUndo) {
+ deleted = true;
+ undoList.removeLast();
+ }
+
+ if (deleted) newUndo();
+}
+
+void KateDocument::setUndoSteps(int steps) {
+ if (steps < 5) steps = 5;
+ undoSteps = steps;
+}
+
+void KateDocument::setPseudoModal(QWidget *w) {
+// QWidget *old = pseudoModal;
+
+ // (glenebob)
+ // this is a temporary hack to make the spell checker work a little
+ // better - as kspell progresses, this sort of thing should become
+ // obsolete or worked around more cleanly
+ // this is relied upon *only* by the spell-check code
+ if (pseudoModal && pseudoModal != (QWidget*)1L)
+ delete pseudoModal;
+
+// pseudoModal = 0L;
+// if (old || w) recordReset();
+
+ pseudoModal = w;
+}
+
+
+void KateDocument::newBracketMark(PointStruc &cursor, BracketMark &bm)
+{
+ TextLine::Ptr textLine;
+ int x, line, count, attr;
+ QChar bracket, opposite, ch;
+ Attribute *a;
+
+ bm.eXPos = -1; //mark bracked mark as invalid
+ x = cursor.x -1; // -1 to look at left side of cursor
+ if (x < 0) return;
+ line = cursor.y; //current line
+ count = 0; //bracket counter for nested brackets
+
+ textLine = getTextLine(line);
+ if (!textLine) return;
+
+ bracket = textLine->getChar(x);
+ attr = textLine->getAttr(x);
+
+ if (bracket == '(' || bracket == '[' || bracket == '{')
+ {
+ //get opposite bracket
+ opposite = ')';
+ if (bracket == '[') opposite = ']';
+ if (bracket == '{') opposite = '}';
+ //get attribute of bracket (opposite bracket must have the same attribute)
+ x++;
+ while (line - cursor.y < 40) {
+ //go to next line on end of line
+ while (x >= (int) textLine->length()) {
+ line++;
+ if (line > lastLine()) return;
+ textLine = getTextLine(line);
+ x = 0;
+ }
+ if (textLine->getAttr(x) == attr) {
+ //try to find opposite bracked
+ ch = textLine->getChar(x);
+ if (ch == bracket) count++; //same bracket : increase counter
+ if (ch == opposite) {
+ count--;
+ if (count < 0) goto found;
+ }
+ }
+ x++;
+ }
+ }
+ else if (bracket == ')' || bracket == ']' || bracket == '}')
+ {
+ opposite = '(';
+ if (bracket == ']') opposite = '[';
+ if (bracket == '}') opposite = '{';
+ x--;
+ while (cursor.y - line < 20) {
+
+ while (x < 0) {
+ line--;
+ if (line < 0) return;
+ textLine = getTextLine(line);
+ x = textLine->length() -1;
+ }
+ if (textLine->getAttr(x) == attr) {
+ ch = textLine->getChar(x);
+ if (ch == bracket) count++;
+ if (ch == opposite) {
+ count--;
+ if (count < 0) goto found;
+ }
+ }
+ x--;
+ }
+ }
+ return;
+
+found:
+ //cursor position of opposite bracket
+ bm.cursor.x = x;
+ bm.cursor.y = line;
+ //x position (start and end) of related bracket
+ bm.sXPos = textWidth(textLine, x);
+ a = &m_attribs[attr];
+
+ if (a->bold && a->italic)
+ bm.eXPos = bm.sXPos + myFontMetricsBI.width(bracket);
+ else if (a->bold)
+ bm.eXPos = bm.sXPos + myFontMetricsBold.width(bracket);
+ else if (a->italic)
+ bm.eXPos = bm.sXPos + myFontMetricsItalic.width(bracket);
+ else
+ bm.eXPos = bm.sXPos + myFontMetrics.width(bracket);
+}
+
+void KateDocument::clipboardChanged() { //slot
+//#if defined(_WS_X11_)
+ if (m_singleSelection) {
+ disconnect(QApplication::clipboard(), SIGNAL(dataChanged()),
+ this, SLOT(clipboardChanged()));
+ deselectAll();
+ updateViews();
+ }
+//#endif
+}
+
+#if 0
+void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
+{
+ KParts::ReadWritePart::guiActivateEvent( ev );
+ if ( ev->activated() )
+ emit selectionChanged();
+}
+#endif
+
+void KateDocument::setDocName (QString docName)
+{
+ myDocName = docName;
+ emit nameChanged (this);
+}
+
+void KateDocument::setMTime()
+{
+ if (fileInfo && !fileInfo->fileName().isEmpty()) {
+ fileInfo->refresh();
+ mTime = fileInfo->lastModified();
+ }
+}
+
+void KateDocument::isModOnHD(bool forceReload)
+{
+ if (fileInfo && !fileInfo->fileName().isEmpty()) {
+ fileInfo->refresh();
+ if (fileInfo->lastModified() > mTime) {
+ if ( forceReload ||
+ (KMessageBox::warningContinueCancel(0,
+ (i18n("The file %1 has changed on disk.\nDo you want to reload it?\n\nIf you cancel you will lose these changes next time you save this file")).arg(m_url),
+ i18n("File has changed on Disk"),
+ i18n("Yes") ) == KMessageBox::Continue)
+ )
+ reloadFile();
+ else
+ setMTime();
+ }
+ }
+}
+
+void KateDocument::reloadFile()
+{
+#warning fixme
+#if 0
+ if (fileInfo && !fileInfo->fileName().isEmpty()) {
+ KateDocument::openFile();
+ setMTime();
+ }
+#endif
+}
+
+void KateDocument::slotModChanged()
+{
+ emit modStateChanged (this);
+}
+
+QList<Kate::Mark> KateDocument::marks ()
+{
+ QList<Kate::Mark> list;
+ TextLine::Ptr line;
+
+ for (int i=0; i < numLines(); i++)
+ {
+ line = getTextLine(i);
+ if (line->mark() != 0)
+ {
+ Kate::Mark *mark=new Kate::Mark;
+ mark->line = i;
+ mark->type = line->mark();
+ list.append (mark);
+ }
+ }
+
+ return list;
+}
+
+void KateDocument::flush ()
+{
+ if (isReadOnly())
+ return;
+
+ m_url = QString::null;
+ fileInfo->setFile (QString());
+ setMTime();
+
+ clear();
+ updateViews();
+
+ emit fileNameChanged ();
+}
+
+void KateDocument::open (const QString &name)
+{
+ openURL (name);
+}
+
+void KateDocument::wrapText (uint col)
+{
+ int line = 0;
+ int z = 0;
+
+ while(true)
+ {
+ TextLine::Ptr l = getTextLine(line);
+
+ if (l->length() > col)
+ {
+ TextLine::Ptr tl = new TextLine();
+ buffer->insertLine(line+1,tl);
+ const QChar *text = l->getText();
+
+ for (z=col; z>0; z--)
+ {
+ if (z < 1) break;
+ if (text[z].isSpace()) break;
+ }
+
+ if (z < 1) z=col;
+
+ l->wrap (tl, z);
+ }
+
+ line++;
+ if (line >= numLines()) break;
+ };
+
+ newDocGeometry=true;
+ updateLines();
+ updateViews();
+}
+
+void KateDocument::setWordWrap (bool on)
+{
+ if (on != myWordWrap && on)
+ wrapText (myWordWrapAt);
+
+ myWordWrap = on;
+}
+
+void KateDocument::setWordWrapAt (uint col)
+{
+ if (myWordWrapAt != col && myWordWrap)
+ wrapText (myWordWrapAt);
+
+ myWordWrapAt = col;
+}
+
+void KateDocument::applyWordWrap ()
+{
+ wrapText (myWordWrapAt);
+}