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();