2 * Copyright 2006-2016 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, jscipione@gmail.com
9 * Ingo Weinhold, bonefish@cs.tu-berlin.de
13 #include <MenuField.h>
18 // for printf in TRACE
22 #include <AbstractLayoutItem.h>
23 #include <Archivable.h>
24 #include <BMCPrivate.h>
25 #include <ControlLook.h>
26 #include <LayoutUtils.h>
29 #include <MenuItemPrivate.h>
30 #include <MenuPrivate.h>
32 #include <MessageFilter.h>
36 #include <binary_compatibility/Interface.h>
37 #include <binary_compatibility/Support.h>
47 //#define TRACE_MENU_FIELD
48 #ifdef TRACE_MENU_FIELD
49 # include <FunctionTracer.h>
50 static int32 sFunctionDepth
= -1;
51 # define CALLED(x...) FunctionTracer _ft("BMenuField", __FUNCTION__, \
53 # define TRACE(x...) { BString _to; \
54 _to.Append(' ', (sFunctionDepth + 1) * 2); \
55 printf("%s", _to.String()); printf(x); }
62 static const float kMinMenuBarWidth
= 20.0f
;
63 // found by experimenting on BeOS R5
67 const char* const kFrameField
= "BMenuField:layoutItem:frame";
68 const char* const kMenuBarItemField
= "BMenuField:barItem";
69 const char* const kLabelItemField
= "BMenuField:labelItem";
73 // #pragma mark - LabelLayoutItem
76 class BMenuField::LabelLayoutItem
: public BAbstractLayoutItem
{
78 LabelLayoutItem(BMenuField
* parent
);
79 LabelLayoutItem(BMessage
* archive
);
81 BRect
FrameInParent() const;
83 virtual bool IsVisible();
84 virtual void SetVisible(bool visible
);
86 virtual BRect
Frame();
87 virtual void SetFrame(BRect frame
);
89 void SetParent(BMenuField
* parent
);
90 virtual BView
* View();
92 virtual BSize
BaseMinSize();
93 virtual BSize
BaseMaxSize();
94 virtual BSize
BasePreferredSize();
95 virtual BAlignment
BaseAlignment();
97 virtual status_t
Archive(BMessage
* into
, bool deep
= true) const;
98 static BArchivable
* Instantiate(BMessage
* from
);
106 // #pragma mark - MenuBarLayoutItem
109 class BMenuField::MenuBarLayoutItem
: public BAbstractLayoutItem
{
111 MenuBarLayoutItem(BMenuField
* parent
);
112 MenuBarLayoutItem(BMessage
* from
);
114 BRect
FrameInParent() const;
116 virtual bool IsVisible();
117 virtual void SetVisible(bool visible
);
119 virtual BRect
Frame();
120 virtual void SetFrame(BRect frame
);
122 void SetParent(BMenuField
* parent
);
123 virtual BView
* View();
125 virtual BSize
BaseMinSize();
126 virtual BSize
BaseMaxSize();
127 virtual BSize
BasePreferredSize();
128 virtual BAlignment
BaseAlignment();
130 virtual status_t
Archive(BMessage
* into
, bool deep
= true) const;
131 static BArchivable
* Instantiate(BMessage
* from
);
139 // #pragma mark - LayoutData
142 struct BMenuField::LayoutData
{
145 label_layout_item(NULL
),
146 menu_bar_layout_item(NULL
),
152 LabelLayoutItem
* label_layout_item
;
153 MenuBarLayoutItem
* menu_bar_layout_item
;
154 float previous_height
; // used in FrameResized() for
156 font_height font_info
;
165 // #pragma mark - MouseDownFilter
168 class MouseDownFilter
: public BMessageFilter
172 virtual ~MouseDownFilter();
174 virtual filter_result
Filter(BMessage
* message
, BHandler
** target
);
178 MouseDownFilter::MouseDownFilter()
180 BMessageFilter(B_ANY_DELIVERY
, B_ANY_SOURCE
)
185 MouseDownFilter::~MouseDownFilter()
191 MouseDownFilter::Filter(BMessage
* message
, BHandler
** target
)
193 return message
->what
== B_MOUSE_DOWN
? B_SKIP_MESSAGE
: B_DISPATCH_MESSAGE
;
197 // #pragma mark - BMenuField
200 BMenuField::BMenuField(BRect frame
, const char* name
, const char* label
,
201 BMenu
* menu
, uint32 resizingMode
, uint32 flags
)
203 BView(frame
, name
, resizingMode
, flags
)
207 TRACE("frame.width: %.2f, height: %.2f\n", frame
.Width(), frame
.Height());
211 frame
.OffsetTo(B_ORIGIN
);
212 _InitMenuBar(menu
, frame
, false);
218 BMenuField::BMenuField(BRect frame
, const char* name
, const char* label
,
219 BMenu
* menu
, bool fixedSize
, uint32 resizingMode
, uint32 flags
)
221 BView(frame
, name
, resizingMode
, flags
)
225 fFixedSizeMB
= fixedSize
;
227 frame
.OffsetTo(B_ORIGIN
);
228 _InitMenuBar(menu
, frame
, fixedSize
);
234 BMenuField::BMenuField(const char* name
, const char* label
, BMenu
* menu
,
237 BView(name
, flags
| B_FRAME_EVENTS
)
241 _InitMenuBar(menu
, BRect(0, 0, 100, 15), true);
247 BMenuField::BMenuField(const char* label
, BMenu
* menu
, uint32 flags
)
249 BView(NULL
, flags
| B_FRAME_EVENTS
)
253 _InitMenuBar(menu
, BRect(0, 0, 100, 15), true);
259 //! Copy&Paste error, should be removed at some point (already private)
260 BMenuField::BMenuField(const char* name
, const char* label
, BMenu
* menu
,
261 BMessage
* message
, uint32 flags
)
263 BView(name
, flags
| B_FRAME_EVENTS
)
267 _InitMenuBar(menu
, BRect(0, 0, 100, 15), true);
273 //! Copy&Paste error, should be removed at some point (already private)
274 BMenuField::BMenuField(const char* label
, BMenu
* menu
, BMessage
* message
)
276 BView(NULL
, B_WILL_DRAW
| B_NAVIGABLE
| B_FRAME_EVENTS
)
280 _InitMenuBar(menu
, BRect(0, 0, 100, 15), true);
286 BMenuField::BMenuField(BMessage
* data
)
288 BView(BUnarchiver::PrepareArchive(data
))
290 BUnarchiver
unarchiver(data
);
291 const char* label
= NULL
;
292 data
->FindString("_label", &label
);
296 data
->FindFloat("_divide", &fDivider
);
299 if (data
->FindInt32("_align", &align
) == B_OK
)
300 SetAlignment((alignment
)align
);
302 if (!BUnarchiver::IsArchiveManaged(data
))
309 BMenuField::~BMenuField()
314 if (fMenuTaskID
>= 0)
315 wait_for_thread(fMenuTaskID
, &dummy
);
318 delete fMouseDownFilter
;
323 BMenuField::Instantiate(BMessage
* data
)
325 if (validate_instantiation(data
, "BMenuField"))
326 return new BMenuField(data
);
333 BMenuField::Archive(BMessage
* data
, bool deep
) const
335 BArchiver
archiver(data
);
336 status_t ret
= BView::Archive(data
, deep
);
338 if (ret
== B_OK
&& Label())
339 ret
= data
->AddString("_label", Label());
341 if (ret
== B_OK
&& !IsEnabled())
342 ret
= data
->AddBool("_disable", true);
345 ret
= data
->AddInt32("_align", Alignment());
347 ret
= data
->AddFloat("_divide", Divider());
349 if (ret
== B_OK
&& fFixedSizeMB
)
350 ret
= data
->AddBool("be:fixeds", true);
353 if (_BMCMenuBar_
* menuBar
= dynamic_cast<_BMCMenuBar_
*>(fMenuBar
))
354 dmark
= menuBar
->IsPopUpMarkerShown();
356 data
->AddBool("be:dmark", dmark
);
358 return archiver
.Finish(ret
);
363 BMenuField::AllArchived(BMessage
* into
) const
366 if ((err
= BView::AllArchived(into
)) != B_OK
)
369 BArchiver
archiver(into
);
371 BArchivable
* menuBarItem
= fLayoutData
->menu_bar_layout_item
;
372 if (archiver
.IsArchived(menuBarItem
))
373 err
= archiver
.AddArchivable(kMenuBarItemField
, menuBarItem
);
378 BArchivable
* labelBarItem
= fLayoutData
->label_layout_item
;
379 if (archiver
.IsArchived(labelBarItem
))
380 err
= archiver
.AddArchivable(kLabelItemField
, labelBarItem
);
387 BMenuField::AllUnarchived(const BMessage
* from
)
389 BUnarchiver
unarchiver(from
);
392 if ((err
= BView::AllUnarchived(from
)) != B_OK
)
397 if (unarchiver
.IsInstantiated(kMenuBarItemField
)) {
398 MenuBarLayoutItem
*& menuItem
= fLayoutData
->menu_bar_layout_item
;
399 err
= unarchiver
.FindObject(kMenuBarItemField
,
400 BUnarchiver::B_DONT_ASSUME_OWNERSHIP
, menuItem
);
403 menuItem
->SetParent(this);
408 if (unarchiver
.IsInstantiated(kLabelItemField
)) {
409 LabelLayoutItem
*& labelItem
= fLayoutData
->label_layout_item
;
410 err
= unarchiver
.FindObject(kLabelItemField
,
411 BUnarchiver::B_DONT_ASSUME_OWNERSHIP
, labelItem
);
414 labelItem
->SetParent(this);
422 BMenuField::Draw(BRect updateRect
)
424 _DrawLabel(updateRect
);
425 _DrawMenuBar(updateRect
);
430 BMenuField::AttachedToWindow()
434 // Our low color must match the parent's view color.
435 if (Parent() != NULL
) {
438 float tint
= B_NO_TINT
;
439 color_which which
= ViewUIColor(&tint
);
441 if (which
== B_NO_COLOR
)
442 SetLowColor(ViewColor());
444 SetLowUIColor(which
, tint
);
451 BMenuField::AllAttached()
455 TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
457 float width
= Bounds().Width();
458 if (!fFixedSizeMB
&& _MenuBarWidth() < kMinMenuBarWidth
) {
459 // The menu bar is too narrow, resize it to fit the menu items
460 BMenuItem
* item
= fMenuBar
->ItemAt(0);
463 fMenuBar
->GetItemMargins(NULL
, NULL
, &right
, NULL
);
464 width
= item
->Frame().Width() + kVMargin
+ _MenuBarOffset() + right
;
468 ResizeTo(width
, fMenuBar
->Bounds().Height() + kVMargin
* 2);
470 TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
475 BMenuField::MouseDown(BPoint where
)
477 BRect bounds
= fMenuBar
->ConvertFromParent(Bounds());
479 fMenuBar
->StartMenuBar(-1, false, true, &bounds
);
481 fMenuTaskID
= spawn_thread((thread_func
)_thread_entry
,
482 "_m_task_", B_NORMAL_PRIORITY
, this);
483 if (fMenuTaskID
>= 0 && resume_thread(fMenuTaskID
) == B_OK
) {
484 if (fMouseDownFilter
->Looper() == NULL
)
485 Window()->AddCommonFilter(fMouseDownFilter
);
487 MouseDownThread
<BMenuField
>::TrackMouse(this, &BMenuField::_DoneTracking
,
488 &BMenuField::_Track
);
494 BMenuField::KeyDown(const char* bytes
, int32 numBytes
)
504 BRect bounds
= fMenuBar
->ConvertFromParent(Bounds());
506 fMenuBar
->StartMenuBar(0, true, true, &bounds
);
509 bounds
.right
= fDivider
;
515 BView::KeyDown(bytes
, numBytes
);
521 BMenuField::MakeFocus(bool focused
)
523 if (IsFocus() == focused
)
526 BView::MakeFocus(focused
);
528 if (Window() != NULL
)
529 Invalidate(); // TODO: use fLayoutData->label_width
534 BMenuField::MessageReceived(BMessage
* message
)
536 BView::MessageReceived(message
);
541 BMenuField::WindowActivated(bool active
)
543 BView::WindowActivated(active
);
551 BMenuField::MouseMoved(BPoint point
, uint32 code
, const BMessage
* message
)
553 BView::MouseMoved(point
, code
, message
);
558 BMenuField::MouseUp(BPoint where
)
560 BView::MouseUp(where
);
565 BMenuField::DetachedFromWindow()
567 BView::DetachedFromWindow();
572 BMenuField::AllDetached()
574 BView::AllDetached();
579 BMenuField::FrameMoved(BPoint newPosition
)
581 BView::FrameMoved(newPosition
);
586 BMenuField::FrameResized(float newWidth
, float newHeight
)
588 BView::FrameResized(newWidth
, newHeight
);
591 // we have let the menubar resize itself, but
592 // in fixed size mode, the menubar is supposed to
593 // be at the right end of the view always. Since
594 // the menu bar is in follow left/right mode then,
595 // resizing ourselfs might have caused the menubar
597 fMenuBar
->ResizeTo(_MenuBarWidth(), fMenuBar
->Frame().Height());
600 if (newHeight
!= fLayoutData
->previous_height
&& Label()) {
601 // The height changed, which means the label has to move and we
602 // probably also invalidate a part of the borders around the menu bar.
603 // So don't be shy and invalidate the whole thing.
607 fLayoutData
->previous_height
= newHeight
;
612 BMenuField::Menu() const
619 BMenuField::MenuBar() const
626 BMenuField::MenuItem() const
628 return fMenuBar
->ItemAt(0);
633 BMenuField::SetLabel(const char* label
)
636 if (label
&& strcmp(fLabel
, label
) == 0)
642 fLabel
= strdup(label
);
652 BMenuField::Label() const
659 BMenuField::SetEnabled(bool on
)
665 fMenuBar
->SetEnabled(on
);
668 fMenuBar
->Invalidate(fMenuBar
->Bounds());
669 Invalidate(Bounds());
675 BMenuField::IsEnabled() const
682 BMenuField::SetAlignment(alignment label
)
689 BMenuField::Alignment() const
696 BMenuField::SetDivider(float position
)
698 position
= roundf(position
);
700 float delta
= fDivider
- position
;
706 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0) {
707 // We should never get here, since layout support means, we also
708 // layout the divider, and don't use this method at all.
711 BRect
dirty(fMenuBar
->Frame());
713 fMenuBar
->MoveTo(_MenuBarOffset(), kVMargin
);
716 fMenuBar
->ResizeTo(_MenuBarWidth(), dirty
.Height());
718 dirty
= dirty
| fMenuBar
->Frame();
719 dirty
.InsetBy(-kVMargin
, -kVMargin
);
727 BMenuField::Divider() const
734 BMenuField::ShowPopUpMarker()
736 if (_BMCMenuBar_
* menuBar
= dynamic_cast<_BMCMenuBar_
*>(fMenuBar
)) {
737 menuBar
->TogglePopUpMarker(true);
738 menuBar
->Invalidate();
744 BMenuField::HidePopUpMarker()
746 if (_BMCMenuBar_
* menuBar
= dynamic_cast<_BMCMenuBar_
*>(fMenuBar
)) {
747 menuBar
->TogglePopUpMarker(false);
748 menuBar
->Invalidate();
754 BMenuField::ResolveSpecifier(BMessage
* message
, int32 index
,
755 BMessage
* specifier
, int32 form
, const char* property
)
757 return BView::ResolveSpecifier(message
, index
, specifier
, form
, property
);
762 BMenuField::GetSupportedSuites(BMessage
* data
)
764 return BView::GetSupportedSuites(data
);
769 BMenuField::ResizeToPreferred()
773 TRACE("fMenuBar->Frame().width: %.2f, height: %.2f\n",
774 fMenuBar
->Frame().Width(), fMenuBar
->Frame().Height());
776 fMenuBar
->ResizeToPreferred();
778 TRACE("fMenuBar->Frame().width: %.2f, height: %.2f\n",
779 fMenuBar
->Frame().Width(), fMenuBar
->Frame().Height());
781 BView::ResizeToPreferred();
788 BMenuField::GetPreferredSize(float* _width
, float* _height
)
792 _ValidateLayoutData();
795 *_width
= fLayoutData
->min
.width
;
798 *_height
= fLayoutData
->min
.height
;
803 BMenuField::MinSize()
807 _ValidateLayoutData();
808 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData
->min
);
813 BMenuField::MaxSize()
817 _ValidateLayoutData();
819 BSize max
= fLayoutData
->min
;
820 max
.width
= B_SIZE_UNLIMITED
;
822 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max
);
827 BMenuField::PreferredSize()
831 _ValidateLayoutData();
832 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), fLayoutData
->min
);
837 BMenuField::CreateLabelLayoutItem()
839 if (fLayoutData
->label_layout_item
== NULL
)
840 fLayoutData
->label_layout_item
= new LabelLayoutItem(this);
842 return fLayoutData
->label_layout_item
;
847 BMenuField::CreateMenuBarLayoutItem()
849 if (fLayoutData
->menu_bar_layout_item
== NULL
) {
850 // align the menu bar in the full available space
851 fMenuBar
->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH
,
852 B_ALIGN_VERTICAL_UNSET
));
853 fLayoutData
->menu_bar_layout_item
= new MenuBarLayoutItem(this);
856 return fLayoutData
->menu_bar_layout_item
;
861 BMenuField::Perform(perform_code code
, void* _data
)
864 case PERFORM_CODE_MIN_SIZE
:
865 ((perform_data_min_size
*)_data
)->return_value
866 = BMenuField::MinSize();
869 case PERFORM_CODE_MAX_SIZE
:
870 ((perform_data_max_size
*)_data
)->return_value
871 = BMenuField::MaxSize();
874 case PERFORM_CODE_PREFERRED_SIZE
:
875 ((perform_data_preferred_size
*)_data
)->return_value
876 = BMenuField::PreferredSize();
879 case PERFORM_CODE_LAYOUT_ALIGNMENT
:
880 ((perform_data_layout_alignment
*)_data
)->return_value
881 = BMenuField::LayoutAlignment();
884 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH
:
885 ((perform_data_has_height_for_width
*)_data
)->return_value
886 = BMenuField::HasHeightForWidth();
889 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH
:
891 perform_data_get_height_for_width
* data
892 = (perform_data_get_height_for_width
*)_data
;
893 BMenuField::GetHeightForWidth(data
->width
, &data
->min
, &data
->max
,
898 case PERFORM_CODE_SET_LAYOUT
:
900 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
901 BMenuField::SetLayout(data
->layout
);
905 case PERFORM_CODE_LAYOUT_INVALIDATED
:
907 perform_data_layout_invalidated
* data
908 = (perform_data_layout_invalidated
*)_data
;
909 BMenuField::LayoutInvalidated(data
->descendants
);
913 case PERFORM_CODE_DO_LAYOUT
:
915 BMenuField::DoLayout();
919 case PERFORM_CODE_ALL_UNARCHIVED
:
921 perform_data_all_unarchived
* data
922 = (perform_data_all_unarchived
*)_data
;
923 data
->return_value
= BMenuField::AllUnarchived(data
->archive
);
927 case PERFORM_CODE_ALL_ARCHIVED
:
929 perform_data_all_archived
* data
930 = (perform_data_all_archived
*)_data
;
931 data
->return_value
= BMenuField::AllArchived(data
->archive
);
936 return BView::Perform(code
, _data
);
941 BMenuField::LayoutInvalidated(bool descendants
)
945 fLayoutData
->valid
= false;
950 BMenuField::DoLayout()
952 // Bail out, if we shan't do layout.
953 if ((Flags() & B_SUPPORTS_LAYOUT
) == 0)
958 // If the user set a layout, we let the base class version call its
960 if (GetLayout() != NULL
) {
965 _ValidateLayoutData();
967 // validate current size
968 BSize
size(Bounds().Size());
969 if (size
.width
< fLayoutData
->min
.width
)
970 size
.width
= fLayoutData
->min
.width
;
972 if (size
.height
< fLayoutData
->min
.height
)
973 size
.height
= fLayoutData
->min
.height
;
977 if (fLayoutData
->label_layout_item
!= NULL
978 && fLayoutData
->menu_bar_layout_item
!= NULL
979 && fLayoutData
->label_layout_item
->Frame().IsValid()
980 && fLayoutData
->menu_bar_layout_item
->Frame().IsValid()) {
981 // We have valid layout items, they define the divider location.
982 divider
= fabs(fLayoutData
->menu_bar_layout_item
->Frame().left
983 - fLayoutData
->label_layout_item
->Frame().left
);
984 } else if (fLayoutData
->label_width
> 0) {
985 divider
= fLayoutData
->label_width
986 + be_control_look
->DefaultLabelSpacing();
990 BRect
dirty(fMenuBar
->Frame());
991 BRect
menuBarFrame(divider
+ kVMargin
, kVMargin
, size
.width
- kVMargin
,
992 size
.height
- kVMargin
);
994 // place the menu bar and set the divider
995 BLayoutUtils::AlignInFrame(fMenuBar
, menuBarFrame
);
999 // invalidate dirty region
1000 dirty
= dirty
| fMenuBar
->Frame();
1001 dirty
.InsetBy(-kVMargin
, -kVMargin
);
1007 void BMenuField::_ReservedMenuField1() {}
1008 void BMenuField::_ReservedMenuField2() {}
1009 void BMenuField::_ReservedMenuField3() {}
1013 BMenuField::InitObject(const char* label
)
1020 fAlign
= B_ALIGN_LEFT
;
1022 fFixedSizeMB
= false;
1024 fLayoutData
= new LayoutData
;
1025 fMouseDownFilter
= new MouseDownFilter();
1030 fDivider
= floorf(Frame().Width() / 2.0f
);
1037 BMenuField::InitObject2()
1041 if (!fFixedSizeMB
) {
1043 fMenuBar
->GetPreferredSize(NULL
, &height
);
1044 fMenuBar
->ResizeTo(_MenuBarWidth(), height
);
1047 TRACE("frame(%.1f, %.1f, %.1f, %.1f) (%.2f, %.2f)\n",
1048 fMenuBar
->Frame().left
, fMenuBar
->Frame().top
,
1049 fMenuBar
->Frame().right
, fMenuBar
->Frame().bottom
,
1050 fMenuBar
->Frame().Width(), fMenuBar
->Frame().Height());
1052 fMenuBar
->AddFilter(new _BMCFilter_(this, B_MOUSE_DOWN
));
1057 BMenuField::_DrawLabel(BRect updateRect
)
1061 _ValidateLayoutData();
1063 const char* label
= Label();
1068 if (fLayoutData
->label_layout_item
!= NULL
)
1069 rect
= fLayoutData
->label_layout_item
->FrameInParent();
1072 rect
.right
= fDivider
;
1075 if (!rect
.IsValid() || !rect
.Intersects(updateRect
))
1080 flags
|= BControlLook::B_DISABLED
;
1082 // save the current low color
1084 rgb_color textColor
;
1086 BPrivate::MenuPrivate
menuPrivate(fMenuBar
);
1087 if (menuPrivate
.State() != MENU_STATE_CLOSED
) {
1088 // highlight the background of the label grey (like BeOS R5)
1089 SetLowColor(ui_color(B_MENU_SELECTED_BACKGROUND_COLOR
));
1090 BRect
fillRect(rect
.InsetByCopy(0, kVMargin
));
1091 FillRect(fillRect
, B_SOLID_LOW
);
1092 textColor
= ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR
);
1094 textColor
= ui_color(B_PANEL_TEXT_COLOR
);
1096 be_control_look
->DrawLabel(this, label
, rect
, updateRect
, LowColor(), flags
,
1097 BAlignment(fAlign
, B_ALIGN_MIDDLE
), &textColor
);
1099 // restore the previous low color
1105 BMenuField::_DrawMenuBar(BRect updateRect
)
1109 BRect
rect(fMenuBar
->Frame().InsetByCopy(-kVMargin
, -kVMargin
));
1110 if (!rect
.IsValid() || !rect
.Intersects(updateRect
))
1115 flags
|= BControlLook::B_DISABLED
;
1117 if (IsFocus() && Window()->IsActive())
1118 flags
|= BControlLook::B_FOCUSED
;
1120 be_control_look
->DrawMenuFieldFrame(this, rect
, updateRect
,
1121 fMenuBar
->LowColor(), LowColor(), flags
);
1126 BMenuField::InitMenu(BMenu
* menu
)
1128 menu
->SetFont(be_plain_font
);
1133 while ((subMenu
= menu
->SubmenuAt(index
++)) != NULL
)
1139 BMenuField::_thread_entry(void* arg
)
1141 return static_cast<BMenuField
*>(arg
)->_MenuTask();
1146 BMenuField::_MenuTask()
1160 tracking
= fMenuBar
->fTracking
;
1175 BMenuField::_UpdateFrame()
1179 if (fLayoutData
->label_layout_item
== NULL
1180 || fLayoutData
->menu_bar_layout_item
== NULL
) {
1184 BRect labelFrame
= fLayoutData
->label_layout_item
->Frame();
1185 BRect menuFrame
= fLayoutData
->menu_bar_layout_item
->Frame();
1187 if (!labelFrame
.IsValid() || !menuFrame
.IsValid())
1191 fDivider
= menuFrame
.left
- labelFrame
.left
;
1194 MoveTo(labelFrame
.left
, labelFrame
.top
);
1195 BSize oldSize
= Bounds().Size();
1196 ResizeTo(menuFrame
.left
+ menuFrame
.Width() - labelFrame
.left
,
1197 menuFrame
.top
+ menuFrame
.Height() - labelFrame
.top
);
1198 BSize newSize
= Bounds().Size();
1200 // If the size changes, ResizeTo() will trigger a relayout, otherwise
1201 // we need to do that explicitly.
1202 if (newSize
!= oldSize
)
1208 BMenuField::_InitMenuBar(BMenu
* menu
, BRect frame
, bool fixedSize
)
1212 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0) {
1213 fMenuBar
= new _BMCMenuBar_(this);
1215 frame
.left
= _MenuBarOffset();
1216 frame
.top
= kVMargin
;
1217 frame
.right
-= kVMargin
;
1218 frame
.bottom
-= kVMargin
;
1220 TRACE("frame(%.1f, %.1f, %.1f, %.1f) (%.2f, %.2f)\n",
1221 frame
.left
, frame
.top
, frame
.right
, frame
.bottom
,
1222 frame
.Width(), frame
.Height());
1224 fMenuBar
= new _BMCMenuBar_(frame
, fixedSize
, this);
1228 // align the menu bar in the full available space
1229 fMenuBar
->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH
,
1230 B_ALIGN_VERTICAL_UNSET
));
1232 // align the menu bar left in the available space
1233 fMenuBar
->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT
,
1234 B_ALIGN_VERTICAL_UNSET
));
1241 fMenuBar
->SetFont(be_plain_font
);
1246 BMenuField::_InitMenuBar(const BMessage
* archive
)
1249 if (archive
->FindBool("be:fixeds", &fixed
) == B_OK
)
1250 fFixedSizeMB
= fixed
;
1252 fMenuBar
= (BMenuBar
*)FindView("_mc_mb_");
1253 if (fMenuBar
== NULL
) {
1254 _InitMenuBar(new BMenu(""), BRect(0, 0, 100, 15), fFixedSizeMB
);
1257 fMenuBar
->AddFilter(new _BMCFilter_(this, B_MOUSE_DOWN
));
1258 // this is normally done in InitObject2()
1261 _AddMenu(fMenuBar
->SubmenuAt(0));
1264 if (archive
->FindBool("_disable", &disable
) == B_OK
)
1265 SetEnabled(!disable
);
1268 archive
->FindBool("be:dmark", &dmark
);
1269 _BMCMenuBar_
* menuBar
= dynamic_cast<_BMCMenuBar_
*>(fMenuBar
);
1270 if (menuBar
!= NULL
)
1271 menuBar
->TogglePopUpMarker(dmark
);
1276 BMenuField::_AddMenu(BMenu
* menu
)
1278 if (menu
== NULL
|| fMenuBar
== NULL
)
1284 BMenuItem
* item
= NULL
;
1285 if (!menu
->IsRadioMode() || (item
= menu
->FindMarked()) == NULL
) {
1286 // find the first enabled non-seperator item
1287 int32 itemCount
= menu
->CountItems();
1288 for (int32 i
= 0; i
< itemCount
; i
++) {
1289 item
= menu
->ItemAt((int32
)i
);
1290 if (item
== NULL
|| !item
->IsEnabled()
1291 || dynamic_cast<BSeparatorItem
*>(item
) != NULL
) {
1300 fMenuBar
->AddItem(menu
);
1304 // build an empty copy of item
1307 status_t result
= item
->Archive(&data
, false);
1308 if (result
!= B_OK
) {
1309 fMenuBar
->AddItem(menu
);
1313 BArchivable
* object
= instantiate_object(&data
);
1314 if (object
== NULL
) {
1315 fMenuBar
->AddItem(menu
);
1319 BMenuItem
* newItem
= static_cast<BMenuItem
*>(object
);
1322 BPrivate::MenuItemPrivate
newMenuItemPrivate(newItem
);
1323 newMenuItemPrivate
.Uninstall();
1326 newMenuItemPrivate
.SetSubmenu(menu
);
1327 fMenuBar
->AddItem(newItem
);
1332 BMenuField::_ValidateLayoutData()
1336 if (fLayoutData
->valid
)
1339 // cache font height
1340 font_height
& fh
= fLayoutData
->font_info
;
1343 const char* label
= Label();
1344 if (label
!= NULL
) {
1345 fLayoutData
->label_width
= ceilf(StringWidth(label
));
1346 fLayoutData
->label_height
= ceilf(fh
.ascent
) + ceilf(fh
.descent
);
1348 fLayoutData
->label_width
= 0;
1349 fLayoutData
->label_height
= 0;
1352 // compute the minimal divider
1354 if (fLayoutData
->label_width
> 0) {
1355 divider
= fLayoutData
->label_width
1356 + be_control_look
->DefaultLabelSpacing();
1359 // If we shan't do real layout, we let the current divider take influence.
1360 if ((Flags() & B_SUPPORTS_LAYOUT
) == 0)
1361 divider
= std::max(divider
, fDivider
);
1363 // get the minimal (== preferred) menu bar size
1364 // TODO: BMenu::MinSize() is using the ResizeMode() to decide the
1365 // minimum width. If the mode is B_FOLLOW_LEFT_RIGHT, it will use the
1366 // parent's frame width or window's frame width. So at least the returned
1367 // size is wrong, but apparantly it doesn't have much bad effect.
1368 fLayoutData
->menu_bar_min
= fMenuBar
->MinSize();
1370 TRACE("menu bar min width: %.2f\n", fLayoutData
->menu_bar_min
.width
);
1372 // compute our minimal (== preferred) size
1373 BSize
min(fLayoutData
->menu_bar_min
);
1374 min
.width
+= 2 * kVMargin
;
1375 min
.height
+= 2 * kVMargin
;
1378 min
.width
+= divider
;
1380 if (fLayoutData
->label_height
> min
.height
)
1381 min
.height
= fLayoutData
->label_height
;
1383 fLayoutData
->min
= min
;
1385 fLayoutData
->valid
= true;
1386 ResetLayoutInvalidation();
1388 TRACE("width: %.2f, height: %.2f\n", min
.width
, min
.height
);
1393 BMenuField::_MenuBarOffset() const
1395 return std::max(fDivider
+ kVMargin
, kVMargin
);
1400 BMenuField::_MenuBarWidth() const
1402 return Bounds().Width() - (_MenuBarOffset() + kVMargin
);
1407 BMenuField::_DoneTracking(BPoint point
)
1409 Window()->RemoveCommonFilter(fMouseDownFilter
);
1414 BMenuField::_Track(BPoint point
, uint32
)
1419 // #pragma mark - BMenuField::LabelLayoutItem
1422 BMenuField::LabelLayoutItem::LabelLayoutItem(BMenuField
* parent
)
1430 BMenuField::LabelLayoutItem::LabelLayoutItem(BMessage
* from
)
1432 BAbstractLayoutItem(from
),
1436 from
->FindRect(kFrameField
, &fFrame
);
1441 BMenuField::LabelLayoutItem::FrameInParent() const
1443 return fFrame
.OffsetByCopy(-fParent
->Frame().left
, -fParent
->Frame().top
);
1448 BMenuField::LabelLayoutItem::IsVisible()
1450 return !fParent
->IsHidden(fParent
);
1455 BMenuField::LabelLayoutItem::SetVisible(bool visible
)
1462 BMenuField::LabelLayoutItem::Frame()
1469 BMenuField::LabelLayoutItem::SetFrame(BRect frame
)
1472 fParent
->_UpdateFrame();
1477 BMenuField::LabelLayoutItem::SetParent(BMenuField
* parent
)
1484 BMenuField::LabelLayoutItem::View()
1491 BMenuField::LabelLayoutItem::BaseMinSize()
1493 fParent
->_ValidateLayoutData();
1495 if (fParent
->Label() == NULL
)
1496 return BSize(-1, -1);
1498 return BSize(fParent
->fLayoutData
->label_width
1499 + be_control_look
->DefaultLabelSpacing(),
1500 fParent
->fLayoutData
->label_height
);
1505 BMenuField::LabelLayoutItem::BaseMaxSize()
1507 return BaseMinSize();
1512 BMenuField::LabelLayoutItem::BasePreferredSize()
1514 return BaseMinSize();
1519 BMenuField::LabelLayoutItem::BaseAlignment()
1521 return BAlignment(B_ALIGN_USE_FULL_WIDTH
, B_ALIGN_USE_FULL_HEIGHT
);
1526 BMenuField::LabelLayoutItem::Archive(BMessage
* into
, bool deep
) const
1528 BArchiver
archiver(into
);
1529 status_t err
= BAbstractLayoutItem::Archive(into
, deep
);
1532 err
= into
->AddRect(kFrameField
, fFrame
);
1534 return archiver
.Finish(err
);
1539 BMenuField::LabelLayoutItem::Instantiate(BMessage
* from
)
1541 if (validate_instantiation(from
, "BMenuField::LabelLayoutItem"))
1542 return new LabelLayoutItem(from
);
1548 // #pragma mark - BMenuField::MenuBarLayoutItem
1551 BMenuField::MenuBarLayoutItem::MenuBarLayoutItem(BMenuField
* parent
)
1556 // by default the part right of the divider shall have an unlimited maximum
1558 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
1562 BMenuField::MenuBarLayoutItem::MenuBarLayoutItem(BMessage
* from
)
1564 BAbstractLayoutItem(from
),
1568 from
->FindRect(kFrameField
, &fFrame
);
1573 BMenuField::MenuBarLayoutItem::FrameInParent() const
1575 return fFrame
.OffsetByCopy(-fParent
->Frame().left
, -fParent
->Frame().top
);
1580 BMenuField::MenuBarLayoutItem::IsVisible()
1582 return !fParent
->IsHidden(fParent
);
1587 BMenuField::MenuBarLayoutItem::SetVisible(bool visible
)
1594 BMenuField::MenuBarLayoutItem::Frame()
1601 BMenuField::MenuBarLayoutItem::SetFrame(BRect frame
)
1604 fParent
->_UpdateFrame();
1609 BMenuField::MenuBarLayoutItem::SetParent(BMenuField
* parent
)
1616 BMenuField::MenuBarLayoutItem::View()
1623 BMenuField::MenuBarLayoutItem::BaseMinSize()
1625 fParent
->_ValidateLayoutData();
1627 BSize size
= fParent
->fLayoutData
->menu_bar_min
;
1628 size
.width
+= 2 * kVMargin
;
1629 size
.height
+= 2 * kVMargin
;
1636 BMenuField::MenuBarLayoutItem::BaseMaxSize()
1638 BSize
size(BaseMinSize());
1639 size
.width
= B_SIZE_UNLIMITED
;
1646 BMenuField::MenuBarLayoutItem::BasePreferredSize()
1648 return BaseMinSize();
1653 BMenuField::MenuBarLayoutItem::BaseAlignment()
1655 return BAlignment(B_ALIGN_USE_FULL_WIDTH
, B_ALIGN_USE_FULL_HEIGHT
);
1660 BMenuField::MenuBarLayoutItem::Archive(BMessage
* into
, bool deep
) const
1662 BArchiver
archiver(into
);
1663 status_t err
= BAbstractLayoutItem::Archive(into
, deep
);
1666 err
= into
->AddRect(kFrameField
, fFrame
);
1668 return archiver
.Finish(err
);
1673 BMenuField::MenuBarLayoutItem::Instantiate(BMessage
* from
)
1675 if (validate_instantiation(from
, "BMenuField::MenuBarLayoutItem"))
1676 return new MenuBarLayoutItem(from
);
1682 B_IF_GCC_2(InvalidateLayout__10BMenuFieldb
, _ZN10BMenuField16InvalidateLayoutEb
)(
1683 BMenuField
* field
, bool descendants
)
1685 perform_data_layout_invalidated data
;
1686 data
.descendants
= descendants
;
1688 field
->Perform(PERFORM_CODE_LAYOUT_INVALIDATED
, &data
);