00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include "qeditor.h"
00044 #include "qeditor_settings.h"
00045
00046 #include "qsourcecolorizer.h"
00047 #include "cpp_colorizer.h"
00048 #include "java_colorizer.h"
00049 #include "js_colorizer.h"
00050 #include "jsp_colorizer.h"
00051 #include "python_colorizer.h"
00052 #include "xml_colorizer.h"
00053 #include "qmake_colorizer.h"
00054 #include "cs_colorizer.h"
00055 #include "ocaml_colorizer.h"
00056 #include "pascal_colorizer.h"
00057 #include "ada_colorizer.h"
00058 #include "sql_colorizer.h"
00059
00060 #if defined(HAVE_PERL_MODE)
00061 # include "perl_colorizer.h"
00062 #endif
00063
00064 #include "qeditor_indenter.h"
00065 #include "simple_indent.h"
00066 #include "python_indent.h"
00067 #include "cindent.h"
00068 #include "pascal_indent.h"
00069 #include "ada_indent.h"
00070
00071 #include "parenmatcher.h"
00072 #include "paragdata.h"
00073
00074 #include <private/qrichtext_p.h>
00075 #include <qregexp.h>
00076 #include <qmap.h>
00077 #include <qpopupmenu.h>
00078
00079 #include <kdebug.h>
00080 #include <klocale.h>
00081
00082 using namespace std;
00083
00084 int QEditor::backspace_indentation( const QString &s )
00085 {
00086 int tabwidth = tabStop();
00087 int i = 0;
00088 int ind = 0;
00089 while ( i < (int)s.length() ) {
00090 QChar c = s.at( i );
00091 if ( c == ' ' ){
00092 ind++;
00093 } else if ( c == '\t' ){
00094 ind += tabwidth;
00095 } else {
00096 break;
00097 }
00098 ++i;
00099 }
00100 return ind;
00101 }
00102
00103
00104 int QEditor::backspace_indentForLine( int line )
00105 {
00106
00107 int line_ind = backspace_indentation( text(line) );
00108 line_ind = line_ind > 0 ? line_ind-1 : 0;
00109 int ind = 0;
00110
00111 --line;
00112 while( line>=0 ){
00113 QString raw_text = text( line );
00114 QString lineText = raw_text.stripWhiteSpace();
00115 if( !lineText.isEmpty() ){
00116 int new_ind = backspace_indentation( raw_text );
00117 if( new_ind < line_ind ){
00118 ind = new_ind;
00119 break;
00120 }
00121 }
00122 --line;
00123 }
00124 return ind;
00125 }
00126
00127 struct QEditorKey{
00128 int key;
00129 int ascii;
00130 int state;
00131 QString text;
00132 bool autorep;
00133 ushort count;
00134 };
00135
00136 QEditor::QEditor( QWidget* parent, const char* name )
00137 : KTextEdit( parent, name )
00138 {
00139 document()->setUseFormatCollection( FALSE );
00140
00141 parenMatcher = new ParenMatcher();
00142
00143 m_tabIndent = TRUE;
00144 m_backspaceIndent = TRUE;
00145 m_currentLine = -1;
00146 m_tabStop = 8;
00147 m_applicationMenu = 0;
00148 m_recording = FALSE;
00149 m_keys.setAutoDelete( TRUE );
00150
00151 document()->addSelection( ParenMatcher::Match );
00152 document()->addSelection( ParenMatcher::Mismatch );
00153 document()->setSelectionColor( ParenMatcher::Match, QColor( 204, 232, 195 ) );
00154 document()->setSelectionColor( ParenMatcher::Mismatch, Qt::magenta );
00155 document()->setInvertSelectionText( ParenMatcher::Match, FALSE );
00156 document()->setInvertSelectionText( ParenMatcher::Mismatch, FALSE );
00157
00158 document()->addSelection( 1000 );
00159 document()->setSelectionColor( 1000, QColor( 204, 232, 195 ) );
00160
00161 connect( this, SIGNAL(cursorPositionChanged(QTextCursor*) ),
00162 this, SLOT(doMatch(QTextCursor*)) );
00163
00164
00165 }
00166
00167 QEditor::~QEditor()
00168 {
00169 m_keys.clear();
00170 delete( parenMatcher );
00171 }
00172
00173 QPopupMenu* QEditor::createPopupMenu( const QPoint& pt )
00174 {
00175 QPopupMenu* menu = KTextEdit::createPopupMenu( pt );
00176 if( m_applicationMenu ){
00177 menu->insertSeparator();
00178 menu->insertItem( i18n("&Application"), m_applicationMenu );
00179 }
00180 return menu;
00181 }
00182
00183
00184 int QEditor::tabStop() const
00185 {
00186 return m_tabStop;
00187 }
00188
00189 void QEditor::setTabStop( int tabStop )
00190 {
00191 m_tabStop = tabStop;
00192 if( m_tabStop == 0 )
00193 m_tabStop = 8;
00194 }
00195
00196 void QEditor::keyPressEvent( QKeyEvent* e )
00197 {
00198
00199 if( e->key() == Key_Tab ){
00200 if( tabIndentEnabled() ){
00201 int line, col;
00202 getCursorPosition( &line, &col );
00203 QString s = text( line );
00204 if( hasSelectedText() || s.stripWhiteSpace().isEmpty() || !s.mid( col ).stripWhiteSpace().isEmpty() )
00205 indent();
00206 else
00207 insert( "\t" );
00208 } else
00209 insert( "\t" );
00210 e->accept();
00211 } else if( m_electricKeys.contains( e->ascii() ) ){
00212 insert( e->text(), FALSE );
00213 indent();
00214 e->accept();
00215 } else if( e->ascii() == '{' || e->ascii() == '}' ||
00216 e->ascii() == ':' || e->ascii() == '#' ){
00217 insert( e->text(), FALSE );
00218 e->accept();
00219 } else if (e->state() == Qt::ControlButton) {
00220 bool bRemove = false;
00221 switch (e->key()) {
00222 case Qt::Key_Backspace:
00223 bRemove = true;
00224 if (!hasSelectedText()) {
00225 removeSelection();
00226 }
00227 case Qt::Key_Left: {
00228 QTextCursor* cur = textCursor();
00229 if (cur->index() < 1) {
00230 moveCursor( MoveBackward, bRemove ); break;
00231 }
00232 QChar c(cur->paragraph()->at(cur->index()-1)->c);
00233 bool firstMove = true;
00234 if (c.isSpace()) {
00235 while (cur->index() > 0 && (cur->paragraph()->at(cur->index()-1)->c.isSpace() || firstMove)) {
00236 moveCursor( MoveBackward, bRemove ); firstMove = false; cur = textCursor();
00237 }
00238 }
00239 else if (isDelimiter(c)) {
00240 while (cur->index() > 0 && (isDelimiter(cur->paragraph()->at(cur->index()-1)->c) || firstMove)) {
00241 moveCursor( MoveBackward, bRemove ); firstMove = false; cur = textCursor();
00242 }
00243 }
00244 else if (!isDelimiter(c)) {
00245 while (cur->index() > 0 && (!isDelimiter(cur->paragraph()->at(cur->index()-1)->c) || firstMove)) {
00246 moveCursor( MoveBackward, bRemove ); firstMove = false; cur = textCursor();
00247 }
00248 }
00249 }
00250 break;
00251 case Qt::Key_Delete:
00252 bRemove = true;
00253 if (!hasSelectedText()) {
00254 removeSelection();
00255 }
00256 case Qt::Key_Right: {
00257 QTextCursor* cur = textCursor();
00258 if (cur->atParagEnd()) {
00259 moveCursor( MoveForward, bRemove ); break;
00260 }
00261 QChar c(cur->paragraph()->at(cur->index())->c);
00262 bool firstMove = true;
00263 if (c.isSpace()) {
00264 while (!cur->atParagEnd() && (cur->paragraph()->at(cur->index())->c.isSpace() || firstMove)) {
00265 moveCursor( MoveForward, bRemove ); firstMove = false; cur = textCursor();
00266 }
00267 }
00268 else if (!isDelimiter(c)) {
00269 while (!cur->atParagEnd() && (!isDelimiter(cur->paragraph()->at(cur->index())->c) || firstMove)) {
00270 moveCursor( MoveForward, bRemove ); firstMove = false; cur = textCursor();
00271 }
00272 }
00273 else if (isDelimiter(c)) {
00274 while (!cur->atParagEnd() && (isDelimiter(cur->paragraph()->at(cur->index())->c) || firstMove)) {
00275 moveCursor( MoveForward, bRemove ); firstMove = false; cur = textCursor();
00276 }
00277 }
00278 }
00279 break;
00280 default:
00281 KTextEdit::keyPressEvent( e );
00282 break;
00283 }
00284 if (bRemove) {
00285 removeSelectedText();
00286 }
00287 }
00288 else if( e->key() == Key_Backspace ){
00289 if( backspaceIndentEnabled() ){
00290 backspaceIndent( e );
00291 } else {
00292 KTextEdit::keyPressEvent( e );
00293 }
00294 } else {
00295 KTextEdit::keyPressEvent( e );
00296 }
00297 }
00298
00299 void QEditor::doMatch( QTextCursor* c )
00300 {
00301 if( parenMatcher->match(c) ){
00302 repaintChanged();
00303 }
00304 }
00305
00306 void QEditor::doGotoLine( int line )
00307 {
00308 setCursorPosition( line, 0 );
00309 QTextParagraph *p = document()->paragAt( line );
00310 if ( !p )
00311 return;
00312 QTextCursor c( document() );
00313 emit ensureTextIsVisible( p );
00314 c.setParagraph( p );
00315 c.setIndex( 0 );
00316 document()->removeSelection( 1000 );
00317 document()->setSelectionStart( 1000, c );
00318 c.gotoLineEnd();
00319 document()->setSelectionEnd( 1000, c );
00320 viewport()->repaint( FALSE );
00321 }
00322
00323 QTextCursor* QEditor::textCursor() const
00324 {
00325 return KTextEdit::textCursor();
00326 }
00327
00328 QTextDocument* QEditor::document() const
00329 {
00330 return KTextEdit::document();
00331 }
00332
00333 void QEditor::drawCursor( bool visible )
00334 {
00335 KTextEdit::drawCursor( visible );
00336 }
00337
00338 void QEditor::configChanged()
00339 {
00340 updateStyles();
00341
00342 if( QEditorSettings::self()->wordWrap() ){
00343 setWordWrap( QEditor::WidgetWidth );
00344 setHScrollBarMode( QScrollView::AlwaysOff );
00345 setVScrollBarMode( QScrollView::AlwaysOn );
00346 } else {
00347 setWordWrap( QEditor::NoWrap );
00348 setHScrollBarMode( QScrollView::AlwaysOn );
00349 setVScrollBarMode( QScrollView::AlwaysOn );
00350 }
00351
00352 refresh();
00353 }
00354
00355 void QEditor::zoomIn()
00356 {
00357 KTextEdit::zoomIn();
00358 updateStyles();
00359 refresh();
00360 }
00361
00362 void QEditor::zoomOut()
00363 {
00364 KTextEdit::zoomOut();
00365 updateStyles();
00366 refresh();
00367 }
00368
00369 void QEditor::updateStyles()
00370 {
00371
00372 int tabwidth = tabStop();
00373 QSourceColorizer* colorizer = dynamic_cast<QSourceColorizer*>( document()->preProcessor() );
00374 if( colorizer ){
00375 setFont( colorizer->format( 0 )->font() );
00376 setTabStopWidth( colorizer->format(0)->width('x') * tabwidth );
00377 document()->setTabStops( colorizer->format(0)->width('x') * tabwidth );
00378 }
00379 KTextEdit::updateStyles();
00380 }
00381
00382 void QEditor::backspaceIndent( QKeyEvent* e )
00383 {
00384 QTextCursor* c = textCursor();
00385 QTextParagraph* p = c->paragraph();
00386 QString raw_text = text( p->paragId() );
00387 QString line = raw_text.stripWhiteSpace();
00388
00389 if( raw_text.left(c->index()).stripWhiteSpace().isEmpty()
00390 && c->index() > 0 && !hasSelectedText() ){
00391 drawCursor( FALSE );
00392 int oi = backspace_indentation( raw_text );
00393 int ni = backspace_indentForLine( p->paragId() );
00394
00395 if( indenter() )
00396 indenter()->indentLine( p, oi, ni );
00397
00398 int idx = c->index();
00399 if ( idx >= oi )
00400 idx += ni - oi;
00401 else
00402 idx = ni;
00403 c->setIndex( idx );
00404 repaintChanged();
00405 drawCursor( TRUE );
00406 e->accept();
00407 } else {
00408
00409 KTextEdit::keyPressEvent( e );
00410 }
00411 }
00412
00413 bool QEditor::replace( const QString &text, const QString &replace,
00414 bool cs, bool wo, bool forward, bool startAtCursor,
00415 bool replaceAll )
00416 {
00417
00418 bool ok = FALSE;
00419 if ( startAtCursor ) {
00420 ok = find( text, cs, wo, forward );
00421 } else {
00422 int dummy = 0;
00423 ok = find( text, cs, wo, forward, &dummy, &dummy );
00424 }
00425
00426 if ( ok ) {
00427 removeSelectedText();
00428 insert( replace, FALSE, FALSE );
00429 }
00430
00431 if ( !replaceAll || !ok ) {
00432 if ( ok )
00433 setSelection( textCursor()->paragraph()->paragId(),
00434 textCursor()->index() - replace.length(),
00435 textCursor()->paragraph()->paragId(),
00436 textCursor()->index() );
00437 return ok;
00438 }
00439
00440 bool ok2 = TRUE;
00441 while ( ok2 ) {
00442 ok2 = find( text, cs, wo, forward );
00443 if ( ok2 ) {
00444 removeSelectedText();
00445 insert( replace, FALSE, FALSE );
00446 }
00447 }
00448
00449 return TRUE;
00450
00451 }
00452
00453 void QEditor::setDocument( QTextDocument* doc )
00454 {
00455 KTextEdit::setDocument( doc );
00456 }
00457
00458 void QEditor::repaintChanged()
00459 {
00460 KTextEdit::repaintChanged();
00461 }
00462
00463 QString QEditor::textLine( uint line ) const
00464 {
00465 return text( line );
00466 }
00467
00468 void QEditor::setLanguage( const QString& l )
00469 {
00470 kdDebug(9032) << "QEditor::setLanguage(" << l << ")" << endl;
00471 m_language = l;
00472 if( m_language == "c++" ){
00473 setElectricKeys( "{}" );
00474 document()->setPreProcessor( new CppColorizer(this) );
00475 document()->setIndent( new CIndent(this) );
00476 } else if( m_language == "java" ){
00477 setElectricKeys( "{}" );
00478 document()->setPreProcessor( new JavaColorizer(this) );
00479 document()->setIndent( new CIndent(this) );
00480 } else if( m_language == "javascript" ){
00481 setElectricKeys( "{}" );
00482 document()->setPreProcessor( new JSColorizer(this) );
00483 document()->setIndent( new CIndent(this) );
00484 } else if( m_language == "jsp" ){
00485 setElectricKeys( QString::null );
00486 document()->setPreProcessor( new JspColorizer(this) );
00487 document()->setIndent( new SimpleIndent(this) );
00488 } else if( m_language == "csharp" ){
00489 setElectricKeys( "{}" );
00490 document()->setPreProcessor( new CSharpColorizer(this) );
00491 document()->setIndent( new CIndent(this) );
00492 #if defined(HAVE_PERL_MODE)
00493 } else if( m_language == "perl" ){
00494 setElectricKeys( "{}" );
00495 document()->setPreProcessor( new PerlColorizer(this) );
00496 document()->setIndent( new CIndent(this) );
00497 #endif
00498 } else if( m_language == "python" ){
00499 setElectricKeys( QString::null );
00500 document()->setPreProcessor( new PythonColorizer(this) );
00501 document()->setIndent( new PythonIndent(this) );
00502 } else if( m_language == "xml" ){
00503 setElectricKeys( QString::null );
00504 document()->setPreProcessor( new XMLColorizer(this) );
00505 document()->setIndent( new SimpleIndent(this) );
00506 } else if( m_language == "qmake" ){
00507 setElectricKeys( QString::null );
00508 document()->setPreProcessor( new QMakeColorizer(this) );
00509 document()->setIndent( new SimpleIndent(this) );
00510 } else if( m_language == "ocaml" ){
00511 setElectricKeys( QString::null );
00512 document()->setPreProcessor( new OCamlColorizer(this) );
00513 document()->setIndent( new SimpleIndent(this) );
00514 } else if( m_language == "pascal" ){
00515 setElectricKeys( QString::null );
00516 document()->setPreProcessor( new PascalColorizer(this) );
00517 document()->setIndent( new PascalIndent(this) );
00518 } else if( m_language == "ada" ){
00519 setElectricKeys( QString::null );
00520 document()->setPreProcessor( new AdaColorizer(this) );
00521 document()->setIndent( new AdaIndent(this) );
00522 } else if( m_language == "sql" ){
00523 setElectricKeys( QString::null );
00524 document()->setPreProcessor( new SqlColorizer(this) );
00525 document()->setIndent( new SimpleIndent(this) );
00526 } else {
00527 setElectricKeys( QString::null );
00528 document()->setPreProcessor( 0 );
00529 document()->setIndent( new SimpleIndent(this) );
00530 }
00531
00532 configChanged();
00533 sync();
00534 }
00535
00536 QString QEditor::language() const
00537 {
00538 return m_language;
00539 }
00540
00541 void QEditor::setText( const QString& text )
00542 {
00543 setTextFormat( KTextEdit::PlainText );
00544 QString s = text;
00545
00546 KTextEdit::setText( s );
00547 setTextFormat( KTextEdit::AutoText );
00548 }
00549
00550 void QEditor::slotCursorPositionChanged( int line, int column )
00551 {
00552 Q_UNUSED( line );
00553 Q_UNUSED( column );
00554 }
00555
00556 int QEditor::level( int line) const
00557 {
00558 ParagData* data = (ParagData*) document()->paragAt( line )->extraData();
00559 if( data ){
00560 return data->level();
00561 }
00562 return 0;
00563 }
00564
00565
00566 void QEditor::setLevel( int line, int lev )
00567 {
00568 ParagData* data = (ParagData*) document()->paragAt( line )->extraData();
00569 if( data ){
00570 return data->setLevel( lev );
00571 }
00572 }
00573
00574 QSourceColorizer* QEditor::colorizer() const
00575 {
00576 return dynamic_cast<QSourceColorizer*>( document()->preProcessor() );
00577 }
00578
00579 void QEditor::refresh()
00580 {
00581 document()->invalidate();
00582 QTextParagraph* p = document()->firstParagraph();
00583 while( p ){
00584 p->format();
00585 p = p->next();
00586 }
00587 removeSelection( ParenMatcher::Match );
00588 removeSelection( ParenMatcher::Mismatch );
00589 ensureCursorVisible();
00590 repaintContents( false );
00591 }
00592
00593 bool QEditor::event( QEvent* e )
00594 {
00595 if( isRecording() && e->type() == QEvent::KeyPress ){
00596 QKeyEvent* ke = (QKeyEvent*) e;
00597
00598 QEditorKey* k = new QEditorKey;
00599 k->key = ke->key();
00600 k->ascii = ke->ascii();
00601 k->state = ke->state();
00602 k->text = ke->text();
00603 k->autorep = ke->isAutoRepeat();
00604 k->count = ke->count();
00605
00606 m_keys.append( k );
00607 }
00608 return QTextEdit::event( e );
00609 }
00610
00611 void QEditor::startMacro()
00612 {
00613 m_keys.clear();
00614 setIsRecording( TRUE );
00615 }
00616
00617 void QEditor::stopMacro()
00618 {
00619 setIsRecording( FALSE );
00620 }
00621
00622 void QEditor::executeMacro()
00623 {
00624 QPtrListIterator<QEditorKey> it( m_keys );
00625 while( it.current() ){
00626
00627 QEditorKey* k = it.current();
00628 ++it;
00629
00630 QKeyEvent e( QEvent::KeyPress,
00631 k->key,
00632 k->ascii,
00633 k->state,
00634 k->text,
00635 k->autorep,
00636 k->count );
00637 QApplication::sendEvent( this, &e );
00638 }
00639 }
00640
00641 QEditorIndenter* QEditor::indenter() const
00642 {
00643 return dynamic_cast<QEditorIndenter*>( document()->indent() );
00644 }
00645
00646 void QEditor::indent()
00647 {
00648 KTextEdit::indent();
00649 if( !hasSelectedText() && text( textCursor()->paragraph()->paragId() ).stripWhiteSpace().isEmpty() )
00650 moveCursor( MoveLineEnd, false );
00651 }
00652
00653 void QEditor::contentsMouseDoubleClickEvent( QMouseEvent * e )
00654 {
00655 if ( e->button() != Qt::LeftButton ) {
00656 e->ignore();
00657 return;
00658 }
00659
00660
00661 KTextEdit::contentsMouseDoubleClickEvent(e);
00662
00663
00664 int para = 0;
00665 int index = charAt( e->pos(), ¶ );
00666 setCursorPosition(para, index);
00667
00668 QTextCursor* cur = textCursor();
00669 QTextCursor c1 = *cur;
00670 QTextCursor c2 = *cur;
00671 if (c1.paragraph()->at(c1.index())->c.isSpace()) return;
00672
00673
00674 while (c1.index() > 0 && !isDelimiter(c1.paragraph()->at(c1.index()-1)->c)) {
00675 c1.gotoLeft();
00676 }
00677
00678 while ( !isDelimiter(c2.paragraph()->at(c2.index())->c) && !c2.atParagEnd() ) {
00679 c2.gotoRight();
00680 cur->gotoRight();
00681 }
00682
00683 document()->setSelectionStart( QTextDocument::Standard, c1 );
00684 document()->setSelectionEnd( QTextDocument::Standard, c2 );
00685
00686 repaintChanged();
00687 }
00688
00689 bool QEditor::isDelimiter(const QChar& c)
00690 {
00691 if (c == '_') return false;
00692 return !(c.isLetterOrNumber());
00693 }
00694
00695 #include "qeditor.moc"