-const QString WINDOW_NAME = QObject::tr("Import Voxels");
-const QString IMPORT_BUTTON_NAME = QObject::tr("Import");
-const QString IMPORT_TO_CLIPBOARD_CHECKBOX_STRING = QObject::tr("Import into clipboard");
-const QString PREVIEW_CHECKBOX_STRING = QObject::tr("Load preview");
-const QString IMPORT_FILE_TYPES = QObject::tr("Sparse Voxel Octree Files, "
- "Square PNG, "
- "Schematic Files "
- "(*.svo *.png *.schematic)");
+const QString WINDOW_NAME = QObject::tr("Import Voxels");
+const QString IMPORT_BUTTON_NAME = QObject::tr("Import");
+const QString IMPORT_INFO = QObject::tr("Import %1 as voxels");
+const QString CANCEL_BUTTON_NAME = QObject::tr("Cancel");
+const QString INFO_LABEL_TEXT = QObject::tr(""
+ "This will load the selected file into Hifi and allow you
"
+ "to place it with %1-V; you must be in select or
"
+ "add mode (S or V keys will toggle mode) to place.
");
const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
+const int SHORT_FILE_EXTENSION = 4;
+const int SECOND_INDEX_LETTER = 1;
+QIcon HiFiIconProvider::icon(QFileIconProvider::IconType type) const {
-const glm::vec3 UP_VECT = glm::vec3(0, 1, 0);
-const float ANGULAR_RATE = 0.02f;
-const float VERTICAL_ANGLE = (float)M_PI_4 / 2.0f;
-const float RETURN_RATE = 0.02f;
-const float NEAR_CLIP = 0.5f;
-const float FAR_CLIP = 10.0f;
-const float FIELD_OF_VIEW = 60.0f;
+ switchToResourcesParentIfRequired();
-class GLWidget : public QGLWidget {
-public:
- GLWidget(QWidget* parent = NULL);
- void setDraw(bool draw) {_draw = draw;}
- void setTargetCenter(glm::vec3 targetCenter) { _targetCenter = targetCenter; }
+ // types
+ // Computer, Desktop, Trashcan, Network, Drive, Folder, File
+ QString typeString;
-protected:
- virtual void initializeGL();
- virtual void resizeGL(int width, int height);
- virtual void paintGL();
+ switch (type) {
+ case QFileIconProvider::Computer:
+ typeString = "computer";
+ break;
- void mousePressEvent(QMouseEvent* event);
- void mouseMoveEvent(QMouseEvent* event);
- void mouseReleaseEvent(QMouseEvent* event);
+ case QFileIconProvider::Desktop:
+ typeString = "desktop";
+ break;
-private:
- VoxelSystem* _voxelSystem;
+ case QFileIconProvider::Trashcan:
+ case QFileIconProvider::Network:
+ case QFileIconProvider::Drive:
+ case QFileIconProvider::Folder:
+ typeString = "folder";
+ break;
- bool _draw;
-
- double _a; // horizontal angle of the camera to the center of the object
- double _h; // vertical angle of the camera to the center of the object
- glm::vec3 _targetCenter;
-
- bool _pressed;
- int _mouseX;
- int _mouseY;
-};
-
-GLWidget::GLWidget(QWidget *parent)
- : QGLWidget(parent, Application::getInstance()->getGLWidget()),
- _draw(false),
- _a(0.0f),
- _h(VERTICAL_ANGLE),
- _targetCenter(0.5f, 0.5f, 0.5f),
- _pressed(false),
- _mouseX(0),
- _mouseY(0) {
- _voxelSystem = Application::getInstance()->getSharedVoxelSystem();
-}
-
-void GLWidget::initializeGL() {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glShadeModel (GL_SMOOTH);
- glEnable(GL_DEPTH_TEST);
-}
-
-void GLWidget::resizeGL(int width, int height) {
- glViewport(0, 0, width, height);
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(FIELD_OF_VIEW,
- (float) width / height,
- NEAR_CLIP,
- FAR_CLIP);
-}
-
-void GLWidget::paintGL() {
- glEnable(GL_LINE_SMOOTH);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- if (!_pressed) {
- _a += ANGULAR_RATE;
- _h = (1.0f - RETURN_RATE) * _h + RETURN_RATE * VERTICAL_ANGLE;
+ default:
+ typeString = "file";
+ break;
}
- gluLookAt(_targetCenter.x + (glm::length(_targetCenter) + NEAR_CLIP) * cos(_a),
- _targetCenter.y + (glm::length(_targetCenter) + NEAR_CLIP) * sin(_h),
- _targetCenter.z + (glm::length(_targetCenter) + NEAR_CLIP) * sin(_a),
- _targetCenter.x, _targetCenter.y, _targetCenter.z,
- UP_VECT.x, UP_VECT.y, UP_VECT.z);
+ return QIcon("resources/icons/" + typeString + ".svg");
+}
+QIcon HiFiIconProvider::icon(const QFileInfo &info) const {
+ switchToResourcesParentIfRequired();
+ const QString ext = info.suffix().toLower();
- if (_draw) {
- glBegin(GL_LINES);
- glColor3d(1, 1 ,1);
- glVertex3d(0, 0, 0);
- glVertex3d(1, 0, 0);
-
- glVertex3d(0, 0, 0);
- glVertex3d(0, 1, 0);
-
- glVertex3d(0, 0, 0);
- glVertex3d(0, 0, 1);
-
-
- glColor3d(0.4f, 0.4f ,0.4f);
- glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 2 * _targetCenter.z);
- glVertex3d(0 , 2 * _targetCenter.y, 2 * _targetCenter.z);
-
- glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 2 * _targetCenter.z);
- glVertex3d(2 * _targetCenter.x, 0 , 2 * _targetCenter.z);
-
- glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 2 * _targetCenter.z);
- glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 0 );
- glEnd();
-
- glScalef(1.0f / TREE_SCALE, 1.0f / TREE_SCALE, 1.0f / TREE_SCALE);
- _voxelSystem->render(false);
+ if (info.isDir()) {
+ if (info.absoluteFilePath() == QDir::homePath()) {
+ return QIcon("resources/icons/home.svg");
+ } else if (info.absoluteFilePath() == DESKTOP_LOCATION) {
+ return QIcon("resources/icons/desktop.svg");
+ } else if (info.absoluteFilePath() == QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) {
+ return QIcon("resources/icons/documents.svg");
+ }
+ return QIcon("resources/icons/folder.svg");
}
+
+ QFileInfo iconFile("resources/icons/" + iconsMap[ext]);
+ if (iconFile.exists() && iconFile.isFile()) {
+ return QIcon(iconFile.filePath());
+ }
+
+ return QIcon("resources/icons/file.svg");
}
-void GLWidget::mousePressEvent(QMouseEvent* event) {
- _pressed = true;
- _mouseX = event->globalX();
- _mouseY = event->globalY();
+QString HiFiIconProvider::type(const QFileInfo &info) const {
+ if (info.isFile()) {
+ if (info.suffix().size() > SHORT_FILE_EXTENSION) {
+ // Capitalize extension
+ return info.suffix().left(SECOND_INDEX_LETTER).toUpper() + info.suffix().mid(SECOND_INDEX_LETTER);
+ }
+ return info.suffix().toUpper();
+ }
+
+ return QFileIconProvider::type(info);
}
-void GLWidget::mouseMoveEvent(QMouseEvent* event) {
- _a += (M_PI * (event->globalX() - _mouseX)) / height();
- _h += (M_PI * (event->globalY() - _mouseY)) / height();
- _h = glm::clamp(_h, -M_PI_4, M_PI_4);
+ImportDialog::ImportDialog(QWidget* parent) :
+ QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, NULL),
+ _importButton(IMPORT_BUTTON_NAME, this),
+ _cancelButton(CANCEL_BUTTON_NAME, this),
+ fileAccepted(false) {
- _mouseX = event->globalX();
- _mouseY = event->globalY();
-}
-
-void GLWidget::mouseReleaseEvent(QMouseEvent* event) {
- _pressed = false;
-}
-
-ImportDialog::ImportDialog(QWidget *parent)
- : QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, IMPORT_FILE_TYPES),
- _importButton (IMPORT_BUTTON_NAME, this),
- _clipboardImportBox(IMPORT_TO_CLIPBOARD_CHECKBOX_STRING, this),
- _previewBox (PREVIEW_CHECKBOX_STRING, this),
- _previewBar (this),
- _glPreview (new GLWidget(this)) {
setOption(QFileDialog::DontUseNativeDialog, true);
setFileMode(QFileDialog::ExistingFile);
setViewMode(QFileDialog::Detail);
- QGridLayout* gridLayout = (QGridLayout*) layout();
- gridLayout->addWidget(&_importButton , 2, 2);
- gridLayout->addWidget(&_clipboardImportBox, 2, 3);
- gridLayout->addWidget(&_previewBox , 3, 3);
- gridLayout->addWidget(&_previewBar , 0, 3);
- gridLayout->addWidget(_glPreview , 1, 3);
- gridLayout->setColumnStretch(3, 1);
+#ifdef Q_OS_MAC
+ QString cmdString = ("Command");
+#else
+ QString cmdString = ("Control");
+#endif
+ QLabel* infoLabel = new QLabel(QString(INFO_LABEL_TEXT).arg(cmdString));
+ infoLabel->setObjectName("infoLabel");
- _previewBar.setVisible(false);
- _previewBar.setRange(0, 100);
- _previewBar.setValue(0);
+ QGridLayout* gridLayout = (QGridLayout*) layout();
+ gridLayout->addWidget(infoLabel, 2, 0, 2, 1);
+ gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1);
+ gridLayout->addWidget(&_importButton, 2, 2, 2, 1);
+
+ setImportTypes();
+ setLayout();
connect(&_importButton, SIGNAL(pressed()), SLOT(import()));
- connect(&_previewBox, SIGNAL(toggled(bool)), SIGNAL(previewToggled(bool)));
- connect(&_previewBox, SIGNAL(toggled(bool)), SLOT(preview(bool)));
-
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
- connect(&_glTimer, SIGNAL(timeout()), SLOT(timer()));
+
+ connect(&_cancelButton, SIGNAL(pressed()), SLOT(close()));
+ connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
}
ImportDialog::~ImportDialog() {
- delete _glPreview;
-}
-
-void ImportDialog::init() {
- VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
- connect(voxelSystem, SIGNAL(importSize(float,float,float)), SLOT(setGLCamera(float,float,float)));
- connect(voxelSystem, SIGNAL(importProgress(int)), &_previewBar, SLOT(setValue(int)));
+ deleteLater();
}
void ImportDialog::import() {
- _importButton.setDisabled(true);
- _clipboardImportBox.setDisabled(true);
- _previewBox.setDisabled(true);
-
- _previewBar.setValue(0);
- _previewBar.setVisible(true);
-
+ fileAccepted = true;
emit accepted();
}
void ImportDialog::accept() {
- QFileDialog::accept();
+ // do nothing if import is not enable
+ if (!_importButton.isEnabled()) {
+ return;
+ }
+
+ if (!fileAccepted) {
+ fileAccepted = true;
+ emit accepted();
+ } else {
+ QFileDialog::accept();
+ }
}
void ImportDialog::reject() {
@@ -219,44 +155,141 @@ void ImportDialog::reject() {
}
int ImportDialog::exec() {
+ // deselect selected file
+ selectFile(" ");
return QFileDialog::exec();
}
-void ImportDialog::setGLCamera(float x, float y, float z) {
- _glPreview->setTargetCenter(glm::vec3(x, y, z) / 2.0f);
-}
-
void ImportDialog::reset() {
- _previewBox.setChecked(false);
- _previewBar.setVisible(false);
- _previewBar.setValue(0);
- _importButton.setEnabled(true);
- _clipboardImportBox.setEnabled(true);
- _previewBox.setEnabled(true);
-
- _glTimer.stop();
- _glPreview->setDraw(false);
- _glPreview->updateGL();
-}
-
-void ImportDialog::preview(bool wantPreview) {
- _previewBar.setValue(0);
- _previewBar.setVisible(wantPreview);
- _glPreview->setDraw(wantPreview);
-
- if (wantPreview) {
- _glTimer.start();
- } else {
- _glTimer.stop();
- _glPreview->updateGL();
- }
+ _importButton.setEnabled(false);
}
void ImportDialog::saveCurrentFile(QString filename) {
- _currentFile = filename;
+ if (!filename.isEmpty() && QFileInfo(filename).isFile()) {
+ _currentFile = filename;
+ _importButton.setEnabled(true);
+ } else {
+ _currentFile.clear();
+ _importButton.setEnabled(false);
+ }
}
-void ImportDialog::timer() {
- _glPreview->updateGL();
- _glTimer.start(16);
+void ImportDialog::setLayout() {
+
+ // set ObjectName used in qss for styling
+ _importButton.setObjectName("importButton");
+ _cancelButton.setObjectName("cancelButton");
+
+ // set fixed size
+ _importButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ _cancelButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ // hide unused embedded widgets in QFileDialog
+ QWidget* widget = findChild("lookInCombo");
+ widget->hide();
+
+ widget = findChild("backButton");
+ widget->hide();
+
+ widget = findChild("forwardButton");
+ widget->hide();
+
+ widget = findChild("toParentButton");
+ widget->hide();
+
+ widget = findChild("newFolderButton");
+ widget->hide();
+
+ widget = findChild("listModeButton");
+ widget->hide();
+
+ widget = findChild("detailModeButton");
+ widget->hide();
+
+ widget = findChild("fileNameEdit");
+ widget->hide();
+
+ widget = findChild("fileTypeCombo");
+ widget->hide();
+
+ widget = findChild("fileTypeLabel");
+ widget->hide();
+
+ widget = findChild("fileNameLabel");
+ widget->hide();
+
+ widget = findChild("buttonBox");
+ widget->hide();
+
+ QSplitter* splitter = findChild("splitter");
+ splitter->setHandleWidth(0);
+
+ // remove blue outline on Mac
+ widget = findChild("sidebar");
+ widget->setAttribute(Qt::WA_MacShowFocusRect, false);
+
+ widget = findChild("treeView");
+ widget->setAttribute(Qt::WA_MacShowFocusRect, false);
+
+ // remove reference to treeView
+ widget = NULL;
+ widget->deleteLater();
+
+ switchToResourcesParentIfRequired();
+ QFile styleSheet("resources/styles/import_dialog.qss");
+ if (styleSheet.open(QIODevice::ReadOnly)) {
+ setStyleSheet(styleSheet.readAll());
+ }
+
+}
+
+void ImportDialog::setImportTypes() {
+
+ switchToResourcesParentIfRequired();
+ QFile config("resources/config/config.json");
+ config.open(QFile::ReadOnly | QFile::Text);
+ QJsonDocument document = QJsonDocument::fromJson(config.readAll());
+ if (!document.isNull() && !document.isEmpty()) {
+
+ QString importFormatsInfo;
+ QString importFormatsFilterList;
+ QHash iconsMap;
+
+ QJsonObject configObject = document.object();
+ if (!configObject.isEmpty()) {
+ QJsonArray fileFormats = configObject["importFormats"].toArray();
+ int formatsCounter = 0;
+ foreach (const QJsonValue& fileFormat, fileFormats) {
+ QJsonObject fileFormatObject = fileFormat.toObject();
+
+ QString ext(fileFormatObject["extension"].toString());
+ QString icon(fileFormatObject.value("icon").toString());
+
+ if (formatsCounter > 0) {
+ importFormatsInfo.append(",");
+ }
+
+ // set ' or' on last import type text
+ if (formatsCounter == fileFormats.count() - 1) {
+ importFormatsInfo.append(" or");
+ }
+
+ importFormatsFilterList.append(QString("*.%1 ").arg(ext));
+ importFormatsInfo.append(" .").append(ext);
+ iconsMap[ext] = icon;
+ formatsCounter++;
+ }
+ }
+
+ // set custom file icons
+ setIconProvider(new HiFiIconProvider(iconsMap));
+ setNameFilter(importFormatsFilterList);
+
+#ifdef Q_OS_MAC
+ QString cmdString = ("Command");
+#else
+ QString cmdString = ("Control");
+#endif
+ setLabelText(QFileDialog::LookIn, QString(IMPORT_INFO).arg(importFormatsInfo));
+ }
}
diff --git a/interface/src/ImportDialog.h b/interface/src/ImportDialog.h
index 35102dfe4c..5cfc49e51e 100644
--- a/interface/src/ImportDialog.h
+++ b/interface/src/ImportDialog.h
@@ -9,54 +9,54 @@
#ifndef __hifi__ImportDialog__
#define __hifi__ImportDialog__
-#include
-
#include
#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
-class GLWidget;
+#include
+
+class HiFiIconProvider : public QFileIconProvider {
+public:
+ HiFiIconProvider(const QHash map) { iconsMap = map; };
+ virtual QIcon icon(IconType type) const;
+ virtual QIcon icon(const QFileInfo &info) const;
+ virtual QString type(const QFileInfo &info) const;
+ QHash iconsMap;
+};
class ImportDialog : public QFileDialog {
Q_OBJECT
+
public:
ImportDialog(QWidget* parent = NULL);
~ImportDialog();
- void init();
void reset();
- bool getWantPreview() const { return _previewBox.isChecked(); }
QString getCurrentFile() const { return _currentFile; }
- bool getImportIntoClipboard() const { return _clipboardImportBox.isChecked(); }
signals:
- void previewToggled(bool);
void accepted();
public slots:
int exec();
- void setGLCamera(float x, float y, float z);
void import();
void accept();
void reject();
private slots:
- void preview(bool preview);
void saveCurrentFile(QString);
- void timer();
private:
- QString _currentFile;
- QPushButton _importButton;
- QCheckBox _clipboardImportBox;
- QCheckBox _previewBox;
- QProgressBar _previewBar;
- GLWidget* _glPreview;
- QTimer _glTimer;
+ QString _currentFile;
+ QPushButton _importButton;
+ QPushButton _cancelButton;
+
+ void setLayout();
+ void setImportTypes();
+ bool fileAccepted;
};
#endif /* defined(__hifi__ImportDialog__) */
diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp
index ffee4c7879..a2e518b2d7 100644
--- a/interface/src/Util.cpp
+++ b/interface/src/Util.cpp
@@ -373,9 +373,18 @@ const glm::vec3 randVector() {
}
static TextRenderer* textRenderer(int mono) {
- static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
- static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
- return mono ? monoRenderer : proportionalRenderer;
+ static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
+ static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
+ static TextRenderer* inconsolataRenderer = new TextRenderer(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false);
+ switch (mono) {
+ case 1:
+ return monoRenderer;
+ case 2:
+ return inconsolataRenderer;
+ case 0:
+ default:
+ return proportionalRenderer;
+ }
}
int widthText(float scale, int mono, char const* string) {
diff --git a/interface/src/Util.h b/interface/src/Util.h
index 19509f8254..2a812120f0 100644
--- a/interface/src/Util.h
+++ b/interface/src/Util.h
@@ -25,6 +25,9 @@
// the standard mono font family
#define MONO_FONT_FAMILY "Courier"
+// the Inconsolata font family
+#define INCONSOLATA_FONT_FAMILY "Inconsolata"
+
void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up);
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos);
diff --git a/interface/src/VoxelImporter.cpp b/interface/src/VoxelImporter.cpp
index 570eb5043c..0011b0abb7 100644
--- a/interface/src/VoxelImporter.cpp
+++ b/interface/src/VoxelImporter.cpp
@@ -21,6 +21,9 @@ private:
QString _filename;
};
+const QString SETTINGS_GROUP_NAME = "VoxelImport";
+const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
+
VoxelImporter::VoxelImporter(QWidget* parent)
: QObject(parent),
_voxelTree(true),
@@ -28,13 +31,20 @@ VoxelImporter::VoxelImporter(QWidget* parent)
_currentTask(NULL),
_nextTask(NULL) {
- connect(&_importDialog, SIGNAL(previewToggled(bool)), SLOT(preImport()));
connect(&_importDialog, SIGNAL(currentChanged(QString)), SLOT(preImport()));
connect(&_importDialog, SIGNAL(accepted()), SLOT(import()));
}
-void VoxelImporter::init() {
- _importDialog.init();
+void VoxelImporter::saveSettings(QSettings* settings) {
+ settings->beginGroup(SETTINGS_GROUP_NAME);
+ settings->setValue(IMPORT_DIALOG_SETTINGS_KEY, _importDialog.saveState());
+ settings->endGroup();
+}
+
+void VoxelImporter::init(QSettings* settings) {
+ settings->beginGroup(SETTINGS_GROUP_NAME);
+ _importDialog.restoreState(settings->value(IMPORT_DIALOG_SETTINGS_KEY).toByteArray());
+ settings->endGroup();
}
VoxelImporter::~VoxelImporter() {
@@ -74,15 +84,13 @@ int VoxelImporter::exec() {
reset();
} else {
_importDialog.reset();
-
- if (_importDialog.getImportIntoClipboard()) {
- VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
-
- voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->getRoot(),
- Application::getInstance()->getClipboard(),
- true);
- voxelSystem->changeTree(Application::getInstance()->getClipboard());
- }
+
+ VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
+
+ voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->getRoot(),
+ Application::getInstance()->getClipboard(),
+ true);
+ voxelSystem->changeTree(Application::getInstance()->getClipboard());
}
return ret;
@@ -94,24 +102,22 @@ int VoxelImporter::preImport() {
if (!QFileInfo(filename).isFile()) {
return 0;
}
-
- if (_importDialog.getWantPreview()) {
- _filename = filename;
-
- if (_nextTask) {
- delete _nextTask;
- }
-
- _nextTask = new ImportTask(_filename);
- connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
-
- if (_currentTask != NULL) {
- _voxelTree.cancelImport();
- } else {
- launchTask();
- }
+
+ _filename = filename;
+
+ if (_nextTask) {
+ delete _nextTask;
}
-
+
+ _nextTask = new ImportTask(_filename);
+ connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
+
+ if (_currentTask != NULL) {
+ _voxelTree.cancelImport();
+ } else {
+ launchTask();
+ }
+
return 1;
}
diff --git a/interface/src/VoxelImporter.h b/interface/src/VoxelImporter.h
index a84955f2c9..43a3835e68 100644
--- a/interface/src/VoxelImporter.h
+++ b/interface/src/VoxelImporter.h
@@ -23,8 +23,9 @@ public:
VoxelImporter(QWidget* parent = NULL);
~VoxelImporter();
- void init();
+ void init(QSettings* settings);
void reset();
+ void saveSettings(QSettings* settings);
VoxelTree* getVoxelTree() { return &_voxelTree; }
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 8228ec9e64..df24b4c927 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -63,7 +63,7 @@ void AvatarManager::updateAvatars(float deltaTime) {
// simulate avatars
AvatarHash::iterator avatar = _avatarHash.begin();
- if (avatar != _avatarHash.end()) {
+ while (avatar != _avatarHash.end()) {
if (avatar->data()->getOwningAvatarMixer()) {
// this avatar's mixer is still around, go ahead and simulate it
avatar->data()->simulate(deltaTime, NULL);
@@ -72,6 +72,7 @@ void AvatarManager::updateAvatars(float deltaTime) {
avatar->data()->setMouseRay(applicationInstance->getMouseRayOrigin(),
applicationInstance->getMouseRayDirection());
+ avatar++;
} else {
// the mixer that owned this avatar is gone, give it to the vector of fades and kill it
avatar = removeAvatarAtHashIterator(avatar);
@@ -225,4 +226,4 @@ void AvatarManager::clearHash() {
while (removeAvatar != _avatarHash.end()) {
removeAvatar = removeAvatarAtHashIterator(removeAvatar);
}
-}
\ No newline at end of file
+}
diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp
index bdb3916694..73248b413f 100644
--- a/interface/src/renderer/GeometryCache.cpp
+++ b/interface/src/renderer/GeometryCache.cpp
@@ -12,6 +12,7 @@
#include
#include
+#include
#include "Application.h"
#include "GeometryCache.h"
diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp
index 0ba8f84ad2..96fe84b305 100644
--- a/interface/src/renderer/TextureCache.cpp
+++ b/interface/src/renderer/TextureCache.cpp
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp
new file mode 100644
index 0000000000..f0fef33cee
--- /dev/null
+++ b/interface/src/ui/Snapshot.cpp
@@ -0,0 +1,46 @@
+//
+// Snapshot.cpp
+// hifi
+//
+// Created by Stojce Slavkovski on 1/26/14.
+//
+//
+
+#include "Snapshot.h"
+
+#include
+
+#include
+#include
+#include
+
+// filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg
+// %1 <= username, %2 <= date and time, %3 <= current location
+const QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2@%3.jpg";
+
+const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss";
+const QString SNAPSHOTS_DIRECTORY = "Snapshots";
+
+void Snapshot::saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location) {
+ QImage shot = widget->grabFrameBuffer();
+
+ // add metadata
+ shot.setText("location-x", QString::number(location.x));
+ shot.setText("location-y", QString::number(location.y));
+ shot.setText("location-z", QString::number(location.z));
+
+ QString formattedLocation = QString("%1_%2_%3").arg(location.x).arg(location.y).arg(location.z);
+ // replace decimal . with '-'
+ formattedLocation.replace('.', '-');
+
+ // normalize username, replace all non alphanumeric with '-'
+ username.replace(QRegExp("[^A-Za-z0-9_]"), "-");
+
+ QDateTime now = QDateTime::currentDateTime();
+
+ QString fileName = FileUtils::standardPath(SNAPSHOTS_DIRECTORY);
+ fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation)));
+ shot.save(fileName, 0, 100);
+}
+
+
diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h
new file mode 100644
index 0000000000..39548cdff8
--- /dev/null
+++ b/interface/src/ui/Snapshot.h
@@ -0,0 +1,27 @@
+//
+// Snapshot.h
+// hifi
+//
+// Created by Stojce Slavkovski on 1/26/14.
+// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
+//
+
+#ifndef __hifi__Snapshot__
+#define __hifi__Snapshot__
+
+#import
+#import
+#import
+
+#include
+
+class Snapshot {
+
+public:
+ static void saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location);
+
+private:
+ QString _username;
+};
+
+#endif /* defined(__hifi__Snapshot__) */
diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp
index aef62ccf07..340a560439 100644
--- a/libraries/particles/src/Particle.cpp
+++ b/libraries/particles/src/Particle.cpp
@@ -289,10 +289,7 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef
return bytesRead;
}
-
-Particle Particle::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree) {
-
- //qDebug() << "Particle::fromEditPacket() length=" << length;
+Particle Particle::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid) {
Particle newParticle; // id and _lastUpdated will get set here...
const unsigned char* dataAt = data;
@@ -302,9 +299,6 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
int octets = numberOfThreeBitSectionsInCode(data);
int lengthOfOctcode = bytesRequiredForCodeLength(octets);
- //qDebug() << "Particle::fromEditPacket() lengthOfOctcode=" << lengthOfOctcode;
- //printOctalCode(data);
-
// we don't actually do anything with this octcode...
dataAt += lengthOfOctcode;
processedBytes += lengthOfOctcode;
@@ -321,8 +315,6 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
// special case for handling "new" particles
if (isNewParticle) {
- //qDebug() << "editID == NEW_PARTICLE";
-
// If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that
// we want to send back to the creator as an map to the actual id
uint32_t creatorTokenID;
@@ -330,11 +322,8 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
dataAt += sizeof(creatorTokenID);
processedBytes += sizeof(creatorTokenID);
- //qDebug() << "creatorTokenID:" << creatorTokenID;
-
newParticle.setCreatorTokenID(creatorTokenID);
newParticle._newlyCreated = true;
-
newParticle.setAge(0); // this guy is new!
} else {
@@ -344,15 +333,19 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
// copy existing properties before over-writing with new properties
if (existingParticle) {
newParticle = *existingParticle;
- //qDebug() << "newParticle = *existingParticle... calling debugDump()...";
- //existingParticle->debugDump();
+ } else {
+ // the user attempted to edit a particle that doesn't exist
+ qDebug() << "user attempted to edit a particle that doesn't exist...";
+ valid = false;
+ return newParticle;
}
-
newParticle._id = editID;
newParticle._newlyCreated = false;
-
-
}
+
+ // if we got this far, then our result will be valid
+ valid = true;
+
// lastEdited
memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited));
@@ -838,6 +831,8 @@ ParticleProperties::ParticleProperties() :
_inHand(false),
_shouldDie(false),
+ _id(UNKNOWN_PARTICLE_ID),
+ _idSet(false),
_lastEdited(usecTimestampNow()),
_positionChanged(false),
_colorChanged(false),
@@ -923,6 +918,11 @@ QScriptValue ParticleProperties::copyToScriptValue(QScriptEngine* engine) const
properties.setProperty("script", _script);
properties.setProperty("inHand", _inHand);
properties.setProperty("shouldDie", _shouldDie);
+
+ if (_idSet) {
+ properties.setProperty("id", _id);
+ properties.setProperty("isKnownID", (_id == UNKNOWN_PARTICLE_ID));
+ }
return properties;
}
@@ -1116,6 +1116,9 @@ void ParticleProperties::copyFromParticle(const Particle& particle) {
_inHand = particle.getInHand();
_shouldDie = particle.getShouldDie();
+ _id = particle.getID();
+ _idSet = true;
+
_positionChanged = false;
_colorChanged = false;
_radiusChanged = false;
diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h
index fab528595d..6046ca39de 100644
--- a/libraries/particles/src/Particle.h
+++ b/libraries/particles/src/Particle.h
@@ -94,6 +94,9 @@ public:
void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; _shouldDieChanged = true; }
void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; }
void setScript(QString updateScript) { _script = updateScript; _scriptChanged = true; }
+
+ /// used by ParticleScriptingInterface to return ParticleProperties for unknown particles
+ void setIsUnknownID() { _id = UNKNOWN_PARTICLE_ID; _idSet = true; }
private:
glm::vec3 _position;
@@ -107,6 +110,8 @@ private:
bool _inHand;
bool _shouldDie;
+ uint32_t _id;
+ bool _idSet;
uint64_t _lastEdited;
bool _positionChanged;
bool _colorChanged;
@@ -145,6 +150,7 @@ public:
};
Q_DECLARE_METATYPE(ParticleID);
+Q_DECLARE_METATYPE(QVector);
QScriptValue ParticleIDtoScriptValue(QScriptEngine* engine, const ParticleID& properties);
void ParticleIDfromScriptValue(const QScriptValue &object, ParticleID& properties);
@@ -161,8 +167,9 @@ public:
glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, float lifetime = DEFAULT_LIFETIME,
bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE);
+
/// creates an NEW particle from an PacketType_PARTICLE_ADD_OR_EDIT edit data buffer
- static Particle fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree);
+ static Particle fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid);
virtual ~Particle();
virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity,
diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp
index 0c90a03573..a23c393f44 100644
--- a/libraries/particles/src/ParticleTree.cpp
+++ b/libraries/particles/src/ParticleTree.cpp
@@ -161,6 +161,42 @@ const Particle* ParticleTree::findClosestParticle(glm::vec3 position, float targ
return args.closestParticle;
}
+class FindAllNearPointArgs {
+public:
+ glm::vec3 position;
+ float targetRadius;
+ QVector particles;
+};
+
+
+bool ParticleTree::findInSphereOperation(OctreeElement* element, void* extraData) {
+ FindAllNearPointArgs* args = static_cast(extraData);
+ ParticleTreeElement* particleTreeElement = static_cast(element);
+
+ glm::vec3 penetration;
+ bool sphereIntersection = particleTreeElement->getAABox().findSpherePenetration(args->position,
+ args->targetRadius, penetration);
+
+ // If this particleTreeElement contains the point, then search it...
+ if (sphereIntersection) {
+ QVector moreParticles = particleTreeElement->getParticles(args->position, args->targetRadius);
+ args->particles << moreParticles;
+ return true; // keep searching in case children have closer particles
+ }
+
+ // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
+ return false;
+}
+
+QVector ParticleTree::findParticles(const glm::vec3& center, float radius) {
+ QVector result;
+ FindAllNearPointArgs args = { center, radius };
+ lockForRead();
+ recurseTreeWithOperation(findInSphereOperation, &args);
+ unlock();
+ return args.particles;
+}
+
class FindByIDArgs {
public:
uint32_t id;
@@ -215,23 +251,27 @@ int ParticleTree::processEditPacketData(PacketType packetType, const unsigned ch
// we handle these types of "edit" packets
switch (packetType) {
case PacketTypeParticleAddOrEdit: {
- //qDebug() << " got PacketType_PARTICLE_ADD_OR_EDIT... ";
- Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this);
- storeParticle(newParticle, senderNode);
- if (newParticle.isNewlyCreated()) {
- notifyNewlyCreatedParticle(newParticle, senderNode);
+ bool isValid;
+ Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this, isValid);
+ if (isValid) {
+ storeParticle(newParticle, senderNode);
+ if (newParticle.isNewlyCreated()) {
+ notifyNewlyCreatedParticle(newParticle, senderNode);
+ }
}
- //qDebug() << " DONE... PacketType_PARTICLE_ADD_OR_EDIT... ";
- return processedBytes;
- }
+ } break;
- // TODO: wire in support here for server to get PacketType_PARTICLE_ERASE messages
- // instead of using PacketType_PARTICLE_ADD_OR_EDIT messages to delete particles
+ // TODO: wire in support here for server to get PACKET_TYPE_PARTICLE_ERASE messages
+ // instead of using PACKET_TYPE_PARTICLE_ADD_OR_EDIT messages to delete particles
case PacketTypeParticleErase:
- return 0;
+ processedBytes = 0;
+ break;
default:
- return 0;
+ processedBytes = 0;
+ break;
}
+
+ return processedBytes;
}
void ParticleTree::notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode) {
diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h
index c89bd01b3b..af15666ff7 100644
--- a/libraries/particles/src/ParticleTree.h
+++ b/libraries/particles/src/ParticleTree.h
@@ -42,6 +42,7 @@ public:
void storeParticle(const Particle& particle, Node* senderNode = NULL);
const Particle* findClosestParticle(glm::vec3 position, float targetRadius);
const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false);
+ QVector findParticles(const glm::vec3& center, float radius);
void addNewlyCreatedHook(NewlyCreatedParticleHook* hook);
void removeNewlyCreatedHook(NewlyCreatedParticleHook* hook);
@@ -58,6 +59,7 @@ private:
static bool updateOperation(OctreeElement* element, void* extraData);
static bool findAndUpdateOperation(OctreeElement* element, void* extraData);
static bool findNearPointOperation(OctreeElement* element, void* extraData);
+ static bool findInSphereOperation(OctreeElement* element, void* extraData);
static bool pruneOperation(OctreeElement* element, void* extraData);
static bool findByIDOperation(OctreeElement* element, void* extraData);
static bool findAndDeleteOperation(OctreeElement* element, void* extraData);
diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp
index f767b56a49..1454eadcc9 100644
--- a/libraries/particles/src/ParticleTreeElement.cpp
+++ b/libraries/particles/src/ParticleTreeElement.cpp
@@ -185,6 +185,23 @@ const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) cons
return closestParticle;
}
+QVector ParticleTreeElement::getParticles(glm::vec3 searchPosition, float searchRadius) const {
+ QVector results;
+ uint16_t numberOfParticles = _particles->size();
+ for (uint16_t i = 0; i < numberOfParticles; i++) {
+ const Particle* particle = &(*_particles)[i];
+ glm::vec3 particlePosition = particle->getPosition();
+ float particleRadius = particle->getRadius();
+ glm::vec3 penetration;
+
+ // check to see that the particle (penetrator) penetrates the search area
+ if (findSphereSpherePenetration(particlePosition, particleRadius, searchPosition, searchRadius, penetration)) {
+ results << particle;
+ }
+ }
+ return results;
+}
+
const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
// NOTE: this lookup is O(N) but maybe we don't care? (guaranteed that num particles per elemen is small?)
const Particle* foundParticle = NULL;
diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h
index 6d76594b53..2705ad4292 100644
--- a/libraries/particles/src/ParticleTreeElement.h
+++ b/libraries/particles/src/ParticleTreeElement.h
@@ -89,6 +89,7 @@ public:
bool containsParticle(const Particle& particle) const;
bool updateParticle(const Particle& particle);
const Particle* getClosestParticle(glm::vec3 position) const;
+ QVector getParticles(glm::vec3 position, float radius) const;
const Particle* getParticleWithID(uint32_t id) const;
bool removeParticleWithID(uint32_t id);
diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp
index 987fdf789c..b52ead87e1 100644
--- a/libraries/particles/src/ParticlesScriptingInterface.cpp
+++ b/libraries/particles/src/ParticlesScriptingInterface.cpp
@@ -34,15 +34,44 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr
return id;
}
-void ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) {
+ParticleID ParticlesScriptingInterface::identifyParticle(ParticleID particleID) {
uint32_t actualID = particleID.id;
if (!particleID.isKnownID) {
actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
- // hmmm... we kind of want to bail if someone attempts to edit an unknown
if (actualID == UNKNOWN_PARTICLE_ID) {
- //qDebug() << "ParticlesScriptingInterface::editParticle()... BAILING!!! particleID.creatorTokenID="
- // << particleID.creatorTokenID;
- return; // bailing early
+ return particleID; // bailing early
+ }
+
+ // found it!
+ particleID.id = actualID;
+ particleID.isKnownID = true;
+ }
+ return particleID;
+}
+
+ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID particleID) {
+ ParticleProperties results;
+ ParticleID identity = identifyParticle(particleID);
+ if (!identity.isKnownID) {
+ results.setIsUnknownID();
+ return results;
+ }
+ if (_particleTree) {
+ const Particle* particle = _particleTree->findParticleByID(identity.id);
+ results.copyFromParticle(*particle);
+ }
+
+ return results;
+}
+
+
+
+ParticleID ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) {
+ uint32_t actualID = particleID.id;
+ if (!particleID.isKnownID) {
+ actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
+ if (actualID == UNKNOWN_PARTICLE_ID) {
+ return particleID; // bailing early
}
}
@@ -70,7 +99,9 @@ void ParticlesScriptingInterface::editParticle(ParticleID particleID, const Part
qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getInHand()=" << properties.getInHand();
}
}
+
queueParticleMessage(PacketTypeParticleAddOrEdit, particleID, properties);
+ return particleID;
}
@@ -116,3 +147,17 @@ ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& cen
return result;
}
+
+QVector ParticlesScriptingInterface::findParticles(const glm::vec3& center, float radius) const {
+ QVector result;
+ if (_particleTree) {
+ QVector particles = _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE);
+
+ foreach (const Particle* particle, particles) {
+ ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true);
+ result << thisParticleID;
+ }
+ }
+ return result;
+}
+
diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h
index 624ce8a7b1..a85997ee05 100644
--- a/libraries/particles/src/ParticlesScriptingInterface.h
+++ b/libraries/particles/src/ParticlesScriptingInterface.h
@@ -28,11 +28,33 @@ public:
ParticleTree* getParticleTree(ParticleTree*) { return _particleTree; }
public slots:
+ /// adds a particle with the specific properties
ParticleID addParticle(const ParticleProperties& properties);
- void editParticle(ParticleID particleID, const ParticleProperties& properties);
+
+ /// identify a recently created particle to determine its true ID
+ ParticleID identifyParticle(ParticleID particleID);
+
+ /// gets the current particle properties for a specific particle
+ /// this function will not find return results in script engine contexts which don't have access to particles
+ ParticleProperties getParticleProperties(ParticleID particleID);
+
+ /// edits a particle updating only the included properties, will return the identified ParticleID in case of
+ /// successful edit, if the input particleID is for an unknown particle this function will have no effect
+ ParticleID editParticle(ParticleID particleID, const ParticleProperties& properties);
+
+ /// deletes a particle
void deleteParticle(ParticleID particleID);
+
+ /// finds the closest particle to the center point, within the radius
+ /// will return a ParticleID.isKnownID = false if no particles are in the radius
+ /// this function will not find any particles in script engine contexts which don't have access to particles
ParticleID findClosestParticle(const glm::vec3& center, float radius) const;
+ /// finds particles within the search sphere specified by the center point and radius
+ /// this function will not find any particles in script engine contexts which don't have access to particles
+ QVector findParticles(const glm::vec3& center, float radius) const;
+
+
private:
void queueParticleMessage(PacketType packetType, ParticleID particleID, const ParticleProperties& properties);
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index f511299fcd..937162e0bf 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -118,6 +118,7 @@ void ScriptEngine::init() {
registerMetaTypes(&_engine);
qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue);
qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);
+ qScriptRegisterSequenceMetaType >(&_engine);
QScriptValue agentValue = _engine.newQObject(this);
_engine.globalObject().setProperty("Agent", agentValue);
diff --git a/libraries/shared/src/FileUtils.cpp b/libraries/shared/src/FileUtils.cpp
index b58799104f..efbe1a189b 100644
--- a/libraries/shared/src/FileUtils.cpp
+++ b/libraries/shared/src/FileUtils.cpp
@@ -10,9 +10,9 @@
#include
#include
-void FileUtils::LocateFile(QString filePath) {
+void FileUtils::locateFile(QString filePath) {
- // adopted from
+ // adapted from
// http://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt
// and
// http://lynxline.com/show-in-finder-show-in-explorer/
@@ -54,3 +54,26 @@ void FileUtils::LocateFile(QString filePath) {
QDesktopServices::openUrl(QUrl::fromLocalFile(folder));
}
}
+
+QString FileUtils::standardPath(QString subfolder) {
+ // standard path
+ // Mac: ~/Library/Application Support/Interface
+ QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
+ path.append("/Interface");
+
+ if (!subfolder.startsWith("/")) {
+ subfolder.prepend("/");
+ }
+
+ if (!subfolder.endsWith("/")) {
+ subfolder.append("/");
+ }
+
+ path.append(subfolder);
+ QDir logDir(path);
+ if (!logDir.exists(path)) {
+ logDir.mkpath(path);
+ }
+
+ return path;
+}
diff --git a/libraries/shared/src/FileUtils.h b/libraries/shared/src/FileUtils.h
index a725e72ca1..dd4605218e 100644
--- a/libraries/shared/src/FileUtils.h
+++ b/libraries/shared/src/FileUtils.h
@@ -14,7 +14,8 @@
class FileUtils {
public:
- static void LocateFile(QString);
+ static void locateFile(QString fileName);
+ static QString standardPath(QString subfolder);
};