2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
30 #include "juce_MenuBarComponent.h"
31 #include "../juce_Desktop.h"
32 #include "../lookandfeel/juce_LookAndFeel.h"
35 //==============================================================================
36 MenuBarComponent::MenuBarComponent (MenuBarModel
* model_
)
39 currentPopupIndex (-1),
40 topLevelIndexClicked (0),
44 setRepaintsOnMouseActivity (true);
45 setWantsKeyboardFocus (false);
46 setMouseClickGrabsKeyboardFocus (false);
51 MenuBarComponent::~MenuBarComponent()
54 Desktop::getInstance().removeGlobalMouseListener (this);
57 MenuBarModel
* MenuBarComponent::getModel() const noexcept
62 void MenuBarComponent::setModel (MenuBarModel
* const newModel
)
64 if (model
!= newModel
)
67 model
->removeListener (this);
72 model
->addListener (this);
75 menuBarItemsChanged (nullptr);
79 //==============================================================================
80 void MenuBarComponent::paint (Graphics
& g
)
82 const bool isMouseOverBar
= currentPopupIndex
>= 0 || itemUnderMouse
>= 0 || isMouseOver();
84 getLookAndFeel().drawMenuBarBackground (g
,
92 for (int i
= 0; i
< menuNames
.size(); ++i
)
94 Graphics::ScopedSaveState
ss (g
);
96 g
.setOrigin (xPositions
[i
], 0);
97 g
.reduceClipRegion (0, 0, xPositions
[i
+ 1] - xPositions
[i
], getHeight());
99 getLookAndFeel().drawMenuBarItem (g
,
100 xPositions
[i
+ 1] - xPositions
[i
],
105 i
== currentPopupIndex
,
112 void MenuBarComponent::resized()
118 for (int i
= 0; i
< menuNames
.size(); ++i
)
120 x
+= getLookAndFeel().getMenuBarItemWidth (*this, i
, menuNames
[i
]);
125 int MenuBarComponent::getItemAt (const int x
, const int y
)
127 for (int i
= 0; i
< xPositions
.size(); ++i
)
128 if (x
>= xPositions
[i
] && x
< xPositions
[i
+ 1])
129 return reallyContains (Point
<int> (x
, y
), true) ? i
: -1;
134 void MenuBarComponent::repaintMenuItem (int index
)
136 if (isPositiveAndBelow (index
, xPositions
.size()))
138 const int x1
= xPositions
[index
];
139 const int x2
= xPositions
[index
+ 1];
141 repaint (x1
- 2, 0, x2
- x1
+ 4, getHeight());
145 void MenuBarComponent::setItemUnderMouse (const int index
)
147 if (itemUnderMouse
!= index
)
149 repaintMenuItem (itemUnderMouse
);
150 itemUnderMouse
= index
;
151 repaintMenuItem (itemUnderMouse
);
155 void MenuBarComponent::setOpenItem (int index
)
157 if (currentPopupIndex
!= index
)
159 repaintMenuItem (currentPopupIndex
);
160 currentPopupIndex
= index
;
161 repaintMenuItem (currentPopupIndex
);
164 Desktop::getInstance().addGlobalMouseListener (this);
166 Desktop::getInstance().removeGlobalMouseListener (this);
170 void MenuBarComponent::updateItemUnderMouse (int x
, int y
)
172 setItemUnderMouse (getItemAt (x
, y
));
175 void MenuBarComponent::showMenu (int index
)
177 if (index
!= currentPopupIndex
)
179 PopupMenu::dismissAllActiveMenus();
180 menuBarItemsChanged (nullptr);
183 setItemUnderMouse (index
);
187 PopupMenu
m (model
->getMenuForIndex (itemUnderMouse
,
188 menuNames
[itemUnderMouse
]));
190 if (m
.lookAndFeel
== nullptr)
191 m
.setLookAndFeel (&getLookAndFeel());
193 const Rectangle
<int> itemPos (xPositions
[index
], 0, xPositions
[index
+ 1] - xPositions
[index
], getHeight());
195 m
.showMenuAsync (PopupMenu::Options().withTargetComponent (this)
196 .withTargetScreenArea (localAreaToGlobal (itemPos
))
197 .withMinimumWidth (itemPos
.getWidth()),
198 ModalCallbackFunction::forComponent (menuBarMenuDismissedCallback
, this, index
));
203 void MenuBarComponent::menuBarMenuDismissedCallback (int result
, MenuBarComponent
* bar
, int topLevelIndex
)
206 bar
->menuDismissed (topLevelIndex
, result
);
209 void MenuBarComponent::menuDismissed (int topLevelIndex
, int itemId
)
211 topLevelIndexClicked
= topLevelIndex
;
212 postCommandMessage (itemId
);
215 void MenuBarComponent::handleCommandMessage (int commandId
)
217 const Point
<int> mousePos (getMouseXYRelative());
218 updateItemUnderMouse (mousePos
.getX(), mousePos
.getY());
220 if (currentPopupIndex
== topLevelIndexClicked
)
223 if (commandId
!= 0 && model
!= nullptr)
224 model
->menuItemSelected (commandId
, topLevelIndexClicked
);
227 //==============================================================================
228 void MenuBarComponent::mouseEnter (const MouseEvent
& e
)
230 if (e
.eventComponent
== this)
231 updateItemUnderMouse (e
.x
, e
.y
);
234 void MenuBarComponent::mouseExit (const MouseEvent
& e
)
236 if (e
.eventComponent
== this)
237 updateItemUnderMouse (e
.x
, e
.y
);
240 void MenuBarComponent::mouseDown (const MouseEvent
& e
)
242 if (currentPopupIndex
< 0)
244 const MouseEvent
e2 (e
.getEventRelativeTo (this));
245 updateItemUnderMouse (e2
.x
, e2
.y
);
247 currentPopupIndex
= -2;
248 showMenu (itemUnderMouse
);
252 void MenuBarComponent::mouseDrag (const MouseEvent
& e
)
254 const MouseEvent
e2 (e
.getEventRelativeTo (this));
255 const int item
= getItemAt (e2
.x
, e2
.y
);
261 void MenuBarComponent::mouseUp (const MouseEvent
& e
)
263 const MouseEvent
e2 (e
.getEventRelativeTo (this));
265 updateItemUnderMouse (e2
.x
, e2
.y
);
267 if (itemUnderMouse
< 0 && getLocalBounds().contains (e2
.x
, e2
.y
))
270 PopupMenu::dismissAllActiveMenus();
274 void MenuBarComponent::mouseMove (const MouseEvent
& e
)
276 const MouseEvent
e2 (e
.getEventRelativeTo (this));
278 if (lastMouseX
!= e2
.x
|| lastMouseY
!= e2
.y
)
280 if (currentPopupIndex
>= 0)
282 const int item
= getItemAt (e2
.x
, e2
.y
);
289 updateItemUnderMouse (e2
.x
, e2
.y
);
297 bool MenuBarComponent::keyPressed (const KeyPress
& key
)
300 const int numMenus
= menuNames
.size();
301 const int currentIndex
= jlimit (0, menuNames
.size() - 1, currentPopupIndex
);
303 if (key
.isKeyCode (KeyPress::leftKey
))
305 showMenu ((currentIndex
+ numMenus
- 1) % numMenus
);
308 else if (key
.isKeyCode (KeyPress::rightKey
))
310 showMenu ((currentIndex
+ 1) % numMenus
);
317 void MenuBarComponent::menuBarItemsChanged (MenuBarModel
* /*menuBarModel*/)
319 StringArray newNames
;
321 if (model
!= nullptr)
322 newNames
= model
->getMenuBarNames();
324 if (newNames
!= menuNames
)
326 menuNames
= newNames
;
332 void MenuBarComponent::menuCommandInvoked (MenuBarModel
* /*menuBarModel*/,
333 const ApplicationCommandTarget::InvocationInfo
& info
)
335 if (model
== nullptr || (info
.commandFlags
& ApplicationCommandInfo::dontTriggerVisualFeedback
) != 0)
338 for (int i
= 0; i
< menuNames
.size(); ++i
)
340 const PopupMenu
menu (model
->getMenuForIndex (i
, menuNames
[i
]));
342 if (menu
.containsCommandItem (info
.commandID
))
344 setItemUnderMouse (i
);
351 void MenuBarComponent::timerCallback()
355 const Point
<int> mousePos (getMouseXYRelative());
356 updateItemUnderMouse (mousePos
.getX(), mousePos
.getY());