keditcl1.cpp
00001 /* This file is part of the KDE libraries 00002 00003 Copyright (C) 1997 Bernd Johannes Wuebben <wuebben@math.cornell.edu> 00004 Copyright (C) 2000 Waldo Bastian <bastian@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include <tqdragobject.h> 00023 #include <tqpopupmenu.h> 00024 #include <tqtextstream.h> 00025 #include <tqtimer.h> 00026 00027 #include <kapplication.h> 00028 #include <kcursor.h> 00029 #include <kdebug.h> 00030 #include <kcmenumngr.h> 00031 #include <kfontdialog.h> 00032 #include <klocale.h> 00033 #include <kmessagebox.h> 00034 #include <kstdaccel.h> 00035 #include <kurldrag.h> 00036 00037 #include "keditcl.h" 00038 #include "keditcl.moc" 00039 00040 class KEdit::KEditPrivate 00041 { 00042 public: 00043 bool overwriteEnabled:1; 00044 bool posDirty:1; 00045 bool autoUpdate:1; 00046 }; 00047 00048 00049 KEdit::KEdit(TQWidget *_parent, const char *name) 00050 : TQMultiLineEdit(_parent, name) 00051 { 00052 d = new KEditPrivate; 00053 d->overwriteEnabled = false; 00054 d->posDirty = true; 00055 d->autoUpdate = true; 00056 00057 parent = _parent; 00058 00059 // set some defaults 00060 00061 line_pos = col_pos = 0; 00062 00063 srchdialog = NULL; 00064 replace_dialog= NULL; 00065 gotodialog = NULL; 00066 00067 setAcceptDrops(true); 00068 KCursor::setAutoHideCursor( this, true ); 00069 00070 connect(this, TQT_SIGNAL(cursorPositionChanged(int,int)), 00071 this, TQT_SLOT(slotCursorPositionChanged())); 00072 } 00073 00074 00075 KEdit::~KEdit() 00076 { 00077 delete d; 00078 } 00079 00080 void 00081 KEdit::setAutoUpdate(bool b) 00082 { 00083 d->autoUpdate = b; 00084 } 00085 00086 void 00087 KEdit::insertText(TQTextStream *stream) 00088 { 00089 // setAutoUpdate(false); 00090 int line, col; 00091 getCursorPosition(&line, &col); 00092 int saveline = line; 00093 int savecol = col; 00094 TQString textLine; 00095 00096 // MS: Patch by Martin Schenk <martin@schenk.com> 00097 // MS: disable UNDO, or TQMultiLineEdit remembers every textLine !!! 00098 // memory usage is: 00099 // textLine: 2*size rounded up to nearest power of 2 (520Kb -> 1024Kb) 00100 // widget: about (2*size + 60bytes*lines) 00101 // -> without disabling undo, it often needs almost 8*size 00102 int oldUndoDepth = undoDepth(); 00103 setUndoDepth( 0 ); // ### -1? 00104 00105 // MS: read everything at once if file <= 1MB, 00106 // else read in 5000-line chunks to keep memory usage acceptable. 00107 TQIODevice *dev=stream->device(); 00108 if (dev && dev->size()>(1024*1024)) { 00109 while(1) { 00110 int i; 00111 textLine=""; 00112 for (i=0; i<5000; i++) { 00113 TQString line=stream->readLine(); 00114 if (line.isNull()) break; // EOF 00115 textLine+=line+'\n'; 00116 } 00117 insertAt(textLine, line, col); 00118 line+=i; col=0; 00119 if (i!=5000) break; 00120 } 00121 } 00122 else { 00123 textLine = stream->read(); // Read all ! 00124 insertAt( textLine, line, col); 00125 } 00126 setUndoDepth( oldUndoDepth ); 00127 00128 setCursorPosition(saveline, savecol); 00129 // setAutoUpdate(true); 00130 00131 // repaint(); 00132 00133 setModified(true); 00134 setFocus(); 00135 00136 // Bernd: Please don't leave debug message like that lying around 00137 // they cause ENORMOUSE performance hits. Once upon a day 00138 // kedit used to be really really fast using memmap etc ..... 00139 // oh well .... 00140 00141 // TQString str = text(); 00142 // for (int i = 0; i < (int) str.length(); i++) 00143 // printf("KEdit: U+%04X\n", str[i].unicode()); 00144 00145 } 00146 00147 void 00148 KEdit::cleanWhiteSpace() 00149 { 00150 d->autoUpdate = false; 00151 if (!hasMarkedText()) 00152 selectAll(); 00153 TQString oldText = markedText(); 00154 TQString newText; 00155 TQStringList lines = TQStringList::split('\n', oldText, true); 00156 bool addSpace = false; 00157 bool firstLine = true; 00158 TQChar lastChar = oldText[oldText.length()-1]; 00159 TQChar firstChar = oldText[0]; 00160 for(TQStringList::Iterator it = lines.begin(); 00161 it != lines.end();) 00162 { 00163 TQString line = (*it).simplifyWhiteSpace(); 00164 if (line.isEmpty()) 00165 { 00166 if (addSpace) 00167 newText += TQString::fromLatin1("\n\n"); 00168 if (firstLine) 00169 { 00170 if (firstChar.isSpace()) 00171 newText += '\n'; 00172 firstLine = false; 00173 } 00174 addSpace = false; 00175 } 00176 else 00177 { 00178 if (addSpace) 00179 newText += ' '; 00180 if (firstLine) 00181 { 00182 if (firstChar.isSpace()) 00183 newText += ' '; 00184 firstLine = false; 00185 } 00186 newText += line; 00187 addSpace = true; 00188 } 00189 it = lines.remove(it); 00190 } 00191 if (addSpace) 00192 { 00193 if (lastChar == '\n') 00194 newText += '\n'; 00195 else if (lastChar.isSpace()) 00196 newText += ' '; 00197 } 00198 00199 if (oldText == newText) 00200 { 00201 deselect(); 00202 d->autoUpdate = true; 00203 repaint(); 00204 return; 00205 } 00206 if (wordWrap() == NoWrap) 00207 { 00208 // If wordwrap is off, we have to do some line-wrapping ourselves now 00209 // We use another TQMultiLineEdit for this, so that we get nice undo 00210 // behavior. 00211 TQMultiLineEdit *we = new TQMultiLineEdit(); 00212 we->setWordWrap(FixedColumnWidth); 00213 we->setWrapColumnOrWidth(78); 00214 we->setText(newText); 00215 newText = TQString::null; 00216 for(int i = 0; i < we->numLines(); i++) 00217 { 00218 TQString line = we->textLine(i); 00219 if (line.right(1) != "\n") 00220 line += '\n'; 00221 newText += line; 00222 } 00223 delete we; 00224 } 00225 00226 insert(newText); 00227 d->autoUpdate = true; 00228 repaint(); 00229 00230 setModified(true); 00231 setFocus(); 00232 } 00233 00234 00235 void 00236 KEdit::saveText(TQTextStream *stream) 00237 { 00238 saveText(stream, false); 00239 } 00240 00241 void 00242 KEdit::saveText(TQTextStream *stream, bool softWrap) 00243 { 00244 int line_count = numLines()-1; 00245 if (line_count < 0) 00246 return; 00247 00248 if (softWrap || (wordWrap() == NoWrap)) 00249 { 00250 for(int i = 0; i < line_count; i++) 00251 { 00252 (*stream) << textLine(i) << '\n'; 00253 } 00254 (*stream) << textLine(line_count); 00255 } 00256 else 00257 { 00258 for(int i = 0; i <= line_count; i++) 00259 { 00260 int lines_in_parag = linesOfParagraph(i); 00261 if (lines_in_parag == 1) 00262 { 00263 (*stream) << textLine(i); 00264 } 00265 else 00266 { 00267 TQString parag_text = textLine(i); 00268 int pos = 0; 00269 int first_pos = 0; 00270 int current_line = 0; 00271 while(true) { 00272 while(lineOfChar(i, pos) == current_line) pos++; 00273 (*stream) << parag_text.mid(first_pos, pos - first_pos - 1) << '\n'; 00274 current_line++; 00275 first_pos = pos; 00276 if (current_line+1 == lines_in_parag) 00277 { 00278 // Last line 00279 (*stream) << parag_text.mid(pos); 00280 break; 00281 } 00282 } 00283 } 00284 if (i < line_count) 00285 (*stream) << '\n'; 00286 } 00287 } 00288 } 00289 00290 int KEdit::currentLine(){ 00291 00292 computePosition(); 00293 return line_pos; 00294 00295 } 00296 00297 int KEdit::currentColumn(){ 00298 00299 computePosition(); 00300 return col_pos; 00301 } 00302 00303 void KEdit::slotCursorPositionChanged() 00304 { 00305 d->posDirty = true; 00306 emit CursorPositionChanged(); 00307 } 00308 00309 void KEdit::computePosition() 00310 { 00311 if (!d->posDirty) return; 00312 d->posDirty = false; 00313 00314 int line, col; 00315 00316 getCursorPosition(&line,&col); 00317 00318 // line is expressed in paragraphs, we now need to convert to lines 00319 line_pos = 0; 00320 if (wordWrap() == NoWrap) 00321 { 00322 line_pos = line; 00323 } 00324 else 00325 { 00326 for(int i = 0; i < line; i++) 00327 line_pos += linesOfParagraph(i); 00328 } 00329 00330 int line_offset = lineOfChar(line, col); 00331 line_pos += line_offset; 00332 00333 // We now calculate where the current line starts in the paragraph. 00334 TQString linetext = textLine(line); 00335 int start_of_line = 0; 00336 if (line_offset > 0) 00337 { 00338 start_of_line = col; 00339 while(lineOfChar(line, --start_of_line) == line_offset); 00340 start_of_line++; 00341 } 00342 00343 00344 // O.K here is the deal: The function getCursorPositoin returns the character 00345 // position of the cursor, not the screenposition. I.e,. assume the line 00346 // consists of ab\tc then the character c will be on the screen on position 8 00347 // whereas getCursorPosition will return 3 if the cursors is on the character c. 00348 // Therefore we need to compute the screen position from the character position. 00349 // That's what all the following trouble is all about: 00350 00351 int coltemp = col-start_of_line; 00352 int pos = 0; 00353 int find = 0; 00354 int mem = 0; 00355 bool found_one = false; 00356 00357 // if you understand the following algorithm you are worthy to look at the 00358 // kedit+ sources -- if not, go away ;-) 00359 00360 00361 while(find >=0 && find <= coltemp- 1 ){ 00362 find = linetext.find('\t', find+start_of_line, true )-start_of_line; 00363 if( find >=0 && find <= coltemp - 1 ){ 00364 found_one = true; 00365 pos = pos + find - mem; 00366 pos = pos + 8 - pos % 8; 00367 mem = find; 00368 find ++; 00369 } 00370 } 00371 00372 pos = pos + coltemp - mem; // add the number of characters behind the 00373 // last tab on the line. 00374 00375 if (found_one){ 00376 pos = pos - 1; 00377 } 00378 00379 col_pos = pos; 00380 } 00381 00382 00383 void KEdit::keyPressEvent ( TQKeyEvent *e) 00384 { 00385 // ignore Ctrl-Return so that KDialogBase can catch them 00386 if ( e->key() == Key_Return && e->state() == ControlButton ) { 00387 e->ignore(); 00388 return; 00389 } 00390 00391 KKey key(e); 00392 int keyQt = key.keyCodeQt(); 00393 00394 if ( keyQt == CTRL+Key_K ){ 00395 00396 int line = 0; 00397 int col = 0; 00398 TQString killstring; 00399 00400 if(!killing){ 00401 killbufferstring = ""; 00402 killtrue = false; 00403 lastwasanewline = false; 00404 } 00405 00406 if(!atEnd()){ 00407 00408 getCursorPosition(&line,&col); 00409 killstring = textLine(line); 00410 killstring = killstring.mid(col,killstring.length()); 00411 00412 00413 if(!killbufferstring.isEmpty() && !killtrue && !lastwasanewline){ 00414 killbufferstring += '\n'; 00415 } 00416 00417 if( (killstring.length() == 0) && !killtrue){ 00418 killbufferstring += '\n'; 00419 lastwasanewline = true; 00420 } 00421 00422 if(killstring.length() > 0){ 00423 00424 killbufferstring += killstring; 00425 lastwasanewline = false; 00426 killtrue = true; 00427 00428 }else{ 00429 00430 lastwasanewline = false; 00431 killtrue = !killtrue; 00432 00433 } 00434 00435 }else{ 00436 00437 if(killbufferstring.isEmpty() && !killtrue && !lastwasanewline){ 00438 killtrue = true; 00439 } 00440 00441 } 00442 00443 killing = true; 00444 00445 TQMultiLineEdit::keyPressEvent(e); 00446 setModified(true); 00447 return; 00448 } 00449 else if ( keyQt == CTRL+Key_Y ){ 00450 00451 int line = 0; 00452 int col = 0; 00453 00454 getCursorPosition(&line,&col); 00455 00456 TQString tmpstring = killbufferstring; 00457 if(!killtrue) 00458 tmpstring += '\n'; 00459 00460 insertAt(tmpstring,line,col); 00461 00462 killing = false; 00463 setModified(true); 00464 return; 00465 } 00466 00467 killing = false; 00468 00469 if ( KStdAccel::copy().contains( key ) ) 00470 copy(); 00471 else if ( isReadOnly() ) 00472 TQMultiLineEdit::keyPressEvent( e ); 00473 // If this is an unmodified printable key, send it directly to TQMultiLineEdit. 00474 else if ( !(key.keyCodeQt() & (CTRL | ALT)) && !e->text().isEmpty() && TQString(e->text()).unicode()->isPrint() ) 00475 TQMultiLineEdit::keyPressEvent( e ); 00476 else if ( KStdAccel::paste().contains( key ) ) { 00477 paste(); 00478 setModified(true); 00479 slotCursorPositionChanged(); 00480 } 00481 else if ( KStdAccel::cut().contains( key ) ) { 00482 cut(); 00483 setModified(true); 00484 slotCursorPositionChanged(); 00485 } 00486 else if ( KStdAccel::undo().contains( key ) ) { 00487 undo(); 00488 setModified(true); 00489 slotCursorPositionChanged(); 00490 } 00491 else if ( KStdAccel::redo().contains( key ) ) { 00492 redo(); 00493 setModified(true); 00494 slotCursorPositionChanged(); 00495 } 00496 else if ( KStdAccel::deleteWordBack().contains( key ) ) { 00497 moveCursor(MoveWordBackward, true); 00498 if (hasSelectedText()) 00499 del(); 00500 setModified(true); 00501 slotCursorPositionChanged(); 00502 } 00503 else if ( KStdAccel::deleteWordForward().contains( key ) ) { 00504 moveCursor(MoveWordForward, true); 00505 if (hasSelectedText()) 00506 del(); 00507 setModified(true); 00508 slotCursorPositionChanged(); 00509 } 00510 else if ( KStdAccel::backwardWord().contains( key ) ) { 00511 CursorAction action = MoveWordBackward; 00512 int para, index; 00513 getCursorPosition( ¶, & index ); 00514 if (text(para).isRightToLeft()) 00515 action = MoveWordForward; 00516 moveCursor(action, false ); 00517 slotCursorPositionChanged(); 00518 } 00519 else if ( KStdAccel::forwardWord().contains( key ) ) { 00520 CursorAction action = MoveWordForward; 00521 int para, index; 00522 getCursorPosition( ¶, & index ); 00523 if (text(para).isRightToLeft()) 00524 action = MoveWordBackward; 00525 moveCursor( action, false ); 00526 slotCursorPositionChanged(); 00527 } 00528 else if ( KStdAccel::next().contains( key ) ) { 00529 moveCursor( MovePgDown, false ); 00530 slotCursorPositionChanged(); 00531 } 00532 else if ( KStdAccel::prior().contains( key ) ) { 00533 moveCursor( MovePgUp, false ); 00534 slotCursorPositionChanged(); 00535 } 00536 else if ( KStdAccel::home().contains( key ) ) { 00537 moveCursor( MoveHome, false ); 00538 slotCursorPositionChanged(); 00539 } 00540 else if ( KStdAccel::end().contains( key ) ) { 00541 moveCursor( MoveEnd, false ); 00542 slotCursorPositionChanged(); 00543 } 00544 else if ( KStdAccel::beginningOfLine().contains( key ) ) { 00545 moveCursor( MoveLineStart, false); 00546 slotCursorPositionChanged(); 00547 } 00548 else if ( KStdAccel::endOfLine().contains( key ) ) { 00549 moveCursor( MoveLineEnd, false); 00550 slotCursorPositionChanged(); 00551 } 00552 else if ( key == Key_Insert ) { 00553 if (d->overwriteEnabled) 00554 { 00555 this->setOverwriteMode(!this->isOverwriteMode()); 00556 emit toggle_overwrite_signal(); 00557 } 00558 } 00559 else 00560 TQMultiLineEdit::keyPressEvent(e); 00561 } 00562 00563 void KEdit::installRBPopup(TQPopupMenu *p) { 00564 KContextMenuManager::insert( this, p ); 00565 } 00566 00567 void KEdit::selectFont(){ 00568 00569 TQFont font = this->font(); 00570 KFontDialog::getFont(font); 00571 this->setFont(font); 00572 00573 } 00574 00575 void KEdit::doGotoLine() { 00576 00577 if( !gotodialog ) 00578 gotodialog = new KEdGotoLine( parent, "gotodialog" ); 00579 00580 this->clearFocus(); 00581 00582 gotodialog->exec(); 00583 // this seems to be not necessary 00584 // gotodialog->setFocus(); 00585 if( gotodialog->result() != KEdGotoLine::Accepted) 00586 return; 00587 int target_line = gotodialog->getLineNumber()-1; 00588 if (wordWrap() == NoWrap) 00589 { 00590 setCursorPosition( target_line, 0 ); 00591 setFocus(); 00592 return; 00593 } 00594 00595 int max_parag = paragraphs(); 00596 00597 int line = 0; 00598 int parag = -1; 00599 int lines_in_parag = 0; 00600 while ((++parag < max_parag) && (line + lines_in_parag < target_line)) 00601 { 00602 line += lines_in_parag; 00603 lines_in_parag = linesOfParagraph(parag); 00604 } 00605 00606 int col = 0; 00607 if (parag >= max_parag) 00608 { 00609 target_line = line + lines_in_parag - 1; 00610 parag = max_parag-1; 00611 } 00612 00613 while(1+line+lineOfChar(parag,col) < target_line) col++; 00614 setCursorPosition( parag, col ); 00615 setFocus(); 00616 } 00617 00618 00619 void KEdit::dragMoveEvent(TQDragMoveEvent* e) { 00620 00621 if(KURLDrag::canDecode(e)) 00622 e->accept(); 00623 else if(TQTextDrag::canDecode(e)) 00624 TQMultiLineEdit::dragMoveEvent(e); 00625 } 00626 00627 void KEdit::contentsDragMoveEvent(TQDragMoveEvent* e) { 00628 00629 if(KURLDrag::canDecode(e)) 00630 e->accept(); 00631 else if(TQTextDrag::canDecode(e)) 00632 TQMultiLineEdit::contentsDragMoveEvent(e); 00633 } 00634 00635 void KEdit::dragEnterEvent(TQDragEnterEvent* e) { 00636 00637 kdDebug() << "KEdit::dragEnterEvent()" << endl; 00638 e->accept(KURLDrag::canDecode(e) || TQTextDrag::canDecode(e)); 00639 } 00640 00641 void KEdit::contentsDragEnterEvent(TQDragEnterEvent* e) { 00642 00643 kdDebug() << "KEdit::contentsDragEnterEvent()" << endl; 00644 e->accept(KURLDrag::canDecode(e) || TQTextDrag::canDecode(e)); 00645 } 00646 00647 00648 void KEdit::dropEvent(TQDropEvent* e) { 00649 00650 kdDebug() << "KEdit::dropEvent()" << endl; 00651 00652 if(KURLDrag::canDecode(e)) { 00653 emit gotUrlDrop(e); 00654 } 00655 else if(TQTextDrag::canDecode(e)) 00656 TQMultiLineEdit::dropEvent(e); 00657 } 00658 00659 void KEdit::contentsDropEvent(TQDropEvent* e) { 00660 00661 kdDebug() << "KEdit::contentsDropEvent()" << endl; 00662 00663 if(KURLDrag::canDecode(e)) { 00664 emit gotUrlDrop(e); 00665 } 00666 else if(TQTextDrag::canDecode(e)) 00667 TQMultiLineEdit::contentsDropEvent(e); 00668 } 00669 00670 void KEdit::setOverwriteEnabled(bool b) 00671 { 00672 d->overwriteEnabled = b; 00673 } 00674 00675 // TQWidget::create() turns off mouse-Tracking which would break auto-hiding 00676 void KEdit::create( WId id, bool initializeWindow, bool destroyOldWindow ) 00677 { 00678 TQMultiLineEdit::create( id, initializeWindow, destroyOldWindow ); 00679 KCursor::setAutoHideCursor( this, true ); 00680 } 00681 00682 void KEdit::ensureCursorVisible() 00683 { 00684 if (!d->autoUpdate) 00685 return; 00686 00687 TQMultiLineEdit::ensureCursorVisible(); 00688 } 00689 00690 void KEdit::setCursor( const TQCursor &c ) 00691 { 00692 if (!d->autoUpdate) 00693 return; 00694 00695 TQMultiLineEdit::setCursor(c); 00696 } 00697 00698 void KEdit::viewportPaintEvent( TQPaintEvent*pe ) 00699 { 00700 if (!d->autoUpdate) 00701 return; 00702 00703 TQMultiLineEdit::viewportPaintEvent(pe); 00704 } 00705 00706 00707 void KEdGotoLine::virtual_hook( int id, void* data ) 00708 { KDialogBase::virtual_hook( id, data ); } 00709 00710 void KEdFind::virtual_hook( int id, void* data ) 00711 { KDialogBase::virtual_hook( id, data ); } 00712 00713 void KEdReplace::virtual_hook( int id, void* data ) 00714 { KDialogBase::virtual_hook( id, data ); } 00715 00716 void KEdit::virtual_hook( int, void* ) 00717 { /*BASE::virtual_hook( id, data );*/ } 00718