diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c38c6dc378..0a2f879591 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -257,6 +257,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); + _bookmarks = new Bookmarks(); // Before setting up the menu + // call Menu getInstance static method to set up the menu _window->setMenuBar(Menu::getInstance()); diff --git a/interface/src/Application.h b/interface/src/Application.h index d7a826d9ea..a66a30abce 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -38,6 +38,7 @@ #include #include "Audio.h" +#include "Bookmarks.h" #include "Camera.h" #include "DatagramProcessor.h" #include "Environment.h" @@ -300,6 +301,8 @@ public: QRect getDesirableApplicationGeometry(); RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; } + Bookmarks* getBookmarks() const { return _bookmarks; } + signals: /// Fired when we're simulating; allows external parties to hook in. @@ -576,6 +579,8 @@ private: bool _isVSyncOn; bool _aboutToQuit; + + Bookmarks* _bookmarks; }; #endif // hifi_Application_h diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp new file mode 100644 index 0000000000..d3ecf4097e --- /dev/null +++ b/interface/src/Bookmarks.cpp @@ -0,0 +1,73 @@ +// +// Bookmarks.cpp +// interface/src +// +// Created by David Rowe on 13 Jan 2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include "Bookmarks.h" + +Bookmarks::Bookmarks() { + _bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + BOOKMARKS_FILENAME; + readFromFile(); +} + +void Bookmarks::insert(const QString& name, const QString& address) { + _bookmarks.insert(name, address); + + if (contains(name)) { + qDebug() << "Added bookmark:" << name << "," << address; + persistToFile(); + } else { + qWarning() << "Couldn't add bookmark: " << name << "," << address; + } +} + +void Bookmarks::remove(const QString& name) { + _bookmarks.remove(name); + + if (!contains(name)) { + qDebug() << "Deleted bookmark:" << name; + persistToFile(); + } else { + qWarning() << "Couldn't delete bookmark:" << name; + } +} + +bool Bookmarks::contains(const QString& name) const { + return _bookmarks.contains(name); +} + +void Bookmarks::readFromFile() { + QFile loadFile(_bookmarksFilename); + + if (!loadFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open bookmarks file for reading"); + return; + } + + QByteArray data = loadFile.readAll(); + QJsonDocument json(QJsonDocument::fromJson(data)); + _bookmarks = json.object().toVariantMap(); +} + +void Bookmarks::persistToFile() { + QFile saveFile(_bookmarksFilename); + + if (!saveFile.open(QIODevice::WriteOnly)) { + qWarning("Couldn't open bookmarks file for writing"); + return; + } + + QJsonDocument json(QJsonObject::fromVariantMap(_bookmarks)); + QByteArray data = json.toJson(); + saveFile.write(data); +} diff --git a/interface/src/Bookmarks.h b/interface/src/Bookmarks.h new file mode 100644 index 0000000000..2f054bcdbc --- /dev/null +++ b/interface/src/Bookmarks.h @@ -0,0 +1,41 @@ +// +// Bookmarks.h +// interface/src +// +// Created by David Rowe on 13 Jan 2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Bookmarks_h +#define hifi_Bookmarks_h + +#include +#include +#include +#include + +class Bookmarks: public QObject { + Q_OBJECT + +public: + Bookmarks(); + + void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name. + void remove(const QString& name); + bool contains(const QString& name) const; + + QVariantMap* getBookmarks() { return &_bookmarks; }; + +private: + QVariantMap _bookmarks; // { name: address, ... } + + const QString BOOKMARKS_FILENAME = "bookmarks.json"; + QString _bookmarksFilename; + void readFromFile(); + void persistToFile(); +}; + +#endif // hifi_Bookmarks_h \ No newline at end of file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 220406b21c..cfe4ec0c87 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -124,6 +124,15 @@ Menu::Menu() : appInstance, SLOT(toggleRunningScriptsWidget())); addDisabledActionAndSeparator(fileMenu, "Go"); + addActionToQMenuAndActionHash(fileMenu, MenuOption::BookmarkLocation, 0, + this, SLOT(bookmarkLocation())); + _bookmarksMenu = fileMenu->addMenu(MenuOption::Bookmarks); + _bookmarksMenu->setEnabled(false); + _deleteBookmarksMenu = addActionToQMenuAndActionHash(fileMenu, + MenuOption::DeleteBookmark, 0, + this, SLOT(deleteBookmark())); + _deleteBookmarksMenu->setEnabled(false); + loadBookmarks(); addActionToQMenuAndActionHash(fileMenu, MenuOption::NameLocation, Qt::CTRL | Qt::Key_N, @@ -262,7 +271,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false, avatar, SLOT(updateMotionBehavior())); - QMenu* collisionsMenu = avatarMenu->addMenu("Collide With..."); + QMenu* collisionsMenu = avatarMenu->addMenu("Collide With"); addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false, avatar, SLOT(onToggleRagdoll())); addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithAvatars, @@ -1016,6 +1025,125 @@ void Menu::changeVSync() { Application::getInstance()->setVSyncEnabled(isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn)); } +void Menu::loadBookmarks() { + QVariantMap* bookmarks = Application::getInstance()->getBookmarks()->getBookmarks(); + if (bookmarks->count() > 0) { + + QMapIterator i(*bookmarks); + while (i.hasNext()) { + i.next(); + + QString bookmarkName = i.key(); + QString bookmarkAddress = i.value().toString(); + + QAction* teleportAction = new QAction(getMenu(MenuOption::Bookmarks)); + teleportAction->setData(bookmarkAddress); + connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark())); + + addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, bookmarkName, 0, QAction::NoRole); + } + + _bookmarksMenu->setEnabled(true); + _deleteBookmarksMenu->setEnabled(true); + } +} + +void Menu::bookmarkLocation() { + + QInputDialog bookmarkLocationDialog(Application::getInstance()->getWindow()); + bookmarkLocationDialog.setWindowTitle("Bookmark Location"); + bookmarkLocationDialog.setLabelText("Name:"); + bookmarkLocationDialog.setInputMode(QInputDialog::TextInput); + bookmarkLocationDialog.resize(400, 200); + + if (bookmarkLocationDialog.exec() == QDialog::Rejected) { + return; + } + + QString bookmarkName = bookmarkLocationDialog.textValue().trimmed(); + bookmarkName = bookmarkName.replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); + if (bookmarkName.length() == 0) { + return; + } + + auto addressManager = DependencyManager::get(); + QString bookmarkAddress = addressManager->currentAddress().toString(); + + Bookmarks* bookmarks = Application::getInstance()->getBookmarks(); + if (bookmarks->contains(bookmarkName)) { + QMessageBox duplicateBookmarkMessage; + duplicateBookmarkMessage.setIcon(QMessageBox::Warning); + duplicateBookmarkMessage.setText("The bookmark name you entered already exists in your list."); + duplicateBookmarkMessage.setInformativeText("Would you like to overwrite it?"); + duplicateBookmarkMessage.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + duplicateBookmarkMessage.setDefaultButton(QMessageBox::Yes); + if (duplicateBookmarkMessage.exec() == QMessageBox::No) { + return; + } + removeAction(_bookmarksMenu, bookmarkName); + } + + QAction* teleportAction = new QAction(getMenu(MenuOption::Bookmarks)); + teleportAction->setData(bookmarkAddress); + connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark())); + + QList menuItems = _bookmarksMenu->actions(); + int position = 0; + while (position < menuItems.count() && bookmarkName > menuItems[position]->text()) { + position += 1; + } + + addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, bookmarkName, 0, + QAction::NoRole, position); + + bookmarks->insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName. + + _bookmarksMenu->setEnabled(true); + _deleteBookmarksMenu->setEnabled(true); +} + +void Menu::teleportToBookmark() { + QAction *action = qobject_cast(sender()); + QString address = action->data().toString(); + DependencyManager::get()->handleLookupString(address); +} + +void Menu::deleteBookmark() { + + QStringList bookmarkList; + QList menuItems = _bookmarksMenu->actions(); + for (int i = 0; i < menuItems.count(); i += 1) { + bookmarkList.append(menuItems[i]->text()); + } + + QInputDialog deleteBookmarkDialog(Application::getInstance()->getWindow()); + deleteBookmarkDialog.setWindowTitle("Delete Bookmark"); + deleteBookmarkDialog.setLabelText("Select the bookmark to delete"); + deleteBookmarkDialog.resize(400, 400); + deleteBookmarkDialog.setOption(QInputDialog::UseListViewForComboBoxItems); + deleteBookmarkDialog.setComboBoxItems(bookmarkList); + deleteBookmarkDialog.setOkButtonText("Delete"); + + if (deleteBookmarkDialog.exec() == QDialog::Rejected) { + return; + } + + QString bookmarkName = deleteBookmarkDialog.textValue().trimmed(); + if (bookmarkName.length() == 0) { + return; + } + + removeAction(_bookmarksMenu, bookmarkName); + + Bookmarks* bookmarks = Application::getInstance()->getBookmarks(); + bookmarks->remove(bookmarkName); + + if (_bookmarksMenu->actions().count() == 0) { + _bookmarksMenu->setEnabled(false); + _deleteBookmarksMenu->setEnabled(false); + } +} + void Menu::displayNameLocationResponse(const QString& errorString) { if (!errorString.isEmpty()) { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index f493eaa7f2..bf17fe46df 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -200,6 +200,9 @@ private slots: void editAttachments(); void editAnimations(); void changePrivateKey(); + void bookmarkLocation(); + void teleportToBookmark(); + void deleteBookmark(); void nameLocation(); void toggleLocationList(); void hmdToolsClosed(); @@ -308,6 +311,10 @@ private: bool _shouldRenderTableNeedsRebuilding = true; QMap _shouldRenderTable; + + void loadBookmarks(); + QMenu* _bookmarksMenu; + QAction* _deleteBookmarksMenu; }; namespace MenuOption { @@ -335,6 +342,8 @@ namespace MenuOption { const QString Bandwidth = "Bandwidth Display"; const QString BandwidthDetails = "Bandwidth Details"; const QString BlueSpeechSphere = "Blue Sphere While Speaking"; + const QString BookmarkLocation = "Bookmark Location"; + const QString Bookmarks = "Bookmarks"; const QString CascadedShadows = "Cascaded"; const QString CachesSize = "Caches Size"; const QString Chat = "Chat..."; @@ -345,6 +354,7 @@ namespace MenuOption { const QString Collisions = "Collisions"; const QString Console = "Console..."; const QString ControlWithSpeech = "Control With Speech"; + const QString DeleteBookmark = "Delete Bookmark..."; const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene"; const QString DontDoPrecisionPicking = "Don't Do Precision Picking"; const QString DecreaseAvatarSize = "Decrease Avatar Size";