// 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 "window.h" #include "application.h" #include "../../lib/cframe.h" #include "../../lib/controls/coptionmenu.h" #include "../../lib/dispatchlist.h" #include "../../lib/events.h" #include "../../uidescription/icontroller.h" #include "../include/iapplication.h" #include "../include/icommand.h" #include "../include/ipreference.h" #include "../include/iwindowcontroller.h" #include "platform/iplatformwindow.h" #include #include //------------------------------------------------------------------------ namespace VSTGUI { namespace Standalone { namespace Detail { namespace /*anonymous*/ { //------------------------------------------------------------------------ UTF8String strFromPositionAndSize (const CPoint& pos, const CPoint& size) { std::stringstream str; str << pos.x << "," << pos.y << "," << size.x << "," << size.y; return UTF8String (str.str ()); } //------------------------------------------------------------------------ struct PosAndSize { CPoint pos; CPoint size; }; static CPoint nullPoint {0, 0}; //------------------------------------------------------------------------ PosAndSize positionAndSizeFromString (const UTF8String& str) { PosAndSize r; std::vector elements; std::stringstream stream (str.getString ()); std::string item; while (std::getline (stream, item, ',')) elements.emplace_back (item); if (elements.size () != 4) return r; r.pos.x = UTF8StringView (elements[0].data ()).toDouble (); r.pos.y = UTF8StringView (elements[1].data ()).toDouble (); r.size.x = UTF8StringView (elements[2].data ()).toDouble (); r.size.y = UTF8StringView (elements[3].data ()).toDouble (); return r; } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ class Window : public IPlatformWindowAccess, public Platform::IWindowDelegate, public IMouseObserver, public std::enable_shared_from_this { public: bool init (const WindowConfiguration& config, const WindowControllerPtr& controller); // IWindow const WindowControllerPtr& getController () const override { return controller; } CPoint getSize () const override { return platformWindow->getSize (); } CPoint getPosition () const override { return platformWindow->getPosition (); } double getScaleFactor () const override { return platformWindow->getScaleFactor (); } CRect getFocusViewRect () const override; const UTF8String& getTitle () const override { return title; } WindowType getType () const override { return windowType; } WindowStyle getStyle () const override { return windowStyle; } const UTF8String& getAutoSaveFrameName () const override { return autoSaveFrameName; } void setAutoSaveFrameName (const UTF8String& name) override { autoSaveFrameName = name; } void setSize (const CPoint& newSize) override; void setPosition (const CPoint& newPosition) override { platformWindow->setPosition (newPosition); } void setTitle (const UTF8String& newTitle) override { title = newTitle; platformWindow->setTitle (newTitle); } void setContentView (const SharedPointer& newFrame) override; void setRepresentedPath (const UTF8String& path) override; WindowStyle changeStyle (WindowStyle stylesToAdd, WindowStyle stylesToRemove) override; void show () override; void hide () override { platformWindow->hide (); } void close () override { platformWindow->close (); } void activate () override { platformWindow->activate (); } void registerWindowListener (IWindowListener* listener) override; void unregisterWindowListener (IWindowListener* listener) override; // IPlatformWindowAccess InterfacePtr getPlatformWindow () const override { return platformWindow; } CFrame* getFrame () const override { return frame; } // Platform::IWindowDelegate CPoint constraintSize (const CPoint& newSize) override; void onSizeChanged (const CPoint& newSize) override; void onPositionChanged (const CPoint& newPosition) override; void onShow () override; void onHide () override; void onClosed () override; bool canClose () override; void onActivated () override; void onDeactivated () override; // ICommandHandler bool canHandleCommand (const Command& command) override; bool handleCommand (const Command& command) override; // IMouseObserver void onMouseEntered (CView*, CFrame* ) override {}; void onMouseExited (CView*, CFrame* ) override {}; void onMouseEvent (MouseEvent& event, CFrame*) override; private: WindowControllerPtr controller; WindowStyle windowStyle; WindowType windowType; Platform::WindowPtr platformWindow; SharedPointer frame; UTF8String autoSaveFrameName; UTF8String title; DispatchList windowListeners; }; //------------------------------------------------------------------------ bool Window::init (const WindowConfiguration& config, const WindowControllerPtr& inController) { title = config.title; windowStyle = config.style; windowType = config.type; platformWindow = Platform::makeWindow (config, *this); if (platformWindow) { if (!config.autoSaveFrameName.empty ()) { autoSaveFrameName = config.autoSaveFrameName; } controller = inController; } return platformWindow != nullptr; } //------------------------------------------------------------------------ void Window::setSize (const CPoint& newSize) { CPoint size (newSize); if (controller) size = controller->constraintSize (*this, size); platformWindow->setSize (size); } //------------------------------------------------------------------------ void Window::show () { if (controller) controller->beforeShow (*this); bool positionChanged = false; if (!autoSaveFrameName.empty ()) { if (auto frameName = IApplication::instance ().getPreferences ().get (autoSaveFrameName)) { auto ps = positionAndSizeFromString (*frameName); if (ps.pos != nullPoint && ps.size != nullPoint) { setPosition (ps.pos); setSize (ps.size); positionChanged = true; } } } if (!positionChanged && windowStyle.isCentered ()) platformWindow->center (); platformWindow->show (); } //------------------------------------------------------------------------ void Window::setContentView (const SharedPointer& newFrame) { if (frame) { frame->unregisterMouseObserver (this); frame->close (); } frame = newFrame; if (!frame) { if (controller) controller->onSetContentView (*this, frame); return; } auto frameConfig = controller ? controller->createPlatformFrameConfig (platformWindow->getPlatformType ()) : nullptr; frameConfig = platformWindow->prepareFrameConfig (std::move (frameConfig)); frame->open (platformWindow->getPlatformHandle (), platformWindow->getPlatformType (), frameConfig.get ()); frame->registerMouseObserver (this); platformWindow->onSetContentView (frame); if (controller) controller->onSetContentView (*this, frame); } //------------------------------------------------------------------------ void Window::setRepresentedPath (const UTF8String& path) { platformWindow->setRepresentedPath (path); } //------------------------------------------------------------------------ WindowStyle Window::changeStyle (WindowStyle stylesToAdd, WindowStyle stylesToRemove) { windowStyle = platformWindow->changeStyle (stylesToAdd, stylesToRemove); return windowStyle; } //------------------------------------------------------------------------ CRect Window::getFocusViewRect () const { CRect result; if (frame) { if (auto focusView = frame->getFocusView ()) { result = focusView->getViewSize (); focusView->translateToGlobal (result); } } return result; } //------------------------------------------------------------------------ CPoint Window::constraintSize (const CPoint& _newSize) { CPoint newSize (_newSize); if (frame) newSize = frame->checkSizeConstraint (newSize); return controller ? controller->constraintSize (*this, newSize) : newSize; } //------------------------------------------------------------------------ void Window::onSizeChanged (const CPoint& newSize) { windowListeners.forEach ( [&] (IWindowListener* listener) { listener->onSizeChanged (*this, newSize); }); if (controller) controller->onSizeChanged (*this, newSize); } //------------------------------------------------------------------------ void Window::onPositionChanged (const CPoint& newPosition) { windowListeners.forEach ( [&] (IWindowListener* listener) { listener->onPositionChanged (*this, newPosition); }); if (controller) controller->onPositionChanged (*this, newPosition); } //------------------------------------------------------------------------ void Window::onClosed () { auto self = shared_from_this (); // make sure we live as long as this method executes if (!autoSaveFrameName.empty ()) IApplication::instance ().getPreferences ().set ( autoSaveFrameName, strFromPositionAndSize (getPosition (), getSize ())); windowListeners.forEach ([&] (IWindowListener* listener) { listener->onClosed (*this); windowListeners.remove (listener); }); if (controller) controller->onClosed (*this); platformWindow->onSetContentView (nullptr); if (frame) { frame->unregisterMouseObserver (this); frame->remember (); frame->close (); frame = nullptr; } controller = nullptr; platformWindow = nullptr; } //------------------------------------------------------------------------ void Window::onShow () { windowListeners.forEach ([&] (IWindowListener* listener) { listener->onShow (*this); }); if (controller) controller->onShow (*this); } //------------------------------------------------------------------------ void Window::onHide () { windowListeners.forEach ([&] (IWindowListener* listener) { listener->onHide (*this); }); if (controller) controller->onHide (*this); } //------------------------------------------------------------------------ bool Window::canClose () { return controller ? controller->canClose (*this) : true; } //------------------------------------------------------------------------ void Window::onActivated () { windowListeners.forEach ([&] (IWindowListener* listener) { listener->onActivated (*this); }); if (controller) controller->onActivated (*this); } //------------------------------------------------------------------------ void Window::onDeactivated () { windowListeners.forEach ([&] (IWindowListener* listener) { listener->onDeactivated (*this); }); if (controller) controller->onDeactivated (*this); } //------------------------------------------------------------------------ void Window::registerWindowListener (IWindowListener* listener) { windowListeners.add (listener); } //------------------------------------------------------------------------ void Window::unregisterWindowListener (IWindowListener* listener) { windowListeners.remove (listener); } //------------------------------------------------------------------------ bool Window::canHandleCommand (const Command& command) { if (command == Commands::CloseWindow) return controller->canClose (*this); if (auto focusView = frame->getFocusView ()) { if (auto viewController = getViewController (focusView, false)) { if (auto commandHandler = dynamic_cast (viewController)) { if (commandHandler->canHandleCommand (command)) return true; } } } if (auto commandHandler = dynamicPtrCast (controller)) { if (commandHandler->canHandleCommand (command)) return true; } return false; } //------------------------------------------------------------------------ bool Window::handleCommand (const Command& command) { if (command == Commands::CloseWindow) { close (); return true; } if (auto focusView = frame->getFocusView ()) { if (auto viewController = getViewController (focusView, false)) { if (auto commandHandler = dynamic_cast (viewController)) { if (commandHandler->handleCommand (command)) return true; } } } if (auto commandHandler = dynamicPtrCast (controller)) { if (commandHandler->handleCommand (command)) return true; } return false; } //------------------------------------------------------------------------ struct WindowContextMenuCommandHandler : ICommandMenuItemTarget, NonAtomicReferenceCounted { WindowContextMenuCommandHandler (Window* window) : window (window) {} bool validateCommandMenuItem (CCommandMenuItem* item) override { Command cmd = {item->getCommandCategory (), item->getCommandName ()}; if (window->canHandleCommand (cmd) || getApplicationPlatformAccess ()->canHandleCommand (cmd)) return true; item->setEnabled (false); return false; } bool onCommandMenuItemSelected (CCommandMenuItem* item) override { Command cmd = {item->getCommandCategory (), item->getCommandName ()}; if (window->handleCommand (cmd)) return true; return getApplicationPlatformAccess ()->handleCommand (cmd); } Window* window; }; //------------------------------------------------------------------------ void Window::onMouseEvent (MouseEvent& event, CFrame* inFrame) { if (event.type != EventType::MouseDown || !event.buttonState.isRight ()) return; auto contextMenu = makeOwned (); CPoint where (event.mousePosition); inFrame->getTransform ().transform (where); CViewContainer::ViewList views; if (inFrame->getViewsAt (where, views, GetViewOptions ().deep ().includeViewContainer ())) { for (const auto& view : views) { auto viewController = getViewController (view); auto contextMenuController = dynamic_cast (viewController); auto contextMenuController2 = dynamic_cast (viewController); if (contextMenuController == nullptr && contextMenuController2 == nullptr) continue; if (contextMenu->getNbEntries () != 0) contextMenu->addSeparator (); CPoint p (event.mousePosition); view->frameToLocal (p); if (contextMenuController2) contextMenuController2->appendContextMenuItems (*contextMenu, view, p); else if (contextMenuController) contextMenuController->appendContextMenuItems (*contextMenu, p); } } if (contextMenu->getNbEntries () == 0 && getApplicationPlatformAccess ()->getConfiguration ().showCommandsInWindowContextMenu) { auto commandList = getApplicationPlatformAccess ()->getCommandList ( staticPtrCast (getPlatformWindow ()).get ()); if (!commandList.empty ()) { auto menuHandler = makeOwned (this); for (const auto& cat : commandList) { auto item = new CMenuItem (cat.first); auto catMenu = new COptionMenu (); item->setSubmenu (catMenu); for (const auto& entry : cat.second) { if (entry.name == CommandName::MenuSeparator) { catMenu->addSeparator (); } else { auto catItem = new CCommandMenuItem ({entry.name, menuHandler, entry.group, entry.name}); catMenu->addEntry (catItem); } } if (catMenu->getNbEntries () > 0) contextMenu->addEntry (item); else item->forget (); } } } if (contextMenu->getNbEntries () > 0) { contextMenu->cleanupSeparators (true); contextMenu->setStyle (COptionMenu::kPopupStyle | COptionMenu::kMultipleCheckStyle); contextMenu->popup (inFrame, event.mousePosition); event.consumed = true; castMouseDownEvent (event).ignoreFollowUpMoveAndUpEvents (true); } } //------------------------------------------------------------------------ WindowPtr makeWindow (const WindowConfiguration& config, const WindowControllerPtr& controller) { auto window = std::make_shared (); if (!window->init (config, controller)) return nullptr; return window; } //------------------------------------------------------------------------ } // Detail } // Standalone } // VSTGUI