2 * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Stephan Aßmus, superstippi@gmx.de
7 * Marc Flerackers, mflerackers@androme.be
8 * John Scipione, jcipione@gmail.com
12 #include <BMCPrivate.h>
16 #include <ControlLook.h>
17 #include <LayoutUtils.h>
18 #include <MenuField.h>
21 #include <MessageRunner.h>
25 static const float kPopUpIndicatorWidth
= 13.0f
;
31 // This is kept only for binary compatibility with BeOS R5. This class was
32 // used in their BMenuField implementation and we may come across some archived
33 // BMenuField that needs it.
34 class _BMCItem_
: public BMenuItem
{
36 _BMCItem_(BMessage
* data
);
37 static BArchivable
* Instantiate(BMessage
*data
);
41 _BMCItem_::_BMCItem_(BMessage
* data
)
48 /*static*/ BArchivable
*
49 _BMCItem_::Instantiate(BMessage
*data
) {
50 if (validate_instantiation(data
, "_BMCItem_"))
51 return new _BMCItem_(data
);
60 // #pragma mark - _BMCFilter_
63 _BMCFilter_::_BMCFilter_(BMenuField
* menuField
, uint32 what
)
65 BMessageFilter(B_ANY_DELIVERY
, B_ANY_SOURCE
, what
),
71 _BMCFilter_::~_BMCFilter_()
77 _BMCFilter_::Filter(BMessage
* message
, BHandler
** handler
)
79 if (message
->what
== B_MOUSE_DOWN
) {
80 if (BView
* view
= dynamic_cast<BView
*>(*handler
)) {
82 message
->FindPoint("be:view_where", &point
);
83 view
->ConvertToParent(&point
);
84 message
->ReplacePoint("be:view_where", point
);
85 *handler
= fMenuField
;
89 return B_DISPATCH_MESSAGE
;
93 // #pragma mark - _BMCMenuBar_
96 _BMCMenuBar_::_BMCMenuBar_(BRect frame
, bool fixedSize
, BMenuField
* menuField
)
98 BMenuBar(frame
, "_mc_mb_", B_FOLLOW_LEFT
| B_FOLLOW_TOP
, B_ITEMS_IN_ROW
,
100 fMenuField(menuField
),
101 fFixedSize(fixedSize
),
102 fShowPopUpMarker(true)
108 _BMCMenuBar_::_BMCMenuBar_(BMenuField
* menuField
)
110 BMenuBar("_mc_mb_", B_ITEMS_IN_ROW
),
111 fMenuField(menuField
),
113 fShowPopUpMarker(true)
119 _BMCMenuBar_::_BMCMenuBar_(BMessage
* data
)
124 fShowPopUpMarker(true)
126 SetFlags(Flags() | B_FRAME_EVENTS
);
129 if (data
->FindBool("_rsize_to_fit", &resizeToFit
) == B_OK
)
130 fFixedSize
= !resizeToFit
;
134 _BMCMenuBar_::~_BMCMenuBar_()
139 // #pragma mark - _BMCMenuBar_ public methods
143 _BMCMenuBar_::Instantiate(BMessage
* data
)
145 if (validate_instantiation(data
, "_BMCMenuBar_"))
146 return new _BMCMenuBar_(data
);
153 _BMCMenuBar_::AttachedToWindow()
155 fMenuField
= static_cast<BMenuField
*>(Parent());
157 // Don't cause the KeyMenuBar to change by being attached
158 BMenuBar
* menuBar
= Window()->KeyMenuBar();
159 BMenuBar::AttachedToWindow();
160 Window()->SetKeyMenuBar(menuBar
);
162 if (fFixedSize
&& (Flags() & B_SUPPORTS_LAYOUT
) == 0)
163 SetResizingMode(B_FOLLOW_LEFT_RIGHT
| B_FOLLOW_TOP
);
165 if (Parent() != NULL
) {
166 color_which which
= Parent()->LowUIColor();
167 if (which
== B_NO_COLOR
)
168 SetLowColor(Parent()->LowColor());
170 SetLowUIColor(which
);
173 SetLowUIColor(B_MENU_BACKGROUND_COLOR
);
175 fPreviousWidth
= Bounds().Width();
180 _BMCMenuBar_::Draw(BRect updateRect
)
183 // Set the width of the menu bar because the menu bar bounds may have
184 // been expanded by the selected menu item.
185 ResizeTo(fMenuField
->_MenuBarWidth(), Bounds().Height());
187 // For compatability with BeOS R5:
188 // - Set to the minimum of the menu bar width set by the menu frame
189 // and the selected menu item width.
190 // - Set the height to the preferred height ignoring the height of the
193 BMenuBar::GetPreferredSize(NULL
, &height
);
194 ResizeTo(std::min(Bounds().Width(), fMenuField
->_MenuBarWidth()),
198 BRect
rect(Bounds());
199 rgb_color base
= ui_color(B_MENU_BACKGROUND_COLOR
);
202 flags
|= BControlLook::B_DISABLED
;
204 flags
|= BControlLook::B_FOCUSED
;
206 be_control_look
->DrawMenuFieldBackground(this, rect
,
207 updateRect
, base
, fShowPopUpMarker
, flags
);
209 DrawItems(updateRect
);
214 _BMCMenuBar_::FrameResized(float width
, float height
)
216 // we need to take care of cleaning up the parent menu field
217 float diff
= width
- fPreviousWidth
;
218 fPreviousWidth
= width
;
220 if (Window() != NULL
&& diff
!= 0) {
221 BRect
dirty(fMenuField
->Bounds());
223 // clean up the dirty right border of
224 // the menu field when enlarging
225 dirty
.right
= Frame().right
+ kVMargin
;
226 dirty
.left
= dirty
.right
- diff
- kVMargin
* 2;
227 fMenuField
->Invalidate(dirty
);
228 } else if (diff
< 0) {
229 // clean up the dirty right line of
230 // the menu field when shrinking
231 dirty
.left
= Frame().right
- kVMargin
;
232 fMenuField
->Invalidate(dirty
);
236 BMenuBar::FrameResized(width
, height
);
241 _BMCMenuBar_::MakeFocus(bool focused
)
243 if (IsFocus() == focused
)
246 BMenuBar::MakeFocus(focused
);
251 _BMCMenuBar_::MessageReceived(BMessage
* message
)
253 switch (message
->what
) {
256 BMenuItem
* item
= ItemAt(0);
258 if (item
!= NULL
&& item
->Submenu() != NULL
259 && item
->Submenu()->Window() != NULL
) {
260 BMessage
message(B_KEY_DOWN
);
262 message
.AddInt8("byte", B_ESCAPE
);
263 message
.AddInt8("key", B_ESCAPE
);
264 message
.AddInt32("modifiers", 0);
265 message
.AddInt8("raw_char", B_ESCAPE
);
267 Window()->PostMessage(&message
, this, NULL
);
272 BMenuBar::MessageReceived(message
);
279 _BMCMenuBar_::SetMaxContentWidth(float width
)
283 GetItemMargins(&left
, NULL
, &right
, NULL
);
285 BMenuBar::SetMaxContentWidth(width
- (left
+ right
));
290 _BMCMenuBar_::SetEnabled(bool enabled
)
292 fMenuField
->SetEnabled(enabled
);
294 BMenuBar::SetEnabled(enabled
);
299 _BMCMenuBar_::MinSize()
302 BMenuBar::GetPreferredSize(&size
.width
, &size
.height
);
303 if (fShowPopUpMarker
) {
304 // account for popup indicator + a few pixels margin
305 size
.width
+= kPopUpIndicatorWidth
;
308 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size
);
313 _BMCMenuBar_::MaxSize()
315 // The maximum width of a normal BMenuBar is unlimited, but we want it
318 BMenuBar::GetPreferredSize(&size
.width
, &size
.height
);
320 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size
);
324 // #pragma mark - _BMCMenuBar_ private methods
328 _BMCMenuBar_::_Init()
330 SetFlags(Flags() | B_FRAME_EVENTS
| B_FULL_UPDATE_ON_RESIZE
);
331 SetBorder(B_BORDER_CONTENTS
);
333 float left
, top
, right
, bottom
;
334 GetItemMargins(&left
, &top
, &right
, &bottom
);
337 // TODO: Better fix would be to make BMenuItem draw text properly
339 font_height fontHeight
;
340 GetFontHeight(&fontHeight
);
341 top
= ceilf((Bounds().Height() - ceilf(fontHeight
.ascent
)
342 - ceilf(fontHeight
.descent
)) / 2) + 1;
345 // TODO: Fix content location properly. This is just a quick fix to
346 // make the BMenuField label and the super-item of the BMenuBar
352 left
= right
= be_control_look
->DefaultLabelSpacing();
354 SetItemMargins(left
, top
,
355 right
+ fShowPopUpMarker
? kPopUpIndicatorWidth
: 0, bottom
);