// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "application.h" #include "../../lib/cframe.h" #include "../include/appinit.h" #include "../include/iapplication.h" #include "../include/icommand.h" #include "../include/imenubuilder.h" #include "../include/iwindowcontroller.h" #include "shareduiresources.h" #include "window.h" #include //------------------------------------------------------------------------ namespace VSTGUI { namespace Standalone { namespace Detail { //------------------------------------------------------------------------ class Application final : public IPlatformApplication { public: static Application& instance (); Application () = default; void setDelegate (Standalone::Application::DelegatePtr&& delegate); void setConfiguration (Standalone::Application::Configuration&& config); // IApplication IPreference& getPreferences () const override; const CommandLineArguments& getCommandLineArguments () const override; const ISharedUIResources& getSharedUIResources () const override; const ICommonDirectories& getCommonDirectories () const override; Standalone::Application::IDelegate& getDelegate () const override; WindowPtr createWindow (const WindowConfiguration& config, const WindowControllerPtr& controller) override; const WindowList& getWindows () const override { return windows; } AlertResult showAlertBox (const AlertBoxConfig& config) override; void showAlertBoxForWindow (const AlertBoxForWindowConfig& config) override; void registerCommand (const Command& command, char16_t defaultCommandKey = 0) override; bool executeCommand (const Command& command) override; void enableTooltips (bool state) override; void quit () override; // IWindowListener void onSizeChanged (const IWindow& window, const CPoint& newSize) override {}; void onPositionChanged (const IWindow& window, const CPoint& newPosition) override {}; void onShow (const IWindow& window) override; void onHide (const IWindow& window) override {}; void onClosed (const IWindow& window) override; void onActivated (const IWindow& window) override; void onDeactivated (const IWindow& window) override {}; // ICommandHandler bool canHandleCommand (const Command& command) override; bool handleCommand (const Command& command) override; // IPlatformApplication void init (const InitParams& params) override; CommandList getCommandList (const Platform::IWindow* window) override; const CommandList& getKeyCommandList () override; bool canQuit () override; bool dontClosePopupOnDeactivation (Platform::IWindow* window) override; const Configuration& getConfiguration () const override { return config; } private: void registerStandardCommands (); bool doCommandHandling (const Command& command, bool checkOnly); CommandList getCommandList (const Interface& context, const IMenuBuilder* menuBuilder); bool inQuit () const { return hasBit (flags, flagInQuit); } void setInQuit (bool state) { setBit (flags, flagInQuit, state); } WindowList windows; Standalone::Application::DelegatePtr delegate; IPreference* preferences {nullptr}; ICommonDirectories* commonDirectories {nullptr}; PlatformCallbacks platform; CommandList commandList; CommandLineArguments commandLineArguments; Configuration config; uint64_t flags {flagTooltipsEnabled}; uint16_t commandIDCounter {0}; enum Flags { flagInQuit = 1 << 0, flagTooltipsEnabled = 1 << 1, }; }; //------------------------------------------------------------------------ Application& Application::instance () { static Application app; return app; } //------------------------------------------------------------------------ void Application::init (const InitParams& params) { preferences = ¶ms.preferences; commonDirectories = ¶ms.commonDirectories; commandLineArguments = std::move (params.cmdArgs); platform = std::move (params.callbacks); // TODO: make command registration configurable registerStandardCommands (); if (!params.openFiles.empty ()) getDelegate ().openFiles (params.openFiles); getDelegate ().finishLaunching (); } //------------------------------------------------------------------------ void Application::registerStandardCommands () { registerCommand (Commands::About); registerCommand (Commands::Preferences); registerCommand (Commands::Quit, 'q'); registerCommand (Commands::CloseWindow, 'w'); registerCommand (Commands::Undo, 'z'); registerCommand (Commands::Redo, 'Z'); registerCommand (Commands::Cut, 'x'); registerCommand (Commands::Copy, 'c'); registerCommand (Commands::Paste, 'v'); registerCommand (Commands::Delete, 0x8); registerCommand (Commands::SelectAll, 'a'); } //------------------------------------------------------------------------ void Application::setDelegate (Standalone::Application::DelegatePtr&& inDelegate) { delegate = std::move (inDelegate); } //------------------------------------------------------------------------ void Application::setConfiguration (Standalone::Application::Configuration&& configuration) { using namespace VSTGUI::Standalone::Application; for (auto c : configuration) { switch (c.first) { case ConfigKey::UseCompressedUIDescriptionFiles: { vstgui_assert (c.second.type == ConfigValue::Type::Integer); config.useCompressedUIDescriptionFiles = c.second.value.integer != 0; break; } case ConfigKey::ShowCommandsInContextMenu: { vstgui_assert (c.second.type == ConfigValue::Type::Integer); config.showCommandsInWindowContextMenu = c.second.value.integer != 0; break; } } } } //------------------------------------------------------------------------ Standalone::Application::IDelegate& Application::getDelegate () const { vstgui_assert (delegate.get (), "Delegate cannot be nullptr"); return *(delegate.get ()); } //------------------------------------------------------------------------ IPreference& Application::getPreferences () const { vstgui_assert (preferences); return *preferences; } //------------------------------------------------------------------------ const Application::CommandLineArguments& Application::getCommandLineArguments () const { return commandLineArguments; } //------------------------------------------------------------------------ const ISharedUIResources& Application::getSharedUIResources () const { return Detail::getSharedUIResources (); } //------------------------------------------------------------------------ const ICommonDirectories& Application::getCommonDirectories () const { vstgui_assert (commonDirectories); return *commonDirectories; } //------------------------------------------------------------------------ WindowPtr Application::createWindow (const WindowConfiguration& inConfig, const WindowControllerPtr& controller) { auto window = makeWindow (inConfig, controller); if (window) { windows.emplace_back (window); window->registerWindowListener (this); } return window; } //------------------------------------------------------------------------ AlertResult Application::showAlertBox (const AlertBoxConfig& inConfig) { if (platform.showAlert) return platform.showAlert (inConfig); return AlertResult::Error; } //------------------------------------------------------------------------ void Application::showAlertBoxForWindow (const AlertBoxForWindowConfig& inConfig) { vstgui_assert (inConfig.window); if (platform.showAlertForWindow) platform.showAlertForWindow (inConfig); } //------------------------------------------------------------------------ void Application::enableTooltips (bool state) { setBit (flags, flagTooltipsEnabled, state); for (auto& window : windows) { if (auto frame = staticPtrCast (window)->getFrame ()) frame->enableTooltips (state); } } //------------------------------------------------------------------------ void Application::quit () { if (inQuit () || !canQuit ()) return; setInQuit (true); if (platform.quit) platform.quit (); setInQuit (false); } //------------------------------------------------------------------------ bool Application::canQuit () { if (!delegate->canQuit ()) return false; auto currentWindows = windows; // make a copy for (auto& window : currentWindows) { if (window->getController () && !window->getController ()->canClose (*window)) return false; } return true; } //------------------------------------------------------------------------ auto Application::getCommandList (const Interface& context, const IMenuBuilder* menuBuilder) -> CommandList { CommandList menuCommandList; if (menuBuilder) { for (auto& catList : commandList) { if (catList.second.empty ()) continue; if (!menuBuilder->showCommandGroupInMenu (context, catList.first)) continue; auto catListCopy = catList; for (auto it = catListCopy.second.begin (); it != catListCopy.second.end ();) { auto current = it++; if (!menuBuilder->showCommandInMenu (context, *current)) it = catListCopy.second.erase (current); } if (catListCopy.second.empty ()) continue; if (auto func = menuBuilder->getCommandGroupSortFunction (context, catListCopy.first)) { std::sort (catListCopy.second.begin (), catListCopy.second.end (), [&] (const CommandWithKey& lhs, const CommandWithKey& rhs) { return func (lhs.name, rhs.name); }); } for (auto it = ++catListCopy.second.begin (); it != catListCopy.second.end (); ++it) { if (menuBuilder->prependMenuSeparator (context, *it)) { CommandWithKey separator {}; separator.name = CommandName::MenuSeparator; it = catListCopy.second.emplace (it, std::move (separator)); ++it; } } menuCommandList.emplace_back (std::move (catListCopy)); } return menuCommandList; } menuCommandList.clear (); for (auto& catList : commandList) { if (catList.second.empty ()) continue; auto catListCopy = catList; if (catList.first == CommandGroup::Edit) { for (auto it = ++catListCopy.second.begin (); it != catListCopy.second.end (); ++it) { if (it->name == CommandName::Cut || it->name == CommandName::SelectAll) { CommandWithKey separator {}; separator.name = CommandName::MenuSeparator; it = catListCopy.second.emplace (it, std::move (separator)); ++it; } } } menuCommandList.emplace_back (std::move (catListCopy)); } return menuCommandList; } //------------------------------------------------------------------------ auto Application::getCommandList (const Platform::IWindow* window) -> CommandList { if (window) { for (auto& w : getWindows ()) { if (staticPtrCast (w)->getPlatformWindow ().get () == window) { auto menuBuilder = w->getController ()->getWindowMenuBuilder (*w.get ()); if (!menuBuilder) menuBuilder = delegate.get ()->dynamicCast (); return getCommandList (asInterface (*w.get ()), menuBuilder); } } return {}; } return getCommandList (asInterface (*this), delegate.get ()->dynamicCast ()); } //------------------------------------------------------------------------ auto Application::getKeyCommandList () -> const CommandList& { return commandList; } //------------------------------------------------------------------------ void Application::registerCommand (const Command& command, char16_t defaultCommandKey) { CommandWithKey c; c.group = command.group; c.name = command.name; c.defaultKey = defaultCommandKey; c.id = ++commandIDCounter; bool added = false; for (auto& entry : commandList) { if (entry.first == command.group) { for (auto& cmd : entry.second) { if (cmd == command) return; // already registered } entry.second.emplace_back (c); added = true; break; } } if (!added) commandList.push_back ({command.group, {c}}); if (platform.onCommandUpdate) platform.onCommandUpdate (); } //------------------------------------------------------------------------ bool Application::executeCommand (const Command& command) { if (!windows.empty ()) { if (auto commandHandler = dynamicPtrCast (windows.front ())) { if (commandHandler->canHandleCommand (command)) { if (commandHandler->handleCommand (command)) return true; } } } if (canHandleCommand (command)) return handleCommand (command); return false; } //------------------------------------------------------------------------ bool Application::canHandleCommand (const Command& command) { return doCommandHandling (command, true); } //------------------------------------------------------------------------ bool Application::handleCommand (const Command& command) { return doCommandHandling (command, false); } //------------------------------------------------------------------------ bool Application::doCommandHandling (const Command& command, bool checkOnly) { bool result = false; if (auto commandHandler = dynamic_cast (delegate.get ())) result = checkOnly ? commandHandler->canHandleCommand (command) : commandHandler->handleCommand (command); if (!result) { if (command == Commands::Quit) { if (!checkOnly) { quit (); return true; } return delegate->canQuit (); } else if (command == Commands::About) { if (!checkOnly) { delegate->showAboutDialog (); return true; } return delegate->hasAboutDialog (); } else if (command == Commands::Preferences) { if (!checkOnly) { delegate->showPreferenceDialog (); return true; } return delegate->hasPreferenceDialog (); } } return result; } //------------------------------------------------------------------------ void Application::onShow (const IWindow& window) { if (auto frame = static_cast (window).getFrame ()) frame->enableTooltips (hasBit (flags, flagTooltipsEnabled)); } //------------------------------------------------------------------------ template typename Cont::const_iterator findWindow (const Cont& c, const IWindow& window) { return std::find_if (c.begin (), c.end (), [&] (const WindowPtr& w) { return &window == w.get (); }); } //------------------------------------------------------------------------ void Application::onClosed (const IWindow& window) { auto it = findWindow (windows, window); if (it != windows.end ()) windows.erase (it); } //------------------------------------------------------------------------ void Application::onActivated (const IWindow& window) { // move the window in the window list to the first position auto it = findWindow (windows, window); if (it != windows.begin ()) { auto windowPtr = *it; windows.erase (it); windows.insert (windows.begin (), windowPtr); } } static std::vector popupClosePreventionList; //------------------------------------------------------------------------ bool Application::dontClosePopupOnDeactivation (Platform::IWindow* window) { return std::find (popupClosePreventionList.begin (), popupClosePreventionList.end (), window) != popupClosePreventionList.end (); } //------------------------------------------------------------------------ PreventPopupClose::PreventPopupClose (IWindow& window) { if (auto pwa = static_cast (&window)) { if ((platformWindow = dynamicPtrCast (pwa->getPlatformWindow ()))) popupClosePreventionList.emplace_back (platformWindow.get ()); } } //------------------------------------------------------------------------ PreventPopupClose::~PreventPopupClose () noexcept { auto it = std::find (popupClosePreventionList.begin (), popupClosePreventionList.end (), platformWindow.get ()); if (it != popupClosePreventionList.end ()) { popupClosePreventionList.erase (it); } platformWindow->activate (); } //------------------------------------------------------------------------ } // Detail //------------------------------------------------------------------------ IApplication& IApplication::instance () { return Detail::Application::instance (); } //------------------------------------------------------------------------ namespace Application { //------------------------------------------------------------------------ Init::Init (DelegatePtr&& delegate, Configuration&& config) { CView::kDirtyCallAlwaysOnMainThread = true; Detail::Application::instance ().setDelegate (std::move (delegate)); Detail::Application::instance ().setConfiguration (std::move (config)); } //------------------------------------------------------------------------ } // Application } // Standalone } // VSTGUI