text: /*
    ==============================================================================
  
     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
  {
  
  struct ModalComponentManager::ModalItem  : public ComponentMovementWatcher
  {
      ModalItem (Component* comp, bool shouldAutoDelete)
          : ComponentMovementWatcher (comp),
            component (comp), autoDelete (shouldAutoDelete)
      {
          jassert (comp != nullptr);
      }
  
      ~ModalItem() override
      {
          if (autoDelete)
              std::unique_ptr<Component> componentDeleter (component);
      }
  
      void componentMovedOrResized (bool, bool) override {}
  
      using ComponentMovementWatcher::componentMovedOrResized;
  
      void componentPeerChanged() override
      {
          componentVisibilityChanged();
      }
  
      void componentVisibilityChanged() override
      {
          if (! component->isShowing())
              cancel();
      }
  
      using ComponentMovementWatcher::componentVisibilityChanged;
  
      void componentBeingDeleted (Component& comp) override
      {
          ComponentMovementWatcher::componentBeingDeleted (comp);
  
          if (component == &comp || comp.isParentOf (component))
          {
              autoDelete = false;
              cancel();
          }
      }
  
      void cancel()
      {
          if (isActive)
          {
              isActive = false;
  
              if (auto* mcm = ModalComponentManager::getInstanceWithoutCreating())
                  mcm->triggerAsyncUpdate();
          }
      }
  
      Component* component;
      OwnedArray<Callback> callbacks;
      int returnValue = 0;
      bool isActive = true, autoDelete;
  
      JUCE_DECLARE_NON_COPYABLE (ModalItem)
  };
  
  //==============================================================================
  ModalComponentManager::ModalComponentManager()
  {
  }
  
  ModalComponentManager::~ModalComponentManager()
  {
      stack.clear();
      clearSingletonInstance();
  }
  
  JUCE_IMPLEMENT_SINGLETON (ModalComponentManager)
  
  
  //==============================================================================
  void ModalComponentManager::startModal (Component* component, bool autoDelete)
  {
      if (component != nullptr)
          stack.add (new ModalItem (component, autoDelete));
  }
  
  void ModalComponentManager::attachCallback (Component* component, Callback* callback)
  {
      if (callback != nullptr)
      {
          std::unique_ptr<Callback> callbackDeleter (callback);
  
          for (int i = stack.size(); --i >= 0;)
          {
              auto* item = stack.getUnchecked(i);
  
              if (item->component == component)
              {
                  item->callbacks.add (callback);
                  callbackDeleter.release();
                  break;
              }
          }
      }
  }
  
  void ModalComponentManager::endModal (Component* component)
  {
      for (int i = stack.size(); --i >= 0;)
      {
          auto* item = stack.getUnchecked(i);
  
          if (item->component == component)
              item->cancel();
      }
  }
  
  void ModalComponentManager::endModal (Component* component, int returnValue)
  {
      for (int i = stack.size(); --i >= 0;)
      {
          auto* item = stack.getUnchecked(i);
  
          if (item->component == component)
          {
              item->returnValue = returnValue;
              item->cancel();
          }
      }
  }
  
  int ModalComponentManager::getNumModalComponents() const
  {
      int n = 0;
  
      for (auto* item : stack)
          if (item->isActive)
              ++n;
  
      return n;
  }
  
  Component* ModalComponentManager::getModalComponent (int index) const
  {
      int n = 0;
  
      for (int i = stack.size(); --i >= 0;)
      {
          auto* item = stack.getUnchecked(i);
  
          if (item->isActive)
              if (n++ == index)
                  return item->component;
      }
  
      return nullptr;
  }
  
  bool ModalComponentManager::isModal (const Component* comp) const
  {
      for (auto* item : stack)
          if (item->isActive && item->component == comp)
              return true;
  
      return false;
  }
  
  bool ModalComponentManager::isFrontModalComponent (const Component* comp) const
  {
      return comp == getModalComponent (0);
  }
  
  void ModalComponentManager::handleAsyncUpdate()
  {
      for (int i = stack.size(); --i >= 0;)
      {
          auto* item = stack.getUnchecked(i);
  
          if (! item->isActive)
          {
              std::unique_ptr<ModalItem> deleter (stack.removeAndReturn (i));
              Component::SafePointer<Component> compToDelete (item->autoDelete ? item->component : nullptr);
  
              for (int j = item->callbacks.size(); --j >= 0;)
                  item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue);
  
              compToDelete.deleteAndZero();
          }
      }
  }
  
  void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus)
  {
      ComponentPeer* lastOne = nullptr;
  
      for (int i = 0; i < getNumModalComponents(); ++i)
      {
          auto* c = getModalComponent (i);
  
          if (c == nullptr)
              break;
  
          if (auto* peer = c->getPeer())
          {
              if (peer != lastOne)
              {
                  if (lastOne == nullptr)
                  {
                      peer->toFront (topOneShouldGrabFocus);
  
                      if (topOneShouldGrabFocus)
                          peer->grabFocus();
                  }
                  else
                  {
                      peer->toBehind (lastOne);
                  }
  
                  lastOne = peer;
              }
          }
      }
  }
  
  bool ModalComponentManager::cancelAllModalComponents()
  {
      auto numModal = getNumModalComponents();
  
      for (int i = numModal; --i >= 0;)
          if (auto* c = getModalComponent(i))
              c->exitModalState (0);
  
      return numModal > 0;
  }
  
  //==============================================================================
  #if JUCE_MODAL_LOOPS_PERMITTED
  int ModalComponentManager::runEventLoopForCurrentComponent()
  {
      // This can only be run from the message thread!
      JUCE_ASSERT_MESSAGE_THREAD
  
      int returnValue = 0;
  
      if (auto* currentlyModal = getModalComponent (0))
      {
          FocusRestorer focusRestorer;
          bool finished = false;
  
          attachCallback (currentlyModal, ModalCallbackFunction::create ([&] (int r) { returnValue = r; finished = true; }));
  
          JUCE_TRY
          {
              while (! finished)
              {
                  if  (! MessageManager::getInstance()->runDispatchLoopUntil (20))
                      break;
              }
          }
          JUCE_CATCH_EXCEPTION
      }
  
      return returnValue;
  }
  #endif
  
  //==============================================================================
  struct LambdaCallback  : public ModalComponentManager::Callback
  {
      LambdaCallback (std::function<void (int)> fn) noexcept  : function (fn)  {}
  
      void modalStateFinished (int result) override
      {
          if (function != nullptr)
              function (result);
      }
  
      std::function<void (int)> function;
  
      JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaCallback)
  };
  
  ModalComponentManager::Callback* ModalCallbackFunction::create (std::function<void (int)> f)
  {
      return new LambdaCallback (f);
  }
  
  } // namespace juce
  