From 7848c63fae10e4379a856f55676f1afaff896cd6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 17 Sep 2014 12:14:38 -0700 Subject: [PATCH 1/5] handle name location shortcut explicitly to work around bug --- interface/src/Application.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 371feeb612..efa5a481f6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -288,6 +288,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // set the account manager's root URL and trigger a login request if we don't have the access token accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL); UserActivityLogger::getInstance().launch(applicationVersion()); + + // grab the location manager instance early so it lives in our thread + LocationManager::getInstance(); // once the event loop has started, check and signal for an access token QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); @@ -927,6 +930,13 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; + + case Qt::Key_N: + if (isMeta) { + Menu::getInstance()->triggerOption(MenuOption::NameLocation); + } + + break; case Qt::Key_Up: if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { From e7b9e2b0608c9979a4034e852c2bba987574ad71 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 17 Sep 2014 12:14:53 -0700 Subject: [PATCH 2/5] have LocationManager upload snapshot after location creation --- interface/src/location/LocationManager.cpp | 71 ++++++++++++++++++++-- interface/src/location/LocationManager.h | 6 +- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index 92e8616478..866e92be98 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -9,10 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include +#include "Application.h" +#include "ui/Snapshot.h" + #include "LocationManager.h" const QString POST_LOCATION_CREATE = "/api/v1/locations/"; @@ -24,13 +28,17 @@ LocationManager& LocationManager::getInstance() { const QString UNKNOWN_ERROR_MESSAGE = "Unknown error creating named location. Please try again!"; -void LocationManager::namedLocationDataReceived(const QJsonObject& data) { - if (data.isEmpty()) { - return; - } +const QString LOCATION_OBJECT_KEY = "location"; +const QString LOCATION_ID_KEY = "id"; - if (data.contains("status") && data["status"].toString() == "success") { +void LocationManager::namedLocationDataReceived(const QJsonObject& rootObject) { + + if (rootObject.contains("status") && rootObject["status"].toString() == "success") { emit creationCompleted(QString()); + + // successfuly created a location - grab the ID from the response and create a snapshot to upload + QString locationIDString = rootObject[LOCATION_OBJECT_KEY].toObject()[LOCATION_ID_KEY].toString(); + updateSnapshotForExistingLocation(locationIDString); } else { emit creationCompleted(UNKNOWN_ERROR_MESSAGE); } @@ -87,3 +95,56 @@ void LocationManager::errorDataReceived(QNetworkReply& errorReply) { creationCompleted(UNKNOWN_ERROR_MESSAGE); } } + +void LocationManager::locationImageUpdateSuccess(const QJsonObject& rootObject) { + qDebug() << "Successfuly updated a location image."; +} + +void LocationManager::updateSnapshotForExistingLocation(const QString& locationID) { + // first create a snapshot and save it + Application* application = Application::getInstance(); + + QString filename = Snapshot::saveSnapshot(application->getGLWidget(), application->getAvatar()); + + AccountManager& accountManager = AccountManager::getInstance(); + + // setup a multipart that is in the AccountManager thread - we need this so it can be cleaned up after the QNetworkReply + QHttpMultiPart* imageFileMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + imageFileMultiPart->moveToThread(accountManager.thread()); + + // load the file at that filename, parent the mile to the QHttpMultipart + QFile* locationImageFile = new QFile(filename, imageFileMultiPart); + + if (!locationImageFile->open(QIODevice::ReadOnly)) { + qDebug() << "Couldn't open snapshot file to upload as location image. No location image will be stored."; + return; + } + + qDebug() << "Uploading a snapshot from" << filename << "as location image for" << locationID; + + + + const QString LOCATION_IMAGE_NAME = "location[image]"; + + QHttpPart imagePart; + imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"" + LOCATION_IMAGE_NAME + "\";")); + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + imagePart.setBodyDevice(locationImageFile); + + imageFileMultiPart->append(imagePart); + + const QString LOCATION_IMAGE_PUT_PATH = "api/v1/locations/%1/image"; + + JSONCallbackParameters imageCallbackParams; + imageCallbackParams.jsonCallbackReceiver = this; + imageCallbackParams.jsonCallbackMethod = "locationImageUpdateSuccess"; + + // make an authenticated request via account manager to upload the image + // don't do anything with error or success for now + AccountManager::getInstance().authenticatedRequest(LOCATION_IMAGE_PUT_PATH.arg(locationID), + QNetworkAccessManager::PutOperation, + JSONCallbackParameters(), QByteArray(), imageFileMultiPart); +} + + diff --git a/interface/src/location/LocationManager.h b/interface/src/location/LocationManager.h index b6a662e323..43431a83c9 100644 --- a/interface/src/location/LocationManager.h +++ b/interface/src/location/LocationManager.h @@ -35,8 +35,12 @@ signals: void creationCompleted(const QString& errorMessage); private slots: - void namedLocationDataReceived(const QJsonObject& data); + void namedLocationDataReceived(const QJsonObject& jsonObject); void errorDataReceived(QNetworkReply& errorReply); + void locationImageUpdateSuccess(const QJsonObject& jsonObject); + +private: + void updateSnapshotForExistingLocation(const QString& locationID); }; From 8dddbb9d4b5ec2d742984814c9483a95d8c773ac Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 17 Sep 2014 14:07:10 -0700 Subject: [PATCH 3/5] make snapshots uploaded for locations temp files that will be cleared --- interface/src/location/LocationManager.cpp | 75 +++++++++++----------- interface/src/ui/Snapshot.cpp | 60 ++++++++++++++--- interface/src/ui/Snapshot.h | 4 ++ 3 files changed, 93 insertions(+), 46 deletions(-) diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index 866e92be98..ed27d9e49d 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -104,47 +104,48 @@ void LocationManager::updateSnapshotForExistingLocation(const QString& locationI // first create a snapshot and save it Application* application = Application::getInstance(); - QString filename = Snapshot::saveSnapshot(application->getGLWidget(), application->getAvatar()); + QTemporaryFile* tempImageFile = Snapshot::saveTempSnapshot(application->getGLWidget(), application->getAvatar()); - AccountManager& accountManager = AccountManager::getInstance(); - - // setup a multipart that is in the AccountManager thread - we need this so it can be cleaned up after the QNetworkReply - QHttpMultiPart* imageFileMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); - imageFileMultiPart->moveToThread(accountManager.thread()); - - // load the file at that filename, parent the mile to the QHttpMultipart - QFile* locationImageFile = new QFile(filename, imageFileMultiPart); - - if (!locationImageFile->open(QIODevice::ReadOnly)) { + if (tempImageFile && tempImageFile->open()) { + AccountManager& accountManager = AccountManager::getInstance(); + + // setup a multipart that is in the AccountManager thread - we need this so it can be cleaned up after the QNetworkReply + QHttpMultiPart* imageFileMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + imageFileMultiPart->moveToThread(accountManager.thread()); + + // parent the temp file to the QHttpMultipart after moving it to account manager thread + tempImageFile->moveToThread(accountManager.thread()); + tempImageFile->setParent(imageFileMultiPart); + + qDebug() << "Uploading a snapshot from" << QFileInfo(*tempImageFile).absoluteFilePath() + << "as location image for" << locationID; + + const QString LOCATION_IMAGE_NAME = "location[image]"; + + QHttpPart imagePart; + imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"" + LOCATION_IMAGE_NAME + "\";" + " filename=\"" + QFileInfo(tempImageFile->fileName()).fileName().toUtf8() + "\"")); + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + imagePart.setBodyDevice(tempImageFile); + + imageFileMultiPart->append(imagePart); + + const QString LOCATION_IMAGE_PUT_PATH = "api/v1/locations/%1/image"; + + JSONCallbackParameters imageCallbackParams; + imageCallbackParams.jsonCallbackReceiver = this; + imageCallbackParams.jsonCallbackMethod = "locationImageUpdateSuccess"; + + // make an authenticated request via account manager to upload the image + // don't do anything with error or success for now + AccountManager::getInstance().authenticatedRequest(LOCATION_IMAGE_PUT_PATH.arg(locationID), + QNetworkAccessManager::PutOperation, + JSONCallbackParameters(), QByteArray(), imageFileMultiPart); + } else { qDebug() << "Couldn't open snapshot file to upload as location image. No location image will be stored."; return; } - - qDebug() << "Uploading a snapshot from" << filename << "as location image for" << locationID; - - - - const QString LOCATION_IMAGE_NAME = "location[image]"; - - QHttpPart imagePart; - imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, - QVariant("form-data; name=\"" + LOCATION_IMAGE_NAME + "\";")); - imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); - imagePart.setBodyDevice(locationImageFile); - - imageFileMultiPart->append(imagePart); - - const QString LOCATION_IMAGE_PUT_PATH = "api/v1/locations/%1/image"; - - JSONCallbackParameters imageCallbackParams; - imageCallbackParams.jsonCallbackReceiver = this; - imageCallbackParams.jsonCallbackMethod = "locationImageUpdateSuccess"; - - // make an authenticated request via account manager to upload the image - // don't do anything with error or success for now - AccountManager::getInstance().authenticatedRequest(LOCATION_IMAGE_PUT_PATH.arg(locationID), - QNetworkAccessManager::PutOperation, - JSONCallbackParameters(), QByteArray(), imageFileMultiPart); } diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index d530be57d2..9fe1c332be 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -65,6 +65,24 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { } QString Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) { + QFile* snapshotFile = savedFileForSnapshot(widget, avatar, false); + + // we don't need the snapshot file, so close it, grab its filename and delete it + snapshotFile->close(); + + QString snapshotPath = QFileInfo(*snapshotFile).absoluteFilePath(); + + delete snapshotFile; + + return snapshotPath; +} + +QTemporaryFile* Snapshot::saveTempSnapshot(QGLWidget* widget, Avatar* avatar) { + // return whatever we get back from saved file for snapshot + return static_cast(savedFileForSnapshot(widget, avatar, true));; +} + +QFile* Snapshot::savedFileForSnapshot(QGLWidget* widget, Avatar* avatar, bool isTemporary) { QImage shot = widget->grabFrameBuffer(); glm::vec3 location = avatar->getPosition(); @@ -91,16 +109,40 @@ QString Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) { username.replace(QRegExp("[^A-Za-z0-9_]"), "-"); QDateTime now = QDateTime::currentDateTime(); - QString fileName = Menu::getInstance()->getSnapshotsLocation(); - - if (!fileName.endsWith(QDir::separator())) { - fileName.append(QDir::separator()); - } - - fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation))); - shot.save(fileName, 0, 100); - return fileName; + QString filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation); + + const int IMAGE_QUALITY = 100; + + if (!isTemporary) { + QString snapshotFullPath = Menu::getInstance()->getSnapshotsLocation(); + + if (!snapshotFullPath.endsWith(QDir::separator())) { + snapshotFullPath.append(QDir::separator()); + } + + snapshotFullPath.append(filename); + + QFile* imageFile = new QFile(snapshotFullPath); + imageFile->open(QIODevice::WriteOnly); + + shot.save(imageFile, 0, IMAGE_QUALITY); + imageFile->close(); + + return imageFile; + } else { + QTemporaryFile* imageTempFile = new QTemporaryFile(QDir::tempPath() + "/XXXXXX-" + filename); + + if (!imageTempFile->open()) { + qDebug() << "Unable to open QTemporaryFile for temp snapshot. Will not save."; + return NULL; + } + + shot.save(imageTempFile, 0, IMAGE_QUALITY); + imageTempFile->close(); + + return imageTempFile; + } } diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 2872b3fdcb..8f15532cb5 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -42,7 +42,11 @@ class Snapshot { public: static QString saveSnapshot(QGLWidget* widget, Avatar* avatar); + static QTemporaryFile* saveTempSnapshot(QGLWidget* widget, Avatar* avatar); static SnapshotMetaData* parseSnapshotData(QString snapshotPath); + +private: + static QFile* savedFileForSnapshot(QGLWidget* widget, Avatar* avatar, bool isTemporary); }; #endif // hifi_Snapshot_h From abafaffe44d9922c91b67475819b7abb6f55df7b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 17 Sep 2014 14:11:41 -0700 Subject: [PATCH 4/5] fix a missed Model->Entity rename that caused slot error --- interface/src/Menu.cpp | 4 ++-- interface/src/Menu.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d3d1c8c0d0..34026398f8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -328,9 +328,9 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersVoxelNodes, Qt::CTRL | Qt::SHIFT | Qt::Key_1, false, &nodeBounds, SLOT(setShowVoxelNodes(bool))); - addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersModelNodes, + addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersEntityNodes, Qt::CTRL | Qt::SHIFT | Qt::Key_2, false, - &nodeBounds, SLOT(setShowModelNodes(bool))); + &nodeBounds, SLOT(setShowEntityNodes(bool))); addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersParticleNodes, Qt::CTRL | Qt::SHIFT | Qt::Key_3, false, &nodeBounds, SLOT(setShowParticleNodes(bool))); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b43e7cb75e..f2df7a2885 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -439,7 +439,7 @@ namespace MenuOption { const QString ScriptEditor = "Script Editor..."; const QString SettingsExport = "Export Settings"; const QString SettingsImport = "Import Settings"; - const QString ShowBordersModelNodes = "Show Model Nodes"; + const QString ShowBordersEntityNodes = "Show Entity Nodes"; const QString ShowBordersParticleNodes = "Show Particle Nodes"; const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowIKConstraints = "Show IK Constraints"; From 48e45265a376e49b488b6ae798cf2438c6f9d381 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 17 Sep 2014 14:15:07 -0700 Subject: [PATCH 5/5] only add a directory to the filesystem watcher if not empty --- interface/src/ScriptsModel.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/src/ScriptsModel.cpp b/interface/src/ScriptsModel.cpp index 7b24587129..cbdbb5bf54 100644 --- a/interface/src/ScriptsModel.cpp +++ b/interface/src/ScriptsModel.cpp @@ -42,17 +42,15 @@ ScriptsModel::ScriptsModel(QObject* parent) : _localDirectory(), _fsWatcher(), _localFiles(), - _remoteFiles() { - - QString scriptPath = Menu::getInstance()->getScriptsLocation(); - - _localDirectory.setPath(scriptPath); + _remoteFiles() +{ + _localDirectory.setFilter(QDir::Files | QDir::Readable); _localDirectory.setNameFilters(QStringList("*.js")); - _fsWatcher.addPath(_localDirectory.absolutePath()); + updateScriptsLocation(Menu::getInstance()->getScriptsLocation()); + connect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this, &ScriptsModel::reloadLocalFiles); - connect(Menu::getInstance(), &Menu::scriptLocationChanged, this, &ScriptsModel::updateScriptsLocation); reloadLocalFiles(); @@ -88,8 +86,13 @@ int ScriptsModel::rowCount(const QModelIndex& parent) const { void ScriptsModel::updateScriptsLocation(const QString& newPath) { _fsWatcher.removePath(_localDirectory.absolutePath()); + _localDirectory.setPath(newPath); - _fsWatcher.addPath(_localDirectory.absolutePath()); + + if (!_localDirectory.absolutePath().isEmpty()) { + _fsWatcher.addPath(_localDirectory.absolutePath()); + } + reloadLocalFiles(); }