diff --git a/cmake/modules/MacOSXBundleInfo.plist.in b/cmake/modules/MacOSXBundleInfo.plist.in new file mode 100644 index 0000000000..1682b6c022 --- /dev/null +++ b/cmake/modules/MacOSXBundleInfo.plist.in @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + CFBundleURLTypes + + + CFBundleURLName + ${MACOSX_BUNDLE_BUNDLE_NAME} URL + CFBundleURLSchemes + + hifi + + + + + diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 6af6ed478d..8e96006828 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -90,7 +90,13 @@ qt5_wrap_ui(QT_UI_HEADERS ${QT_UI_FILES}) set(INTERFACE_SRCS ${INTERFACE_SRCS} ${QT_UI_HEADERS}) if (APPLE) + + # configure CMake to use a custom Info.plist + SET_TARGET_PROPERTIES( ${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in ) + set(MACOSX_BUNDLE_BUNDLE_NAME Interface) + set(MACOSX_BUNDLE_GUI_IDENTIFIER io.highfidelity.Interface) + # set how the icon shows up in the Info.plist file SET(MACOSX_BUNDLE_ICON_FILE interface.icns) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 96ace9076a..b445efeb15 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -101,6 +101,8 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D const int STATS_PELS_PER_LINE = 20; +const QString CUSTOM_URL_SCHEME = "hifi:"; + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { if (message.size() > 0) { QString messageWithNewLine = message + "\n"; @@ -679,6 +681,38 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod } } +bool Application::event(QEvent* event) { + + // handle custom URL + if (event->type() == QEvent::FileOpen) { + QFileOpenEvent* fileEvent = static_cast(event); + if (!fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME)) { + QString destination = fileEvent->url().toLocalFile().remove(CUSTOM_URL_SCHEME); + QStringList urlParts = destination.split('/', QString::SkipEmptyParts); + + if (urlParts.count() > 1) { + // if url has 2 or more parts, the first one is domain name + Menu::getInstance()->goToDomain(urlParts[0]); + + // location coordinates + Menu::getInstance()->goToDestination(urlParts[1]); + if (urlParts.count() > 2) { + + // location orientation + Menu::getInstance()->goToOrientation(urlParts[2]); + } + } else if (urlParts.count() == 1) { + + // location coordinates + Menu::getInstance()->goToDestination(urlParts[0]); + } + } + + return false; + } + return QApplication::event(event); +} + void Application::keyPressEvent(QKeyEvent* event) { _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts diff --git a/interface/src/Application.h b/interface/src/Application.h index 3153150457..574f578bde 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -126,7 +126,9 @@ public: void touchUpdateEvent(QTouchEvent* event); void wheelEvent(QWheelEvent* event); - + + bool event(QEvent* event); + void makeVoxel(glm::vec3 position, float scale, unsigned char red, diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f16a276653..69d695b4c9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -884,6 +884,17 @@ void Menu::editPreferences() { sendFakeEnterEvent(); } +void Menu::goToDomain(const QString newDomain) { + if (NodeList::getInstance()->getDomainHostname() != newDomain) { + + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + Application::getInstance()->getAvatar()->sendKillAvatar(); + + // give our nodeList the new domain-server hostname + NodeList::getInstance()->setDomainHostname(newDomain); + } +} + void Menu::goToDomain() { QString currentDomainHostname = NodeList::getInstance()->getDomainHostname(); @@ -908,17 +919,77 @@ void Menu::goToDomain() { // the user input a new hostname, use that newHostname = domainDialog.textValue(); } - - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - Application::getInstance()->getAvatar()->sendKillAvatar(); - - // give our nodeList the new domain-server hostname - NodeList::getInstance()->setDomainHostname(domainDialog.textValue()); + + goToDomain(newHostname); } sendFakeEnterEvent(); } +void Menu::goToOrientation(QString orientation) { + + if (orientation.isEmpty()) { + return; + } + + QStringList orientationItems = orientation.split(QRegExp("_|,"), QString::SkipEmptyParts); + + const int NUMBER_OF_ORIENTATION_ITEMS = 4; + const int W_ITEM = 0; + const int X_ITEM = 1; + const int Y_ITEM = 2; + const int Z_ITEM = 3; + + if (orientationItems.size() == NUMBER_OF_ORIENTATION_ITEMS) { + + double w = replaceLastOccurrence('-', '.', orientationItems[W_ITEM].trimmed()).toDouble(); + double x = replaceLastOccurrence('-', '.', orientationItems[X_ITEM].trimmed()).toDouble(); + double y = replaceLastOccurrence('-', '.', orientationItems[Y_ITEM].trimmed()).toDouble(); + double z = replaceLastOccurrence('-', '.', orientationItems[Z_ITEM].trimmed()).toDouble(); + + glm::quat newAvatarOrientation(w, x, y, z); + + MyAvatar* myAvatar = Application::getInstance()->getAvatar(); + glm::quat avatarOrientation = myAvatar->getOrientation(); + if (newAvatarOrientation != avatarOrientation) { + myAvatar->setOrientation(newAvatarOrientation); + } + } +} + +bool Menu::goToDestination(QString destination) { + + QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts); + + const int NUMBER_OF_COORDINATE_ITEMS = 3; + const int X_ITEM = 0; + const int Y_ITEM = 1; + const int Z_ITEM = 2; + if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { + + double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble(); + double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble(); + double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble(); + + glm::vec3 newAvatarPos(x, y, z); + + MyAvatar* myAvatar = Application::getInstance()->getAvatar(); + glm::vec3 avatarPos = myAvatar->getPosition(); + if (newAvatarPos != avatarPos) { + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + MyAvatar::sendKillAvatar(); + + qDebug("Going To Location: %f, %f, %f...", x, y, z); + myAvatar->setPosition(newAvatarPos); + } + + return true; + } + + // no coordinates were parsed + return false; +} + void Menu::goTo() { QInputDialog gotoDialog(Application::getInstance()->getWindow()); @@ -934,31 +1005,8 @@ void Menu::goTo() { destination = gotoDialog.textValue(); - QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts); - - const int NUMBER_OF_COORDINATE_ITEMS = 3; - const int X_ITEM = 0; - const int Y_ITEM = 1; - const int Z_ITEM = 2; - if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { - - double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble(); - double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble(); - double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble(); - - glm::vec3 newAvatarPos(x, y, z); - - MyAvatar* myAvatar = Application::getInstance()->getAvatar(); - glm::vec3 avatarPos = myAvatar->getPosition(); - if (newAvatarPos != avatarPos) { - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - MyAvatar::sendKillAvatar(); - - qDebug("Going To Location: %f, %f, %f...", x, y, z); - myAvatar->setPosition(newAvatarPos); - } - - } else { + // go to coordinate destination or to Username + if (!goToDestination(destination)) { // there's a username entered by the user, make a request to the data-server DataServerClient::getValuesForKeysAndUserString( QStringList() @@ -989,29 +1037,7 @@ void Menu::goToLocation() { int dialogReturn = coordinateDialog.exec(); if (dialogReturn == QDialog::Accepted && !coordinateDialog.textValue().isEmpty()) { - QByteArray newCoordinates; - - QString delimiterPattern(","); - QStringList coordinateItems = coordinateDialog.textValue().split(delimiterPattern); - - const int NUMBER_OF_COORDINATE_ITEMS = 3; - const int X_ITEM = 0; - const int Y_ITEM = 1; - const int Z_ITEM = 2; - if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { - double x = coordinateItems[X_ITEM].toDouble(); - double y = coordinateItems[Y_ITEM].toDouble(); - double z = coordinateItems[Z_ITEM].toDouble(); - glm::vec3 newAvatarPos(x, y, z); - - if (newAvatarPos != avatarPos) { - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - MyAvatar::sendKillAvatar(); - - qDebug("Going To Location: %f, %f, %f...", x, y, z); - myAvatar->setPosition(newAvatarPos); - } - } + goToDestination(coordinateDialog.textValue()); } sendFakeEnterEvent(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 31b19a64c6..84f5325e8f 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -84,6 +84,9 @@ public: const char* member = NULL, QAction::MenuRole role = QAction::NoRole); virtual void removeAction(QMenu* menu, const QString& actionName); + bool goToDestination(QString destination); + void goToOrientation(QString orientation); + void goToDomain(const QString newDomain); public slots: void bandwidthDetails();