text: /*
    ==============================================================================
  
     This file is part of the JUCE examples.
     Copyright (c) 2017 - ROLI Ltd.
  
     The code included in this file is provided under the terms of the ISC license
     http://www.isc.org/downloads/software-support-policy/isc-license. Permission
     To use, copy, modify, and/or distribute this software for any purpose with or
     without fee is hereby granted provided that the above copyright notice and
     this permission notice appear in all copies.
  
     THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
     WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
     PURPOSE, ARE DISCLAIMED.
  
    ==============================================================================
  */
  
  /*******************************************************************************
   The block below describes the properties of this PIP. A PIP is a short snippet
   of code that can be read by the Projucer and used to generate a JUCE project.
  
   BEGIN_JUCE_PIP_METADATA
  
   name:             MenusDemo
   version:          1.0.0
   vendor:           JUCE
   website:          http://juce.com
   description:      Showcases menu features.
  
   dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
                     juce_gui_basics, juce_gui_extra
   exporters:        xcode_mac, vs2017, linux_make, androidstudio, xcode_iphone
  
   type:             Component
   mainClass:        MenusDemo
  
   useLocalCopy:     1
  
   END_JUCE_PIP_METADATA
  
  *******************************************************************************/
  
  #pragma once
  
  #include "../Assets/DemoUtilities.h"
  
  //==============================================================================
  /**
      This struct contains a header component that will be used when the burger menu
      is enabled. It contains an icon that can be used to show the side panel containing
      the menu.
  */
  struct BurgerMenuHeader  : public Component
  {
      BurgerMenuHeader (SidePanel& sp)
          : sidePanel (sp)
      {
          static const unsigned char burgerMenuPathData[]
              = { 110,109,0,0,128,64,0,0,32,65,108,0,0,224,65,0,0,32,65,98,254,212,232,65,0,0,32,65,0,0,240,65,252,
                  169,17,65,0,0,240,65,0,0,0,65,98,0,0,240,65,8,172,220,64,254,212,232,65,0,0,192,64,0,0,224,65,0,0,
                  192,64,108,0,0,128,64,0,0,192,64,98,16,88,57,64,0,0,192,64,0,0,0,64,8,172,220,64,0,0,0,64,0,0,0,65,
                  98,0,0,0,64,252,169,17,65,16,88,57,64,0,0,32,65,0,0,128,64,0,0,32,65,99,109,0,0,224,65,0,0,96,65,108,
                  0,0,128,64,0,0,96,65,98,16,88,57,64,0,0,96,65,0,0,0,64,4,86,110,65,0,0,0,64,0,0,128,65,98,0,0,0,64,
                  254,212,136,65,16,88,57,64,0,0,144,65,0,0,128,64,0,0,144,65,108,0,0,224,65,0,0,144,65,98,254,212,232,
                  65,0,0,144,65,0,0,240,65,254,212,136,65,0,0,240,65,0,0,128,65,98,0,0,240,65,4,86,110,65,254,212,232,
                  65,0,0,96,65,0,0,224,65,0,0,96,65,99,109,0,0,224,65,0,0,176,65,108,0,0,128,64,0,0,176,65,98,16,88,57,
                  64,0,0,176,65,0,0,0,64,2,43,183,65,0,0,0,64,0,0,192,65,98,0,0,0,64,254,212,200,65,16,88,57,64,0,0,208,
                  65,0,0,128,64,0,0,208,65,108,0,0,224,65,0,0,208,65,98,254,212,232,65,0,0,208,65,0,0,240,65,254,212,
                  200,65,0,0,240,65,0,0,192,65,98,0,0,240,65,2,43,183,65,254,212,232,65,0,0,176,65,0,0,224,65,0,0,176,
                  65,99,101,0,0 };
  
          Path p;
          p.loadPathFromData (burgerMenuPathData, sizeof (burgerMenuPathData));
          burgerButton.setShape (p, true, true, false);
  
          burgerButton.onClick = [this] { showOrHide(); };
          addAndMakeVisible (burgerButton);
      }
  
      ~BurgerMenuHeader()
      {
          sidePanel.showOrHide (false);
      }
  
  private:
      void paint (Graphics& g) override
      {
          auto titleBarBackgroundColour = getLookAndFeel().findColour (ResizableWindow::backgroundColourId)
                                                          .darker();
  
          g.setColour (titleBarBackgroundColour);
          g.fillRect (getLocalBounds());
      }
  
      void resized() override
      {
          auto r = getLocalBounds();
  
          burgerButton.setBounds (r.removeFromRight (40).withSizeKeepingCentre (20, 20));
  
          titleLabel.setFont (Font (getHeight() * 0.5f, Font::plain));
          titleLabel.setBounds (r);
      }
  
      void showOrHide()
      {
          sidePanel.showOrHide (! sidePanel.isPanelShowing());
      }
  
      SidePanel& sidePanel;
  
      Label titleLabel         { "titleLabel", "JUCE Demo" };
      ShapeButton burgerButton { "burgerButton", Colours::lightgrey, Colours::lightgrey, Colours::white };
  
      JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BurgerMenuHeader)
  };
  
  //==============================================================================
  class MenusDemo    : public Component,
                       public ApplicationCommandTarget,
                       public MenuBarModel
  {
  public:
      //==============================================================================
      /** A list of the commands that this demo responds to. */
      enum CommandIDs
      {
          menuPositionInsideWindow = 1,
          menuPositionGlobalMenuBar,
          menuPositionBurgerMenu,
          outerColourRed,
          outerColourGreen,
          outerColourBlue,
          innerColourRed,
          innerColourGreen,
          innerColourBlue
      };
  
      //==============================================================================
      /** Represents the possible menu positions. */
      enum class MenuBarPosition
      {
          window,
          global,
          burger
      };
  
      //==============================================================================
      MenusDemo()
      {
          menuBar.reset (new MenuBarComponent (this));
          addAndMakeVisible (menuBar.get());
          setApplicationCommandManagerToWatch (&commandManager);
          commandManager.registerAllCommandsForTarget (this);
  
          // this lets the command manager use keypresses that arrive in our window to send out commands
          addKeyListener (commandManager.getKeyMappings());
  
          addChildComponent (menuHeader);
          addAndMakeVisible (outerCommandTarget);
          addAndMakeVisible (sidePanel);
  
          setSize (500, 500);
      }
  
      ~MenusDemo()
      {
         #if JUCE_MAC
          MenuBarModel::setMacMainMenu (nullptr);
         #endif
      }
  
      void resized() override
      {
          auto b = getLocalBounds();
  
          if (menuBarPosition == MenuBarPosition::window)
          {
              menuBar->setBounds (b.removeFromTop (LookAndFeel::getDefaultLookAndFeel()
                                                               .getDefaultMenuBarHeight()));
          }
          else if (menuBarPosition == MenuBarPosition::burger)
          {
              menuHeader.setBounds (b.removeFromTop (40));
          }
  
          outerCommandTarget.setBounds (b);
      }
  
      //==============================================================================
      StringArray getMenuBarNames() override
      {
          return { "Menu Position", "Outer Colour", "Inner Colour" };
      }
  
      PopupMenu getMenuForIndex (int menuIndex, const String& /*menuName*/) override
      {
          PopupMenu menu;
  
          if (menuIndex == 0)
          {
              menu.addCommandItem (&commandManager, CommandIDs::menuPositionInsideWindow);
             #if JUCE_MAC
              menu.addCommandItem (&commandManager, CommandIDs::menuPositionGlobalMenuBar);
             #endif
              menu.addCommandItem (&commandManager, CommandIDs::menuPositionBurgerMenu);
          }
          else if (menuIndex == 1)
          {
              menu.addCommandItem (&commandManager, CommandIDs::outerColourRed);
              menu.addCommandItem (&commandManager, CommandIDs::outerColourGreen);
              menu.addCommandItem (&commandManager, CommandIDs::outerColourBlue);
          }
          else if (menuIndex == 2)
          {
              menu.addCommandItem (&commandManager, CommandIDs::innerColourRed);
              menu.addCommandItem (&commandManager, CommandIDs::innerColourGreen);
              menu.addCommandItem (&commandManager, CommandIDs::innerColourBlue);
          }
  
          return menu;
      }
  
      void menuItemSelected (int /*menuItemID*/, int /*topLevelMenuIndex*/) override {}
  
      //==============================================================================
      // The following methods implement the ApplicationCommandTarget interface, allowing
      // this window to publish a set of actions it can perform, and which can be mapped
      // onto menus, keypresses, etc.
  
      ApplicationCommandTarget* getNextCommandTarget() override
      {
          return &outerCommandTarget;
      }
  
      void getAllCommands (Array<CommandID>& c) override
      {
          Array<CommandID> commands { CommandIDs::menuPositionInsideWindow,
                                      CommandIDs::menuPositionGlobalMenuBar,
                                      CommandIDs::menuPositionBurgerMenu };
          c.addArray (commands);
      }
  
      void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
      {
          switch (commandID)
          {
              case CommandIDs::menuPositionInsideWindow:
                  result.setInfo ("Inside Window", "Places the menu bar inside the application window", "Menu", 0);
                  result.setTicked (menuBarPosition == MenuBarPosition::window);
                  result.addDefaultKeypress ('w', ModifierKeys::shiftModifier);
                  break;
              case CommandIDs::menuPositionGlobalMenuBar:
                  result.setInfo ("Global", "Uses a global menu bar", "Menu", 0);
                  result.setTicked (menuBarPosition == MenuBarPosition::global);
                  result.addDefaultKeypress ('g', ModifierKeys::shiftModifier);
                  break;
              case CommandIDs::menuPositionBurgerMenu:
                  result.setInfo ("Burger Menu", "Uses a burger menu", "Menu", 0);
                  result.setTicked (menuBarPosition == MenuBarPosition::burger);
                  result.addDefaultKeypress ('b', ModifierKeys::shiftModifier);
                  break;
              default:
                  break;
          }
      }
  
      bool perform (const InvocationInfo& info) override
      {
          switch (info.commandID)
          {
              case CommandIDs::menuPositionInsideWindow:
                  setMenuBarPosition (MenuBarPosition::window);
                  break;
              case CommandIDs::menuPositionGlobalMenuBar:
                  setMenuBarPosition (MenuBarPosition::global);
                  break;
              case CommandIDs::menuPositionBurgerMenu:
                  setMenuBarPosition (MenuBarPosition::burger);
                  break;
              default:
                  return false;
          }
  
          return true;
      }
  
      void setMenuBarPosition (MenuBarPosition newPosition)
      {
          if (menuBarPosition != newPosition)
          {
              menuBarPosition = newPosition;
  
              if (menuBarPosition != MenuBarPosition::burger)
                  sidePanel.showOrHide (false);
  
             #if JUCE_MAC
              MenuBarModel::setMacMainMenu (menuBarPosition == MenuBarPosition::global ? this : nullptr);
             #endif
  
              menuBar->setVisible   (menuBarPosition == MenuBarPosition::window);
              burgerMenu.setModel   (menuBarPosition == MenuBarPosition::burger ? this : nullptr);
              menuHeader.setVisible (menuBarPosition == MenuBarPosition::burger);
  
              sidePanel.setContent  (menuBarPosition == MenuBarPosition::burger ? &burgerMenu : nullptr, false);
              menuItemsChanged();
  
              resized();
          }
      }
  
  private:
      ApplicationCommandManager commandManager;
  
      std::unique_ptr<MenuBarComponent> menuBar;
      MenuBarPosition menuBarPosition = MenuBarPosition::window;
  
      SidePanel sidePanel { "Menu", 300, false };
  
      BurgerMenuComponent burgerMenu;
      BurgerMenuHeader menuHeader { sidePanel };
  
      //==============================================================================
      /**
          Command messages that aren't handled in the main component will be passed
          to this class to respond to.
      */
      class OuterCommandTarget    : public Component,
                                    public ApplicationCommandTarget
      {
      public:
          OuterCommandTarget (ApplicationCommandManager& m)
              : commandManager (m),
                innerCommandTarget (commandManager)
          {
              commandManager.registerAllCommandsForTarget (this);
  
              addAndMakeVisible (innerCommandTarget);
          }
  
          void resized() override
          {
              innerCommandTarget.setBounds (getLocalBounds().reduced (50));
          }
  
          void paint (Graphics& g) override
          {
              g.fillAll (currentColour);
          }
  
          //==============================================================================
          ApplicationCommandTarget* getNextCommandTarget() override
          {
              return &innerCommandTarget;
          }
  
          void getAllCommands (Array<CommandID>& c) override
          {
              Array<CommandID> commands { CommandIDs::outerColourRed,
                                          CommandIDs::outerColourGreen,
                                          CommandIDs::outerColourBlue };
  
              c.addArray (commands);
          }
  
          void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
          {
              switch (commandID)
              {
                  case CommandIDs::outerColourRed:
                      result.setInfo ("Red", "Sets the outer colour to red", "Outer", 0);
                      result.setTicked (currentColour == Colours::red);
                      result.addDefaultKeypress ('r', ModifierKeys::commandModifier);
                      break;
                  case CommandIDs::outerColourGreen:
                      result.setInfo ("Green", "Sets the outer colour to green", "Outer", 0);
                      result.setTicked (currentColour == Colours::green);
                      result.addDefaultKeypress ('g', ModifierKeys::commandModifier);
                      break;
                  case CommandIDs::outerColourBlue:
                      result.setInfo ("Blue", "Sets the outer colour to blue", "Outer", 0);
                      result.setTicked (currentColour == Colours::blue);
                      result.addDefaultKeypress ('b', ModifierKeys::commandModifier);
                      break;
                  default:
                      break;
              }
          }
  
          bool perform (const InvocationInfo& info) override
          {
              switch (info.commandID)
              {
                  case CommandIDs::outerColourRed:
                      currentColour = Colours::red;
                      break;
                  case CommandIDs::outerColourGreen:
                      currentColour = Colours::green;
                      break;
                  case CommandIDs::outerColourBlue:
                      currentColour = Colours::blue;
                      break;
                  default:
                      return false;
              }
  
              repaint();
              return true;
          }
  
      private:
          //==============================================================================
          /**
              Command messages that aren't handled in the OuterCommandTarget will be passed
              to this class to respond to.
          */
          struct InnerCommandTarget    : public Component,
                                         public ApplicationCommandTarget
          {
              InnerCommandTarget (ApplicationCommandManager& m)
                  : commandManager (m)
              {
                  commandManager.registerAllCommandsForTarget (this);
              }
  
              void paint (Graphics& g) override
              {
                  g.fillAll (currentColour);
              }
  
              //==============================================================================
              ApplicationCommandTarget* getNextCommandTarget() override
              {
                  // this will return the next parent component that is an ApplicationCommandTarget
                  return findFirstTargetParentComponent();
              }
  
              void getAllCommands (Array<CommandID>& c) override
              {
                  Array<CommandID> commands { CommandIDs::innerColourRed,
                                              CommandIDs::innerColourGreen,
                                              CommandIDs::innerColourBlue };
  
                  c.addArray (commands);
              }
  
              void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
              {
                  switch (commandID)
                  {
                      case CommandIDs::innerColourRed:
                          result.setInfo ("Red", "Sets the inner colour to red", "Inner", 0);
                          result.setTicked (currentColour == Colours::red);
                          result.addDefaultKeypress ('r', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
                          break;
                      case CommandIDs::innerColourGreen:
                          result.setInfo ("Green", "Sets the inner colour to green", "Inner", 0);
                          result.setTicked (currentColour == Colours::green);
                          result.addDefaultKeypress ('g', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
                          break;
                      case CommandIDs::innerColourBlue:
                          result.setInfo ("Blue", "Sets the inner colour to blue", "Inner", 0);
                          result.setTicked (currentColour == Colours::blue);
                          result.addDefaultKeypress ('b', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
                          break;
                      default:
                          break;
                  }
              }
  
              bool perform (const InvocationInfo& info) override
              {
                  switch (info.commandID)
                  {
                      case CommandIDs::innerColourRed:
                          currentColour = Colours::red;
                          break;
                      case CommandIDs::innerColourGreen:
                          currentColour = Colours::green;
                          break;
                      case CommandIDs::innerColourBlue:
                          currentColour = Colours::blue;
                          break;
                      default:
                          return false;
                  }
  
                  repaint();
                  return true;
              }
  
              ApplicationCommandManager& commandManager;
  
              Colour currentColour { Colours::blue };
          };
  
          ApplicationCommandManager& commandManager;
          InnerCommandTarget innerCommandTarget;
  
          Colour currentColour { Colours::red };
      };
  
      OuterCommandTarget outerCommandTarget { commandManager };
  
      //==============================================================================
      JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenusDemo)
  };
  