From 8e46043f144d2259f090c3e920929ab2963e5816 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 16 Jun 2015 08:41:21 -0700 Subject: [PATCH] add fixed back/forward, buttons to menu --- interface/src/Menu.cpp | 17 ++- interface/src/Menu.h | 3 +- libraries/networking/src/AddressManager.cpp | 153 ++++++++++++++------ libraries/networking/src/AddressManager.h | 51 +++++-- libraries/networking/src/NodeList.cpp | 2 +- 5 files changed, 164 insertions(+), 62 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6242318170..149fd2e043 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -90,6 +90,22 @@ Menu::Menu() { addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, qApp, SLOT(toggleRunningScriptsWidget())); + auto addressManager = DependencyManager::get(); + + addDisabledActionAndSeparator(fileMenu, "History"); + + addActionToQMenuAndActionHash(fileMenu, + MenuOption::Back, + 0, + addressManager.data(), + SLOT(goBack())); + + addActionToQMenuAndActionHash(fileMenu, + MenuOption::Forward, + 0, + addressManager.data(), + SLOT(goForward())); + addDisabledActionAndSeparator(fileMenu, "Location"); qApp->getBookmarks()->setupMenus(this, fileMenu); @@ -98,7 +114,6 @@ Menu::Menu() { Qt::CTRL | Qt::Key_L, dialogsManager.data(), SLOT(toggleAddressBar())); - auto addressManager = DependencyManager::get(); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0, addressManager.data(), SLOT(copyAddress())); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 6107744abc..6b892ebc3c 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -149,6 +149,7 @@ namespace MenuOption { const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams"; const QString AutoMuteAudio = "Auto Mute Microphone"; const QString AvatarReceiveStats = "Show Receive Stats"; + const QString Back = "Back"; const QString BandwidthDetails = "Bandwidth Details"; const QString BinaryEyelidControl = "Binary Eyelid Control"; const QString BlueSpeechSphere = "Blue Sphere While Speaking"; @@ -194,12 +195,12 @@ namespace MenuOption { const QString Faceshift = "Faceshift"; const QString FilterSixense = "Smooth Sixense Movement"; const QString FirstPerson = "First Person"; + const QString Forward = "Forward"; const QString FrameTimer = "Show Timer"; const QString Fullscreen = "Fullscreen"; const QString FullscreenMirror = "Fullscreen Mirror"; const QString GlowWhenSpeaking = "Glow When Speaking"; const QString NamesAboveHeads = "Names Above Heads"; - const QString GoToUser = "Go To User"; const QString HMDTools = "HMD Tools"; const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString KeyboardMotorControl = "Enable Keyboard Motor Control"; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 51575bfde5..f64db747f9 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -54,9 +54,26 @@ const QUrl AddressManager::currentAddress() const { void AddressManager::loadSettings(const QString& lookupString) { if (lookupString.isEmpty()) { - handleLookupString(currentAddressHandle.get().toString()); + handleUrl(currentAddressHandle.get().toString(), LookupTrigger::StartupFromSettings); } else { - handleLookupString(lookupString); + handleUrl(lookupString, LookupTrigger::StartupFromSettings); + } +} + +void AddressManager::goBack() { + if (_backStack.size() > 0) { + // pop a URL from the backStack + QUrl poppedURL = _backStack.pop(); + + // go to that address + handleUrl(poppedURL, LookupTrigger::Back); + } +} + +void AddressManager::goForward() { + if (_forwardStack.size() > 0) { + // pop a URL from the forwardStack and go to that address + handleUrl(_forwardStack.pop(), LookupTrigger::Forward); } } @@ -102,7 +119,7 @@ const JSONCallbackParameters& AddressManager::apiCallbackParameters() { return callbackParams; } -bool AddressManager::handleUrl(const QUrl& lookupUrl) { +bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { if (lookupUrl.scheme() == HIFI_URL_SCHEME) { qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); @@ -121,17 +138,17 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { // we're assuming this is either a network address or global place name // check if it is a network address first if (handleNetworkAddress(lookupUrl.host() - + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) { + + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger)) { // we may have a path that defines a relative viewpoint - if so we should jump to that now - handlePath(lookupUrl.path()); + handlePath(lookupUrl.path(), trigger); } else if (handleDomainID(lookupUrl.host())){ // no place name - this is probably a domain ID // try to look up the domain ID on the metaverse API - attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path()); + attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger); } else { // wasn't an address - lookup the place name // we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after - attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path()); + attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path(), trigger); } } @@ -140,8 +157,10 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { qCDebug(networking) << "Going to relative path" << lookupUrl.path(); // if this is a relative path then handle it as a relative viewpoint - handlePath(lookupUrl.path()); + handlePath(lookupUrl.path(), trigger, true); emit lookupResultsFinished(); + + return true; } return false; @@ -183,6 +202,7 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { } const char OVERRIDE_PATH_KEY[] = "override_path"; +const char LOOKUP_TRIGGER_KEY[] = "lookup_trigger"; void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) { @@ -243,6 +263,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID); } + LookupTrigger trigger = (LookupTrigger) reply.property(LOOKUP_TRIGGER_KEY).toInt(); + // set our current root place id to the ID that came back const QString PLACE_ID_KEY = "id"; _rootPlaceID = rootMap[PLACE_ID_KEY].toUuid(); @@ -251,16 +273,16 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString PLACE_NAME_KEY = "name"; QString placeName = rootMap[PLACE_NAME_KEY].toString(); if (!placeName.isEmpty()) { - setHost(placeName); + setHost(placeName, trigger); } else { - setHost(domainIDString); + setHost(domainIDString, trigger); } // check if we had a path to override the path returned QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString(); if (!overridePath.isEmpty()) { - handlePath(overridePath); + handlePath(overridePath, trigger); } else { // take the path that came back const QString PLACE_PATH_KEY = "path"; @@ -269,10 +291,14 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY); if (!returnedPath.isEmpty()) { - // try to parse this returned path as a viewpoint, that's the only thing it could be for now - if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) { - qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" - << returnedPath; + if (shouldFaceViewpoint) { + // try to parse this returned path as a viewpoint, that's the only thing it could be for now + if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) { + qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" + << returnedPath; + } + } else { + handlePath(overridePath, trigger); } } else { // we didn't override the path or get one back - ask the DS for the viewpoint of its index path @@ -306,15 +332,20 @@ void AddressManager::handleAPIError(QNetworkReply& errorReply) { const QString GET_PLACE = "/api/v1/places/%1"; -void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath) { +void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger) { // assume this is a place name and see if we can get any info on it QString placeName = QUrl::toPercentEncoding(lookupString); QVariantMap requestParams; + + // if the user asked for a specific path with this lookup then keep it with the request so we can use it later if (!overridePath.isEmpty()) { requestParams.insert(OVERRIDE_PATH_KEY, overridePath); } + // remember how this lookup was triggered for history storage handling later + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(trigger)); + AccountManager::getInstance().sendRequest(GET_PLACE.arg(placeName), AccountManagerAuth::None, QNetworkAccessManager::GetOperation, @@ -324,15 +355,20 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const Q const QString GET_DOMAIN_ID = "/api/v1/domains/%1"; -void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath) { +void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger) { // assume this is a domain ID and see if we can get any info on it QString domainID = QUrl::toPercentEncoding(lookupString); QVariantMap requestParams; + + // if the user asked for a specific path with this lookup then keep it with the request so we can use it later if (!overridePath.isEmpty()) { requestParams.insert(OVERRIDE_PATH_KEY, overridePath); } + // remember how this lookup was triggered for history storage handling later + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(trigger)); + AccountManager::getInstance().sendRequest(GET_DOMAIN_ID.arg(domainID), AccountManagerAuth::None, QNetworkAccessManager::GetOperation, @@ -340,7 +376,7 @@ void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QS QByteArray(), NULL, requestParams); } -bool AddressManager::handleNetworkAddress(const QString& lookupString) { +bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTrigger trigger) { const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" "(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$"; @@ -358,7 +394,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { } emit lookupResultsFinished(); - setDomainInfo(domainIPString, domainPort); + setDomainInfo(domainIPString, domainPort, trigger); return true; } @@ -375,7 +411,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { } emit lookupResultsFinished(); - setDomainInfo(domainHostname, domainPort); + setDomainInfo(domainHostname, domainPort, trigger); return true; } @@ -391,15 +427,23 @@ bool AddressManager::handleDomainID(const QString& host) { return (domainIDRegex.indexIn(host) != -1); } -void AddressManager::handlePath(const QString& path) { +void AddressManager::handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly) { if (!handleViewpoint(path)) { qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path << "- wll attempt to ask domain-server to resolve."; + + if (!wasPathOnly) { + // if we received a path with a host then we need to remember what it was here so we can not + // double set add to the history stack once handle viewpoint is called with the result + _newHostLookupPath = path; + } + emit pathChangeRequired(path); } } -bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace) { +bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace, + bool definitelyPathOnly, const QString& pathString) { const QString FLOAT_REGEX_STRING = "([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)"; const QString SPACED_COMMA_REGEX_STRING = "\\s*,\\s*"; const QString POSITION_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + @@ -416,8 +460,18 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should positionRegex.cap(2).toFloat(), positionRegex.cap(3).toFloat()); - // we're about to jump positions - store the current address in our history - addCurrentAddressToHistory(); + // We need to use definitelyPathOnly, pathString and _newHostLookupPath to determine if the current address + // should be stored in the history before we ask for a position/orientation change. A relative path that was + // not associated with a host lookup should always trigger a history change (definitelyPathOnly) and a viewpointString + // with a non empty pathString (suggesting this is the result of a lookup with the domain-server) that does not match + // _newHostLookupPath should always trigger a history change. + // + // We use _newHostLookupPath to determine if the client has already stored its last address + // before moving to a new host thanks to the information in the same lookup URL. + + if (definitelyPathOnly || (!pathString.isEmpty() && pathString == _newHostLookupPath)) { + addCurrentAddressToHistory(LookupTrigger::UserInput); + } if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) { glm::quat newOrientation; @@ -469,11 +523,11 @@ bool AddressManager::handleUsername(const QString& lookupString) { return false; } -void AddressManager::setHost(const QString& host) { +void AddressManager::setHost(const QString& host, LookupTrigger trigger) { if (host != _host) { // if the host is being changed we should store current address in the history - addCurrentAddressToHistory(); + addCurrentAddressToHistory(trigger); _host = host; emit hostChanged(_host); @@ -481,8 +535,8 @@ void AddressManager::setHost(const QString& host) { } -void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { - setHost(hostname); +void AddressManager::setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger) { + setHost(hostname, trigger); _rootPlaceID = QUuid(); @@ -495,11 +549,17 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { void AddressManager::goToUser(const QString& username) { QString formattedUsername = QUrl::toPercentEncoding(username); + + // for history storage handling we remember how this lookup was trigged - for a username it's always user input + QVariantMap requestParams; + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(LookupTrigger::UserInput)); + // this is a username - pull the captured name and lookup that user's location AccountManager::getInstance().sendRequest(GET_USER_LOCATION.arg(formattedUsername), AccountManagerAuth::Optional, QNetworkAccessManager::GetOperation, - apiCallbackParameters()); + apiCallbackParameters(), + QByteArray(), nullptr, requestParams); } void AddressManager::copyAddress() { @@ -510,21 +570,28 @@ void AddressManager::copyPath() { QApplication::clipboard()->setText(currentPath()); } -void AddressManager::addCurrentAddressToHistory() { - if (_lastHistoryAppend == 0) { - // we don't store the first address on application load - // just update the last append time so the next is stored - _lastHistoryAppend = usecTimestampNow(); - } else { - const quint64 DOUBLE_STORE_THRESHOLD_USECS = 500000; +void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { - // avoid double storing when the host changes and the viewpoint changes immediately after - if (usecTimestampNow() - _lastHistoryAppend > DOUBLE_STORE_THRESHOLD_USECS) { - // add the current address to the history - _history.append(currentAddress()); - - // change our last history append to now - _lastHistoryAppend = usecTimestampNow(); + // if we're cold starting and this is called for the first address (from settings) we don't do anything + if (trigger != LookupTrigger::StartupFromSettings) { + if (trigger == LookupTrigger::UserInput) { + // anyime the user has manually looked up an address we know we should clear the forward stack + qDebug() << "CLEARING THE FORWARD STACK"; + _forwardStack.clear(); } + + if (trigger == LookupTrigger::Back) { + qDebug() << "PUTTING THE CURRENT ADDRESS INTO THE FORWARD STACK"; + // when the user is going back, we move the current address to the forward stack and do not but it into the back stack + _forwardStack.push(currentAddress()); + } else { + qDebug() << "ADDING THE CURRENT ADDRESS TO THE BACK STACK"; + // unless this was triggered from the result of a named path lookup, add the current address to the history + _backStack.push(currentAddress()); + } + + qDebug() << "THE BACK STACK IS NOW:" << _backStack; + qDebug() << "THE FORWARD STACK IS NOW:" << _forwardStack; + } } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index d950ae0275..d985c5d2c6 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -12,8 +12,8 @@ #ifndef hifi_AddressManager_h #define hifi_AddressManager_h -#include -#include +#include +#include #include #include @@ -38,6 +38,14 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) public: + + enum LookupTrigger { + UserInput, + Back, + Forward, + StartupFromSettings + }; + bool isConnected(); const QString& getProtocol() { return HIFI_URL_SCHEME; }; @@ -47,10 +55,7 @@ public: const QUuid& getRootPlaceID() const { return _rootPlaceID; } const QString& getHost() const { return _host; } - void setHost(const QString& host); - void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString()); - void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath = QString()); void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } @@ -60,9 +65,12 @@ public: public slots: void handleLookupString(const QString& lookupString); - void goToUser(const QString& username); - void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); - bool goToViewpoint(const QString& viewpointString) { return handleViewpoint(viewpointString); } + // we currently expect this to be called from NodeList once handleLookupString has been called with a path + bool goToViewpointForPath(const QString& viewpointString, const QString& pathString) + { return handleViewpoint(viewpointString, false, false, pathString); } + + void goBack(); + void goForward(); void storeCurrentAddress(); @@ -85,28 +93,39 @@ protected: private slots: void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); + + void goToUser(const QString& username); + void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); private: - void setDomainInfo(const QString& hostname, quint16 port); + void setHost(const QString& host, LookupTrigger trigger); + void setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger); const JSONCallbackParameters& apiCallbackParameters(); - bool handleUrl(const QUrl& lookupUrl); + bool handleUrl(const QUrl& lookupUrl, LookupTrigger trigger = UserInput); - bool handleNetworkAddress(const QString& lookupString); - void handlePath(const QString& path); - bool handleViewpoint(const QString& viewpointString, bool shouldFace = false); + bool handleNetworkAddress(const QString& lookupString, LookupTrigger trigger); + void handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly = false); + bool handleViewpoint(const QString& viewpointString, bool shouldFace = false, + bool definitelyPathOnly = false, const QString& pathString = QString()); bool handleUsername(const QString& lookupString); bool handleDomainID(const QString& host); - void addCurrentAddressToHistory(); + void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger); + void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger); + + void addCurrentAddressToHistory(LookupTrigger trigger); QString _host; QUuid _rootPlaceID; PositionGetter _positionGetter; OrientationGetter _orientationGetter; - QList _history; - quint64 _lastHistoryAppend = 0; + QStack _backStack; + QStack _forwardStack; + quint64 _lastBackPush = 0; + + QString _newHostLookupPath; }; #endif // hifi_AddressManager_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 44aa5bc644..e7bb4fbb6f 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -467,7 +467,7 @@ void NodeList::handleDSPathQueryResponse(const QByteArray& packet) { QString viewpoint = QString::fromUtf8(currentPosition, numViewpointBytes); // Hand it off to the AddressManager so it can handle it as a relative viewpoint - if (DependencyManager::get()->goToViewpoint(viewpoint)) { + if (DependencyManager::get()->goToViewpointForPath(viewpoint, pathQuery)) { qCDebug(networking) << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery; } else { qCDebug(networking) << "Could not go to viewpoint" << viewpoint