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 f51360cca1..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
@@ -3984,6 +4018,32 @@ void Application::saveScripts() {
settings->endArray();
}
+void Application::stopAllScripts() {
+ // stops all current running scripts
+ QList scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions();
+ foreach (QAction* scriptAction, scriptActions) {
+ scriptAction->activate(QAction::Trigger);
+ qDebug() << "stopping script..." << scriptAction->text();
+ }
+ _activeScripts.clear();
+}
+
+void Application::reloadAllScripts() {
+ // remember all the current scripts so we can reload them
+ QStringList reloadList = _activeScripts;
+ // reloads all current running scripts
+ QList scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions();
+ foreach (QAction* scriptAction, scriptActions) {
+ scriptAction->activate(QAction::Trigger);
+ qDebug() << "stopping script..." << scriptAction->text();
+ }
+ _activeScripts.clear();
+ foreach (QString scriptName, reloadList){
+ qDebug() << "reloading script..." << scriptName;
+ loadScript(scriptName);
+ }
+}
+
void Application::removeScriptName(const QString& fileNameString) {
_activeScripts.removeOne(fileNameString);
}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index c5aafc4e9d..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,
@@ -231,6 +233,8 @@ public slots:
void loadDialog();
void toggleLogDialog();
void initAvatarAndViewFrustum();
+ void stopAllScripts();
+ void reloadAllScripts();
private slots:
void timer();
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 6e2cf17c88..beb8369f44 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -93,6 +93,8 @@ Menu::Menu() :
addDisabledActionAndSeparator(fileMenu, "Scripts");
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadDialog()));
+ addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, appInstance, SLOT(stopAllScripts()));
+ addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, 0, appInstance, SLOT(reloadAllScripts()));
_activeScriptsMenu = fileMenu->addMenu("Running Scripts");
addDisabledActionAndSeparator(fileMenu, "Voxels");
@@ -906,6 +908,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();
@@ -930,17 +943,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());
@@ -956,31 +1029,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()
@@ -1011,29 +1061,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 19e9fbf49f..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();
@@ -243,8 +246,10 @@ namespace MenuOption {
const QString PasteVoxels = "Paste";
const QString PasteToVoxel = "Paste to Voxel...";
const QString PipelineWarnings = "Show Render Pipeline Warnings";
+ const QString PlaySlaps = "Play Slaps";
const QString Preferences = "Preferences...";
const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors";
+ const QString ReloadAllScripts = "Reload All Scripts";
const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetSwatchColors = "Reset Swatch Colors";
const QString RunTimingTests = "Run Timing Tests";
@@ -253,11 +258,10 @@ namespace MenuOption {
const QString SettingsExport = "Export Settings";
const QString ShowAllLocalVoxels = "Show All Local Voxels";
const QString ShowTrueColors = "Show TRUE Colors";
- const QString VoxelDrumming = "Voxel Drumming";
- const QString PlaySlaps = "Play Slaps";
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
const QString Stars = "Stars";
const QString Stats = "Stats";
+ const QString StopAllScripts = "Stop All Scripts";
const QString TestPing = "Test Ping";
const QString TreeStats = "Calculate Tree Stats";
const QString TransmitterDrive = "Transmitter Drive";
@@ -268,6 +272,7 @@ namespace MenuOption {
const QString VoxelAddMode = "Add Voxel Mode";
const QString VoxelColorMode = "Color Voxel Mode";
const QString VoxelDeleteMode = "Delete Voxel Mode";
+ const QString VoxelDrumming = "Voxel Drumming";
const QString VoxelGetColorMode = "Get Color Mode";
const QString VoxelMode = "Cycle Voxel Mode";
const QString VoxelPaintColor = "Voxel Paint Color";
diff --git a/libraries/octree/src/OctreeScriptingInterface.cpp b/libraries/octree/src/OctreeScriptingInterface.cpp
index 553ab961df..89bf5ceb62 100644
--- a/libraries/octree/src/OctreeScriptingInterface.cpp
+++ b/libraries/octree/src/OctreeScriptingInterface.cpp
@@ -11,13 +11,19 @@
#include "OctreeScriptingInterface.h"
OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender,
- JurisdictionListener* jurisdictionListener)
+ JurisdictionListener* jurisdictionListener) :
+ _packetSender(NULL),
+ _jurisdictionListener(NULL),
+ _managedPacketSender(false),
+ _managedJurisdictionListener(false),
+ _initialized(false)
{
setPacketSender(packetSender);
setJurisdictionListener(jurisdictionListener);
}
OctreeScriptingInterface::~OctreeScriptingInterface() {
+qDebug() << "OctreeScriptingInterface::~OctreeScriptingInterface() this=" << this;
cleanupManagedObjects();
}
@@ -45,6 +51,9 @@ void OctreeScriptingInterface::setJurisdictionListener(JurisdictionListener* jur
}
void OctreeScriptingInterface::init() {
+ if (_initialized) {
+ return;
+ }
if (_jurisdictionListener) {
_managedJurisdictionListener = false;
} else {
@@ -64,5 +73,5 @@ void OctreeScriptingInterface::init() {
if (QCoreApplication::instance()) {
connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(cleanupManagedObjects()));
}
-
+ _initialized = true;
}
diff --git a/libraries/octree/src/OctreeScriptingInterface.h b/libraries/octree/src/OctreeScriptingInterface.h
index 34eddd8bed..3c832cbae8 100644
--- a/libraries/octree/src/OctreeScriptingInterface.h
+++ b/libraries/octree/src/OctreeScriptingInterface.h
@@ -93,6 +93,7 @@ protected:
JurisdictionListener* _jurisdictionListener;
bool _managedPacketSender;
bool _managedJurisdictionListener;
+ bool _initialized;
};
#endif /* defined(__hifi__OctreeScriptingInterface__) */