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"