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.
719 lines
17 KiB
719 lines
17 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 <tqdragobject.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqtimer.h>
|
|
|
|
#include <kapplication.h>
|
|
#include <kcursor.h>
|
|
#include <kdebug.h>
|
|
#include <kcmenumngr.h>
|
|
#include <kfontdialog.h>
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <kstdaccel.h>
|
|
#include <kurldrag.h>
|
|
|
|
#include "keditcl.h"
|
|
#include "keditcl.moc"
|
|
|
|
class KEdit::KEditPrivate
|
|
{
|
|
public:
|
|
bool overwriteEnabled:1;
|
|
bool posDirty:1;
|
|
bool autoUpdate:1;
|
|
};
|
|
|
|
|
|
KEdit::KEdit(TQWidget *_parent, const char *name)
|
|
: TQMultiLineEdit(_parent, name)
|
|
{
|
|
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;
|
|
|
|
setAcceptDrops(true);
|
|
KCursor::setAutoHideCursor( this, true );
|
|
|
|
connect(this, TQT_SIGNAL(cursorPositionChanged(int,int)),
|
|
this, TQT_SLOT(slotCursorPositionChanged()));
|
|
}
|
|
|
|
|
|
KEdit::~KEdit()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void
|
|
KEdit::setAutoUpdate(bool b)
|
|
{
|
|
d->autoUpdate = b;
|
|
}
|
|
|
|
void
|
|
KEdit::insertText(TQTextStream *stream)
|
|
{
|
|
// setAutoUpdate(false);
|
|
int line, col;
|
|
getCursorPosition(&line, &col);
|
|
int saveline = line;
|
|
int savecol = col;
|
|
TQString textLine;
|
|
|
|
// MS: Patch by Martin Schenk <martin@schenk.com>
|
|
// MS: disable UNDO, or TQMultiLineEdit 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.
|
|
TQIODevice *dev=stream->device();
|
|
if (dev && dev->size()>(1024*1024)) {
|
|
while(1) {
|
|
int i;
|
|
textLine="";
|
|
for (i=0; i<5000; i++) {
|
|
TQString 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->read(); // 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 ....
|
|
|
|
// TQString 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();
|
|
TQString oldText = markedText();
|
|
TQString newText;
|
|
TQStringList lines = TQStringList::split('\n', oldText, true);
|
|
bool addSpace = false;
|
|
bool firstLine = true;
|
|
TQChar lastChar = oldText[oldText.length()-1];
|
|
TQChar firstChar = oldText[0];
|
|
for(TQStringList::Iterator it = lines.begin();
|
|
it != lines.end();)
|
|
{
|
|
TQString line = (*it).simplifyWhiteSpace();
|
|
if (line.isEmpty())
|
|
{
|
|
if (addSpace)
|
|
newText += TQString::fromLatin1("\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.remove(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 TQMultiLineEdit for this, so that we get nice undo
|
|
// behavior.
|
|
TQMultiLineEdit *we = new TQMultiLineEdit();
|
|
we->setWordWrap(FixedColumnWidth);
|
|
we->setWrapColumnOrWidth(78);
|
|
we->setText(newText);
|
|
newText = TQString::null;
|
|
for(int i = 0; i < we->numLines(); i++)
|
|
{
|
|
TQString 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(TQTextStream *stream)
|
|
{
|
|
saveText(stream, false);
|
|
}
|
|
|
|
void
|
|
KEdit::saveText(TQTextStream *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
|
|
{
|
|
TQString 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.
|
|
TQString 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.find('\t', find+start_of_line, true )-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 ( TQKeyEvent *e)
|
|
{
|
|
// ignore Ctrl-Return so that KDialogBase can catch them
|
|
if ( e->key() == Key_Return && e->state() == ControlButton ) {
|
|
e->ignore();
|
|
return;
|
|
}
|
|
|
|
KKey key(e);
|
|
int keyQt = key.keyCodeQt();
|
|
|
|
if ( keyQt == CTRL+Key_K ){
|
|
|
|
int line = 0;
|
|
int col = 0;
|
|
TQString 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;
|
|
|
|
TQMultiLineEdit::keyPressEvent(e);
|
|
setModified(true);
|
|
return;
|
|
}
|
|
else if ( keyQt == CTRL+Key_Y ){
|
|
|
|
int line = 0;
|
|
int col = 0;
|
|
|
|
getCursorPosition(&line,&col);
|
|
|
|
TQString tmpstring = killbufferstring;
|
|
if(!killtrue)
|
|
tmpstring += '\n';
|
|
|
|
insertAt(tmpstring,line,col);
|
|
|
|
killing = false;
|
|
setModified(true);
|
|
return;
|
|
}
|
|
|
|
killing = false;
|
|
|
|
if ( KStdAccel::copy().contains( key ) )
|
|
copy();
|
|
else if ( isReadOnly() )
|
|
TQMultiLineEdit::keyPressEvent( e );
|
|
// If this is an unmodified printable key, send it directly to TQMultiLineEdit.
|
|
else if ( !(key.keyCodeQt() & (CTRL | ALT)) && !e->text().isEmpty() && e->text().unicode()->isPrint() )
|
|
TQMultiLineEdit::keyPressEvent( e );
|
|
else if ( KStdAccel::paste().contains( key ) ) {
|
|
paste();
|
|
setModified(true);
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::cut().contains( key ) ) {
|
|
cut();
|
|
setModified(true);
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::undo().contains( key ) ) {
|
|
undo();
|
|
setModified(true);
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::redo().contains( key ) ) {
|
|
redo();
|
|
setModified(true);
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::deleteWordBack().contains( key ) ) {
|
|
moveCursor(MoveWordBackward, true);
|
|
if (hasSelectedText())
|
|
del();
|
|
setModified(true);
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::deleteWordForward().contains( key ) ) {
|
|
moveCursor(MoveWordForward, true);
|
|
if (hasSelectedText())
|
|
del();
|
|
setModified(true);
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::backwardWord().contains( key ) ) {
|
|
CursorAction action = MoveWordBackward;
|
|
int para, index;
|
|
getCursorPosition( ¶, & index );
|
|
if (text(para).isRightToLeft())
|
|
action = MoveWordForward;
|
|
moveCursor(action, false );
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::forwardWord().contains( key ) ) {
|
|
CursorAction action = MoveWordForward;
|
|
int para, index;
|
|
getCursorPosition( ¶, & index );
|
|
if (text(para).isRightToLeft())
|
|
action = MoveWordBackward;
|
|
moveCursor( action, false );
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::next().contains( key ) ) {
|
|
moveCursor( MovePgDown, false );
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::prior().contains( key ) ) {
|
|
moveCursor( MovePgUp, false );
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::home().contains( key ) ) {
|
|
moveCursor( MoveHome, false );
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::end().contains( key ) ) {
|
|
moveCursor( MoveEnd, false );
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::beginningOfLine().contains( key ) ) {
|
|
moveCursor( MoveLineStart, false);
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( KStdAccel::endOfLine().contains( key ) ) {
|
|
moveCursor( MoveLineEnd, false);
|
|
slotCursorPositionChanged();
|
|
}
|
|
else if ( key == Key_Insert ) {
|
|
if (d->overwriteEnabled)
|
|
{
|
|
this->setOverwriteMode(!this->isOverwriteMode());
|
|
emit toggle_overwrite_signal();
|
|
}
|
|
}
|
|
else
|
|
TQMultiLineEdit::keyPressEvent(e);
|
|
}
|
|
|
|
void KEdit::installRBPopup(TQPopupMenu *p) {
|
|
KContextMenuManager::insert( this, p );
|
|
}
|
|
|
|
void KEdit::selectFont(){
|
|
|
|
TQFont font = this->font();
|
|
KFontDialog::getFont(font);
|
|
this->setFont(font);
|
|
|
|
}
|
|
|
|
void KEdit::doGotoLine() {
|
|
|
|
if( !gotodialog )
|
|
gotodialog = new KEdGotoLine( parent, "gotodialog" );
|
|
|
|
this->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::dragMoveEvent(TQDragMoveEvent* e) {
|
|
|
|
if(KURLDrag::canDecode(e))
|
|
e->accept();
|
|
else if(TQTextDrag::canDecode(e))
|
|
TQMultiLineEdit::dragMoveEvent(e);
|
|
}
|
|
|
|
void KEdit::contentsDragMoveEvent(TQDragMoveEvent* e) {
|
|
|
|
if(KURLDrag::canDecode(e))
|
|
e->accept();
|
|
else if(TQTextDrag::canDecode(e))
|
|
TQMultiLineEdit::contentsDragMoveEvent(e);
|
|
}
|
|
|
|
void KEdit::dragEnterEvent(TQDragEnterEvent* e) {
|
|
|
|
kdDebug() << "KEdit::dragEnterEvent()" << endl;
|
|
e->accept(KURLDrag::canDecode(e) || TQTextDrag::canDecode(e));
|
|
}
|
|
|
|
void KEdit::contentsDragEnterEvent(TQDragEnterEvent* e) {
|
|
|
|
kdDebug() << "KEdit::contentsDragEnterEvent()" << endl;
|
|
e->accept(KURLDrag::canDecode(e) || TQTextDrag::canDecode(e));
|
|
}
|
|
|
|
|
|
void KEdit::dropEvent(TQDropEvent* e) {
|
|
|
|
kdDebug() << "KEdit::dropEvent()" << endl;
|
|
|
|
if(KURLDrag::canDecode(e)) {
|
|
emit gotUrlDrop(e);
|
|
}
|
|
else if(TQTextDrag::canDecode(e))
|
|
TQMultiLineEdit::dropEvent(e);
|
|
}
|
|
|
|
void KEdit::contentsDropEvent(TQDropEvent* e) {
|
|
|
|
kdDebug() << "KEdit::contentsDropEvent()" << endl;
|
|
|
|
if(KURLDrag::canDecode(e)) {
|
|
emit gotUrlDrop(e);
|
|
}
|
|
else if(TQTextDrag::canDecode(e))
|
|
TQMultiLineEdit::contentsDropEvent(e);
|
|
}
|
|
|
|
void KEdit::setOverwriteEnabled(bool b)
|
|
{
|
|
d->overwriteEnabled = b;
|
|
}
|
|
|
|
// TQWidget::create() turns off mouse-Tracking which would break auto-hiding
|
|
void KEdit::create( WId id, bool initializeWindow, bool destroyOldWindow )
|
|
{
|
|
TQMultiLineEdit::create( id, initializeWindow, destroyOldWindow );
|
|
KCursor::setAutoHideCursor( this, true );
|
|
}
|
|
|
|
void KEdit::ensureCursorVisible()
|
|
{
|
|
if (!d->autoUpdate)
|
|
return;
|
|
|
|
TQMultiLineEdit::ensureCursorVisible();
|
|
}
|
|
|
|
void KEdit::setCursor( const TQCursor &c )
|
|
{
|
|
if (!d->autoUpdate)
|
|
return;
|
|
|
|
TQMultiLineEdit::setCursor(c);
|
|
}
|
|
|
|
void KEdit::viewportPaintEvent( TQPaintEvent*pe )
|
|
{
|
|
if (!d->autoUpdate)
|
|
return;
|
|
|
|
TQMultiLineEdit::viewportPaintEvent(pe);
|
|
}
|
|
|
|
|
|
void KEdGotoLine::virtual_hook( int id, void* data )
|
|
{ KDialogBase::virtual_hook( id, data ); }
|
|
|
|
void KEdFind::virtual_hook( int id, void* data )
|
|
{ KDialogBase::virtual_hook( id, data ); }
|
|
|
|
void KEdReplace::virtual_hook( int id, void* data )
|
|
{ KDialogBase::virtual_hook( id, data ); }
|
|
|
|
void KEdit::virtual_hook( int, void* )
|
|
{ /*BASE::virtual_hook( id, data );*/ }
|
|
|