diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-07-10 15:24:15 -0500 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-07-10 15:24:15 -0500 |
commit | bd0f3345a938b35ce6a12f6150373b0955b8dd12 (patch) | |
tree | 7a520322212d48ebcb9fbe1087e7fca28b76185c /tools/designer/editor | |
download | qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.tar.gz qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.zip |
Add Qt3 development HEAD version
Diffstat (limited to 'tools/designer/editor')
23 files changed, 5480 insertions, 0 deletions
diff --git a/tools/designer/editor/arghintwidget.cpp b/tools/designer/editor/arghintwidget.cpp new file mode 100644 index 0000000..9d600f6 --- /dev/null +++ b/tools/designer/editor/arghintwidget.cpp @@ -0,0 +1,239 @@ +/********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "arghintwidget.h" +#include <qbutton.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qpixmap.h> + +static const char * left_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFFFF", +"+ c #000000", +" ", +" ", +" + ", +" ++ ", +" +++ ", +" ++++ ", +" +++++ ", +" ++++++ ", +" ++++++ ", +" +++++ ", +" ++++ ", +" +++ ", +" ++ ", +" + ", +" ", +" "}; + +static const char * right_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFFFF", +"+ c #000000", +" ", +" ", +" + ", +" ++ ", +" +++ ", +" ++++ ", +" +++++ ", +" ++++++ ", +" ++++++ ", +" +++++ ", +" ++++ ", +" +++ ", +" ++ ", +" + ", +" ", +" "}; + +static const char * left_disabled_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFFFF", +"+ c darkgray", +" ", +" ", +" + ", +" ++ ", +" +++ ", +" ++++ ", +" +++++ ", +" ++++++ ", +" ++++++ ", +" +++++ ", +" ++++ ", +" +++ ", +" ++ ", +" + ", +" ", +" "}; + +static const char * right_disabled_xpm[] = { +"16 16 3 1", +" c None", +". c #FFFFFF", +"+ c darkgray", +" ", +" ", +" + ", +" ++ ", +" +++ ", +" ++++ ", +" +++++ ", +" ++++++ ", +" ++++++ ", +" +++++ ", +" ++++ ", +" +++ ", +" ++ ", +" + ", +" ", +" "}; + +class ArrowButton : public QButton +{ + Q_OBJECT + +public: + enum Dir { Left, Right }; + + ArrowButton( QWidget *parent, const char *name, Dir d ); + void drawButton( QPainter *p ); + +private: + QPixmap pix, pix_disabled; + +}; + +ArrowButton::ArrowButton( QWidget *parent, const char *name, Dir d ) + : QButton( parent, name ) +{ + setFixedSize( 16, 16 ); + if ( d == Left ) { + pix = QPixmap( left_xpm ); + pix_disabled = QPixmap( left_disabled_xpm ); + } else { + pix = QPixmap( right_xpm ); + pix_disabled = QPixmap( right_disabled_xpm ); + } +} + +void ArrowButton::drawButton( QPainter *p ) +{ + if ( isDown() ) + p->fillRect( 0, 0, width(), height(), darkGray ); + else + p->fillRect( 0, 0, width(), height(), lightGray ); + if ( isEnabled() ) + p->drawPixmap( 0, 0, pix ); + else + p->drawPixmap( 0, 0, pix_disabled ); +} + + +ArgHintWidget::ArgHintWidget( QWidget *parent, const char*name ) + : QFrame( parent, name, WType_Popup ), curFunc( 0 ), numFuncs( 0 ) +{ + setFrameStyle( QFrame::Box | QFrame::Plain ); + setLineWidth( 1 ); + setBackgroundColor( white ); + QHBoxLayout *hbox = new QHBoxLayout( this ); + hbox->setMargin( 1 ); + hbox->addWidget( ( prev = new ArrowButton( this, "editor_left_btn", ArrowButton::Left ) ) ); + hbox->addWidget( ( funcLabel = new QLabel( this, "editor_func_lbl" ) ) ); + hbox->addWidget( ( next = new ArrowButton( this, "editor_right_btn", ArrowButton::Right ) ) ); + funcLabel->setBackgroundColor( white ); + funcLabel->setAlignment( AlignCenter ); + connect( prev, SIGNAL( clicked() ), this, SLOT( gotoPrev() ) ); + connect( next, SIGNAL( clicked() ), this, SLOT( gotoNext() ) ); + updateState(); + setFocusPolicy( NoFocus ); + prev->setFocusPolicy( NoFocus ); + next->setFocusPolicy( NoFocus ); + funcLabel->setFocusPolicy( NoFocus ); +} + +void ArgHintWidget::setFunctionText( int func, const QString &text ) +{ + funcs.replace( func, text ); + if ( func == curFunc ) { + funcLabel->clear(); + funcLabel->setText( text ); + } +} + +void ArgHintWidget::setNumFunctions( int num ) +{ + funcs.clear(); + numFuncs = num; + curFunc = 0; + updateState(); +} + +void ArgHintWidget::gotoPrev() +{ + if ( curFunc > 0 ) { + curFunc--; + funcLabel->setText( funcs[ curFunc ] ); + updateState(); + } +} + +void ArgHintWidget::gotoNext() +{ + if ( curFunc < numFuncs - 1 ) { + curFunc++; + funcLabel->setText( funcs[ curFunc ] ); + updateState(); + } +} + +void ArgHintWidget::updateState() +{ + prev->setEnabled( curFunc > 0 ); + next->setEnabled( curFunc < numFuncs - 1 ); +} + +void ArgHintWidget::relayout() +{ + funcLabel->setText( "" ); + funcLabel->setText( funcs[ curFunc ] ); +} + +#include "arghintwidget.moc" diff --git a/tools/designer/editor/arghintwidget.h b/tools/designer/editor/arghintwidget.h new file mode 100644 index 0000000..79a4ed6 --- /dev/null +++ b/tools/designer/editor/arghintwidget.h @@ -0,0 +1,69 @@ +/********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef ARGHINTWIDGET_H +#define ARGHINTWIDGET_H + +#include <qframe.h> + +class QLabel; +class ArrowButton; + +class ArgHintWidget : public QFrame +{ + Q_OBJECT + +public: + ArgHintWidget( QWidget *parent, const char*name ); + + void setFunctionText( int func, const QString &text ); + void setNumFunctions( int num ); + +public slots: + void relayout(); + void gotoPrev(); + void gotoNext(); + +private: + void updateState(); + +private: + int curFunc; + int numFuncs; + QMap<int, QString> funcs; + QLabel *funcLabel; + ArrowButton *prev, *next; + +}; + +#endif diff --git a/tools/designer/editor/browser.cpp b/tools/designer/editor/browser.cpp new file mode 100644 index 0000000..9d8d818 --- /dev/null +++ b/tools/designer/editor/browser.cpp @@ -0,0 +1,149 @@ +/********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "browser.h" +#include "editor.h" +#include <private/qrichtext_p.h> + + +EditorBrowser::EditorBrowser( Editor *e ) + : curEditor( e ), oldHighlightedParag( 0 ) +{ + curEditor = e; + + // ### disabled for now +// curEditor->viewport()->installEventFilter( this ); +// curEditor->installEventFilter( this ); + + QFont fn( curEditor->font() ); + fn.setUnderline( TRUE ); + highlightedFormat = new QTextFormat( fn, blue ); +} + +EditorBrowser::~EditorBrowser() +{ + delete highlightedFormat; +} + +bool EditorBrowser::eventFilter( QObject *o, QEvent *e ) +{ + if ( ::qt_cast<Editor*>(o->parent()) || ::qt_cast<Editor*>(o) ) { + QMouseEvent *me; + QKeyEvent *ke; + switch ( e->type() ) { + case QEvent::MouseMove: + me = (QMouseEvent*)e; + if ( ( me->state() & ControlButton ) == ControlButton ) { + curEditor->viewport()->setCursor( pointingHandCursor ); + QTextCursor c( curEditor->document() ); + curEditor->placeCursor( curEditor->viewportToContents( me->pos() ), &c ); + QTextCursor from, to; + if ( oldHighlightedParag ) { + oldHighlightedParag->setEndState( -1 ); + oldHighlightedParag->format(); + oldHighlightedParag = 0; + } + if ( findCursor( c, from, to ) && from.paragraph() == to.paragraph() ) { + // avoid collision with other selections + for ( int i = 0; i < curEditor->document()->numSelections(); ++i ) + curEditor->document()->removeSelection( i ); + from.paragraph()->setFormat( from.index(), to.index() - from.index() + 1, highlightedFormat, FALSE ); + lastWord = from.paragraph()->string()->toString().mid( from.index(), to.index() - from.index() + 1 ); + oldHighlightedParag = from.paragraph(); + } else { + lastWord = ""; + } + curEditor->repaintChanged(); + return TRUE; + } + break; + case QEvent::MouseButtonPress: { + bool killEvent = !lastWord.isEmpty(); + if ( !lastWord.isEmpty() ) + showHelp( lastWord ); + lastWord = ""; + curEditor->viewport()->setCursor( ibeamCursor ); + if ( oldHighlightedParag ) { + oldHighlightedParag->setEndState( -1 ); + oldHighlightedParag->format(); + curEditor->repaintChanged(); + oldHighlightedParag = 0; + } + if ( killEvent ) + return TRUE; + } break; + case QEvent::KeyRelease: + lastWord = ""; + ke = (QKeyEvent*)e; + if ( ke->key() == Key_Control ) { + curEditor->viewport()->setCursor( ibeamCursor ); + if ( oldHighlightedParag ) { + oldHighlightedParag->setEndState( -1 ); + oldHighlightedParag->format(); + curEditor->repaintChanged(); + oldHighlightedParag = 0; + } + } + default: + break; + } + } + return FALSE; +} + +void EditorBrowser::setCurrentEdior( Editor *e ) +{ + curEditor = e; + curEditor->installEventFilter( this ); +} + +void EditorBrowser::addEditor( Editor *e ) +{ + e->installEventFilter( this ); +} + +bool EditorBrowser::findCursor( const QTextCursor &c, QTextCursor &from, QTextCursor &to ) +{ + from = c; + while ( from.paragraph()->at( from.index() )->c != ' ' && from.paragraph()->at( from.index() )->c != '\t' && from.index() > 0 ) + from.gotoLeft(); + if ( from.paragraph()->at( from.index() )->c == ' ' || from.paragraph()->at( from.index() )->c == '\t' ) + from.gotoRight(); + to = c; + while ( to.paragraph()->at( to.index() )->c != ' ' && to.paragraph()->at( to.index() )->c != '\t' && + to.index() < to.paragraph()->length() - 1 ) + to.gotoRight(); + if ( to.paragraph()->at( to.index() )->c == ' ' || to.paragraph()->at( to.index() )->c == '\t' ) + to.gotoLeft(); + return TRUE; +} diff --git a/tools/designer/editor/browser.h b/tools/designer/editor/browser.h new file mode 100644 index 0000000..a90a9df --- /dev/null +++ b/tools/designer/editor/browser.h @@ -0,0 +1,66 @@ +/********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef BROWSER_H +#define BROWSER_H + +#include <qobject.h> + +class Editor; +class QTextCursor; +class QTextParagraph; +class QTextFormat; + +class EditorBrowser : public QObject +{ + Q_OBJECT + +public: + EditorBrowser( Editor *e ); + ~EditorBrowser(); + + bool eventFilter( QObject *o, QEvent *e ); + virtual void setCurrentEdior( Editor *e ); + virtual void addEditor( Editor *e ); + virtual bool findCursor( const QTextCursor &c, QTextCursor &from, QTextCursor &to ); + virtual void showHelp( const QString & ) {} + +protected: + Editor *curEditor; + QTextParagraph *oldHighlightedParag; + QString lastWord; + QTextFormat *highlightedFormat; + +}; + +#endif diff --git a/tools/designer/editor/cindent.cpp b/tools/designer/editor/cindent.cpp new file mode 100644 index 0000000..545c374 --- /dev/null +++ b/tools/designer/editor/cindent.cpp @@ -0,0 +1,163 @@ +/********************************************************************** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "cindent.h" +#include "qregexp.h" + +extern int indentForBottomLine( const QStringList& program, QChar typedIn ); +extern void setTabSize( int s ); +extern void setIndentSize( int s ); + +CIndent::CIndent() + : QTextIndent(), tabSize( 8 ), indentSize( 4 ), + autoIndent( TRUE ), keepTabs( TRUE ), lastDoc( 0 ) +{ +} + +static int indentation( const QString &s ) +{ + if ( s.simplifyWhiteSpace().length() == 0 ) + return 0; + int i = 0; + int ind = 0; + while ( i < (int)s.length() ) { + QChar c = s.at( i ); + if ( c == ' ' ) + ind++; + else if ( c == '\t' ) + ind += 8; + else + break; + ++i; + } + return ind; +} + +void CIndent::tabify( QString &s ) +{ + if ( !keepTabs ) + return; + int i = 0; + for ( ;; ) { + for ( int j = i; j < (int)s.length(); ++j ) { + if ( s[ j ] != ' ' && s[ j ] != '\t' ) { + if ( j > i ) { + QString t = s.mid( i, j - i ); + int spaces = 0; + for ( int k = 0; k < (int)t.length(); ++k ) + spaces += ( t[ k ] == ' ' ? 1 : tabSize ); + s.remove( i, t.length() ); + int tabs = spaces / tabSize; + spaces = spaces - ( tabSize * tabs ); + QString tmp; + tmp.fill( ' ', spaces ); + if ( spaces > 0 ) + s.insert( i, tmp ); + tmp.fill( '\t', tabs ); + if ( tabs > 0 ) + s.insert( i, tmp ); + } + break; + } + } + i = s.find( '\n', i ); + if ( i == -1 ) + break; + ++i; + } +} + +void CIndent::indentLine( QTextParagraph *p, int &oldIndent, int &newIndent ) +{ + QString indentString; + indentString.fill( ' ', newIndent ); + indentString.append( "a" ); + tabify( indentString ); + indentString.remove( indentString.length() - 1, 1 ); + newIndent = indentString.length(); + oldIndent = 0; + while ( p->length() > 0 && ( p->at( 0 )->c == ' ' || p->at( 0 )->c == '\t' ) ) { + ++oldIndent; + p->remove( 0, 1 ); + } + if ( p->string()->length() == 0 ) + p->append( " " ); + if ( !indentString.isEmpty() ) + p->insert( 0, indentString ); +} + + +void CIndent::indent( QTextDocument *doc, QTextParagraph *p, int *oldIndent, int *newIndent ) +{ + lastDoc = doc; + int oi = indentation( p->string()->toString() ); + QStringList code; + QTextParagraph *parag = doc->firstParagraph(); + while ( parag ) { + code << parag->string()->toString(); + if ( parag == p ) + break; + parag = parag->next(); + } + + int ind = indentForBottomLine( code, QChar::null ); + indentLine( p, oi, ind ); + if ( oldIndent ) + *oldIndent = oi; + if ( newIndent ) + *newIndent = ind; +} + +void CIndent::reindent() +{ + if ( !lastDoc ) + return; + // #### this is sloooooooow (O(n^2)) + QTextParagraph *parag = lastDoc->firstParagraph(); + while ( parag ) { + indent( lastDoc, parag, 0, 0 ); + parag = parag->next(); + } +} + +void CIndent::setTabSize( int ts ) +{ + tabSize = ts; + ::setTabSize( ts ); +} + +void CIndent::setIndentSize( int is ) +{ + indentSize = is; + ::setIndentSize( is ); +} diff --git a/tools/designer/editor/cindent.h b/tools/designer/editor/cindent.h new file mode 100644 index 0000000..b184049 --- /dev/null +++ b/tools/designer/editor/cindent.h @@ -0,0 +1,63 @@ +/********************************************************************** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef CINDENT_H +#define CINDENT_H + +#include <private/qrichtext_p.h> + +class CIndent : public QTextIndent +{ +public: + CIndent(); + virtual ~CIndent() {} + void indent( QTextDocument *doc, QTextParagraph *parag, int *oldIndent, int *newIndent ); + + void setTabSize( int ts ); + void setIndentSize( int is ); + void setAutoIndent( bool ai ) { autoIndent = ai; reindent(); } + void setKeepTabs( bool kt ) { keepTabs = kt; } + +private: + void reindent(); + void indentLine( QTextParagraph *p, int &oldIndent, int &newIndent ); + void tabify( QString &s ); + +public: + int tabSize, indentSize; + bool autoIndent, keepTabs; + QTextDocument *lastDoc; + +}; + +#endif diff --git a/tools/designer/editor/completion.cpp b/tools/designer/editor/completion.cpp new file mode 100644 index 0000000..9ab09fa --- /dev/null +++ b/tools/designer/editor/completion.cpp @@ -0,0 +1,714 @@ +/********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "completion.h" +#include "paragdata.h" +#include "editor.h" +#include <qlistbox.h> +#include <qvbox.h> +#include <qmap.h> +#include <private/qrichtext_p.h> +#include <qapplication.h> +#include <qregexp.h> +#include "arghintwidget.h" +#include <qsizegrip.h> +#include <qtimer.h> + +static QColor getColor( const QString &type ) +{ + if ( type == "function" || type == "slot" || type == "package" ) + return Qt::blue; + else if ( type == "variable" || type == "widget" || type == "dir" ) + return Qt::darkRed; + else if ( type == "object" || type == "class" ) + return Qt::darkBlue; + else if ( type == "property" ) + return Qt::darkGreen; + else if ( type == "enum" ) + return Qt::darkYellow; + return Qt::black; +} + +class CompletionItem : public QListBoxItem +{ +public: + CompletionItem( QListBox *lb, const QString &txt, const QString &t, const QString &p, + const QString &pre, const QString &p2 ) + : QListBoxItem( lb ), type( t ), postfix( p ), prefix( pre ), postfix2( p2 ), + parag( 0 ), lastState( FALSE ) { setText( txt ); } + ~CompletionItem() { delete parag; } + void paint( QPainter *painter ) { + if ( lastState != isSelected() ) { + delete parag; + parag = 0; + } + lastState = isSelected(); + if ( !parag ) + setupParagraph(); + parag->paint( *painter, listBox()->colorGroup() ); + } + + int height( const QListBox * ) const { + if ( !parag ) + ( (CompletionItem*)this )->setupParagraph(); + return parag->rect().height(); + } + int width( const QListBox * ) const { + if ( !parag ) + ( (CompletionItem*)this )->setupParagraph(); + return parag->rect().width() - 2; + } + QString text() const { return QListBoxItem::text() + postfix; } + +private: + void setupParagraph(); + QString type, postfix, prefix, postfix2; + QTextParagraph *parag; + bool lastState; + +}; + +void CompletionItem::setupParagraph() { + if ( !parag ) { + QTextFormatter *formatter; + formatter = new QTextFormatterBreakWords; + formatter->setWrapEnabled( FALSE ); + parag = new QTextParagraph( 0 ); + parag->setTabStops( listBox()->fontMetrics().width( "propertyXXXX" ) ); + parag->pseudoDocument()->pFormatter = formatter; + parag->insert( 0, " " + type + ( type.isEmpty() ? " " : "\t" ) + prefix + + QListBoxItem::text() + postfix + postfix2 ); + bool selCol = isSelected() && listBox()->colorGroup().highlightedText() != listBox()->colorGroup().text(); + QColor sc = selCol ? listBox()->colorGroup().highlightedText() : getColor( type ); + QTextFormat *f1 = parag->formatCollection()->format( listBox()->font(), sc ); + QTextFormat *f3 = parag->formatCollection()->format( listBox()->font(), isSelected() ? + listBox()->colorGroup().highlightedText() : + listBox()->colorGroup().text() ); + QFont f( listBox()->font() ); + f.setBold( TRUE ); + QTextFormat *f2 = + parag->formatCollection()->format( f, isSelected() ? listBox()->colorGroup().highlightedText() : + listBox()->colorGroup().text() ); + parag->setFormat( 1, type.length() + 1, f1 ); + parag->setFormat( type.length() + 2, prefix.length() + QListBoxItem::text().length(), f2 ); + if ( !postfix.isEmpty() ) + parag->setFormat( type.length() + 2 + prefix.length() + QListBoxItem::text().length(), + postfix.length(), f3 ); + parag->setFormat( type.length() + 2 + prefix.length() + QListBoxItem::text().length() + postfix.length(), + postfix2.length(), f3 ); + f1->removeRef(); + f2->removeRef(); + f3->removeRef(); + parag->format(); + } +} + + +EditorCompletion::EditorCompletion( Editor *e ) +{ + enabled = TRUE; + lastDoc = 0; + completionPopup = new QVBox( e->topLevelWidget(), 0, WType_Popup ); + completionPopup->setFrameStyle( QFrame::Box | QFrame::Plain ); + completionPopup->setLineWidth( 1 ); + functionLabel = new ArgHintWidget( e->topLevelWidget(), "editor_function_lbl" ); + functionLabel->hide(); + completionListBox = new QListBox( completionPopup, "editor_completion_lb" ); + completionListBox->setFrameStyle( QFrame::NoFrame ); + completionListBox->installEventFilter( this ); + completionListBox->setHScrollBarMode( QScrollView::AlwaysOn ); + completionListBox->setVScrollBarMode( QScrollView::AlwaysOn ); + completionListBox->setCornerWidget( new QSizeGrip( completionListBox, "editor_cornerwidget" ) ); + completionPopup->installEventFilter( this ); + functionLabel->installEventFilter( this ); + completionPopup->setFocusProxy( completionListBox ); + completionOffset = 0; + curEditor = e; + curEditor->installEventFilter( this ); +} + +EditorCompletion::~EditorCompletion() +{ + delete completionPopup; + delete functionLabel; +} + +void EditorCompletion::addCompletionEntry( const QString &s, QTextDocument *, bool strict ) +{ + QChar key( s[ 0 ] ); + QMap<QChar, QStringList>::Iterator it = completionMap.find( key ); + if ( it == completionMap.end() ) { + completionMap.insert( key, QStringList( s ) ); + } else { + if ( strict ) { + QStringList::Iterator sit; + for ( sit = (*it).begin(); sit != (*it).end(); ) { + QStringList::Iterator it2 = sit; + ++sit; + if ( (*it2).length() > s.length() && (*it2).left( s.length() ) == s ) { + if ( (*it2)[ (int)s.length() ].isLetter() && (*it2)[ (int)s.length() ].upper() != (*it2)[ (int)s.length() ] ) + return; + } else if ( s.length() > (*it2).length() && s.left( (*it2).length() ) == *it2 ) { + if ( s[ (int)(*it2).length() ].isLetter() && s[ (int)(*it2).length() ].upper() != s[ (int)(*it2).length() ] ) + (*it).remove( it2 ); + } + } + } + (*it).append( s ); + } +} + +QValueList<CompletionEntry> EditorCompletion::completionList( const QString &s, QTextDocument *doc ) const +{ + if ( doc ) + ( (EditorCompletion*)this )->updateCompletionMap( doc ); + + QChar key( s[ 0 ] ); + QMap<QChar, QStringList>::ConstIterator it = completionMap.find( key ); + if ( it == completionMap.end() ) + return QValueList<CompletionEntry>(); + QStringList::ConstIterator it2 = (*it).begin(); + QValueList<CompletionEntry> lst; + int len = s.length(); + for ( ; it2 != (*it).end(); ++it2 ) { + CompletionEntry c; + c.type = ""; + c.text = *it2; + c.postfix = ""; + c.prefix = ""; + c.postfix2 = ""; + if ( (int)(*it2).length() > len && (*it2).left( len ) == s && lst.find( c ) == lst.end() ) + lst << c; + } + + return lst; +} + +void EditorCompletion::updateCompletionMap( QTextDocument *doc ) +{ + bool strict = TRUE; + if ( doc != lastDoc ) + strict = FALSE; + lastDoc = doc; + QTextParagraph *s = doc->firstParagraph(); + if ( !s->extraData() ) + s->setExtraData( new ParagData ); + while ( s ) { + if ( s->length() == ( (ParagData*)s->extraData() )->lastLengthForCompletion ) { + s = s->next(); + continue; + } + + QChar c; + QString buffer; + for ( int i = 0; i < s->length(); ++i ) { + c = s->at( i )->c; + if ( c.isLetter() || c.isNumber() || c == '_' || c == '#' ) { + buffer += c; + } else { + addCompletionEntry( buffer, doc, strict ); + buffer = QString::null; + } + } + if ( !buffer.isEmpty() ) + addCompletionEntry( buffer, doc, strict ); + + ( (ParagData*)s->extraData() )->lastLengthForCompletion = s->length(); + s = s->next(); + } +} + +bool EditorCompletion::doCompletion() +{ + searchString = ""; + if ( !curEditor ) + return FALSE; + + QTextCursor *cursor = curEditor->textCursor(); + QTextDocument *doc = curEditor->document(); + + if ( cursor->index() > 0 && cursor->paragraph()->at( cursor->index() - 1 )->c == '.' && + ( cursor->index() == 1 || cursor->paragraph()->at( cursor->index() - 2 )->c != '.' ) ) + return doObjectCompletion(); + + int idx = cursor->index(); + if ( idx == 0 ) + return FALSE; + QChar c = cursor->paragraph()->at( idx - 1 )->c; + if ( !c.isLetter() && !c.isNumber() && c != '_' && c != '#' ) + return FALSE; + + QString s; + idx--; + completionOffset = 1; + for (;;) { + s.prepend( QString( cursor->paragraph()->at( idx )->c ) ); + idx--; + if ( idx < 0 ) + break; + if ( !cursor->paragraph()->at( idx )->c.isLetter() && + !cursor->paragraph()->at( idx )->c.isNumber() && + cursor->paragraph()->at( idx )->c != '_' && + cursor->paragraph()->at( idx )->c != '#' ) + break; + completionOffset++; + } + + searchString = s; + + QValueList<CompletionEntry> lst( completionList( s, doc ) ); + if ( lst.count() > 1 ) { + QTextStringChar *chr = cursor->paragraph()->at( cursor->index() ); + int h = cursor->paragraph()->lineHeightOfChar( cursor->index() ); + int x = cursor->paragraph()->rect().x() + chr->x; + int y, dummy; + cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y ); + y += cursor->paragraph()->rect().y(); + completionListBox->clear(); + for ( QValueList<CompletionEntry>::ConstIterator it = lst.begin(); it != lst.end(); ++it ) + (void)new CompletionItem( completionListBox, (*it).text, (*it).type, (*it).postfix, + (*it).prefix, (*it).postfix2 ); + cList = lst; + completionPopup->resize( completionListBox->sizeHint() + + QSize( completionListBox->verticalScrollBar()->width() + 4, + completionListBox->horizontalScrollBar()->height() + 4 ) ); + completionListBox->setCurrentItem( 0 ); + completionListBox->setFocus(); + if ( curEditor->mapToGlobal( QPoint( 0, y ) ).y() + h + completionPopup->height() < QApplication::desktop()->height() ) + completionPopup->move( curEditor->mapToGlobal( curEditor-> + contentsToViewport( QPoint( x, y + h ) ) ) ); + else + completionPopup->move( curEditor->mapToGlobal( curEditor-> + contentsToViewport( QPoint( x, y - completionPopup->height() ) ) ) ); + completionPopup->show(); + } else if ( lst.count() == 1 ) { + curEditor->insert( lst.first().text.mid( completionOffset, 0xFFFFFF ), + (uint) ( QTextEdit::RedoIndentation | + QTextEdit::CheckNewLines | + QTextEdit::RemoveSelected ) ); + } else { + return FALSE; + } + + return TRUE; +} + +bool EditorCompletion::eventFilter( QObject *o, QEvent *e ) +{ + if ( !enabled ) + return FALSE; + if ( e->type() == QEvent::KeyPress && ::qt_cast<Editor*>(o)) { + curEditor = (Editor*)o; + QKeyEvent *ke = (QKeyEvent*)e; + if ( ke->key() == Key_Tab ) { + QString s = curEditor->textCursor()->paragraph()->string()->toString(). + left( curEditor->textCursor()->index() ); + if ( curEditor->document()->hasSelection( QTextDocument::Standard ) || + s.simplifyWhiteSpace().isEmpty() ) { + if ( curEditor->document()->indent() ) { + curEditor->indent(); + int i = 0; + for ( ; i < curEditor->textCursor()->paragraph()->length() - 1; ++i ) { + if ( curEditor->textCursor()->paragraph()->at( i )->c != ' ' && + curEditor->textCursor()->paragraph()->at( i )->c != '\t' ) + break; + } + curEditor->drawCursor( FALSE ); + curEditor->textCursor()->setIndex( i ); + curEditor->drawCursor( TRUE ); + } else { + curEditor->insert( "\t" ); + } + return TRUE; + } + } + + if ( functionLabel->isVisible() ) { + if ( ke->key() == Key_Up && ( ke->state() & ControlButton ) == ControlButton ) { + functionLabel->gotoPrev(); + return TRUE; + } else if ( ke->key() == Key_Down && ( ke->state() & ControlButton ) == ControlButton ) { + functionLabel->gotoNext(); + return TRUE; + } + } + + if ( ke->text().length() && !( ke->state() & AltButton ) && + ( !ke->ascii() || ke->ascii() >= 32 ) || + ( ke->text() == "\t" && !( ke->state() & ControlButton ) ) ) { + if ( ke->key() == Key_Tab ) { + if ( curEditor->textCursor()->index() == 0 && + curEditor->textCursor()->paragraph()->isListItem() ) + return FALSE; + if ( doCompletion() ) + return TRUE; + } else if ( ke->key() == Key_Period && + ( curEditor->textCursor()->index() == 0 || + curEditor->textCursor()->paragraph()->at( curEditor->textCursor()->index() - 1 )->c != '.' ) + || + ke->key() == Key_Greater && + curEditor->textCursor()->index() > 0 && + curEditor->textCursor()->paragraph()->at( curEditor->textCursor()->index() - 1 )->c == '-' ) { + doObjectCompletion(); + } else { + if ( !doArgumentHint( ke->text() == "(" ) ) + functionLabel->hide(); + } + } + } else if ( o == completionPopup || o == completionListBox || + o == completionListBox->viewport() ) { + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *ke = (QKeyEvent*)e; + if ( ke->key() == Key_Enter || ke->key() == Key_Return || ke->key() == Key_Tab ) { + if ( ke->key() == Key_Tab && completionListBox->count() > 1 && + completionListBox->currentItem() < (int)completionListBox->count() - 1 ) { + completionListBox->setCurrentItem( completionListBox->currentItem() + 1 ); + return TRUE; + } + completeCompletion(); + return TRUE; + } else if ( ke->key() == Key_Left || ke->key() == Key_Right || + ke->key() == Key_Up || ke->key() == Key_Down || + ke->key() == Key_Home || ke->key() == Key_End || + ke->key() == Key_Prior || ke->key() == Key_Next ) { + return FALSE; + } else if ( ke->key() != Key_Shift && ke->key() != Key_Control && + ke->key() != Key_Alt ) { + int l = searchString.length(); + if ( ke->key() == Key_Backspace ) { + searchString.remove( searchString.length() - 1, 1 ); + } else { + searchString += ke->text(); + l = 1; + } + if ( !l || !continueComplete() ) { + completionPopup->close(); + curEditor->setFocus(); + } + QApplication::sendEvent( curEditor, e ); + return TRUE; + } + } else if ( e->type() == QEvent::MouseButtonDblClick ) { + completeCompletion(); + return TRUE; + } + } + if ( o == functionLabel || ::qt_cast<Editor*>(o) && functionLabel->isVisible() ) { + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *ke = (QKeyEvent*)e; + if ( ke->key() == Key_Escape ) { + functionLabel->hide(); + } else { + if ( !doArgumentHint( ke->text() == "(" ) ) + functionLabel->hide(); + if ( o == functionLabel ) { + QApplication::sendEvent( curEditor, e ); + return TRUE; + } + } + } + } + return FALSE; +} + +void EditorCompletion::completeCompletion() +{ + int idx = curEditor->textCursor()->index(); + QString s = completionListBox->currentText().mid( searchString.length() ); + curEditor->insert( s, (uint) ( QTextEdit::RedoIndentation | + QTextEdit::CheckNewLines | + QTextEdit::RemoveSelected ) ); + int i = s.find( '(' ); + completionPopup->close(); + curEditor->setFocus(); + if ( i != -1 && i < (int)s.length() ) { + curEditor->setCursorPosition( curEditor->textCursor()->paragraph()->paragId(), idx + i + 1 ); + doArgumentHint( FALSE ); + } +} + +void EditorCompletion::setCurrentEdior( Editor *e ) +{ + curEditor = e; + curEditor->installEventFilter( this ); +} + +void EditorCompletion::addEditor( Editor *e ) +{ + e->installEventFilter( this ); +} + +bool EditorCompletion::doObjectCompletion() +{ + searchString = ""; + QString object; + int i = curEditor->textCursor()->index(); + i--; + QTextParagraph *p = curEditor->textCursor()->paragraph(); + for (;;) { + if ( i < 0 ) + break; + if ( p->at( i )->c == ' ' || p->at( i )->c == '\t' ) + break; + object.prepend( p->at( i )->c ); + i--; + } + + if ( object[ (int)object.length() - 1 ] == '-' ) + object.remove( object.length() - 1, 1 ); + + if ( object.isEmpty() ) + return FALSE; + return doObjectCompletion( object ); +} + +bool EditorCompletion::doObjectCompletion( const QString & ) +{ + return FALSE; +} + +static void strip( QString &txt ) +{ + int i = txt.find( "(" ); + if ( i == -1 ) + return; + txt = txt.left( i ); +} + +bool EditorCompletion::continueComplete() +{ + if ( searchString.isEmpty() ) { + completionListBox->clear(); + for ( QValueList<CompletionEntry>::ConstIterator it = cList.begin(); it != cList.end(); ++it ) + (void)new CompletionItem( completionListBox, (*it).text, (*it).type, + (*it).postfix, (*it).prefix, (*it).postfix2 ); + completionListBox->setCurrentItem( 0 ); + completionListBox->setSelected( completionListBox->currentItem(), TRUE ); + return TRUE; + } + + QListBoxItem *i = completionListBox->findItem( searchString ); + if ( !i ) + return FALSE; + + QString txt1 = i->text(); + QString txt2 = searchString; + strip( txt1 ); + strip( txt2 ); + if ( txt1 == txt2 && !i->next() ) + return FALSE; + + QValueList<CompletionEntry> res; + for ( QValueList<CompletionEntry>::ConstIterator it = cList.begin(); it != cList.end(); ++it ) { + if ( (*it).text.left( searchString.length() ) == searchString ) + res << *it; + } + if ( res.isEmpty() ) + return FALSE; + completionListBox->clear(); + for ( QValueList<CompletionEntry>::ConstIterator it2 = res.begin(); it2 != res.end(); ++it2 ) + (void)new CompletionItem( completionListBox, (*it2).text, (*it2).type, + (*it2).postfix, (*it2).prefix, (*it2).postfix2 ); + completionListBox->setCurrentItem( 0 ); + completionListBox->setSelected( completionListBox->currentItem(), TRUE ); + return TRUE; +} + +bool EditorCompletion::doArgumentHint( bool useIndex ) +{ + QTextCursor *cursor = curEditor->textCursor(); + int i = cursor->index() ; + if ( !useIndex ) { + bool foundParen = FALSE; + int closeParens = 0; + while ( i >= 0 ) { + if ( cursor->paragraph()->at( i )->c == ')' && i != cursor->index() ) + closeParens++; + if ( cursor->paragraph()->at( i )->c == '(' ) { + closeParens--; + if ( closeParens == -1 ) { + foundParen = TRUE; + break; + } + } + --i; + } + + if ( !foundParen ) + return FALSE; + } + int j = i - 1; + bool foundSpace = FALSE; + bool foundNonSpace = FALSE; + while ( j >= 0 ) { + if ( foundNonSpace && ( cursor->paragraph()->at( j )->c == ' ' || cursor->paragraph()->at( j )->c == ',' ) ) { + foundSpace = TRUE; + break; + } + if ( !foundNonSpace && ( cursor->paragraph()->at( j )->c != ' ' || cursor->paragraph()->at( j )->c != ',' ) ) + foundNonSpace = TRUE; + --j; + } + if ( foundSpace ) + ++j; + j = QMAX( j, 0 ); + QString function( cursor->paragraph()->string()->toString().mid( j, i - j + 1 ) ); + QString part = cursor->paragraph()->string()->toString().mid( j, cursor->index() - j + 1 ); + function = function.simplifyWhiteSpace(); + for (;;) { + if ( function[ (int)function.length() - 1 ] == '(' ) { + function.remove( function.length() - 1, 1 ); + function = function.simplifyWhiteSpace(); + } else if ( function[ (int)function.length() - 1 ] == ')' ) { + function.remove( function.length() - 1, 1 ); + function = function.simplifyWhiteSpace(); + } else { + break; + } + } + + QChar sep; + QString pre, post; + QValueList<QStringList> argl = functionParameters( function, sep, pre, post ); + if ( argl.isEmpty() ) + return FALSE; + + QString label; + int w = 0; + int num = 0; + if ( !functionLabel->isVisible() ) + functionLabel->setNumFunctions( (int)argl.count() ); + for ( QValueList<QStringList>::Iterator vit = argl.begin(); vit != argl.end(); ++vit, ++num ) { + QStringList args = *vit; + int argNum = 0; + int inParen = 0; + for ( int k = 0; k < (int)part.length(); ++k ) { + if ( part[ k ] == sep && inParen < 2 ) + argNum++; + if ( part[ k ] == '(' ) + inParen++; + if ( part[ k ] == ')' ) + inParen--; + } + + QString func = function; + int pnt = -1; + pnt = func.findRev( '.' ); + if ( pnt == -1 ) + func.findRev( '>' ); + if ( pnt != -1 ) + func = func.mid( pnt + 1 ); + + QString s = func + "( "; + if ( s[ 0 ] == '\"' ) + s.remove( (uint)0, 1 ); + i = 0; + for ( QStringList::Iterator it = args.begin(); it != args.end(); ++it, ++i ) { + if ( i == argNum ) + s += "<b>" + *it + "</b>"; + else + s += *it; + if ( i < (int)args.count() - 1 ) + s += ", "; + else + s += " "; + } + s += ")"; + s.prepend( pre ); + s.append( post ); + label += "<p>" + s + "</p>"; + functionLabel->setFunctionText( num, s ); + w = QMAX( w, functionLabel->fontMetrics().width( s ) + 10 ); + } + w += 16; + if ( label.isEmpty() ) + return FALSE; + if ( functionLabel->isVisible() ) { + functionLabel->resize( w + 50, QMAX( functionLabel->fontMetrics().height(), 16 ) ); + } else { + QTextStringChar *chr = cursor->paragraph()->at( cursor->index() ); + int h = cursor->paragraph()->lineHeightOfChar( cursor->index() ); + int x = cursor->paragraph()->rect().x() + chr->x; + int y, dummy; + cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y ); + y += cursor->paragraph()->rect().y(); + functionLabel->resize( w + 50, QMAX( functionLabel->fontMetrics().height(), 16 ) ); + functionLabel->move( curEditor->mapToGlobal( curEditor->contentsToViewport( QPoint( x, y + h ) ) ) ); + if ( functionLabel->x() + functionLabel->width() > QApplication::desktop()->width() ) + functionLabel->move( QMAX( 0, QApplication::desktop()->width() - functionLabel->width() ), + functionLabel->y() ); + functionLabel->show(); + curEditor->setFocus(); + } + QTimer::singleShot( 0, functionLabel, SLOT( relayout() ) ); + + return TRUE; +} + +QValueList<QStringList> EditorCompletion::functionParameters( const QString &, QChar &, QString &, QString & ) +{ + return QValueList<QStringList>(); +} + +void EditorCompletion::setContext( QObject * ) +{ +} + +void EditorCompletion::showCompletion( const QValueList<CompletionEntry> &lst ) +{ + QTextCursor *cursor = curEditor->textCursor(); + QTextStringChar *chr = cursor->paragraph()->at( cursor->index() ); + int h = cursor->paragraph()->lineHeightOfChar( cursor->index() ); + int x = cursor->paragraph()->rect().x() + chr->x; + int y, dummy; + cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y ); + y += cursor->paragraph()->rect().y(); + completionListBox->clear(); + for ( QValueList<CompletionEntry>::ConstIterator it = lst.begin(); it != lst.end(); ++it ) + (void)new CompletionItem( completionListBox, (*it).text, (*it).type, + (*it).postfix, (*it).prefix, (*it).postfix2 ); + cList = lst; + completionPopup->resize( completionListBox->sizeHint() + + QSize( completionListBox->verticalScrollBar()->width() + 4, + completionListBox->horizontalScrollBar()->height() + 4 ) ); + completionListBox->setCurrentItem( 0 ); + completionListBox->setFocus(); + if ( curEditor->mapToGlobal( QPoint( 0, y ) ).y() + h + completionPopup->height() < QApplication::desktop()->height() ) + completionPopup->move( curEditor->mapToGlobal( curEditor-> + contentsToViewport( QPoint( x, y + h ) ) ) ); + else + completionPopup->move( curEditor->mapToGlobal( curEditor-> + contentsToViewport( QPoint( x, y - completionPopup->height() ) ) ) ); + + completionPopup->show(); +} diff --git a/tools/designer/editor/completion.h b/tools/designer/editor/completion.h new file mode 100644 index 0000000..94acf6b --- /dev/null +++ b/tools/designer/editor/completion.h @@ -0,0 +1,110 @@ + /********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef COMPLETION_H +#define COMPLETION_H + +#include <qstring.h> +#include <qstringlist.h> +#include <qobject.h> +#include <qmap.h> + +class QTextDocument; +class Editor; +class QVBox; +class QListBox; +class ArgHintWidget; + +struct CompletionEntry +{ + QString type; + QString text; + QString postfix; + QString prefix; + QString postfix2; + + bool operator==( const CompletionEntry &c ) const { + return ( c.type == type && + c.text == text && + c.postfix == postfix && + c.prefix == prefix && + c.postfix2 == postfix2 ); + } +}; + +class EditorCompletion : public QObject +{ + Q_OBJECT + +public: + EditorCompletion( Editor *e ); + ~EditorCompletion(); + + virtual void addCompletionEntry( const QString &s, QTextDocument *doc, bool strict ); + virtual QValueList<CompletionEntry> completionList( const QString &s, QTextDocument *doc ) const; + virtual void updateCompletionMap( QTextDocument *doc ); + + bool eventFilter( QObject *o, QEvent *e ); + virtual void setCurrentEdior( Editor *e ); + virtual bool doCompletion(); + virtual bool doObjectCompletion(); + virtual bool doObjectCompletion( const QString &object ); + virtual bool doArgumentHint( bool useIndex ); + + virtual void addEditor( Editor *e ); + virtual QValueList<QStringList> functionParameters( const QString &func, QChar &, QString &prefix, QString &postfix ); + + virtual void setContext( QObject *this_ ); + + void setEnabled( bool b ) { enabled = b; } + +protected: + virtual bool continueComplete(); + virtual void showCompletion( const QValueList<CompletionEntry> &lst ); + virtual void completeCompletion(); + +protected: + QVBox *completionPopup; + QListBox *completionListBox; + ArgHintWidget *functionLabel; + int completionOffset; + Editor *curEditor; + QString searchString; + QValueList<CompletionEntry> cList; + QMap<QChar, QStringList> completionMap; + bool enabled; + QTextDocument *lastDoc; + +}; + +#endif diff --git a/tools/designer/editor/conf.cpp b/tools/designer/editor/conf.cpp new file mode 100644 index 0000000..11e4f0a --- /dev/null +++ b/tools/designer/editor/conf.cpp @@ -0,0 +1,269 @@ +/********************************************************************** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "conf.h" +#include <qapplication.h> +#include <qfont.h> +#include <qcolor.h> +#include <qsettings.h> + +QMap<QString, ConfigStyle> Config::defaultStyles() +{ + ConfigStyle s; + QMap<QString, ConfigStyle> styles; + int normalSize = qApp->font().pointSize(); + QString normalFamily = qApp->font().family(); + QString commentFamily = "times"; + int normalWeight = qApp->font().weight(); + + s.font = QFont( normalFamily, normalSize, normalWeight ); + s.color = Qt::black; + styles.insert( "Standard", s ); + + s.font = QFont( commentFamily, normalSize, normalWeight, TRUE ); + s.color = Qt::red; + styles.insert( "Comment", s ); + + s.font = QFont( normalFamily, normalSize, normalWeight ); + s.color = Qt::blue; + styles.insert( "Number", s ); + + s.font = QFont( normalFamily, normalSize, normalWeight ); + s.color = Qt::darkGreen; + styles.insert( "String", s ); + + s.font = QFont( normalFamily, normalSize, normalWeight ); + s.color = Qt::darkMagenta; + styles.insert( "Type", s ); + + s.font = QFont( normalFamily, normalSize, normalWeight ); + s.color = Qt::darkYellow; + styles.insert( "Keyword", s ); + + s.font = QFont( normalFamily, normalSize, normalWeight ); + s.color = Qt::darkBlue; + styles.insert( "Preprocessor", s ); + + s.font = QFont( normalFamily, normalSize, normalWeight ); + s.color = Qt::darkRed; + styles.insert( "Label", s ); + + return styles; +} + +QMap<QString, ConfigStyle> Config::readStyles( const QString &path ) +{ + QMap<QString, ConfigStyle> styles; + styles = defaultStyles(); + + QString family; + int size = 10; + bool bold = FALSE, italic = FALSE, underline = FALSE; + int red = 0, green = 0, blue = 0; + + QString elements[] = { + "Comment", + "Number", + "String", + "Type", + "Keyword", + "Preprocessor", + "Label", + "Standard", + QString::null + }; + + for ( int i = 0; elements[ i ] != QString::null; ++i ) { + QSettings settings; + bool ok = TRUE; + for (;;) { + family = settings.readEntry( path + elements[ i ] + "/family", QString::null, &ok ); + if ( !ok ) + break; + size = settings.readNumEntry( path + elements[ i ] + "/size", 10, &ok ); + if ( !ok ) + break; + bold = settings.readBoolEntry( path + elements[ i ] + "/bold", FALSE, &ok ); + if ( !ok ) + break; + italic = settings.readBoolEntry( path + elements[ i ] + "/italic", FALSE, &ok ); + if ( !ok ) + break; + underline = settings.readBoolEntry( path + elements[ i ] + "/underline", FALSE, &ok ); + if ( !ok ) + break; + red = settings.readNumEntry( path + elements[ i ] + "/red", 0, &ok ); + if ( !ok ) + break; + green = settings.readNumEntry( path + elements[ i ] + "/green", 0, &ok ); + if ( !ok ) + break; + blue = settings.readNumEntry( path + elements[ i ] + "/blue", 0, &ok ); + if ( !ok ) + break; + break; + } + if ( !ok ) + continue; + QFont f( family ); + f.setPointSize( size ); + f.setBold( bold ); + f.setItalic( italic ); + f.setUnderline( underline ); + QColor c( red, green, blue ); + ConfigStyle s; + s.font = f; + s.color = c; + styles.remove( elements[ i ] ); + styles.insert( elements[ i ], s ); + } + return styles; +} + +void Config::saveStyles( const QMap<QString, ConfigStyle> &styles, const QString &path ) +{ + QString elements[] = { + "Comment", + "Number", + "String", + "Type", + "Keyword", + "Preprocessor", + "Label", + "Standard", + QString::null + }; + + QSettings settings; + for ( int i = 0; elements[ i ] != QString::null; ++i ) { + settings.writeEntry( path + "/" + elements[ i ] + "/family", styles[ elements[ i ] ].font.family() ); + settings.writeEntry( path + "/" + elements[ i ] + "/size", styles[ elements[ i ] ].font.pointSize() ); + settings.writeEntry( path + "/" + elements[ i ] + "/bold", styles[ elements[ i ] ].font.bold() ); + settings.writeEntry( path + "/" + elements[ i ] + "/italic", styles[ elements[ i ] ].font.italic() ); + settings.writeEntry( path + "/" + elements[ i ] + "/underline", styles[ elements[ i ] ].font.underline() ); + settings.writeEntry( path + "/" + elements[ i ] + "/red", styles[ elements[ i ] ].color.red() ); + settings.writeEntry( path + "/" + elements[ i ] + "/green", styles[ elements[ i ] ].color.green() ); + settings.writeEntry( path + "/" + elements[ i ] + "/blue", styles[ elements[ i ] ].color.blue() ); + } +} + +bool Config::completion( const QString &path ) +{ + QSettings settings; + bool ret = settings.readBoolEntry( path + "/completion", TRUE ); + return ret; +} + +bool Config::wordWrap( const QString &path ) +{ + QSettings settings; + bool ret = settings.readBoolEntry( path + "/wordWrap", TRUE ); + return ret; +} + +bool Config::parenMatching( const QString &path ) +{ + QSettings settings; + bool ret = settings.readBoolEntry( path + "/parenMatching", TRUE ); + return ret; +} + +int Config::indentTabSize( const QString &path ) +{ + QSettings settings; + int ret = settings.readNumEntry( path + "/indentTabSize", 8 ); + return ret; +} + +int Config::indentIndentSize( const QString &path ) +{ + QSettings settings; + int ret = settings.readNumEntry( path + "/indentIndentSize", 4 ); + return ret; +} + +bool Config::indentKeepTabs( const QString &path ) +{ + QSettings settings; + bool ret = settings.readBoolEntry( path + "/indentKeepTabs", TRUE ); + return ret; +} + +bool Config::indentAutoIndent( const QString &path ) +{ + QSettings settings; + bool ret = settings.readBoolEntry( path + "/indentAutoIndent", TRUE ); + return ret; +} + +void Config::setCompletion( bool b, const QString &path ) +{ + QSettings settings; + settings.writeEntry( path + "/completion", b ); +} + +void Config::setWordWrap( bool b, const QString &path ) +{ + QSettings settings; + settings.writeEntry( path + "/wordWrap", b ); +} + +void Config::setParenMatching( bool b,const QString &path ) +{ + QSettings settings; + settings.writeEntry( path + "/parenMatching", b ); +} + +void Config::setIndentTabSize( int s, const QString &path ) +{ + QSettings settings; + settings.writeEntry( path + "/indentTabSize", s ); +} + +void Config::setIndentIndentSize( int s, const QString &path ) +{ + QSettings settings; + settings.writeEntry( path + "/indentIndentSize", s ); +} + +void Config::setIndentKeepTabs( bool b, const QString &path ) +{ + QSettings settings; + settings.writeEntry( path + "/indentKeepTabs", b ); +} + +void Config::setIndentAutoIndent( bool b, const QString &path ) +{ + QSettings settings; + settings.writeEntry( path + "/indentAutoIndent", b ); +} diff --git a/tools/designer/editor/conf.h b/tools/designer/editor/conf.h new file mode 100644 index 0000000..fe5111f --- /dev/null +++ b/tools/designer/editor/conf.h @@ -0,0 +1,75 @@ +/********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef CONF_H +#define CONF_H + +#include <qfont.h> +#include <qcolor.h> +#include <qmap.h> + +struct ConfigStyle +{ + QFont font; + QColor color; + + Q_DUMMY_COMPARISON_OPERATOR( ConfigStyle ) +}; + +struct Config +{ + QMap<QString, ConfigStyle> styles; + bool hasCompletion, hasParenMatching, hasWordWrap; + + static QMap<QString, ConfigStyle> defaultStyles(); + static QMap<QString, ConfigStyle> readStyles( const QString &path ); + static void saveStyles( const QMap<QString, ConfigStyle> &styles, const QString &path ); + static bool completion( const QString &path ); + static bool wordWrap( const QString &path ); + static bool parenMatching( const QString &path ); + static int indentTabSize( const QString &path ); + static int indentIndentSize( const QString &path ); + static bool indentKeepTabs( const QString &path ); + static bool indentAutoIndent( const QString &path ); + + static void setCompletion( bool b, const QString &path ); + static void setWordWrap( bool b, const QString &path ); + static void setParenMatching( bool b,const QString &path ); + static void setIndentTabSize( int s, const QString &path ); + static void setIndentIndentSize( int s, const QString &path ); + static void setIndentKeepTabs( bool b, const QString &path ); + static void setIndentAutoIndent( bool b, const QString &path ); + +}; + +#endif diff --git a/tools/designer/editor/editor.cpp b/tools/designer/editor/editor.cpp new file mode 100644 index 0000000..e75f052 --- /dev/null +++ b/tools/designer/editor/editor.cpp @@ -0,0 +1,251 @@ + /********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "editor.h" +#include "parenmatcher.h" +#include <qfile.h> +#include <private/qrichtext_p.h> +#include "conf.h" +#include <qapplication.h> +#include <qpopupmenu.h> +#include <qaccel.h> + +Editor::Editor( const QString &fn, QWidget *parent, const char *name ) + : QTextEdit( parent, name ), hasError( FALSE ) +{ + document()->setFormatter( new QTextFormatterBreakInWords ); + if ( !fn.isEmpty() ) + load( fn ); + setHScrollBarMode( QScrollView::AlwaysOff ); + setVScrollBarMode( QScrollView::AlwaysOn ); + document()->setUseFormatCollection( FALSE ); + parenMatcher = new ParenMatcher; + connect( this, SIGNAL( cursorPositionChanged( QTextCursor * ) ), + this, SLOT( cursorPosChanged( QTextCursor * ) ) ); + cfg = new Config; + document()->addSelection( Error ); + document()->addSelection( Step ); + document()->setSelectionColor( Error, red ); + document()->setSelectionColor( Step, yellow ); + document()->setInvertSelectionText( Error, FALSE ); + document()->setInvertSelectionText( Step, FALSE ); + document()->addSelection( ParenMatcher::Match ); + document()->addSelection( ParenMatcher::Mismatch ); + document()->setSelectionColor( ParenMatcher::Match, QColor( 204, 232, 195 ) ); + document()->setSelectionColor( ParenMatcher::Mismatch, Qt::magenta ); + document()->setInvertSelectionText( ParenMatcher::Match, FALSE ); + document()->setInvertSelectionText( ParenMatcher::Mismatch, FALSE ); + + accelComment = new QAccel( this ); + accelComment->connectItem( accelComment->insertItem( ALT + Key_C ), + this, SLOT( commentSelection() ) ); + accelUncomment = new QAccel( this ); + accelUncomment->connectItem( accelUncomment->insertItem( ALT + Key_U ), + this, SLOT( uncommentSelection() ) ); + editable = TRUE; +} + +Editor::~Editor() +{ + delete cfg; + delete parenMatcher; +} + +void Editor::cursorPosChanged( QTextCursor *c ) +{ + if ( parenMatcher->match( c ) ) + repaintChanged(); + if ( hasError ) { + emit clearErrorMarker(); + hasError = FALSE; + } +} + +void Editor::load( const QString &fn ) +{ + filename = fn; + QFile f( filename ); + if ( !f.open( IO_ReadOnly ) ) + return; + QCString txt; + txt.resize( f.size() ); + f.readBlock( txt.data(), f.size() ); + QString s( QString::fromLatin1( txt ) ); + setText( s ); +} + +void Editor::save( const QString &fn ) +{ + if ( !filename.isEmpty() ) + filename = fn; +} + +void Editor::configChanged() +{ + document()->invalidate(); + viewport()->repaint( FALSE ); +} + +void Editor::setErrorSelection( int line ) +{ + QTextParagraph *p = document()->paragAt( line ); + if ( !p ) + return; + QTextCursor c( document() ); + c.setParagraph( p ); + c.setIndex( 0 ); + document()->removeSelection( Error ); + document()->setSelectionStart( Error, c ); + c.gotoLineEnd(); + document()->setSelectionEnd( Error, c ); + hasError = TRUE; + viewport()->repaint( FALSE ); +} + +void Editor::setStepSelection( int line ) +{ + QTextParagraph *p = document()->paragAt( line ); + if ( !p ) + return; + QTextCursor c( document() ); + c.setParagraph( p ); + c.setIndex( 0 ); + document()->removeSelection( Step ); + document()->setSelectionStart( Step, c ); + c.gotoLineEnd(); + document()->setSelectionEnd( Step, c ); + viewport()->repaint( FALSE ); +} + +void Editor::clearStepSelection() +{ + document()->removeSelection( Step ); + viewport()->repaint( FALSE ); +} + +void Editor::doChangeInterval() +{ + emit intervalChanged(); + QTextEdit::doChangeInterval(); +} + +void Editor::commentSelection() +{ + QTextParagraph *start = document()->selectionStartCursor( QTextDocument::Standard ).paragraph(); + QTextParagraph *end = document()->selectionEndCursor( QTextDocument::Standard ).paragraph(); + if ( !start || !end ) + start = end = textCursor()->paragraph(); + while ( start ) { + if ( start == end && textCursor()->index() == 0 ) + break; + start->insert( 0, "//" ); + if ( start == end ) + break; + start = start->next(); + } + document()->removeSelection( QTextDocument::Standard ); + repaintChanged(); + setModified( TRUE ); +} + +void Editor::uncommentSelection() +{ + QTextParagraph *start = document()->selectionStartCursor( QTextDocument::Standard ).paragraph(); + QTextParagraph *end = document()->selectionEndCursor( QTextDocument::Standard ).paragraph(); + if ( !start || !end ) + start = end = textCursor()->paragraph(); + while ( start ) { + if ( start == end && textCursor()->index() == 0 ) + break; + while ( start->at( 0 )->c == '/' ) + start->remove( 0, 1 ); + if ( start == end ) + break; + start = start->next(); + } + document()->removeSelection( QTextDocument::Standard ); + repaintChanged(); + setModified( TRUE ); +} + +QPopupMenu *Editor::createPopupMenu( const QPoint &p ) +{ + QPopupMenu *menu = QTextEdit::createPopupMenu( p ); + menu->insertSeparator(); + menu->insertItem( tr( "C&omment Code\tAlt+C" ), this, SLOT( commentSelection() ) ); + menu->insertItem( tr( "Unco&mment Code\tAlt+U" ), this, SLOT( uncommentSelection() ) ); + return menu; +} + +bool Editor::eventFilter( QObject *o, QEvent *e ) +{ + if ( ( e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut ) && + ( o == this || o == viewport() ) ) { + accelUncomment->setEnabled( e->type() == QEvent::FocusIn ); + accelComment->setEnabled( e->type() == QEvent::FocusIn ); + } + return QTextEdit::eventFilter( o, e ); +} + +void Editor::doKeyboardAction( KeyboardAction action ) +{ + if ( !editable ) + return; + QTextEdit::doKeyboardAction( action ); +} + +void Editor::keyPressEvent( QKeyEvent *e ) +{ + if ( editable ) { + QTextEdit::keyPressEvent( e ); + return; + } + + switch ( e->key() ) { + case Key_Left: + case Key_Right: + case Key_Up: + case Key_Down: + case Key_Home: + case Key_End: + case Key_Prior: + case Key_Next: + case Key_Direction_L: + case Key_Direction_R: + QTextEdit::keyPressEvent( e ); + break; + default: + e->accept(); + break; + } +} diff --git a/tools/designer/editor/editor.h b/tools/designer/editor/editor.h new file mode 100644 index 0000000..1364bbb --- /dev/null +++ b/tools/designer/editor/editor.h @@ -0,0 +1,111 @@ + /********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef EDITOR_H +#define EDITOR_H + +#include <qtextedit.h> + +struct Config; +class ParenMatcher; +class EditorCompletion; +class EditorBrowser; +class QAccel; + +class Editor : public QTextEdit +{ + Q_OBJECT + +public: + enum Selection { + Error = 3, + Step = 4 + }; + + Editor( const QString &fn, QWidget *parent, const char *name ); + ~Editor(); + virtual void load( const QString &fn ); + virtual void save( const QString &fn ); + QTextDocument *document() const { return QTextEdit::document(); } + void placeCursor( const QPoint &p, QTextCursor *c ) { QTextEdit::placeCursor( p, c ); } + void setDocument( QTextDocument *doc ) { QTextEdit::setDocument( doc ); } + QTextCursor *textCursor() const { return QTextEdit::textCursor(); } + void repaintChanged() { QTextEdit::repaintChanged(); } + + virtual EditorCompletion *completionManager() { return 0; } + virtual EditorBrowser *browserManager() { return 0; } + virtual void configChanged(); + + Config *config() { return cfg; } + + void setErrorSelection( int line ); + void setStepSelection( int line ); + void clearStepSelection(); + void clearSelections(); + + virtual bool supportsErrors() const { return TRUE; } + virtual bool supportsBreakPoints() const { return TRUE; } + virtual void makeFunctionVisible( QTextParagraph * ) {} + + void drawCursor( bool b ) { QTextEdit::drawCursor( b ); } + + QPopupMenu *createPopupMenu( const QPoint &p ); + bool eventFilter( QObject *o, QEvent *e ); + + void setEditable( bool b ) { editable = b; } + +protected: + void doKeyboardAction( KeyboardAction action ); + void keyPressEvent( QKeyEvent *e ); + +signals: + void clearErrorMarker(); + void intervalChanged(); + +private slots: + void cursorPosChanged( QTextCursor *c ); + void doChangeInterval(); + void commentSelection(); + void uncommentSelection(); + +protected: + ParenMatcher *parenMatcher; + QString filename; + Config *cfg; + bool hasError; + QAccel *accelComment, *accelUncomment; + bool editable; + +}; + +#endif diff --git a/tools/designer/editor/editor.pro b/tools/designer/editor/editor.pro new file mode 100644 index 0000000..dc9235f --- /dev/null +++ b/tools/designer/editor/editor.pro @@ -0,0 +1,40 @@ +TEMPLATE = lib +CONFIG += qt warn_on staticlib +CONFIG -= dll +HEADERS = editor.h \ + parenmatcher.h \ + completion.h \ + viewmanager.h \ + markerwidget.h\ + conf.h \ + browser.h \ + arghintwidget.h \ + cindent.h + +SOURCES = editor.cpp \ + parenmatcher.cpp \ + completion.cpp \ + viewmanager.cpp \ + markerwidget.cpp \ + conf.cpp \ + browser.cpp \ + arghintwidget.cpp \ + cindent.cpp \ + yyindent.cpp + +FORMS = preferences.ui + +TARGET = editor +DESTDIR = ../../../lib +VERSION = 1.0.0 + +INCLUDEPATH += $$QT_SOURCE_TREE/tools/designer/interfaces + +target.path=$$libs.path + +INSTALLS += target + +unix { + QMAKE_CFLAGS += $$QMAKE_CFLAGS_SHLIB + QMAKE_CXXFLAGS += $$QMAKE_CXXFLAGS_SHLIB +} diff --git a/tools/designer/editor/markerwidget.cpp b/tools/designer/editor/markerwidget.cpp new file mode 100644 index 0000000..cabe39a --- /dev/null +++ b/tools/designer/editor/markerwidget.cpp @@ -0,0 +1,491 @@ + /********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "markerwidget.h" +#include "viewmanager.h" +#include <private/qrichtext_p.h> +#include "editor.h" +#include <qpainter.h> +#include <qpopupmenu.h> +#include "paragdata.h" + +static const char * error_xpm[] = { +"15 15 35 1", +" c None", +". c #FF0000", +"+ c #F50F0F", +"@ c #BF5F5F", +"# c #FF1010", +"$ c #FF7878", +"% c #FF0A0A", +"& c #FF0606", +"* c #FF1414", +"= c #FFFFFF", +"- c #FFA3A3", +"; c #FF0707", +"> c #FF0202", +", c #FF9898", +"' c #FF8888", +") c #D04747", +"! c #FFA7A7", +"~ c #FF9D9D", +"{ c #FFB1B1", +"] c #FF0C0C", +"^ c #F90A0A", +"/ c #FFB5B5", +"( c #FF0909", +"_ c #A08F8F", +": c #FFACAC", +"< c #FF0303", +"[ c #9F8F8F", +"} c #FB0606", +"| c #9F9090", +"1 c #CE4949", +"2 c #999999", +"3 c #FF1919", +"4 c #F70C0C", +"5 c #A38A8A", +"6 c #B37272", +" .... ", +" ........ ", +" .........+@ ", +" ..#$%..&$*.+ ", +" ..$=-;>,='..) ", +"...%-=!~={]..^ ", +"....;!==/(...._", +"....>~==:<....[", +"...&,=/:=!;..}|", +" ..$={(<!='..12", +" ..*']..;'3.45 ", +" +........462 ", +" @+......462 ", +" )^..}152 ", +" _[|2 "}; + +static const char * breakpoint_xpm[] = { +"15 15 3 1", +" c None", +". c #8B0000", +"+ c #FFFFFF", +" ....... ", +" ......... ", +" ........... ", +" ............. ", +"..+.+++.+..++..", +".+.+.+.+.+.+.+.", +".+...+.+.+.+.+.", +"..+..+.+.+.++..", +"...+.+.+.+.+...", +".+.+.+.+.+.+...", +"..+..+..+..+...", +" ............. ", +" ........... ", +" ......... ", +" ....... " }; + +static const char * step_xpm[] = { +"16 16 128 2", +" c None", +". c #B4B6BF", +"+ c #7893D8", +"@ c #8D95BF", +"# c #B8BFC1", +"$ c #B6D1E6", +"% c #7193E6", +"& c #8893C2", +"* c #B3BDC4", +"= c #AAD2EC", +"- c #9AD0FF", +"; c #6690EF", +"> c #8894C8", +", c #AFBAC4", +"' c #95BFEC", +") c #99CBFF", +"! c #8EC3FF", +"~ c #6D95F0", +"{ c #8792CA", +"] c #9DA7C3", +"^ c #8BA2E3", +"/ c #809AE0", +"( c #8398D1", +"_ c #93A0CC", +": c #ACB3CB", +"< c #B4B9C4", +"[ c #B6BAC4", +"} c #93A4CC", +"| c #82B0F5", +"1 c #8BBCFF", +"2 c #8EC0FF", +"3 c #8FC1FF", +"4 c #6594F4", +"5 c #7381CC", +"6 c #81A7E9", +"7 c #D0F5FF", +"8 c #C1EBFF", +"9 c #AEDAFF", +"0 c #A2D1FC", +"a c #A3C8F3", +"b c #AACAE6", +"c c #B4CFE9", +"d c #ADCCF9", +"e c #84B2FF", +"f c #82B4FF", +"g c #86B7FF", +"h c #88B7FF", +"i c #83B4FF", +"j c #5F8AF3", +"k c #7585C8", +"l c #77A4F3", +"m c #ABDFFF", +"n c #9CCAFF", +"o c #96C7FF", +"p c #97C8FF", +"q c #95C5FF", +"r c #9DCCFF", +"s c #A0CDFF", +"t c #90C0FF", +"u c #82AFFF", +"v c #7FAFFF", +"w c #7DAEFF", +"x c #79AAFF", +"y c #6C9EFF", +"z c #4366EB", +"A c #6894F2", +"B c #93C6FF", +"C c #82B3FF", +"D c #7AABFF", +"E c #73A5FF", +"F c #71A3FF", +"G c #6C9DFF", +"H c #699BFF", +"I c #76A8FF", +"J c #7EB0FF", +"K c #7BADFF", +"L c #74A5FF", +"M c #608BFF", +"N c #3462FF", +"O c #2444E5", +"P c #577AE0", +"Q c #5D90FF", +"R c #4C7AFF", +"S c #3B66FF", +"T c #335CF9", +"U c #365AF1", +"V c #3858E5", +"W c #3959E0", +"X c #416CF9", +"Y c #75A5FF", +"Z c #78A9FF", +"` c #74A4FF", +" . c #6191FF", +".. c #3059FF", +"+. c #1B37F1", +"@. c #6A75C7", +"#. c #828BC1", +"$. c #4358D8", +"%. c #374BDA", +"&. c #4759CA", +"*. c #636CC4", +"=. c #8489C0", +"-. c #9DA1C1", +";. c #A3A6BF", +">. c #7486CB", +",. c #6E98F5", +"'. c #719EFF", +"). c #608DFF", +"!. c #315EFF", +"~. c #1432F4", +"{. c #5C63C8", +"]. c #B1B4B9", +"^. c #B3BABB", +"/. c #ABB4C3", +"(. c #7299E9", +"_. c #5486FF", +":. c #224EFF", +"<. c #1733F2", +"[. c #7079C5", +"}. c #5C7DE9", +"|. c #2450FF", +"1. c #1B39EC", +"2. c #7077C5", +"3. c #3A54E1", +"4. c #1E36EA", +"5. c #858CBF", +"6. c #525FCB", +"7. c #727CBC", +" ", +" . + @ ", +" # $ % & ", +" * = - ; > ", +" , ' ) ! ~ { ", +"] ^ / ( _ : < [ } | 1 2 3 4 5 ", +"6 7 8 9 0 a b c d e f g h i j k ", +"l m n o p q r s t u v u w x y z ", +"A B C D E F G H I J K D L M N O ", +"P Q R S T U V W X Y Z ` ...+.@.", +"#.$.%.&.*.=.-.;.>.,.'.).!.~.{. ", +" ].^. /.(._.:.<.[. ", +" }.|.1.2. ", +" 3.4.5. ", +" 6.7. ", +" "}; + +static const char *stack_frame_xpm[]={ +"16 16 2 1", +". c None", +"# c #00c000", +"................", +".###............", +".#####..........", +".#######........", +".#########......", +".###########....", +".#############..", +".##############.", +".##############.", +".#############..", +".###########....", +".#########......", +".#######........", +".#####..........", +".###............", +"................"}; + +static QPixmap *errorPixmap = 0; +static QPixmap *breakpointPixmap = 0; +static QPixmap *stepPixmap = 0; +static QPixmap *stackFrame = 0; + +MarkerWidget::MarkerWidget( ViewManager *parent, const char*name ) + : QWidget( parent, name, WRepaintNoErase | WStaticContents | WResizeNoErase ), viewManager( parent ) +{ + if ( !errorPixmap ) { + errorPixmap = new QPixmap( error_xpm ); + breakpointPixmap = new QPixmap( breakpoint_xpm ); + stepPixmap = new QPixmap( step_xpm ); + stackFrame = new QPixmap( stack_frame_xpm ); + } +} + +void MarkerWidget::paintEvent( QPaintEvent * ) +{ + buffer.fill( backgroundColor() ); + + QTextParagraph *p = ( (Editor*)viewManager->currentView() )->document()->firstParagraph(); + QPainter painter( &buffer ); + int yOffset = ( (Editor*)viewManager->currentView() )->contentsY(); + while ( p ) { + if ( !p->isVisible() ) { + p = p->next(); + continue; + } + if ( p->rect().y() + p->rect().height() - yOffset < 0 ) { + p = p->next(); + continue; + } + if ( p->rect().y() - yOffset > height() ) + break; + if ( !((p->paragId() + 1) % 10) ) { + painter.save(); + painter.setPen( colorGroup().dark() ); + painter.drawText( 0, p->rect().y() - yOffset, width() - 20, p->rect().height(), + Qt::AlignRight | Qt::AlignTop, QString::number( p->paragId() + 1 ) ); + painter.restore(); + } + ParagData *paragData = (ParagData*)p->extraData(); + if ( paragData ) { + switch ( paragData->marker ) { + case ParagData::Error: + painter.drawPixmap( 3, p->rect().y() + + ( p->rect().height() - errorPixmap->height() ) / 2 - + yOffset, *errorPixmap ); + break; + case ParagData::Breakpoint: + painter.drawPixmap( 3, p->rect().y() + + ( p->rect().height() - breakpointPixmap->height() ) / 2 - + yOffset, *breakpointPixmap ); + break; + default: + break; + } + switch ( paragData->lineState ) { + case ParagData::FunctionStart: + painter.setPen( colorGroup().foreground() ); + painter.setBrush( colorGroup().base() ); + painter.drawLine( width() - 11, p->rect().y() - yOffset, + width() - 11, p->rect().y() + p->rect().height() - yOffset ); + painter.drawRect( width() - 15, p->rect().y() + ( p->rect().height() - 9 ) / 2 - yOffset, 9, 9 ); + painter.drawLine( width() - 13, p->rect().y() + ( p->rect().height() - 9 ) / 2 - yOffset + 4, + width() - 9, p->rect().y() + + ( p->rect().height() - 9 ) / 2 - yOffset + 4 ); + if ( !paragData->functionOpen ) + painter.drawLine( width() - 11, + p->rect().y() + ( p->rect().height() - 9 ) / 2 - yOffset + 2, + width() - 11, + p->rect().y() + + ( p->rect().height() - 9 ) / 2 - yOffset + 6 ); + break; + case ParagData::InFunction: + painter.setPen( colorGroup().foreground() ); + painter.drawLine( width() - 11, p->rect().y() - yOffset, + width() - 11, p->rect().y() + p->rect().height() - yOffset ); + break; + case ParagData::FunctionEnd: + painter.setPen( colorGroup().foreground() ); + painter.drawLine( width() - 11, p->rect().y() - yOffset, + width() - 11, p->rect().y() + p->rect().height() - yOffset ); + painter.drawLine( width() - 11, p->rect().y() + p->rect().height() - yOffset, + width() - 7, p->rect().y() + p->rect().height() - yOffset ); + break; + default: + break; + } + if ( paragData->step ) + painter.drawPixmap( 3, p->rect().y() + + ( p->rect().height() - stepPixmap->height() ) / 2 - + yOffset, *stepPixmap ); + if ( paragData->stackFrame ) + painter.drawPixmap( 3, p->rect().y() + + ( p->rect().height() - stackFrame->height() ) / 2 - + yOffset, *stackFrame ); + } + p = p->next(); + } + painter.end(); + + bitBlt( this, 0, 0, &buffer ); +} + +void MarkerWidget::resizeEvent( QResizeEvent *e ) +{ + buffer.resize( e->size() ); + QWidget::resizeEvent( e ); +} + +void MarkerWidget::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + bool supports = ( (Editor*)viewManager->currentView() )->supportsBreakPoints(); + QTextParagraph *p = ( (Editor*)viewManager->currentView() )->document()->firstParagraph(); + int yOffset = ( (Editor*)viewManager->currentView() )->contentsY(); + while ( p ) { + if ( e->y() >= p->rect().y() - yOffset && e->y() <= p->rect().y() + p->rect().height() - yOffset ) { + QTextParagraphData *d = p->extraData(); + if ( !d ) + return; + ParagData *data = (ParagData*)d; + if ( supports && ( e->x() < width() - 15 ) ) { + if ( data->marker == ParagData::Breakpoint ) { + data->marker = ParagData::NoMarker; + } else { + bool ok = TRUE; + isBreakpointPossible( ok, ( (Editor*)viewManager->currentView() )->text(), p->paragId() ); + if ( ok ) + data->marker = ParagData::Breakpoint; + else + emit showMessage( tr( "<font color=red>Can't set breakpoint here!</font>" ) ); + } + } else { + if ( data->lineState == ParagData::FunctionStart ) { + if ( data->functionOpen ) + emit collapseFunction( p ); + else + emit expandFunction( p ); + } + } + break; + } + p = p->next(); + } + doRepaint(); + emit markersChanged(); +} + +void MarkerWidget::contextMenuEvent( QContextMenuEvent *e ) +{ + QPopupMenu m( 0, "editor_breakpointsmenu" ); + + int toggleBreakPoint = 0; +// int editBreakpoints = 0; + + QTextParagraph *p = ( (Editor*)viewManager->currentView() )->document()->firstParagraph(); + int yOffset = ( (Editor*)viewManager->currentView() )->contentsY(); + bool supports = ( (Editor*)viewManager->currentView() )->supportsBreakPoints(); + while ( p && supports ) { + if ( e->y() >= p->rect().y() - yOffset && e->y() <= p->rect().y() + p->rect().height() - yOffset ) { + if ( ( (ParagData*)p->extraData() )->marker == ParagData::Breakpoint ) + toggleBreakPoint = m.insertItem( tr( "Clear Breakpoint\tF9" ) ); + else + toggleBreakPoint = m.insertItem( tr( "Set Breakpoint\tF9" ) ); +// editBreakpoints = m.insertItem( tr( "Edit Breakpoints..." ) ); + m.insertSeparator(); + break; + } + p = p->next(); + } + + const int collapseAll = m.insertItem( tr( "Collapse All" ) ); + const int expandAll = m.insertItem( tr( "Expand All" ) ); + const int collapseFunctions = m.insertItem( tr( "Collapse all Functions" ) ); + const int expandFunctions = m.insertItem( tr( "Expand all Functions" ) ); + + int res = m.exec( e->globalPos() ); + if ( res == -1) + return; + + if ( res == collapseAll ) { + emit collapse( TRUE ); + } else if ( res == collapseFunctions ) { + emit collapse( FALSE ); + } else if ( res == expandAll ) { + emit expand( TRUE ); + } else if ( res == expandFunctions ) { + emit expand( FALSE ); + } else if ( res == toggleBreakPoint ) { + if ( ( (ParagData*)p->extraData() )->marker == ParagData::Breakpoint ) { + ( (ParagData*)p->extraData() )->marker = ParagData::NoMarker; + } else { + bool ok; + isBreakpointPossible( ok, ( (Editor*)viewManager->currentView() )->text(), p->paragId() ); + if ( ok ) + ( (ParagData*)p->extraData() )->marker = ParagData::Breakpoint; + else + emit showMessage( tr( "<font color=red>Can't set breakpoint here!</font>" ) ); + } +// } else if ( res == editBreakpoints ) { +// emit editBreakPoints(); + } + doRepaint(); + emit markersChanged(); +} diff --git a/tools/designer/editor/markerwidget.h b/tools/designer/editor/markerwidget.h new file mode 100644 index 0000000..f1d406e --- /dev/null +++ b/tools/designer/editor/markerwidget.h @@ -0,0 +1,75 @@ + /********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef MARKERWIDGET_H +#define MARKERWIDGET_H + +#include <qwidget.h> +#include <qpixmap.h> + +class ViewManager; +class QTextParagraph; + +class MarkerWidget : public QWidget +{ + Q_OBJECT + +public: + MarkerWidget( ViewManager *parent, const char*name ); + +signals: + void markersChanged(); + void expandFunction( QTextParagraph *p ); + void collapseFunction( QTextParagraph *p ); + void collapse( bool all /*else only functions*/ ); + void expand( bool all /*else only functions*/ ); + void editBreakPoints(); + void isBreakpointPossible( bool &possible, const QString &code, int line ); + void showMessage( const QString &msg ); + +public slots: + void doRepaint() { repaint( FALSE ); } + +protected: + void paintEvent( QPaintEvent *e ); + void resizeEvent( QResizeEvent *e ); + void mousePressEvent( QMouseEvent *e ); + void contextMenuEvent( QContextMenuEvent *e ); + +private: + QPixmap buffer; + ViewManager *viewManager; + +}; + +#endif diff --git a/tools/designer/editor/paragdata.h b/tools/designer/editor/paragdata.h new file mode 100644 index 0000000..64bb050 --- /dev/null +++ b/tools/designer/editor/paragdata.h @@ -0,0 +1,65 @@ + /********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef PARAGDATA_H +#define PARAGDATA_H + +#include "parenmatcher.h" +#include <private/qrichtext_p.h> + +struct ParagData : public QTextParagraphData +{ +public: + enum MarkerType { NoMarker, Error, Breakpoint }; + enum LineState { FunctionStart, InFunction, FunctionEnd, Invalid }; + + ParagData() : lastLengthForCompletion( -1 ), marker( NoMarker ), + lineState( Invalid ), functionOpen( TRUE ), step( FALSE ), stackFrame( FALSE ) {} + ~ParagData() {} + void join( QTextParagraphData *data ) { + ParagData *d = (ParagData*)data; + if ( marker == NoMarker ) + marker = d->marker; + lineState = d->lineState; + } + ParenList parenList; + int lastLengthForCompletion; + MarkerType marker; + LineState lineState; + bool functionOpen; + bool step; + bool stackFrame; + +}; + +#endif diff --git a/tools/designer/editor/parenmatcher.cpp b/tools/designer/editor/parenmatcher.cpp new file mode 100644 index 0000000..ae7a3b5 --- /dev/null +++ b/tools/designer/editor/parenmatcher.cpp @@ -0,0 +1,215 @@ +/********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "parenmatcher.h" +#include "paragdata.h" + +#include "qtextedit.h" +#include <private/qrichtext_p.h> +#include <qapplication.h> + +ParenMatcher::ParenMatcher() +{ + enabled = TRUE; +} + +bool ParenMatcher::match( QTextCursor *cursor ) +{ + if ( !enabled ) + return FALSE; + bool ret = FALSE; + + QChar c( cursor->paragraph()->at( cursor->index() )->c ); + bool ok1 = FALSE; + bool ok2 = FALSE; + if ( c == '{' || c == '(' || c == '[' ) { + ok1 = checkOpenParen( cursor ); + ret = ok1 || ret; + } else if ( cursor->index() > 0 ) { + c = cursor->paragraph()->at( cursor->index() - 1 )->c; + if ( c == '}' || c == ')' || c == ']' ) { + ok2 = checkClosedParen( cursor ); + ret = ok2 || ret; + } + } + + return ret; +} + +bool ParenMatcher::checkOpenParen( QTextCursor *cursor ) +{ + if ( !cursor->paragraph()->extraData() ) + return FALSE; + ParenList parenList = ( (ParagData*)cursor->paragraph()->extraData() )->parenList; + + Paren openParen, closedParen; + QTextParagraph *closedParenParag = cursor->paragraph(); + + int i = 0; + int ignore = 0; + bool foundOpen = FALSE; + QChar c = cursor->paragraph()->at( cursor->index() )->c; + for (;;) { + if ( !foundOpen ) { + if ( i >= (int)parenList.count() ) + goto bye; + openParen = parenList[ i ]; + if ( openParen.pos != cursor->index() ) { + ++i; + continue; + } else { + foundOpen = TRUE; + ++i; + } + } + + if ( i >= (int)parenList.count() ) { + for (;;) { + closedParenParag = closedParenParag->next(); + if ( !closedParenParag ) + goto bye; + if ( closedParenParag->extraData() && + ( (ParagData*)closedParenParag->extraData() )->parenList.count() > 0 ) { + parenList = ( (ParagData*)closedParenParag->extraData() )->parenList; + break; + } + } + i = 0; + } + + closedParen = parenList[ i ]; + if ( closedParen.type == Paren::Open ) { + ignore++; + ++i; + continue; + } else { + if ( ignore > 0 ) { + ignore--; + ++i; + continue; + } + + int id = Match; + if ( c == '{' && closedParen.chr != '}' || + c == '(' && closedParen.chr != ')' || + c == '[' && closedParen.chr != ']' ) + id = Mismatch; + cursor->document()->setSelectionStart( id, *cursor ); + int tidx = cursor->index(); + QTextParagraph *tstring = cursor->paragraph(); + cursor->setParagraph( closedParenParag ); + cursor->setIndex( closedParen.pos + 1 ); + cursor->document()->setSelectionEnd( id, *cursor ); + cursor->setParagraph( tstring ); + cursor->setIndex( tidx ); + return TRUE; + } + } + + bye: + return FALSE; +} + +bool ParenMatcher::checkClosedParen( QTextCursor *cursor ) +{ + if ( !cursor->paragraph()->extraData() ) + return FALSE; + ParenList parenList = ( (ParagData*)cursor->paragraph()->extraData() )->parenList; + + Paren openParen, closedParen; + QTextParagraph *openParenParag = cursor->paragraph(); + + int i = (int)parenList.count() - 1; + int ignore = 0; + bool foundClosed = FALSE; + QChar c = cursor->paragraph()->at( cursor->index() - 1 )->c; + for (;;) { + if ( !foundClosed ) { + if ( i < 0 ) + goto bye; + closedParen = parenList[ i ]; + if ( closedParen.pos != cursor->index() - 1 ) { + --i; + continue; + } else { + foundClosed = TRUE; + --i; + } + } + + if ( i < 0 ) { + for (;;) { + openParenParag = openParenParag->prev(); + if ( !openParenParag ) + goto bye; + if ( openParenParag->extraData() && + ( (ParagData*)openParenParag->extraData() )->parenList.count() > 0 ) { + parenList = ( (ParagData*)openParenParag->extraData() )->parenList; + break; + } + } + i = (int)parenList.count() - 1; + } + + openParen = parenList[ i ]; + if ( openParen.type == Paren::Closed ) { + ignore++; + --i; + continue; + } else { + if ( ignore > 0 ) { + ignore--; + --i; + continue; + } + + int id = Match; + if ( c == '}' && openParen.chr != '{' || + c == ')' && openParen.chr != '(' || + c == ']' && openParen.chr != '[' ) + id = Mismatch; + cursor->document()->setSelectionStart( id, *cursor ); + int tidx = cursor->index(); + QTextParagraph *tstring = cursor->paragraph(); + cursor->setParagraph( openParenParag ); + cursor->setIndex( openParen.pos ); + cursor->document()->setSelectionEnd( id, *cursor ); + cursor->setParagraph( tstring ); + cursor->setIndex( tidx ); + return TRUE; + } + } + + bye: + return FALSE; +} diff --git a/tools/designer/editor/parenmatcher.h b/tools/designer/editor/parenmatcher.h new file mode 100644 index 0000000..9bb2568 --- /dev/null +++ b/tools/designer/editor/parenmatcher.h @@ -0,0 +1,78 @@ + /********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef PARENMATCHER_H +#define PARENMATCHER_H + +#include <qstring.h> +#include <qvaluelist.h> + +class QTextCursor; + +struct Paren +{ + Paren() : type( Open ), chr( ' ' ), pos( -1 ) {} + Paren( int t, const QChar &c, int p ) : type( (Type)t ), chr( c ), pos( p ) {} + enum Type { Open, Closed }; + Type type; + QChar chr; + int pos; + + Q_DUMMY_COMPARISON_OPERATOR( Paren ) +}; + +typedef QValueList<Paren> ParenList; + +class ParenMatcher +{ +public: + enum Selection { + Match = 1, + Mismatch + }; + + ParenMatcher(); + + virtual bool match( QTextCursor *c ); + + void setEnabled( bool b ) { enabled = b; } + +private: + bool checkOpenParen( QTextCursor *c ); + bool checkClosedParen( QTextCursor *c ); + + bool enabled; + +}; + +#endif diff --git a/tools/designer/editor/preferences.ui b/tools/designer/editor/preferences.ui new file mode 100644 index 0000000..5dc1d53 --- /dev/null +++ b/tools/designer/editor/preferences.ui @@ -0,0 +1,510 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>PreferencesBase</class> +<comment>********************************************************************* +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +*********************************************************************</comment> +<widget class="QWidget"> + <property name="name"> + <cstring>PreferencesBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>362</width> + <height>396</height> + </rect> + </property> + <property name="caption"> + <string>Form1</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>6</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>GroupBox2</cstring> + </property> + <property name="frameShape"> + <enum>Box</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="title"> + <string>S&yntax Highlighting</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="text"> + <string>&Element:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>listElements</cstring> + </property> + </widget> + <widget class="QLayoutWidget" row="1" column="1" rowspan="3" colspan="1"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>checkBold</cstring> + </property> + <property name="text"> + <string>&Bold</string> + </property> + </widget> + <widget class="QToolButton" row="5" column="1"> + <property name="name"> + <cstring>buttonColor</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <property name="name"> + <cstring>comboFamily</cstring> + </property> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>&Family:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>comboFamily</cstring> + </property> + </widget> + <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>checkUnderline</cstring> + </property> + <property name="text"> + <string>&Underline</string> + </property> + </widget> + <widget class="QSpinBox" row="1" column="1"> + <property name="name"> + <cstring>spinSize</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>checkItalic</cstring> + </property> + <property name="text"> + <string>&Italic</string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>TextLabel4</cstring> + </property> + <property name="text"> + <string>Change co&lor:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>buttonColor</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel3</cstring> + </property> + <property name="text"> + <string>&Size:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>spinSize</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QListBox" row="1" column="0"> + <item> + <property name="text"> + <string>Comment</string> + </property> + </item> + <item> + <property name="text"> + <string>Number</string> + </property> + </item> + <item> + <property name="text"> + <string>String</string> + </property> + </item> + <item> + <property name="text"> + <string>Type</string> + </property> + </item> + <item> + <property name="text"> + <string>Keyword</string> + </property> + </item> + <item> + <property name="text"> + <string>Preprocessor</string> + </property> + </item> + <item> + <property name="text"> + <string>Label</string> + </property> + </item> + <item> + <property name="text"> + <string>Standard</string> + </property> + </item> + <property name="name"> + <cstring>listElements</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>TextLabel2_2</cstring> + </property> + <property name="text"> + <string>&Preview:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>editPreview</cstring> + </property> + </widget> + <widget class="QLineEdit" row="3" column="0"> + <property name="name"> + <cstring>editPreview</cstring> + </property> + <property name="text"> + <string>Some Text</string> + </property> + </widget> + <spacer row="4" column="1"> + <property name="name"> + <cstring>Spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QGroupBox" row="1" column="0"> + <property name="name"> + <cstring>GroupBox3</cstring> + </property> + <property name="title"> + <string>Optio&ns</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkWordWrap</cstring> + </property> + <property name="text"> + <string>&Word Wrap</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkCompletion</cstring> + </property> + <property name="text"> + <string>Comple&tion</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkParenMatching</cstring> + </property> + <property name="text"> + <string>Parentheses &Matching</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox" row="1" column="1"> + <property name="name"> + <cstring>GroupBox3_2</cstring> + </property> + <property name="title"> + <string>Indentation</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout2</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QSpinBox" row="0" column="1"> + <property name="name"> + <cstring>spinTabSize</cstring> + </property> + <property name="value"> + <number>8</number> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel1_2</cstring> + </property> + <property name="text"> + <string>Tab Size:</string> + </property> + </widget> + <widget class="QSpinBox" row="1" column="1"> + <property name="name"> + <cstring>spinIndentSize</cstring> + </property> + <property name="value"> + <number>4</number> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel2_3</cstring> + </property> + <property name="text"> + <string>Indent Size:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkKeepTabs</cstring> + </property> + <property name="text"> + <string>Keep Tabs</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkAutoIndent</cstring> + </property> + <property name="text"> + <string>Auto Indent</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </vbox> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>spinSize</sender> + <signal>valueChanged(int)</signal> + <receiver>PreferencesBase</receiver> + <slot>sizeChanged(int)</slot> + </connection> + <connection> + <sender>checkBold</sender> + <signal>toggled(bool)</signal> + <receiver>PreferencesBase</receiver> + <slot>boldChanged(bool)</slot> + </connection> + <connection> + <sender>checkItalic</sender> + <signal>toggled(bool)</signal> + <receiver>PreferencesBase</receiver> + <slot>italicChanged(bool)</slot> + </connection> + <connection> + <sender>checkUnderline</sender> + <signal>toggled(bool)</signal> + <receiver>PreferencesBase</receiver> + <slot>underlineChanged(bool)</slot> + </connection> + <connection> + <sender>buttonColor</sender> + <signal>clicked()</signal> + <receiver>PreferencesBase</receiver> + <slot>colorClicked()</slot> + </connection> + <connection> + <sender>comboFamily</sender> + <signal>activated(const QString&)</signal> + <receiver>PreferencesBase</receiver> + <slot>familyChanged(const QString&)</slot> + </connection> + <connection> + <sender>listElements</sender> + <signal>highlighted(const QString&)</signal> + <receiver>PreferencesBase</receiver> + <slot>elementChanged(const QString&)</slot> + </connection> +</connections> +<tabstops> + <tabstop>listElements</tabstop> + <tabstop>editPreview</tabstop> + <tabstop>comboFamily</tabstop> + <tabstop>spinSize</tabstop> + <tabstop>checkBold</tabstop> + <tabstop>checkItalic</tabstop> + <tabstop>checkUnderline</tabstop> + <tabstop>checkWordWrap</tabstop> + <tabstop>checkCompletion</tabstop> + <tabstop>checkParenMatching</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in declaration">qmap.h</include> + <include location="local" impldecl="in declaration">conf.h</include> + <include location="global" impldecl="in implementation">qpalette.h</include> + <include location="global" impldecl="in implementation">qlineedit.h</include> + <include location="global" impldecl="in implementation">qpixmap.h</include> + <include location="global" impldecl="in implementation">qcombobox.h</include> + <include location="global" impldecl="in implementation">qfontdatabase.h</include> + <include location="local" impldecl="in implementation">editor.h</include> + <include location="global" impldecl="in implementation">qpushbutton.h</include> + <include location="global" impldecl="in implementation">qcheckbox.h</include> + <include location="global" impldecl="in implementation">qcolordialog.h</include> + <include location="global" impldecl="in implementation">qsettings.h</include> + <include location="local" impldecl="in implementation">preferences.ui.h</include> +</includes> +<variables> + <variable>QString path;</variable> + <variable>QMap<QString, ConfigStyle> styles;</variable> + <variable>ConfigStyle currentStyle;</variable> + <variable>QString currentElement;</variable> +</variables> +<slots> + <slot>init()</slot> + <slot>destroy()</slot> + <slot>colorClicked()</slot> + <slot>reInit()</slot> + <slot>save()</slot> + <slot>updatePreview()</slot> + <slot>boldChanged( bool b )</slot> + <slot>elementChanged( const QString & element )</slot> + <slot>familyChanged( const QString & f )</slot> + <slot>italicChanged( bool b )</slot> + <slot>setColorPixmap( const QColor & c )</slot> + <slot>setPath( const QString & p )</slot> + <slot>sizeChanged( int s )</slot> + <slot>underlineChanged( bool b )</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/tools/designer/editor/preferences.ui.h b/tools/designer/editor/preferences.ui.h new file mode 100644 index 0000000..b175ca8 --- /dev/null +++ b/tools/designer/editor/preferences.ui.h @@ -0,0 +1,181 @@ +/********************************************************************** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +void PreferencesBase::init() +{ + QFontDatabase fdb; + comboFamily->insertStringList( fdb.families() ); + listElements->setCurrentItem( listElements->firstItem() ); + currentElement = ""; +} + +void PreferencesBase::destroy() +{ + +} + +void PreferencesBase::colorClicked() +{ + QColor c = QColorDialog::getColor( currentStyle.color, this, "editor_getcolor_dlg" ); + if ( c.isValid() ) { + currentStyle.color = c; + setColorPixmap( c ); + } +} + +void PreferencesBase::reInit() +{ + styles = Config::readStyles( path ); + currentElement = ""; + elementChanged( "Comment" ); + for ( int i = 0; i < comboFamily->count(); ++i ) { + if ( listElements->text( i ) == "Comment" ) { + listElements->setCurrentItem( i ); + break; + } + } + checkWordWrap->setChecked( Config::wordWrap( path ) ); + checkCompletion->setChecked( Config::completion( path ) ); + checkParenMatching->setChecked( Config::parenMatching( path ) ); + spinTabSize->setValue( Config::indentTabSize( path ) ); + spinIndentSize->setValue( Config::indentIndentSize( path ) ); + checkKeepTabs->setChecked( Config::indentKeepTabs( path ) ); + checkAutoIndent->setChecked( Config::indentAutoIndent( path ) ); +} + +void PreferencesBase::save() +{ + if ( !currentElement.isEmpty() ) { + styles.remove( currentElement ); + styles.insert( currentElement, currentStyle ); + currentElement = ""; + } + + QSettings settings; + Config::saveStyles( styles, path ); + Config::setWordWrap( checkWordWrap->isChecked(), path ); + Config::setCompletion( checkCompletion->isChecked(), path ); + Config::setParenMatching( checkParenMatching->isChecked(), path ); + Config::setIndentTabSize( spinTabSize->value(), path ); + Config::setIndentIndentSize( spinIndentSize->value(), path ); + Config::setIndentKeepTabs( checkKeepTabs->isChecked(), path ); + Config::setIndentAutoIndent( checkAutoIndent->isChecked(), path ); +} + +void PreferencesBase::updatePreview() +{ + editPreview->setFont( currentStyle.font ); + QPalette pal = editPreview->palette(); + pal.setColor( QPalette::Active, QColorGroup::Text, currentStyle.color ); + pal.setColor( QPalette::Active, QColorGroup::Foreground, currentStyle.color ); + editPreview->setPalette( pal ); +} + +void PreferencesBase::boldChanged( bool b ) +{ + currentStyle.font.setBold( b ); + updatePreview(); +} + +void PreferencesBase::elementChanged( const QString &element ) +{ + if ( !currentElement.isEmpty() ) { + styles.remove( currentElement ); + styles.insert( currentElement, currentStyle ); + currentElement = ""; + } + QMap<QString, ConfigStyle>::Iterator it = styles.find( element ); + if ( it == styles.end() ) + return; + ConfigStyle s = *it; + currentStyle = s; + comboFamily->lineEdit()->setText( s.font.family() ); + spinSize->setValue( s.font.pointSize() ); + checkBold->setChecked( s.font.bold() ); + checkItalic->setChecked( s.font.italic() ); + checkUnderline->setChecked( s.font.underline() ); + setColorPixmap( s.color ); + currentElement = element; + updatePreview(); +} + +void PreferencesBase::familyChanged( const QString &f ) +{ + QString oldFamily = currentStyle.font.family(); + currentStyle.font.setFamily( f ); + if ( currentElement == "Standard" ) { + for ( QMap<QString, ConfigStyle>::Iterator it = styles.begin(); it != styles.end(); ++it ) { + if ( (*it).font.family() == oldFamily ) + (*it).font.setFamily( f ); + } + } + updatePreview(); +} + +void PreferencesBase::italicChanged( bool b ) +{ + currentStyle.font.setItalic( b ); + updatePreview(); +} + +void PreferencesBase::setColorPixmap( const QColor &c ) +{ + QPixmap pm( 20, 20 ); + pm.fill( c ); + buttonColor->setPixmap( pm ); + updatePreview(); +} + +void PreferencesBase::setPath( const QString &p ) +{ + path = p; +} + +void PreferencesBase::sizeChanged( int s ) +{ + int oldSize = currentStyle.font.pointSize(); + currentStyle.font.setPointSize( s ); + if ( currentElement == "Standard" ) { + for ( QMap<QString, ConfigStyle>::Iterator it = styles.begin(); it != styles.end(); ++it ) { + if ( (*it).font.pointSize() == oldSize ) + (*it).font.setPointSize( s ); + } + } + updatePreview(); +} + +void PreferencesBase::underlineChanged( bool b ) +{ + currentStyle.font.setUnderline( b ); + updatePreview(); +} diff --git a/tools/designer/editor/viewmanager.cpp b/tools/designer/editor/viewmanager.cpp new file mode 100644 index 0000000..cc6f634 --- /dev/null +++ b/tools/designer/editor/viewmanager.cpp @@ -0,0 +1,276 @@ + /********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "viewmanager.h" +#include "editor.h" +#include "markerwidget.h" +#include <qlayout.h> +#include <private/qrichtext_p.h> +#include "paragdata.h" +#include <qobjectlist.h> +#include <qlabel.h> +#include <qtimer.h> + +ViewManager::ViewManager( QWidget *parent, const char *name ) + : QWidget( parent, name ), curView( 0 ) +{ + QHBoxLayout *l = new QHBoxLayout( this ); + markerWidget = new MarkerWidget( this, "editor_markerwidget" ); + connect( markerWidget, SIGNAL( markersChanged() ), + this, SIGNAL( markersChanged() ) ); + connect( markerWidget, SIGNAL( collapseFunction( QTextParagraph * ) ), + this, SIGNAL( collapseFunction( QTextParagraph * ) ) ); + connect( markerWidget, SIGNAL( expandFunction( QTextParagraph * ) ), + this, SIGNAL( expandFunction( QTextParagraph * ) ) ); + connect( markerWidget, SIGNAL( collapse( bool ) ), + this, SIGNAL( collapse( bool ) ) ); + connect( markerWidget, SIGNAL( expand( bool ) ), + this, SIGNAL( expand( bool ) ) ); + connect( markerWidget, SIGNAL( editBreakPoints() ), + this, SIGNAL( editBreakPoints() ) ); + connect( markerWidget, SIGNAL( isBreakpointPossible( bool&, const QString &, int ) ), + this, SIGNAL( isBreakpointPossible( bool&, const QString &, int ) ) ); + connect( markerWidget, SIGNAL( showMessage( const QString & ) ), + this, SLOT( showMessage( const QString & ) ) ); + messageTimer = new QTimer( this ); + connect( messageTimer, SIGNAL( timeout() ), this, SLOT( clearStatusBar() ) ); + markerWidget->setFixedWidth( fontMetrics().width( "0000" ) + 20 ); + l->addWidget( markerWidget ); + layout = new QVBoxLayout( l ); +} + +void ViewManager::addView( QWidget *view ) +{ + layout->addWidget( view ); + curView = view; + connect( ( (Editor*)curView )->verticalScrollBar(), SIGNAL( valueChanged( int ) ), + markerWidget, SLOT( doRepaint() ) ); + connect( (Editor*)curView, SIGNAL( textChanged() ), + markerWidget, SLOT( doRepaint() ) ); + connect( (Editor*)curView, SIGNAL( clearErrorMarker() ), + this, SLOT( clearErrorMarker() ) ); + posLabel = new QLabel( this, "editor_poslabel" ); + posLabel->setAlignment( Qt::AlignVCenter | Qt::AlignRight ); + posLabel->setText( " Line: 1 Col: 1" ); + posLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel ); + posLabel->setLineWidth( 1 ); + posLabel->setFixedHeight( posLabel->fontMetrics().height() ); + layout->addWidget( posLabel ); + connect( curView, SIGNAL( cursorPositionChanged( int, int ) ), + this, SLOT( cursorPositionChanged( int, int ) ) ); +} + +QWidget *ViewManager::currentView() const +{ + return curView; +} + +void ViewManager::childEvent( QChildEvent *e ) +{ + if ( e->type() == QEvent::ChildInserted && ::qt_cast<Editor*>(e->child()) ) + addView( (QWidget*)e->child() ); + QWidget::childEvent( e ); +} + +void ViewManager::setError( int line ) +{ + QTextParagraph *p = ( (Editor*)curView )->document()->paragAt( line ); + if ( p ) { + ( (Editor*)curView )->setErrorSelection( line ); + ( (Editor*)curView )->setCursorPosition( line, 0 ); + ( (Editor*)curView )->viewport()->setFocus(); + ( (Editor*)curView )->makeFunctionVisible( p ); + ParagData *paragData = (ParagData*)p->extraData(); + if ( !paragData ) + paragData = new ParagData; + paragData->marker = ParagData::Error; + p->setExtraData( paragData ); + markerWidget->doRepaint(); + } +} + +void ViewManager::setStep( int line ) +{ + QTextParagraph *p = ( (Editor*)curView )->document()->firstParagraph(); + while ( p ) { + if ( p->extraData() ) + ( (ParagData*)p->extraData() )->step = FALSE; + p = p->next(); + } + p = ( (Editor*)curView )->document()->paragAt( line ); + if ( p ) { + ( (Editor*)curView )->setStepSelection( line ); + ( (Editor*)curView )->setCursorPosition( line, 0 ); + ( (Editor*)curView )->viewport()->setFocus(); + ( (Editor*)curView )->makeFunctionVisible( p ); + ParagData *paragData = (ParagData*)p->extraData(); + if ( !paragData ) + paragData = new ParagData; + paragData->step = TRUE; + p->setExtraData( paragData ); + markerWidget->doRepaint(); + } +} + +void ViewManager::clearStep() +{ + ( (Editor*)curView )->clearStepSelection(); + QTextParagraph *p = ( (Editor*)curView )->document()->firstParagraph(); + while ( p ) { + if ( p->extraData() ) + ( (ParagData*)p->extraData() )->step = FALSE; + p = p->next(); + } + markerWidget->doRepaint(); +} + +void ViewManager::setStackFrame( int line ) +{ + QTextParagraph *p = ( (Editor*)curView )->document()->paragAt( line ); + if ( p ) { + ( (Editor*)curView )->sync(); + ( (Editor*)curView )->setCursorPosition( line, 0 ); + ( (Editor*)curView )->ensureCursorVisible(); + ( (Editor*)curView )->viewport()->setFocus(); + ( (Editor*)curView )->makeFunctionVisible( p ); + ParagData *paragData = (ParagData*)p->extraData(); + if ( !paragData ) + paragData = new ParagData; + paragData->stackFrame = TRUE; + p->setExtraData( paragData ); + markerWidget->doRepaint(); + } +} + +void ViewManager::clearStackFrame() +{ + QTextParagraph *p = ( (Editor*)curView )->document()->firstParagraph(); + while ( p ) { + if ( p->extraData() ) { + ( (ParagData*)p->extraData() )->stackFrame = FALSE; + if ( ( (ParagData*)p->extraData() )->marker == ParagData::Error ) + ( (ParagData*)p->extraData() )->marker = ParagData::NoMarker; + } + p = p->next(); + } + markerWidget->doRepaint(); +} + +void ViewManager::resizeEvent( QResizeEvent *e ) +{ + QWidget::resizeEvent( e ); + markerWidget->doRepaint(); +} + +void ViewManager::clearErrorMarker() +{ + QTextParagraph *p = ( (Editor*)curView )->document()->firstParagraph(); + while ( p ) { + if ( p->extraData() ) + ( (ParagData*)p->extraData() )->marker = ParagData::NoMarker; + p = p->next(); + } + markerWidget->doRepaint(); +} + +void ViewManager::setBreakPoints( const QValueList<uint> &l ) +{ + QTextParagraph *p = ( (Editor*)curView )->document()->firstParagraph(); + int i = 0; + while ( p ) { + if ( l.find( i ) != l.end() ) { + if ( !p->extraData() ) { + ParagData *data = new ParagData; + p->setExtraData( data ); + } + ParagData *data = (ParagData*)p->extraData(); + data->marker = ParagData::Breakpoint; + } else if ( p->extraData() ) { + ParagData *data = (ParagData*)p->extraData(); + data->marker = ParagData::NoMarker; + } + p = p->next(); + ++i; + } + markerWidget->doRepaint(); +} + +QValueList<uint> ViewManager::breakPoints() const +{ + QValueList<uint> l; + int i = 0; + QTextParagraph *p = ( (Editor*)curView )->document()->firstParagraph(); + while ( p ) { + if ( p->extraData() && + ( (ParagData*)p->extraData() )->marker == ParagData::Breakpoint ) + l << i; + p = p->next(); + ++i; + } + return l; +} + +void ViewManager::showMarkerWidget( bool b ) +{ + if ( b ) + markerWidget->show(); + else + markerWidget->hide(); +} + +void ViewManager::emitMarkersChanged() +{ + emit markersChanged(); +} + +void ViewManager::cursorPositionChanged( int row, int col ) +{ + posLabel->setText( QString( " Line: %1 Col: %2" ).arg( row + 1 ).arg( col + 1 ) ); +} + +void ViewManager::showMessage( const QString &msg ) +{ + int row; + int col; + ( (QTextEdit*)currentView() )->getCursorPosition( &row, &col ); + posLabel->setText( msg ); + messageTimer->start( 1000, TRUE ); +} + +void ViewManager::clearStatusBar() +{ + int row; + int col; + ( (QTextEdit*)currentView() )->getCursorPosition( &row, &col ); + posLabel->setText( QString( " Line: %1 Col: %2" ).arg( row + 1 ).arg( col + 1 ) ); +} diff --git a/tools/designer/editor/viewmanager.h b/tools/designer/editor/viewmanager.h new file mode 100644 index 0000000..72d9859 --- /dev/null +++ b/tools/designer/editor/viewmanager.h @@ -0,0 +1,100 @@ + /********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef VIEWMANAGER_H +#define VIEWMANAGER_H + +#include <qwidget.h> +#include <qvaluelist.h> + +class QChildEvent; +class MarkerWidget; +class QVBoxLayout; +class QDockArea; +class QTextParagraph; +class QLabel; +class QTimer; + +class ViewManager : public QWidget +{ + Q_OBJECT + +public: + ViewManager( QWidget *parent, const char *name ); + + void addView( QWidget *view ); + QWidget *currentView() const; + void showMarkerWidget( bool ); + + void setError( int line ); + void setStep( int line ); + void setStackFrame( int line ); + void clearStep(); + void clearStackFrame(); + void setBreakPoints( const QValueList<uint> &l ); + QValueList<uint> breakPoints() const; + + void emitMarkersChanged(); + MarkerWidget *marker_widget() const { return markerWidget; } + +signals: + void markersChanged(); + void expandFunction( QTextParagraph *p ); + void collapseFunction( QTextParagraph *p ); + void collapse( bool all /*else only functions*/ ); + void expand( bool all /*else only functions*/ ); + void editBreakPoints(); + void isBreakpointPossible( bool &possible, const QString &code, int line ); + +protected slots: + void clearErrorMarker(); + void cursorPositionChanged( int row, int col ); + void showMessage( const QString &msg ); + void clearStatusBar(); + +protected: + void childEvent( QChildEvent *e ); + void resizeEvent( QResizeEvent *e ); + +private: + QWidget *curView; + MarkerWidget *markerWidget; + QVBoxLayout *layout; + QDockArea *dockArea; + QLabel *posLabel; + QString extraText; + QTimer *messageTimer; + +}; + +#endif diff --git a/tools/designer/editor/yyindent.cpp b/tools/designer/editor/yyindent.cpp new file mode 100644 index 0000000..df9a78f --- /dev/null +++ b/tools/designer/editor/yyindent.cpp @@ -0,0 +1,1170 @@ +/********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +/* + This file is a self-contained interactive indenter for C++ and Qt + Script. + + The general problem of indenting a C++ program is ill posed. On the + one hand, an indenter has to analyze programs written in a + free-form formal language that is best described in terms of + tokens, not characters, not lines. On the other hand, indentation + applies to lines and white space characters matter, and otherwise + the programs to indent are formally invalid in general, as they are + begin edited. + + The approach taken here works line by line. We receive a program + consisting of N lines or more, and we want to compute the + indentation appropriate for the Nth line. Lines beyond the Nth + lines are of no concern to us, so for simplicity we pretend the + program has exactly N lines and we call the Nth line the "bottom + line". Typically, we have to indent the bottom line when it's still + empty, so we concentrate our analysis on the N - 1 lines that + precede. + + By inspecting the (N - 1)-th line, the (N - 2)-th line, ... + backwards, we determine the kind of the bottom line and indent it + accordingly. + + * The bottom line is a comment line. See + bottomLineStartsInCComment() and + indentWhenBottomLineStartsInCComment(). + * The bottom line is a continuation line. See isContinuationLine() + and indentForContinuationLine(). + * The bottom line is a standalone line. See + indentForStandaloneLine(). + + Certain tokens that influence the indentation, notably braces, are + looked for in the lines. This is done by simple string comparison, + without a real tokenizer. Confusing constructs such as comments and + string literals are removed beforehand. +*/ + +#include <qregexp.h> + +/* qmake ignore Q_OBJECT */ + +/* + The indenter avoids getting stuck in almost infinite loops by + imposing arbitrary limits on the number of lines it analyzes when + looking for a construct. + + For example, the indenter never considers more than BigRoof lines + backwards when looking for the start of a C-style comment. +*/ +static const int SmallRoof = 40; +static const int BigRoof = 400; + +/* + The indenter supports a few parameters: + + * ppHardwareTabSize is the size of a '\t' in your favorite editor. + * ppIndentSize is the size of an indentation, or software tab + size. + * ppContinuationIndentSize is the extra indent for a continuation + line, when there is nothing to align against on the previous + line. + * ppCommentOffset is the indentation within a C-style comment, + when it cannot be picked up. +*/ + +static int ppHardwareTabSize = 8; +static int ppIndentSize = 4; +static int ppContinuationIndentSize = 8; + +static const int ppCommentOffset = 2; + +void setTabSize( int size ) +{ + ppHardwareTabSize = size; +} + +void setIndentSize( int size ) +{ + ppIndentSize = size; + ppContinuationIndentSize = 2 * size; +} + +static QRegExp *literal = 0; +static QRegExp *label = 0; +static QRegExp *inlineCComment = 0; +static QRegExp *braceX = 0; +static QRegExp *iflikeKeyword = 0; + +/* + Returns the first non-space character in the string t, or + QChar::null if the string is made only of white space. +*/ +static QChar firstNonWhiteSpace( const QString& t ) +{ + int i = 0; + while ( i < (int) t.length() ) { + if ( !t[i].isSpace() ) + return t[i]; + i++; + } + return QChar::null; +} + +/* + Returns TRUE if string t is made only of white space; otherwise + returns FALSE. +*/ +static bool isOnlyWhiteSpace( const QString& t ) +{ + return firstNonWhiteSpace( t ).isNull(); +} + +/* + Assuming string t is a line, returns the column number of a given + index. Column numbers and index are identical for strings that don't + contain '\t's. +*/ +int columnForIndex( const QString& t, int index ) +{ + int col = 0; + if ( index > (int) t.length() ) + index = t.length(); + + for ( int i = 0; i < index; i++ ) { + if ( t[i] == QChar('\t') ) { + col = ( (col / ppHardwareTabSize) + 1 ) * ppHardwareTabSize; + } else { + col++; + } + } + return col; +} + +/* + Returns the indentation size of string t. +*/ +int indentOfLine( const QString& t ) +{ + return columnForIndex( t, t.find(firstNonWhiteSpace(t)) ); +} + +/* + Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better + left alone since they break the "index equals column" rule. No + provisions are taken against '\n' or '\r', which shouldn't occur in + t anyway. +*/ +static inline void eraseChar( QString& t, int k, QChar ch ) +{ + if ( t[k] != '\t' ) + t[k] = ch; +} + +/* + Removes some nefast constructs from a code line and returns the + resulting line. +*/ +static QString trimmedCodeLine( const QString& t ) +{ + QString trimmed = t; + int k; + + /* + Replace character and string literals by X's, since they may + contain confusing characters (such as '{' and ';'). "Hello!" is + replaced by XXXXXXXX. The literals are rigourously of the same + length before and after; otherwise, we would break alignment of + continuation lines. + */ + k = 0; + while ( (k = trimmed.find(*literal, k)) != -1 ) { + for ( int i = 0; i < literal->matchedLength(); i++ ) + eraseChar( trimmed, k + i, 'X' ); + k += literal->matchedLength(); + } + + /* + Replace inline C-style comments by spaces. Other comments are + handled elsewhere. + */ + k = 0; + while ( (k = trimmed.find(*inlineCComment, k)) != -1 ) { + for ( int i = 0; i < inlineCComment->matchedLength(); i++ ) + eraseChar( trimmed, k + i, ' ' ); + k += inlineCComment->matchedLength(); + } + + /* + Replace goto and switch labels by whitespace, but be careful + with this case: + + foo1: bar1; + bar2; + */ + while ( trimmed.findRev(':') != -1 && trimmed.find(*label) != -1 ) { + QString cap1 = label->cap( 1 ); + int pos1 = label->pos( 1 ); + int stop = cap1.length(); + + if ( pos1 + stop < (int) trimmed.length() && ppIndentSize < stop ) + stop = ppIndentSize; + + int i = 0; + while ( i < stop ) { + eraseChar( trimmed, pos1 + i, ' ' ); + i++; + } + while ( i < (int) cap1.length() ) { + eraseChar( trimmed, pos1 + i, ';' ); + i++; + } + } + + /* + Remove C++-style comments. + */ + k = trimmed.find( "//" ); + if ( k != -1 ) + trimmed.truncate( k ); + + return trimmed; +} + +/* + Returns '(' if the last parenthesis is opening, ')' if it is + closing, and QChar::null if there are no parentheses in t. +*/ +static inline QChar lastParen( const QString& t ) +{ + int i = t.length(); + while ( i > 0 ) { + i--; + if ( t[i] == QChar('(') || t[i] == QChar(')') ) + return t[i]; + } + return QChar::null; +} + +/* + Returns TRUE if typedIn the same as okayCh or is null; otherwise + returns FALSE. +*/ +static inline bool okay( QChar typedIn, QChar okayCh ) +{ + return typedIn == QChar::null || typedIn == okayCh; +} + +/* + The "linizer" is a group of functions and variables to iterate + through the source code of the program to indent. The program is + given as a list of strings, with the bottom line being the line to + indent. The actual program might contain extra lines, but those are + uninteresting and not passed over to us. +*/ + +struct LinizerState +{ + QString line; + int braceDepth; + bool leftBraceFollows; + + QStringList::ConstIterator iter; + bool inCComment; + bool pendingRightBrace; +}; + +static QStringList *yyProgram = 0; +static LinizerState *yyLinizerState = 0; + +// shorthands +static const QString *yyLine = 0; +static const int *yyBraceDepth = 0; +static const bool *yyLeftBraceFollows = 0; + +/* + Saves and restores the state of the global linizer. This enables + backtracking. +*/ +#define YY_SAVE() \ + LinizerState savedState = *yyLinizerState +#define YY_RESTORE() \ + *yyLinizerState = savedState + +/* + Advances to the previous line in yyProgram and update yyLine + accordingly. yyLine is cleaned from comments and other damageable + constructs. Empty lines are skipped. +*/ +static bool readLine() +{ + int k; + + yyLinizerState->leftBraceFollows = + ( firstNonWhiteSpace(yyLinizerState->line) == QChar('{') ); + + do { + if ( yyLinizerState->iter == yyProgram->begin() ) { + yyLinizerState->line = QString::null; + return FALSE; + } + + --yyLinizerState->iter; + yyLinizerState->line = *yyLinizerState->iter; + + yyLinizerState->line = trimmedCodeLine( yyLinizerState->line ); + + /* + Remove C-style comments that span multiple lines. If the + bottom line starts in a C-style comment, we are not aware + of that and eventually yyLine will contain a slash-aster. + + Notice that both if's can be executed, since + yyLinizerState->inCComment is potentially set to FALSE in + the first if. The order of the if's is also important. + */ + + if ( yyLinizerState->inCComment ) { + QString slashAster( "/*" ); + + k = yyLinizerState->line.find( slashAster ); + if ( k == -1 ) { + yyLinizerState->line = QString::null; + } else { + yyLinizerState->line.truncate( k ); + yyLinizerState->inCComment = FALSE; + } + } + + if ( !yyLinizerState->inCComment ) { + QString asterSlash( "*/" ); + + k = yyLinizerState->line.find( asterSlash ); + if ( k != -1 ) { + for ( int i = 0; i < k + 2; i++ ) + eraseChar( yyLinizerState->line, i, ' ' ); + yyLinizerState->inCComment = TRUE; + } + } + + /* + Remove preprocessor directives. + */ + k = 0; + while ( k < (int) yyLinizerState->line.length() ) { + QChar ch = yyLinizerState->line[k]; + if ( ch == QChar('#') ) { + yyLinizerState->line = QString::null; + } else if ( !ch.isSpace() ) { + break; + } + k++; + } + + /* + Remove trailing spaces. + */ + k = yyLinizerState->line.length(); + while ( k > 0 && yyLinizerState->line[k - 1].isSpace() ) + k--; + yyLinizerState->line.truncate( k ); + + /* + '}' increment the brace depth and '{' decrements it and not + the other way around, as we are parsing backwards. + */ + yyLinizerState->braceDepth += + yyLinizerState->line.contains( '}' ) - + yyLinizerState->line.contains( '{' ); + + /* + We use a dirty trick for + + } else ... + + We don't count the '}' yet, so that it's more or less + equivalent to the friendly construct + + } + else ... + */ + if ( yyLinizerState->pendingRightBrace ) + yyLinizerState->braceDepth++; + yyLinizerState->pendingRightBrace = + ( yyLinizerState->line.find(*braceX) == 0 ); + if ( yyLinizerState->pendingRightBrace ) + yyLinizerState->braceDepth--; + } while ( yyLinizerState->line.isEmpty() ); + + return TRUE; +} + +/* + Resets the linizer to its initial state, with yyLine containing the + line above the bottom line of the program. +*/ +static void startLinizer() +{ + yyLinizerState->braceDepth = 0; + yyLinizerState->inCComment = FALSE; + yyLinizerState->pendingRightBrace = FALSE; + + yyLine = &yyLinizerState->line; + yyBraceDepth = &yyLinizerState->braceDepth; + yyLeftBraceFollows = &yyLinizerState->leftBraceFollows; + + yyLinizerState->iter = yyProgram->end(); + --yyLinizerState->iter; + yyLinizerState->line = *yyLinizerState->iter; + readLine(); +} + +/* + Returns TRUE if the start of the bottom line of yyProgram (and + potentially the whole line) is part of a C-style comment; otherwise + returns FALSE. +*/ +static bool bottomLineStartsInCComment() +{ + QString slashAster( "/*" ); + QString asterSlash( "*/" ); + + /* + We could use the linizer here, but that would slow us down + terribly. We are better to trim only the code lines we need. + */ + QStringList::ConstIterator p = yyProgram->end(); + --p; // skip bottom line + + for ( int i = 0; i < BigRoof; i++ ) { + if ( p == yyProgram->begin() ) + return FALSE; + --p; + + if ( (*p).find(slashAster) != -1 || (*p).find(asterSlash) != -1 ) { + QString trimmed = trimmedCodeLine( *p ); + + if ( trimmed.find(slashAster) != -1 ) { + return TRUE; + } else if ( trimmed.find(asterSlash) != -1 ) { + return FALSE; + } + } + } + return FALSE; +} + +/* + Returns the recommended indent for the bottom line of yyProgram + assuming that it starts in a C-style comment, a condition that is + tested elsewhere. + + Essentially, we're trying to align against some text on the previous + line. +*/ +static int indentWhenBottomLineStartsInCComment() +{ + int k = yyLine->findRev( "/*" ); + if ( k == -1 ) { + /* + We found a normal text line in a comment. Align the + bottom line with the text on this line. + */ + return indentOfLine( *yyLine ); + } else { + /* + The C-style comment starts on this line. If there is + text on the same line, align with it. Otherwise, align + with the slash-aster plus a given offset. + */ + int indent = columnForIndex( *yyLine, k ); + k += 2; + while ( k < (int) yyLine->length() ) { + if ( !(*yyLine)[k].isSpace() ) + return columnForIndex( *yyLine, k ); + k++; + } + return indent + ppCommentOffset; + } +} + +/* + A function called match...() modifies the linizer state. If it + returns TRUE, yyLine is the top line of the matched construct; + otherwise, the linizer is left in an unknown state. + + A function called is...() keeps the linizer state intact. +*/ + +/* + Returns TRUE if the current line (and upwards) forms a braceless + control statement; otherwise returns FALSE. + + The first line of the following example is a "braceless control + statement": + + if ( x ) + y; +*/ +static bool matchBracelessControlStatement() +{ + int delimDepth = 0; + + if ( yyLine->endsWith("else") ) + return TRUE; + + if ( !yyLine->endsWith(")") ) + return FALSE; + + for ( int i = 0; i < SmallRoof; i++ ) { + int j = yyLine->length(); + while ( j > 0 ) { + j--; + QChar ch = (*yyLine)[j]; + + switch ( ch.unicode() ) { + case ')': + delimDepth++; + break; + case '(': + delimDepth--; + if ( delimDepth == 0 ) { + if ( yyLine->find(*iflikeKeyword) != -1 ) { + /* + We have + + if ( x ) + y + + "if ( x )" is not part of the statement + "y". + */ + return TRUE; + } + } + if ( delimDepth == -1 ) { + /* + We have + + if ( (1 + + 2) + + and not + + if ( 1 + + 2 ) + */ + return FALSE; + } + break; + case '{': + case '}': + case ';': + /* + We met a statement separator, but not where we + expected it. What follows is probably a weird + continuation line. Be careful with ';' in for, + though. + */ + if ( ch != QChar(';') || delimDepth == 0 ) + return FALSE; + } + } + + if ( !readLine() ) + break; + } + return FALSE; +} + +/* + Returns TRUE if yyLine is an unfinished line; otherwise returns + FALSE. + + In many places we'll use the terms "standalone line", "unfinished + line" and "continuation line". The meaning of these should be + evident from this code example: + + a = b; // standalone line + c = d + // unfinished line + e + // unfinished continuation line + f + // unfinished continuation line + g; // continuation line +*/ +static bool isUnfinishedLine() +{ + bool unf = FALSE; + + YY_SAVE(); + + if ( yyLine->isEmpty() ) + return FALSE; + + QChar lastCh = (*yyLine)[(int) yyLine->length() - 1]; + if ( QString("{};").find(lastCh) == -1 && !yyLine->endsWith("...") ) { + /* + It doesn't end with ';' or similar. If it's neither + "Q_OBJECT" nor "if ( x )", it must be an unfinished line. + */ + unf = ( yyLine->contains("Q_OBJECT") == 0 && + !matchBracelessControlStatement() ); + } else if ( lastCh == QChar(';') ) { + if ( lastParen(*yyLine) == QChar('(') ) { + /* + Exception: + + for ( int i = 1; i < 10; + */ + unf = TRUE; + } else if ( readLine() && yyLine->endsWith(";") && + lastParen(*yyLine) == QChar('(') ) { + /* + Exception: + + for ( int i = 1; + i < 10; + */ + unf = TRUE; + } + } + + YY_RESTORE(); + return unf; +} + +/* + Returns TRUE if yyLine is a continuation line; otherwise returns + FALSE. +*/ +static bool isContinuationLine() +{ + bool cont = FALSE; + + YY_SAVE(); + if ( readLine() ) + cont = isUnfinishedLine(); + YY_RESTORE(); + return cont; +} + +/* + Returns the recommended indent for the bottom line of yyProgram, + assuming it's a continuation line. + + We're trying to align the continuation line against some parenthesis + or other bracked left opened on a previous line, or some interesting + operator such as '='. +*/ +static int indentForContinuationLine() +{ + int braceDepth = 0; + int delimDepth = 0; + + bool leftBraceFollowed = *yyLeftBraceFollows; + + for ( int i = 0; i < SmallRoof; i++ ) { + int hook = -1; + + int j = yyLine->length(); + while ( j > 0 && hook < 0 ) { + j--; + QChar ch = (*yyLine)[j]; + + switch ( ch.unicode() ) { + case ')': + case ']': + delimDepth++; + break; + case '}': + braceDepth++; + break; + case '(': + case '[': + delimDepth--; + /* + An unclosed delimiter is a good place to align at, + at least for some styles (including Trolltech's). + */ + if ( delimDepth == -1 ) + hook = j; + break; + case '{': + braceDepth--; + /* + A left brace followed by other stuff on the same + line is typically for an enum or an initializer. + Such a brace must be treated just like the other + delimiters. + */ + if ( braceDepth == -1 ) { + if ( j < (int) yyLine->length() - 1 ) { + hook = j; + } else { + return 0; // shouldn't happen + } + } + break; + case '=': + /* + An equal sign is a very natural alignment hook + because it's usually the operator with the lowest + precedence in statements it appears in. Case in + point: + + int x = 1 + + 2; + + However, we have to beware of constructs such as + default arguments and explicit enum constant + values: + + void foo( int x = 0, + int y = 0 ); + + And not + + void foo( int x = 0, + int y = 0 ); + + These constructs are caracterized by a ',' at the + end of the unfinished lines or by unbalanced + parentheses. + */ + if ( QString("!=<>").find((*yyLine)[j - 1]) == -1 && + (*yyLine)[j + 1] != '=' ) { + if ( braceDepth == 0 && delimDepth == 0 && + j < (int) yyLine->length() - 1 && + !yyLine->endsWith(",") && + (yyLine->contains('(') == yyLine->contains(')')) ) + hook = j; + } + } + } + + if ( hook >= 0 ) { + /* + Yes, we have a delimiter or an operator to align + against! We don't really align against it, but rather + against the following token, if any. In this example, + the following token is "11": + + int x = ( 11 + + 2 ); + + If there is no such token, we use a continuation indent: + + static QRegExp foo( QString( + "foo foo foo foo foo foo foo foo foo") ); + */ + hook++; + while ( hook < (int) yyLine->length() ) { + if ( !(*yyLine)[hook].isSpace() ) + return columnForIndex( *yyLine, hook ); + hook++; + } + return indentOfLine( *yyLine ) + ppContinuationIndentSize; + } + + if ( braceDepth != 0 ) + break; + + /* + The line's delimiters are balanced. It looks like a + continuation line or something. + */ + if ( delimDepth == 0 ) { + if ( leftBraceFollowed ) { + /* + We have + + int main() + { + + or + + Bar::Bar() + : Foo( x ) + { + + The "{" should be flush left. + */ + if ( !isContinuationLine() ) + return indentOfLine( *yyLine ); + } else if ( isContinuationLine() || yyLine->endsWith(",") ) { + /* + We have + + x = a + + b + + c; + + or + + int t[] = { + 1, 2, 3, + 4, 5, 6 + + The "c;" should fall right under the "b +", and the + "4, 5, 6" right under the "1, 2, 3,". + */ + return indentOfLine( *yyLine ); + } else { + /* + We have + + stream << 1 + + 2; + + We could, but we don't, try to analyze which + operator has precedence over which and so on, to + obtain the excellent result + + stream << 1 + + 2; + + We do have a special trick above for the assignment + operator above, though. + */ + return indentOfLine( *yyLine ) + ppContinuationIndentSize; + } + } + + if ( !readLine() ) + break; + } + return 0; +} + +/* + Returns the recommended indent for the bottom line of yyProgram if + that line is standalone (or should be indented likewise). + + Indenting a standalone line is tricky, mostly because of braceless + control statements. Grossly, we are looking backwards for a special + line, a "hook line", that we can use as a starting point to indent, + and then modify the indentation level according to the braces met + along the way to that hook. + + Let's consider a few examples. In all cases, we want to indent the + bottom line. + + Example 1: + + x = 1; + y = 2; + + The hook line is "x = 1;". We met 0 opening braces and 0 closing + braces. Therefore, "y = 2;" inherits the indent of "x = 1;". + + Example 2: + + if ( x ) { + y; + + The hook line is "if ( x ) {". No matter what precedes it, "y;" has + to be indented one level deeper than the hook line, since we met one + opening brace along the way. + + Example 3: + + if ( a ) + while ( b ) { + c; + } + d; + + To indent "d;" correctly, we have to go as far as the "if ( a )". + Compare with + + if ( a ) { + while ( b ) { + c; + } + d; + + Still, we're striving to go back as little as possible to accomodate + people with irregular indentation schemes. A hook line near at hand + is much more reliable than a remote one. +*/ +static int indentForStandaloneLine() +{ + for ( int i = 0; i < SmallRoof; i++ ) { + if ( !*yyLeftBraceFollows ) { + YY_SAVE(); + + if ( matchBracelessControlStatement() ) { + /* + The situation is this, and we want to indent "z;": + + if ( x && + y ) + z; + + yyLine is "if ( x &&". + */ + return indentOfLine( *yyLine ) + ppIndentSize; + } + YY_RESTORE(); + } + + if ( yyLine->endsWith(";") || yyLine->contains('{') > 0 ) { + /* + The situation is possibly this, and we want to indent + "z;": + + while ( x ) + y; + z; + + We return the indent of "while ( x )". In place of "y;", + any arbitrarily complex compound statement can appear. + */ + + if ( *yyBraceDepth > 0 ) { + do { + if ( !readLine() ) + break; + } while ( *yyBraceDepth > 0 ); + } + + LinizerState hookState; + + while ( isContinuationLine() ) + readLine(); + hookState = *yyLinizerState; + + readLine(); + if ( *yyBraceDepth <= 0 ) { + do { + if ( !matchBracelessControlStatement() ) + break; + hookState = *yyLinizerState; + } while ( readLine() ); + } + + *yyLinizerState = hookState; + + while ( isContinuationLine() ) + readLine(); + + /* + Never trust lines containing only '{' or '}', as some + people (Richard M. Stallman) format them weirdly. + */ + if ( yyLine->stripWhiteSpace().length() > 1 ) + return indentOfLine( *yyLine ) - *yyBraceDepth * ppIndentSize; + } + + if ( !readLine() ) + return -*yyBraceDepth * ppIndentSize; + } + return 0; +} + +/* + Constructs global variables used by the indenter. +*/ +static void initializeIndenter() +{ + literal = new QRegExp( "([\"'])(?:\\\\.|[^\\\\])*\\1" ); + literal->setMinimal( TRUE ); + label = new QRegExp( + "^\\s*((?:case\\b([^:]|::)+|[a-zA-Z_0-9]+)(?:\\s+slots)?:)(?!:)" ); + inlineCComment = new QRegExp( "/\\*.*\\*/" ); + inlineCComment->setMinimal( TRUE ); + braceX = new QRegExp( "^\\s*\\}\\s*(?:else|catch)\\b" ); + iflikeKeyword = new QRegExp( "\\b(?:catch|do|for|if|while)\\b" ); + + yyLinizerState = new LinizerState; +} + +/* + Destroys global variables used by the indenter. +*/ +static void terminateIndenter() +{ + delete literal; + delete label; + delete inlineCComment; + delete braceX; + delete iflikeKeyword; + delete yyLinizerState; +} + +/* + Returns the recommended indent for the bottom line of program. + Unless null, typedIn stores the character of yyProgram that + triggered reindentation. + + This function works better if typedIn is set properly; it is + slightly more conservative if typedIn is completely wild, and + slighly more liberal if typedIn is always null. The user might be + annoyed by the liberal behavior. +*/ +int indentForBottomLine( const QStringList& program, QChar typedIn ) +{ + if ( program.isEmpty() ) + return 0; + + initializeIndenter(); + + yyProgram = new QStringList( program ); + startLinizer(); + + const QString& bottomLine = program.last(); + QChar firstCh = firstNonWhiteSpace( bottomLine ); + int indent; + + if ( bottomLineStartsInCComment() ) { + /* + The bottom line starts in a C-style comment. Indent it + smartly, unless the user has already played around with it, + in which case it's better to leave her stuff alone. + */ + if ( isOnlyWhiteSpace(bottomLine) ) { + indent = indentWhenBottomLineStartsInCComment(); + } else { + indent = indentOfLine( bottomLine ); + } + } else if ( okay(typedIn, '#') && firstCh == QChar('#') ) { + /* + Preprocessor directives go flush left. + */ + indent = 0; + } else { + if ( isUnfinishedLine() ) { + indent = indentForContinuationLine(); + } else { + indent = indentForStandaloneLine(); + } + + if ( okay(typedIn, '}') && firstCh == QChar('}') ) { + /* + A closing brace is one level more to the left than the + code it follows. + */ + indent -= ppIndentSize; + } else if ( okay(typedIn, ':') ) { + QRegExp caseLabel( + "\\s*(?:case\\b(?:[^:]|::)+" + "|(?:public|protected|private|signals|default)(?:\\s+slots)?\\s*" + ")?:.*" ); + + if ( caseLabel.exactMatch(bottomLine) ) { + /* + Move a case label (or the ':' in front of a + constructor initialization list) one level to the + left, but only if the user did not play around with + it yet. Some users have exotic tastes in the + matter, and most users probably are not patient + enough to wait for the final ':' to format their + code properly. + + We don't attempt the same for goto labels, as the + user is probably the middle of "foo::bar". (Who + uses goto, anyway?) + */ + if ( indentOfLine(bottomLine) <= indent ) + indent -= ppIndentSize; + else + indent = indentOfLine( bottomLine ); + } + } + } + delete yyProgram; + terminateIndenter(); + return QMAX( 0, indent ); +} + +#ifdef Q_TEST_YYINDENT +/* + Test driver. +*/ + +#include <qfile.h> +#include <qtextstream.h> + +#include <errno.h> + +static QString fileContents( const QString& fileName ) +{ + QFile f( fileName ); + if ( !f.open(IO_ReadOnly) ) { + qWarning( "yyindent error: Cannot open file '%s' for reading: %s", + fileName.latin1(), strerror(errno) ); + return QString::null; + } + + QTextStream t( &f ); + QString contents = t.read(); + f.close(); + if ( contents.isEmpty() ) + qWarning( "yyindent error: File '%s' is empty", fileName.latin1() ); + return contents; +} + +int main( int argc, char **argv ) +{ + if ( argc != 2 ) { + qWarning( "usage: yyindent file.cpp" ); + return 1; + } + + QString code = fileContents( argv[1] ); + QStringList program = QStringList::split( '\n', code, TRUE ); + QStringList p; + QString out; + + while ( !program.isEmpty() && program.last().stripWhiteSpace().isEmpty() ) + program.remove( program.fromLast() ); + + QStringList::ConstIterator line = program.begin(); + while ( line != program.end() ) { + p.push_back( *line ); + QChar typedIn = firstNonWhiteSpace( *line ); + if ( p.last().endsWith(":") ) + typedIn = ':'; + + int indent = indentForBottomLine( p, typedIn ); + + if ( !(*line).stripWhiteSpace().isEmpty() ) { + for ( int j = 0; j < indent; j++ ) + out += " "; + out += (*line).stripWhiteSpace(); + } + out += "\n"; + ++line; + } + + while ( out.endsWith("\n") ) + out.truncate( out.length() - 1 ); + + printf( "%s\n", out.latin1() ); + return 0; +} +#endif |