| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2020 - Raw Material Software Limited
 - 
 -    JUCE is an open source library subject to commercial or open-source
 -    licensing.
 - 
 -    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 -    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 - 
 -    End User License Agreement: www.juce.com/juce-6-licence
 -    Privacy Policy: www.juce.com/juce-privacy-policy
 - 
 -    Or: You may also use this code under the terms of the GPL v3 (see
 -    www.gnu.org/licenses).
 - 
 -    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 -    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 -    DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - namespace juce
 - {
 - 
 - namespace PopupMenuSettings
 - {
 -     const int scrollZone = 24;
 -     const int dismissCommandId = 0x6287345f;
 - 
 -     static bool menuWasHiddenBecauseOfAppChange = false;
 - }
 - 
 - //==============================================================================
 - struct PopupMenu::HelperClasses
 - {
 - 
 - class MouseSourceState;
 - struct MenuWindow;
 - 
 - static bool canBeTriggered (const PopupMenu::Item& item) noexcept        { return item.isEnabled && item.itemID != 0 && ! item.isSectionHeader; }
 - static bool hasActiveSubMenu (const PopupMenu::Item& item) noexcept      { return item.isEnabled && item.subMenu != nullptr && item.subMenu->items.size() > 0; }
 - 
 - //==============================================================================
 - struct HeaderItemComponent  : public PopupMenu::CustomComponent
 - {
 -     HeaderItemComponent (const String& name, const Options& opts)
 -         : CustomComponent (false), options (opts)
 -     {
 -         setName (name);
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         getLookAndFeel().drawPopupMenuSectionHeaderWithOptions (g,
 -                                                                 getLocalBounds(),
 -                                                                 getName(),
 -                                                                 options);
 -     }
 - 
 -     void getIdealSize (int& idealWidth, int& idealHeight) override
 -     {
 -         getLookAndFeel().getIdealPopupMenuItemSizeWithOptions (getName(),
 -                                                                false,
 -                                                                -1,
 -                                                                idealWidth,
 -                                                                idealHeight,
 -                                                                options);
 -         idealHeight += idealHeight / 2;
 -         idealWidth += idealWidth / 4;
 -     }
 - 
 -     const Options& options;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HeaderItemComponent)
 - };
 - 
 - //==============================================================================
 - struct ItemComponent  : public Component
 - {
 -     ItemComponent (const PopupMenu::Item& i,
 -                    const PopupMenu::Options& o,
 -                    MenuWindow& parent)
 -         : item (i), parentWindow (parent), options (o), customComp (i.customComponent)
 -     {
 -         if (item.isSectionHeader)
 -             customComp = *new HeaderItemComponent (item.text, options);
 - 
 -         if (customComp != nullptr)
 -         {
 -             setItem (*customComp, &item);
 -             addAndMakeVisible (*customComp);
 -         }
 - 
 -         parent.addAndMakeVisible (this);
 - 
 -         updateShortcutKeyDescription();
 - 
 -         int itemW = 80;
 -         int itemH = 16;
 -         getIdealSize (itemW, itemH, options.getStandardItemHeight());
 -         setSize (itemW, jlimit (1, 600, itemH));
 - 
 -         addMouseListener (&parent, false);
 -     }
 - 
 -     ~ItemComponent() override
 -     {
 -         if (customComp != nullptr)
 -             setItem (*customComp, nullptr);
 - 
 -         removeChildComponent (customComp.get());
 -     }
 - 
 -     void getIdealSize (int& idealWidth, int& idealHeight, const int standardItemHeight)
 -     {
 -         if (customComp != nullptr)
 -             customComp->getIdealSize (idealWidth, idealHeight);
 -         else
 -             getLookAndFeel().getIdealPopupMenuItemSizeWithOptions (getTextForMeasurement(),
 -                                                                    item.isSeparator,
 -                                                                    standardItemHeight,
 -                                                                    idealWidth, idealHeight,
 -                                                                    options);
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         if (customComp == nullptr)
 -             getLookAndFeel().drawPopupMenuItemWithOptions (g, getLocalBounds(),
 -                                                            isHighlighted,
 -                                                            item,
 -                                                            options);
 -     }
 - 
 -     void resized() override
 -     {
 -         if (auto* child = getChildComponent (0))
 -         {
 -             const auto border = getLookAndFeel().getPopupMenuBorderSizeWithOptions (options);
 -             child->setBounds (getLocalBounds().reduced (border, 0));
 -         }
 -     }
 - 
 -     void setHighlighted (bool shouldBeHighlighted)
 -     {
 -         shouldBeHighlighted = shouldBeHighlighted && item.isEnabled;
 - 
 -         if (isHighlighted != shouldBeHighlighted)
 -         {
 -             isHighlighted = shouldBeHighlighted;
 - 
 -             if (customComp != nullptr)
 -                 customComp->setHighlighted (shouldBeHighlighted);
 - 
 -             if (isHighlighted)
 -                 if (auto* handler = getAccessibilityHandler())
 -                     handler->grabFocus();
 - 
 -             repaint();
 -         }
 -     }
 - 
 -     PopupMenu::Item item;
 - 
 - private:
 -     //==============================================================================
 -     class ItemAccessibilityHandler  : public AccessibilityHandler
 -     {
 -     public:
 -         explicit ItemAccessibilityHandler (ItemComponent& itemComponentToWrap)
 -             : AccessibilityHandler (itemComponentToWrap,
 -                                     AccessibilityRole::menuItem,
 -                                     getAccessibilityActions (*this, itemComponentToWrap)),
 -               itemComponent (itemComponentToWrap)
 -         {
 -         }
 - 
 -         String getTitle() const override
 -         {
 -             return itemComponent.item.text;
 -         }
 - 
 -         AccessibleState getCurrentState() const override
 -         {
 -             auto state = AccessibilityHandler::getCurrentState().withSelectable()
 -                                                                 .withAccessibleOffscreen();
 - 
 -             if (hasActiveSubMenu (itemComponent.item))
 -             {
 -                 state = itemComponent.parentWindow.isSubMenuVisible() ? state.withExpandable().withExpanded()
 -                                                                       : state.withExpandable().withCollapsed();
 -             }
 - 
 -             return state.isFocused() ? state.withSelected() : state;
 -         }
 - 
 -     private:
 -         static AccessibilityActions getAccessibilityActions (ItemAccessibilityHandler& handler,
 -                                                              ItemComponent& item)
 -         {
 -             auto onFocus = [&item]
 -             {
 -                 item.parentWindow.disableTimerUntilMouseMoves();
 -                 item.parentWindow.ensureItemComponentIsVisible (item, -1);
 -                 item.parentWindow.setCurrentlyHighlightedChild (&item);
 -             };
 - 
 -             auto onPress = [&item]
 -             {
 -                 item.parentWindow.setCurrentlyHighlightedChild (&item);
 -                 item.parentWindow.triggerCurrentlyHighlightedItem();
 -             };
 - 
 -             auto onToggle = [&handler, &item, onFocus]
 -             {
 -                 if (handler.getCurrentState().isSelected())
 -                     item.parentWindow.setCurrentlyHighlightedChild (nullptr);
 -                 else
 -                     onFocus();
 -             };
 - 
 -             auto actions = AccessibilityActions().addAction (AccessibilityActionType::focus,  std::move (onFocus))
 -                                                  .addAction (AccessibilityActionType::press,  std::move (onPress))
 -                                                  .addAction (AccessibilityActionType::toggle, std::move (onToggle));
 - 
 -             if (hasActiveSubMenu (item.item))
 -             {
 -                 auto showSubMenu = [&item]
 -                 {
 -                     item.parentWindow.showSubMenuFor (&item);
 - 
 -                     if (auto* subMenu = item.parentWindow.activeSubMenu.get())
 -                         subMenu->setCurrentlyHighlightedChild (subMenu->items.getFirst());
 -                 };
 - 
 -                 actions.addAction (AccessibilityActionType::press,    showSubMenu);
 -                 actions.addAction (AccessibilityActionType::showMenu, showSubMenu);
 -             }
 - 
 -             return actions;
 -         }
 - 
 -         ItemComponent& itemComponent;
 -     };
 - 
 -     std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
 -     {
 -         return item.isSeparator ? nullptr : std::make_unique<ItemAccessibilityHandler> (*this);
 -     }
 - 
 -     //==============================================================================
 -     MenuWindow& parentWindow;
 -     const PopupMenu::Options& options;
 -     // NB: we use a copy of the one from the item info in case we're using our own section comp
 -     ReferenceCountedObjectPtr<CustomComponent> customComp;
 -     bool isHighlighted = false;
 - 
 -     void updateShortcutKeyDescription()
 -     {
 -         if (item.commandManager != nullptr
 -              && item.itemID != 0
 -              && item.shortcutKeyDescription.isEmpty())
 -         {
 -             String shortcutKey;
 - 
 -             for (auto& keypress : item.commandManager->getKeyMappings()
 -                                     ->getKeyPressesAssignedToCommand (item.itemID))
 -             {
 -                 auto key = keypress.getTextDescriptionWithIcons();
 - 
 -                 if (shortcutKey.isNotEmpty())
 -                     shortcutKey << ", ";
 - 
 -                 if (key.length() == 1 && key[0] < 128)
 -                     shortcutKey << "shortcut: '" << key << '\'';
 -                 else
 -                     shortcutKey << key;
 -             }
 - 
 -             item.shortcutKeyDescription = shortcutKey.trim();
 -         }
 -     }
 - 
 -     String getTextForMeasurement() const
 -     {
 -         return item.shortcutKeyDescription.isNotEmpty() ? item.text + "   " + item.shortcutKeyDescription
 -                                                         : item.text;
 -     }
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent)
 - };
 - 
 - //==============================================================================
 - struct MenuWindow  : public Component
 - {
 -     MenuWindow (const PopupMenu& menu, MenuWindow* parentWindow,
 -                 Options opts, bool alignToRectangle, bool shouldDismissOnMouseUp,
 -                 ApplicationCommandManager** manager, float parentScaleFactor = 1.0f)
 -         : Component ("menu"),
 -           parent (parentWindow),
 -           options (opts.withParentComponent (getLookAndFeel().getParentComponentForMenuOptions (opts))),
 -           managerOfChosenCommand (manager),
 -           componentAttachedTo (options.getTargetComponent()),
 -           dismissOnMouseUp (shouldDismissOnMouseUp),
 -           windowCreationTime (Time::getMillisecondCounter()),
 -           lastFocusedTime (windowCreationTime),
 -           timeEnteredCurrentChildComp (windowCreationTime),
 -           scaleFactor (parentWindow != nullptr ? parentScaleFactor : 1.0f)
 -     {
 -         setWantsKeyboardFocus (false);
 -         setMouseClickGrabsKeyboardFocus (false);
 -         setAlwaysOnTop (true);
 -         setFocusContainerType (FocusContainerType::focusContainer);
 - 
 -         setLookAndFeel (parent != nullptr ? &(parent->getLookAndFeel())
 -                                           : menu.lookAndFeel.get());
 - 
 -         auto& lf = getLookAndFeel();
 - 
 -         if (auto* pc = options.getParentComponent())
 -         {
 -             pc->addChildComponent (this);
 -         }
 -         else
 -         {
 -             const auto shouldDisableAccessibility = [this]
 -             {
 -                 const auto* compToCheck = parent != nullptr ? parent
 -                                                             : options.getTargetComponent();
 - 
 -                 return compToCheck != nullptr && ! compToCheck->isAccessible();
 -             }();
 - 
 -             if (shouldDisableAccessibility)
 -                 setAccessible (false);
 - 
 -             addToDesktop (ComponentPeer::windowIsTemporary
 -                           | ComponentPeer::windowIgnoresKeyPresses
 -                           | lf.getMenuWindowFlags());
 - 
 -             Desktop::getInstance().addGlobalMouseListener (this);
 -         }
 - 
 -         if (options.getParentComponent() == nullptr && parentWindow == nullptr && lf.shouldPopupMenuScaleWithTargetComponent (options))
 -             if (auto* targetComponent = options.getTargetComponent())
 -                 scaleFactor = Component::getApproximateScaleFactorForComponent (targetComponent);
 - 
 -         setOpaque (lf.findColour (PopupMenu::backgroundColourId).isOpaque()
 -                      || ! Desktop::canUseSemiTransparentWindows());
 - 
 -         const auto initialSelectedId = options.getInitiallySelectedItemId();
 - 
 -         for (int i = 0; i < menu.items.size(); ++i)
 -         {
 -             auto& item = menu.items.getReference (i);
 - 
 -             if (i + 1 < menu.items.size() || ! item.isSeparator)
 -             {
 -                 auto* child = items.add (new ItemComponent (item, options, *this));
 - 
 -                 if (initialSelectedId != 0 && item.itemID == initialSelectedId)
 -                     setCurrentlyHighlightedChild (child);
 -             }
 -         }
 - 
 -         auto targetArea = options.getTargetScreenArea() / scaleFactor;
 - 
 -         calculateWindowPos (targetArea, alignToRectangle);
 -         setTopLeftPosition (windowPos.getPosition());
 - 
 -         if (auto visibleID = options.getItemThatMustBeVisible())
 -         {
 -             for (auto* item : items)
 -             {
 -                 if (item->item.itemID == visibleID)
 -                 {
 -                     const auto targetPosition = [&]
 -                     {
 -                         if (auto* pc = options.getParentComponent())
 -                             return pc->getLocalPoint (nullptr, targetArea.getTopLeft());
 - 
 -                         return targetArea.getTopLeft();
 -                     }();
 - 
 -                     auto y = targetPosition.getY() - windowPos.getY();
 -                     ensureItemComponentIsVisible (*item, isPositiveAndBelow (y, windowPos.getHeight()) ? y : -1);
 - 
 -                     break;
 -                 }
 -             }
 -         }
 - 
 -         resizeToBestWindowPos();
 - 
 -         getActiveWindows().add (this);
 -         lf.preparePopupMenuWindow (*this);
 - 
 -         getMouseState (Desktop::getInstance().getMainMouseSource()); // forces creation of a mouse source watcher for the main mouse
 -     }
 - 
 -     ~MenuWindow() override
 -     {
 -         getActiveWindows().removeFirstMatchingValue (this);
 -         Desktop::getInstance().removeGlobalMouseListener (this);
 -         activeSubMenu.reset();
 -         items.clear();
 -     }
 - 
 -     //==============================================================================
 -     void paint (Graphics& g) override
 -     {
 -         if (isOpaque())
 -             g.fillAll (Colours::white);
 - 
 -         auto& theme = getLookAndFeel();
 -         theme.drawPopupMenuBackgroundWithOptions (g, getWidth(), getHeight(), options);
 - 
 -         if (columnWidths.isEmpty())
 -             return;
 - 
 -         const auto separatorWidth = theme.getPopupMenuColumnSeparatorWidthWithOptions (options);
 -         const auto border = theme.getPopupMenuBorderSizeWithOptions (options);
 - 
 -         auto currentX = 0;
 - 
 -         std::for_each (columnWidths.begin(), std::prev (columnWidths.end()), [&] (int width)
 -         {
 -             const Rectangle<int> separator (currentX + width,
 -                                             border,
 -                                             separatorWidth,
 -                                             getHeight() - border * 2);
 -             theme.drawPopupMenuColumnSeparatorWithOptions (g, separator, options);
 -             currentX += width + separatorWidth;
 -         });
 -     }
 - 
 -     void paintOverChildren (Graphics& g) override
 -     {
 -         auto& lf = getLookAndFeel();
 - 
 -         if (options.getParentComponent())
 -             lf.drawResizableFrame (g, getWidth(), getHeight(),
 -                                    BorderSize<int> (getLookAndFeel().getPopupMenuBorderSizeWithOptions (options)));
 - 
 -         if (canScroll())
 -         {
 -             if (isTopScrollZoneActive())
 -             {
 -                 lf.drawPopupMenuUpDownArrowWithOptions (g,
 -                                                         getWidth(),
 -                                                         PopupMenuSettings::scrollZone,
 -                                                         true,
 -                                                         options);
 -             }
 - 
 -             if (isBottomScrollZoneActive())
 -             {
 -                 g.setOrigin (0, getHeight() - PopupMenuSettings::scrollZone);
 -                 lf.drawPopupMenuUpDownArrowWithOptions (g,
 -                                                         getWidth(),
 -                                                         PopupMenuSettings::scrollZone,
 -                                                         false,
 -                                                         options);
 -             }
 -         }
 -     }
 - 
 -     //==============================================================================
 -     // hide this and all sub-comps
 -     void hide (const PopupMenu::Item* item, bool makeInvisible)
 -     {
 -         if (isVisible())
 -         {
 -             WeakReference<Component> deletionChecker (this);
 - 
 -             activeSubMenu.reset();
 -             currentChild = nullptr;
 - 
 -             if (item != nullptr
 -                  && item->commandManager != nullptr
 -                  && item->itemID != 0)
 -             {
 -                 *managerOfChosenCommand = item->commandManager;
 -             }
 - 
 -             auto resultID = options.hasWatchedComponentBeenDeleted() ? 0 : getResultItemID (item);
 - 
 -             exitModalState (resultID);
 - 
 -             if (makeInvisible && deletionChecker != nullptr)
 -                 setVisible (false);
 - 
 -             if (resultID != 0
 -                  && item != nullptr
 -                  && item->action != nullptr)
 -                 MessageManager::callAsync (item->action);
 -         }
 -     }
 - 
 -     static int getResultItemID (const PopupMenu::Item* item)
 -     {
 -         if (item == nullptr)
 -             return 0;
 - 
 -         if (auto* cc = item->customCallback.get())
 -             if (! cc->menuItemTriggered())
 -                 return 0;
 - 
 -         return item->itemID;
 -     }
 - 
 -     void dismissMenu (const PopupMenu::Item* item)
 -     {
 -         if (parent != nullptr)
 -         {
 -             parent->dismissMenu (item);
 -         }
 -         else
 -         {
 -             if (item != nullptr)
 -             {
 -                 // need a copy of this on the stack as the one passed in will get deleted during this call
 -                 auto mi (*item);
 -                 hide (&mi, false);
 -             }
 -             else
 -             {
 -                 hide (nullptr, true);
 -             }
 -         }
 -     }
 - 
 -     float getDesktopScaleFactor() const override    { return scaleFactor * Desktop::getInstance().getGlobalScaleFactor(); }
 - 
 -     void visibilityChanged() override
 -     {
 -         if (! isShowing())
 -             return;
 - 
 -         auto* accessibleFocus = [this]
 -         {
 -           if (currentChild != nullptr)
 -               if (auto* childHandler = currentChild->getAccessibilityHandler())
 -                   return childHandler;
 - 
 -             return getAccessibilityHandler();
 -         }();
 - 
 -         if (accessibleFocus != nullptr)
 -             accessibleFocus->grabFocus();
 -     }
 - 
 -     //==============================================================================
 -     bool keyPressed (const KeyPress& key) override
 -     {
 -         if (key.isKeyCode (KeyPress::downKey))
 -         {
 -             selectNextItem (MenuSelectionDirection::forwards);
 -         }
 -         else if (key.isKeyCode (KeyPress::upKey))
 -         {
 -             selectNextItem (MenuSelectionDirection::backwards);
 -         }
 -         else if (key.isKeyCode (KeyPress::leftKey))
 -         {
 -             if (parent != nullptr)
 -             {
 -                 Component::SafePointer<MenuWindow> parentWindow (parent);
 -                 ItemComponent* currentChildOfParent = parentWindow->currentChild;
 - 
 -                 hide (nullptr, true);
 - 
 -                 if (parentWindow != nullptr)
 -                     parentWindow->setCurrentlyHighlightedChild (currentChildOfParent);
 - 
 -                 disableTimerUntilMouseMoves();
 -             }
 -             else if (componentAttachedTo != nullptr)
 -             {
 -                 componentAttachedTo->keyPressed (key);
 -             }
 -         }
 -         else if (key.isKeyCode (KeyPress::rightKey))
 -         {
 -             disableTimerUntilMouseMoves();
 - 
 -             if (showSubMenuFor (currentChild))
 -             {
 -                 if (isSubMenuVisible())
 -                     activeSubMenu->selectNextItem (MenuSelectionDirection::current);
 -             }
 -             else if (componentAttachedTo != nullptr)
 -             {
 -                 componentAttachedTo->keyPressed (key);
 -             }
 -         }
 -         else if (key.isKeyCode (KeyPress::returnKey) || key.isKeyCode (KeyPress::spaceKey))
 -         {
 -             triggerCurrentlyHighlightedItem();
 -         }
 -         else if (key.isKeyCode (KeyPress::escapeKey))
 -         {
 -             dismissMenu (nullptr);
 -         }
 -         else
 -         {
 -             return false;
 -         }
 - 
 -         return true;
 -     }
 - 
 -     void inputAttemptWhenModal() override
 -     {
 -         WeakReference<Component> deletionChecker (this);
 - 
 -         for (auto* ms : mouseSourceStates)
 -         {
 -             ms->timerCallback();
 - 
 -             if (deletionChecker == nullptr)
 -                 return;
 -         }
 - 
 -         if (! isOverAnyMenu())
 -         {
 -             if (componentAttachedTo != nullptr)
 -             {
 -                 // we want to dismiss the menu, but if we do it synchronously, then
 -                 // the mouse-click will be allowed to pass through. That's good, except
 -                 // when the user clicks on the button that originally popped the menu up,
 -                 // as they'll expect the menu to go away, and in fact it'll just
 -                 // come back. So only dismiss synchronously if they're not on the original
 -                 // comp that we're attached to.
 -                 auto mousePos = componentAttachedTo->getMouseXYRelative();
 - 
 -                 if (componentAttachedTo->reallyContains (mousePos, true))
 -                 {
 -                     postCommandMessage (PopupMenuSettings::dismissCommandId); // dismiss asynchronously
 -                     return;
 -                 }
 -             }
 - 
 -             dismissMenu (nullptr);
 -         }
 -     }
 - 
 -     void handleCommandMessage (int commandId) override
 -     {
 -         Component::handleCommandMessage (commandId);
 - 
 -         if (commandId == PopupMenuSettings::dismissCommandId)
 -             dismissMenu (nullptr);
 -     }
 - 
 -     //==============================================================================
 -     void mouseMove  (const MouseEvent& e) override    { handleMouseEvent (e); }
 -     void mouseDown  (const MouseEvent& e) override    { handleMouseEvent (e); }
 -     void mouseDrag  (const MouseEvent& e) override    { handleMouseEvent (e); }
 -     void mouseUp    (const MouseEvent& e) override    { handleMouseEvent (e); }
 - 
 -     void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel) override
 -     {
 -         alterChildYPos (roundToInt (-10.0f * wheel.deltaY * PopupMenuSettings::scrollZone));
 -     }
 - 
 -     void handleMouseEvent (const MouseEvent& e)
 -     {
 -         getMouseState (e.source).handleMouseEvent (e);
 -     }
 - 
 -     bool windowIsStillValid()
 -     {
 -         if (! isVisible())
 -             return false;
 - 
 -         if (componentAttachedTo != options.getTargetComponent())
 -         {
 -             dismissMenu (nullptr);
 -             return false;
 -         }
 - 
 -         if (auto* currentlyModalWindow = dynamic_cast<MenuWindow*> (Component::getCurrentlyModalComponent()))
 -             if (! treeContains (currentlyModalWindow))
 -                 return false;
 - 
 -         return true;
 -     }
 - 
 -     static Array<MenuWindow*>& getActiveWindows()
 -     {
 -         static Array<MenuWindow*> activeMenuWindows;
 -         return activeMenuWindows;
 -     }
 - 
 -     MouseSourceState& getMouseState (MouseInputSource source)
 -     {
 -         MouseSourceState* mouseState = nullptr;
 - 
 -         for (auto* ms : mouseSourceStates)
 -         {
 -             if      (ms->source == source)                        mouseState = ms;
 -             else if (ms->source.getType() != source.getType())    ms->stopTimer();
 -         }
 - 
 -         if (mouseState == nullptr)
 -         {
 -             mouseState = new MouseSourceState (*this, source);
 -             mouseSourceStates.add (mouseState);
 -         }
 - 
 -         return *mouseState;
 -     }
 - 
 -     //==============================================================================
 -     bool isOverAnyMenu() const
 -     {
 -         return parent != nullptr ? parent->isOverAnyMenu()
 -                                  : isOverChildren();
 -     }
 - 
 -     bool isOverChildren() const
 -     {
 -         return isVisible()
 -                 && (isAnyMouseOver() || (activeSubMenu != nullptr && activeSubMenu->isOverChildren()));
 -     }
 - 
 -     bool isAnyMouseOver() const
 -     {
 -         for (auto* ms : mouseSourceStates)
 -             if (ms->isOver())
 -                 return true;
 - 
 -         return false;
 -     }
 - 
 -     bool treeContains (const MenuWindow* const window) const noexcept
 -     {
 -         auto* mw = this;
 - 
 -         while (mw->parent != nullptr)
 -             mw = mw->parent;
 - 
 -         while (mw != nullptr)
 -         {
 -             if (mw == window)
 -                 return true;
 - 
 -             mw = mw->activeSubMenu.get();
 -         }
 - 
 -         return false;
 -     }
 - 
 -     bool doesAnyJuceCompHaveFocus()
 -     {
 -         if (! isForegroundOrEmbeddedProcess (componentAttachedTo))
 -             return false;
 - 
 -         if (Component::getCurrentlyFocusedComponent() != nullptr)
 -             return true;
 - 
 -         for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
 -         {
 -             if (ComponentPeer::getPeer (i)->isFocused())
 -             {
 -                 hasAnyJuceCompHadFocus = true;
 -                 return true;
 -             }
 -         }
 - 
 -         return ! hasAnyJuceCompHadFocus;
 -     }
 - 
 -     //==============================================================================
 -     Rectangle<int> getParentArea (Point<int> targetPoint, Component* relativeTo = nullptr)
 -     {
 -         if (relativeTo != nullptr)
 -             targetPoint = relativeTo->localPointToGlobal (targetPoint);
 - 
 -         auto* display = Desktop::getInstance().getDisplays().getDisplayForPoint (targetPoint * scaleFactor);
 -         auto parentArea = display->safeAreaInsets.subtractedFrom (display->totalArea);
 - 
 -         if (auto* pc = options.getParentComponent())
 -         {
 -             return pc->getLocalArea (nullptr,
 -                                      pc->getScreenBounds()
 -                                            .reduced (getLookAndFeel().getPopupMenuBorderSizeWithOptions (options))
 -                                            .getIntersection (parentArea));
 -         }
 - 
 -         return parentArea;
 -     }
 - 
 -     void calculateWindowPos (Rectangle<int> target, const bool alignToRectangle)
 -     {
 -         auto parentArea = getParentArea (target.getCentre()) / scaleFactor;
 - 
 -         if (auto* pc = options.getParentComponent())
 -             target = pc->getLocalArea (nullptr, target).getIntersection (parentArea);
 - 
 -         auto maxMenuHeight = parentArea.getHeight() - 24;
 - 
 -         int x, y, widthToUse, heightToUse;
 -         layoutMenuItems (parentArea.getWidth() - 24, maxMenuHeight, widthToUse, heightToUse);
 - 
 -         if (alignToRectangle)
 -         {
 -             x = target.getX();
 - 
 -             auto spaceUnder = parentArea.getBottom() - target.getBottom();
 -             auto spaceOver = target.getY() - parentArea.getY();
 -             auto bufferHeight = 30;
 - 
 -             if (options.getPreferredPopupDirection() == Options::PopupDirection::upwards)
 -                 y = (heightToUse < spaceOver - bufferHeight  || spaceOver >= spaceUnder) ? target.getY() - heightToUse
 -                                                                                          : target.getBottom();
 -             else
 -                 y = (heightToUse < spaceUnder - bufferHeight || spaceUnder >= spaceOver) ? target.getBottom()
 -                                                                                          : target.getY() - heightToUse;
 -         }
 -         else
 -         {
 -             bool tendTowardsRight = target.getCentreX() < parentArea.getCentreX();
 - 
 -             if (parent != nullptr)
 -             {
 -                 if (parent->parent != nullptr)
 -                 {
 -                     const bool parentGoingRight = (parent->getX() + parent->getWidth() / 2
 -                                                     > parent->parent->getX() + parent->parent->getWidth() / 2);
 - 
 -                     if (parentGoingRight && target.getRight() + widthToUse < parentArea.getRight() - 4)
 -                         tendTowardsRight = true;
 -                     else if ((! parentGoingRight) && target.getX() > widthToUse + 4)
 -                         tendTowardsRight = false;
 -                 }
 -                 else if (target.getRight() + widthToUse < parentArea.getRight() - 32)
 -                 {
 -                     tendTowardsRight = true;
 -                 }
 -             }
 - 
 -             auto biggestSpace = jmax (parentArea.getRight() - target.getRight(),
 -                                       target.getX() - parentArea.getX()) - 32;
 - 
 -             if (biggestSpace < widthToUse)
 -             {
 -                 layoutMenuItems (biggestSpace + target.getWidth() / 3, maxMenuHeight, widthToUse, heightToUse);
 - 
 -                 if (numColumns > 1)
 -                     layoutMenuItems (biggestSpace - 4, maxMenuHeight, widthToUse, heightToUse);
 - 
 -                 tendTowardsRight = (parentArea.getRight() - target.getRight()) >= (target.getX() - parentArea.getX());
 -             }
 - 
 -             x = tendTowardsRight ? jmin (parentArea.getRight() - widthToUse - 4, target.getRight())
 -                                  : jmax (parentArea.getX() + 4, target.getX() - widthToUse);
 - 
 -             if (getLookAndFeel().getPopupMenuBorderSizeWithOptions (options) == 0) // workaround for dismissing the window on mouse up when border size is 0
 -                 x += tendTowardsRight ? 1 : -1;
 - 
 -             const auto border = getLookAndFeel().getPopupMenuBorderSizeWithOptions (options);
 -             y = target.getCentreY() > parentArea.getCentreY() ? jmax (parentArea.getY(), target.getBottom() - heightToUse) + border
 -                                                               : target.getY() - border;
 -         }
 - 
 -         x = jmax (parentArea.getX() + 1, jmin (parentArea.getRight()  - (widthToUse  + 6), x));
 -         y = jmax (parentArea.getY() + 1, jmin (parentArea.getBottom() - (heightToUse + 6), y));
 - 
 -         windowPos.setBounds (x, y, widthToUse, heightToUse);
 - 
 -         // sets this flag if it's big enough to obscure any of its parent menus
 -         hideOnExit = parent != nullptr
 -                       && parent->windowPos.intersects (windowPos.expanded (-4, -4));
 -     }
 - 
 -     void layoutMenuItems (const int maxMenuW, const int maxMenuH, int& width, int& height)
 -     {
 -         // Ensure we don't try to add an empty column after the final item
 -         if (auto* last = items.getLast())
 -             last->item.shouldBreakAfter = false;
 - 
 -         const auto isBreak = [] (const ItemComponent* item) { return item->item.shouldBreakAfter; };
 -         const auto numBreaks = static_cast<int> (std::count_if (items.begin(), items.end(), isBreak));
 -         numColumns = numBreaks + 1;
 - 
 -         if (numBreaks == 0)
 -             insertColumnBreaks (maxMenuW, maxMenuH);
 - 
 -         workOutManualSize (maxMenuW);
 -         height = jmin (contentHeight, maxMenuH);
 - 
 -         needsToScroll = contentHeight > height;
 - 
 -         width = updateYPositions();
 -     }
 - 
 -     void insertColumnBreaks (const int maxMenuW, const int maxMenuH)
 -     {
 -         numColumns = options.getMinimumNumColumns();
 -         contentHeight = 0;
 - 
 -         auto maximumNumColumns = options.getMaximumNumColumns() > 0 ? options.getMaximumNumColumns() : 7;
 - 
 -         for (;;)
 -         {
 -             auto totalW = workOutBestSize (maxMenuW);
 - 
 -             if (totalW > maxMenuW)
 -             {
 -                 numColumns = jmax (1, numColumns - 1);
 -                 workOutBestSize (maxMenuW); // to update col widths
 -                 break;
 -             }
 - 
 -             if (totalW > maxMenuW / 2
 -                 || contentHeight < maxMenuH
 -                 || numColumns >= maximumNumColumns)
 -                 break;
 - 
 -             ++numColumns;
 -         }
 - 
 -         const auto itemsPerColumn = (items.size() + numColumns - 1) / numColumns;
 - 
 -         for (auto i = 0;; i += itemsPerColumn)
 -         {
 -             const auto breakIndex = i + itemsPerColumn - 1;
 - 
 -             if (breakIndex >= items.size())
 -                 break;
 - 
 -             items[breakIndex]->item.shouldBreakAfter = true;
 -         }
 - 
 -         if (! items.isEmpty())
 -             (*std::prev (items.end()))->item.shouldBreakAfter = false;
 -     }
 - 
 -     int correctColumnWidths (const int maxMenuW)
 -     {
 -         auto totalW = std::accumulate (columnWidths.begin(), columnWidths.end(), 0);
 -         const auto minWidth = jmin (maxMenuW, options.getMinimumWidth());
 - 
 -         if (totalW < minWidth)
 -         {
 -             totalW = minWidth;
 - 
 -             for (auto& column : columnWidths)
 -                 column = totalW / numColumns;
 -         }
 - 
 -         return totalW;
 -     }
 - 
 -     void workOutManualSize (const int maxMenuW)
 -     {
 -         contentHeight = 0;
 -         columnWidths.clear();
 - 
 -         for (auto it = items.begin(), end = items.end(); it != end;)
 -         {
 -             const auto isBreak = [] (const ItemComponent* item) { return item->item.shouldBreakAfter; };
 -             const auto nextBreak = std::find_if (it, end, isBreak);
 -             const auto columnEnd = nextBreak == end ? end : std::next (nextBreak);
 - 
 -             const auto getMaxWidth = [] (int acc, const ItemComponent* item) { return jmax (acc, item->getWidth()); };
 -             const auto colW = std::accumulate (it, columnEnd, options.getStandardItemHeight(), getMaxWidth);
 -             const auto adjustedColW = jmin (maxMenuW / jmax (1, numColumns - 2),
 -                                             colW + getLookAndFeel().getPopupMenuBorderSizeWithOptions (options) * 2);
 - 
 -             const auto sumHeight = [] (int acc, const ItemComponent* item) { return acc + item->getHeight(); };
 -             const auto colH = std::accumulate (it, columnEnd, 0, sumHeight);
 - 
 -             contentHeight = jmax (contentHeight, colH);
 -             columnWidths.add (adjustedColW);
 -             it = columnEnd;
 -         }
 - 
 -         contentHeight += getLookAndFeel().getPopupMenuBorderSizeWithOptions (options) * 2;
 - 
 -         correctColumnWidths (maxMenuW);
 -     }
 - 
 -     int workOutBestSize (const int maxMenuW)
 -     {
 -         contentHeight = 0;
 -         int childNum = 0;
 - 
 -         for (int col = 0; col < numColumns; ++col)
 -         {
 -             int colW = options.getStandardItemHeight(), colH = 0;
 - 
 -             auto numChildren = jmin (items.size() - childNum,
 -                                      (items.size() + numColumns - 1) / numColumns);
 - 
 -             for (int i = numChildren; --i >= 0;)
 -             {
 -                 colW = jmax (colW, items.getUnchecked (childNum + i)->getWidth());
 -                 colH += items.getUnchecked (childNum + i)->getHeight();
 -             }
 - 
 -             colW = jmin (maxMenuW / jmax (1, numColumns - 2),
 -                          colW + getLookAndFeel().getPopupMenuBorderSizeWithOptions (options) * 2);
 - 
 -             columnWidths.set (col, colW);
 -             contentHeight = jmax (contentHeight, colH);
 - 
 -             childNum += numChildren;
 -         }
 - 
 -         return correctColumnWidths (maxMenuW);
 -     }
 - 
 -     void ensureItemComponentIsVisible (const ItemComponent& itemComp, int wantedY)
 -     {
 -         if (windowPos.getHeight() > PopupMenuSettings::scrollZone * 4)
 -         {
 -             auto currentY = itemComp.getY();
 - 
 -             if (wantedY > 0 || currentY < 0 || itemComp.getBottom() > windowPos.getHeight())
 -             {
 -                 if (wantedY < 0)
 -                     wantedY = jlimit (PopupMenuSettings::scrollZone,
 -                                       jmax (PopupMenuSettings::scrollZone,
 -                                             windowPos.getHeight() - (PopupMenuSettings::scrollZone + itemComp.getHeight())),
 -                                       currentY);
 - 
 -                 auto parentArea = getParentArea (windowPos.getPosition(), options.getParentComponent()) / scaleFactor;
 -                 auto deltaY = wantedY - currentY;
 - 
 -                 windowPos.setSize (jmin (windowPos.getWidth(), parentArea.getWidth()),
 -                                    jmin (windowPos.getHeight(), parentArea.getHeight()));
 - 
 -                 auto newY = jlimit (parentArea.getY(),
 -                                     parentArea.getBottom() - windowPos.getHeight(),
 -                                     windowPos.getY() + deltaY);
 - 
 -                 deltaY -= newY - windowPos.getY();
 - 
 -                 childYOffset -= deltaY;
 -                 windowPos.setPosition (windowPos.getX(), newY);
 - 
 -                 updateYPositions();
 -             }
 -         }
 -     }
 - 
 -     void resizeToBestWindowPos()
 -     {
 -         auto r = windowPos;
 - 
 -         if (childYOffset < 0)
 -         {
 -             r = r.withTop (r.getY() - childYOffset);
 -         }
 -         else if (childYOffset > 0)
 -         {
 -             auto spaceAtBottom = r.getHeight() - (contentHeight - childYOffset);
 - 
 -             if (spaceAtBottom > 0)
 -                 r.setSize (r.getWidth(), r.getHeight() - spaceAtBottom);
 -         }
 - 
 -         setBounds (r);
 -         updateYPositions();
 -     }
 - 
 -     void alterChildYPos (int delta)
 -     {
 -         if (canScroll())
 -         {
 -             childYOffset += delta;
 - 
 -             childYOffset = [&]
 -             {
 -                 if (delta < 0)
 -                     return jmax (childYOffset, 0);
 - 
 -                 if (delta > 0)
 -                 {
 -                     const auto limit = contentHeight
 -                                         - windowPos.getHeight()
 -                                         + getLookAndFeel().getPopupMenuBorderSizeWithOptions (options);
 -                     return jmin (childYOffset, limit);
 -                 }
 - 
 -                 return childYOffset;
 -             }();
 - 
 -             updateYPositions();
 -         }
 -         else
 -         {
 -             childYOffset = 0;
 -         }
 - 
 -         resizeToBestWindowPos();
 -         repaint();
 -     }
 - 
 -     int updateYPositions()
 -     {
 -         const auto separatorWidth = getLookAndFeel().getPopupMenuColumnSeparatorWidthWithOptions (options);
 -         const auto initialY = getLookAndFeel().getPopupMenuBorderSizeWithOptions (options)
 -                               - (childYOffset + (getY() - windowPos.getY()));
 - 
 -         auto col = 0;
 -         auto x = 0;
 -         auto y = initialY;
 - 
 -         for (const auto& item : items)
 -         {
 -             jassert (col < columnWidths.size());
 -             const auto columnWidth = columnWidths[col];
 -             item->setBounds (x, y, columnWidth, item->getHeight());
 -             y += item->getHeight();
 - 
 -             if (item->item.shouldBreakAfter)
 -             {
 -                 col += 1;
 -                 x += columnWidth + separatorWidth;
 -                 y = initialY;
 -             }
 -         }
 - 
 -         return std::accumulate (columnWidths.begin(), columnWidths.end(), 0)
 -                + (separatorWidth * (columnWidths.size() - 1));
 -     }
 - 
 -     void setCurrentlyHighlightedChild (ItemComponent* child)
 -     {
 -         if (currentChild != nullptr)
 -             currentChild->setHighlighted (false);
 - 
 -         currentChild = child;
 - 
 -         if (currentChild != nullptr)
 -         {
 -             currentChild->setHighlighted (true);
 -             timeEnteredCurrentChildComp = Time::getApproximateMillisecondCounter();
 -         }
 - 
 -         if (auto* handler = getAccessibilityHandler())
 -             handler->notifyAccessibilityEvent (AccessibilityEvent::rowSelectionChanged);
 -     }
 - 
 -     bool isSubMenuVisible() const noexcept          { return activeSubMenu != nullptr && activeSubMenu->isVisible(); }
 - 
 -     bool showSubMenuFor (ItemComponent* childComp)
 -     {
 -         activeSubMenu.reset();
 - 
 -         if (childComp != nullptr
 -              && hasActiveSubMenu (childComp->item))
 -         {
 -             activeSubMenu.reset (new HelperClasses::MenuWindow (*(childComp->item.subMenu), this,
 -                                                                 options.withTargetScreenArea (childComp->getScreenBounds())
 -                                                                        .withMinimumWidth (0)
 -                                                                        .withTargetComponent (nullptr),
 -                                                                 false, dismissOnMouseUp, managerOfChosenCommand, scaleFactor));
 - 
 -             activeSubMenu->setVisible (true); // (must be called before enterModalState on Windows to avoid DropShadower confusion)
 -             activeSubMenu->enterModalState (false);
 -             activeSubMenu->toFront (false);
 -             return true;
 -         }
 - 
 -         return false;
 -     }
 - 
 -     void triggerCurrentlyHighlightedItem()
 -     {
 -         if (currentChild != nullptr
 -              && canBeTriggered (currentChild->item)
 -              && (currentChild->item.customComponent == nullptr
 -                   || currentChild->item.customComponent->isTriggeredAutomatically()))
 -         {
 -             dismissMenu (¤tChild->item);
 -         }
 -     }
 - 
 -     enum class MenuSelectionDirection
 -     {
 -         forwards,
 -         backwards,
 -         current
 -     };
 - 
 -     void selectNextItem (MenuSelectionDirection direction)
 -     {
 -         disableTimerUntilMouseMoves();
 - 
 -         auto start = [&]
 -         {
 -             auto index = items.indexOf (currentChild);
 - 
 -             if (index >= 0)
 -                 return index;
 - 
 -             return direction == MenuSelectionDirection::backwards ? items.size() - 1
 -                                                                   : 0;
 -         }();
 - 
 -         auto preIncrement = (direction != MenuSelectionDirection::current && currentChild != nullptr);
 - 
 -         for (int i = items.size(); --i >= 0;)
 -         {
 -             if (preIncrement)
 -                 start += (direction == MenuSelectionDirection::backwards ? -1 : 1);
 - 
 -             if (auto* mic = items.getUnchecked ((start + items.size()) % items.size()))
 -             {
 -                 if (canBeTriggered (mic->item) || hasActiveSubMenu (mic->item))
 -                 {
 -                     setCurrentlyHighlightedChild (mic);
 -                     return;
 -                 }
 -             }
 - 
 -             if (! preIncrement)
 -                 preIncrement = true;
 -         }
 -     }
 - 
 -     void disableTimerUntilMouseMoves()
 -     {
 -         disableMouseMoves = true;
 - 
 -         if (parent != nullptr)
 -             parent->disableTimerUntilMouseMoves();
 -     }
 - 
 -     bool canScroll() const noexcept                 { return childYOffset != 0 || needsToScroll; }
 -     bool isTopScrollZoneActive() const noexcept     { return canScroll() && childYOffset > 0; }
 -     bool isBottomScrollZoneActive() const noexcept  { return canScroll() && childYOffset < contentHeight - windowPos.getHeight(); }
 - 
 -     //==============================================================================
 -     std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
 -     {
 -         return std::make_unique<AccessibilityHandler> (*this,
 -                                                        AccessibilityRole::popupMenu,
 -                                                        AccessibilityActions().addAction (AccessibilityActionType::focus, [this]
 -                                                        {
 -                                                            if (currentChild != nullptr)
 -                                                            {
 -                                                                if (auto* handler = currentChild->getAccessibilityHandler())
 -                                                                    handler->grabFocus();
 -                                                            }
 -                                                            else
 -                                                            {
 -                                                                selectNextItem (MenuSelectionDirection::forwards);
 -                                                            }
 -                                                        }));
 -     }
 - 
 -     //==============================================================================
 -     MenuWindow* parent;
 -     const Options options;
 -     OwnedArray<ItemComponent> items;
 -     ApplicationCommandManager** managerOfChosenCommand;
 -     WeakReference<Component> componentAttachedTo;
 -     Rectangle<int> windowPos;
 -     bool hasBeenOver = false, needsToScroll = false;
 -     bool dismissOnMouseUp, hideOnExit = false, disableMouseMoves = false, hasAnyJuceCompHadFocus = false;
 -     int numColumns = 0, contentHeight = 0, childYOffset = 0;
 -     Component::SafePointer<ItemComponent> currentChild;
 -     std::unique_ptr<MenuWindow> activeSubMenu;
 -     Array<int> columnWidths;
 -     uint32 windowCreationTime, lastFocusedTime, timeEnteredCurrentChildComp;
 -     OwnedArray<MouseSourceState> mouseSourceStates;
 -     float scaleFactor;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuWindow)
 - };
 - 
 - //==============================================================================
 - class MouseSourceState  : public Timer
 - {
 - public:
 -     MouseSourceState (MenuWindow& w, MouseInputSource s)
 -         : window (w), source (s), lastScrollTime (Time::getMillisecondCounter())
 -     {
 -         startTimerHz (20);
 -     }
 - 
 -     void handleMouseEvent (const MouseEvent& e)
 -     {
 -         if (! window.windowIsStillValid())
 -             return;
 - 
 -         startTimerHz (20);
 -         handleMousePosition (e.getScreenPosition());
 -     }
 - 
 -     void timerCallback() override
 -     {
 -        #if JUCE_WINDOWS
 -         // touch and pen devices on Windows send an offscreen mouse move after mouse up events
 -         // but we don't want to forward these on as they will dismiss the menu
 -         if ((source.isTouch() || source.isPen()) && ! isValidMousePosition())
 -             return;
 -        #endif
 - 
 -         if (window.windowIsStillValid())
 -             handleMousePosition (source.getScreenPosition().roundToInt());
 -     }
 - 
 -     bool isOver() const
 -     {
 -         return window.reallyContains (window.getLocalPoint (nullptr, source.getScreenPosition()).roundToInt(), true);
 -     }
 - 
 -     MenuWindow& window;
 -     MouseInputSource source;
 - 
 - private:
 -     Point<int> lastMousePos;
 -     double scrollAcceleration = 0;
 -     uint32 lastScrollTime, lastMouseMoveTime = 0;
 -     bool isDown = false;
 - 
 -     void handleMousePosition (Point<int> globalMousePos)
 -     {
 -         auto localMousePos = window.getLocalPoint (nullptr, globalMousePos);
 -         auto timeNow = Time::getMillisecondCounter();
 - 
 -         if (timeNow > window.timeEnteredCurrentChildComp + 100
 -              && window.reallyContains (localMousePos, true)
 -              && window.currentChild != nullptr
 -              && ! (window.disableMouseMoves || window.isSubMenuVisible()))
 -         {
 -             window.showSubMenuFor (window.currentChild);
 -         }
 - 
 -         highlightItemUnderMouse (globalMousePos, localMousePos, timeNow);
 - 
 -         const bool overScrollArea = scrollIfNecessary (localMousePos, timeNow);
 -         const bool isOverAny = window.isOverAnyMenu();
 - 
 -         if (window.hideOnExit && window.hasBeenOver && ! isOverAny)
 -             window.hide (nullptr, true);
 -         else
 -             checkButtonState (localMousePos, timeNow, isDown, overScrollArea, isOverAny);
 -     }
 - 
 -     void checkButtonState (Point<int> localMousePos, const uint32 timeNow,
 -                            const bool wasDown, const bool overScrollArea, const bool isOverAny)
 -     {
 -         isDown = window.hasBeenOver
 -                     && (ModifierKeys::currentModifiers.isAnyMouseButtonDown()
 -                          || ComponentPeer::getCurrentModifiersRealtime().isAnyMouseButtonDown());
 - 
 -         if (! window.doesAnyJuceCompHaveFocus())
 -         {
 -             if (timeNow > window.lastFocusedTime + 10)
 -             {
 -                 PopupMenuSettings::menuWasHiddenBecauseOfAppChange = true;
 -                 window.dismissMenu (nullptr);
 -                 // Note: This object may have been deleted by the previous call.
 -             }
 -         }
 -         else if (wasDown && timeNow > window.windowCreationTime + 250
 -                    && ! (isDown || overScrollArea))
 -         {
 -             if (window.reallyContains (localMousePos, true))
 -                 window.triggerCurrentlyHighlightedItem();
 -             else if ((window.hasBeenOver || ! window.dismissOnMouseUp) && ! isOverAny)
 -                 window.dismissMenu (nullptr);
 - 
 -             // Note: This object may have been deleted by the previous call.
 -         }
 -         else
 -         {
 -             window.lastFocusedTime = timeNow;
 -         }
 -     }
 - 
 -     void highlightItemUnderMouse (Point<int> globalMousePos, Point<int> localMousePos, const uint32 timeNow)
 -     {
 -         if (globalMousePos != lastMousePos || timeNow > lastMouseMoveTime + 350)
 -         {
 -             const auto isMouseOver = window.reallyContains (localMousePos, true);
 - 
 -             if (isMouseOver)
 -                 window.hasBeenOver = true;
 - 
 -             if (lastMousePos.getDistanceFrom (globalMousePos) > 2)
 -             {
 -                 lastMouseMoveTime = timeNow;
 - 
 -                 if (window.disableMouseMoves && isMouseOver)
 -                     window.disableMouseMoves = false;
 -             }
 - 
 -             if (window.disableMouseMoves || (window.activeSubMenu != nullptr && window.activeSubMenu->isOverChildren()))
 -                 return;
 - 
 -             const bool isMovingTowardsMenu = isMouseOver && globalMousePos != lastMousePos
 -                                                 && isMovingTowardsSubmenu (globalMousePos);
 - 
 -             lastMousePos = globalMousePos;
 - 
 -             if (! isMovingTowardsMenu)
 -             {
 -                 auto* c = window.getComponentAt (localMousePos);
 - 
 -                 if (c == &window)
 -                     c = nullptr;
 - 
 -                 auto* itemUnderMouse = dynamic_cast<ItemComponent*> (c);
 - 
 -                 if (itemUnderMouse == nullptr && c != nullptr)
 -                     itemUnderMouse = c->findParentComponentOfClass<ItemComponent>();
 - 
 -                 if (itemUnderMouse != window.currentChild
 -                       && (isMouseOver || (window.activeSubMenu == nullptr) || ! window.activeSubMenu->isVisible()))
 -                 {
 -                     if (isMouseOver && (c != nullptr) && (window.activeSubMenu != nullptr))
 -                         window.activeSubMenu->hide (nullptr, true);
 - 
 -                     if (! isMouseOver)
 -                     {
 -                         if (! window.hasBeenOver)
 -                             return;
 - 
 -                         itemUnderMouse = nullptr;
 -                     }
 - 
 -                     window.setCurrentlyHighlightedChild (itemUnderMouse);
 -                 }
 -             }
 -         }
 -     }
 - 
 -     bool isMovingTowardsSubmenu (Point<int> newGlobalPos) const
 -     {
 -         if (window.activeSubMenu == nullptr)
 -             return false;
 - 
 -         // try to intelligently guess whether the user is moving the mouse towards a currently-open
 -         // submenu. To do this, look at whether the mouse stays inside a triangular region that
 -         // extends from the last mouse pos to the submenu's rectangle..
 - 
 -         auto itemScreenBounds = window.activeSubMenu->getScreenBounds();
 -         auto subX = (float) itemScreenBounds.getX();
 - 
 -         auto oldGlobalPos = lastMousePos;
 - 
 -         if (itemScreenBounds.getX() > window.getX())
 -         {
 -             oldGlobalPos -= Point<int> (2, 0);  // to enlarge the triangle a bit, in case the mouse only moves a couple of pixels
 -         }
 -         else
 -         {
 -             oldGlobalPos += Point<int> (2, 0);
 -             subX += (float) itemScreenBounds.getWidth();
 -         }
 - 
 -         Path areaTowardsSubMenu;
 -         areaTowardsSubMenu.addTriangle ((float) oldGlobalPos.x, (float) oldGlobalPos.y,
 -                                         subX, (float) itemScreenBounds.getY(),
 -                                         subX, (float) itemScreenBounds.getBottom());
 - 
 -         return areaTowardsSubMenu.contains (newGlobalPos.toFloat());
 -     }
 - 
 -     bool scrollIfNecessary (Point<int> localMousePos, const uint32 timeNow)
 -     {
 -         if (window.canScroll()
 -              && isPositiveAndBelow (localMousePos.x, window.getWidth())
 -              && (isPositiveAndBelow (localMousePos.y, window.getHeight()) || source.isDragging()))
 -         {
 -             if (window.isTopScrollZoneActive() && localMousePos.y < PopupMenuSettings::scrollZone)
 -                 return scroll (timeNow, -1);
 - 
 -             if (window.isBottomScrollZoneActive() && localMousePos.y > window.getHeight() - PopupMenuSettings::scrollZone)
 -                 return scroll (timeNow, 1);
 -         }
 - 
 -         scrollAcceleration = 1.0;
 -         return false;
 -     }
 - 
 -     bool scroll (const uint32 timeNow, const int direction)
 -     {
 -         if (timeNow > lastScrollTime + 20)
 -         {
 -             scrollAcceleration = jmin (4.0, scrollAcceleration * 1.04);
 -             int amount = 0;
 - 
 -             for (int i = 0; i < window.items.size() && amount == 0; ++i)
 -                 amount = ((int) scrollAcceleration) * window.items.getUnchecked (i)->getHeight();
 - 
 -             window.alterChildYPos (amount * direction);
 -             lastScrollTime = timeNow;
 -         }
 - 
 -         return true;
 -     }
 - 
 -    #if JUCE_WINDOWS
 -     bool isValidMousePosition()
 -     {
 -         auto screenPos = source.getScreenPosition();
 -         auto localPos = (window.activeSubMenu == nullptr) ? window.getLocalPoint (nullptr, screenPos)
 -                                                           : window.activeSubMenu->getLocalPoint (nullptr, screenPos);
 - 
 -         if (localPos.x < 0 && localPos.y < 0)
 -             return false;
 - 
 -         return true;
 -     }
 -    #endif
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseSourceState)
 - };
 - 
 - //==============================================================================
 - struct NormalComponentWrapper : public PopupMenu::CustomComponent
 - {
 -     NormalComponentWrapper (Component& comp, int w, int h, bool triggerMenuItemAutomaticallyWhenClicked)
 -         : PopupMenu::CustomComponent (triggerMenuItemAutomaticallyWhenClicked),
 -           width (w), height (h)
 -     {
 -         addAndMakeVisible (comp);
 -     }
 - 
 -     void getIdealSize (int& idealWidth, int& idealHeight) override
 -     {
 -         idealWidth = width;
 -         idealHeight = height;
 -     }
 - 
 -     void resized() override
 -     {
 -         if (auto* child = getChildComponent (0))
 -             child->setBounds (getLocalBounds());
 -     }
 - 
 -     const int width, height;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NormalComponentWrapper)
 - };
 - 
 - };
 - 
 - //==============================================================================
 - PopupMenu::PopupMenu (const PopupMenu& other)
 -     : items (other.items),
 -       lookAndFeel (other.lookAndFeel)
 - {
 - }
 - 
 - PopupMenu& PopupMenu::operator= (const PopupMenu& other)
 - {
 -     if (this != &other)
 -     {
 -         items = other.items;
 -         lookAndFeel = other.lookAndFeel;
 -     }
 - 
 -     return *this;
 - }
 - 
 - PopupMenu::PopupMenu (PopupMenu&& other) noexcept
 -     : items (std::move (other.items)),
 -       lookAndFeel (std::move (other.lookAndFeel))
 - {
 - }
 - 
 - PopupMenu& PopupMenu::operator= (PopupMenu&& other) noexcept
 - {
 -     items = std::move (other.items);
 -     lookAndFeel = other.lookAndFeel;
 -     return *this;
 - }
 - 
 - PopupMenu::~PopupMenu() = default;
 - 
 - void PopupMenu::clear()
 - {
 -     items.clear();
 - }
 - 
 - //==============================================================================
 - PopupMenu::Item::Item() = default;
 - PopupMenu::Item::Item (String t) : text (std::move (t)), itemID (-1) {}
 - 
 - PopupMenu::Item::Item (Item&&) = default;
 - PopupMenu::Item& PopupMenu::Item::operator= (Item&&) = default;
 - 
 - PopupMenu::Item::Item (const Item& other)
 -   : text (other.text),
 -     itemID (other.itemID),
 -     action (other.action),
 -     subMenu (createCopyIfNotNull (other.subMenu.get())),
 -     image (other.image != nullptr ? other.image->createCopy() : nullptr),
 -     customComponent (other.customComponent),
 -     customCallback (other.customCallback),
 -     commandManager (other.commandManager),
 -     shortcutKeyDescription (other.shortcutKeyDescription),
 -     colour (other.colour),
 -     isEnabled (other.isEnabled),
 -     isTicked (other.isTicked),
 -     isSeparator (other.isSeparator),
 -     isSectionHeader (other.isSectionHeader),
 -     shouldBreakAfter (other.shouldBreakAfter)
 - {}
 - 
 - PopupMenu::Item& PopupMenu::Item::operator= (const Item& other)
 - {
 -     text = other.text;
 -     itemID = other.itemID;
 -     action = other.action;
 -     subMenu.reset (createCopyIfNotNull (other.subMenu.get()));
 -     image = other.image != nullptr ? other.image->createCopy() : std::unique_ptr<Drawable>();
 -     customComponent = other.customComponent;
 -     customCallback = other.customCallback;
 -     commandManager = other.commandManager;
 -     shortcutKeyDescription = other.shortcutKeyDescription;
 -     colour = other.colour;
 -     isEnabled = other.isEnabled;
 -     isTicked = other.isTicked;
 -     isSeparator = other.isSeparator;
 -     isSectionHeader = other.isSectionHeader;
 -     shouldBreakAfter = other.shouldBreakAfter;
 -     return *this;
 - }
 - 
 - PopupMenu::Item& PopupMenu::Item::setTicked (bool shouldBeTicked) & noexcept
 - {
 -     isTicked = shouldBeTicked;
 -     return *this;
 - }
 - 
 - PopupMenu::Item& PopupMenu::Item::setEnabled (bool shouldBeEnabled) & noexcept
 - {
 -     isEnabled = shouldBeEnabled;
 -     return *this;
 - }
 - 
 - PopupMenu::Item& PopupMenu::Item::setAction (std::function<void()> newAction) & noexcept
 - {
 -     action = std::move (newAction);
 -     return *this;
 - }
 - 
 - PopupMenu::Item& PopupMenu::Item::setID (int newID) & noexcept
 - {
 -     itemID = newID;
 -     return *this;
 - }
 - 
 - PopupMenu::Item& PopupMenu::Item::setColour (Colour newColour) & noexcept
 - {
 -     colour = newColour;
 -     return *this;
 - }
 - 
 - PopupMenu::Item& PopupMenu::Item::setCustomComponent (ReferenceCountedObjectPtr<CustomComponent> comp) & noexcept
 - {
 -     customComponent = comp;
 -     return *this;
 - }
 - 
 - PopupMenu::Item& PopupMenu::Item::setImage (std::unique_ptr<Drawable> newImage) & noexcept
 - {
 -     image = std::move (newImage);
 -     return *this;
 - }
 - 
 - PopupMenu::Item&& PopupMenu::Item::setTicked (bool shouldBeTicked) && noexcept
 - {
 -     isTicked = shouldBeTicked;
 -     return std::move (*this);
 - }
 - 
 - PopupMenu::Item&& PopupMenu::Item::setEnabled (bool shouldBeEnabled) && noexcept
 - {
 -     isEnabled = shouldBeEnabled;
 -     return std::move (*this);
 - }
 - 
 - PopupMenu::Item&& PopupMenu::Item::setAction (std::function<void()> newAction) && noexcept
 - {
 -     action = std::move (newAction);
 -     return std::move (*this);
 - }
 - 
 - PopupMenu::Item&& PopupMenu::Item::setID (int newID) && noexcept
 - {
 -     itemID = newID;
 -     return std::move (*this);
 - }
 - 
 - PopupMenu::Item&& PopupMenu::Item::setColour (Colour newColour) && noexcept
 - {
 -     colour = newColour;
 -     return std::move (*this);
 - }
 - 
 - PopupMenu::Item&& PopupMenu::Item::setCustomComponent (ReferenceCountedObjectPtr<CustomComponent> comp) && noexcept
 - {
 -     customComponent = comp;
 -     return std::move (*this);
 - }
 - 
 - PopupMenu::Item&& PopupMenu::Item::setImage (std::unique_ptr<Drawable> newImage) && noexcept
 - {
 -     image = std::move (newImage);
 -     return std::move (*this);
 - }
 - 
 - void PopupMenu::addItem (Item newItem)
 - {
 -     // An ID of 0 is used as a return value to indicate that the user
 -     // didn't pick anything, so you shouldn't use it as the ID for an item..
 -     jassert (newItem.itemID != 0
 -               || newItem.isSeparator || newItem.isSectionHeader
 -               || newItem.subMenu != nullptr);
 - 
 -     items.add (std::move (newItem));
 - }
 - 
 - void PopupMenu::addItem (String itemText, std::function<void()> action)
 - {
 -     addItem (std::move (itemText), true, false, std::move (action));
 - }
 - 
 - void PopupMenu::addItem (String itemText, bool isActive, bool isTicked, std::function<void()> action)
 - {
 -     Item i (std::move (itemText));
 -     i.action = std::move (action);
 -     i.isEnabled = isActive;
 -     i.isTicked = isTicked;
 -     addItem (std::move (i));
 - }
 - 
 - void PopupMenu::addItem (int itemResultID, String itemText, bool isActive, bool isTicked)
 - {
 -     Item i (std::move (itemText));
 -     i.itemID = itemResultID;
 -     i.isEnabled = isActive;
 -     i.isTicked = isTicked;
 -     addItem (std::move (i));
 - }
 - 
 - static std::unique_ptr<Drawable> createDrawableFromImage (const Image& im)
 - {
 -     if (im.isValid())
 -     {
 -         auto d = new DrawableImage();
 -         d->setImage (im);
 -         return std::unique_ptr<Drawable> (d);
 -     }
 - 
 -     return {};
 - }
 - 
 - void PopupMenu::addItem (int itemResultID, String itemText, bool isActive, bool isTicked, const Image& iconToUse)
 - {
 -     addItem (itemResultID, std::move (itemText), isActive, isTicked, createDrawableFromImage (iconToUse));
 - }
 - 
 - void PopupMenu::addItem (int itemResultID, String itemText, bool isActive,
 -                          bool isTicked, std::unique_ptr<Drawable> iconToUse)
 - {
 -     Item i (std::move (itemText));
 -     i.itemID = itemResultID;
 -     i.isEnabled = isActive;
 -     i.isTicked = isTicked;
 -     i.image = std::move (iconToUse);
 -     addItem (std::move (i));
 - }
 - 
 - void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager,
 -                                 const CommandID commandID,
 -                                 String displayName,
 -                                 std::unique_ptr<Drawable> iconToUse)
 - {
 -     jassert (commandManager != nullptr && commandID != 0);
 - 
 -     if (auto* registeredInfo = commandManager->getCommandForID (commandID))
 -     {
 -         ApplicationCommandInfo info (*registeredInfo);
 -         auto* target = commandManager->getTargetForCommand (commandID, info);
 - 
 -         Item i;
 -         i.text = displayName.isNotEmpty() ? std::move (displayName) : info.shortName;
 -         i.itemID = (int) commandID;
 -         i.commandManager = commandManager;
 -         i.isEnabled = target != nullptr && (info.flags & ApplicationCommandInfo::isDisabled) == 0;
 -         i.isTicked = (info.flags & ApplicationCommandInfo::isTicked) != 0;
 -         i.image = std::move (iconToUse);
 -         addItem (std::move (i));
 -     }
 - }
 - 
 - void PopupMenu::addColouredItem (int itemResultID, String itemText, Colour itemTextColour,
 -                                  bool isActive, bool isTicked, std::unique_ptr<Drawable> iconToUse)
 - {
 -     Item i (std::move (itemText));
 -     i.itemID = itemResultID;
 -     i.colour = itemTextColour;
 -     i.isEnabled = isActive;
 -     i.isTicked = isTicked;
 -     i.image = std::move (iconToUse);
 -     addItem (std::move (i));
 - }
 - 
 - void PopupMenu::addColouredItem (int itemResultID, String itemText, Colour itemTextColour,
 -                                  bool isActive, bool isTicked, const Image& iconToUse)
 - {
 -     Item i (std::move (itemText));
 -     i.itemID = itemResultID;
 -     i.colour = itemTextColour;
 -     i.isEnabled = isActive;
 -     i.isTicked = isTicked;
 -     i.image = createDrawableFromImage (iconToUse);
 -     addItem (std::move (i));
 - }
 - 
 - void PopupMenu::addCustomItem (int itemResultID,
 -                                std::unique_ptr<CustomComponent> cc,
 -                                std::unique_ptr<const PopupMenu> subMenu)
 - {
 -     Item i;
 -     i.itemID = itemResultID;
 -     i.customComponent = cc.release();
 -     i.subMenu.reset (createCopyIfNotNull (subMenu.get()));
 -     addItem (std::move (i));
 - }
 - 
 - void PopupMenu::addCustomItem (int itemResultID,
 -                                Component& customComponent,
 -                                int idealWidth, int idealHeight,
 -                                bool triggerMenuItemAutomaticallyWhenClicked,
 -                                std::unique_ptr<const PopupMenu> subMenu)
 - {
 -     auto comp = std::make_unique<HelperClasses::NormalComponentWrapper> (customComponent, idealWidth, idealHeight,
 -                                                                          triggerMenuItemAutomaticallyWhenClicked);
 -     addCustomItem (itemResultID, std::move (comp), std::move (subMenu));
 - }
 - 
 - void PopupMenu::addSubMenu (String subMenuName, PopupMenu subMenu, bool isActive)
 - {
 -     addSubMenu (std::move (subMenuName), std::move (subMenu), isActive, nullptr, false, 0);
 - }
 - 
 - void PopupMenu::addSubMenu (String subMenuName, PopupMenu subMenu, bool isActive,
 -                             const Image& iconToUse, bool isTicked, int itemResultID)
 - {
 -     addSubMenu (std::move (subMenuName), std::move (subMenu), isActive,
 -                 createDrawableFromImage (iconToUse), isTicked, itemResultID);
 - }
 - 
 - void PopupMenu::addSubMenu (String subMenuName, PopupMenu subMenu, bool isActive,
 -                             std::unique_ptr<Drawable> iconToUse, bool isTicked, int itemResultID)
 - {
 -     Item i (std::move (subMenuName));
 -     i.itemID = itemResultID;
 -     i.isEnabled = isActive && (itemResultID != 0 || subMenu.getNumItems() > 0);
 -     i.subMenu.reset (new PopupMenu (std::move (subMenu)));
 -     i.isTicked = isTicked;
 -     i.image = std::move (iconToUse);
 -     addItem (std::move (i));
 - }
 - 
 - void PopupMenu::addSeparator()
 - {
 -     if (items.size() > 0 && ! items.getLast().isSeparator)
 -     {
 -         Item i;
 -         i.isSeparator = true;
 -         addItem (std::move (i));
 -     }
 - }
 - 
 - void PopupMenu::addSectionHeader (String title)
 - {
 -     Item i (std::move (title));
 -     i.itemID = 0;
 -     i.isSectionHeader = true;
 -     addItem (std::move (i));
 - }
 - 
 - void PopupMenu::addColumnBreak()
 - {
 -     if (! items.isEmpty())
 -         std::prev (items.end())->shouldBreakAfter = true;
 - }
 - 
 - //==============================================================================
 - PopupMenu::Options::Options()
 - {
 -     targetArea.setPosition (Desktop::getMousePosition());
 - }
 - 
 - template <typename Member, typename Item>
 - static PopupMenu::Options with (PopupMenu::Options options, Member&& member, Item&& item)
 - {
 -     options.*member = std::forward<Item> (item);
 -     return options;
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withTargetComponent (Component* comp) const
 - {
 -     auto o = with (*this, &Options::targetComponent, comp);
 - 
 -     if (comp != nullptr)
 -         o.targetArea = comp->getScreenBounds();
 - 
 -     return o;
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withTargetComponent (Component& comp) const
 - {
 -     return withTargetComponent (&comp);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withTargetScreenArea (Rectangle<int> area) const
 - {
 -     return with (*this, &Options::targetArea, area);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withMousePosition() const
 - {
 -     return withTargetScreenArea (Rectangle<int>{}.withPosition (Desktop::getMousePosition()));
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withDeletionCheck (Component& comp) const
 - {
 -     return with (with (*this, &Options::isWatchingForDeletion, true),
 -                  &Options::componentToWatchForDeletion,
 -                  &comp);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withMinimumWidth (int w) const
 - {
 -     return with (*this, &Options::minWidth, w);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withMinimumNumColumns (int cols) const
 - {
 -     return with (*this, &Options::minColumns, cols);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withMaximumNumColumns (int cols) const
 - {
 -     return with (*this, &Options::maxColumns, cols);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withStandardItemHeight (int height) const
 - {
 -     return with (*this, &Options::standardHeight, height);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withItemThatMustBeVisible (int idOfItemToBeVisible) const
 - {
 -     return with (*this, &Options::visibleItemID, idOfItemToBeVisible);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withParentComponent (Component* parent) const
 - {
 -     return with (*this, &Options::parentComponent, parent);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withPreferredPopupDirection (PopupDirection direction) const
 - {
 -     return with (*this, &Options::preferredPopupDirection, direction);
 - }
 - 
 - PopupMenu::Options PopupMenu::Options::withInitiallySelectedItem (int idOfItemToBeSelected) const
 - {
 -     return with (*this, &Options::initiallySelectedItemId, idOfItemToBeSelected);
 - }
 - 
 - Component* PopupMenu::createWindow (const Options& options,
 -                                     ApplicationCommandManager** managerOfChosenCommand) const
 - {
 -     return items.isEmpty() ? nullptr
 -                            : new HelperClasses::MenuWindow (*this, nullptr, options,
 -                                                             ! options.getTargetScreenArea().isEmpty(),
 -                                                             ModifierKeys::currentModifiers.isAnyMouseButtonDown(),
 -                                                             managerOfChosenCommand);
 - }
 - 
 - //==============================================================================
 - // This invokes any command manager commands and deletes the menu window when it is dismissed
 - struct PopupMenuCompletionCallback  : public ModalComponentManager::Callback
 - {
 -     PopupMenuCompletionCallback() = default;
 - 
 -     void modalStateFinished (int result) override
 -     {
 -         if (managerOfChosenCommand != nullptr && result != 0)
 -         {
 -             ApplicationCommandTarget::InvocationInfo info (result);
 -             info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
 - 
 -             managerOfChosenCommand->invoke (info, true);
 -         }
 - 
 -         // (this would be the place to fade out the component, if that's what's required)
 -         component.reset();
 - 
 -         if (PopupMenuSettings::menuWasHiddenBecauseOfAppChange)
 -             return;
 - 
 -         if (auto* focusComponent = Component::getCurrentlyFocusedComponent())
 -         {
 -             const auto focusedIsNotMinimised = [focusComponent]
 -             {
 -                 if (auto* peer = focusComponent->getPeer())
 -                     return ! peer->isMinimised();
 - 
 -                 return false;
 -             }();
 - 
 -             if (focusedIsNotMinimised)
 -             {
 -                 if (auto* topLevel = focusComponent->getTopLevelComponent())
 -                     topLevel->toFront (true);
 - 
 -                 if (focusComponent->isShowing() && ! focusComponent->hasKeyboardFocus (true))
 -                     focusComponent->grabKeyboardFocus();
 -             }
 -         }
 -     }
 - 
 -     ApplicationCommandManager* managerOfChosenCommand = nullptr;
 -     std::unique_ptr<Component> component;
 - 
 -     JUCE_DECLARE_NON_COPYABLE (PopupMenuCompletionCallback)
 - };
 - 
 - int PopupMenu::showWithOptionalCallback (const Options& options,
 -                                          ModalComponentManager::Callback* userCallback,
 -                                          bool canBeModal)
 - {
 -     std::unique_ptr<ModalComponentManager::Callback> userCallbackDeleter (userCallback);
 -     std::unique_ptr<PopupMenuCompletionCallback> callback (new PopupMenuCompletionCallback());
 - 
 -     if (auto* window = createWindow (options, &(callback->managerOfChosenCommand)))
 -     {
 -         callback->component.reset (window);
 - 
 -         PopupMenuSettings::menuWasHiddenBecauseOfAppChange = false;
 - 
 -         window->setVisible (true); // (must be called before enterModalState on Windows to avoid DropShadower confusion)
 -         window->enterModalState (false, userCallbackDeleter.release());
 -         ModalComponentManager::getInstance()->attachCallback (window, callback.release());
 - 
 -         window->toFront (false);  // need to do this after making it modal, or it could
 -                                   // be stuck behind other comps that are already modal..
 - 
 -        #if JUCE_MODAL_LOOPS_PERMITTED
 -         if (userCallback == nullptr && canBeModal)
 -             return window->runModalLoop();
 -        #else
 -         ignoreUnused (canBeModal);
 -         jassert (! (userCallback == nullptr && canBeModal));
 -        #endif
 -     }
 - 
 -     return 0;
 - }
 - 
 - //==============================================================================
 - #if JUCE_MODAL_LOOPS_PERMITTED
 - int PopupMenu::showMenu (const Options& options)
 - {
 -     return showWithOptionalCallback (options, nullptr, true);
 - }
 - #endif
 - 
 - void PopupMenu::showMenuAsync (const Options& options)
 - {
 -     showWithOptionalCallback (options, nullptr, false);
 - }
 - 
 - void PopupMenu::showMenuAsync (const Options& options, ModalComponentManager::Callback* userCallback)
 - {
 -    #if ! JUCE_MODAL_LOOPS_PERMITTED
 -     jassert (userCallback != nullptr);
 -    #endif
 - 
 -     showWithOptionalCallback (options, userCallback, false);
 - }
 - 
 - void PopupMenu::showMenuAsync (const Options& options, std::function<void (int)> userCallback)
 - {
 -     showWithOptionalCallback (options, ModalCallbackFunction::create (userCallback), false);
 - }
 - 
 - //==============================================================================
 - #if JUCE_MODAL_LOOPS_PERMITTED
 - int PopupMenu::show (int itemIDThatMustBeVisible, int minimumWidth,
 -                      int maximumNumColumns, int standardItemHeight,
 -                      ModalComponentManager::Callback* callback)
 - {
 -     return showWithOptionalCallback (Options().withItemThatMustBeVisible (itemIDThatMustBeVisible)
 -                                               .withMinimumWidth (minimumWidth)
 -                                               .withMaximumNumColumns (maximumNumColumns)
 -                                               .withStandardItemHeight (standardItemHeight),
 -                                      callback, true);
 - }
 - 
 - int PopupMenu::showAt (Rectangle<int> screenAreaToAttachTo,
 -                        int itemIDThatMustBeVisible, int minimumWidth,
 -                        int maximumNumColumns, int standardItemHeight,
 -                        ModalComponentManager::Callback* callback)
 - {
 -     return showWithOptionalCallback (Options().withTargetScreenArea (screenAreaToAttachTo)
 -                                               .withItemThatMustBeVisible (itemIDThatMustBeVisible)
 -                                               .withMinimumWidth (minimumWidth)
 -                                               .withMaximumNumColumns (maximumNumColumns)
 -                                               .withStandardItemHeight (standardItemHeight),
 -                                      callback, true);
 - }
 - 
 - int PopupMenu::showAt (Component* componentToAttachTo,
 -                        int itemIDThatMustBeVisible, int minimumWidth,
 -                        int maximumNumColumns, int standardItemHeight,
 -                        ModalComponentManager::Callback* callback)
 - {
 -     auto options = Options().withItemThatMustBeVisible (itemIDThatMustBeVisible)
 -                             .withMinimumWidth (minimumWidth)
 -                             .withMaximumNumColumns (maximumNumColumns)
 -                             .withStandardItemHeight (standardItemHeight);
 - 
 -     if (componentToAttachTo != nullptr)
 -         options = options.withTargetComponent (componentToAttachTo);
 - 
 -     return showWithOptionalCallback (options, callback, true);
 - }
 - #endif
 - 
 - bool JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus()
 - {
 -     auto& windows = HelperClasses::MenuWindow::getActiveWindows();
 -     auto numWindows = windows.size();
 - 
 -     for (int i = numWindows; --i >= 0;)
 -     {
 -         if (auto* pmw = windows[i])
 -         {
 -             pmw->setLookAndFeel (nullptr);
 -             pmw->dismissMenu (nullptr);
 -         }
 -     }
 - 
 -     return numWindows > 0;
 - }
 - 
 - //==============================================================================
 - int PopupMenu::getNumItems() const noexcept
 - {
 -     int num = 0;
 - 
 -     for (auto& mi : items)
 -         if (! mi.isSeparator)
 -             ++num;
 - 
 -     return num;
 - }
 - 
 - bool PopupMenu::containsCommandItem (const int commandID) const
 - {
 -     for (auto& mi : items)
 -         if ((mi.itemID == commandID && mi.commandManager != nullptr)
 -               || (mi.subMenu != nullptr && mi.subMenu->containsCommandItem (commandID)))
 -             return true;
 - 
 -     return false;
 - }
 - 
 - bool PopupMenu::containsAnyActiveItems() const noexcept
 - {
 -     for (auto& mi : items)
 -     {
 -         if (mi.subMenu != nullptr)
 -         {
 -             if (mi.subMenu->containsAnyActiveItems())
 -                 return true;
 -         }
 -         else if (mi.isEnabled)
 -         {
 -             return true;
 -         }
 -     }
 - 
 -     return false;
 - }
 - 
 - void PopupMenu::setLookAndFeel (LookAndFeel* const newLookAndFeel)
 - {
 -     lookAndFeel = newLookAndFeel;
 - }
 - 
 - void PopupMenu::setItem (CustomComponent& c, const Item* itemToUse)
 - {
 -     c.item = itemToUse;
 -     c.repaint();
 - }
 - 
 - //==============================================================================
 - PopupMenu::CustomComponent::CustomComponent (bool autoTrigger)
 -     : triggeredAutomatically (autoTrigger)
 - {
 - }
 - 
 - PopupMenu::CustomComponent::~CustomComponent()
 - {
 - }
 - 
 - void PopupMenu::CustomComponent::setHighlighted (bool shouldBeHighlighted)
 - {
 -     isHighlighted = shouldBeHighlighted;
 -     repaint();
 - }
 - 
 - void PopupMenu::CustomComponent::triggerMenuItem()
 - {
 -     if (auto* mic = findParentComponentOfClass<HelperClasses::ItemComponent>())
 -     {
 -         if (auto* pmw = mic->findParentComponentOfClass<HelperClasses::MenuWindow>())
 -         {
 -             pmw->dismissMenu (&mic->item);
 -         }
 -         else
 -         {
 -             // something must have gone wrong with the component hierarchy if this happens..
 -             jassertfalse;
 -         }
 -     }
 -     else
 -     {
 -         // why isn't this component inside a menu? Not much point triggering the item if
 -         // there's no menu.
 -         jassertfalse;
 -     }
 - }
 - 
 - //==============================================================================
 - PopupMenu::CustomCallback::CustomCallback() {}
 - PopupMenu::CustomCallback::~CustomCallback() {}
 - 
 - //==============================================================================
 - PopupMenu::MenuItemIterator::MenuItemIterator (const PopupMenu& m, bool recurse) : searchRecursively (recurse)
 - {
 -     index.add (0);
 -     menus.add (&m);
 - }
 - 
 - PopupMenu::MenuItemIterator::~MenuItemIterator() = default;
 - 
 - bool PopupMenu::MenuItemIterator::next()
 - {
 -     if (index.size() == 0 || menus.getLast()->items.size() == 0)
 -         return false;
 - 
 -     currentItem = const_cast<PopupMenu::Item*> (&(menus.getLast()->items.getReference (index.getLast())));
 - 
 -     if (searchRecursively && currentItem->subMenu != nullptr)
 -     {
 -         index.add (0);
 -         menus.add (currentItem->subMenu.get());
 -     }
 -     else
 -     {
 -         index.setUnchecked (index.size() - 1, index.getLast() + 1);
 -     }
 - 
 -     while (index.size() > 0 && index.getLast() >= (int) menus.getLast()->items.size())
 -     {
 -         index.removeLast();
 -         menus.removeLast();
 - 
 -         if (index.size() > 0)
 -             index.setUnchecked (index.size() - 1, index.getLast() + 1);
 -     }
 - 
 -     return true;
 - }
 - 
 - PopupMenu::Item& PopupMenu::MenuItemIterator::getItem() const
 - {
 -     jassert (currentItem != nullptr);
 -     return *(currentItem);
 - }
 - 
 - void PopupMenu::LookAndFeelMethods::drawPopupMenuBackground (Graphics&, int, int) {}
 - 
 - void PopupMenu::LookAndFeelMethods::drawPopupMenuItem (Graphics&, const Rectangle<int>&,
 -                                                        bool, bool, bool,
 -                                                        bool, bool,
 -                                                        const String&,
 -                                                        const String&,
 -                                                        const Drawable*,
 -                                                        const Colour*) {}
 - 
 - void PopupMenu::LookAndFeelMethods::drawPopupMenuSectionHeader (Graphics&, const Rectangle<int>&,
 -                                                                 const String&) {}
 - 
 - void PopupMenu::LookAndFeelMethods::drawPopupMenuUpDownArrow (Graphics&, int, int, bool) {}
 - 
 - void PopupMenu::LookAndFeelMethods::getIdealPopupMenuItemSize (const String&, bool, int, int&, int&) {}
 - 
 - int PopupMenu::LookAndFeelMethods::getPopupMenuBorderSize() { return 0; }
 - 
 - } // namespace juce
 
 
  |