1 #include "additionalgui.h"
2 #include "../common/mlexception.h"
3
4 #include <QStylePainter>
5 #include <QHBoxLayout>
6 #include <QToolTip>
7 #include <QWidgetAction>
8 #include <QApplication>
9 #include <QScrollBar>
10 #include <QStyle>
11 #include <QDebug>
12 #include <QMetaEnum>
13 #include <qgl.h>
14
CheckBoxListItemDelegate(QObject * parent)15 CheckBoxListItemDelegate::CheckBoxListItemDelegate(QObject *parent)
16 : QStyledItemDelegate(parent)
17 {
18 }
19
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const20 void CheckBoxListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,const QModelIndex &index) const
21 {
22 bool value = index.data(Qt::CheckStateRole).toBool();
23 QString text = index.data(Qt::DisplayRole).toString();
24
25 // fill style options with item data
26 const QStyle *style = QApplication::style();
27 QStyleOptionButton opt;
28 opt.state |= value ? QStyle::State_On : QStyle::State_Off;
29 opt.state |= QStyle::State_Enabled;
30 opt.text = text;
31 opt.rect = QRect(option.rect.x(),option.rect.y(),16,16);
32
33 QRect textrect(option.rect.x() + 16,option.rect.y(),option.rect.width() - 16,option.rect.height());
34 style->drawPrimitive(QStyle::PE_IndicatorCheckBox,&opt,painter);
35 style->drawItemText(painter,textrect,Qt::AlignLeft,opt.palette,true,text);
36 }
37
CheckBoxList(const QString & defaultValue,QWidget * widget)38 CheckBoxList::CheckBoxList(const QString& defaultValue,QWidget *widget )
39 :QComboBox(widget),highli(0),defaultval(defaultValue),popupopen(false)
40 {
41 view()->viewport()->installEventFilter(this);
42 view()->setItemDelegate(new CheckBoxListItemDelegate(this));
43 connect(this,SIGNAL(highlighted(int)),this,SLOT(currentHighlighted(int)));
44 }
45
CheckBoxList(QWidget * widget)46 CheckBoxList::CheckBoxList( QWidget *widget /*= 0*/ )
47 :QComboBox(widget),highli(0),defaultval(),popupopen(false)
48 {
49 view()->viewport()->installEventFilter(this);
50 view()->setItemDelegate(new CheckBoxListItemDelegate(this));
51 connect(this,SIGNAL(highlighted(int)),this,SLOT(currentHighlighted(int)));
52 }
53
~CheckBoxList()54 CheckBoxList::~CheckBoxList()
55 {
56 }
57
paintEvent(QPaintEvent *)58 void CheckBoxList::paintEvent(QPaintEvent *)
59 {
60 QStylePainter painter(this);
61 painter.setPen(palette().color(QPalette::Text));
62 QStyleOptionComboBox opt;
63 initStyleOption(&opt);
64 opt.currentText = "";
65 if (selectedItemsNames().empty())
66 opt.currentText = defaultval;
67 else
68 opt.currentText = selectedItemsString(QString(" | "));
69 for (int ii=0;ii<count();++ii)
70 {
71 Qt::CheckState v;
72 if (sel.contains(itemText(ii)))
73 v = Qt::Checked;
74 else
75 v = Qt::Unchecked;
76 setItemData(ii,QVariant(v),Qt::CheckStateRole);
77 }
78 painter.drawComplexControl(QStyle::CC_ComboBox, opt);
79 painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
80 }
81
eventFilter(QObject * object,QEvent * event)82 bool CheckBoxList::eventFilter(QObject *object, QEvent * event)
83 {
84 if ((event->type() == QEvent::MouseButtonPress) && (!popupopen))
85 {
86 popupopen = true;
87 return true;
88 }
89 if((event->type() == QEvent::MouseButtonRelease) &&
90 (object==view()->viewport()) && popupopen)
91 {
92 updateSelected(highli);
93 repaint();
94 popupopen = true;
95 return true;
96 }
97 return QComboBox::eventFilter(object,event);
98 }
99
focusOutEvent(QFocusEvent *)100 void CheckBoxList::focusOutEvent ( QFocusEvent * /*e*/ )
101 {
102 if (popupopen)
103 popupopen = false;
104 }
105
getSelected() const106 QStringList CheckBoxList::getSelected() const
107 {
108 return sel;
109 }
110
updateSelected(const int ind)111 void CheckBoxList::updateSelected(const int ind)
112 {
113 bool checked = itemData(ind,Qt::CheckStateRole).toBool();
114 QString text = itemText(highli);
115 if (checked)
116 sel.removeAll(text);
117 else
118 sel.push_back(text);
119 }
120
insertCheckableItem(const int pos,const QString & lab,const bool checked)121 void CheckBoxList::insertCheckableItem( const int pos,const QString&
122 lab,const bool checked )
123 {
124 insertItem(pos,lab);
125 if (checked)
126 sel.push_back(lab);
127 }
128
insertCheckableItem(const QString & lab,const bool checked)129 void CheckBoxList::insertCheckableItem(const QString& lab,const bool
130 checked )
131 {
132 addItem(lab);
133 if (checked)
134 sel.push_back(lab);
135 }
136
currentHighlighted(int high)137 void CheckBoxList::currentHighlighted( int high )
138 {
139 highli = high;
140 }
141
selectedItemsNames() const142 QStringList CheckBoxList::selectedItemsNames() const
143 {
144 return sel;
145 }
146
selectedItemsString(const QString & sep) const147 QString CheckBoxList::selectedItemsString(const QString& sep) const
148 {
149 QStringList ll = selectedItemsNames();
150 if (ll.isEmpty())
151 return defaultval;
152 return ll.join(sep);
153 }
154
setDefaultValue(const QString & defaultValue)155 void CheckBoxList::setDefaultValue( const QString& defaultValue )
156 {
157 defaultval = defaultValue;
158 }
159
setCurrentValue(const QStringList & st)160 void CheckBoxList::setCurrentValue( const QStringList& st )
161 {
162 sel = st;
163 sel.removeAll(defaultval);
164 }
165
pixmapGeneratorFromQtPrimitiveElement(const QSize & pixmapsize,const QStyle::PrimitiveElement primitive,QStyle * style,const QStyleOption & opt)166 QPixmap UsefulGUIFunctions::pixmapGeneratorFromQtPrimitiveElement(const QSize& pixmapsize,const QStyle::PrimitiveElement primitive, QStyle *style,const QStyleOption& opt)
167 {
168 QPixmap pix(pixmapsize);
169 pix.fill(Qt::transparent);
170 QPainter p;
171 p.begin(&pix);
172 style->drawPrimitive(primitive, &opt, &p);
173 p.end();
174 return pix;
175 }
176
generateUniqueDefaultName(const QString & basename,const QStringList & namelist)177 QString UsefulGUIFunctions::generateUniqueDefaultName( const QString& basename,const QStringList& namelist)
178 {
179 int max = INT_MIN;
180 QString regexp(basename + "_(\\d)+");
181 QStringList items = namelist.filter(QRegExp(regexp));
182 for(int ii = 0;ii < items.size();++ii)
183 {
184 QRegExp reg("(\\d)+");
185 items[ii].indexOf(reg);
186 int index = reg.cap().toInt();
187 if (index > max)
188 max = index;
189 }
190 QString tmpname = basename + "_";
191 if (items.size() == 0)
192 tmpname += QString::number(namelist.size());
193 else
194 tmpname += QString::number(max + 1);
195 return tmpname;
196 }
197
generateFunctionName(const QString & originaltext)198 QString UsefulGUIFunctions::generateFunctionName(const QString& originaltext)
199 {
200 QString newname;
201 if (originaltext.isEmpty())
202 return newname;
203 QRegExp nonchar("\\W+");
204 int index = 0;
205 do
206 {
207 originaltext.indexOf(nonchar,index);
208 QRegExp validchar("\\w+");
209 int validcharind = originaltext.indexOf(validchar,index);
210 if (validcharind != -1)
211 {
212 QString captured = validchar.cap();
213 if (captured.size() > 0)
214 captured[0] = captured[0].toUpper();
215 newname.push_back(captured);
216 }
217 index = index + validchar.cap().size() + nonchar.cap().size();
218 } while (index < originaltext.size());
219 if (originaltext[0].isLetter() && (newname.size() > 0))
220 newname[0] = originaltext[0].toLower();
221 return newname;
222 }
223
changeNameIfAlreadyInList(const QString & name,const QStringList & allnames)224 QString UsefulGUIFunctions::changeNameIfAlreadyInList( const QString& name,const QStringList& allnames )
225 {
226 QStringList ls;
227 QString tmpname = name;
228 do
229 {
230 ls = allnames.filter(tmpname);
231 if (ls.size() > 1)
232 tmpname = tmpname + "_" + QString::number(ls.size() - 1);
233 } while(ls.size() > 1);
234 return tmpname;
235 }
236
generateBackupName(const QFileInfo & finfo)237 QString UsefulGUIFunctions::generateBackupName( const QFileInfo& finfo )
238 {
239 QDir dir = finfo.absoluteDir();
240 QFileInfoList list = dir.entryInfoList(QDir::Files);
241 QString oldnm = finfo.fileName();
242 oldnm.replace('.',"\\.");
243 QRegExp oldexp(oldnm + "\\.old(\\d+)");
244 int max = 0;
245 for (int ii = 0;ii < list.size();++ii)
246 {
247 if (list[ii].fileName().contains(oldexp))
248 {
249 QRegExp num("\\d+");
250 list[ii].suffix().indexOf(num);
251 int ver = num.cap().toInt();
252 if (ver > max)
253 max = ver;
254 }
255 }
256 return QString(finfo.absolutePath() + "/" + finfo.fileName() + ".old" + QString::number(max + 1));
257 }
258
avoidProblemsWithHTMLTagInsideXML(const QString & text)259 QString UsefulGUIFunctions::avoidProblemsWithHTMLTagInsideXML( const QString& text )
260 {
261 return "<![CDATA[" + text + "]]>";
262 }
263
ExpandButtonWidget(QWidget * parent)264 ExpandButtonWidget::ExpandButtonWidget( QWidget* parent )
265 :QWidget(parent),isExpanded(false)
266 {
267 exp = new PrimitiveButton(QStyle::PE_IndicatorArrowDown,this);
268 exp->setMaximumSize(16,16);
269 QHBoxLayout *hlay = new QHBoxLayout(this);
270 hlay->addWidget(exp,0,Qt::AlignHCenter);
271 connect(exp,SIGNAL(clicked(bool)),this,SLOT(changeIcon()));
272 }
273
~ExpandButtonWidget()274 ExpandButtonWidget::~ExpandButtonWidget()
275 {
276
277 }
278
changeIcon()279 void ExpandButtonWidget::changeIcon()
280 {
281 isExpanded = !isExpanded;
282 if (isExpanded)
283 exp->setPrimitiveElement(QStyle::PE_IndicatorArrowUp);
284 else
285 exp->setPrimitiveElement(QStyle::PE_IndicatorArrowDown);
286 emit expandView(isExpanded);
287 }
288
PrimitiveButton(const QStyle::PrimitiveElement el,QWidget * parent)289 PrimitiveButton::PrimitiveButton(const QStyle::PrimitiveElement el,QWidget* parent )
290 :QPushButton(parent),elem(el)
291 {
292 }
293
PrimitiveButton(QWidget * parent)294 PrimitiveButton::PrimitiveButton( QWidget* parent )
295 :QPushButton(parent),elem(QStyle::PE_CustomBase)
296 {
297
298 }
299
~PrimitiveButton()300 PrimitiveButton::~PrimitiveButton()
301 {
302
303 }
304
paintEvent(QPaintEvent *)305 void PrimitiveButton::paintEvent( QPaintEvent * /*event*/ )
306 {
307 QStylePainter painter(this);
308 QStyleOptionButton option;
309 option.initFrom(this);
310 //painter.drawControl(QStyle::CE_PushButton,option);
311 painter.drawPrimitive (elem,option);
312 }
313
setPrimitiveElement(const QStyle::PrimitiveElement el)314 void PrimitiveButton::setPrimitiveElement( const QStyle::PrimitiveElement el)
315 {
316 elem = el;
317 }
318
TreeWidgetWithMenu(QWidget * parent)319 TreeWidgetWithMenu::TreeWidgetWithMenu( QWidget* parent /*= NULL*/ )
320 :QTreeWidget(parent)
321 {
322 menu = new QMenu(this);
323 connect(menu,SIGNAL(triggered(QAction*)),this,SIGNAL(selectedAction(QAction*)));
324 }
325
~TreeWidgetWithMenu()326 TreeWidgetWithMenu::~TreeWidgetWithMenu()
327 {
328
329 }
330
contextMenuEvent(QContextMenuEvent * event)331 void TreeWidgetWithMenu::contextMenuEvent( QContextMenuEvent * event )
332 {
333 menu->popup(event->globalPos());
334 }
335
insertInMenu(const QString & st,const QVariant & data)336 void TreeWidgetWithMenu::insertInMenu(const QString& st,const QVariant& data)
337 {
338 QAction* act = menu->addAction(st);
339 act->setData(data);
340 }
341
MLScriptEditor(QWidget * par)342 MLScriptEditor::MLScriptEditor( QWidget* par /*= NULL*/ )
343 :QPlainTextEdit(par),regexps(),synt(NULL),synhigh(NULL),comp(NULL)
344 {
345 QTextDocument* mydoc = new QTextDocument(this);
346 QPlainTextDocumentLayout* ld = new QPlainTextDocumentLayout(mydoc);
347 mydoc->setDocumentLayout(ld);
348 setDocument(mydoc);
349
350 narea = new MLNumberArea(this);
351 connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
352 //connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateCursorPos(int)));
353 connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
354 connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
355
356 updateLineNumberAreaWidth(0);
357 highlightCurrentLine();
358 }
359
~MLScriptEditor()360 MLScriptEditor::~MLScriptEditor()
361 {
362
363 }
364
lineNumberAreaPaintEvent(QPaintEvent * event,const QColor & col)365 void MLScriptEditor::lineNumberAreaPaintEvent( QPaintEvent *event,const QColor& col)
366 {
367 QPainter painter(narea);
368 painter.fillRect(event->rect(),col);
369 QTextBlock block = firstVisibleBlock();
370 int blockNumber = block.blockNumber();
371 int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
372 int bottom = top + (int) blockBoundingRect(block).height();
373 while (block.isValid() && top <= event->rect().bottom())
374 {
375 if (block.isVisible() && bottom >= event->rect().top())
376 {
377 QString number = QString::number(blockNumber + 1);
378 painter.setPen(Qt::black);
379 painter.drawText(0, top, narea->width(), fontMetrics().height(),Qt::AlignRight, number);
380 }
381
382 block = block.next();
383 top = bottom;
384 bottom = top + (int) blockBoundingRect(block).height();
385 ++blockNumber;
386 }
387 }
388
lineNumberAreaWidth()389 int MLScriptEditor::lineNumberAreaWidth()
390 {
391 int digits = 1;
392 int max = qMax(1, blockCount());
393 while (max >= 10) {
394 max /= 10;
395 ++digits;
396 }
397
398 int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
399
400 return space;
401 }
402
resizeEvent(QResizeEvent * e)403 void MLScriptEditor::resizeEvent( QResizeEvent* e)
404 {
405 QPlainTextEdit::resizeEvent(e);
406
407 QRect cr = contentsRect();
408 narea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
409 }
410
updateLineNumberAreaWidth(int)411 void MLScriptEditor::updateLineNumberAreaWidth( int /*newBlockCount*/)
412 {
413 setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
414 }
415
highlightCurrentLine()416 void MLScriptEditor::highlightCurrentLine()
417 {
418 QList<QTextEdit::ExtraSelection> extraSelections;
419
420 if (!isReadOnly()) {
421 QTextEdit::ExtraSelection selection;
422
423 QColor lineColor = QColor(Qt::yellow).lighter(160);
424
425 selection.format.setBackground(lineColor);
426 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
427 selection.cursor = textCursor();
428 selection.cursor.clearSelection();
429 extraSelections.append(selection);
430 }
431
432 setExtraSelections(extraSelections);
433 }
434
updateLineNumberArea(const QRect & r,int dy)435 void MLScriptEditor::updateLineNumberArea( const QRect & r, int dy)
436 {
437 if (dy)
438 narea->scroll(0, dy);
439 else
440 narea->update(0, r.y(), narea->width(), r.height());
441
442 if (r.contains(viewport()->rect()))
443 updateLineNumberAreaWidth(0);
444 }
445
currentLine() const446 QString MLScriptEditor::currentLine() const
447 {
448 QTextCursor cur = textCursor();
449 cur.select(QTextCursor::LineUnderCursor);
450 return cur.selectedText();
451 }
452
keyPressEvent(QKeyEvent * e)453 void MLScriptEditor::keyPressEvent( QKeyEvent * e )
454 {
455 switch(e->key())
456 {
457 case (Qt::Key_Return):
458 case (Qt::Key_Enter):
459 {
460 if (comp->popup()->isHidden())
461 {
462 QTextBlock b = textCursor().block();
463 QRegExp tab("(\\t)+\\w");
464 bool tabfound = (b.text().indexOf(tab) == 0);
465 textCursor().insertText("\n");
466 if (tabfound)
467 {
468 QString cap = tab.cap();
469 int tabcount = cap.lastIndexOf(QRegExp("\\t")) + 1;
470 QString tabst;
471 for(int ii = 0;ii < tabcount;++ii)
472 tabst += '\t';
473 textCursor().insertText(tabst);
474 }
475 }
476 else
477 {
478 insertSuggestedWord(comp->currentCompletion());
479 comp->popup()->hide();
480 }
481 return;
482 }
483 case (Qt::Key_Tab):
484 {
485 if (!comp->popup()->isHidden())
486 {
487 insertSuggestedWord(comp->currentCompletion());
488 comp->popup()->hide();
489 return;
490 }
491 break;
492 }
493 }
494 QPlainTextEdit::keyPressEvent(e);
495 //!(e->text().isEmpty) is meaningful: you need it when (only) a modifier (SHIFT/CTRL) has been pressed in order to avoid the autocompleter to be visualized
496 if (!(e->text().isEmpty()) && (e->text().indexOf(synt->worddelimiter) == -1))
497 showAutoComplete(e);
498 }
499
500 //void MLScriptEditor::setSyntaxHighlighter( MLSyntaxHighlighter* high )
501 //{
502 // slh = high;
503 // if (slh != NULL)
504 // {
505 // slh->setDocument(document());
506 // connect(&slh->comp,SIGNAL(activated(const QString &)),this,SLOT(insertSuggestedWord( const QString &)));
507 // }
508 //}
509
showAutoComplete(QKeyEvent *)510 void MLScriptEditor::showAutoComplete( QKeyEvent * /*e*/ )
511 {
512 QString w = wordUnderTextCursor();
513 QTextCursor tc = textCursor();
514 comp->setCompletionPrefix(w);
515 comp->popup()->setModel(comp->completionModel());
516 QRect rect = cursorRect();
517 rect.setWidth(comp->popup()->sizeHintForColumn(0) + comp->popup()->verticalScrollBar()->sizeHint().width());
518 comp->complete(rect);
519 }
520
insertSuggestedWord(const QString & str)521 void MLScriptEditor::insertSuggestedWord( const QString& str )
522 {
523 QTextCursor tc = textCursor();
524 int extra = str.length() - comp->completionPrefix().length();
525 tc.insertText(str.right(extra));
526 setTextCursor(tc);
527 }
528
lastInsertedWord() const529 QString MLScriptEditor::lastInsertedWord() const
530 {
531 QString cur = currentLine();
532 QStringList ls = cur.split(synt->worddelimiter,QString::SkipEmptyParts);
533 if (ls.size() > 0)
534 return ls[ls.size() - 1];
535 return QString();
536 }
537
setScriptLanguage(MLScriptLanguage * syntax)538 void MLScriptEditor::setScriptLanguage( MLScriptLanguage* syntax )
539 {
540 if (syntax != NULL)
541 {
542 delete synt;
543 synt = syntax;
544 delete synhigh;
545 synhigh = new MLSyntaxHighlighter(*synt,this);
546 synhigh->setDocument(document());
547 delete comp;
548 comp = new MLAutoCompleter(*synt,this);
549 comp->setWidget(this);
550 comp->setModel(synt->functionsLibrary());
551 connect(comp,SIGNAL(activated(const QString &)),this,SLOT(insertSuggestedWord( const QString &)));
552 connect(comp,SIGNAL(highlighted(const QModelIndex&)),comp,SLOT(changeCurrent(const QModelIndex&)));
553
554 }
555 }
556
wordUnderTextCursor() const557 QString MLScriptEditor::wordUnderTextCursor() const
558 {
559 QTextCursor tc = this->textCursor();
560 int endpos = tc.position();
561 tc.select(QTextCursor::LineUnderCursor);
562 QString line = tc.selectedText();
563 line = line.left(endpos);
564 QRegExp id = synt->joinedWordExpression();
565 int index = 0;
566 while ((index >= 0) && (index < line.size()))
567 {
568 int tmp = line.indexOf(id,index);
569 QString cap = id.cap();
570 index = tmp + id.matchedLength();
571 }
572
573 if (index >= 0)
574 return id.cap();
575 tc.select(QTextCursor::WordUnderCursor);
576 return tc.selectedText();
577 }
578
579 //void MLScriptEditor::setToolTip( const QString& st )
580 //{
581 //}
582
583
584
MLNumberArea(MLScriptEditor * editor)585 MLNumberArea::MLNumberArea( MLScriptEditor* editor ) : QWidget(editor)
586 {
587 mledit = editor;
588 }
589
sizeHint() const590 QSize MLNumberArea::sizeHint() const
591 {
592 return QSize(mledit->lineNumberAreaWidth(), 0);
593 }
594
paintEvent(QPaintEvent * e)595 void MLNumberArea::paintEvent(QPaintEvent* e)
596 {
597 mledit->lineNumberAreaPaintEvent(e,UsefulGUIFunctions::editorMagicColor());
598 }
599
MLSyntaxHighlighter(const MLScriptLanguage & synt,QWidget * parent)600 MLSyntaxHighlighter::MLSyntaxHighlighter(const MLScriptLanguage& synt, QWidget* parent)
601 :QSyntaxHighlighter(parent),syntax(synt),highlightingRules()
602 {
603 /*HighlightingRule pvar;
604 pvar.format.setForeground(Qt::red);
605 HighlightingRule res;
606 res.format.setForeground(Qt::darkBlue);
607 res.format.setFontWeight(QFont::Bold);
608 foreach(QString word,synt.reserved)
609 {
610 res.pattern = QRegExp(addIDBoundary(word));
611 highlightingRules << res;
612 }*/
613 QTextCharFormat f;
614 QTextCharFormat res;
615 res.setForeground(Qt::darkBlue);
616 res.setFontWeight(QFont::Bold);
617 tokenformat[MLScriptLanguage::RESERVED] = res;
618 QTextCharFormat nmspace;
619 nmspace.setForeground(Qt::red);
620 res.setFontWeight(QFont::Bold);
621 tokenformat[MLScriptLanguage::NAMESPACE] = nmspace;
622 QTextCharFormat fun;
623 fun.setForeground(Qt::darkCyan);
624 //fun.setFontItalic(true);
625 tokenformat[MLScriptLanguage::FUNCTION] = fun;
626 QTextCharFormat memfield;
627 memfield.setForeground(Qt::darkGreen);
628 tokenformat[MLScriptLanguage::MEMBERFIELD] = memfield;
629 }
630
highlightBlock(const QString & text)631 void MLSyntaxHighlighter::highlightBlock( const QString& text )
632 {
633
634 /*foreach (const HighlightingRule &rule, highlightingRules)
635 {
636 QRegExp expression(rule.pattern);
637 int index = expression.indexIn(text);
638 while (index >= 0)
639 {
640 int length = expression.matchedLength();
641 setFormat(index, length, rule.format);
642 index = expression.indexIn(text, index + length);
643 }
644 }*/
645 QRegExp keyword = syntax.matchOnlyReservedWords();
646 int index = keyword.indexIn(text);
647 while(index >=0)
648 {
649
650 int length = keyword.matchedLength();
651 setFormat(index, length, tokenformat[MLScriptLanguage::RESERVED]);
652 index = keyword.indexIn(text, index + length);
653 }
654 QRegExp nokeyword = syntax.matchIdentifiersButNotReservedWords();
655 index = 0;
656 //QTextCharFormat form;
657 //form.setForeground(Qt::red);
658 while(index >= 0)
659 {
660 index = nokeyword.indexIn(text,index);
661 if (index >= 0)
662 index = index + nokeyword.matchedLength();
663 }
664 setCurrentBlockState(0);
665 }
666
addIDBoundary(const QString & st)667 QString MLSyntaxHighlighter::addIDBoundary( const QString& st )
668 {
669 return "\\b" + st + "\\b";
670 }
671
colorTextIfInsideTree(const QString & text,SyntaxTreeNode * node,int start)672 bool MLSyntaxHighlighter::colorTextIfInsideTree(const QString& text,SyntaxTreeNode* node,int start)
673 {
674 if (node != NULL)
675 {
676 QRegExp exp;
677 QTextCharFormat form;
678 form = tokenformat[MLScriptLanguage::LANG_TOKEN(node->data(4).toInt())];
679 //it's the root. The root is not meaningful
680 if (node->parent() == NULL)
681 {
682 int ii = 0;
683 while(ii < node->childCount())
684 {
685 bool found = colorTextIfInsideTree(text,node->child(ii),start);
686 if (found)
687 return true;
688 ++ii;
689 }
690 }
691 QString nodevalue = addIDBoundary(node->data(0).toString());
692 exp.setPattern( nodevalue + "(\\s*\\" + node->data(2).toString() + "\\s*)?");
693
694 int index = exp.indexIn(text);
695 if (index < 0)
696 {
697 setCurrentBlockState(0);
698 return false;
699 }
700 /*if (node->childCount() == 0)
701 emit functionRecognized(node->data(3).toString());*/
702 setFormat(start + index, exp.matchedLength(), form);
703 if (text.size() == exp.matchedLength())
704 return true;
705 int ii = 0;
706 while(ii < node->childCount())
707 {
708 bool found = colorTextIfInsideTree(text.right(text.size() - (exp.matchedLength())),node->child(ii),start + exp.matchedLength());
709 if (found)
710 return true;
711 ++ii;
712 }
713 return false;
714 }
715 return false;
716 }
717
718 //void MLSyntaxHighlighter::functionRecognized( const QString& sign )
719 //{
720 //
721 //}
722
MLAutoCompleter(const MLScriptLanguage & synt,QWidget * parent)723 MLAutoCompleter::MLAutoCompleter( const MLScriptLanguage& synt,QWidget* parent )
724 :QCompleter(parent),syntax(synt)
725 {
726 setCompletionRole(Qt::DisplayRole);
727 setCaseSensitivity(Qt::CaseSensitive);
728 setCompletionMode(QCompleter::PopupCompletion);
729 MLAutoCompleterPopUp* pop = new MLAutoCompleterPopUp(parent);
730 setPopup(pop);
731 }
732
splitPath(const QString & path) const733 QStringList MLAutoCompleter::splitPath( const QString &path ) const
734 {
735 QString tmp = path;
736 QString parst = "\\s*" + syntax.openpar.pattern() + "." + syntax.closepar.pattern();
737 QRegExp par(parst);
738 tmp.remove(par);
739 QStringList res = path.split(syntax.wordsjoiner);
740 return res;
741 }
742
pathFromIndex(const QModelIndex & index) const743 QString MLAutoCompleter::pathFromIndex( const QModelIndex &index ) const
744 {
745 QString completename;
746 for (QModelIndex i = index; i.isValid(); i = i.parent())
747 {
748 QString tmp = model()->data(i, completionRole()).toString();
749 if (i != index)
750 {
751 QModelIndex sepindex = i.sibling(i.row(),2);
752 if (sepindex.isValid())
753 tmp = model()->data(sepindex).toString() + tmp;
754 }
755 completename = tmp + completename;
756
757 }
758 return completename;
759 }
760
changeCurrent(const QModelIndex & ind)761 void MLAutoCompleter::changeCurrent( const QModelIndex& ind )
762 {
763 setCurrentRow(ind.row());
764 }
765
766
MLAutoCompleterPopUp(QWidget * parent)767 MLAutoCompleterPopUp::MLAutoCompleterPopUp( QWidget* parent )
768 :QListView(parent)
769 {
770 }
771
~MLAutoCompleterPopUp()772 MLAutoCompleterPopUp::~MLAutoCompleterPopUp()
773 {
774 }
775
event(QEvent * event)776 bool MLAutoCompleterPopUp::event( QEvent *event )
777 {
778 if (event->type() == QEvent::ToolTip)
779 {
780 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
781 QModelIndex indexid = indexAt(helpEvent->pos());
782 QModelIndex indexhelp = indexid.sibling(indexid.row(),1);
783 QModelIndex indexsign = indexid.sibling(indexid.row(),3);
784 QString tooltiptext = indexsign.data().toString();
785 QString help = indexhelp.data().toString();
786 if (!help.isEmpty())
787 tooltiptext = tooltiptext + "\n" + help;
788 if (indexsign.isValid())
789 QToolTip::showText(helpEvent->globalPos(), tooltiptext);
790 else
791 {
792 QToolTip::hideText();
793 event->ignore();
794 }
795 return true;
796 }
797 return QListView::event(event);
798 }
799
SearchMenu(const WordActionsMapAccessor & wm,const int max,QWidget * parent,const int fixedwidth)800 SearchMenu::SearchMenu(const WordActionsMapAccessor& wm,const int max,QWidget* parent,const int fixedwidth)
801 :MenuWithToolTip(QString(),parent),searchline(NULL),wama(wm),maxres(max),fixedwidthsize(fixedwidth)
802 {
803 searchline = new MenuLineEdit(this);
804 //searchline->resize(fixedwidth,searchline->height());
805 QWidgetAction* searchact = new QWidgetAction(this);
806 searchact->setDefaultWidget(searchline);
807 addAction(searchact);
808 connect(searchline,SIGNAL(textEdited( const QString&)),this,SLOT(edited( const QString&)));
809 connect(searchline,SIGNAL(arrowPressed(const int)),this,SLOT(changeFocus(const int)));
810 connect(this,SIGNAL(aboutToShow()),this,SLOT(onAboutToShowEvent()));
811 setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
812 }
813
getResults(const QString & text,QList<QAction * > & result)814 void SearchMenu::getResults(const QString& text,QList<QAction*>& result)
815 {
816 try
817 {
818 RankedMatches rm;
819 int ii = wama.rankedMatchesPerInputString(text,rm);
820 int inserted = 0;
821 while(ii > 0)
822 {
823 QList<QAction*> myacts;
824 rm.getActionsWithNMatches(ii,myacts);
825 if (inserted + myacts.size() > maxres)
826 myacts = myacts.mid(0,myacts.size() - (inserted + myacts.size() - maxres));
827 result.append(myacts);
828 QAction* sep = new QAction(this);
829 sep->setSeparator(true);
830 result.append(sep);
831 inserted += myacts.size();
832 --ii;
833 }
834 }
835 catch(InvalidInvariantException& e)
836 {
837 qDebug() << "WARNING!!!!!!!!!!!!!!!!!!!" << e.what() << "\n";
838 }
839 }
840
updateGUI(const QList<QAction * > & results)841 void SearchMenu::updateGUI( const QList<QAction*>& results )
842 {
843 QList<QAction*> old = actions();
844 //list of separators to be deleted and/or menu items (actions) that are no more in the results
845 QList<QAction*> delsepremact;
846 bool maybeallequal = (old.size() - 1) == results.size();
847 //tt start from 1 because the first one is the QWidgetAction containing the QLineEdit
848 for(int tt = 1;tt < old.size();++tt)
849 {
850 QAction* oldact = old[tt];
851 if (oldact != NULL)
852 {
853 if (maybeallequal && (results.size() > 0))
854 {
855 QAction* resit = results[tt-1];
856 maybeallequal = (resit != NULL) && ((oldact->isSeparator() && resit->isSeparator()) || (oldact == resit));
857 }
858
859 if (oldact->isSeparator() || !results.contains(oldact))
860 {
861 delsepremact.push_back(oldact);
862 }
863 }
864 }
865 if (!maybeallequal)
866 {
867 #ifdef Q_OS_MAC
868 this->hide();
869 #endif
870 for(int jj = 0;jj < delsepremact.size();++jj)
871 {
872 QAction* todel = delsepremact[jj];
873 if ((todel != NULL) && (todel->isSeparator()))
874 {
875 delete todel;
876 delsepremact[jj] = NULL;
877 }
878 else
879 {
880 removeAction(todel);
881 }
882 }
883 addActions(results);
884 #ifdef Q_OS_MAC
885 this->show();
886 #endif
887 //if (results.size() > 0 && (results[0] != NULL))
888 // setActiveAction(results[0]);
889 alignToParentGeometry();
890 }
891 }
892
edited(const QString & text)893 void SearchMenu::edited( const QString& text)
894 {
895 QList<QAction*> results;
896 getResults(text,results);
897 updateGUI(results);
898 }
899
clearResults()900 void SearchMenu::clearResults()
901 {
902 QList<QAction*> actlst = actions();
903 foreach(QAction* act,actlst)
904 {
905 //QLineEdit MUST NOT be deleted!
906 if (qobject_cast<QWidgetAction*>(act) == 0)
907 removeAction(act);
908 }
909 }
910
setLineEditFocus()911 void SearchMenu::setLineEditFocus()
912 {
913 searchline->setFocus();
914 // const QList<QAction*>& acts = actions();
915 //if (acts.size() > 1 && acts[1] != NULL)
916 // setActiveAction(acts[1]);
917 }
918
alignToParentGeometry()919 void SearchMenu::alignToParentGeometry()
920 {
921 if (parentWidget() != NULL)
922 {
923 QPoint p = parentWidget()->mapToGlobal(QPoint(0,0));
924 int borderx = p.x() + parentWidget()->frameGeometry().width();
925 QSize sz = sizeHint();
926 move(borderx - sz.width(),y());
927 }
928 }
929
changeFocus(const int k)930 void SearchMenu::changeFocus( const int k )
931 {
932 setFocus();
933 QAction* act = NULL;
934 int next = nextEnabledAction(k,0,actions(),act);
935 if (next != -1)
936 setActiveAction(act);
937 }
938
nextEnabledAction(const int k,const int currentind,const QList<QAction * > & acts,QAction * & nextact) const939 int SearchMenu::nextEnabledAction( const int k,const int currentind,const QList<QAction*>& acts,QAction*& nextact) const
940 {
941 const int errorind = -1;
942 if ((currentind < 0) || (currentind >= acts.size()))
943 return errorind;
944 QAction* current = acts[currentind];
945 if (acts.size() > 0)
946 {
947 if (current == NULL)
948 return errorind;
949 int ind = currentind;
950 do
951 {
952 if (k == Qt::Key_Up)
953 ind = (acts.size() + (ind - 1)) % acts.size();
954 else
955 ind = (ind + 1) % acts.size();
956 QAction* curract = acts[ind];
957 if ((curract != NULL) && (curract->isEnabled()) && !curract->isSeparator())
958 {
959 nextact = curract;
960 return ind;
961 }
962 } while (ind != currentind);
963 return errorind;
964 }
965 return errorind;
966 }
967
keyPressEvent(QKeyEvent * event)968 void SearchMenu::keyPressEvent( QKeyEvent * event )
969 {
970 int k = event->key();
971 if ((k != Qt::Key_Up) && (k != Qt::Key_Down))
972 QMenu::keyPressEvent(event);
973 else
974 {
975 const QList<QAction*>& acts = actions();
976 QAction* current = activeAction();
977 if (current != NULL)
978 {
979 int currentind = acts.indexOf(current);
980 if (currentind > -1)
981 {
982 QAction* act = NULL;
983 int next = nextEnabledAction(k,currentind,actions(),act);
984 if (next != -1)
985 {
986 if (next == 0)
987 {
988 searchline->setFocus();
989 selectTextIfNotEmpty();
990 }
991 setActiveAction(act);
992 }
993 }
994 }
995 }
996 }
997
selectTextIfNotEmpty()998 void SearchMenu::selectTextIfNotEmpty()
999 {
1000 if (!searchline->text().isEmpty())
1001 searchline->selectAll();
1002 }
1003
sizeHint() const1004 QSize SearchMenu::sizeHint() const
1005 {
1006 if (fixedwidthsize == -1)
1007 return QMenu::sizeHint();
1008 int borderx = style()->pixelMetric(QStyle::PM_MenuPanelWidth);
1009 return QSize(fixedwidthsize + borderx * 2,QMenu::sizeHint().height());
1010 }
1011
onAboutToShowEvent()1012 void SearchMenu::onAboutToShowEvent()
1013 {
1014 setLineEditFocus();
1015 selectTextIfNotEmpty();
1016 //resizeGUI();
1017 alignToParentGeometry();
1018 }
1019
resizeEvent(QResizeEvent * event)1020 void SearchMenu::resizeEvent( QResizeEvent * event )
1021 {
1022 if (fixedwidthsize != -1)
1023 {
1024 searchline->setMinimumWidth(fixedwidthsize);
1025 searchline->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Fixed);
1026 }
1027 QMenu::resizeEvent(event);
1028 alignToParentGeometry();
1029 }
1030
searchLineWidth()1031 int& SearchMenu::searchLineWidth()
1032 {
1033 return fixedwidthsize;
1034 }
1035 //MyToolButton class has been introduced to overcome the "always on screen small down arrow visualization problem" officially recognized qt bug.
MyToolButton(int msecdelay,QWidget * parent)1036 MyToolButton::MyToolButton( int msecdelay,QWidget * parent /*= 0 */ )
1037 : QToolButton( parent )
1038 {
1039 if (msecdelay != 0)
1040 {
1041 setPopupMode(QToolButton::DelayedPopup);
1042 DelayedToolButtonPopUpStyle* delstyle = new DelayedToolButtonPopUpStyle(msecdelay);
1043 setStyle(delstyle);
1044 }
1045 else
1046 setPopupMode(QToolButton::InstantPopup);
1047 }
1048
MyToolButton(QAction * act,int msecdelay,QWidget * parent)1049 MyToolButton::MyToolButton( QAction* act, int msecdelay /*= 0*/,QWidget * parent /*= 0*/ )
1050 :QToolButton(parent)
1051 {
1052 if (msecdelay != 0)
1053 {
1054 setPopupMode(QToolButton::DelayedPopup);
1055 DelayedToolButtonPopUpStyle* delstyle = new DelayedToolButtonPopUpStyle(msecdelay);
1056 setStyle(delstyle);
1057 }
1058 else
1059 setPopupMode(QToolButton::InstantPopup);
1060 setDefaultAction(act);
1061 }
1062
paintEvent(QPaintEvent *)1063 void MyToolButton::paintEvent( QPaintEvent * )
1064 {
1065 QStylePainter p(this);
1066 QStyleOptionToolButton opt;
1067 initStyleOption( & opt );
1068 opt.features &= (~ QStyleOptionToolButton::HasMenu);
1069 p.drawComplexControl( QStyle::CC_ToolButton, opt );
1070 }
1071
openMenu()1072 void MyToolButton::openMenu()
1073 {
1074 if (menu()->isHidden())
1075 showMenu();
1076 }
1077
MenuLineEdit(QWidget * parent)1078 MenuLineEdit::MenuLineEdit( QWidget* parent )
1079 :QLineEdit(parent)
1080 {
1081
1082 }
1083
keyPressEvent(QKeyEvent * event)1084 void MenuLineEdit::keyPressEvent( QKeyEvent * event )
1085 {
1086 int k = event->key();
1087 if ((k != Qt::Key_Up) && (k != Qt::Key_Down))
1088 QLineEdit::keyPressEvent(event);
1089 else
1090 emit arrowPressed(k);
1091 }
1092
1093
MenuWithToolTip(const QString & name,QWidget * par)1094 MenuWithToolTip::MenuWithToolTip( const QString& name,QWidget* par )
1095 :QMenu(name,par)
1096 {
1097
1098 }
1099
event(QEvent * e)1100 bool MenuWithToolTip::event(QEvent * e)
1101 {
1102 // QString pippo = QEvent::staticMetaObject.enumerator(QEvent::staticMetaObject.indexOfEnumerator("Type")).valueToKey(e->type());
1103
1104 const QHelpEvent *helpEvent = static_cast <QHelpEvent *>(e);
1105 if ((helpEvent->type() == QEvent::ToolTip) && (activeAction() != 0))
1106 QToolTip::showText(helpEvent->globalPos(), activeAction()->toolTip());
1107 /*else
1108 QToolTip::hideText();*/
1109 return QMenu::event(e);
1110 }
1111
1112
DelayedToolButtonPopUpStyle(int msec)1113 DelayedToolButtonPopUpStyle::DelayedToolButtonPopUpStyle( int msec )
1114 :QProxyStyle()
1115 {
1116 _msec = msec;
1117 }
1118
styleHint(QStyle::StyleHint sh,const QStyleOption * opt,const QWidget * widget,QStyleHintReturn * hret) const1119 int DelayedToolButtonPopUpStyle::styleHint(QStyle::StyleHint sh, const QStyleOption * opt /*= 0*/, const QWidget * widget /*= 0*/, QStyleHintReturn * hret /*= 0*/ ) const
1120 {
1121 if (sh == QProxyStyle::SH_ToolButton_PopupDelay)
1122 return _msec;
1123 return QProxyStyle::styleHint(sh,opt,widget,hret);
1124 }
1125
MLFloatSlider(QWidget * parent)1126 MLFloatSlider::MLFloatSlider( QWidget *parent /*= 0*/ )
1127 : QSlider(parent)
1128 {
1129 connect(this, SIGNAL(valueChanged(int)),
1130 this, SLOT(notifyValueChanged(int)));
1131 }
1132
notifyValueChanged(int value)1133 void MLFloatSlider::notifyValueChanged( int value )
1134 {
1135 emit floatValueChanged(float(value));
1136 }
1137
setValue(float val)1138 void MLFloatSlider::setValue( float val )
1139 {
1140 setSliderPosition(int(val));
1141 }
1142