SeExpr
ExprBrowser.cpp
Go to the documentation of this file.
1 /*
2 * Copyright Disney Enterprises, Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License
6 * and the following modification to it: Section 6 Trademarks.
7 * deleted and replaced with:
8 *
9 * 6. Trademarks. This License does not grant permission to use the
10 * trade names, trademarks, service marks, or product names of the
11 * Licensor and its affiliates, except as required for reproducing
12 * the content of the NOTICE file.
13 *
14 * You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * @file ExprBrowser.cpp
18 * @brief Qt browser widget for list of expressions
19 * @author aselle
20 */
21 #include <QtGui/QTreeWidget>
22 #include <QtGui/QTreeWidgetItem>
23 #include <QtGui/QVBoxLayout>
24 #include <QtGui/QTabWidget>
25 #include <QtGui/QHeaderView>
26 #include <QtGui/QLabel>
27 #include <QtGui/QTextBrowser>
28 #include <QtGui/QPushButton>
29 #include <QtGui/QSpacerItem>
30 #include <QtGui/QSizePolicy>
31 #include <QtGui/QSortFilterProxyModel>
32 #include <QtCore/QDir>
33 #include <QtCore/QFileInfo>
34 #include <QtGui/QFileDialog>
35 #include <QtGui/QMessageBox>
36 #include <cassert>
37 #include "ExprEditor.h"
38 #include "ExprBrowser.h"
39 
40 #define P3D_CONFIG_ENVVAR "P3D_CONFIG_PATH"
41 
42 
44 {
45 public:
46  ExprTreeItem(ExprTreeItem* parent,const QString& label,const QString& path)
47  :row(-1),parent(parent),label(label),path(path),populated(parent==0)
48  {}
49 
51  {
52  for(unsigned int i=0;i<children.size();i++)
53  delete children[i];
54  }
55 
57  {
58  if(this->path==path) return this;
59  else{
60  populate();
61  for(unsigned int i=0;i<children.size();i++){
62  ExprTreeItem* ret=children[i]->find(path);
63  if(ret) return ret;
64  }
65  }
66  return 0;
67  }
68 
69  void clear()
70  {
71  for(unsigned int i=0;i<children.size();i++) {
72  delete children[i];
73  }
74  children.clear();
75  }
76 
77  void populate()
78  {
79  if(populated) return;
80  populated=true;
81  QFileInfo info(path);
82  if(info.isDir()){
83  QFileInfoList infos=QDir(path).entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
84 
85  //std::cerr<<"is dir and populating "<<path.toStdString()<<std::endl;
86  for(QList<QFileInfo>::ConstIterator it=infos.constBegin();it!=infos.constEnd();++it){
87  const QFileInfo* fi=&*it;
88  if(fi->isDir() || fi->fileName().endsWith(".se")){
89  addChild(new ExprTreeItem(this,fi->fileName(),fi->filePath()));
90  }
91  }
92  }
93  }
94 
95  void addChild(ExprTreeItem* child)
96  {
97  child->row=children.size();
98  children.push_back(child);
99  }
100 
102  {
103  populate();
104  if(row<0 || row>(int)children.size()){
105  assert(false);
106  }
107  return children[row];
108  }
109 
111  {
112  populate();
113  return children.size();
114  }
115 
116  void regen()
117  {
118  std::vector<QString> labels,paths;
119  for(unsigned int i=0;i<children.size();i++){
120  labels.push_back(children[i]->label);
121  paths.push_back(children[i]->path);
122  delete children[i];
123  }
124  children.clear();
125 
126  for(unsigned int i=0;i<labels.size();i++)
127  addChild(new ExprTreeItem(this,labels[i],paths[i]));
128 
129  }
130 
131  int row;
133  QString label;
134  QString path;
135 private:
136  std::vector<ExprTreeItem*> children;
137  bool populated;
138 };
139 
140 class ExprTreeModel:public QAbstractItemModel
141 {
143 public:
145  :root(new ExprTreeItem(0,"",""))
146  {}
147 
149  {
150  delete root;
151  }
152 
153  void update()
154  {
155  reset();
156  }
157 
158  void clear()
159  {
160  root->clear();
161  update();
162  }
163 
164  void addPath(const char* label,const char* path)
165  {
166  root->addChild(new ExprTreeItem(root,label,path));
167  }
168 
169  QModelIndex parent(const QModelIndex& index) const
170  {
171  if(!index.isValid()) return QModelIndex();
172  ExprTreeItem* item=(ExprTreeItem*)(index.internalPointer());
173  ExprTreeItem* parentItem=item->parent;
174  if(parentItem==root) return QModelIndex();
175  else return createIndex(parentItem->row,0,parentItem);
176  }
177 
178  QModelIndex index(int row,int column,const QModelIndex& parent=QModelIndex()) const
179  {
180  if(!hasIndex(row,column,parent))
181  return QModelIndex();
182  else if(!parent.isValid()) return createIndex(row,column,root->getChild(row));
183  else{
184  ExprTreeItem* item=(ExprTreeItem*)(parent.internalPointer());
185  return createIndex(row,column,item->getChild(row));
186  }
187  }
188 
189  int columnCount(const QModelIndex& parent) const
190  {Q_UNUSED(parent); return 1;}
191 
192  int rowCount(const QModelIndex& parent=QModelIndex()) const
193  {
194  if(!parent.isValid()) return root->getChildCount();
195  else{
196  ExprTreeItem* item=(ExprTreeItem*)(parent.internalPointer());
197  if(!item) return root->getChildCount();
198  else return item->getChildCount();
199  }
200  }
201 
202  QVariant data(const QModelIndex& index,int role=Qt::DisplayRole) const
203  {
204  if(!index.isValid()) return QVariant();
205  if(role!=Qt::DisplayRole) return QVariant();
206  ExprTreeItem* item=(ExprTreeItem*)(index.internalPointer());
207  if(!item) return QVariant();
208  else return QVariant(item->label);
209  }
210 
211  QModelIndex find(QString path)
212  {
213  ExprTreeItem* item=root->find(path);
214  if(!item) {
215  root->regen();
216  reset();
217  item=root->find(path);
218  }
219  if(item){
220  std::cerr<<"found it "<<std::endl;
221  return createIndex(item->row,0,item);
222  }
223 
224  return QModelIndex();
225  }
226 
227 };
228 
229 class ExprTreeFilterModel:public QSortFilterProxyModel
230 {
231 public:
232  ExprTreeFilterModel(QWidget* parent=0)
233  :QSortFilterProxyModel(parent)
234  {}
235 
236  void update()
237  {
238  reset();
239  }
240 
241  bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
242  {
243  if(sourceParent.isValid() &&
244  sourceModel()->data(sourceParent).toString().contains(filterRegExp())) return true;
245  QString data=sourceModel()->data(sourceModel()->index(sourceRow,0,sourceParent)).toString();
246  bool keep=data.contains(filterRegExp());
247 
248  QModelIndex subIndex=sourceModel()->index(sourceRow,0,sourceParent);
249  if(subIndex.isValid()){
250  for(int i=0;i<sourceModel()->rowCount(subIndex);++i)
251  keep = keep || filterAcceptsRow(i, subIndex);
252  }
253  return keep;
254  }
255 
256 };
257 
259 {
260  delete treeModel;
261 }
262 
263 ExprBrowser::ExprBrowser(QWidget* parent, ExprEditor* editor)
264  :QWidget(parent),editor(editor),
265  _context(""),
266  _searchPath(""),
267  _applyOnSelect(true)
268 {
269  QVBoxLayout* rootLayout = new QVBoxLayout;
270  rootLayout->setMargin(0);
271  this->setLayout(rootLayout);
272  // search and clear widgets
273  QHBoxLayout* searchAndClearLayout=new QHBoxLayout();
274  exprFilter=new QLineEdit();
275  connect(exprFilter,SIGNAL(textChanged(const QString&)),SLOT(filterChanged(const QString&)));
276  searchAndClearLayout->addWidget(exprFilter,2);
277  QPushButton* clearFilterButton=new QPushButton("X");
278  clearFilterButton->setFixedWidth(24);
279  searchAndClearLayout->addWidget(clearFilterButton,1);
280  rootLayout->addLayout(searchAndClearLayout);
281  connect(clearFilterButton,SIGNAL(clicked()),SLOT(clearFilter()));
282  // model of tree
283  treeModel=new ExprTreeModel();
285  proxyModel->setSourceModel(treeModel);
286  // tree widget
287  treeNew=new QTreeView;
288  treeNew->setModel(proxyModel);
289  treeNew->hideColumn(1);
290  treeNew->setHeaderHidden(true);
291  rootLayout->addWidget(treeNew);
292  // selection mode and signal
293  treeNew->setSelectionMode(QAbstractItemView::SingleSelection);
294  connect(treeNew->selectionModel(),SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)),SLOT(handleSelection(const QModelIndex&,const QModelIndex&)));
295 }
296 
297 void ExprBrowser::addPath(const std::string& name,const std::string& path)
298 {
299  labels.append(QString::fromStdString(name));
300  paths.append(QString::fromStdString(path));
301  treeModel->addPath(name.c_str(),path.c_str());
302 }
303 
304 void ExprBrowser::setSearchPath(const QString& context, const QString& path)
305 {
306  _context = context.toStdString();
307  _searchPath = path.toStdString();
308 }
309 
311 {
312  QModelIndex sel=treeNew->currentIndex();
313  if(sel.isValid()){
314  QModelIndex realCurrent=proxyModel->mapToSource(sel);
315  ExprTreeItem* item=(ExprTreeItem*)realCurrent.internalPointer();
316  return item->path.toStdString();
317  }
318  return std::string("");
319 }
320 
321 void ExprBrowser::selectPath(const char * path)
322 {
323  QModelIndex index=treeModel->find(path);
324  treeNew->setCurrentIndex(proxyModel->mapFromSource(index));
325 }
326 
328 {
329  treeModel->update();
330  proxyModel->update();
331 }
332 
333 void ExprBrowser::handleSelection(const QModelIndex& current,const QModelIndex& previous)
334 {
335  Q_UNUSED(previous)
336  if(current.isValid()){
337  QModelIndex realCurrent=proxyModel->mapToSource(current);
338  ExprTreeItem* item=(ExprTreeItem*)realCurrent.internalPointer();
339  QString path=item->path;
340  if(path.endsWith(".se")){
341  std::ifstream file(path.toStdString().c_str());
342  std::string fileContents((std::istreambuf_iterator<char>(file)),std::istreambuf_iterator<char>());
343  editor->setExpr(fileContents,_applyOnSelect);
344  }
345  }
346 }
347 
349 {
350  labels.clear();
351  paths.clear();
352  clearSelection();
353 
354  treeModel->clear();
355 }
356 
357 
359 {
360  treeNew->clearSelection();
361 }
362 
364 {
365  exprFilter->clear();
366 }
367 
368 void ExprBrowser::filterChanged(const QString& str)
369 {
370  proxyModel->setFilterRegExp(QRegExp(str));
371  proxyModel->setFilterKeyColumn(0);
372  if(str!=""){
373  treeNew->expandAll();
374  }else{
375  treeNew->collapseAll();
376  }
377 }
378 
380 {
381  QString path = QFileDialog::getSaveFileName(
382  this, "Save Expression", QString::fromStdString(_userExprDir),
383  "*.se");
384 
385  if (path.length() > 0) {
386  std::ofstream file(path.toStdString().c_str());
387  if (!file) {
388  QString msg = QString("Could not open file %1 for writing").arg(path);
389  QMessageBox::warning(this, "Error",
390  QString("<font face=fixed>%1</font>")
391  .arg(msg));
392  return;
393  }
394  file << editor->getExpr();
395  file.close();
396 
397  update();
398  selectPath(path.toStdString().c_str());
399  }
400 }
401 
403 {
404  QString path = QFileDialog::getSaveFileName(
405  this, "Save Expression", QString::fromStdString(_localExprDir),
406  "*.se");
407 
408  if (path.length() > 0) {
409  std::ofstream file(path.toStdString().c_str());
410  if (!file) {
411  QString msg = QString("Could not open file %1 for writing").arg(path);
412  QMessageBox::warning(this, "Error",
413  QString("<font face=fixed>%1</font>")
414  .arg(msg));
415  return;
416  }
417  file << editor->getExpr();
418  file.close();
419 
420  update();
421  selectPath(path.toStdString().c_str());
422  }
423 }
424 
425 
427 {
428  std::string path = getSelectedPath();
429  if (path.length() == 0) {
431  return;
432  }
433  std::ofstream file(path.c_str());
434  if (!file) {
435  QString msg = QString("Could not open file %1 for writing. Is it read-only?").arg(QString::fromStdString(path));
436  QMessageBox::warning(this, "Error",
437  QString("<font face=fixed>%1</font>")
438  .arg(msg));
439  return;
440  }
441  file << editor->getExpr();
442  file.close();
443 }
444 
446 {
447  treeNew->expandAll();
448 }
449 
451 {
452  treeNew->expandToDepth(depth);
453 }
454 
455 // Location for storing user's expression files
457 {
458  char* homepath = getenv("HOME");
459  if(homepath){
460  std::string path= std::string(homepath) + "/" + context + "/expressions/";
461  if(QDir(QString(path.c_str())).exists()){
462  _userExprDir=path;
463  addPath("My Expressions",path);
464  }
465  }
466 }
467 
468 /*
469  * NOTE: The hard-coded paint3d assumptions can be removed once
470  * it (and bonsai?) are adjusted to call setSearchPath(context, path)
471  */
472 
474 {
475  const char *env;
476  bool enableLocal = false;
477  /*bool homeFound = false; -- for xgen's config.txt UserRepo section below */
478 
479  if (_searchPath.length() > 0)
480  env = _searchPath.c_str();
481  else
482  env = getenv(P3D_CONFIG_ENVVAR); /* For backwards compatibility */
483 
484  if (!env)
485  return enableLocal;
486 
487  std::string context;
488  if (_context.length() > 0) {
489  context = _context;
490  } else {
491  context = "paint3d"; /* For backwards compatibility */
492  }
493 
494  clear();
495 
496  std::string configFile=std::string(env)+"/config.txt";
497  std::ifstream file(configFile.c_str());
498  if(file){
499 
500  std::string key;
501  while(file){
502  file>>key;
503 
504  if(key[0]=='#'){
505  char buffer[1024];
506  file.getline(buffer,1024);
507  } else {
508  if (key=="ExpressionDir"){
509  std::string label,path;
510  file>>label;
511  file>>path;
512  if(QDir(QString(path.c_str())).exists())
513  addPath(label,path);
514  } else if (key=="ExpressionSubDir"){
515  std::string path;
516  file>>path;
517  _localExprDir=path;
518  if(QDir(QString(path.c_str())).exists()){
519  addPath("Local",_localExprDir);
520  enableLocal=true;
521  }
522  /* These are for compatibility with xgen.
523  * Long-term, xgen should use the same format.
524  * Longer-term, we should use JSON or something */
525  } else if (key == "GlobalRepo") {
526  std::string path;
527  file>>path;
528  path += "/expressions/";
529  if(QDir(QString(path.c_str())).exists())
530  addPath("Global", path);
531  } else if (key == "LocalRepo") {
532  std::string path;
533  file>>path;
534  path += "/expressions/";
535  _localExprDir=path;
536  if(QDir(QString(path.c_str())).exists()){
537  addPath("Local",_localExprDir);
538  enableLocal=true;
539  }
540 
541  /*
542  * xgen's config.txt has a "UserRepo" section but we
543  * intentionally ignore it since we already add the user dir
544  * down where the HOME stuff is handled
545  */
546 
547  /*
548  } else if (key == "UserRepo") {
549  std::string path;
550  file>>path;
551  path += "/expressions/";
552 
553  size_t found = path.find("${HOME}");
554 
555  if (found != std::string::npos) {
556  char *homepath = getenv("HOME");
557  if (homepath) {
558  path.replace(found, strlen("${HOME}"), homepath);
559  } else {
560  continue;
561  }
562  }
563  if(QDir(QString(path.c_str())).exists()){
564  addPath("User", path);
565  homeFound = true;
566  }
567  */
568  } else{
569  char buffer[1024];
570  file.getline(buffer,1024);
571  }
572  }
573  }
574  }
575  addUserExpressionPath(context);
576  update();
577  return enableLocal;
578 }
579 
void setSearchPath(const QString &context, const QString &path)
void selectPath(const char *path)
void addPath(const std::string &name, const std::string &path)
QTreeView * treeNew
Definition: ExprBrowser.h:53
void addChild(ExprTreeItem *child)
Definition: ExprBrowser.cpp:95
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
void clearFilter()
std::string _searchPath
Definition: ExprBrowser.h:58
QModelIndex parent(const QModelIndex &index) const
ExprTreeItem * root
QLineEdit * exprFilter
Definition: ExprBrowser.h:54
void filterChanged(const QString &str)
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
std::vector< ExprTreeItem * > children
void expandAll()
QList< QString > labels
Definition: ExprBrowser.h:49
ExprTreeModel * treeModel
Definition: ExprBrowser.h:51
void clearSelection()
void addPath(const char *label, const char *path)
The result is computed int int< br >< divstyle="margin-left:40px;"> Picks values randomly between loRange and hiRange based on supplied index(which is automatically hashed).&nbsp
ExprTreeFilterModel * proxyModel
Definition: ExprBrowser.h:52
int getChildCount()
std::string getSelectedPath()
void saveLocalExpressionAs()
ExprTreeItem * find(QString path)
Definition: ExprBrowser.cpp:56
ExprEditor * editor
Definition: ExprBrowser.h:48
void addUserExpressionPath(const std::string &context)
void populate()
Definition: ExprBrowser.cpp:77
void saveExpressionAs()
QList< QString > paths
Definition: ExprBrowser.h:50
ExprTreeItem * getChild(const int row)
If a scalar is used in a vector context
Definition: userdoc.txt:456
std::string _localExprDir
Definition: ExprBrowser.h:56
int rowCount(const QModelIndex &parent=QModelIndex()) const
bool getExpressionDirs()
int columnCount(const QModelIndex &parent) const
void expandToDepth(int depth)
void saveExpression()
ExprBrowser(QWidget *parent, ExprEditor *editor)
QModelIndex find(QString path)
void handleSelection(const QModelIndex &current, const QModelIndex &previous)
std::string getExpr()
Definition: ExprEditor.cpp:400
ExprTreeFilterModel(QWidget *parent=0)
#define P3D_CONFIG_ENVVAR
Definition: ExprBrowser.cpp:40
ExprTreeItem * parent
bool _applyOnSelect
Definition: ExprBrowser.h:59
std::string _userExprDir
Definition: ExprBrowser.h:55
you may not use this file except in compliance with the License and the following modification to it
Definition: license.txt:10
std::string _context
Definition: ExprBrowser.h:57
void setExpr(const std::string &expression, const bool apply=false)
Definition: ExprEditor.cpp:405
ExprTreeItem(ExprTreeItem *parent, const QString &label, const QString &path)
Definition: ExprBrowser.cpp:46