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