1 /***************************************************************************
2 * *
3 * copyright : (C) 2011 Joshua Netterfield *
4 * joshua.netterfield@gmail.com *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 ***************************************************************************/
12
13 #include "cclineedit.h"
14 #include "cclineedit_p.h"
15
16 #include "dialoglauncher.h"
17 #include "scalar.h"
18 #include "vector.h"
19 #include "objectstore.h"
20
21 #include <QCompleter>
22 #include <QAbstractItemView>
23 #include <QPushButton>
24 #include <QStringListModel>
25 #include <QSortFilterProxyModel>
26 #include <QComboBox>
27 #include <QDebug>
28 #include <QKeyEvent>
29 #include <QApplication>
30 #include <QDesktopWidget>
31 #include <QScrollBar>
32 #include <QTableWidget>
33 #include <QHeaderView>
34 #include <QHash>
35 #include <QMenu>
36 #include <QTimer>
37 #include <QLabel>
38
39 #ifdef QT5
40 #define setResizeMode setSectionResizeMode
41 #endif
42
43 namespace Kst {
44
45 QList<CCCommonEdit*> CCCommonEdit::_u;
46
47 // CompletionCase
operator <(const CompletionCase & a,const CompletionCase & b)48 bool operator<(const CompletionCase&a,const CompletionCase&b)
49 {
50 return a.prefix().size()<b.prefix().size();
51 }
52
53 // CategoricalCompleter
CategoricalCompleter(QLineEdit * lineEdit,QList<CompletionCase> data)54 CategoricalCompleter::CategoricalCompleter(QLineEdit *lineEdit, QList<CompletionCase> data) : QCompleter(getDefault(data)),
55 _data(data), _tableView(new CCTableView(&_data[0])), _currentSubset(&data[0])
56 {
57 setPopup(_tableView);
58 lineEdit->setCompleter(this);
59 _tableView->setCompleter(this);
60 _tableView->updateSuggestions();
61 setCompletionMode(PopupCompletion);
62 setCaseSensitivity(Qt::CaseInsensitive);
63 setWrapAround(false);
64 qSort(_data.begin(),_data.end());
65 connect(_tableView,SIGNAL(activateHint(QString)),this,SIGNAL(activated(QString)));
66 }
67
CategoricalCompleter(QTextEdit * textEdit,QList<CompletionCase> data)68 CategoricalCompleter::CategoricalCompleter(QTextEdit *textEdit, QList<CompletionCase> data) : QCompleter(getDefault(data)),
69 _data(data), _tableView(new CCTableView(&_data[0])), _currentSubset(&data[0])
70 {
71 setPopup(_tableView);
72 _tableView->setCompleter(this);
73 _tableView->updateSuggestions();
74 setCompletionMode(PopupCompletion);
75 setCaseSensitivity(Qt::CaseInsensitive);
76 setWrapAround(false);
77 qSort(_data.begin(),_data.end());
78 connect(_tableView,SIGNAL(activateHint(QString)),this,SIGNAL(activated(QString)));
79 }
80
eventFilter(QObject * o,QEvent * e)81 bool CategoricalCompleter::eventFilter(QObject *o, QEvent *e)
82 {
83 if(e->type()!=QEvent::KeyPress) {
84 return QCompleter::eventFilter(o,e);
85 } else {
86 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
87 int key=ke->key();
88 #if defined(__QNX__) || defined(__ANDROID__)
89 if (key == Qt::Key_Return || key == Qt::Key_Enter) {
90 _tableView->hide();
91 return 1;
92 }
93 #endif
94 int cc=_tableView->currentIndex().column();
95 bool cantMoveRight=(cc==_tableView->model()->columnCount()-1);
96 bool cantMoveLeft=!cc;
97 if(!cantMoveLeft) {
98 bool ok=1;
99 for(int i=0;i<cc;i++) {
100 if(!_tableView->model()->data(_tableView->model()->index(_tableView->currentIndex().row(),i)).toString().isEmpty()) {
101 ok=0;
102 break;
103 }
104 }
105 cantMoveLeft=ok;
106 }
107 if(!cantMoveRight) {
108 bool ok=1;
109 for(int i=cc+1;i<_tableView->model()->columnCount();i++) {
110 if(!_tableView->model()->data(_tableView->model()->index(_tableView->currentIndex().row(),i)).toString().isEmpty()) {
111 ok=0;
112 break;
113 }
114 }
115 cantMoveRight=ok;
116
117 }
118 if((key!=Qt::Key_Left||!cantMoveLeft)&&(key!=Qt::Key_Right||!cantMoveRight)) {
119 if(_tableView->isVisible()) {
120 _tableView->keyPressEvent(ke);
121 }
122 }
123
124 if(_tableView->isHidden()||((key!=Qt::Key_Enter)&&(key!=Qt::Key_Return)/*&&(key!=Qt::Key_Space)*/&&(key!=Qt::Key_Up)&&(key!=Qt::Key_Down&&key!=Qt::Key_PageDown&&key!=Qt::Key_PageUp))) {
125 if(_tableView->isHidden()||(((key==Qt::Key_Left&&cantMoveLeft)||(key==Qt::Key_Right&&cantMoveRight))||(key!=Qt::Key_Left&&key!=Qt::Key_Right))) {
126 if(_tableView->isHidden()||(!(cantMoveRight&&key==Qt::Key_Right&&_tableView->widgetCursorPosition()==_tableView->widgetText().size()))) {
127 if(_tableView->isHidden()||(!(cantMoveLeft&&key==Qt::Key_Left&&!_tableView->widgetCursorPosition()))) {
128 if(key!=Qt::Key_Alt&&key!=Qt::Key_Shift) {
129 _tableView->_goingRight=key==Qt::Key_Right;
130 (static_cast<QObject *>(widget()))->event(ke); //in QWidget, event(...) is protected
131 }
132 }
133 }
134 }
135 }
136 switch (key) {
137 case Qt::Key_Return:
138 case Qt::Key_Enter:
139 case Qt::Key_Tab:
140 if(_tableView->isVisible()) {
141 if(_tableView->currentIndex().row()!=-1&&_tableView->currentIndex().column()!=-1) {
142 setCompletionPrefix(_tableView->currentIndex().model()->data(_tableView->currentIndex()).toString());
143 emit activated(completionPrefix());
144 _tableView->hide();
145 }
146 }
147 break;
148
149 case Qt::Key_Backtab:
150 case Qt::Key_Escape:
151 _tableView->hide();
152 break;
153 default:
154 break;
155 }
156
157 verifyPrefix();
158 }
159 return 1;
160 }
161
verifyPrefix()162 void CategoricalCompleter::verifyPrefix()
163 {
164 QString search=completionPrefix();
165 QString altsearch=search;
166 QStringList altlist;
167 if(search.contains("*")) {
168 search.remove(search.indexOf('*'),99999);
169 altsearch.remove(0,altsearch.indexOf('*')+1);
170 altlist=altsearch.split('*');
171 setCompletionPrefix(search);
172 }
173
174 for(int i=_data.size()-1;i>=0;i--) {
175 if(!_data[i].prefix().size()||!search.indexOf(_data[i].prefix())) {
176 SVCCLineEdit* hack=qobject_cast<SVCCLineEdit*>(widget());
177 if(hack&&_data[i].prefix().isEmpty()&&_data[i].size()&&_data[i][0].title().contains("Fun")) {
178 QString operatorNextList="])0123456789";
179 QString functionNextList="&=<>!+-/*&^|(";
180 int last1=-1,last2=-1;
181 for(int i=0;i<operatorNextList.size();i++) {
182 last1=qMax(last1,hack->text().lastIndexOf(operatorNextList[i],hack->cursorPosition()-1));
183 }
184 last1=qMax(last1,hack->text().lastIndexOf("PI ",hack->cursorPosition()-1,Qt::CaseInsensitive));
185 last1=qMax(last1,hack->text().lastIndexOf("e ",hack->cursorPosition()-1,Qt::CaseInsensitive));
186
187 for(int i=0;i<functionNextList.size();i++) {
188 last2=qMax(last2,hack->text().lastIndexOf(functionNextList[i],hack->cursorPosition()-1));
189 }
190 if(last2<last1) {
191 continue;
192 }
193 }
194
195 if(_currentSubset!=&_data[i]||search!=altsearch) {
196 setModel(new QStringListModel(join(_data[i],_data[i].prefix(),(search==altsearch)?QStringList():altlist,search.size())));
197 _tableView->setData(&_data[i],_data[i].prefix());
198 complete();
199 _currentSubset=&_data[i];
200 setCompletionPrefix(search);
201 }
202 break;
203 }
204 }
205 }
206
~CategoricalCompleter()207 CategoricalCompleter::~CategoricalCompleter()
208 {
209 //_tableView is child
210 }
211
join(CompletionCase & l,QString prefix,QStringList searchpattern,int complength)212 QStringList CategoricalCompleter::join(CompletionCase& l,QString prefix,QStringList searchpattern,int complength)
213 {
214 QStringList ret;
215 for(int i=0;i<l.size();i++) {
216 ret<<l[i];
217 }
218 if(prefix.size()||searchpattern.size()) {
219 for(int i=0;i<ret.size();i++) {
220 ret[i].prepend(prefix);
221 int lastindex=prefix.size()+complength;
222 for(int j=0;j<searchpattern.size();j++) {
223 if(ret[i].indexOf(searchpattern[j],lastindex,Qt::CaseInsensitive)==-1) {
224 ret.takeAt(i);
225 i-=1;
226 break;
227 }
228 lastindex=ret[i].indexOf(searchpattern[j],lastindex)+searchpattern.size();
229 }
230 }
231 }
232 return ret;
233 }
234
getDefault(QList<CompletionCase> & ccl)235 QStringList CategoricalCompleter::getDefault(QList<CompletionCase>& ccl)
236 {
237 for(int i=0;i<ccl.size();i++) {
238 if(ccl[i].prefix().isEmpty()) {
239 return join(ccl[i]);
240 }
241 }
242 //if none found:
243 ccl.push_back(CompletionCase(""));
244 return join(ccl.back());
245 }
246
247 // CCLineEdit and CCTextEdit
CCLineEdit(QWidget * p)248 CCLineEdit::CCLineEdit(QWidget*p) : QLineEdit(p), _cc() { }
CCTextEdit(QWidget * p)249 CCTextEdit::CCTextEdit(QWidget*p) : QTextEdit(p), _cc() { }
250
CCLineEdit(const QString & s,QWidget * p)251 CCLineEdit::CCLineEdit(const QString &s, QWidget *p) : QLineEdit(s,p), _cc() { }
CCTextEdit(const QString & s,QWidget * p)252 CCTextEdit::CCTextEdit(const QString &s, QWidget *p) : QTextEdit(s,p), _cc() { }
253
~CCLineEdit()254 CCLineEdit::~CCLineEdit() { delete _cc; }
~CCTextEdit()255 CCTextEdit::~CCTextEdit() { delete _cc; }
256
Insert(const QString & i,bool completion)257 void CCCommonEdit::Insert(const QString &i,bool completion)
258 {
259 QString x=Text(),y=Text();
260 y.remove(0,CursorPosition());
261 x.truncate(CursorPosition());
262
263 if(completion) {
264 bool caught=0;
265 if(i.size()) {
266 caught= i[i.size()-1]==']'; // this is a hard-coded exception to allow spaces in vector or scalar names. make less hacky
267 }
268
269 QChar search;
270 if(caught) {
271 search='[';
272 } else {
273 const QString& possiblePhraseEndings=" =$.\n:/*]()%^&|!<>0245+1337-6789\\";
274 // also change in divide!!
275 int maxIndex=-1;
276 for(int j=0;j<possiblePhraseEndings.size();j++) {
277 if(x.lastIndexOf(possiblePhraseEndings[j])>maxIndex) {
278 maxIndex=x.lastIndexOf(possiblePhraseEndings[j]);
279 search=possiblePhraseEndings[j];
280 }
281 }
282 }
283
284 if(!caught||(x.lastIndexOf(']')<x.lastIndexOf('['))) {
285 if(x.lastIndexOf(search)!=-1) {
286 x.remove(x.lastIndexOf(search)+(caught?0:1),1000000);
287 } else {
288 x.remove(0,x.size());
289 }
290 }
291 if(search=='\\') {
292 x.chop(1);
293 }
294 }
295 SetText(x+i+y);
296 SetCursorPosition((x+i).size());
297
298 //Special exception:
299 int cbPos=Text().indexOf("]",(x+i).size());
300 int cbPosNot=Text().indexOf("\\]",(x+i).size())+1;
301 int obPos=Text().indexOf("[",(x+i).size());
302 if(cbPos!=-1&&cbPos!=cbPosNot&&(cbPos<obPos||obPos==-1))
303 {
304 QString t=Text();
305 t.remove(CursorPosition(),cbPos-CursorPosition()+1);
306 int cp=CursorPosition();
307 SetText(t);
308 SetCursorPosition(cp);
309 }
310 }
311
Divide(QString x)312 void CCCommonEdit::Divide(QString x)
313 {
314 x=(x=="\0")?Text():x;
315 x.truncate(CursorPosition());
316
317 // this is a hard-coded exception to allow spaces in vector or scalar names. make less hacky {
318
319 bool caught = (x.indexOf("[")!=-1)&&(x.indexOf("]",x.lastIndexOf("["))==-1);
320 if(caught) {
321 caught=((x.lastIndexOf("\\[")==-1)||(x.lastIndexOf("\\[")+1!=x.lastIndexOf("[")));
322 }
323
324 //}
325
326 QChar search;
327 if(caught) {
328 search='[';
329 } else {
330 const QString& possiblePhraseEndings=" =$.\n:/*]()%^&|!<>0245+1337-6789";
331 // also change in insert!!
332
333 int maxIndex=-2;
334 for(int i=0;i<possiblePhraseEndings.size();i++) {
335 if(x.lastIndexOf(possiblePhraseEndings[i])>maxIndex) {
336 maxIndex=x.lastIndexOf(possiblePhraseEndings[i]);
337 search=possiblePhraseEndings[i];
338 }
339 }
340 }
341
342 if(!caught&&x.lastIndexOf("\\")>x.lastIndexOf(search)&&x.lastIndexOf("\\")) // this is an exception to allow non-space-sperated Latex commands
343 {
344 caught=1;
345 x.remove(0,x.lastIndexOf("\\"));
346 }
347 else if(x.lastIndexOf(search)) {
348 x.remove(0,x.lastIndexOf(search)+(caught?0:1));
349 }
350
351 ChangeCurrentPrefix(x);
352 }
353
keyPressEvent(QKeyEvent * ev)354 void CCLineEdit::keyPressEvent(QKeyEvent*ev)
355 {
356 if(!_cc) {
357 QLineEdit::keyPressEvent(ev);
358 return;
359 }
360 _cc->_tableView->keyPressEvent(ev);
361 QLineEdit::keyPressEvent(ev);
362 _cc->verifyPrefix();
363 _cc->_tableView->updateSuggestions();
364 }
insert(const QString & i,bool stringIsCompletion)365 void CCTextEdit::insert(const QString &i, bool stringIsCompletion)
366 {
367 Insert(i,stringIsCompletion);
368 }
369
divide(QString x)370 void CCTextEdit::divide(QString x)
371 {
372 Divide(x);
373 }
keyPressEvent(QKeyEvent * ev)374 void CCTextEdit::keyPressEvent(QKeyEvent*ev)
375 {
376 if(!_cc) {
377 QTextEdit::keyPressEvent(ev);
378 return;
379 }
380 _cc->_tableView->keyPressEvent(ev);
381 QTextEdit::keyPressEvent(ev);
382 _cc->verifyPrefix();
383 _cc->_tableView->updateSuggestions();
384 }
385
mousePressEvent(QMouseEvent * ev)386 void CCLineEdit::mousePressEvent(QMouseEvent*ev)
387 {
388 if(ev->buttons()==Qt::LeftButton) {
389 _cc->verifyPrefix();
390 _cc->_tableView->updateSuggestions();
391 }
392 QLineEdit::mousePressEvent(ev);
393 }
394
ChangeCurrentPrefix(QString x)395 void CCLineEdit::ChangeCurrentPrefix(QString x)
396 {
397 emit currentPrefixChanged(x);
398 }
399
ChangeCurrentPrefix(QString x)400 void CCTextEdit::ChangeCurrentPrefix(QString x)
401 {
402 emit currentPrefixChanged(x);
403 }
404
init(QList<CompletionCase> data)405 void CCLineEdit::init(QList<CompletionCase> data)
406 {
407 delete _cc;
408
409 _cc=new CategoricalCompleter(this,data);
410 setCompleter(0);
411 connect(this,SIGNAL(textChanged(QString)),this,SLOT(divide(QString)));
412 connect(this,SIGNAL(cursorPositionChanged(int,int)),this,SLOT(divide()));
413
414 connect(this,SIGNAL(currentPrefixChanged(QString)),_cc,SLOT(setCompletionPrefix(QString)));
415 connect(_cc,SIGNAL(activated(QString)),this,SLOT(insert(QString)));
416 _cc->_tableView->setLineEdit(this);
417 _cc->setWidget(this);
418 }
419
init(QList<CompletionCase> data)420 void CCTextEdit::init(QList<CompletionCase> data)
421 {
422 delete _cc;
423
424 _cc=new CategoricalCompleter(this,data);
425 connect(this,SIGNAL(textChanged()),this,SLOT(divide()));
426 connect(this,SIGNAL(cursorPositionChanged()),this,SLOT(divide()));
427
428 connect(this,SIGNAL(currentPrefixChanged(QString)),_cc,SLOT(setCompletionPrefix(QString)));
429 connect(_cc,SIGNAL(activated(QString)),this,SLOT(insert(QString)));
430 _cc->_tableView->setTextEdit(this);
431 _cc->setWidget(this);
432 }
433
insert(const QString & i,bool stringIsCompletion)434 void CCLineEdit::insert(const QString &i,bool stringIsCompletion)
435 {
436 Insert(i,stringIsCompletion);
437 QTimer* timer=new QTimer;
438 connect(timer,SIGNAL(timeout()),_cc->_tableView,SLOT(updateSuggestions()));
439 connect(timer,SIGNAL(timeout()),timer,SLOT(deleteLater()));
440 timer->start(0);
441 #if !defined(__QNX__) && !defined(__ANDROID__)
442 setFocus();
443 #endif
444 }
445
divide(QString x)446 void CCLineEdit::divide(QString x)
447 {
448 Divide(x);
449 }
450
451
452 // SVCCLineEdit & SVCCTextEdit
453
SVCCLineEdit(QWidget * p)454 SVCCLineEdit::SVCCLineEdit(QWidget*p) : CCLineEdit(p),_extraData(0),_svData(0),_allData(0) { }
SVCCTextEdit(QWidget * p)455 SVCCTextEdit::SVCCTextEdit(QWidget*p) : CCTextEdit(p),_extraData(0),_svData(0),_allData(0) { }
456
SVCCLineEdit(const QString & s,QWidget * p)457 SVCCLineEdit::SVCCLineEdit(const QString &s, QWidget *p) : CCLineEdit(s,p),_extraData(0),_svData(0),_allData(0) { }
SVCCTextEdit(const QString & s,QWidget * p)458 SVCCTextEdit::SVCCTextEdit(const QString &s, QWidget *p) : CCTextEdit(s,p),_extraData(0),_svData(0),_allData(0) { }
459
~SVCCLineEdit()460 SVCCLineEdit::~SVCCLineEdit() { delete _extraData; delete _svData; delete _allData; }
~SVCCTextEdit()461 SVCCTextEdit::~SVCCTextEdit() { delete _extraData; delete _svData; delete _allData; }
462
setObjectStore(ObjectStore * store)463 void SVCCLineEdit::setObjectStore(ObjectStore *store)
464 {
465 _store=store;
466 fillKstObjects();
467 }
468
setObjectStore(ObjectStore * store)469 void SVCCTextEdit::setObjectStore(ObjectStore *store)
470 {
471 _store=store;
472 fillKstObjects();
473 }
474
475
init(QList<CompletionCase> data)476 void SVCCLineEdit::init(QList<CompletionCase> data)
477 {
478 if(data.size())
479 {
480 _extraData=_extraData?_extraData:new QList<CompletionCase>();
481 *_extraData=data;
482 }
483 if(_cc) {
484 delete _allData;
485 }
486 _allData = new QList<CompletionCase>;
487 for(int i=0;_extraData&&i<_extraData->size();i++) {
488 _allData->push_back((*_extraData)[i]);
489 }
490 if(!_allData->size()) {
491 _allData->push_back(CompletionCase(""));
492 }
493 //hacky {
494 if(!_svData||!_svData->size()||(_svData->at(0).size()<2)) {
495 return;
496 }
497 for(int i=0;i<_svData->front().size();i++) {
498 _allData[0][1].push_back(_svData->front()[i]);
499 }
500 for(int i=1;_svData&&i<_svData->size();i++) {
501 _allData->push_back((*_svData)[i]);
502 }
503 // }
504 CCLineEdit::init(*_allData);
505 }
506
init(QList<CompletionCase> data)507 void SVCCTextEdit::init(QList<CompletionCase> data)
508 {
509 if(data.size())
510 {
511 _extraData=_extraData?_extraData:new QList<CompletionCase>();
512 *_extraData=data;
513 }
514 if(_cc) {
515 delete _allData;
516 }
517 _allData = new QList<CompletionCase>;
518 for(int k=0;k<2;k++) {
519 QList<CompletionCase>* add=k?_svData:_extraData;
520 for(int i=0;add&&i<add->size();i++) {
521 _allData->push_back((*add)[i]);
522 }
523 }
524 _allData->push_back(CompletionCase("\\"));
525
526 /*<qt>The syntax for labels is a derivative of a subset of LaTeX. "
527 "Supported syntax is: <b>\\[greeklettername]</b> and <b>\\[Greeklettername]</b>, "
528 "<b>\\approx</b>, <b>\\cdot</b>, <b>\\ge</b>, <b>\\geq</b>, <b>\\inf</b> ,"
529 "<b>\\int</b>, <b>\\le</b>, <b>\\leq</b>, <b>\\ne</b>, <b>\\n</b>, "
530 "<b>\\partial</b>, <b>\\prod</b>, <b>\\pm</b>, "
531 "<b>\\textcolor{color name}{colored text}</b>, <b>\\textbf{bold text}</b>, "
532 "<b>\\textit{italicized text}</b>, <b>\\t</b>, <b>\\sum</b>, <b>\\sqrt</b>, "
533 "<b>\\underline{underlined text}</b>, <b>\\overline{overlined text}</b>, "
534 "<b>x^y</b>, <b>x_y</b>. "
535 "Scalars, equations, and vector elements can be embedded. "
536 "Scalar: <i>[V1/Mean]</i>. Vector Element: <i>[V1[4]]</i>. "
537 "Equation: <i>[=[V1/Mean]^2]</i>. A [ character can be inserted as <i>\\[</i>.*/
538
539 _allData->back().push_back(Category("Greek Letter Names"));
540 _allData->back().back().push_back("Alpha");
541 _allData->back().back().push_back("Beta");
542 _allData->back().back().push_back("Gamma");
543 _allData->back().back().push_back("Delta");
544 _allData->back().back().push_back("Epsilon");
545 _allData->back().back().push_back("Zeta");
546 _allData->back().back().push_back("Eta");
547 _allData->back().back().push_back("Theta");
548 _allData->back().back().push_back("Iota");
549 _allData->back().back().push_back("Kappa");
550 _allData->back().back().push_back("Lambda");
551 _allData->back().back().push_back("Mu");
552 _allData->back().back().push_back("Nu");
553 _allData->back().back().push_back("Xi");
554 _allData->back().back().push_back("Omicron");
555 _allData->back().back().push_back("Pi");
556 _allData->back().back().push_back("Rho");
557 _allData->back().back().push_back("Sigma");
558 _allData->back().back().push_back("Tau");
559 _allData->back().back().push_back("Upsilon");
560 _allData->back().back().push_back("Phi");
561 _allData->back().back().push_back("Chi");
562 _allData->back().back().push_back("Psi");
563 _allData->back().back().push_back("Omega");
564 int max=_allData->back().back().size();
565 for(int i=0;i<max;i++) {
566 _allData->back().back().push_back(_allData->back().back()[i].toLower());
567 }
568 _allData->back().push_back(Category("Math Symbols"));
569 _allData->back().back().push_back("Approx");
570 _allData->back().back().push_back("Cdot");
571 _allData->back().back().push_back("Epsilon");
572 _allData->back().back().push_back("Ell");
573 _allData->back().back().push_back("Geq");
574 _allData->back().back().push_back("Ge");
575 _allData->back().back().push_back("Inf");
576 _allData->back().back().push_back("Int");
577 _allData->back().back().push_back("Leq");
578 _allData->back().back().push_back("Le");
579 _allData->back().back().push_back("Ne");
580 _allData->back().back().push_back("Overline{");
581 _allData->back().back().push_back("Odot");
582 _allData->back().back().push_back("Partial");
583 _allData->back().back().push_back("Prod");
584 _allData->back().back().push_back("Pm");
585 _allData->back().back().push_back("Textcolor{");
586 _allData->back().back().push_back("Textbf{");
587 _allData->back().back().push_back("Textit{");
588 _allData->back().back().push_back("Sum");
589 _allData->back().back().push_back("Sqrt");
590 _allData->back().back().push_back("Underline{");
591
592
593 CCTextEdit::init(*_allData);
594 }
595
596 #define SIZE_LIMITED_NAME sizeLimitedName( (_cc&&_cc->_tableView)?_cc->_tableView->font():QFont(), \
597 (_cc&&_cc->_tableView)? ((_cc->_tableView->width()/2-50)): INT_MAX)
598
fillKstObjects()599 void SVCCLineEdit::fillKstObjects()
600 {
601 if(!_store) {
602 qDebug()<<"Warning: SVCCLineEdit::fillKstObjects() called without object store...";
603 return;
604 }
605 if(_svData) {
606 delete _svData;
607 }
608 CCLineEdit::init(*_extraData);
609 _svData = new QList<CompletionCase>;
610 _svData->push_back(CompletionCase(""));
611 _svData->back().push_back(Category("Scalars"));
612 _svData->back().back().push_back("PI ");
613 _svData->back().back().push_back("e ");
614 _svData->back().push_back(Category("Vectors"));
615 _svData->push_back(CompletionCase("["));
616 _svData->back().push_back(Category("Scalars"));
617 _svData->back().push_back(Category("Vectors"));
618
619 ScalarList scalarList = _store->getObjects<Scalar>();
620 VectorList vectorList = _store->getObjects<Vector>();
621
622 if(_cc&&_cc->_tableView) {
623 _cc->_tableView->setFixedWidth(_cc->_tableView->width());
624 }
625
626 ScalarList::ConstIterator scalarIt = scalarList.constBegin();
627 for (; scalarIt != scalarList.constEnd(); ++scalarIt) {
628 ScalarPtr scalar = (*scalarIt);
629
630 scalar->readLock();
631 if (!scalar->hidden()) {
632 _svData->back()[0].push_back(scalar->SIZE_LIMITED_NAME+']');
633 _svData->front()[0].push_back('['+scalar->SIZE_LIMITED_NAME+']');
634 }
635 scalar->unlock();
636 }
637
638 VectorList::ConstIterator vectorIt = vectorList.constBegin();
639 for (; vectorIt != vectorList.constEnd(); ++vectorIt) {
640 VectorPtr vector = (*vectorIt);
641
642 vector->readLock();
643 _svData->back()[1].push_back(vector->SIZE_LIMITED_NAME+']');
644 _svData->front()[1].push_back('['+vector->SIZE_LIMITED_NAME+']');
645 vector->unlock();
646 }
647
648 init();
649 }
650
fillKstObjects()651 void SVCCTextEdit::fillKstObjects()
652 {
653 if(!_store) {
654 qDebug()<<"Warning: SVCCTextEdit::fillKstObjects() called without object store...";
655 return;
656 }
657 if(_svData) {
658 delete _svData;
659 }
660 if(!_extraData) _extraData=new QList<CompletionCase>;
661 CCTextEdit::init(*_extraData);
662 _svData = new QList<CompletionCase>;
663 _svData->push_back(CompletionCase("["));
664 _svData->back().push_back(Category("Scalars"));
665
666 if(_cc&&_cc->_tableView) {
667 _cc->_tableView->setFixedWidth(_cc->_tableView->width());
668 }
669
670 ScalarList scalarList = _store->getObjects<Scalar>();
671 StringList stringList = _store->getObjects<String>();
672
673 ScalarList::ConstIterator scalarIt = scalarList.constBegin();
674 for (; scalarIt != scalarList.constEnd(); ++scalarIt) {
675 ScalarPtr scalar = (*scalarIt);
676
677 scalar->readLock();
678 _svData->back()[0].push_back(scalar->SIZE_LIMITED_NAME+']');
679 scalar->unlock();
680 }
681
682 _svData->back().push_back(Category("Strings"));
683
684 StringList::ConstIterator stringIt = stringList.constBegin();
685 for (; stringIt != stringList.constEnd(); ++stringIt) {
686 StringPtr string = (*stringIt);
687
688 string->readLock();
689 _svData->back()[1].push_back(string->SIZE_LIMITED_NAME+']');
690 string->unlock();
691 }
692
693 init();
694 }
695
contextMenuEvent(QContextMenuEvent * event)696 void SVCCLineEdit::contextMenuEvent(QContextMenuEvent *event)
697 {
698 QMouseEvent fake(QEvent::MouseButtonPress,event->pos(),Qt::NoButton,Qt::NoButton,Qt::NoModifier);
699 mousePressEvent(&fake); //update cursor
700 QMenu *menu = createStandardContextMenu();
701 QAction* newVectorAction=new QAction(tr("Insert New &Vector"),this);
702 connect(newVectorAction,SIGNAL(triggered()),this,SLOT(newVector()));
703 menu->insertAction(menu->actions()[0],newVectorAction);
704
705 QAction* newScalarAction=new QAction(tr("Insert New &Scalar"),this);
706 connect(newScalarAction,SIGNAL(triggered()),this,SLOT(newScalar()));
707 menu->insertAction(menu->actions()[1],newScalarAction);
708
709 QAction* editAction=0;
710 QString x=text();
711 int openVector=x.lastIndexOf("[",cursorPosition());
712 int closeVector=x.indexOf("]",cursorPosition());
713 if(openVector!=-1&&closeVector!=-1) {
714 x.remove(0,openVector+1);
715 x.remove(closeVector-openVector-1,99999);
716 if(x.indexOf("[")==x.indexOf("]")&&x.indexOf("[")==-1) {
717
718 QString x=text();
719 int openVector=x.lastIndexOf("[",cursorPosition());
720 int closeVector=x.indexOf("]",cursorPosition());
721 if(openVector!=-1&&closeVector!=-1) {
722 x.remove(0,openVector+1);
723 x.remove(closeVector-openVector-1,99999);
724 if(x.indexOf("[")==x.indexOf("]")&&x.indexOf("[")==-1) {
725 ObjectPtr ptr = _store->retrieveObject(x);
726
727 VectorPtr vector = kst_cast<Vector>(ptr);
728 ScalarPtr scalar = kst_cast<Scalar>(ptr);
729
730 if((scalar&&scalar->editable())||(vector&&vector->editable())) {
731
732 editAction=new QAction(tr("Edit")+" "+x,this);
733 connect(editAction,SIGNAL(triggered()),this,SLOT(editItem()));
734 menu->insertAction(menu->actions()[2],editAction);
735 }
736 }
737 }
738 }
739 }
740
741 menu->insertSeparator(menu->actions()[editAction?3:2]);
742
743 menu->exec(event->globalPos());
744 delete menu;
745 delete newVectorAction;
746 delete newScalarAction;
747 delete editAction;
748 }
749
contextMenuEvent(QContextMenuEvent * event)750 void SVCCTextEdit::contextMenuEvent(QContextMenuEvent *event)
751 {
752 QMouseEvent fake(QEvent::MouseButtonPress,event->pos(),Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
753 mousePressEvent(&fake); //update cursor
754 QMenu *menu = createStandardContextMenu();
755 /*QAction* newVectorAction=new QAction(tr("Insert New &Vector"),this);
756 connect(newVectorAction,SIGNAL(triggered()),this,SLOT(newVector()));
757 menu->insertAction(menu->actions()[0],newVectorAction);*/
758
759 QAction* newScalarAction=new QAction(tr("Insert New &Scalar"),this);
760 connect(newScalarAction,SIGNAL(triggered()),this,SLOT(newScalar()));
761 menu->insertAction(menu->actions()[0],newScalarAction);
762
763 QAction* newStringAction=new QAction(tr("Insert New &String"),this);
764 connect(newStringAction,SIGNAL(triggered()),this,SLOT(newString()));
765 menu->insertAction(menu->actions()[1],newStringAction);
766
767 QAction* editAction=0;
768 QString x=toPlainText();
769 int openVector=x.lastIndexOf("[",textCursor().position());
770 int closeVector=x.indexOf("]",textCursor().position());
771 if(openVector!=-1&&closeVector!=-1) {
772 x.remove(0,openVector+1);
773 x.remove(closeVector-openVector-1,99999);
774 if(x.indexOf("[")==x.indexOf("]")&&x.indexOf("[")==-1) {
775 QString x=toPlainText();
776 int openVector=x.lastIndexOf("[",textCursor().position());
777 int closeVector=x.indexOf("]",textCursor().position());
778 if(openVector!=-1&&closeVector!=-1) {
779 x.remove(0,openVector+1);
780 x.remove(closeVector-openVector-1,99999);
781 if(x.indexOf("[")==x.indexOf("]")&&x.indexOf("[")==-1) {
782 ObjectPtr ptr = _store->retrieveObject(x);
783
784 //VectorPtr vector = kst_cast<Vector>(ptr);
785 ScalarPtr scalar = kst_cast<Scalar>(ptr);
786 StringPtr string = kst_cast<String>(ptr);
787
788 if((scalar&&scalar->editable())||(string&&string->editable())) {
789 editAction=new QAction(tr("Edit")+" "+x,this);
790 connect(editAction,SIGNAL(triggered()),this,SLOT(editItem()));
791 menu->insertAction(menu->actions()[2],editAction);
792 }
793 }
794 }
795 }
796 }
797
798 menu->insertSeparator(menu->actions()[editAction?3:2]);
799
800 menu->exec(event->globalPos());
801 delete menu;
802 //delete newVectorAction;
803 delete newScalarAction;
804 delete newStringAction;
805 delete editAction;
806 }
807
NewVector()808 void CCCommonEdit::NewVector()
809 {
810 QString newName;
811 DialogLauncher::self()->showVectorDialog(newName, 0, true);
812 foreach(CCCommonEdit* ccc, _u) {
813 ccc->fillKstObjects();
814 }
815 VectorPtr vector = kst_cast<Vector>(_store->retrieveObject(newName));
816
817 if (vector) {
818 QString vName='['+vector->Name()+']';
819 Insert(vName,0);
820 }
821 }
822
NewScalar()823 void CCCommonEdit::NewScalar()
824 {
825 QString scalarName;
826 DialogLauncher::self()->showScalarDialog(scalarName, 0, true);
827 foreach(CCCommonEdit* ccc, _u) {
828 ccc->fillKstObjects();
829 } ScalarPtr scalar = kst_cast<Scalar>(_store->retrieveObject(scalarName));
830
831 if (scalar) {
832 QString sName='['+scalar->Name()+']';
833 Insert(sName,0);
834 }
835 }
836
NewString()837 void CCCommonEdit::NewString()
838 {
839 QString stringName;
840 DialogLauncher::self()->showStringDialog(stringName, 0, true);
841 foreach(CCCommonEdit* ccc, _u) {
842 ccc->fillKstObjects();
843 } StringPtr string = kst_cast<String>(_store->retrieveObject(stringName));
844
845 if (string) {
846 QString sName='['+string->Name()+']';
847 Insert(sName,0);
848 }
849 }
850
EditItem()851 void CCCommonEdit::EditItem()
852 {
853 QString x=Text();
854 int openVector=x.lastIndexOf("[",CursorPosition());
855 int closeVector=x.indexOf("]",CursorPosition());
856 if(openVector!=-1&&closeVector!=-1) {
857 x.remove(0,openVector+1);
858 x.remove(closeVector-openVector-1,99999);
859 if(x.indexOf("[")==x.indexOf("]")&&x.indexOf("[")==-1) {
860 ObjectPtr ptr = _store->retrieveObject(x);
861
862 //could be:
863 VectorPtr vector = kst_cast<Vector>(ptr);
864 ScalarPtr scalar = kst_cast<Scalar>(ptr);
865 StringPtr string = kst_cast<String>(ptr);
866 if(vector) {
867 if (vector->provider()) {
868 DialogLauncher::self()->showObjectDialog(vector->provider());
869 } else {
870 if(ObjectPtr(vector))
871 {
872 QString vectorName;
873 DialogLauncher::self()->showVectorDialog(vectorName,ObjectPtr(vector),1);
874 }
875 }
876 foreach(CCCommonEdit* ccc, _u) {
877 ccc->fillKstObjects();
878 }
879 } else if(scalar) {
880 if (scalar->provider()) {
881 DialogLauncher::self()->showObjectDialog(scalar->provider());
882 } else {
883 if(ObjectPtr(scalar))
884 {
885 QString scalarName;
886 DialogLauncher::self()->showScalarDialog(scalarName,ObjectPtr(scalar),1);
887 }
888 }
889 foreach(CCCommonEdit* ccc, _u) {
890 ccc->fillKstObjects();
891 }
892 } else if(string) {
893 if (string->provider()) {
894 DialogLauncher::self()->showObjectDialog(string->provider());
895 } else {
896 if(ObjectPtr(string))
897 {
898 QString stringName;
899 DialogLauncher::self()->showScalarDialog(stringName,ObjectPtr(string),1);
900 }
901 }
902 foreach(CCCommonEdit* ccc, _u) {
903 ccc->fillKstObjects();
904 }
905 }
906 }
907 }
908 }
909
910 // CCTableModel
911
rowCount(const QModelIndex &) const912 int CCTableModel::rowCount(const QModelIndex &) const
913 {
914 int max=0;
915 for(int i=0;i<_visibleData.size();i++) {
916 max=qMax(max,_visibleData[i].size());
917 }
918 return max-1; // top row is title
919 }
920
columnCount(const QModelIndex &) const921 int CCTableModel::columnCount(const QModelIndex &) const
922 {
923 return _visibleData.size();
924 }
925
data(const QModelIndex & index,int role) const926 QVariant CCTableModel::data(const QModelIndex &index, int role) const
927 {
928 if(role==Qt::SizeHintRole) { //evil problems have evil solutions -_-
929 emit checkSize();
930 if(index.column()>=columnCount(QModelIndex())) {
931 return QVariant();
932 }
933
934 if(s_minSizeCache[index.column()]!=QSize(-1,-1)) {
935 return s_minSizeCache[index.column()];
936 }
937 QLabel tmp;
938 int wid=0;
939 for(int i=0;i<_visibleData[index.column()].size();i++) {
940 tmp.setText(_visibleData[index.column()][i]);
941 wid=qMax(wid,tmp.sizeHint().width());
942 }
943 QSize s=tmp.sizeHint();
944 s.setWidth(wid*1.1);
945 return const_cast<CCTableModel*>(this)->s_minSizeCache[index.column()]=s;
946 }
947 if(role!=Qt::DisplayRole) return QVariant(); // Return unvalid QVariant
948 Q_ASSERT(index.column()<columnCount(QModelIndex()));
949 if(index.column()==-1) {
950 return QVariant("");
951 }
952 if (index.row()+1>=_visibleData[index.column()].size()) return QVariant(""); // +1 because top row is title
953 return _visibleData[index.column()][index.row()+1]; // +1 because top row is title
954 }
955
flags(const QModelIndex &) const956 Qt::ItemFlags CCTableModel::flags(const QModelIndex &) const
957 {
958 return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
959 }
960
headerData(int section,Qt::Orientation orientation,int role) const961 QVariant CCTableModel::headerData ( int section, Qt::Orientation orientation, int role ) const
962 {
963 Q_UNUSED(orientation);
964
965 if (_visibleData.size()<1) return QVariant();
966
967 if(role!=Qt::DisplayRole) return QVariant();
968 else return _visibleData[section][0];
969 }
970
971 // CCTableView
CCTableView(CompletionCase * data)972 CCTableView::CCTableView(CompletionCase* data) : _data(data), origModel(0), completer(0), _le(0), _te(0), _goingRight(0)
973 {
974 #if defined(__QNX__) || defined(__ANDROID__)
975 _close = new QPushButton("X", this);
976 _close->setGeometry(width()-50,0,40,40);
977 _close->show();
978 connect(_close, SIGNAL(clicked()), this, SLOT(hide()));
979 #endif
980
981 setSelectionMode(QAbstractItemView::SingleSelection);
982 setSelectionBehavior(QAbstractItemView::SelectItems);
983 setMinimumHeight(150);
984 verticalHeader()->hide();
985 setWindowModality(Qt::NonModal);
986 }
987
updateSuggestions()988 void CCTableView::updateSuggestions()
989 {
990 if(parentWidget()) {
991 setFixedWidth(qMin(width(),parentWidget()->width()));
992 }
993
994 //this could be made more efficient.
995 if(!model()||(!_le&&!_te)) {
996 return;
997 }
998
999 if(!origModel) origModel=model();
1000 else delete model();
1001
1002 QList<QStringList> items;
1003 for(int i=0;i<_data->size();i++) {
1004 items.push_back(QStringList());
1005 items[i].push_back((*_data)[i].title());
1006 }
1007 QString x=completer->completionPrefix();
1008 if(_le) {
1009 x.truncate(_le->cursorPosition());
1010 } else if (_te) {
1011 x.truncate(_te->textCursor().position());
1012 }
1013
1014 for(int i=0;i<completer->completionCount();i++) {
1015 completer->setCurrentRow(i);
1016 QString item=completer->currentCompletion();
1017 item.remove(_prefix);
1018 int x=-1;
1019 for(int j=0;j<_data->size();j++) {
1020 switch((*_data)[j].indexOf(item)) {
1021 case -1:
1022 break;
1023 default:
1024 x=j;
1025 break;
1026 }
1027 if(x==j) break;
1028 }
1029 if(x!=-1) {
1030 items[x]<<_data->prefix()+item;
1031 }
1032 }
1033 for(int i=0;i<items.size();i++) {
1034 if(items[i].size()==1) {
1035 horizontalHeader()->hideSection(i);
1036 } else {
1037 horizontalHeader()->showSection(i);
1038 }
1039 }
1040 CCTableModel* tmodel=new CCTableModel(items);
1041 setModel(tmodel);
1042 int minOK=-1;
1043 int maxOK=-1;
1044 for(int i=0;i<_data->size();i++) {
1045 if(items[i].size()>1) {
1046 minOK=(minOK==-1)?i:minOK;
1047 maxOK=i;
1048 }
1049 }
1050 if(minOK!=-1) {
1051 setCurrentIndex(model()->index(0,_goingRight?maxOK:minOK));
1052 }
1053
1054 resizeColumnsToContents();
1055 horizontalHeader()->setStretchLastSection(1);
1056 completer->complete();
1057 }
1058
setColumnHeaders(QStringList columnHeaders)1059 void CCTableView::setColumnHeaders(QStringList columnHeaders)
1060 {
1061 for(int i=0;i<columnHeaders.size();i++) {
1062 model()->setHeaderData(i,Qt::Horizontal,columnHeaders[i]);
1063 }
1064 }
1065
setCompleter(CategoricalCompleter * completer)1066 void CCTableView::setCompleter(CategoricalCompleter* completer)
1067 {
1068 setWindowModality(Qt::NonModal);
1069 this->completer=completer;
1070 }
1071
setData(CompletionCase * data,QString prefix)1072 void CCTableView::setData(CompletionCase* data,QString prefix)
1073 {
1074 _prefix=prefix;
1075 _data=data;
1076 origModel=0;
1077 updateSuggestions();
1078 }
1079
keyPressEvent(QKeyEvent * event)1080 void CCTableView::keyPressEvent(QKeyEvent *event)
1081 {
1082 QTableView::keyPressEvent(event);
1083 }
1084
mousePressEvent(QMouseEvent * event)1085 void CCTableView::mousePressEvent(QMouseEvent *event)
1086 {
1087 QPoint pos = event->pos();
1088 QModelIndex idx = indexAt(pos);
1089 setCurrentIndex(idx);
1090 if(currentIndex().row()!=-1&¤tIndex().column()!=-1)
1091 {
1092 completer->setCompletionPrefix(currentIndex().model()->data(currentIndex()).toString());
1093 emit activateHint(completer->completionPrefix());
1094 if(_le) {
1095 _le->insert(completer->completionPrefix());
1096 } else {
1097 _te->insert(completer->completionPrefix());
1098 }
1099 hide();
1100 } else {
1101 hide();
1102 return;
1103 }
1104 completer->verifyPrefix();
1105 updateSuggestions();
1106 }
1107
showEvent(QShowEvent *)1108 void CCTableView::showEvent(QShowEvent *)
1109 {
1110 horizontalHeader()->setResizeMode(QHeaderView::Interactive);
1111 horizontalHeader()->setStretchLastSection(1);
1112 }
1113
resizeEvent(QResizeEvent * ev)1114 void CCTableView::resizeEvent(QResizeEvent*ev)
1115 {
1116 #if defined(__QNX__) || defined(__ANDROID__)
1117 _close->setGeometry(width()-50,0,40,40);
1118 #endif
1119 QTableView::resizeEvent(ev);
1120 }
1121
1122 }
1123