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