// // Menu.cpp // interface/src // // Created by Stephen Birarda on 8/12/13. // Copyright 2013 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Application.h" #include "AccountManager.h" #include "Audio.h" #include "audio/AudioIOStatsRenderer.h" #include "audio/AudioScope.h" #include "devices/Visage.h" #include "Menu.h" #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "Util.h" #include "ui/AddressBarDialog.h" #include "ui/AnimationsDialog.h" #include "ui/AttachmentsDialog.h" #include "ui/BandwidthDialog.h" #include "ui/CachesSizeDialog.h" #include "ui/DataWebDialog.h" #include "ui/HMDToolsDialog.h" #include "ui/LodToolsDialog.h" #include "ui/LoginDialog.h" #include "ui/OctreeStatsDialog.h" #include "ui/PreferencesDialog.h" #include "ui/InfoView.h" #include "ui/MetavoxelEditor.h" #include "ui/MetavoxelNetworkSimulator.h" #include "ui/ModelsBrowser.h" #include "ui/NodeBounds.h" Menu* Menu::_instance = NULL; Menu* Menu::getInstance() { static QMutex menuInstanceMutex; // lock the menu instance mutex to make sure we don't race and create two menus and crash menuInstanceMutex.lock(); if (!_instance) { qDebug("First call to Menu::getInstance() - initing menu."); _instance = new Menu(); } menuInstanceMutex.unlock(); return _instance; } Menu::Menu() : _lastAdjust(usecTimestampNow()), _lastAvatarDetailDrop(usecTimestampNow()) { Application *appInstance = Application::getInstance(); QMenu* fileMenu = addMenu("File"); #ifdef Q_OS_MAC addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, this, SLOT(aboutApp()), QAction::AboutRole); #endif AccountManager& accountManager = AccountManager::getInstance(); _loginAction = addActionToQMenuAndActionHash(fileMenu, MenuOption::Logout); // call our toggle login function now so the menu option is setup properly toggleLoginMenuItem(); // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item connect(&accountManager, &AccountManager::profileChanged, this, &Menu::toggleLoginMenuItem); connect(&accountManager, &AccountManager::logoutComplete, this, &Menu::toggleLoginMenuItem); addDisabledActionAndSeparator(fileMenu, "Scripts"); addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadDialog())); addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL, Qt::CTRL | Qt::SHIFT | Qt::Key_O, appInstance, SLOT(loadScriptURLDialog())); addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, appInstance, SLOT(stopAllScripts())); addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R, appInstance, SLOT(reloadAllScripts())); addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, appInstance, SLOT(toggleRunningScriptsWidget())); addDisabledActionAndSeparator(fileMenu, "Location"); addActionToQMenuAndActionHash(fileMenu, MenuOption::NameLocation, Qt::CTRL | Qt::Key_N, this, SLOT(nameLocation())); addActionToQMenuAndActionHash(fileMenu, MenuOption::MyLocations, Qt::CTRL | Qt::Key_K, this, SLOT(toggleLocationList())); addActionToQMenuAndActionHash(fileMenu, MenuOption::AddressBar, Qt::Key_Enter, this, SLOT(toggleAddressBar())); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0, this, SLOT(copyAddress())); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, this, SLOT(copyPath())); addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead())); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, Application::getInstance(), SLOT(uploadSkeleton())); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadAttachment, 0, Application::getInstance(), SLOT(uploadAttachment())); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadEntity, 0, Application::getInstance(), SLOT(uploadEntity())); addDisabledActionAndSeparator(fileMenu, "Settings"); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings())); addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, appInstance, SLOT(quit()), QAction::QuitRole); QMenu* editMenu = addMenu("Edit"); QUndoStack* undoStack = Application::getInstance()->getUndoStack(); QAction* undoAction = undoStack->createUndoAction(editMenu); undoAction->setShortcut(Qt::CTRL | Qt::Key_Z); addActionToQMenuAndActionHash(editMenu, undoAction); QAction* redoAction = undoStack->createRedoAction(editMenu); redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z); addActionToQMenuAndActionHash(editMenu, redoAction); addActionToQMenuAndActionHash(editMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, this, SLOT(editPreferences()), QAction::PreferencesRole); addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, this, SLOT(editAttachments())); addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0, this, SLOT(editAnimations())); QMenu* toolsMenu = addMenu("Tools"); addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, this, SLOT(showScriptEditor())); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::ControlWithSpeech, Qt::CTRL | Qt::SHIFT | Qt::Key_C, _speechRecognizer.getEnabled(), &_speechRecognizer, SLOT(setEnabled(bool))); connect(&_speechRecognizer, SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool))); #endif #ifdef HAVE_QXMPP _chatAction = addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, Qt::Key_Backslash, this, SLOT(showChat())); const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); toggleChat(); connect(&xmppClient, &QXmppClient::connected, this, &Menu::toggleChat); connect(&xmppClient, &QXmppClient::disconnected, this, &Menu::toggleChat); QDir::setCurrent(PathUtils::resourcesPath()); // init chat window to listen chat _chatWindow = new ChatWindow(Application::getInstance()->getWindow()); #endif addActionToQMenuAndActionHash(toolsMenu, MenuOption::ToolWindow, Qt::CTRL | Qt::ALT | Qt::Key_T, this, SLOT(toggleToolWindow())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J, this, SLOT(toggleConsole())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::ResetSensors, Qt::Key_Apostrophe, appInstance, SLOT(resetSensors())); QMenu* avatarMenu = addMenu("Avatar"); QMenu* avatarSizeMenu = avatarMenu->addMenu("Size"); addActionToQMenuAndActionHash(avatarSizeMenu, MenuOption::IncreaseAvatarSize, Qt::Key_Plus, appInstance->getAvatar(), SLOT(increaseSize())); addActionToQMenuAndActionHash(avatarSizeMenu, MenuOption::DecreaseAvatarSize, Qt::Key_Minus, appInstance->getAvatar(), SLOT(decreaseSize())); addActionToQMenuAndActionHash(avatarSizeMenu, MenuOption::ResetAvatarSize, Qt::Key_Equal, appInstance->getAvatar(), SLOT(resetSize())); QObject* avatar = appInstance->getAvatar(); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::KeyboardMotorControl, Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehavior())); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ScriptedMotorControl, 0, true, avatar, SLOT(updateMotionBehavior())); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ChatCircling, 0, false); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false, avatar, SLOT(updateMotionBehavior())); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true, avatar, SLOT(updateMotionBehavior())); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false, avatar, SLOT(updateMotionBehavior())); QMenu* collisionsMenu = avatarMenu->addMenu("Collide With..."); addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false, avatar, SLOT(onToggleRagdoll())); addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithAvatars, 0, true, avatar, SLOT(updateCollisionGroups())); addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithEnvironment, 0, false, avatar, SLOT(updateCollisionGroups())); QMenu* viewMenu = addMenu("View"); #ifdef Q_OS_MAC addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Fullscreen, Qt::CTRL | Qt::META | Qt::Key_F, false, appInstance, SLOT(setFullscreen(bool))); #else addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Fullscreen, Qt::CTRL | Qt::Key_F, false, appInstance, SLOT(setFullscreen(bool))); #endif addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true, appInstance,SLOT(cameraMenuChanged())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false, appInstance, SLOT(cameraMenuChanged())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::UserInterface, Qt::Key_Slash, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::META | Qt::Key_H, false, this, SLOT(hmdTools(bool))); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0, false, appInstance, SLOT(setEnableVRMode(bool))); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, false, appInstance, SLOT(setEnable3DTVMode(bool))); QMenu* nodeBordersMenu = viewMenu->addMenu("Server Borders"); NodeBounds& nodeBounds = appInstance->getNodeBoundsDisplay(); addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersEntityNodes, Qt::CTRL | Qt::SHIFT | Qt::Key_1, false, &nodeBounds, SLOT(setShowEntityNodes(bool))); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false); addDisabledActionAndSeparator(viewMenu, "Stats"); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Percent); addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::EditEntitiesHelp, 0, this, SLOT(showEditEntitiesHelp())); QMenu* developerMenu = addMenu("Developer"); QMenu* renderOptionsMenu = developerMenu->addMenu("Render"); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Avatars, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Entities, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DisableAutoAdjustLOD); QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows"); QActionGroup* shadowGroup = new QActionGroup(shadowMenu); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true)); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false)); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false)); { QMenu* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate); QActionGroup* framerateGroup = new QActionGroup(framerateMenu); framerateGroup->setExclusive(true); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true)); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false)); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false)); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false)); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false)); #if defined(Q_OS_MAC) #else addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, this, SLOT(changeVSync())); #endif } QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); resolutionGroup->setExclusive(true); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true)); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false)); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false)); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false)); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false)); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true, DependencyManager::get().data(), SLOT(toggleGlowEffect(bool))); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false); addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools())); QMenu* avatarDebugMenu = developerMenu->addMenu("Avatar"); #ifdef HAVE_FACESHIFT addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Faceshift, 0, true, DependencyManager::get().data(), SLOT(setTCPEnabled(bool))); #endif #ifdef HAVE_VISAGE addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Visage, 0, false, DependencyManager::get().data(), SLOT(updateEnabled())); #endif addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels"); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false, Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData())); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderSpanners, 0, true); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderDualContourSurfaces, 0, true); addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this, SLOT(showMetavoxelNetworkSimulator())); QMenu* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false); QMenu* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense"); #ifdef __APPLE__ addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseEnabled, 0, false, this, SLOT(toggleSixense(bool))); #endif addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::FilterSixense, 0, true, &SixenseManager::getInstance(), SLOT(setFilter(bool))); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::LowVelocityFilter, 0, true, appInstance, SLOT(setLowVelocityFilter(bool))); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false); QMenu* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false); QMenu* networkMenu = developerMenu->addMenu("Network"); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, 0, false, &UserActivityLogger::getInstance(), SLOT(disable(bool))); addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0, this, SLOT(cachesSizeDialog())); addActionToQMenuAndActionHash(developerMenu, MenuOption::WalletPrivateKey, 0, this, SLOT(changePrivateKey())); QMenu* timingMenu = developerMenu->addMenu("Timing and Stats"); QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer"); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests())); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings); auto audioIO = DependencyManager::get