summaryrefslogtreecommitdiff
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/apps/tinykate/libkate/document/katedocument.cpp10
-rw-r--r--noncore/apps/tinykate/libkate/document/katedocument.h11
2 files changed, 15 insertions, 6 deletions
diff --git a/noncore/apps/tinykate/libkate/document/katedocument.cpp b/noncore/apps/tinykate/libkate/document/katedocument.cpp
index 10bc976..df1de8d 100644
--- a/noncore/apps/tinykate/libkate/document/katedocument.cpp
+++ b/noncore/apps/tinykate/libkate/document/katedocument.cpp
@@ -1,3169 +1,3177 @@
/***************************************************************************
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 ;
}
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 ;
}
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");
+// 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::setDocFile (QString docFile)
+{
+ m_file = docFile;
+ emit fileNameChanged ();
+}
+
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);
}
diff --git a/noncore/apps/tinykate/libkate/document/katedocument.h b/noncore/apps/tinykate/libkate/document/katedocument.h
index 220d188..356541f 100644
--- a/noncore/apps/tinykate/libkate/document/katedocument.h
+++ b/noncore/apps/tinykate/libkate/document/katedocument.h
@@ -1,569 +1,570 @@
/***************************************************************************
katedocument.h - 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.
*/
#ifndef kate_document_h
#define kate_document_h
#include <qobject.h>
#include <qlist.h>
#include <qcolor.h>
#include <qfont.h>
#include <qfontmetrics.h>
#include <qdatetime.h>
#include <qfileinfo.h>
#include "../view/kateview.h"
#include "katehighlight.h"
#include "katebuffer.h"
#include "katetextline.h"
#include <qptrdict.h>
class KateCmd;
class CachedFontMetrics : public QFontMetrics {
private:
short *warray[256];
public:
CachedFontMetrics(const QFont& f) : QFontMetrics(f) {
for (int i=0; i<256; i++) warray[i]=0;
}
~CachedFontMetrics() {
for (int i=0; i<256; i++)
if (warray[i]) delete[] warray[i];
}
int width(QChar c) {
uchar cell=c.cell();
uchar row=c.row();
short *wa=warray[row];
if (!wa) {
// qDebug("create row: %d",row);
wa=warray[row]=new short[256];
for (int i=0; i<256; i++) wa[i]=-1;
}
if (wa[cell]<0) wa[cell]=(short) QFontMetrics::width(c);
return (int)wa[cell];
}
int width(QString s) { return QFontMetrics::width(s); }
};
class Attribute {
public:
Attribute() { ; };
QColor col;
QColor selCol;
bool bold;
bool italic;
};
class KateAction {
public:
enum Action {replace, wordWrap, wordUnWrap, newLine, delLine,
insLine, killLine};//, doubleLine, removeLine};
KateAction(Action, PointStruc &cursor, int len = 0,
const QString &text = QString::null);
Action action;
PointStruc cursor;
int len;
QString text;
KateAction *next;
};
class KateActionGroup {
public:
// the undo group types
enum { ugNone, //
ugPaste, // paste
ugDelBlock, // delete/replace selected text
ugIndent, // indent
ugUnindent, // unindent
ugComment, // comment
ugUncomment, // uncomment
ugReplace, // text search/replace
ugSpell, // spell check
ugInsChar, // char type/deleting
ugDelChar, // '' ''
ugInsLine, // line insert/delete
ugDelLine // '' ''
};
KateActionGroup(PointStruc &aStart, int type = ugNone);
~KateActionGroup();
void insertAction(KateAction *);
static const char * typeName(int type);
PointStruc start;
PointStruc end;
KateAction *action;
int undoType;
};
/**
The text document. It contains the textlines, controls the
document changing operations and does undo/redo. WARNING: do not change
the text contents directly in methods where this is not explicitly
permitted. All changes have to be made with some basic operations,
which are recorded by the undo/redo system.
@see TextLine
@author Jochen Wilhelmy
*/
class KateDocument: public Kate::Document
{
Q_OBJECT
friend class KateViewInternal;
friend class KateView;
friend class KateIconBorder;
public:
KateDocument(bool bSingleViewMode=false, bool bBrowserView=false, QWidget *parentWidget = 0, const char *widgetName = 0, QObject * = 0, const char * = 0);
~KateDocument();
protected:
QFont myFont, myFontBold, myFontItalic, myFontBI;
CachedFontMetrics myFontMetrics, myFontMetricsBold, myFontMetricsItalic, myFontMetricsBI;
public:
void setFont (QFont font);
QFont getFont () { return myFont; };
CachedFontMetrics getFontMetrics () { return myFontMetrics; };
virtual bool saveFile();
virtual KTextEditor::View *createView( QWidget *parent, const char *name );
virtual QString textLine( int line ) const;
virtual void insertLine( const QString &s, int line = -1 );
void insert_Line(const QString& s,int line=-1, bool update=true);
void remove_Line(int line,bool update=true);
void replaceLine(const QString& s,int line=-1);
virtual void insertAt( const QString &s, int line, int col, bool mark = FALSE );
virtual void removeLine( int line );
virtual int length() const;
virtual void setSelection( int row_from, int col_from, int row_to, int col_t );
virtual bool hasSelection() const;
virtual QString selection() const;
// only to make part work, don't change it !
bool m_bSingleViewMode;
// public interface
/**
* gets the number of lines
*/
virtual int numLines() const;
/**
* gets the last line number (numLines() -1)
*/
int lastLine() const {return numLines()-1;}
/**
gets the given line
@return the TextLine object at the given line
@see TextLine
*/
TextLine::Ptr getTextLine(int line) const;
/**
get the length in pixels of the given line
*/
int textLength(int line);
void setTabWidth(int);
int tabWidth() {return tabChars;}
void setReadOnly(bool);
bool isReadOnly() const;
void setNewDoc( bool );
bool isNewDoc() const;
virtual void setReadWrite( bool ){};
virtual bool isReadWrite() const {return true;}
virtual void setModified(bool);
virtual bool isModified() const;
void setSingleSelection(bool ss) {m_singleSelection = ss;}
bool singleSelection() {return m_singleSelection;}
void readConfig();
void writeConfig();
void readSessionConfig(KConfig *);
void writeSessionConfig(KConfig *);
bool hasBrowserExtension() const { return m_bBrowserView; }
protected:
bool m_bBrowserView;
signals:
void selectionChanged();
void highlightChanged();
void modifiedChanged ();
void preHighlightChanged(long);
// search stuff
protected:
static QStringList searchForList;
static QStringList replaceWithList;
static uint uniqueID;
// highlight stuff
public:
Highlight *highlight() {return m_highlight;}
int highlightNum() {return hlManager->findHl(m_highlight);}
int numAttribs() {return m_numAttribs;}
Attribute *attribs() {return m_attribs;}
void setDontChangeHlOnSave();
protected:
void setHighlight(int n);
void makeAttribs();
void updateFontData();
protected slots:
void hlChanged();
// view interaction
public:
virtual void addView(KTextEditor::View *);
virtual void removeView(KTextEditor::View *);
bool ownedView(KateView *);
bool isLastView(int numViews);
int getTextLineCount() {return numLines();}
int textWidth(const TextLine::Ptr &, int cursorX);
int textWidth(PointStruc &cursor);
int textWidth(bool wrapCursor, PointStruc &cursor, int xPos);
int textPos(const TextLine::Ptr &, int xPos);
// int textPos(TextLine::Ptr &, int xPos, int &newXPos);
int textWidth();
int textHeight();
void insert(VConfig &, const QString &);
void insertFile(VConfig &, QIODevice &);
int currentColumn(PointStruc &cursor);
bool insertChars(VConfig &, const QString &chars);
void newLine(VConfig &);
void killLine(VConfig &);
void backspace(VConfig &);
void del(VConfig &);
void clear();
void cut(VConfig &);
void copy(int flags);
void paste(VConfig &);
void toggleRect(int, int, int, int);
void selectTo(VConfig &c, PointStruc &cursor, int cXPos);
void selectAll();
void deselectAll();
void invertSelection();
void selectWord(PointStruc &cursor, int flags);
void selectLength(PointStruc &cursor, int length, int flags);
void indent(VConfig &c) {doIndent(c, 1);}
void unIndent(VConfig &c) {doIndent(c, -1);}
void cleanIndent(VConfig &c) {doIndent(c, 0);}
// called by indent/unIndent/cleanIndent
// just does some setup and then calls optimizeLeadingSpace()
void doIndent(VConfig &, int change);
// optimize leading whitespace on a single line - see kwdoc.cpp for full description
void optimizeLeadingSpace(int line, int flags, int change);
void comment(VConfig &c) {doComment(c, 1);}
void unComment(VConfig &c) {doComment(c, -1);}
void doComment(VConfig &, int change);
virtual QString text() const;
QString getWord(PointStruc &cursor);
public slots:
virtual void setText(const QString &);
public:
long needPreHighlight(long till);
bool hasMarkedText() {return (selectEnd >= selectStart);}
QString markedText(int flags);
void delMarkedText(VConfig &/*, bool undo = true*/);
void tagLineRange(int line, int x1, int x2);
void tagLines(int start, int end);
void tagAll();
void updateLines(int startLine = 0, int endLine = 0xffffff, int flags = 0, int cursorY = -1);
void updateMaxLength(TextLine::Ptr &);
void updateViews(KateView *exclude = 0L);
QColor &cursorCol(int x, int y);
void paintTextLine(QPainter &, int line, int xStart, int xEnd, bool showTabs);
void paintTextLine(QPainter &, int line, int y, int xStart, int xEnd, bool showTabs);
bool doSearch(SConfig &s, const QString &searchFor);
// internal
void tagLine(int line);
void insLine(int line);
void delLine(int line);
void optimizeSelection();
void doAction(KateAction *);
void doReplace(KateAction *);
void doWordWrap(KateAction *);
void doWordUnWrap(KateAction *);
void doNewLine(KateAction *);
void doDelLine(KateAction *);
void doInsLine(KateAction *);
void doKillLine(KateAction *);
void newUndo();
void recordStart(VConfig &, int newUndoType);
void recordStart(KateView *, PointStruc &, int flags, int newUndoType, bool keepModal = false, bool mergeUndo = false);
void recordAction(KateAction::Action, PointStruc &);
void recordInsert(VConfig &, const QString &text);
void recordReplace(VConfig &, int len, const QString &text);
void recordInsert(PointStruc &, const QString &text);
void recordDelete(PointStruc &, int len);
void recordReplace(PointStruc &, int len, const QString &text);
void recordEnd(VConfig &);
void recordEnd(KateView *, PointStruc &, int flags);
void doActionGroup(KateActionGroup *, int flags, bool undo = false);
int nextUndoType();
int nextRedoType();
void undoTypeList(QValueList<int> &lst);
void redoTypeList(QValueList<int> &lst);
void undo(VConfig &, int count = 1);
void redo(VConfig &, int count = 1);
void clearRedo();
void setUndoSteps(int steps);
void setPseudoModal(QWidget *);
void newBracketMark(PointStruc &, BracketMark &);
protected slots:
void clipboardChanged();
void slotBufferChanged();
void slotBufferHighlight(long,long);
void doPreHighlight();
private slots:
void slotViewDestroyed();
// member variables
protected:
long PreHighlightedTill;
long RequestPreHighlightTill;
KWBuffer *buffer;
QColor colors[2];
HlManager *hlManager;
Highlight *m_highlight;
int m_numAttribs;
static const int maxAttribs;
Attribute *m_attribs;
int eolMode;
int tabChars;
int m_tabWidth;
int fontHeight;
int fontAscent;
QList<KateView> views;
bool newDocGeometry;
TextLine::Ptr longestLine;
float maxLength;
PointStruc select;
PointStruc anchor;
int aXPos;
int selectStart;
int selectEnd;
bool oldMarkState;
bool m_singleSelection; // false: windows-like, true: X11-like
bool readOnly;
bool newDoc; // True if the file is a new document (used to determine whether
// to check for overwriting files on save)
bool modified;
bool myWordWrap;
uint myWordWrapAt;
QList<KateActionGroup> undoList;
int currentUndo;
int undoState;
int undoSteps;
int tagStart;
int tagEnd;
int undoCount; //counts merged undo steps
QWidget *pseudoModal; //the replace prompt is pseudo modal
public:
/** Tjecks if the file on disk is newer than document contents.
If forceReload is true, the document is reloaded without asking the user,
otherwise [default] the user is asked what to do. */
void isModOnHD(bool forceReload=false);
uint docID () {return myDocID;};
QString docName () {return myDocName;};
void setDocName (QString docName);
+ void setDocFile (QString docFile);
public slots:
/** Reloads the current document from disk if possible */
void reloadFile();
private slots:
void slotModChanged ();
private:
/** updates mTime to reflect file on fs.
called from constructor and from saveFile. */
void setMTime();
uint myDocID;
QFileInfo* fileInfo;
QDateTime mTime;
QString myDocName;
QString m_url;
QString m_file;
void openURL(const QString &filename);
private:
KateCmd *myCmd;
public:
KateCmd *cmd () { return myCmd; };
private:
QString myEncoding;
public:
void setEncoding (QString e) { myEncoding = e; };
QString encoding() { return myEncoding; };
void setWordWrap (bool on);
bool wordWrap () { return myWordWrap; };
void setWordWrapAt (uint col);
uint wordWrapAt () { return myWordWrapAt; };
signals:
void modStateChanged (KateDocument *doc);
void nameChanged (KateDocument *doc);
public:
QList<Kate::Mark> marks ();
public slots:
// clear buffer/filename - update the views
void flush ();
signals:
/**
The file has been saved (perhaps the name has changed). The main window
can use this to change its caption
*/
void fileNameChanged ();
public:
//end of line settings
enum Eol_settings {eolUnix=0,eolDos=1,eolMacintosh=2};
// for the DCOP interface
public:
void open (const QString &name=0);
public:
// wrap the text of the document at the column col
void wrapText (uint col);
public slots:
void applyWordWrap ();
private:
- class KateDocPrivate
- {
- public:
- bool hlSetByUser;
- };
+ class KateDocPrivate
+ {
+ public:
+ bool hlSetByUser;
+ };
// BCI: Add a real d-pointer in the next BIC release
static QPtrDict<KateDocPrivate>* d_ptr;
static void cleanup_d_ptr()
{
delete d_ptr;
}
KateDocPrivate* d( const KateDocument* foo )
{
if ( !d_ptr ) {
d_ptr = new QPtrDict<KateDocPrivate>;
//qAddPostRoutine( cleanup_d_ptr );
}
KateDocPrivate* ret = d_ptr->find( (void*) foo );
if ( ! ret ) {
ret = new KateDocPrivate;
d_ptr->replace( (void*) foo, ret );
}
return ret;
}
void delete_d( const KateDocument* foo )
{
if ( d_ptr )
d_ptr->remove( (void*) foo );
}
};
#endif