1 | /**********************************************************************
|
---|
2 | ** Copyright (C) 2005-2007 Trolltech ASA. All rights reserved.
|
---|
3 | **
|
---|
4 | ** This file is part of Qt Designer.
|
---|
5 | **
|
---|
6 | ** This file may be distributed and/or modified under the terms of the
|
---|
7 | ** GNU General Public License version 2 as published by the Free Software
|
---|
8 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
9 | ** packaging of this file.
|
---|
10 | **
|
---|
11 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
|
---|
12 | ** licenses may use this file in accordance with the Qt Commercial License
|
---|
13 | ** Agreement provided with the Software.
|
---|
14 | **
|
---|
15 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
---|
16 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
---|
17 | **
|
---|
18 | ** See http://www.trolltech.com/gpl/ for GPL licensing information.
|
---|
19 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
|
---|
20 | ** information about Qt Commercial License Agreements.
|
---|
21 | **
|
---|
22 | ** Contact info@trolltech.com if any conditions of this licensing are
|
---|
23 | ** not clear to you.
|
---|
24 | **
|
---|
25 | **********************************************************************/
|
---|
26 |
|
---|
27 | #include "completion.h"
|
---|
28 | #include "paragdata.h"
|
---|
29 | #include "editor.h"
|
---|
30 | #include <qlistbox.h>
|
---|
31 | #include <qvbox.h>
|
---|
32 | #include <qmap.h>
|
---|
33 | #include <private/qrichtext_p.h>
|
---|
34 | #include <qapplication.h>
|
---|
35 | #include <qregexp.h>
|
---|
36 | #include "arghintwidget.h"
|
---|
37 | #include <qsizegrip.h>
|
---|
38 | #include <qtimer.h>
|
---|
39 |
|
---|
40 | static QColor getColor( const QString &type )
|
---|
41 | {
|
---|
42 | if ( type == "function" || type == "slot" || type == "package" )
|
---|
43 | return Qt::blue;
|
---|
44 | else if ( type == "variable" || type == "widget" || type == "dir" )
|
---|
45 | return Qt::darkRed;
|
---|
46 | else if ( type == "object" || type == "class" )
|
---|
47 | return Qt::darkBlue;
|
---|
48 | else if ( type == "property" )
|
---|
49 | return Qt::darkGreen;
|
---|
50 | else if ( type == "enum" )
|
---|
51 | return Qt::darkYellow;
|
---|
52 | return Qt::black;
|
---|
53 | }
|
---|
54 |
|
---|
55 | class CompletionItem : public QListBoxItem
|
---|
56 | {
|
---|
57 | public:
|
---|
58 | CompletionItem( QListBox *lb, const QString &txt, const QString &t, const QString &p,
|
---|
59 | const QString &pre, const QString &p2 )
|
---|
60 | : QListBoxItem( lb ), type( t ), postfix( p ), prefix( pre ), postfix2( p2 ),
|
---|
61 | parag( 0 ), lastState( FALSE ) { setText( txt ); }
|
---|
62 | ~CompletionItem() { delete parag; }
|
---|
63 | void paint( QPainter *painter ) {
|
---|
64 | if ( lastState != isSelected() ) {
|
---|
65 | delete parag;
|
---|
66 | parag = 0;
|
---|
67 | }
|
---|
68 | lastState = isSelected();
|
---|
69 | if ( !parag )
|
---|
70 | setupParagraph();
|
---|
71 | parag->paint( *painter, listBox()->colorGroup() );
|
---|
72 | }
|
---|
73 |
|
---|
74 | int height( const QListBox * ) const {
|
---|
75 | if ( !parag )
|
---|
76 | ( (CompletionItem*)this )->setupParagraph();
|
---|
77 | return parag->rect().height();
|
---|
78 | }
|
---|
79 | int width( const QListBox * ) const {
|
---|
80 | if ( !parag )
|
---|
81 | ( (CompletionItem*)this )->setupParagraph();
|
---|
82 | return parag->rect().width() - 2;
|
---|
83 | }
|
---|
84 | QString text() const { return QListBoxItem::text() + postfix; }
|
---|
85 |
|
---|
86 | private:
|
---|
87 | void setupParagraph();
|
---|
88 | QString type, postfix, prefix, postfix2;
|
---|
89 | QTextParagraph *parag;
|
---|
90 | bool lastState;
|
---|
91 |
|
---|
92 | };
|
---|
93 |
|
---|
94 | void CompletionItem::setupParagraph() {
|
---|
95 | if ( !parag ) {
|
---|
96 | QTextFormatter *formatter;
|
---|
97 | formatter = new QTextFormatterBreakWords;
|
---|
98 | formatter->setWrapEnabled( FALSE );
|
---|
99 | parag = new QTextParagraph( 0 );
|
---|
100 | parag->setTabStops( listBox()->fontMetrics().width( "propertyXXXX" ) );
|
---|
101 | parag->pseudoDocument()->pFormatter = formatter;
|
---|
102 | parag->insert( 0, " " + type + ( type.isEmpty() ? " " : "\t" ) + prefix +
|
---|
103 | QListBoxItem::text() + postfix + postfix2 );
|
---|
104 | bool selCol = isSelected() && listBox()->colorGroup().highlightedText() != listBox()->colorGroup().text();
|
---|
105 | QColor sc = selCol ? listBox()->colorGroup().highlightedText() : getColor( type );
|
---|
106 | QTextFormat *f1 = parag->formatCollection()->format( listBox()->font(), sc );
|
---|
107 | QTextFormat *f3 = parag->formatCollection()->format( listBox()->font(), isSelected() ?
|
---|
108 | listBox()->colorGroup().highlightedText() :
|
---|
109 | listBox()->colorGroup().text() );
|
---|
110 | QFont f( listBox()->font() );
|
---|
111 | f.setBold( TRUE );
|
---|
112 | QTextFormat *f2 =
|
---|
113 | parag->formatCollection()->format( f, isSelected() ? listBox()->colorGroup().highlightedText() :
|
---|
114 | listBox()->colorGroup().text() );
|
---|
115 | parag->setFormat( 1, type.length() + 1, f1 );
|
---|
116 | parag->setFormat( type.length() + 2, prefix.length() + QListBoxItem::text().length(), f2 );
|
---|
117 | if ( !postfix.isEmpty() )
|
---|
118 | parag->setFormat( type.length() + 2 + prefix.length() + QListBoxItem::text().length(),
|
---|
119 | postfix.length(), f3 );
|
---|
120 | parag->setFormat( type.length() + 2 + prefix.length() + QListBoxItem::text().length() + postfix.length(),
|
---|
121 | postfix2.length(), f3 );
|
---|
122 | f1->removeRef();
|
---|
123 | f2->removeRef();
|
---|
124 | f3->removeRef();
|
---|
125 | parag->format();
|
---|
126 | }
|
---|
127 | }
|
---|
128 |
|
---|
129 |
|
---|
130 | EditorCompletion::EditorCompletion( Editor *e )
|
---|
131 | {
|
---|
132 | enabled = TRUE;
|
---|
133 | lastDoc = 0;
|
---|
134 | completionPopup = new QVBox( e->topLevelWidget(), 0, WType_Popup );
|
---|
135 | completionPopup->setFrameStyle( QFrame::Box | QFrame::Plain );
|
---|
136 | completionPopup->setLineWidth( 1 );
|
---|
137 | functionLabel = new ArgHintWidget( e->topLevelWidget(), "editor_function_lbl" );
|
---|
138 | functionLabel->hide();
|
---|
139 | completionListBox = new QListBox( completionPopup, "editor_completion_lb" );
|
---|
140 | completionListBox->setFrameStyle( QFrame::NoFrame );
|
---|
141 | completionListBox->installEventFilter( this );
|
---|
142 | completionListBox->setHScrollBarMode( QScrollView::AlwaysOn );
|
---|
143 | completionListBox->setVScrollBarMode( QScrollView::AlwaysOn );
|
---|
144 | completionListBox->setCornerWidget( new QSizeGrip( completionListBox, "editor_cornerwidget" ) );
|
---|
145 | completionPopup->installEventFilter( this );
|
---|
146 | functionLabel->installEventFilter( this );
|
---|
147 | completionPopup->setFocusProxy( completionListBox );
|
---|
148 | completionOffset = 0;
|
---|
149 | curEditor = e;
|
---|
150 | curEditor->installEventFilter( this );
|
---|
151 | }
|
---|
152 |
|
---|
153 | EditorCompletion::~EditorCompletion()
|
---|
154 | {
|
---|
155 | delete completionPopup;
|
---|
156 | delete functionLabel;
|
---|
157 | }
|
---|
158 |
|
---|
159 | void EditorCompletion::addCompletionEntry( const QString &s, QTextDocument *, bool strict )
|
---|
160 | {
|
---|
161 | QChar key( s[ 0 ] );
|
---|
162 | QMap<QChar, QStringList>::Iterator it = completionMap.find( key );
|
---|
163 | if ( it == completionMap.end() ) {
|
---|
164 | completionMap.insert( key, QStringList( s ) );
|
---|
165 | } else {
|
---|
166 | if ( strict ) {
|
---|
167 | QStringList::Iterator sit;
|
---|
168 | for ( sit = (*it).begin(); sit != (*it).end(); ) {
|
---|
169 | QStringList::Iterator it2 = sit;
|
---|
170 | ++sit;
|
---|
171 | if ( (*it2).length() > s.length() && (*it2).left( s.length() ) == s ) {
|
---|
172 | if ( (*it2)[ (int)s.length() ].isLetter() && (*it2)[ (int)s.length() ].upper() != (*it2)[ (int)s.length() ] )
|
---|
173 | return;
|
---|
174 | } else if ( s.length() > (*it2).length() && s.left( (*it2).length() ) == *it2 ) {
|
---|
175 | if ( s[ (int)(*it2).length() ].isLetter() && s[ (int)(*it2).length() ].upper() != s[ (int)(*it2).length() ] )
|
---|
176 | (*it).remove( it2 );
|
---|
177 | }
|
---|
178 | }
|
---|
179 | }
|
---|
180 | (*it).append( s );
|
---|
181 | }
|
---|
182 | }
|
---|
183 |
|
---|
184 | QValueList<CompletionEntry> EditorCompletion::completionList( const QString &s, QTextDocument *doc ) const
|
---|
185 | {
|
---|
186 | if ( doc )
|
---|
187 | ( (EditorCompletion*)this )->updateCompletionMap( doc );
|
---|
188 |
|
---|
189 | QChar key( s[ 0 ] );
|
---|
190 | QMap<QChar, QStringList>::ConstIterator it = completionMap.find( key );
|
---|
191 | if ( it == completionMap.end() )
|
---|
192 | return QValueList<CompletionEntry>();
|
---|
193 | QStringList::ConstIterator it2 = (*it).begin();
|
---|
194 | QValueList<CompletionEntry> lst;
|
---|
195 | int len = s.length();
|
---|
196 | for ( ; it2 != (*it).end(); ++it2 ) {
|
---|
197 | CompletionEntry c;
|
---|
198 | c.type = "";
|
---|
199 | c.text = *it2;
|
---|
200 | c.postfix = "";
|
---|
201 | c.prefix = "";
|
---|
202 | c.postfix2 = "";
|
---|
203 | if ( (int)(*it2).length() > len && (*it2).left( len ) == s && lst.find( c ) == lst.end() )
|
---|
204 | lst << c;
|
---|
205 | }
|
---|
206 |
|
---|
207 | return lst;
|
---|
208 | }
|
---|
209 |
|
---|
210 | void EditorCompletion::updateCompletionMap( QTextDocument *doc )
|
---|
211 | {
|
---|
212 | bool strict = TRUE;
|
---|
213 | if ( doc != lastDoc )
|
---|
214 | strict = FALSE;
|
---|
215 | lastDoc = doc;
|
---|
216 | QTextParagraph *s = doc->firstParagraph();
|
---|
217 | if ( !s->extraData() )
|
---|
218 | s->setExtraData( new ParagData );
|
---|
219 | while ( s ) {
|
---|
220 | if ( s->length() == ( (ParagData*)s->extraData() )->lastLengthForCompletion ) {
|
---|
221 | s = s->next();
|
---|
222 | continue;
|
---|
223 | }
|
---|
224 |
|
---|
225 | QChar c;
|
---|
226 | QString buffer;
|
---|
227 | for ( int i = 0; i < s->length(); ++i ) {
|
---|
228 | c = s->at( i )->c;
|
---|
229 | if ( c.isLetter() || c.isNumber() || c == '_' || c == '#' ) {
|
---|
230 | buffer += c;
|
---|
231 | } else {
|
---|
232 | addCompletionEntry( buffer, doc, strict );
|
---|
233 | buffer = QString::null;
|
---|
234 | }
|
---|
235 | }
|
---|
236 | if ( !buffer.isEmpty() )
|
---|
237 | addCompletionEntry( buffer, doc, strict );
|
---|
238 |
|
---|
239 | ( (ParagData*)s->extraData() )->lastLengthForCompletion = s->length();
|
---|
240 | s = s->next();
|
---|
241 | }
|
---|
242 | }
|
---|
243 |
|
---|
244 | bool EditorCompletion::doCompletion()
|
---|
245 | {
|
---|
246 | searchString = "";
|
---|
247 | if ( !curEditor )
|
---|
248 | return FALSE;
|
---|
249 |
|
---|
250 | QTextCursor *cursor = curEditor->textCursor();
|
---|
251 | QTextDocument *doc = curEditor->document();
|
---|
252 |
|
---|
253 | if ( cursor->index() > 0 && cursor->paragraph()->at( cursor->index() - 1 )->c == '.' &&
|
---|
254 | ( cursor->index() == 1 || cursor->paragraph()->at( cursor->index() - 2 )->c != '.' ) )
|
---|
255 | return doObjectCompletion();
|
---|
256 |
|
---|
257 | int idx = cursor->index();
|
---|
258 | if ( idx == 0 )
|
---|
259 | return FALSE;
|
---|
260 | QChar c = cursor->paragraph()->at( idx - 1 )->c;
|
---|
261 | if ( !c.isLetter() && !c.isNumber() && c != '_' && c != '#' )
|
---|
262 | return FALSE;
|
---|
263 |
|
---|
264 | QString s;
|
---|
265 | idx--;
|
---|
266 | completionOffset = 1;
|
---|
267 | for (;;) {
|
---|
268 | s.prepend( QString( cursor->paragraph()->at( idx )->c ) );
|
---|
269 | idx--;
|
---|
270 | if ( idx < 0 )
|
---|
271 | break;
|
---|
272 | if ( !cursor->paragraph()->at( idx )->c.isLetter() &&
|
---|
273 | !cursor->paragraph()->at( idx )->c.isNumber() &&
|
---|
274 | cursor->paragraph()->at( idx )->c != '_' &&
|
---|
275 | cursor->paragraph()->at( idx )->c != '#' )
|
---|
276 | break;
|
---|
277 | completionOffset++;
|
---|
278 | }
|
---|
279 |
|
---|
280 | searchString = s;
|
---|
281 |
|
---|
282 | QValueList<CompletionEntry> lst( completionList( s, doc ) );
|
---|
283 | if ( lst.count() > 1 ) {
|
---|
284 | QTextStringChar *chr = cursor->paragraph()->at( cursor->index() );
|
---|
285 | int h = cursor->paragraph()->lineHeightOfChar( cursor->index() );
|
---|
286 | int x = cursor->paragraph()->rect().x() + chr->x;
|
---|
287 | int y, dummy;
|
---|
288 | cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y );
|
---|
289 | y += cursor->paragraph()->rect().y();
|
---|
290 | completionListBox->clear();
|
---|
291 | for ( QValueList<CompletionEntry>::ConstIterator it = lst.begin(); it != lst.end(); ++it )
|
---|
292 | (void)new CompletionItem( completionListBox, (*it).text, (*it).type, (*it).postfix,
|
---|
293 | (*it).prefix, (*it).postfix2 );
|
---|
294 | cList = lst;
|
---|
295 | completionPopup->resize( completionListBox->sizeHint() +
|
---|
296 | QSize( completionListBox->verticalScrollBar()->width() + 4,
|
---|
297 | completionListBox->horizontalScrollBar()->height() + 4 ) );
|
---|
298 | completionListBox->setCurrentItem( 0 );
|
---|
299 | completionListBox->setFocus();
|
---|
300 | if ( curEditor->mapToGlobal( QPoint( 0, y ) ).y() + h + completionPopup->height() < QApplication::desktop()->height() )
|
---|
301 | completionPopup->move( curEditor->mapToGlobal( curEditor->
|
---|
302 | contentsToViewport( QPoint( x, y + h ) ) ) );
|
---|
303 | else
|
---|
304 | completionPopup->move( curEditor->mapToGlobal( curEditor->
|
---|
305 | contentsToViewport( QPoint( x, y - completionPopup->height() ) ) ) );
|
---|
306 | completionPopup->show();
|
---|
307 | } else if ( lst.count() == 1 ) {
|
---|
308 | curEditor->insert( lst.first().text.mid( completionOffset, 0xFFFFFF ),
|
---|
309 | (uint) ( QTextEdit::RedoIndentation |
|
---|
310 | QTextEdit::CheckNewLines |
|
---|
311 | QTextEdit::RemoveSelected ) );
|
---|
312 | } else {
|
---|
313 | return FALSE;
|
---|
314 | }
|
---|
315 |
|
---|
316 | return TRUE;
|
---|
317 | }
|
---|
318 |
|
---|
319 | bool EditorCompletion::eventFilter( QObject *o, QEvent *e )
|
---|
320 | {
|
---|
321 | if ( !enabled )
|
---|
322 | return FALSE;
|
---|
323 | if ( e->type() == QEvent::KeyPress && ::qt_cast<Editor*>(o)) {
|
---|
324 | curEditor = (Editor*)o;
|
---|
325 | QKeyEvent *ke = (QKeyEvent*)e;
|
---|
326 | if ( ke->key() == Key_Tab ) {
|
---|
327 | QString s = curEditor->textCursor()->paragraph()->string()->toString().
|
---|
328 | left( curEditor->textCursor()->index() );
|
---|
329 | if ( curEditor->document()->hasSelection( QTextDocument::Standard ) ||
|
---|
330 | s.simplifyWhiteSpace().isEmpty() ) {
|
---|
331 | if ( curEditor->document()->indent() ) {
|
---|
332 | curEditor->indent();
|
---|
333 | int i = 0;
|
---|
334 | for ( ; i < curEditor->textCursor()->paragraph()->length() - 1; ++i ) {
|
---|
335 | if ( curEditor->textCursor()->paragraph()->at( i )->c != ' ' &&
|
---|
336 | curEditor->textCursor()->paragraph()->at( i )->c != '\t' )
|
---|
337 | break;
|
---|
338 | }
|
---|
339 | curEditor->drawCursor( FALSE );
|
---|
340 | curEditor->textCursor()->setIndex( i );
|
---|
341 | curEditor->drawCursor( TRUE );
|
---|
342 | } else {
|
---|
343 | curEditor->insert( "\t" );
|
---|
344 | }
|
---|
345 | return TRUE;
|
---|
346 | }
|
---|
347 | }
|
---|
348 |
|
---|
349 | if ( functionLabel->isVisible() ) {
|
---|
350 | if ( ke->key() == Key_Up && ( ke->state() & ControlButton ) == ControlButton ) {
|
---|
351 | functionLabel->gotoPrev();
|
---|
352 | return TRUE;
|
---|
353 | } else if ( ke->key() == Key_Down && ( ke->state() & ControlButton ) == ControlButton ) {
|
---|
354 | functionLabel->gotoNext();
|
---|
355 | return TRUE;
|
---|
356 | }
|
---|
357 | }
|
---|
358 |
|
---|
359 | if ( ke->text().length() && !( ke->state() & AltButton ) &&
|
---|
360 | ( !ke->ascii() || ke->ascii() >= 32 ) ||
|
---|
361 | ( ke->text() == "\t" && !( ke->state() & ControlButton ) ) ) {
|
---|
362 | if ( ke->key() == Key_Tab ) {
|
---|
363 | if ( curEditor->textCursor()->index() == 0 &&
|
---|
364 | curEditor->textCursor()->paragraph()->isListItem() )
|
---|
365 | return FALSE;
|
---|
366 | if ( doCompletion() )
|
---|
367 | return TRUE;
|
---|
368 | } else if ( ke->key() == Key_Period &&
|
---|
369 | ( curEditor->textCursor()->index() == 0 ||
|
---|
370 | curEditor->textCursor()->paragraph()->at( curEditor->textCursor()->index() - 1 )->c != '.' )
|
---|
371 | ||
|
---|
372 | ke->key() == Key_Greater &&
|
---|
373 | curEditor->textCursor()->index() > 0 &&
|
---|
374 | curEditor->textCursor()->paragraph()->at( curEditor->textCursor()->index() - 1 )->c == '-' ) {
|
---|
375 | doObjectCompletion();
|
---|
376 | } else {
|
---|
377 | if ( !doArgumentHint( ke->text() == "(" ) )
|
---|
378 | functionLabel->hide();
|
---|
379 | }
|
---|
380 | }
|
---|
381 | } else if ( o == completionPopup || o == completionListBox ||
|
---|
382 | o == completionListBox->viewport() ) {
|
---|
383 | if ( e->type() == QEvent::KeyPress ) {
|
---|
384 | QKeyEvent *ke = (QKeyEvent*)e;
|
---|
385 | if ( ke->key() == Key_Enter || ke->key() == Key_Return || ke->key() == Key_Tab ) {
|
---|
386 | if ( ke->key() == Key_Tab && completionListBox->count() > 1 &&
|
---|
387 | completionListBox->currentItem() < (int)completionListBox->count() - 1 ) {
|
---|
388 | completionListBox->setCurrentItem( completionListBox->currentItem() + 1 );
|
---|
389 | return TRUE;
|
---|
390 | }
|
---|
391 | completeCompletion();
|
---|
392 | return TRUE;
|
---|
393 | } else if ( ke->key() == Key_Left || ke->key() == Key_Right ||
|
---|
394 | ke->key() == Key_Up || ke->key() == Key_Down ||
|
---|
395 | ke->key() == Key_Home || ke->key() == Key_End ||
|
---|
396 | ke->key() == Key_Prior || ke->key() == Key_Next ) {
|
---|
397 | return FALSE;
|
---|
398 | } else if ( ke->key() != Key_Shift && ke->key() != Key_Control &&
|
---|
399 | ke->key() != Key_Alt ) {
|
---|
400 | int l = searchString.length();
|
---|
401 | if ( ke->key() == Key_Backspace ) {
|
---|
402 | searchString.remove( searchString.length() - 1, 1 );
|
---|
403 | } else {
|
---|
404 | searchString += ke->text();
|
---|
405 | l = 1;
|
---|
406 | }
|
---|
407 | if ( !l || !continueComplete() ) {
|
---|
408 | completionPopup->close();
|
---|
409 | curEditor->setFocus();
|
---|
410 | }
|
---|
411 | QApplication::sendEvent( curEditor, e );
|
---|
412 | return TRUE;
|
---|
413 | }
|
---|
414 | } else if ( e->type() == QEvent::MouseButtonDblClick ) {
|
---|
415 | completeCompletion();
|
---|
416 | return TRUE;
|
---|
417 | }
|
---|
418 | }
|
---|
419 | if ( o == functionLabel || ::qt_cast<Editor*>(o) && functionLabel->isVisible() ) {
|
---|
420 | if ( e->type() == QEvent::KeyPress ) {
|
---|
421 | QKeyEvent *ke = (QKeyEvent*)e;
|
---|
422 | if ( ke->key() == Key_Escape ) {
|
---|
423 | functionLabel->hide();
|
---|
424 | } else {
|
---|
425 | if ( !doArgumentHint( ke->text() == "(" ) )
|
---|
426 | functionLabel->hide();
|
---|
427 | if ( o == functionLabel ) {
|
---|
428 | QApplication::sendEvent( curEditor, e );
|
---|
429 | return TRUE;
|
---|
430 | }
|
---|
431 | }
|
---|
432 | }
|
---|
433 | }
|
---|
434 | return FALSE;
|
---|
435 | }
|
---|
436 |
|
---|
437 | void EditorCompletion::completeCompletion()
|
---|
438 | {
|
---|
439 | int idx = curEditor->textCursor()->index();
|
---|
440 | QString s = completionListBox->currentText().mid( searchString.length() );
|
---|
441 | curEditor->insert( s, (uint) ( QTextEdit::RedoIndentation |
|
---|
442 | QTextEdit::CheckNewLines |
|
---|
443 | QTextEdit::RemoveSelected ) );
|
---|
444 | int i = s.find( '(' );
|
---|
445 | completionPopup->close();
|
---|
446 | curEditor->setFocus();
|
---|
447 | if ( i != -1 && i < (int)s.length() ) {
|
---|
448 | curEditor->setCursorPosition( curEditor->textCursor()->paragraph()->paragId(), idx + i + 1 );
|
---|
449 | doArgumentHint( FALSE );
|
---|
450 | }
|
---|
451 | }
|
---|
452 |
|
---|
453 | void EditorCompletion::setCurrentEdior( Editor *e )
|
---|
454 | {
|
---|
455 | curEditor = e;
|
---|
456 | curEditor->installEventFilter( this );
|
---|
457 | }
|
---|
458 |
|
---|
459 | void EditorCompletion::addEditor( Editor *e )
|
---|
460 | {
|
---|
461 | e->installEventFilter( this );
|
---|
462 | }
|
---|
463 |
|
---|
464 | bool EditorCompletion::doObjectCompletion()
|
---|
465 | {
|
---|
466 | searchString = "";
|
---|
467 | QString object;
|
---|
468 | int i = curEditor->textCursor()->index();
|
---|
469 | i--;
|
---|
470 | QTextParagraph *p = curEditor->textCursor()->paragraph();
|
---|
471 | for (;;) {
|
---|
472 | if ( i < 0 )
|
---|
473 | break;
|
---|
474 | if ( p->at( i )->c == ' ' || p->at( i )->c == '\t' )
|
---|
475 | break;
|
---|
476 | object.prepend( p->at( i )->c );
|
---|
477 | i--;
|
---|
478 | }
|
---|
479 |
|
---|
480 | if ( object[ (int)object.length() - 1 ] == '-' )
|
---|
481 | object.remove( object.length() - 1, 1 );
|
---|
482 |
|
---|
483 | if ( object.isEmpty() )
|
---|
484 | return FALSE;
|
---|
485 | return doObjectCompletion( object );
|
---|
486 | }
|
---|
487 |
|
---|
488 | bool EditorCompletion::doObjectCompletion( const QString & )
|
---|
489 | {
|
---|
490 | return FALSE;
|
---|
491 | }
|
---|
492 |
|
---|
493 | static void strip( QString &txt )
|
---|
494 | {
|
---|
495 | int i = txt.find( "(" );
|
---|
496 | if ( i == -1 )
|
---|
497 | return;
|
---|
498 | txt = txt.left( i );
|
---|
499 | }
|
---|
500 |
|
---|
501 | bool EditorCompletion::continueComplete()
|
---|
502 | {
|
---|
503 | if ( searchString.isEmpty() ) {
|
---|
504 | completionListBox->clear();
|
---|
505 | for ( QValueList<CompletionEntry>::ConstIterator it = cList.begin(); it != cList.end(); ++it )
|
---|
506 | (void)new CompletionItem( completionListBox, (*it).text, (*it).type,
|
---|
507 | (*it).postfix, (*it).prefix, (*it).postfix2 );
|
---|
508 | completionListBox->setCurrentItem( 0 );
|
---|
509 | completionListBox->setSelected( completionListBox->currentItem(), TRUE );
|
---|
510 | return TRUE;
|
---|
511 | }
|
---|
512 |
|
---|
513 | QListBoxItem *i = completionListBox->findItem( searchString );
|
---|
514 | if ( !i )
|
---|
515 | return FALSE;
|
---|
516 |
|
---|
517 | QString txt1 = i->text();
|
---|
518 | QString txt2 = searchString;
|
---|
519 | strip( txt1 );
|
---|
520 | strip( txt2 );
|
---|
521 | if ( txt1 == txt2 && !i->next() )
|
---|
522 | return FALSE;
|
---|
523 |
|
---|
524 | QValueList<CompletionEntry> res;
|
---|
525 | for ( QValueList<CompletionEntry>::ConstIterator it = cList.begin(); it != cList.end(); ++it ) {
|
---|
526 | if ( (*it).text.left( searchString.length() ) == searchString )
|
---|
527 | res << *it;
|
---|
528 | }
|
---|
529 | if ( res.isEmpty() )
|
---|
530 | return FALSE;
|
---|
531 | completionListBox->clear();
|
---|
532 | for ( QValueList<CompletionEntry>::ConstIterator it2 = res.begin(); it2 != res.end(); ++it2 )
|
---|
533 | (void)new CompletionItem( completionListBox, (*it2).text, (*it2).type,
|
---|
534 | (*it2).postfix, (*it2).prefix, (*it2).postfix2 );
|
---|
535 | completionListBox->setCurrentItem( 0 );
|
---|
536 | completionListBox->setSelected( completionListBox->currentItem(), TRUE );
|
---|
537 | return TRUE;
|
---|
538 | }
|
---|
539 |
|
---|
540 | bool EditorCompletion::doArgumentHint( bool useIndex )
|
---|
541 | {
|
---|
542 | QTextCursor *cursor = curEditor->textCursor();
|
---|
543 | int i = cursor->index() ;
|
---|
544 | if ( !useIndex ) {
|
---|
545 | bool foundParen = FALSE;
|
---|
546 | int closeParens = 0;
|
---|
547 | while ( i >= 0 ) {
|
---|
548 | if ( cursor->paragraph()->at( i )->c == ')' && i != cursor->index() )
|
---|
549 | closeParens++;
|
---|
550 | if ( cursor->paragraph()->at( i )->c == '(' ) {
|
---|
551 | closeParens--;
|
---|
552 | if ( closeParens == -1 ) {
|
---|
553 | foundParen = TRUE;
|
---|
554 | break;
|
---|
555 | }
|
---|
556 | }
|
---|
557 | --i;
|
---|
558 | }
|
---|
559 |
|
---|
560 | if ( !foundParen )
|
---|
561 | return FALSE;
|
---|
562 | }
|
---|
563 | int j = i - 1;
|
---|
564 | bool foundSpace = FALSE;
|
---|
565 | bool foundNonSpace = FALSE;
|
---|
566 | while ( j >= 0 ) {
|
---|
567 | if ( foundNonSpace && ( cursor->paragraph()->at( j )->c == ' ' || cursor->paragraph()->at( j )->c == ',' ) ) {
|
---|
568 | foundSpace = TRUE;
|
---|
569 | break;
|
---|
570 | }
|
---|
571 | if ( !foundNonSpace && ( cursor->paragraph()->at( j )->c != ' ' || cursor->paragraph()->at( j )->c != ',' ) )
|
---|
572 | foundNonSpace = TRUE;
|
---|
573 | --j;
|
---|
574 | }
|
---|
575 | if ( foundSpace )
|
---|
576 | ++j;
|
---|
577 | j = QMAX( j, 0 );
|
---|
578 | QString function( cursor->paragraph()->string()->toString().mid( j, i - j + 1 ) );
|
---|
579 | QString part = cursor->paragraph()->string()->toString().mid( j, cursor->index() - j + 1 );
|
---|
580 | function = function.simplifyWhiteSpace();
|
---|
581 | for (;;) {
|
---|
582 | if ( function[ (int)function.length() - 1 ] == '(' ) {
|
---|
583 | function.remove( function.length() - 1, 1 );
|
---|
584 | function = function.simplifyWhiteSpace();
|
---|
585 | } else if ( function[ (int)function.length() - 1 ] == ')' ) {
|
---|
586 | function.remove( function.length() - 1, 1 );
|
---|
587 | function = function.simplifyWhiteSpace();
|
---|
588 | } else {
|
---|
589 | break;
|
---|
590 | }
|
---|
591 | }
|
---|
592 |
|
---|
593 | QChar sep;
|
---|
594 | QString pre, post;
|
---|
595 | QValueList<QStringList> argl = functionParameters( function, sep, pre, post );
|
---|
596 | if ( argl.isEmpty() )
|
---|
597 | return FALSE;
|
---|
598 |
|
---|
599 | QString label;
|
---|
600 | int w = 0;
|
---|
601 | int num = 0;
|
---|
602 | if ( !functionLabel->isVisible() )
|
---|
603 | functionLabel->setNumFunctions( (int)argl.count() );
|
---|
604 | for ( QValueList<QStringList>::Iterator vit = argl.begin(); vit != argl.end(); ++vit, ++num ) {
|
---|
605 | QStringList args = *vit;
|
---|
606 | int argNum = 0;
|
---|
607 | int inParen = 0;
|
---|
608 | for ( int k = 0; k < (int)part.length(); ++k ) {
|
---|
609 | if ( part[ k ] == sep && inParen < 2 )
|
---|
610 | argNum++;
|
---|
611 | if ( part[ k ] == '(' )
|
---|
612 | inParen++;
|
---|
613 | if ( part[ k ] == ')' )
|
---|
614 | inParen--;
|
---|
615 | }
|
---|
616 |
|
---|
617 | QString func = function;
|
---|
618 | int pnt = -1;
|
---|
619 | pnt = func.findRev( '.' );
|
---|
620 | if ( pnt == -1 )
|
---|
621 | func.findRev( '>' );
|
---|
622 | if ( pnt != -1 )
|
---|
623 | func = func.mid( pnt + 1 );
|
---|
624 |
|
---|
625 | QString s = func + "( ";
|
---|
626 | if ( s[ 0 ] == '\"' )
|
---|
627 | s.remove( (uint)0, 1 );
|
---|
628 | i = 0;
|
---|
629 | for ( QStringList::Iterator it = args.begin(); it != args.end(); ++it, ++i ) {
|
---|
630 | if ( i == argNum )
|
---|
631 | s += "<b>" + *it + "</b>";
|
---|
632 | else
|
---|
633 | s += *it;
|
---|
634 | if ( i < (int)args.count() - 1 )
|
---|
635 | s += ", ";
|
---|
636 | else
|
---|
637 | s += " ";
|
---|
638 | }
|
---|
639 | s += ")";
|
---|
640 | s.prepend( pre );
|
---|
641 | s.append( post );
|
---|
642 | label += "<p>" + s + "</p>";
|
---|
643 | functionLabel->setFunctionText( num, s );
|
---|
644 | w = QMAX( w, functionLabel->fontMetrics().width( s ) + 10 );
|
---|
645 | }
|
---|
646 | w += 16;
|
---|
647 | if ( label.isEmpty() )
|
---|
648 | return FALSE;
|
---|
649 | if ( functionLabel->isVisible() ) {
|
---|
650 | functionLabel->resize( w + 50, QMAX( functionLabel->fontMetrics().height(), 16 ) );
|
---|
651 | } else {
|
---|
652 | QTextStringChar *chr = cursor->paragraph()->at( cursor->index() );
|
---|
653 | int h = cursor->paragraph()->lineHeightOfChar( cursor->index() );
|
---|
654 | int x = cursor->paragraph()->rect().x() + chr->x;
|
---|
655 | int y, dummy;
|
---|
656 | cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y );
|
---|
657 | y += cursor->paragraph()->rect().y();
|
---|
658 | functionLabel->resize( w + 50, QMAX( functionLabel->fontMetrics().height(), 16 ) );
|
---|
659 | functionLabel->move( curEditor->mapToGlobal( curEditor->contentsToViewport( QPoint( x, y + h ) ) ) );
|
---|
660 | if ( functionLabel->x() + functionLabel->width() > QApplication::desktop()->width() )
|
---|
661 | functionLabel->move( QMAX( 0, QApplication::desktop()->width() - functionLabel->width() ),
|
---|
662 | functionLabel->y() );
|
---|
663 | functionLabel->show();
|
---|
664 | curEditor->setFocus();
|
---|
665 | }
|
---|
666 | QTimer::singleShot( 0, functionLabel, SLOT( relayout() ) );
|
---|
667 |
|
---|
668 | return TRUE;
|
---|
669 | }
|
---|
670 |
|
---|
671 | QValueList<QStringList> EditorCompletion::functionParameters( const QString &, QChar &, QString &, QString & )
|
---|
672 | {
|
---|
673 | return QValueList<QStringList>();
|
---|
674 | }
|
---|
675 |
|
---|
676 | void EditorCompletion::setContext( QObject * )
|
---|
677 | {
|
---|
678 | }
|
---|
679 |
|
---|
680 | void EditorCompletion::showCompletion( const QValueList<CompletionEntry> &lst )
|
---|
681 | {
|
---|
682 | QTextCursor *cursor = curEditor->textCursor();
|
---|
683 | QTextStringChar *chr = cursor->paragraph()->at( cursor->index() );
|
---|
684 | int h = cursor->paragraph()->lineHeightOfChar( cursor->index() );
|
---|
685 | int x = cursor->paragraph()->rect().x() + chr->x;
|
---|
686 | int y, dummy;
|
---|
687 | cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y );
|
---|
688 | y += cursor->paragraph()->rect().y();
|
---|
689 | completionListBox->clear();
|
---|
690 | for ( QValueList<CompletionEntry>::ConstIterator it = lst.begin(); it != lst.end(); ++it )
|
---|
691 | (void)new CompletionItem( completionListBox, (*it).text, (*it).type,
|
---|
692 | (*it).postfix, (*it).prefix, (*it).postfix2 );
|
---|
693 | cList = lst;
|
---|
694 | completionPopup->resize( completionListBox->sizeHint() +
|
---|
695 | QSize( completionListBox->verticalScrollBar()->width() + 4,
|
---|
696 | completionListBox->horizontalScrollBar()->height() + 4 ) );
|
---|
697 | completionListBox->setCurrentItem( 0 );
|
---|
698 | completionListBox->setFocus();
|
---|
699 | if ( curEditor->mapToGlobal( QPoint( 0, y ) ).y() + h + completionPopup->height() < QApplication::desktop()->height() )
|
---|
700 | completionPopup->move( curEditor->mapToGlobal( curEditor->
|
---|
701 | contentsToViewport( QPoint( x, y + h ) ) ) );
|
---|
702 | else
|
---|
703 | completionPopup->move( curEditor->mapToGlobal( curEditor->
|
---|
704 | contentsToViewport( QPoint( x, y - completionPopup->height() ) ) ) );
|
---|
705 |
|
---|
706 | completionPopup->show();
|
---|
707 | }
|
---|