The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

345 lines
9.8KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. MenuBarComponent::MenuBarComponent (MenuBarModel* model_)
  19. : model (nullptr),
  20. itemUnderMouse (-1),
  21. currentPopupIndex (-1),
  22. topLevelIndexClicked (0)
  23. {
  24. setRepaintsOnMouseActivity (true);
  25. setWantsKeyboardFocus (false);
  26. setMouseClickGrabsKeyboardFocus (false);
  27. setModel (model_);
  28. }
  29. MenuBarComponent::~MenuBarComponent()
  30. {
  31. setModel (nullptr);
  32. Desktop::getInstance().removeGlobalMouseListener (this);
  33. }
  34. MenuBarModel* MenuBarComponent::getModel() const noexcept
  35. {
  36. return model;
  37. }
  38. void MenuBarComponent::setModel (MenuBarModel* const newModel)
  39. {
  40. if (model != newModel)
  41. {
  42. if (model != nullptr)
  43. model->removeListener (this);
  44. model = newModel;
  45. if (model != nullptr)
  46. model->addListener (this);
  47. repaint();
  48. menuBarItemsChanged (nullptr);
  49. }
  50. }
  51. //==============================================================================
  52. void MenuBarComponent::paint (Graphics& g)
  53. {
  54. const bool isMouseOverBar = currentPopupIndex >= 0 || itemUnderMouse >= 0 || isMouseOver();
  55. getLookAndFeel().drawMenuBarBackground (g,
  56. getWidth(),
  57. getHeight(),
  58. isMouseOverBar,
  59. *this);
  60. if (model != nullptr)
  61. {
  62. for (int i = 0; i < menuNames.size(); ++i)
  63. {
  64. Graphics::ScopedSaveState ss (g);
  65. g.setOrigin (xPositions [i], 0);
  66. g.reduceClipRegion (0, 0, xPositions[i + 1] - xPositions[i], getHeight());
  67. getLookAndFeel().drawMenuBarItem (g,
  68. xPositions[i + 1] - xPositions[i],
  69. getHeight(),
  70. i,
  71. menuNames[i],
  72. i == itemUnderMouse,
  73. i == currentPopupIndex,
  74. isMouseOverBar,
  75. *this);
  76. }
  77. }
  78. }
  79. void MenuBarComponent::resized()
  80. {
  81. xPositions.clear();
  82. int x = 0;
  83. xPositions.add (x);
  84. for (int i = 0; i < menuNames.size(); ++i)
  85. {
  86. x += getLookAndFeel().getMenuBarItemWidth (*this, i, menuNames[i]);
  87. xPositions.add (x);
  88. }
  89. }
  90. int MenuBarComponent::getItemAt (const Point<int>& p)
  91. {
  92. for (int i = 0; i < xPositions.size(); ++i)
  93. if (p.x >= xPositions[i] && p.x < xPositions[i + 1])
  94. return reallyContains (p, true) ? i : -1;
  95. return -1;
  96. }
  97. void MenuBarComponent::repaintMenuItem (int index)
  98. {
  99. if (isPositiveAndBelow (index, xPositions.size()))
  100. {
  101. const int x1 = xPositions [index];
  102. const int x2 = xPositions [index + 1];
  103. repaint (x1 - 2, 0, x2 - x1 + 4, getHeight());
  104. }
  105. }
  106. void MenuBarComponent::setItemUnderMouse (const int index)
  107. {
  108. if (itemUnderMouse != index)
  109. {
  110. repaintMenuItem (itemUnderMouse);
  111. itemUnderMouse = index;
  112. repaintMenuItem (itemUnderMouse);
  113. }
  114. }
  115. void MenuBarComponent::setOpenItem (int index)
  116. {
  117. if (currentPopupIndex != index)
  118. {
  119. repaintMenuItem (currentPopupIndex);
  120. currentPopupIndex = index;
  121. repaintMenuItem (currentPopupIndex);
  122. Desktop& desktop = Desktop::getInstance();
  123. if (index >= 0)
  124. desktop.addGlobalMouseListener (this);
  125. else
  126. desktop.removeGlobalMouseListener (this);
  127. }
  128. }
  129. void MenuBarComponent::updateItemUnderMouse (const Point<int>& p)
  130. {
  131. setItemUnderMouse (getItemAt (p));
  132. }
  133. void MenuBarComponent::showMenu (int index)
  134. {
  135. if (index != currentPopupIndex)
  136. {
  137. PopupMenu::dismissAllActiveMenus();
  138. menuBarItemsChanged (nullptr);
  139. setOpenItem (index);
  140. setItemUnderMouse (index);
  141. if (index >= 0)
  142. {
  143. PopupMenu m (model->getMenuForIndex (itemUnderMouse,
  144. menuNames [itemUnderMouse]));
  145. if (m.lookAndFeel == nullptr)
  146. m.setLookAndFeel (&getLookAndFeel());
  147. const Rectangle<int> itemPos (xPositions [index], 0, xPositions [index + 1] - xPositions [index], getHeight());
  148. m.showMenuAsync (PopupMenu::Options().withTargetComponent (this)
  149. .withTargetScreenArea (localAreaToGlobal (itemPos))
  150. .withMinimumWidth (itemPos.getWidth()),
  151. ModalCallbackFunction::forComponent (menuBarMenuDismissedCallback, this, index));
  152. }
  153. }
  154. }
  155. void MenuBarComponent::menuBarMenuDismissedCallback (int result, MenuBarComponent* bar, int topLevelIndex)
  156. {
  157. if (bar != nullptr)
  158. bar->menuDismissed (topLevelIndex, result);
  159. }
  160. void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId)
  161. {
  162. topLevelIndexClicked = topLevelIndex;
  163. postCommandMessage (itemId);
  164. }
  165. void MenuBarComponent::handleCommandMessage (int commandId)
  166. {
  167. const Point<int> mousePos (getMouseXYRelative());
  168. updateItemUnderMouse (mousePos);
  169. if (currentPopupIndex == topLevelIndexClicked)
  170. setOpenItem (-1);
  171. if (commandId != 0 && model != nullptr)
  172. model->menuItemSelected (commandId, topLevelIndexClicked);
  173. }
  174. //==============================================================================
  175. void MenuBarComponent::mouseEnter (const MouseEvent& e)
  176. {
  177. if (e.eventComponent == this)
  178. updateItemUnderMouse (e.getPosition());
  179. }
  180. void MenuBarComponent::mouseExit (const MouseEvent& e)
  181. {
  182. if (e.eventComponent == this)
  183. updateItemUnderMouse (e.getPosition());
  184. }
  185. void MenuBarComponent::mouseDown (const MouseEvent& e)
  186. {
  187. if (currentPopupIndex < 0)
  188. {
  189. const MouseEvent e2 (e.getEventRelativeTo (this));
  190. updateItemUnderMouse (e2.getPosition());
  191. currentPopupIndex = -2;
  192. showMenu (itemUnderMouse);
  193. }
  194. }
  195. void MenuBarComponent::mouseDrag (const MouseEvent& e)
  196. {
  197. const MouseEvent e2 (e.getEventRelativeTo (this));
  198. const int item = getItemAt (e2.getPosition());
  199. if (item >= 0)
  200. showMenu (item);
  201. }
  202. void MenuBarComponent::mouseUp (const MouseEvent& e)
  203. {
  204. const MouseEvent e2 (e.getEventRelativeTo (this));
  205. updateItemUnderMouse (e2.getPosition());
  206. if (itemUnderMouse < 0 && getLocalBounds().contains (e2.x, e2.y))
  207. {
  208. setOpenItem (-1);
  209. PopupMenu::dismissAllActiveMenus();
  210. }
  211. }
  212. void MenuBarComponent::mouseMove (const MouseEvent& e)
  213. {
  214. const MouseEvent e2 (e.getEventRelativeTo (this));
  215. if (lastMousePos != e2.getPosition())
  216. {
  217. if (currentPopupIndex >= 0)
  218. {
  219. const int item = getItemAt (e2.getPosition());
  220. if (item >= 0)
  221. showMenu (item);
  222. }
  223. else
  224. {
  225. updateItemUnderMouse (e2.getPosition());
  226. }
  227. lastMousePos = e2.getPosition();
  228. }
  229. }
  230. bool MenuBarComponent::keyPressed (const KeyPress& key)
  231. {
  232. bool used = false;
  233. const int numMenus = menuNames.size();
  234. const int currentIndex = jlimit (0, menuNames.size() - 1, currentPopupIndex);
  235. if (key.isKeyCode (KeyPress::leftKey))
  236. {
  237. showMenu ((currentIndex + numMenus - 1) % numMenus);
  238. used = true;
  239. }
  240. else if (key.isKeyCode (KeyPress::rightKey))
  241. {
  242. showMenu ((currentIndex + 1) % numMenus);
  243. used = true;
  244. }
  245. return used;
  246. }
  247. void MenuBarComponent::menuBarItemsChanged (MenuBarModel* /*menuBarModel*/)
  248. {
  249. StringArray newNames;
  250. if (model != nullptr)
  251. newNames = model->getMenuBarNames();
  252. if (newNames != menuNames)
  253. {
  254. menuNames = newNames;
  255. repaint();
  256. resized();
  257. }
  258. }
  259. void MenuBarComponent::menuCommandInvoked (MenuBarModel* /*menuBarModel*/,
  260. const ApplicationCommandTarget::InvocationInfo& info)
  261. {
  262. if (model == nullptr || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0)
  263. return;
  264. for (int i = 0; i < menuNames.size(); ++i)
  265. {
  266. const PopupMenu menu (model->getMenuForIndex (i, menuNames [i]));
  267. if (menu.containsCommandItem (info.commandID))
  268. {
  269. setItemUnderMouse (i);
  270. startTimer (200);
  271. break;
  272. }
  273. }
  274. }
  275. void MenuBarComponent::timerCallback()
  276. {
  277. stopTimer();
  278. updateItemUnderMouse (getMouseXYRelative());
  279. }