You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
631 lines
15 KiB
631 lines
15 KiB
/* This file is part of the KDE libraries |
|
|
|
Copyright (C) 1997 Bernd Johannes Wuebben <wuebben@math.cornell.edu> |
|
Copyright (C) 2000 Waldo Bastian <bastian@kde.org> |
|
|
|
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., 51 Franklin Street, Fifth Floor, |
|
Boston, MA 02110-1301, USA. |
|
*/ |
|
|
|
#include <qtextstream.h> |
|
#include <qtimer.h> |
|
|
|
#include <kcursor.h> |
|
#include <kdebug.h> |
|
#include <kfontdialog.h> |
|
#include <klocale.h> |
|
#include <kmessagebox.h> |
|
#include <kstandardshortcut.h> |
|
#include <QKeyEvent> |
|
#include <QMenu> |
|
|
|
#include "keditcl.h" |
|
#include "keditcl.moc" |
|
|
|
class KEdit::KEditPrivate |
|
{ |
|
public: |
|
bool overwriteEnabled:1; |
|
bool posDirty:1; |
|
bool autoUpdate:1; |
|
}; |
|
|
|
|
|
KEdit::KEdit(QWidget *_parent) |
|
: Q3MultiLineEdit(_parent),d(new KEditPrivate) |
|
{ |
|
d->overwriteEnabled = false; |
|
d->posDirty = true; |
|
d->autoUpdate = true; |
|
|
|
parent = _parent; |
|
|
|
// set some defaults |
|
|
|
line_pos = col_pos = 0; |
|
|
|
srchdialog = NULL; |
|
replace_dialog= NULL; |
|
gotodialog = NULL; |
|
|
|
KCursor::setAutoHideCursor( this, true ); |
|
|
|
connect(this, SIGNAL(cursorPositionChanged(int,int)), |
|
this, SLOT(slotCursorPositionChanged())); |
|
} |
|
|
|
|
|
KEdit::~KEdit() |
|
{ |
|
delete d; |
|
} |
|
|
|
void |
|
KEdit::setAutoUpdate(bool b) |
|
{ |
|
d->autoUpdate = b; |
|
} |
|
|
|
void |
|
KEdit::insertText(QTextStream *stream) |
|
{ |
|
// setAutoUpdate(false); |
|
int line, col; |
|
getCursorPosition(&line, &col); |
|
int saveline = line; |
|
int savecol = col; |
|
QString textLine; |
|
|
|
// MS: Patch by Martin Schenk <martin@schenk.com> |
|
// MS: disable UNDO, or QMultiLineEdit remembers every textLine !!! |
|
// memory usage is: |
|
// textLine: 2*size rounded up to nearest power of 2 (520Kb -> 1024Kb) |
|
// widget: about (2*size + 60bytes*lines) |
|
// -> without disabling undo, it often needs almost 8*size |
|
int oldUndoDepth = undoDepth(); |
|
setUndoDepth( 0 ); // ### -1? |
|
|
|
// MS: read everything at once if file <= 1MB, |
|
// else read in 5000-line chunks to keep memory usage acceptable. |
|
QIODevice *dev=stream->device(); |
|
if (dev && dev->size()>(1024*1024)) { |
|
while(1) { |
|
int i; |
|
textLine=""; |
|
for (i=0; i<5000; i++) { |
|
QString line=stream->readLine(); |
|
if (line.isNull()) break; // EOF |
|
textLine+=line+'\n'; |
|
} |
|
insertAt(textLine, line, col); |
|
line+=i; col=0; |
|
if (i!=5000) break; |
|
} |
|
} |
|
else { |
|
textLine = stream->readAll(); // Read all ! |
|
insertAt( textLine, line, col); |
|
} |
|
setUndoDepth( oldUndoDepth ); |
|
|
|
setCursorPosition(saveline, savecol); |
|
// setAutoUpdate(true); |
|
|
|
// repaint(); |
|
|
|
setModified(true); |
|
setFocus(); |
|
|
|
// Bernd: Please don't leave debug message like that lying around |
|
// they cause ENORMOUSE performance hits. Once upon a day |
|
// kedit used to be really really fast using memmap etc ..... |
|
// oh well .... |
|
|
|
// QString str = text(); |
|
// for (int i = 0; i < (int) str.length(); i++) |
|
// printf("KEdit: U+%04X\n", str[i].unicode()); |
|
|
|
} |
|
|
|
void |
|
KEdit::cleanWhiteSpace() |
|
{ |
|
d->autoUpdate = false; |
|
if (!hasMarkedText()) |
|
selectAll(); |
|
QString oldText = markedText(); |
|
QString newText; |
|
QStringList lines = oldText.split('\n', QString::KeepEmptyParts); |
|
bool addSpace = false; |
|
bool firstLine = true; |
|
QChar lastChar = oldText[oldText.length()-1]; |
|
QChar firstChar = oldText[0]; |
|
for(QStringList::Iterator it = lines.begin(); |
|
it != lines.end();) |
|
{ |
|
QString line = (*it).simplified(); |
|
if (line.isEmpty()) |
|
{ |
|
if (addSpace) |
|
newText += QLatin1String("\n\n"); |
|
if (firstLine) |
|
{ |
|
if (firstChar.isSpace()) |
|
newText += '\n'; |
|
firstLine = false; |
|
} |
|
addSpace = false; |
|
} |
|
else |
|
{ |
|
if (addSpace) |
|
newText += ' '; |
|
if (firstLine) |
|
{ |
|
if (firstChar.isSpace()) |
|
newText += ' '; |
|
firstLine = false; |
|
} |
|
newText += line; |
|
addSpace = true; |
|
} |
|
it = lines.erase(it); |
|
} |
|
if (addSpace) |
|
{ |
|
if (lastChar == '\n') |
|
newText += '\n'; |
|
else if (lastChar.isSpace()) |
|
newText += ' '; |
|
} |
|
|
|
if (oldText == newText) |
|
{ |
|
deselect(); |
|
d->autoUpdate = true; |
|
repaint(); |
|
return; |
|
} |
|
if (wordWrap() == NoWrap) |
|
{ |
|
// If wordwrap is off, we have to do some line-wrapping ourselves now |
|
// We use another QMultiLineEdit for this, so that we get nice undo |
|
// behavior. |
|
Q3MultiLineEdit *we = new Q3MultiLineEdit(); |
|
we->setWordWrap(FixedColumnWidth); |
|
we->setWrapColumnOrWidth(78); |
|
we->setText(newText); |
|
newText.clear(); |
|
for(int i = 0; i < we->numLines(); i++) |
|
{ |
|
QString line = we->textLine(i); |
|
if (line.right(1) != "\n") |
|
line += '\n'; |
|
newText += line; |
|
} |
|
delete we; |
|
} |
|
|
|
insert(newText); |
|
d->autoUpdate = true; |
|
repaint(); |
|
|
|
setModified(true); |
|
setFocus(); |
|
} |
|
|
|
|
|
void |
|
KEdit::saveText(QTextStream *stream, bool softWrap) |
|
{ |
|
int line_count = numLines()-1; |
|
if (line_count < 0) |
|
return; |
|
|
|
if (softWrap || (wordWrap() == NoWrap)) |
|
{ |
|
for(int i = 0; i < line_count; i++) |
|
{ |
|
(*stream) << textLine(i) << '\n'; |
|
} |
|
(*stream) << textLine(line_count); |
|
} |
|
else |
|
{ |
|
for(int i = 0; i <= line_count; i++) |
|
{ |
|
int lines_in_parag = linesOfParagraph(i); |
|
if (lines_in_parag == 1) |
|
{ |
|
(*stream) << textLine(i); |
|
} |
|
else |
|
{ |
|
QString parag_text = textLine(i); |
|
int pos = 0; |
|
int first_pos = 0; |
|
int current_line = 0; |
|
while(true) { |
|
while(lineOfChar(i, pos) == current_line) pos++; |
|
(*stream) << parag_text.mid(first_pos, pos - first_pos - 1) << '\n'; |
|
current_line++; |
|
first_pos = pos; |
|
if (current_line+1 == lines_in_parag) |
|
{ |
|
// Last line |
|
(*stream) << parag_text.mid(pos); |
|
break; |
|
} |
|
} |
|
} |
|
if (i < line_count) |
|
(*stream) << '\n'; |
|
} |
|
} |
|
} |
|
|
|
int KEdit::currentLine(){ |
|
|
|
computePosition(); |
|
return line_pos; |
|
|
|
} |
|
|
|
int KEdit::currentColumn(){ |
|
|
|
computePosition(); |
|
return col_pos; |
|
} |
|
|
|
void KEdit::slotCursorPositionChanged() |
|
{ |
|
d->posDirty = true; |
|
emit CursorPositionChanged(); |
|
} |
|
|
|
void KEdit::computePosition() |
|
{ |
|
if (!d->posDirty) return; |
|
d->posDirty = false; |
|
|
|
int line, col; |
|
|
|
getCursorPosition(&line,&col); |
|
|
|
// line is expressed in paragraphs, we now need to convert to lines |
|
line_pos = 0; |
|
if (wordWrap() == NoWrap) |
|
{ |
|
line_pos = line; |
|
} |
|
else |
|
{ |
|
for(int i = 0; i < line; i++) |
|
line_pos += linesOfParagraph(i); |
|
} |
|
|
|
int line_offset = lineOfChar(line, col); |
|
line_pos += line_offset; |
|
|
|
// We now calculate where the current line starts in the paragraph. |
|
QString linetext = textLine(line); |
|
int start_of_line = 0; |
|
if (line_offset > 0) |
|
{ |
|
start_of_line = col; |
|
while(lineOfChar(line, --start_of_line) == line_offset); |
|
start_of_line++; |
|
} |
|
|
|
|
|
// O.K here is the deal: The function getCursorPositoin returns the character |
|
// position of the cursor, not the screenposition. I.e,. assume the line |
|
// consists of ab\tc then the character c will be on the screen on position 8 |
|
// whereas getCursorPosition will return 3 if the cursors is on the character c. |
|
// Therefore we need to compute the screen position from the character position. |
|
// That's what all the following trouble is all about: |
|
|
|
int coltemp = col-start_of_line; |
|
int pos = 0; |
|
int find = 0; |
|
int mem = 0; |
|
bool found_one = false; |
|
|
|
// if you understand the following algorithm you are worthy to look at the |
|
// kedit+ sources -- if not, go away ;-) |
|
|
|
|
|
while(find >=0 && find <= coltemp- 1 ){ |
|
find = linetext.indexOf('\t', find+start_of_line, Qt::CaseSensitive )-start_of_line; |
|
if( find >=0 && find <= coltemp - 1 ){ |
|
found_one = true; |
|
pos = pos + find - mem; |
|
pos = pos + 8 - pos % 8; |
|
mem = find; |
|
find ++; |
|
} |
|
} |
|
|
|
pos = pos + coltemp - mem; // add the number of characters behind the |
|
// last tab on the line. |
|
|
|
if (found_one){ |
|
pos = pos - 1; |
|
} |
|
|
|
col_pos = pos; |
|
} |
|
|
|
|
|
void KEdit::keyPressEvent ( QKeyEvent *e) |
|
{ |
|
// ignore Ctrl-Return so that KDialog can catch them |
|
if ( e->key() == Qt::Key_Return && e->modifiers() == Qt::ControlModifier ) { |
|
e->ignore(); |
|
return; |
|
} |
|
|
|
int key = e->key() | e->modifiers(); |
|
|
|
if ( key == Qt::CTRL+Qt::Key_K ){ |
|
|
|
int line = 0; |
|
int col = 0; |
|
QString killstring; |
|
|
|
if(!killing){ |
|
killbufferstring = ""; |
|
killtrue = false; |
|
lastwasanewline = false; |
|
} |
|
|
|
if(!atEnd()){ |
|
|
|
getCursorPosition(&line,&col); |
|
killstring = textLine(line); |
|
killstring = killstring.mid(col,killstring.length()); |
|
|
|
|
|
if(!killbufferstring.isEmpty() && !killtrue && !lastwasanewline){ |
|
killbufferstring += '\n'; |
|
} |
|
|
|
if( (killstring.length() == 0) && !killtrue){ |
|
killbufferstring += '\n'; |
|
lastwasanewline = true; |
|
} |
|
|
|
if(killstring.length() > 0){ |
|
|
|
killbufferstring += killstring; |
|
lastwasanewline = false; |
|
killtrue = true; |
|
|
|
}else{ |
|
|
|
lastwasanewline = false; |
|
killtrue = !killtrue; |
|
|
|
} |
|
|
|
}else{ |
|
|
|
if(killbufferstring.isEmpty() && !killtrue && !lastwasanewline){ |
|
killtrue = true; |
|
} |
|
|
|
} |
|
|
|
killing = true; |
|
|
|
Q3MultiLineEdit::keyPressEvent(e); |
|
setModified(true); |
|
return; |
|
} |
|
else if ( key == Qt::CTRL+Qt::Key_Y ){ |
|
|
|
int line = 0; |
|
int col = 0; |
|
|
|
getCursorPosition(&line,&col); |
|
|
|
QString tmpstring = killbufferstring; |
|
if(!killtrue) |
|
tmpstring += '\n'; |
|
|
|
insertAt(tmpstring,line,col); |
|
|
|
killing = false; |
|
setModified(true); |
|
return; |
|
} |
|
|
|
killing = false; |
|
|
|
if ( KStandardShortcut::copy().contains( key ) ) |
|
copy(); |
|
else if ( isReadOnly() ) |
|
Q3MultiLineEdit::keyPressEvent( e ); |
|
// If this is an unmodified printable key, send it directly to QMultiLineEdit. |
|
else if ( !(key & (Qt::CTRL | Qt::ALT)) && !e->text().isEmpty() && e->text().unicode()->isPrint() ) |
|
Q3MultiLineEdit::keyPressEvent( e ); |
|
else if ( KStandardShortcut::paste().contains( key ) ) { |
|
paste(); |
|
setModified(true); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::cut().contains( key ) ) { |
|
cut(); |
|
setModified(true); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::undo().contains( key ) ) { |
|
undo(); |
|
setModified(true); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::redo().contains( key ) ) { |
|
redo(); |
|
setModified(true); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::deleteWordBack().contains( key ) ) { |
|
moveCursor(MoveWordBackward, true); |
|
if (hasSelectedText()) |
|
del(); |
|
setModified(true); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::deleteWordForward().contains( key ) ) { |
|
moveCursor(MoveWordForward, true); |
|
if (hasSelectedText()) |
|
del(); |
|
setModified(true); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::backwardWord().contains( key ) ) { |
|
moveCursor(MoveWordBackward, false ); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::forwardWord().contains( key ) ) { |
|
moveCursor( MoveWordForward, false ); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::next().contains( key ) ) { |
|
moveCursor( MovePgDown, false ); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::prior().contains( key ) ) { |
|
moveCursor( MovePgUp, false ); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::home().contains( key ) ) { |
|
moveCursor( MoveHome, false ); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::end().contains( key ) ) { |
|
moveCursor( MoveEnd, false ); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::beginningOfLine().contains( key ) ) { |
|
moveCursor( MoveLineStart, false); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( KStandardShortcut::endOfLine().contains( key ) ) { |
|
moveCursor( MoveLineEnd, false); |
|
slotCursorPositionChanged(); |
|
} |
|
else if ( key == Qt::Key_Insert ) { |
|
if (d->overwriteEnabled) |
|
{ |
|
setOverwriteMode(!isOverwriteMode()); |
|
emit toggle_overwrite_signal(); |
|
} |
|
} |
|
else |
|
Q3MultiLineEdit::keyPressEvent(e); |
|
} |
|
|
|
void KEdit::installRBPopup(QMenu *p) { |
|
setContextMenuPolicy(Qt::ActionsContextMenu); |
|
addActions(p->actions()); |
|
} |
|
|
|
void KEdit::selectFont(){ |
|
QFont newFont = font(); |
|
KFontDialog::getFont(newFont); |
|
setFont(newFont); |
|
} |
|
|
|
void KEdit::doGotoLine() { |
|
|
|
if( !gotodialog ) |
|
gotodialog = new KEdGotoLine( parent, "gotodialog" ); |
|
|
|
clearFocus(); |
|
|
|
gotodialog->exec(); |
|
// this seems to be not necessary |
|
// gotodialog->setFocus(); |
|
if( gotodialog->result() != KEdGotoLine::Accepted) |
|
return; |
|
int target_line = gotodialog->getLineNumber()-1; |
|
if (wordWrap() == NoWrap) |
|
{ |
|
setCursorPosition( target_line, 0 ); |
|
setFocus(); |
|
return; |
|
} |
|
|
|
int max_parag = paragraphs(); |
|
|
|
int line = 0; |
|
int parag = -1; |
|
int lines_in_parag = 0; |
|
while ((++parag < max_parag) && (line + lines_in_parag < target_line)) |
|
{ |
|
line += lines_in_parag; |
|
lines_in_parag = linesOfParagraph(parag); |
|
} |
|
|
|
int col = 0; |
|
if (parag >= max_parag) |
|
{ |
|
target_line = line + lines_in_parag - 1; |
|
parag = max_parag-1; |
|
} |
|
|
|
while(1+line+lineOfChar(parag,col) < target_line) col++; |
|
setCursorPosition( parag, col ); |
|
setFocus(); |
|
} |
|
|
|
void KEdit::setOverwriteEnabled(bool b) |
|
{ |
|
d->overwriteEnabled = b; |
|
} |
|
|
|
// QWidget::create() turns off mouse-Tracking which would break auto-hiding |
|
void KEdit::create( WId id, bool initializeWindow, bool destroyOldWindow ) |
|
{ |
|
Q3MultiLineEdit::create( id, initializeWindow, destroyOldWindow ); |
|
KCursor::setAutoHideCursor( this, true ); |
|
} |
|
|
|
void KEdit::ensureCursorVisible() |
|
{ |
|
if (!d->autoUpdate) |
|
return; |
|
|
|
Q3MultiLineEdit::ensureCursorVisible(); |
|
} |
|
|
|
void KEdit::setCursor( const QCursor &c ) |
|
{ |
|
if (!d->autoUpdate) |
|
return; |
|
|
|
Q3MultiLineEdit::setCursor(c); |
|
} |
|
|
|
void KEdit::viewportPaintEvent( QPaintEvent*pe ) |
|
{ |
|
if (!d->autoUpdate) |
|
return; |
|
|
|
Q3MultiLineEdit::viewportPaintEvent(pe); |
|
} |
|
|
|
|
|
|