diff --git a/KEdit.cpp b/KEdit.cpp new file mode 100644 index 000000000..e3ca1e22a --- /dev/null +++ b/KEdit.cpp @@ -0,0 +1,1807 @@ + /* + + $Id$ + + kedit, a simple text editor for the KDE project + + Requires the Qt widget libraries, available at no cost at + http://www.troll.no + + Copyright (C) 1997 Bernd Johannes Wuebben + wuebben@math.cornell.edu + + parts: + Alexander Sanda + + 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. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + KEdit, simple editor class, hacked version of the original by + + + */ + +#include "KEdit.h" +#include "KEdit.moc" + + +KEdit::KEdit(KApplication *a, QWidget *parent, const char *name, + const char *fname) : QMultiLineEdit(parent, name){ + + + mykapp = a; + filename = fname; + filename.detach(); + + modified = FALSE; + + // set some defaults + + line_pos = col_pos = 0; + fill_column_is_set = TRUE; + word_wrap_is_set = TRUE; + fill_column_value = 79; + autoIndentMode = false; + + make_backup_copies = TRUE; + + installEventFilter( this ); + + srchdialog = NULL; + replace_dialog= NULL; + + connect(this, SIGNAL(textChanged()), this, SLOT(setModified())); + setContextSens(); +} + + + +KEdit::~KEdit(){ + +} + + +int KEdit::currentLine(){ + + computePosition(); + return line_pos; + +}; + +int KEdit::currentColumn(){ + + computePosition(); + return col_pos; + +} + + +bool KEdit::WordWrap(){ + + return word_wrap_is_set; + +} + +void KEdit::setWordWrap(bool flag ){ + + word_wrap_is_set = flag; +} + +bool KEdit::FillColumnMode(){ + + return fill_column_is_set; +} + +void KEdit::setFillColumnMode(int line){ + + if (line <= 0) { + fill_column_is_set = FALSE; + fill_column_value = 0; + } + else{ + fill_column_is_set = TRUE; + fill_column_value = line; + } + +} + + +int KEdit::loadFile(QString name, int mode){ + + int fdesc; + struct stat s; + char *mb_caption = "Load file:"; + char *addr; + QFileInfo info(name); + + if(!info.exists()){ + QMessageBox::message("Sorry","The specified File does not exist","OK"); +// TODO: finer error control + return KEDIT_RETRY; + } + + if(!info.isReadable()){ + QMessageBox::message("Sorry","You do not have read permission to this file.","OK"); +// TODO: finer error control + return KEDIT_RETRY; + } + + + fdesc = open(name, O_RDONLY); + + if(fdesc == -1) { + switch(errno) { + case EACCES: + QMessageBox::message("Sorry", + "You have do not have Permission to \n"\ + "read this Document", "Ok"); +// TODO: finer error control + return KEDIT_OS_ERROR; + + default: + QMessageBox::message(mb_caption, + "An Error occured while trying to open this Document", + "Ok"); +// TODO: finer error control + return KEDIT_OS_ERROR; + } + } + + emit loading(); + mykapp->processEvents(); + + fstat(fdesc, &s); + addr = (char *)mmap(0, s.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fdesc, 0); + + setAutoUpdate(FALSE); + + disconnect(this, SIGNAL(textChanged()), this, SLOT(setModified())); + +// The following is a horrible hack that we need to put up with until +// Qt 1.3 comes along. The problem is that QMultiLineEdit::setText(char*) +// is O(n^2) which makes loading larger files very slow. We resort here to +// a workaroud which load the file line by line. I use a memmap construction +// but probably a QTextStream is just about as fast. + + if(mode & OPEN_INSERT) { + + register long int i; + char *beginning_of_line; + beginning_of_line = addr; + int line, col; + + char c = '\n'; + + if(s.st_size != 0){ + c = *(addr + s.st_size - 1 ); // save the last character of the file + } + + // this boolean will indicate whether we already have inserted the first line. + bool past_first_line = false; + + getCursorPosition(&line,&col); + + // let's save the position of the cursor + int save_line = line; + int save_col = col; + + for(i = 0; i < s.st_size; i++){ + + if( *(addr + i) == '\n' ){ + + *(addr + i) = '\0'; + + if(!past_first_line){ + QString string; + string = beginning_of_line; + string += '\n'; + insertAt(string,line,col); + past_first_line = true; + } + else{ + insertLine(beginning_of_line,line); + } + + line ++; + beginning_of_line = addr + i + 1; + + } + } + + // What if the last char of the file wasn't a newline? + // In this case we have to manually insert the last "couple" of characters. + // This routine could be avoided if I knew for sure that I could + // memmap a file into a block of memory larger than the file size. + // In that case I would simply put a zero after the last char in the file. + // and the above would go through almost unmodified. Well, I don't, so + // here we go: + + if( c != '\n'){ // we're in here if s.st_size != 0 and last char != '\n' + + char* buf = (char*)malloc(addr + i - 1 - beginning_of_line + 2); + strncpy(buf, beginning_of_line, addr + i - 1 - beginning_of_line +1); + buf[ addr + i - 1 - beginning_of_line + 2 ] = '\0'; + append(buf); + + free(buf); + + } + // restore the initial Curosor Position + setCursorPosition(save_line,save_col); + + } + else{ // Not inserting but loading a completely new file. + + register long int i; + char *beginning_of_line; + beginning_of_line = addr; + + // eradicate the old text. + this->clear(); + char c = '\n'; + + if(s.st_size != 0){ + c = *(addr + s.st_size - 1 ); // save the last character of the file + } + + for(i = 0; i < s.st_size; i++){ + if( *(addr + i) == '\n' ){ + *(addr + i) = '\0'; + append(beginning_of_line); + beginning_of_line = addr + i + 1; + } + } + + // Same consideration as above: + + if( c != '\n'){ // we're in here if s.st_size != 0 and last char != '\n' + + char* buf = (char*)malloc(addr + i - 1 - beginning_of_line + 2); + strncpy(buf, beginning_of_line, addr + i - 1 - beginning_of_line +1); + buf[ addr + i - 1 - beginning_of_line + 2 ] = '\0'; + append(buf); + + free(buf); + + } + } + + setAutoUpdate(TRUE); + repaint(); + + connect(this, SIGNAL(textChanged()), this, SLOT(setModified())); + + munmap(addr, s.st_size); + + if ( mode == OPEN_INSERT) + toggleModified(TRUE); + else + toggleModified(FALSE); + + + if(!(mode == OPEN_INSERT)){ + filename = name; + filename.detach(); + } + + if( mode == OPEN_READONLY) + this->setReadOnly(TRUE); + else + this->setReadOnly(FALSE); + + + emit(fileChanged()); + setFocus(); + + return KEDIT_OK; +} + + +int KEdit::insertFile(){ + + QFileDialog *box; + QString file_to_insert; + + box = new QFileDialog( this, "fbox", TRUE); + + box->setCaption("Select Document to Insert"); + + box->show(); + + if (!box->result()) { + delete box; + return KEDIT_USER_CANCEL; + } + + if(box->selectedFile().isEmpty()) { /* no selection */ + delete box; + return KEDIT_USER_CANCEL; + } + + file_to_insert = box->selectedFile(); + file_to_insert.detach(); + + delete box; + + + int result = loadFile(file_to_insert, OPEN_INSERT); + + if (result == KEDIT_OK ) + setModified(); + + return result; + + +} + +int KEdit::openFile(int mode) +{ + QString fname; + QFileDialog *box; + + + if( isModified() ) { + if((QMessageBox::query("Message", + "The current Document has been modified.\n"\ + "Would you like to save it?"))) { + + if (doSave() != KEDIT_OK){ + + QMessageBox::message("Sorry", "Could not Save the Document", "OK"); + return KEDIT_OS_ERROR; + + } + } + } + + box = new QFileDialog( this, "fbox", TRUE); + + box->setCaption("Select Document to Open"); + + box->show(); + + if (!box->result()) /* cancelled */ + return KEDIT_USER_CANCEL; + if(box->selectedFile().isEmpty()) { /* no selection */ + return KEDIT_USER_CANCEL; + } + + fname = box->selectedFile(); + delete box; + + int result = loadFile(fname, mode); + + if ( result == KEDIT_OK ) + toggleModified(FALSE); + + return result; + + +} + +int KEdit::newFile(){ + + + if( isModified() ) { + if((QMessageBox::query("Message", + "The current Document has been modified.\n"\ + "Would you like to save it?"))) { + + if (doSave() != KEDIT_OK){ + + QMessageBox::message("Sorry", "Could not Save the Document", "OK"); + return KEDIT_OS_ERROR; + + } + } + } + + this->clear(); + toggleModified(FALSE); + + setFocus(); + + filename = "Untitled"; + + computePosition(); + emit(fileChanged()); + + return KEDIT_OK; + + +} + + + +void KEdit::computePosition(){ + + int line, col, coltemp; + + getCursorPosition(&line,&col); + QString linetext = textLine(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: + + coltemp = col; + 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, TRUE ); + 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; + } + + line_pos = line; + col_pos = pos; + +} + + +void KEdit::keyPressEvent ( QKeyEvent *e){ + + QString* pstring; + + if (e->key() == Key_Tab){ + if (isReadOnly()) + return; + QMultiLineEdit::insertChar((char)'\t'); + setModified(); + emit CursorPositionChanged(); + return; + } + + if (e->key() == Key_Insert){ + this->setOverwriteMode(!this->isOverwriteMode()); + emit toggle_overwrite_signal(); + return; + } + + //printf("fill %d word %d value %d\n", fill_column_is_set, + // word_wrap_is_set,fill_column_value); + + if(fill_column_is_set && word_wrap_is_set ){ + + // word break algorithm + if(isprint(e->ascii())){ + + // printf("col_pos %d\n",col_pos); + if( col_pos > fill_column_value - 1){ + + if (e->ascii() == 32 ){ // a space we can just break here + mynewLine(); + // setModified(); + emit CursorPositionChanged(); + return; + } + + pstring = getString(line_pos); + + // find a space to break at + int space_pos = pstring->findRev(" ", -1,TRUE); + + if( space_pos == -1 ){ + + // no space to be found on line, just break, what else could we do? + mynewLine(); + computePosition(); + + } + else{ + + // Can't use insertAt('\n'...) due to a bug in Qt 1.2, and must resort + // to the following work around: + + // go back to the space + + focusOutEvent(&QFocusEvent(Event_FocusOut)); + + for(uint i = 0; i < (pstring->length() - space_pos -1 ); i++){ + cursorLeft(); + } + + // insert a newline there + + mynewLine(); + end(FALSE); + + focusOutEvent(&QFocusEvent(Event_FocusIn)); + + computePosition(); + } + } + + QMultiLineEdit::keyPressEvent(e); + // setModified(); + emit CursorPositionChanged(); + return; + } + else{ // not isprint that is some control character or some such + + if(e->key() == Key_Return || e->key() == Key_Enter){ + + mynewLine(); + //setModified(); + emit CursorPositionChanged(); + return; + } + + QMultiLineEdit::keyPressEvent(e); + // setModified(); + emit CursorPositionChanged(); + return; + + } + } // end do_wordbreak && fillcolumn_set + + + // fillcolumn but no wordbreak + + if (fill_column_is_set){ + + if(e->key() == Key_Return || e->key() == Key_Enter){ + + mynewLine(); + // setModified(); + emit CursorPositionChanged(); + return; + + } + + if(isprint(e->ascii())){ + + if( col_pos > fill_column_value - 1){ + mynewLine(); + // setModified(); + } + + } + + QMultiLineEdit::keyPressEvent(e); + //setModified(); + emit CursorPositionChanged(); + return; + + } + + // default action + if(e->key() == Key_Return || e->key() == Key_Enter){ + + mynewLine(); + //setModified(); + emit CursorPositionChanged(); + return; + + } + + QMultiLineEdit::keyPressEvent(e); + //setModified(); + emit CursorPositionChanged(); + +} + + +void KEdit::mynewLine(){ + + if (isReadOnly()) + return; + + setModified(); + + if(!autoIndentMode){ // if not indent mode + newLine(); + return; + } + + int line,col; + bool found_one = false; + + getCursorPosition(&line,&col); + + QString string, string2; + + while(line >= 0){ + + string = textLine(line); + string2 = string.stripWhiteSpace(); + + if(!string2.isEmpty()){ + string = prefixString(string); + found_one = TRUE; + break; + } + + line --; + } + + // string will now contain those whitespace characters that I need to insert + // on the next line. + + if(found_one){ + + // don't ask my why I programmed it this way. I am quite sick of the Qt 1.2 + // MultiLineWidget -- It is anoyingly buggy. + // I have to put in obscure workarounds all over the place. + + focusOutEvent(&QFocusEvent(Event_FocusOut)); + newLine(); + + for(uint i = 0; i < string.length();i++){ + insertChar(string.data()[i]); + } + + // this f***king doesn't work. + // insertAt(string.data(),line + 1,0); + + focusInEvent(&QFocusEvent(Event_FocusIn)); + + } + else{ + newLine(); + } +} + +void KEdit::setAutoIndentMode(bool mode){ + + autoIndentMode = mode; + +} + + +QString KEdit::prefixString(QString string){ + + // This routine return the whitespace before the first none white space + // character in string. This is used in mynewLine() for indent mode. + // It is assumed that string contains at least one non whitespace character + // ie \n \r \t \v \f and space + + // printf(":%s\n",string.data()); + + int size = string.size(); + char* buffer = (char*) malloc(size + 1); + strncpy (buffer, string.data(),size - 1); + buffer[size] = '\0'; + + int i; + for (i = 0 ; i < size; i++){ + if(!isspace(buffer[i])) + break; + } + + buffer[i] = '\0'; + + QString returnstring = buffer; + + free(buffer); + + // printf(":%s:\n",returnstring.data()); + return returnstring; + +} + +void KEdit::mousePressEvent (QMouseEvent* e){ + + + QMultiLineEdit::mousePressEvent(e); + emit CursorPositionChanged(); + +} + +void KEdit::mouseMoveEvent (QMouseEvent* e){ + + QMultiLineEdit::mouseMoveEvent(e); + emit CursorPositionChanged(); + + +} + + +void KEdit::installRBPopup(QPopupMenu* p){ + + rb_popup = p; + +} + +void KEdit::mouseReleaseEvent (QMouseEvent* e){ + + + QMultiLineEdit::mouseReleaseEvent(e); + emit CursorPositionChanged(); + +} + + +int KEdit::saveFile(){ + + + if(!modified) { + emit saving(); + mykapp->processEvents(); + return KEDIT_OK; + } + + QFile file(filename); + QString backup_filename; + + if(file.exists()){ + + backup_filename = filename; + backup_filename.detach(); + backup_filename += '~'; + + rename(filename.data(),backup_filename.data()); + } + + if( !file.open( IO_WriteOnly | IO_Truncate )) { + rename(backup_filename.data(),filename.data()); + QMessageBox::message("Sorry","Could not save the document\n","OK"); + return KEDIT_OS_ERROR; + } + + emit saving(); + mykapp->processEvents(); + + QTextStream t(&file); + + int line_count = numLines(); + + for(int i = 0 ; i < line_count ; i++){ + t << textLine(i) << '\n'; + } + + modified = FALSE; + file.close(); + + return KEDIT_OK; + +} + +int KEdit::saveAs() +{ + + QFileDialog *box; + QFileInfo info; + QString tmpfilename; + int result; + + box = new QFileDialog( this, "box", TRUE); + box->setCaption("Save Document As"); + +try_again: + + box->show(); + + if (!box->result()) + { + delete box; + return KEDIT_USER_CANCEL; + } + + if(box->selectedFile().isEmpty()){ + delete box; + return KEDIT_USER_CANCEL; + } + + info.setFile(box->selectedFile()); + + if(info.exists()){ + if(!(QMessageBox::query("Warning:", + "A Document with this Name exists already\n"\ + "Do you want to overwrite it ?"))) + goto try_again; + + } + + + tmpfilename = filename; + + filename = box->selectedFile(); + + // we need this for saveFile(); + modified = TRUE; + + delete box; + + result = saveFile(); + + if( result != KEDIT_OK) + filename = tmpfilename; // revert filename + + return result; + +} + + + +int KEdit::doSave() +{ + + + int result = 0; + + if(filename == "Untitled") { + result = saveAs(); + + if(result == KEDIT_OK) + setCaption(filename); + + return result; + } + + QFileInfo info(filename); + if(!info.isWritable()){ + QMessageBox::message("Sorry:", + "You do not have write permission to this file.\n","OK"); + return KEDIT_NOPERMISSIONS; + } + + + result = saveFile(); + return result; + +} + +int KEdit::doSave( const char *_name ){ + + QString temp = filename; + filename = _name; + filename.detach(); + + int result = saveFile(); + + filename = temp; + filename.detach(); + return result; +} + + +void KEdit::setName( const char *_name ){ + + filename = _name; + filename.detach(); +} + + +QString KEdit::getName(){ + + return filename; +} + + +void KEdit::saveBackupCopy(bool par){ + + make_backup_copies = par; + +} +void KEdit::selectFont(){ + + QFont font = this->font(); + KFontDialog::getFont(font); + this->setFont(font); + +} + +void KEdit::setModified(){ + + modified = TRUE; + + +} + +void KEdit::toggleModified( bool _mod ){ + + modified = _mod; +} + + + +bool KEdit::isModified(){ + + return modified; +} + + + +void KEdit::setContextSens(){ + +} + + +bool KEdit::eventFilter(QObject *o, QEvent *ev){ + + static QPoint tmp_point; + + (void) o; + + if(ev->type() != Event_MouseButtonPress) + return FALSE; + + QMouseEvent *e = (QMouseEvent *)ev; + + if(e->button() != RightButton) + return FALSE; + + tmp_point = QCursor::pos(); + + if(rb_popup) + rb_popup->popup(tmp_point); + + return TRUE; + +} + + +QString KEdit::markedText(){ + + return QMultiLineEdit::markedText(); + +} + +void KEdit::doGotoLine() { + + if( !gotodialog ) + gotodialog = new KEdGotoLine( this, "gotodialog" ); + + this->clearFocus(); + + gotodialog->show(); + // this seems to be not necessary + // gotodialog->setFocus(); + if( gotodialog->result() ) { + setCursorPosition( gotodialog->getLineNumber() , 0, FALSE ); + emit CursorPositionChanged(); + setFocus(); + } +} + +////////////////////////////////////////////////////////////////////////// +// +// Find Methods +// + +void KEdit::Search(){ + + + if(!srchdialog){ + srchdialog = new KEdSrch(this, "searchdialog"); + connect(srchdialog,SIGNAL(search_signal()),this,SLOT(search_slot())); + connect(srchdialog,SIGNAL(search_done_signal()),this,SLOT(searchdone_slot())); + } + + // If we already searched / replaced something before make sure it shows + // up in the find dialog line-edit. + + QString string; + string = srchdialog->getText(); + if(string.isEmpty()) + srchdialog->setText(pattern); + + this->deselect(); + last_search = NONE; + + this->clearFocus(); + srchdialog->show(); + srchdialog->result(); +} + + +void KEdit::search_slot(){ + + int line, col; + + if (!srchdialog) + return; + + QString to_find_string = srchdialog->getText(); + getCursorPosition(&line,&col); + + // srchdialog->get_direction() is true if searching backward + + if (last_search != NONE && srchdialog->get_direction()){ + col = col - pattern.length() - 1 ; + } + +again: + int result = doSearch(to_find_string, srchdialog->case_sensitive(), + FALSE, (!srchdialog->get_direction()),line,col); + + if(result == 0){ + if(!srchdialog->get_direction()){ // forward search + + int query = QMessageBox::query("Find", "End of document reached.\n"\ + "Continue from the beginning?", "Yes","No"); + if (query){ + line = 0; + col = 0; + goto again; + } + } + else{ //backward search + + int query = QMessageBox::query("Find", "Beginning of document reached.\n"\ + "Continue from the end?", "Yes","No"); + if (query){ + QString string = textLine( numLines() - 1 ); + line = numLines() - 1; + col = string.length(); + last_search = BACKWARD; + goto again; + } + } + } + else{ + emit CursorPositionChanged(); + } +} + + + +void KEdit::searchdone_slot(){ + + if (!srchdialog) + return; + + srchdialog->hide(); + this->setFocus(); + last_search = NONE; + +} + +int KEdit::doSearch(QString s_pattern, bool case_sensitive, + bool wildcard, bool forward, int line, int col){ + + (void) wildcard; // reserved for possible extension to regex + + + int i, length; + int pos = -1; + + if(forward){ + + QString string; + + for(i = line; i < numLines(); i++) { + + string = textLine(i); + + pos = string.find(s_pattern, i == line ? col : 0, case_sensitive); + + if( pos != -1){ + + length = s_pattern.length(); + + setCursorPosition(i,pos,FALSE); + + for(int l = 0 ; l < length; l++){ + cursorRight(TRUE); + } + + setCursorPosition( i , pos + length, TRUE ); + pattern = s_pattern; + last_search = FORWARD; + + return 1; + } + } + } + else{ // searching backwards + + QString string; + + for(i = line; i >= 0; i--) { + + string = textLine(i); + int line_length = string.length(); + + pos = string.findRev(s_pattern, line == i ? col : line_length , case_sensitive); + + if (pos != -1){ + + length = s_pattern.length(); + + if( ! (line == i && pos > col ) ){ + + setCursorPosition(i ,pos ,FALSE ); + + for(int l = 0 ; l < length; l++){ + cursorRight(TRUE); + } + + setCursorPosition(i ,pos + length ,TRUE ); + pattern = s_pattern; + last_search = BACKWARD; + return 1; + + } + } + + } + } + + return 0; + +} + + + +int KEdit::repeatSearch() { + + if(!srchdialog) + return 0; + + + if(pattern.isEmpty()) // there wasn't a previous search + return 0; + + search_slot(); + + this->setFocus(); + return 1; + +} + + +////////////////////////////////////////////////////////////////////////// +// +// Replace Methods +// + + +void KEdit::Replace(){ + + + if(!replace_dialog){ + + replace_dialog = new KEdReplace(this, "replace_dialog"); + connect(replace_dialog,SIGNAL(find_signal()),this,SLOT(replace_search_slot())); + connect(replace_dialog,SIGNAL(replace_signal()),this,SLOT(replace_slot())); + connect(replace_dialog,SIGNAL(replace_all_signal()),this,SLOT(replace_all_slot())); + connect(replace_dialog,SIGNAL(replace_done_signal()),this,SLOT(replacedone_slot())); + + } + + QString string = replace_dialog->getText(); + + if(string.isEmpty()) + replace_dialog->setText(pattern); + + + this->deselect(); + last_replace = NONE; + + this->clearFocus(); + // replace_dialog->setFocus(); + // replace_dialog->exec(); + replace_dialog->show(); + replace_dialog->result(); +} + + +void KEdit::replace_slot(){ + + if (!replace_dialog) + return; + + if(!can_replace){ + QApplication::beep(); + return; + } + + int line,col, length; + + QString string = replace_dialog->getReplaceText(); + length = string.length(); + + this->cut(); + + getCursorPosition(&line,&col); + + insertAt(string,line,col); + setModified(); + can_replace = FALSE; + + setCursorPosition(line,col); + for( int k = 0; k < length; k++){ + cursorRight(TRUE); + } + +} + +void KEdit::replace_all_slot(){ + + if (!replace_dialog) + return; + + QString to_find_string = replace_dialog->getText(); + getCursorPosition(&replace_all_line,&replace_all_col); + + // replace_dialog->get_direction() is true if searching backward + + if (last_replace != NONE && replace_dialog->get_direction()){ + replace_all_col = replace_all_col - pattern.length() - 1 ; + } + + deselect(); + +again: + + setAutoUpdate(FALSE); + int result = 1; + + while(result){ + + result = doReplace(to_find_string, replace_dialog->case_sensitive(), + FALSE, (!replace_dialog->get_direction()), + replace_all_line,replace_all_col,TRUE); + + } + + setAutoUpdate(TRUE); + update(); + + if(!replace_dialog->get_direction()){ // forward search + + int query = QMessageBox::query("Find", "End of document reached.\n"\ + "Continue from the beginning?", "Yes","No"); + if (query){ + replace_all_line = 0; + replace_all_col = 0; + goto again; + } + } + else{ //backward search + + int query = QMessageBox::query("Find", "Beginning of document reached.\n"\ + "Continue from the end?", "Yes","No"); + if (query){ + QString string = textLine( numLines() - 1 ); + replace_all_line = numLines() - 1; + replace_all_col = string.length(); + last_replace = BACKWARD; + goto again; + } + } + + emit CursorPositionChanged(); + +} + + +void KEdit::replace_search_slot(){ + + int line, col; + + if (!replace_dialog) + return; + + QString to_find_string = replace_dialog->getText(); + getCursorPosition(&line,&col); + + // replace_dialog->get_direction() is true if searching backward + + //printf("col %d length %d\n",col, pattern.length()); + + if (last_replace != NONE && replace_dialog->get_direction()){ + col = col - pattern.length() -1; + if (col < 0 ) { + if(line !=0){ + col = strlen(textLine(line - 1)); + line --; + } + else{ + + int query = QMessageBox::query("Find", "Beginning of document reached.\n"\ + "Continue from the end?", "Yes","No"); + + if (query){ + QString string = textLine( numLines() - 1 ); + line = numLines() - 1; + col = string.length(); + last_replace = BACKWARD; + } + } + } + } + +again: + + // printf("Col %d \n",col); + + int result = doReplace(to_find_string, replace_dialog->case_sensitive(), + FALSE, (!replace_dialog->get_direction()), line, col, FALSE ); + + if(result == 0){ + if(!replace_dialog->get_direction()){ // forward search + + int query = QMessageBox::query("Find", "End of document reached.\n"\ + "Continue from the beginning?", "Yes","No"); + if (query){ + line = 0; + col = 0; + goto again; + } + } + else{ //backward search + + int query = QMessageBox::query("Find", "Beginning of document reached.\n"\ + "Continue from the end?", "Yes","No"); + if (query){ + QString string = textLine( numLines() - 1 ); + line = numLines() - 1; + col = string.length(); + last_replace = BACKWARD; + goto again; + } + } + } + else{ + + emit CursorPositionChanged(); + } +} + + + +void KEdit::replacedone_slot(){ + + if (!replace_dialog) + return; + + replace_dialog->hide(); + // replace_dialog->clearFocus(); + + this->setFocus(); + + last_replace = NONE; + can_replace = FALSE; + +} + + + +int KEdit::doReplace(QString s_pattern, bool case_sensitive, + bool wildcard, bool forward, int line, int col, bool replace_all){ + + + (void) wildcard; // reserved for possible extension to regex + + int line_counter, length; + int pos = -1; + + QString string; + QString stringnew; + QString replacement; + + replacement = replace_dialog->getReplaceText(); + line_counter = line; + replace_all_col = col; + + if(forward){ + + int num_lines = numLines(); + + while (line_counter < num_lines){ + + string = ""; + string = textLine(line_counter); + + if (replace_all){ + pos = string.find(s_pattern, replace_all_col, case_sensitive); + } + else{ + pos = string.find(s_pattern, line_counter == line ? col : 0, case_sensitive); + } + + if (pos == -1 ){ + line_counter ++; + replace_all_col = 0; + replace_all_line = line_counter; + } + + if( pos != -1){ + + length = s_pattern.length(); + + if(replace_all){ // automatic + + stringnew = string.copy(); + stringnew.replace(pos,length,replacement); + + removeLine(line_counter); + insertLine(stringnew.data(),line_counter); + + replace_all_col = replace_all_col + replacement.length(); + replace_all_line = line_counter; + + setModified(); + } + else{ // interactive + + setCursorPosition( line_counter , pos, FALSE ); + + for(int l = 0 ; l < length; l++){ + cursorRight(TRUE); + } + + setCursorPosition( line_counter , pos + length, TRUE ); + pattern = s_pattern; + last_replace = FORWARD; + can_replace = TRUE; + + return 1; + + } + + } + } + } + else{ // searching backwards + + while(line_counter >= 0){ + + string = ""; + string = textLine(line_counter); + + int line_length = string.length(); + + if( replace_all ){ + pos = string.findRev(s_pattern, replace_all_col , case_sensitive); + } + else{ + pos = string.findRev(s_pattern, + line == line_counter ? col : line_length , case_sensitive); + } + + if (pos == -1 ){ + line_counter --; + + if(line_counter >= 0){ + string = ""; + string = textLine(line_counter); + replace_all_col = string.length(); + + } + replace_all_line = line_counter; + } + + + if (pos != -1){ + length = s_pattern.length(); + + if(replace_all){ // automatic + + stringnew = string.copy(); + stringnew.replace(pos,length,replacement); + + removeLine(line_counter); + insertLine(stringnew.data(),line_counter); + + replace_all_col = replace_all_col - replacement.length(); + replace_all_line = line_counter; + + setModified(); + + } + else{ // interactive + + // printf("line_counter %d pos %d col %d\n",line_counter, pos,col); + if( ! (line == line_counter && pos > col ) ){ + + setCursorPosition(line_counter ,pos ,FALSE ); + + for(int l = 0 ; l < length; l++){ + cursorRight(TRUE); + } + + setCursorPosition(line_counter ,pos + length ,TRUE ); + pattern = s_pattern; + + last_replace = BACKWARD; + can_replace = TRUE; + + return 1; + } + } + } + } + } + + return 0; + +} + + + + + +//////////////////////////////////////////////////////////////////// +// +// Find Dialog +// + + +KEdSrch::KEdSrch(QWidget *parent, const char *name) + : QDialog(parent, name,TRUE){ + + this->setFocusPolicy(QWidget::StrongFocus); + frame1 = new QGroupBox("Find", this, "frame1"); + + value = new QLineEdit( this, "value"); + value->setFocus(); + connect(value, SIGNAL(returnPressed()), this, SLOT(ok_slot())); + + sensitive = new QCheckBox("Case Sensitive", this, "case"); + direction = new QCheckBox("Find Backwards", this, "direction"); + + ok = new QPushButton("Find", this, "find"); + connect(ok, SIGNAL(clicked()), this, SLOT(ok_slot())); + + cancel = new QPushButton("Done", this, "cancel"); + connect(cancel, SIGNAL(clicked()), this, SLOT(done_slot())); + // connect(cancel, SIGNAL(clicked()), this, SLOT(reject())); + + setFixedSize(330, 130); + +} + +void KEdSrch::focusInEvent( QFocusEvent *) +{ + value->setFocus(); + //value->selectAll(); +} + +QString KEdSrch::getText() { return value->text(); } + +void KEdSrch::setText(QString string){ + + value->setText(string); + +} + +void KEdSrch::done_slot(){ + + emit search_done_signal(); + +} + + +bool KEdSrch::case_sensitive(){ + + return sensitive->isChecked(); + +} + +bool KEdSrch::get_direction(){ + + return direction->isChecked(); + +} + + +void KEdSrch::ok_slot(){ + + QString text; + + text = value->text(); + + if (!text.isEmpty()) + emit search_signal(); + +} + + +void KEdSrch::resizeEvent(QResizeEvent *){ + + + frame1->setGeometry(5, 5, width() - 10, 80); + cancel->setGeometry(width() - 80, height() - 30, 70, 25); + ok->setGeometry(10, height() - 30, 70, 25); + value->setGeometry(20, 25, width() - 40, 25); + sensitive->setGeometry(20, 55, 110, 25); + direction->setGeometry(width()- 20 - 130, 55, 130, 25); + +} + + + +//////////////////////////////////////////////////////////////////// +// +// Replace Dialog +// + + +KEdReplace::KEdReplace(QWidget *parent, const char *name) + : QDialog(parent, name,TRUE){ + + + this->setFocusPolicy(QWidget::StrongFocus); + + frame1 = new QGroupBox("Find:", this, "frame1"); + + value = new QLineEdit( this, "value"); + value->setFocus(); + connect(value, SIGNAL(returnPressed()), this, SLOT(ok_slot())); + + replace_value = new QLineEdit( this, "replac_value"); + connect(replace_value, SIGNAL(returnPressed()), this, SLOT(ok_slot())); + + label = new QLabel(this,"Rlabel"); + label->setText("Replace with:"); + + sensitive = new QCheckBox("Case Sensitive", this, "case"); + sensitive->setChecked(TRUE); + + direction = new QCheckBox("Find Backwards", this, "direction"); + + ok = new QPushButton("Find", this, "find"); + connect(ok, SIGNAL(clicked()), this, SLOT(ok_slot())); + + replace = new QPushButton("Replace", this, "rep"); + connect(replace, SIGNAL(clicked()), this, SLOT(replace_slot())); + + replace_all = new QPushButton("Replace All", this, "repall"); + connect(replace_all, SIGNAL(clicked()), this, SLOT(replace_all_slot())); + + cancel = new QPushButton("Done", this, "cancel"); + connect(cancel, SIGNAL(clicked()), this, SLOT(done_slot())); + + setFixedSize(330, 180); + +} + + +void KEdReplace::focusInEvent( QFocusEvent *){ + + value->setFocus(); + // value->selectAll(); +} + +QString KEdReplace::getText() { return value->text(); } + +QString KEdReplace::getReplaceText() { return replace_value->text(); } + +void KEdReplace::setText(QString string) { + + value->setText(string); + +} + +void KEdReplace::done_slot(){ + + emit replace_done_signal(); + +} + + +void KEdReplace::replace_slot(){ + + emit replace_signal(); + +} + +void KEdReplace::replace_all_slot(){ + + emit replace_all_signal(); + +} + + +bool KEdReplace::case_sensitive(){ + + return sensitive->isChecked(); + +} + + +bool KEdReplace::get_direction(){ + + return direction->isChecked(); + +} + + +void KEdReplace::ok_slot(){ + + QString text; + text = value->text(); + + if (!text.isEmpty()) + emit find_signal(); + +} + + +void KEdReplace::resizeEvent(QResizeEvent *){ + + frame1->setGeometry(5, 5, width() - 10, 135); + + cancel->setGeometry(width() - 80, height() - 30, 70, 25); + ok->setGeometry(10, height() - 30, 70, 25); + replace->setGeometry(85, height() - 30, 70, 25); + replace_all->setGeometry(160, height() - 30, 85, 25); + + value->setGeometry(20, 25, width() - 40, 25); + replace_value->setGeometry(20, 80, width() - 40, 25); + + label->setGeometry(20,55,80,20); + sensitive->setGeometry(20, 110, 110, 25); + direction->setGeometry(width()- 20 - 130, 110, 130, 25); + +} + + + + +void KEdGotoLine::selected(int) +{ + accept(); +} + +KEdGotoLine::KEdGotoLine( QWidget *parent, const char *name) + : QDialog( parent, name, TRUE ) +{ + frame = new QGroupBox( "Goto Line", this ); + lineNum = new KIntLineEdit( this ); + this->setFocusPolicy( QWidget::StrongFocus ); + connect(lineNum, SIGNAL(returnPressed()), this, SLOT(accept())); + + ok = new QPushButton("Go", this ); + cancel = new QPushButton("Cancel", this ); + + connect(cancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(ok, SIGNAL(clicked()), this, SLOT(accept())); + resize(300, 120); + +} + +void KEdGotoLine::resizeEvent(QResizeEvent *) +{ + frame->setGeometry(5, 5, width() - 10, 80); + cancel->setGeometry(width() - 80, height() - 30, 70, 25); + ok->setGeometry(10, height() - 30, 70, 25); + lineNum->setGeometry(20, 35, width() - 40, 25); +} + +void KEdGotoLine::focusInEvent( QFocusEvent *) +{ + lineNum->setFocus(); + lineNum->selectAll(); +} + +int KEdGotoLine::getLineNumber() +{ + return lineNum->getValue(); +} diff --git a/KEdit.h b/KEdit.h new file mode 100644 index 000000000..8eb0f68d0 --- /dev/null +++ b/KEdit.h @@ -0,0 +1,520 @@ +/* + $Id$ + + kedit, a simple text editor for the KDE project + + Copyright (C) 1996 Bernd Johannes Wuebben + wuebben@math.cornell.edu + + Parts: Alexander Sanda + + 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. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + + +#ifndef __KEDIT_H__ +#define __KEDIT_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/// +class KIntLineEdit : public QLineEdit +{ + Q_OBJECT; + +public: + KIntLineEdit( QWidget *parent = 0, const char *name = 0 ) + : QLineEdit( parent, name ) {}; + + int getValue() { return atoi( text() ); }; + +protected: + + void keyPressEvent( QKeyEvent *e ) { + char key = e->ascii(); + + if( isdigit( key ) + || ( e->key() == Key_Return) || ( e->key() == Key_Enter ) + || ( e->key() == Key_Delete) || ( e->key() == Key_Backspace) + || ( e->key() == Key_Left ) || ( e->key() == Key_Right )){ + + QLineEdit::keyPressEvent( e ); + return; + } else { + e->ignore(); + return; + } + }; +}; + +/// +class KEdGotoLine : public QDialog +{ + Q_OBJECT; + +public: + + KEdGotoLine( QWidget *parent = 0, const char *name = 0 ); + + int getLineNumber(); + KIntLineEdit *lineNum; + +private: + QPushButton *ok, *cancel; + QGroupBox *frame; + void resizeEvent(QResizeEvent *); + void focusInEvent(QFocusEvent *); + +public slots: + + void selected( int ); +}; + +/// +class KEdSrch : public QDialog +{ + Q_OBJECT; + +public: + + KEdSrch ( QWidget *parent = 0, const char *name=0); + + QString getText(); + void setText(QString string); + bool case_sensitive(); + bool get_direction(); + +protected: + void focusInEvent( QFocusEvent *); + void resizeEvent(QResizeEvent *); + +private: + + QPushButton *ok, *cancel; + QCheckBox *sensitive; + QCheckBox *direction; + QGroupBox *frame1; + QLineEdit *value; + +signals: + + void search_signal(); + void search_done_signal(); + +public slots: + + void done_slot(); + void ok_slot(); + +}; + +/// +class KEdReplace : public QDialog +{ + Q_OBJECT; + +public: + + KEdReplace ( QWidget *parent = 0, const char *name=0); + + QString getText(); + QString getReplaceText(); + void setText(QString); + QLineEdit *value; + QLineEdit *replace_value; + QLabel *label; + bool case_sensitive(); + bool get_direction(); + +protected: + + void focusInEvent( QFocusEvent *); + void resizeEvent ( QResizeEvent *); + +private: + + QPushButton *ok, *cancel, *replace, *replace_all; + QCheckBox *sensitive; + QCheckBox *direction; + QGroupBox *frame1; + + +signals: + + void replace_signal(); + void find_signal(); + void replace_all_signal(); + void replace_done_signal(); + +public slots: + + void done_slot(); + void replace_slot(); + void replace_all_slot(); + void ok_slot(); + +}; + + +/// +class KEdit : public QMultiLineEdit +{ + Q_OBJECT; + +public: + + KEdit (KApplication *a=NULL,QWidget *parent=NULL, const char *name=NULL, + const char *filename=NULL); + + ~KEdit(); + + + enum { NONE, + FORWARD, + BACKWARD }; + + enum { KEDIT_OK = 0, + KEDIT_OS_ERROR = 1, + KEDIT_USER_CANCEL = 2 , + KEDIT_RETRY = 3, + KEDIT_NOPERMISSIONS = 4}; + + enum { OPEN_READWRITE = 1, + OPEN_READONLY = 2, + OPEN_INSERT = 4 }; + + + /// Opens a new untitled document in the text widget + /** Opens a new untitled document in the text widget The user is given + a chance to save the current document if the current document has + been modified. + */ + int newFile(); + + /// Saves the file if necessary under the current file name + /** Saves the file if necessary under the current file name. If the current file + name is Untitled, as it is after a call to newFile(), this routing will + call saveAs(). + */ + int doSave(); + + /// Saves the file as filename + /** Saves the file as filename + */ + int doSave( const char *filename ); + + /// Allows the user to save the file under a new name + /** Allows the user to save the file under a new name + */ + int saveAs(); + + /// Let the user open an new file + /** This will present an open file dialog and open the file specified by the user, + if possible. The return codes are KEDIT_OK, KEDIT_USER_CANCEL and + KEDIT_OS_ERROR. The user will be given a chance to save the current file if + it has been modified. mode is one of OPEN_READONLY, OPEN_READWRITE. + OPEN_READONLY means that the user will not be able to insert characters + into the document. + */ + int openFile( int mode ); + + /// Lets the user insert a file at the current cursor position + /** Calling this method will let the user insert a file at the current cursor + position. Return codes are KEDIT_OK, KEDIT_USER_CANCEL, KDEDIT_OS_ERROR. + */ + int insertFile(); + + /// Loads the file filemane in the editor + /** Loads the file filename into editor. The possible modes are + OPEN_READONLY, OPEN_READWRITE, OPEN_INSERT. + OPEN_READONLY means that the user will not be able to insert characters + into the document. OPEN_INSERT means that the file will be inserted + into the current document at the current cursor position. + */ + int loadFile( QString filename , int mode ); + + + /// Returns the filename of the current file. + /** Returns the filename of the currnet file. You can use setName() to set the + filename of the current file + */ + QString getName(); + + /// Sets the filename of the current file. + /** Sets the filename of the currnet file. You can use getName() to set the + filename of the current file + */ + void setName( const char *_name ); + + /// Returns the currently marked text. + /** Returns the currently marked text. + */ + QString markedText(); + + /// Lets the user select a font and sets the font of the textwidget. + /** Lets the user select a font and sets the font of the textwidget to that + selected font. + */ + void selectFont(); + + /// Presents a search dialog to the user + /** Presents a search dialog to the user + */ + void Search(); + + /// Repeats the last search specified on the search dialog. + /** Repeasts the last search specified on the search dialog. If the user + hasn't searched for anything until now, this method will simply return + without doing anything. + */ + int repeatSearch(); + + /// Presents a Search and Replace Dialog to the User. + /** Presents a Search and Replace Dialog to the User. + */ + void Replace(); + + /// Presents a "Goto Line" dialog to the User + /** Presents a "Goto Line" dialog to the User + */ + void doGotoLine(); + + /// Returns true if the document has been modified. + /**Returns true if the document has been modified. + */ + bool isModified(); + + /// Toggles the modification status of the document + /** Toggles the modification status of the document. TRUE = Modified, + FALSE = UNMODIFIED. Methods such as doSave() rely on this to see whether + the document needs to be saved. + */ + void toggleModified( bool ); + + /// Sets Indent Mode + /** Sets the Indent Mode. TRUE = Indent mode on, FALSE = Indent mode off. + */ + void setAutoIndentMode( bool ); + + /// Returns the Indent Mode + /** Returns the Indent Mode. TRUE = Indent mode on, FALSE = Indent mode off. + */ + bool AutoIndentMode(){ return autoIndentMode; }; + + /// Install a Popup Menue for KEdit. + /** Install a Popup Menue for KEdit. The Popup Menu will be activated on + a right mouse button press event. + */ + void installRBPopup( QPopupMenu* ); + + /// Return the current Line number + /** Returns the current line number, that is the line the cursor is on. + */ + int currentLine(); + + /// Returns the current Column number + /** This returns the actual column number the cursor is on. This call differs + from QMultiLineEdit::getCursorPosition in that it returns the actual cursor + position and not the character position. Use currentLine() and currentColumn() + if you want to display the current line or column in the status bar for + example. + */ + int currentColumn(); + + /// Returns TRUE if word wrap is on + /** Returns TRUE if word wrap is on. You also need to specify the fill column + with setFillColumnMode() otherwise wordwrap is not in effect. + */ + bool WordWrap(); + + /// Turn word wrap mode on or off. + /** You also need to specify the fill column + with setFillColumnMode() otherwise wordwrap is not in effect. + */ + void setWordWrap(bool ); + + /// Returns TRUE if fill column mode is on + /** Returns TRUE if fill column mode is on, that is if the line will + be broken automatically when if a character is to be inserted past + this position. + */ + bool FillColumnMode(); + + /// Set the fill column to column col if line is strictly larger than 0. + /** Set the fill column to column col, if col is strictly larger than 0. + If col is 0, fill column mode is turned off. + In fill column mode, the line will + be broken automatically at column col, when a character is + inserted past column col.. + */ + void setFillColumnMode(int col ); + + /// save a backup copy + /** If copy is TRUE KEdit will make a backup copy of the document that + is being edited on opening it. The backup copy will receive the + suffix ~. The default is TRUE. + */ + void saveBackupCopy(bool copy); + +signals: + + /// This signal is emitted when the document in the textwidget has changed + /** This signal is emitted when the document in the textwidget has changed + */ + void fileChanged(); + + /// This signal is emitted whenever the cursor position changed. + /** This signal is emitted whenever the cursor position changed. + Use this in conjunction with currentLine(), currentColumn() + if you need to know the cursor position. + */ + void CursorPositionChanged(); + + /// This signal is emitted just before saving a file. + /** This signal is emitted just before saving a file. Since KEdit calls + kapp->processEvents(), you have a chance to let the user know what's + going to happen. + */ + void saving(); + + /// This signal is emitted just before loading a file. + /** This signal is emitted just before loading a file. Since KEdit calls + kapp->processEvents(), you have a chance to let the user know what's + going to happen. + */ + void loading(); + + /// This signal is emitted if the user toggles from overwrite to insert mode + /** This signal is emitted if the user toggles from overwrite to insert mode. + He can do so by pressing the "Insert" Button on a PC keyboard. + */ + void toggle_overwrite_signal(); + + +public slots: + + void setModified(); + + void search_slot(); + + void searchdone_slot(); + + void replace_slot(); + + void replace_all_slot(); + + void replace_search_slot(); + + void replacedone_slot(); + + void computePosition(); + + +protected: + + int saveFile(); + + int doSearch(QString s_pattern, bool case_sensitive, + bool regex, bool forward,int line, int col); + + int doReplace(QString s_pattern, bool case_sensitive, + bool regex, bool forward,int line, int col,bool replace); + + +protected: + + bool eventFilter ( QObject *, QEvent * ); + void keyPressEvent ( QKeyEvent * ); + void mousePressEvent ( QMouseEvent* ); + void mouseReleaseEvent( QMouseEvent* ); + void mouseMoveEvent ( QMouseEvent* ); + + +private: + + void setContextSens(); + void mynewLine(); + QString prefixString(QString); + + +private: + + QWidget *p_parent; + QFileDialog *fbox; + KEdSrch *srchdialog; + KEdReplace *replace_dialog; + KEdGotoLine *gotodialog; + QPopupMenu *rb_popup; + KApplication* mykapp; + + QString filename; + QString pattern; + + bool modified; + bool autoIndentMode; + bool can_replace; + + int edit_mode; + int last_search; + int last_replace; + int replace_all_line; + int replace_all_col; + + int line_pos, col_pos; + bool fill_column_is_set; + bool word_wrap_is_set; + int fill_column_value; + bool make_backup_copies; + +}; + + + +#endif