2 * Copyright 2001-2013 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 SetLowColor(Parent()->LowColor());
168 SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR
));
170 fPreviousWidth
= Bounds().Width();
175 _BMCMenuBar_::Draw(BRect updateRect
)
178 // Set the width of the menu bar because the menu bar bounds may have
179 // been expanded by the selected menu item.
180 ResizeTo(fMenuField
->_MenuBarWidth(), Bounds().Height());
182 // For compatability with BeOS R5:
183 // - Set to the minimum of the menu bar width set by the menu frame
184 // and the selected menu item width.
185 // - Set the height to the preferred height ignoring the height of the
188 BMenuBar::GetPreferredSize(NULL
, &height
);
189 ResizeTo(std::min(Bounds().Width(), fMenuField
->_MenuBarWidth()),
193 BRect
rect(Bounds());
194 rgb_color base
= ui_color(B_MENU_BACKGROUND_COLOR
);
197 flags
|= BControlLook::B_DISABLED
;
199 flags
|= BControlLook::B_FOCUSED
;
201 be_control_look
->DrawMenuFieldBackground(this, rect
,
202 updateRect
, base
, fShowPopUpMarker
, flags
);
204 _DrawItems(updateRect
);
209 _BMCMenuBar_::FrameResized(float width
, float height
)
211 // we need to take care of cleaning up the parent menu field
212 float diff
= width
- fPreviousWidth
;
213 fPreviousWidth
= width
;
215 if (Window() != NULL
&& diff
!= 0) {
216 BRect
dirty(fMenuField
->Bounds());
218 // clean up the dirty right border of
219 // the menu field when enlarging
220 dirty
.right
= Frame().right
+ kVMargin
;
221 dirty
.left
= dirty
.right
- diff
- kVMargin
* 2;
222 fMenuField
->Invalidate(dirty
);
223 } else if (diff
< 0) {
224 // clean up the dirty right line of
225 // the menu field when shrinking
226 dirty
.left
= Frame().right
- kVMargin
;
227 fMenuField
->Invalidate(dirty
);
231 BMenuBar::FrameResized(width
, height
);
236 _BMCMenuBar_::MakeFocus(bool focused
)
238 if (IsFocus() == focused
)
241 BMenuBar::MakeFocus(focused
);
246 _BMCMenuBar_::MessageReceived(BMessage
* message
)
248 switch (message
->what
) {
251 BMenuItem
* item
= ItemAt(0);
253 if (item
&& item
->Submenu() && item
->Submenu()->Window()) {
254 BMessage
message(B_KEY_DOWN
);
256 message
.AddInt8("byte", B_ESCAPE
);
257 message
.AddInt8("key", B_ESCAPE
);
258 message
.AddInt32("modifiers", 0);
259 message
.AddInt8("raw_char", B_ESCAPE
);
261 Window()->PostMessage(&message
, this, NULL
);
266 BMenuBar::MessageReceived(message
);
273 _BMCMenuBar_::SetMaxContentWidth(float width
)
277 GetItemMargins(&left
, NULL
, &right
, NULL
);
279 BMenuBar::SetMaxContentWidth(width
- (left
+ right
));
284 _BMCMenuBar_::SetEnabled(bool enabled
)
286 fMenuField
->SetEnabled(enabled
);
288 BMenuBar::SetEnabled(enabled
);
293 _BMCMenuBar_::MinSize()
296 BMenuBar::GetPreferredSize(&size
.width
, &size
.height
);
297 if (fShowPopUpMarker
) {
298 // account for popup indicator + a few pixels margin
299 size
.width
+= kPopUpIndicatorWidth
;
302 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size
);
307 _BMCMenuBar_::MaxSize()
309 // The maximum width of a normal BMenuBar is unlimited, but we want it
312 BMenuBar::GetPreferredSize(&size
.width
, &size
.height
);
314 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size
);
318 // #pragma mark - _BMCMenuBar_ private methods
322 _BMCMenuBar_::_Init()
324 SetFlags(Flags() | B_FRAME_EVENTS
| B_FULL_UPDATE_ON_RESIZE
);
325 SetBorder(B_BORDER_CONTENTS
);
327 float left
, top
, right
, bottom
;
328 GetItemMargins(&left
, &top
, &right
, &bottom
);
331 // TODO: Better fix would be to make BMenuItem draw text properly
333 font_height fontHeight
;
334 GetFontHeight(&fontHeight
);
335 top
= ceilf((Bounds().Height() - ceilf(fontHeight
.ascent
)
336 - ceilf(fontHeight
.descent
)) / 2) + 1;
339 // TODO: Fix content location properly. This is just a quick fix to
340 // make the BMenuField label and the super-item of the BMenuBar
346 if (be_control_look
!= NULL
)
347 left
= right
= be_control_look
->DefaultLabelSpacing();
349 SetItemMargins(left
, top
,
350 right
+ fShowPopUpMarker
? kPopUpIndicatorWidth
: 0, bottom
);