xref: /haiku/src/kits/interface/MenuField.cpp (revision 362f1bd35b70e0c904bacb00f4708fbd6bc0173c)
1 /*
2  * Copyright 2006-2016 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
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
10  */
11 
12 
13 #include <MenuField.h>
14 
15 #include <algorithm>
16 
17 #include <stdio.h>
18 	// for printf in TRACE
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include <AbstractLayoutItem.h>
23 #include <Archivable.h>
24 #include <BMCPrivate.h>
25 #include <ControlLook.h>
26 #include <LayoutUtils.h>
27 #include <MenuBar.h>
28 #include <MenuItem.h>
29 #include <MenuItemPrivate.h>
30 #include <MenuPrivate.h>
31 #include <Message.h>
32 #include <MessageFilter.h>
33 #include <Window.h>
34 
35 #include <binary_compatibility/Interface.h>
36 #include <binary_compatibility/Support.h>
37 
38 
39 #ifdef CALLED
40 #	undef CALLED
41 #endif
42 #ifdef TRACE
43 #	undef TRACE
44 #endif
45 
46 //#define TRACE_MENU_FIELD
47 #ifdef TRACE_MENU_FIELD
48 #	include <FunctionTracer.h>
49 	static int32 sFunctionDepth = -1;
50 #	define CALLED(x...)	FunctionTracer _ft("BMenuField", __FUNCTION__, \
51 							sFunctionDepth)
52 #	define TRACE(x...)	{ BString _to; \
53 							_to.Append(' ', (sFunctionDepth + 1) * 2); \
54 							printf("%s", _to.String()); printf(x); }
55 #else
56 #	define CALLED(x...)
57 #	define TRACE(x...)
58 #endif
59 
60 
61 static const float kMinMenuBarWidth = 20.0f;
62 	// found by experimenting on BeOS R5
63 
64 
65 namespace {
66 	const char* const kFrameField = "BMenuField:layoutItem:frame";
67 	const char* const kMenuBarItemField = "BMenuField:barItem";
68 	const char* const kLabelItemField = "BMenuField:labelItem";
69 }
70 
71 
72 //	#pragma mark - LabelLayoutItem
73 
74 
75 class BMenuField::LabelLayoutItem : public BAbstractLayoutItem {
76 public:
77 								LabelLayoutItem(BMenuField* parent);
78 								LabelLayoutItem(BMessage* archive);
79 
80 			BRect				FrameInParent() const;
81 
82 	virtual	bool				IsVisible();
83 	virtual	void				SetVisible(bool visible);
84 
85 	virtual	BRect				Frame();
86 	virtual	void				SetFrame(BRect frame);
87 
88 			void				SetParent(BMenuField* parent);
89 	virtual	BView*				View();
90 
91 	virtual	BSize				BaseMinSize();
92 	virtual	BSize				BaseMaxSize();
93 	virtual	BSize				BasePreferredSize();
94 	virtual	BAlignment			BaseAlignment();
95 
96 	virtual status_t			Archive(BMessage* into, bool deep = true) const;
97 	static	BArchivable*		Instantiate(BMessage* from);
98 
99 private:
100 			BMenuField*			fParent;
101 			BRect				fFrame;
102 };
103 
104 
105 //	#pragma mark - MenuBarLayoutItem
106 
107 
108 class BMenuField::MenuBarLayoutItem : public BAbstractLayoutItem {
109 public:
110 								MenuBarLayoutItem(BMenuField* parent);
111 								MenuBarLayoutItem(BMessage* from);
112 
113 			BRect				FrameInParent() const;
114 
115 	virtual	bool				IsVisible();
116 	virtual	void				SetVisible(bool visible);
117 
118 	virtual	BRect				Frame();
119 	virtual	void				SetFrame(BRect frame);
120 
121 			void				SetParent(BMenuField* parent);
122 	virtual	BView*				View();
123 
124 	virtual	BSize				BaseMinSize();
125 	virtual	BSize				BaseMaxSize();
126 	virtual	BSize				BasePreferredSize();
127 	virtual	BAlignment			BaseAlignment();
128 
129 	virtual status_t			Archive(BMessage* into, bool deep = true) const;
130 	static	BArchivable*		Instantiate(BMessage* from);
131 
132 private:
133 			BMenuField*			fParent;
134 			BRect				fFrame;
135 };
136 
137 
138 //	#pragma mark - LayoutData
139 
140 
141 struct BMenuField::LayoutData {
142 	LayoutData()
143 		:
144 		label_layout_item(NULL),
145 		menu_bar_layout_item(NULL),
146 		previous_height(-1),
147 		valid(false)
148 	{
149 	}
150 
151 	LabelLayoutItem*	label_layout_item;
152 	MenuBarLayoutItem*	menu_bar_layout_item;
153 	float				previous_height;	// used in FrameResized() for
154 											// invalidation
155 	font_height			font_info;
156 	float				label_width;
157 	float				label_height;
158 	BSize				min;
159 	BSize				menu_bar_min;
160 	bool				valid;
161 };
162 
163 
164 // #pragma mark - MouseDownFilter
165 
166 
167 class MouseDownFilter : public BMessageFilter
168 {
169 public:
170 								MouseDownFilter();
171 	virtual						~MouseDownFilter();
172 
173 	virtual	filter_result		Filter(BMessage* message, BHandler** target);
174 };
175 
176 
177 MouseDownFilter::MouseDownFilter()
178 	:
179 	BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE)
180 {
181 }
182 
183 
184 MouseDownFilter::~MouseDownFilter()
185 {
186 }
187 
188 
189 filter_result
190 MouseDownFilter::Filter(BMessage* message, BHandler** target)
191 {
192 	return message->what == B_MOUSE_DOWN ? B_SKIP_MESSAGE : B_DISPATCH_MESSAGE;
193 }
194 
195 
196 // #pragma mark - BMenuField
197 
198 
199 BMenuField::BMenuField(BRect frame, const char* name, const char* label,
200 	BMenu* menu, uint32 resizingMode, uint32 flags)
201 	:
202 	BView(frame, name, resizingMode, flags)
203 {
204 	CALLED();
205 
206 	TRACE("frame.width: %.2f, height: %.2f\n", frame.Width(), frame.Height());
207 
208 	InitObject(label);
209 
210 	frame.OffsetTo(B_ORIGIN);
211 	_InitMenuBar(menu, frame, false);
212 
213 	InitObject2();
214 }
215 
216 
217 BMenuField::BMenuField(BRect frame, const char* name, const char* label,
218 	BMenu* menu, bool fixedSize, uint32 resizingMode, uint32 flags)
219 	:
220 	BView(frame, name, resizingMode, flags)
221 {
222 	InitObject(label);
223 
224 	fFixedSizeMB = fixedSize;
225 
226 	frame.OffsetTo(B_ORIGIN);
227 	_InitMenuBar(menu, frame, fixedSize);
228 
229 	InitObject2();
230 }
231 
232 
233 BMenuField::BMenuField(const char* name, const char* label, BMenu* menu,
234 	uint32 flags)
235 	:
236 	BView(name, flags | B_FRAME_EVENTS)
237 {
238 	InitObject(label);
239 
240 	_InitMenuBar(menu, BRect(0, 0, 100, 15), true);
241 
242 	InitObject2();
243 }
244 
245 
246 BMenuField::BMenuField(const char* label, BMenu* menu, uint32 flags)
247 	:
248 	BView(NULL, flags | B_FRAME_EVENTS)
249 {
250 	InitObject(label);
251 
252 	_InitMenuBar(menu, BRect(0, 0, 100, 15), true);
253 
254 	InitObject2();
255 }
256 
257 
258 //! Copy&Paste error, should be removed at some point (already private)
259 BMenuField::BMenuField(const char* name, const char* label, BMenu* menu,
260 		BMessage* message, uint32 flags)
261 	:
262 	BView(name, flags | B_FRAME_EVENTS)
263 {
264 	InitObject(label);
265 
266 	_InitMenuBar(menu, BRect(0, 0, 100, 15), true);
267 
268 	InitObject2();
269 }
270 
271 
272 //! Copy&Paste error, should be removed at some point (already private)
273 BMenuField::BMenuField(const char* label, BMenu* menu, BMessage* message)
274 	:
275 	BView(NULL, B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS)
276 {
277 	InitObject(label);
278 
279 	_InitMenuBar(menu, BRect(0, 0, 100, 15), true);
280 
281 	InitObject2();
282 }
283 
284 
285 BMenuField::BMenuField(BMessage* data)
286 	:
287 	BView(BUnarchiver::PrepareArchive(data))
288 {
289 	BUnarchiver unarchiver(data);
290 	const char* label = NULL;
291 	data->FindString("_label", &label);
292 
293 	InitObject(label);
294 
295 	data->FindFloat("_divide", &fDivider);
296 
297 	int32 align;
298 	if (data->FindInt32("_align", &align) == B_OK)
299 		SetAlignment((alignment)align);
300 
301 	if (!BUnarchiver::IsArchiveManaged(data))
302 		_InitMenuBar(data);
303 
304 	unarchiver.Finish();
305 }
306 
307 
308 BMenuField::~BMenuField()
309 {
310 	free(fLabel);
311 
312 	status_t dummy;
313 	if (fMenuTaskID >= 0)
314 		wait_for_thread(fMenuTaskID, &dummy);
315 
316 	delete fLayoutData;
317 	delete fMouseDownFilter;
318 }
319 
320 
321 BArchivable*
322 BMenuField::Instantiate(BMessage* data)
323 {
324 	if (validate_instantiation(data, "BMenuField"))
325 		return new BMenuField(data);
326 
327 	return NULL;
328 }
329 
330 
331 status_t
332 BMenuField::Archive(BMessage* data, bool deep) const
333 {
334 	BArchiver archiver(data);
335 	status_t ret = BView::Archive(data, deep);
336 
337 	if (ret == B_OK && Label())
338 		ret = data->AddString("_label", Label());
339 
340 	if (ret == B_OK && !IsEnabled())
341 		ret = data->AddBool("_disable", true);
342 
343 	if (ret == B_OK)
344 		ret = data->AddInt32("_align", Alignment());
345 	if (ret == B_OK)
346 		ret = data->AddFloat("_divide", Divider());
347 
348 	if (ret == B_OK && fFixedSizeMB)
349 		ret = data->AddBool("be:fixeds", true);
350 
351 	bool dmark = false;
352 	if (_BMCMenuBar_* menuBar = dynamic_cast<_BMCMenuBar_*>(fMenuBar))
353 		dmark = menuBar->IsPopUpMarkerShown();
354 
355 	data->AddBool("be:dmark", dmark);
356 
357 	return archiver.Finish(ret);
358 }
359 
360 
361 status_t
362 BMenuField::AllArchived(BMessage* into) const
363 {
364 	status_t err;
365 	if ((err = BView::AllArchived(into)) != B_OK)
366 		return err;
367 
368 	BArchiver archiver(into);
369 
370 	BArchivable* menuBarItem = fLayoutData->menu_bar_layout_item;
371 	if (archiver.IsArchived(menuBarItem))
372 		err = archiver.AddArchivable(kMenuBarItemField, menuBarItem);
373 
374 	if (err != B_OK)
375 		return err;
376 
377 	BArchivable* labelBarItem = fLayoutData->label_layout_item;
378 	if (archiver.IsArchived(labelBarItem))
379 		err = archiver.AddArchivable(kLabelItemField, labelBarItem);
380 
381 	return err;
382 }
383 
384 
385 status_t
386 BMenuField::AllUnarchived(const BMessage* from)
387 {
388 	BUnarchiver unarchiver(from);
389 
390 	status_t err = B_OK;
391 	if ((err = BView::AllUnarchived(from)) != B_OK)
392 		return err;
393 
394 	_InitMenuBar(from);
395 
396 	if (unarchiver.IsInstantiated(kMenuBarItemField)) {
397 		MenuBarLayoutItem*& menuItem = fLayoutData->menu_bar_layout_item;
398 		err = unarchiver.FindObject(kMenuBarItemField,
399 			BUnarchiver::B_DONT_ASSUME_OWNERSHIP, menuItem);
400 
401 		if (err == B_OK)
402 			menuItem->SetParent(this);
403 		else
404 			return err;
405 	}
406 
407 	if (unarchiver.IsInstantiated(kLabelItemField)) {
408 		LabelLayoutItem*& labelItem = fLayoutData->label_layout_item;
409 		err = unarchiver.FindObject(kLabelItemField,
410 			BUnarchiver::B_DONT_ASSUME_OWNERSHIP, labelItem);
411 
412 		if (err == B_OK)
413 			labelItem->SetParent(this);
414 	}
415 
416 	return err;
417 }
418 
419 
420 void
421 BMenuField::Draw(BRect updateRect)
422 {
423 	_DrawLabel(updateRect);
424 	_DrawMenuBar(updateRect);
425 }
426 
427 
428 void
429 BMenuField::AttachedToWindow()
430 {
431 	CALLED();
432 
433 	// Our low color must match the parent's view color.
434 	if (Parent() != NULL) {
435 		AdoptParentColors();
436 
437 		float tint = B_NO_TINT;
438 		color_which which = ViewUIColor(&tint);
439 
440 		if (which == B_NO_COLOR)
441 			SetLowColor(ViewColor());
442 		else
443 			SetLowUIColor(which, tint);
444 	} else
445 		AdoptSystemColors();
446 }
447 
448 
449 void
450 BMenuField::AllAttached()
451 {
452 	CALLED();
453 
454 	TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
455 
456 	float width = Bounds().Width();
457 	if (!fFixedSizeMB && _MenuBarWidth() < kMinMenuBarWidth) {
458 		// The menu bar is too narrow, resize it to fit the menu items
459 		BMenuItem* item = fMenuBar->ItemAt(0);
460 		if (item != NULL) {
461 			float right;
462 			fMenuBar->GetItemMargins(NULL, NULL, &right, NULL);
463 			width = item->Frame().Width() + kVMargin + _MenuBarOffset() + right;
464 		}
465 	}
466 
467 	ResizeTo(width, fMenuBar->Bounds().Height() + kVMargin * 2);
468 
469 	TRACE("width: %.2f, height: %.2f\n", Frame().Width(), Frame().Height());
470 }
471 
472 
473 void
474 BMenuField::MouseDown(BPoint where)
475 {
476 	BRect bounds = fMenuBar->ConvertFromParent(Bounds());
477 
478 	fMenuBar->StartMenuBar(-1, false, true, &bounds);
479 
480 	fMenuTaskID = spawn_thread((thread_func)_thread_entry,
481 		"_m_task_", B_NORMAL_PRIORITY, this);
482 	if (fMenuTaskID >= 0 && resume_thread(fMenuTaskID) == B_OK) {
483 		if (fMouseDownFilter->Looper() == NULL)
484 			Window()->AddCommonFilter(fMouseDownFilter);
485 
486 		SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
487 	}
488 }
489 
490 
491 void
492 BMenuField::KeyDown(const char* bytes, int32 numBytes)
493 {
494 	switch (bytes[0]) {
495 		case B_SPACE:
496 		case B_RIGHT_ARROW:
497 		case B_DOWN_ARROW:
498 		{
499 			if (!IsEnabled())
500 				break;
501 
502 			BRect bounds = fMenuBar->ConvertFromParent(Bounds());
503 
504 			fMenuBar->StartMenuBar(0, true, true, &bounds);
505 
506 			bounds = Bounds();
507 			bounds.right = fDivider;
508 
509 			Invalidate(bounds);
510 		}
511 
512 		default:
513 			BView::KeyDown(bytes, numBytes);
514 	}
515 }
516 
517 
518 void
519 BMenuField::MakeFocus(bool focused)
520 {
521 	if (IsFocus() == focused)
522 		return;
523 
524 	BView::MakeFocus(focused);
525 
526 	if (Window() != NULL)
527 		Invalidate(); // TODO: use fLayoutData->label_width
528 }
529 
530 
531 void
532 BMenuField::MessageReceived(BMessage* message)
533 {
534 	BView::MessageReceived(message);
535 }
536 
537 
538 void
539 BMenuField::WindowActivated(bool active)
540 {
541 	BView::WindowActivated(active);
542 
543 	if (IsFocus())
544 		Invalidate();
545 }
546 
547 
548 void
549 BMenuField::MouseMoved(BPoint point, uint32 code, const BMessage* message)
550 {
551 	BView::MouseMoved(point, code, message);
552 }
553 
554 
555 void
556 BMenuField::MouseUp(BPoint where)
557 {
558 	Window()->RemoveCommonFilter(fMouseDownFilter);
559 	BView::MouseUp(where);
560 }
561 
562 
563 void
564 BMenuField::DetachedFromWindow()
565 {
566 	BView::DetachedFromWindow();
567 }
568 
569 
570 void
571 BMenuField::AllDetached()
572 {
573 	BView::AllDetached();
574 }
575 
576 
577 void
578 BMenuField::FrameMoved(BPoint newPosition)
579 {
580 	BView::FrameMoved(newPosition);
581 }
582 
583 
584 void
585 BMenuField::FrameResized(float newWidth, float newHeight)
586 {
587 	BView::FrameResized(newWidth, newHeight);
588 
589 	if (fFixedSizeMB) {
590 		// we have let the menubar resize itself, but
591 		// in fixed size mode, the menubar is supposed to
592 		// be at the right end of the view always. Since
593 		// the menu bar is in follow left/right mode then,
594 		// resizing ourselfs might have caused the menubar
595 		// to be outside now
596 		fMenuBar->ResizeTo(_MenuBarWidth(), fMenuBar->Frame().Height());
597 	}
598 
599 	if (newHeight != fLayoutData->previous_height && Label()) {
600 		// The height changed, which means the label has to move and we
601 		// probably also invalidate a part of the borders around the menu bar.
602 		// So don't be shy and invalidate the whole thing.
603 		Invalidate();
604 	}
605 
606 	fLayoutData->previous_height = newHeight;
607 }
608 
609 
610 BMenu*
611 BMenuField::Menu() const
612 {
613 	return fMenu;
614 }
615 
616 
617 BMenuBar*
618 BMenuField::MenuBar() const
619 {
620 	return fMenuBar;
621 }
622 
623 
624 BMenuItem*
625 BMenuField::MenuItem() const
626 {
627 	return fMenuBar->ItemAt(0);
628 }
629 
630 
631 void
632 BMenuField::SetLabel(const char* label)
633 {
634 	if (fLabel) {
635 		if (label && strcmp(fLabel, label) == 0)
636 			return;
637 
638 		free(fLabel);
639 	}
640 
641 	fLabel = strdup(label);
642 
643 	if (Window())
644 		Invalidate();
645 
646 	InvalidateLayout();
647 }
648 
649 
650 const char*
651 BMenuField::Label() const
652 {
653 	return fLabel;
654 }
655 
656 
657 void
658 BMenuField::SetEnabled(bool on)
659 {
660 	if (fEnabled == on)
661 		return;
662 
663 	fEnabled = on;
664 	fMenuBar->SetEnabled(on);
665 
666 	if (Window()) {
667 		fMenuBar->Invalidate(fMenuBar->Bounds());
668 		Invalidate(Bounds());
669 	}
670 }
671 
672 
673 bool
674 BMenuField::IsEnabled() const
675 {
676 	return fEnabled;
677 }
678 
679 
680 void
681 BMenuField::SetAlignment(alignment label)
682 {
683 	fAlign = label;
684 }
685 
686 
687 alignment
688 BMenuField::Alignment() const
689 {
690 	return fAlign;
691 }
692 
693 
694 void
695 BMenuField::SetDivider(float position)
696 {
697 	position = roundf(position);
698 
699 	float delta = fDivider - position;
700 	if (delta == 0.0f)
701 		return;
702 
703 	fDivider = position;
704 
705 	if ((Flags() & B_SUPPORTS_LAYOUT) != 0) {
706 		// We should never get here, since layout support means, we also
707 		// layout the divider, and don't use this method at all.
708 		Relayout();
709 	} else {
710 		BRect dirty(fMenuBar->Frame());
711 
712 		fMenuBar->MoveTo(_MenuBarOffset(), kVMargin);
713 
714 		if (fFixedSizeMB)
715 			fMenuBar->ResizeTo(_MenuBarWidth(), dirty.Height());
716 
717 		dirty = dirty | fMenuBar->Frame();
718 		dirty.InsetBy(-kVMargin, -kVMargin);
719 
720 		Invalidate(dirty);
721 	}
722 }
723 
724 
725 float
726 BMenuField::Divider() const
727 {
728 	return fDivider;
729 }
730 
731 
732 void
733 BMenuField::ShowPopUpMarker()
734 {
735 	if (_BMCMenuBar_* menuBar = dynamic_cast<_BMCMenuBar_*>(fMenuBar)) {
736 		menuBar->TogglePopUpMarker(true);
737 		menuBar->Invalidate();
738 	}
739 }
740 
741 
742 void
743 BMenuField::HidePopUpMarker()
744 {
745 	if (_BMCMenuBar_* menuBar = dynamic_cast<_BMCMenuBar_*>(fMenuBar)) {
746 		menuBar->TogglePopUpMarker(false);
747 		menuBar->Invalidate();
748 	}
749 }
750 
751 
752 BHandler*
753 BMenuField::ResolveSpecifier(BMessage* message, int32 index,
754 	BMessage* specifier, int32 form, const char* property)
755 {
756 	return BView::ResolveSpecifier(message, index, specifier, form, property);
757 }
758 
759 
760 status_t
761 BMenuField::GetSupportedSuites(BMessage* data)
762 {
763 	return BView::GetSupportedSuites(data);
764 }
765 
766 
767 void
768 BMenuField::ResizeToPreferred()
769 {
770 	CALLED();
771 
772 	TRACE("fMenuBar->Frame().width: %.2f, height: %.2f\n",
773 		fMenuBar->Frame().Width(), fMenuBar->Frame().Height());
774 
775 	fMenuBar->ResizeToPreferred();
776 
777 	TRACE("fMenuBar->Frame().width: %.2f, height: %.2f\n",
778 		fMenuBar->Frame().Width(), fMenuBar->Frame().Height());
779 
780 	BView::ResizeToPreferred();
781 
782 	Invalidate();
783 }
784 
785 
786 void
787 BMenuField::GetPreferredSize(float* _width, float* _height)
788 {
789 	CALLED();
790 
791 	_ValidateLayoutData();
792 
793 	if (_width)
794 		*_width = fLayoutData->min.width;
795 
796 	if (_height)
797 		*_height = fLayoutData->min.height;
798 }
799 
800 
801 BSize
802 BMenuField::MinSize()
803 {
804 	CALLED();
805 
806 	_ValidateLayoutData();
807 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min);
808 }
809 
810 
811 BSize
812 BMenuField::MaxSize()
813 {
814 	CALLED();
815 
816 	_ValidateLayoutData();
817 
818 	BSize max = fLayoutData->min;
819 	max.width = B_SIZE_UNLIMITED;
820 
821 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
822 }
823 
824 
825 BSize
826 BMenuField::PreferredSize()
827 {
828 	CALLED();
829 
830 	_ValidateLayoutData();
831 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), fLayoutData->min);
832 }
833 
834 
835 BLayoutItem*
836 BMenuField::CreateLabelLayoutItem()
837 {
838 	if (fLayoutData->label_layout_item == NULL)
839 		fLayoutData->label_layout_item = new LabelLayoutItem(this);
840 
841 	return fLayoutData->label_layout_item;
842 }
843 
844 
845 BLayoutItem*
846 BMenuField::CreateMenuBarLayoutItem()
847 {
848 	if (fLayoutData->menu_bar_layout_item == NULL) {
849 		// align the menu bar in the full available space
850 		fMenuBar->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
851 			B_ALIGN_VERTICAL_UNSET));
852 		fLayoutData->menu_bar_layout_item = new MenuBarLayoutItem(this);
853 	}
854 
855 	return fLayoutData->menu_bar_layout_item;
856 }
857 
858 
859 status_t
860 BMenuField::Perform(perform_code code, void* _data)
861 {
862 	switch (code) {
863 		case PERFORM_CODE_MIN_SIZE:
864 			((perform_data_min_size*)_data)->return_value
865 				= BMenuField::MinSize();
866 			return B_OK;
867 
868 		case PERFORM_CODE_MAX_SIZE:
869 			((perform_data_max_size*)_data)->return_value
870 				= BMenuField::MaxSize();
871 			return B_OK;
872 
873 		case PERFORM_CODE_PREFERRED_SIZE:
874 			((perform_data_preferred_size*)_data)->return_value
875 				= BMenuField::PreferredSize();
876 			return B_OK;
877 
878 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
879 			((perform_data_layout_alignment*)_data)->return_value
880 				= BMenuField::LayoutAlignment();
881 			return B_OK;
882 
883 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
884 			((perform_data_has_height_for_width*)_data)->return_value
885 				= BMenuField::HasHeightForWidth();
886 			return B_OK;
887 
888 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
889 		{
890 			perform_data_get_height_for_width* data
891 				= (perform_data_get_height_for_width*)_data;
892 			BMenuField::GetHeightForWidth(data->width, &data->min, &data->max,
893 				&data->preferred);
894 			return B_OK;
895 		}
896 
897 		case PERFORM_CODE_SET_LAYOUT:
898 		{
899 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
900 			BMenuField::SetLayout(data->layout);
901 			return B_OK;
902 		}
903 
904 		case PERFORM_CODE_LAYOUT_INVALIDATED:
905 		{
906 			perform_data_layout_invalidated* data
907 				= (perform_data_layout_invalidated*)_data;
908 			BMenuField::LayoutInvalidated(data->descendants);
909 			return B_OK;
910 		}
911 
912 		case PERFORM_CODE_DO_LAYOUT:
913 		{
914 			BMenuField::DoLayout();
915 			return B_OK;
916 		}
917 
918 		case PERFORM_CODE_ALL_UNARCHIVED:
919 		{
920 			perform_data_all_unarchived* data
921 				= (perform_data_all_unarchived*)_data;
922 			data->return_value = BMenuField::AllUnarchived(data->archive);
923 			return B_OK;
924 		}
925 
926 		case PERFORM_CODE_ALL_ARCHIVED:
927 		{
928 			perform_data_all_archived* data
929 				= (perform_data_all_archived*)_data;
930 			data->return_value = BMenuField::AllArchived(data->archive);
931 			return B_OK;
932 		}
933 	}
934 
935 	return BView::Perform(code, _data);
936 }
937 
938 
939 void
940 BMenuField::LayoutInvalidated(bool descendants)
941 {
942 	CALLED();
943 
944 	fLayoutData->valid = false;
945 }
946 
947 
948 void
949 BMenuField::DoLayout()
950 {
951 	// Bail out, if we shan't do layout.
952 	if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
953 		return;
954 
955 	CALLED();
956 
957 	// If the user set a layout, we let the base class version call its
958 	// hook.
959 	if (GetLayout() != NULL) {
960 		BView::DoLayout();
961 		return;
962 	}
963 
964 	_ValidateLayoutData();
965 
966 	// validate current size
967 	BSize size(Bounds().Size());
968 	if (size.width < fLayoutData->min.width)
969 		size.width = fLayoutData->min.width;
970 
971 	if (size.height < fLayoutData->min.height)
972 		size.height = fLayoutData->min.height;
973 
974 	// divider
975 	float divider = 0;
976 	if (fLayoutData->label_layout_item != NULL
977 		&& fLayoutData->menu_bar_layout_item != NULL
978 		&& fLayoutData->label_layout_item->Frame().IsValid()
979 		&& fLayoutData->menu_bar_layout_item->Frame().IsValid()) {
980 		// We have valid layout items, they define the divider location.
981 		divider = fabs(fLayoutData->menu_bar_layout_item->Frame().left
982 			- fLayoutData->label_layout_item->Frame().left);
983 	} else if (fLayoutData->label_width > 0) {
984 		divider = fLayoutData->label_width
985 			+ be_control_look->DefaultLabelSpacing();
986 	}
987 
988 	// menu bar
989 	BRect dirty(fMenuBar->Frame());
990 	BRect menuBarFrame(divider + kVMargin, kVMargin, size.width - kVMargin,
991 		size.height - kVMargin);
992 
993 	// place the menu bar and set the divider
994 	BLayoutUtils::AlignInFrame(fMenuBar, menuBarFrame);
995 
996 	fDivider = divider;
997 
998 	// invalidate dirty region
999 	dirty = dirty | fMenuBar->Frame();
1000 	dirty.InsetBy(-kVMargin, -kVMargin);
1001 
1002 	Invalidate(dirty);
1003 }
1004 
1005 
1006 void BMenuField::_ReservedMenuField1() {}
1007 void BMenuField::_ReservedMenuField2() {}
1008 void BMenuField::_ReservedMenuField3() {}
1009 
1010 
1011 void
1012 BMenuField::InitObject(const char* label)
1013 {
1014 	CALLED();
1015 
1016 	fLabel = NULL;
1017 	fMenu = NULL;
1018 	fMenuBar = NULL;
1019 	fAlign = B_ALIGN_LEFT;
1020 	fEnabled = true;
1021 	fFixedSizeMB = false;
1022 	fMenuTaskID = -1;
1023 	fLayoutData = new LayoutData;
1024 	fMouseDownFilter = new MouseDownFilter();
1025 
1026 	SetLabel(label);
1027 
1028 	if (label)
1029 		fDivider = floorf(Frame().Width() / 2.0f);
1030 	else
1031 		fDivider = 0;
1032 }
1033 
1034 
1035 void
1036 BMenuField::InitObject2()
1037 {
1038 	CALLED();
1039 
1040 	if (!fFixedSizeMB) {
1041 		float height;
1042 		fMenuBar->GetPreferredSize(NULL, &height);
1043 		fMenuBar->ResizeTo(_MenuBarWidth(), height);
1044 	}
1045 
1046 	TRACE("frame(%.1f, %.1f, %.1f, %.1f) (%.2f, %.2f)\n",
1047 		fMenuBar->Frame().left, fMenuBar->Frame().top,
1048 		fMenuBar->Frame().right, fMenuBar->Frame().bottom,
1049 		fMenuBar->Frame().Width(), fMenuBar->Frame().Height());
1050 
1051 	fMenuBar->AddFilter(new _BMCFilter_(this, B_MOUSE_DOWN));
1052 }
1053 
1054 
1055 void
1056 BMenuField::_DrawLabel(BRect updateRect)
1057 {
1058 	CALLED();
1059 
1060 	_ValidateLayoutData();
1061 
1062 	const char* label = Label();
1063 	if (label == NULL)
1064 		return;
1065 
1066 	BRect rect;
1067 	if (fLayoutData->label_layout_item != NULL)
1068 		rect = fLayoutData->label_layout_item->FrameInParent();
1069 	else {
1070 		rect = Bounds();
1071 		rect.right = fDivider;
1072 	}
1073 
1074 	if (!rect.IsValid() || !rect.Intersects(updateRect))
1075 		return;
1076 
1077 	uint32 flags = 0;
1078 	if (!IsEnabled())
1079 		flags |= BControlLook::B_DISABLED;
1080 
1081 	// save the current low color
1082 	PushState();
1083 	rgb_color textColor;
1084 
1085 	BPrivate::MenuPrivate menuPrivate(fMenuBar);
1086 	if (menuPrivate.State() != MENU_STATE_CLOSED) {
1087 		// highlight the background of the label grey (like BeOS R5)
1088 		SetLowColor(ui_color(B_MENU_SELECTED_BACKGROUND_COLOR));
1089 		BRect fillRect(rect.InsetByCopy(0, kVMargin));
1090 		FillRect(fillRect, B_SOLID_LOW);
1091 		textColor = ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR);
1092 	} else
1093 		textColor = ui_color(B_PANEL_TEXT_COLOR);
1094 
1095 	be_control_look->DrawLabel(this, label, rect, updateRect, LowColor(), flags,
1096 		BAlignment(fAlign, B_ALIGN_MIDDLE), &textColor);
1097 
1098 	// restore the previous low color
1099 	PopState();
1100 }
1101 
1102 
1103 void
1104 BMenuField::_DrawMenuBar(BRect updateRect)
1105 {
1106 	CALLED();
1107 
1108 	BRect rect(fMenuBar->Frame().InsetByCopy(-kVMargin, -kVMargin));
1109 	if (!rect.IsValid() || !rect.Intersects(updateRect))
1110 		return;
1111 
1112 	uint32 flags = 0;
1113 	if (!IsEnabled())
1114 		flags |= BControlLook::B_DISABLED;
1115 
1116 	if (IsFocus() && Window()->IsActive())
1117 		flags |= BControlLook::B_FOCUSED;
1118 
1119 	be_control_look->DrawMenuFieldFrame(this, rect, updateRect,
1120 		fMenuBar->LowColor(), LowColor(), flags);
1121 }
1122 
1123 
1124 void
1125 BMenuField::InitMenu(BMenu* menu)
1126 {
1127 	menu->SetFont(be_plain_font);
1128 
1129 	int32 index = 0;
1130 	BMenu* subMenu;
1131 
1132 	while ((subMenu = menu->SubmenuAt(index++)) != NULL)
1133 		InitMenu(subMenu);
1134 }
1135 
1136 
1137 /*static*/ int32
1138 BMenuField::_thread_entry(void* arg)
1139 {
1140 	return static_cast<BMenuField*>(arg)->_MenuTask();
1141 }
1142 
1143 
1144 int32
1145 BMenuField::_MenuTask()
1146 {
1147 	if (!LockLooper())
1148 		return 0;
1149 
1150 	Invalidate();
1151 	UnlockLooper();
1152 
1153 	bool tracking;
1154 	do {
1155 		snooze(20000);
1156 		if (!LockLooper())
1157 			return 0;
1158 
1159 		tracking = fMenuBar->fTracking;
1160 
1161 		UnlockLooper();
1162 	} while (tracking);
1163 
1164 	if (LockLooper()) {
1165 		Invalidate();
1166 		UnlockLooper();
1167 	}
1168 
1169 	return 0;
1170 }
1171 
1172 
1173 void
1174 BMenuField::_UpdateFrame()
1175 {
1176 	CALLED();
1177 
1178 	if (fLayoutData->label_layout_item == NULL
1179 		|| fLayoutData->menu_bar_layout_item == NULL) {
1180 		return;
1181 	}
1182 
1183 	BRect labelFrame = fLayoutData->label_layout_item->Frame();
1184 	BRect menuFrame = fLayoutData->menu_bar_layout_item->Frame();
1185 
1186 	if (!labelFrame.IsValid() || !menuFrame.IsValid())
1187 		return;
1188 
1189 	// update divider
1190 	fDivider = menuFrame.left - labelFrame.left;
1191 
1192 	// update our frame
1193 	MoveTo(labelFrame.left, labelFrame.top);
1194 	BSize oldSize = Bounds().Size();
1195 	ResizeTo(menuFrame.left + menuFrame.Width() - labelFrame.left,
1196 		menuFrame.top + menuFrame.Height() - labelFrame.top);
1197 	BSize newSize = Bounds().Size();
1198 
1199 	// If the size changes, ResizeTo() will trigger a relayout, otherwise
1200 	// we need to do that explicitly.
1201 	if (newSize != oldSize)
1202 		Relayout();
1203 }
1204 
1205 
1206 void
1207 BMenuField::_InitMenuBar(BMenu* menu, BRect frame, bool fixedSize)
1208 {
1209 	CALLED();
1210 
1211 	if ((Flags() & B_SUPPORTS_LAYOUT) != 0) {
1212 		fMenuBar = new _BMCMenuBar_(this);
1213 	} else {
1214 		frame.left = _MenuBarOffset();
1215 		frame.top = kVMargin;
1216 		frame.right -= kVMargin;
1217 		frame.bottom -= kVMargin;
1218 
1219 		TRACE("frame(%.1f, %.1f, %.1f, %.1f) (%.2f, %.2f)\n",
1220 			frame.left, frame.top, frame.right, frame.bottom,
1221 			frame.Width(), frame.Height());
1222 
1223 		fMenuBar = new _BMCMenuBar_(frame, fixedSize, this);
1224 	}
1225 
1226 	if (fixedSize) {
1227 		// align the menu bar in the full available space
1228 		fMenuBar->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
1229 			B_ALIGN_VERTICAL_UNSET));
1230 	} else {
1231 		// align the menu bar left in the available space
1232 		fMenuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
1233 			B_ALIGN_VERTICAL_UNSET));
1234 	}
1235 
1236 	AddChild(fMenuBar);
1237 
1238 	_AddMenu(menu);
1239 
1240 	fMenuBar->SetFont(be_plain_font);
1241 }
1242 
1243 
1244 void
1245 BMenuField::_InitMenuBar(const BMessage* archive)
1246 {
1247 	bool fixed;
1248 	if (archive->FindBool("be:fixeds", &fixed) == B_OK)
1249 		fFixedSizeMB = fixed;
1250 
1251 	fMenuBar = (BMenuBar*)FindView("_mc_mb_");
1252 	if (fMenuBar == NULL) {
1253 		_InitMenuBar(new BMenu(""), BRect(0, 0, 100, 15), fFixedSizeMB);
1254 		InitObject2();
1255 	} else {
1256 		fMenuBar->AddFilter(new _BMCFilter_(this, B_MOUSE_DOWN));
1257 			// this is normally done in InitObject2()
1258 	}
1259 
1260 	_AddMenu(fMenuBar->SubmenuAt(0));
1261 
1262 	bool disable;
1263 	if (archive->FindBool("_disable", &disable) == B_OK)
1264 		SetEnabled(!disable);
1265 
1266 	bool dmark = false;
1267 	archive->FindBool("be:dmark", &dmark);
1268 	_BMCMenuBar_* menuBar = dynamic_cast<_BMCMenuBar_*>(fMenuBar);
1269 	if (menuBar != NULL)
1270 		menuBar->TogglePopUpMarker(dmark);
1271 }
1272 
1273 
1274 void
1275 BMenuField::_AddMenu(BMenu* menu)
1276 {
1277 	if (menu == NULL || fMenuBar == NULL)
1278 		return;
1279 
1280 	fMenu = menu;
1281 	InitMenu(menu);
1282 
1283 	BMenuItem* item = NULL;
1284 	if (!menu->IsRadioMode() || (item = menu->FindMarked()) == NULL) {
1285 		// find the first enabled non-seperator item
1286 		int32 itemCount = menu->CountItems();
1287 		for (int32 i = 0; i < itemCount; i++) {
1288 			item = menu->ItemAt((int32)i);
1289 			if (item == NULL || !item->IsEnabled()
1290 				|| dynamic_cast<BSeparatorItem*>(item) != NULL) {
1291 				item = NULL;
1292 				continue;
1293 			}
1294 			break;
1295 		}
1296 	}
1297 
1298 	if (item == NULL) {
1299 		fMenuBar->AddItem(menu);
1300 		return;
1301 	}
1302 
1303 	// build an empty copy of item
1304 
1305 	BMessage data;
1306 	status_t result = item->Archive(&data, false);
1307 	if (result != B_OK) {
1308 		fMenuBar->AddItem(menu);
1309 		return;
1310 	}
1311 
1312 	BArchivable* object = instantiate_object(&data);
1313 	if (object == NULL) {
1314 		fMenuBar->AddItem(menu);
1315 		return;
1316 	}
1317 
1318 	BMenuItem* newItem = static_cast<BMenuItem*>(object);
1319 
1320 	// unset parameters
1321 	BPrivate::MenuItemPrivate newMenuItemPrivate(newItem);
1322 	newMenuItemPrivate.Uninstall();
1323 
1324 	// set the menu
1325 	newMenuItemPrivate.SetSubmenu(menu);
1326 	fMenuBar->AddItem(newItem);
1327 }
1328 
1329 
1330 void
1331 BMenuField::_ValidateLayoutData()
1332 {
1333 	CALLED();
1334 
1335 	if (fLayoutData->valid)
1336 		return;
1337 
1338 	// cache font height
1339 	font_height& fh = fLayoutData->font_info;
1340 	GetFontHeight(&fh);
1341 
1342 	const char* label = Label();
1343 	if (label != NULL) {
1344 		fLayoutData->label_width = ceilf(StringWidth(label));
1345 		fLayoutData->label_height = ceilf(fh.ascent) + ceilf(fh.descent);
1346 	} else {
1347 		fLayoutData->label_width = 0;
1348 		fLayoutData->label_height = 0;
1349 	}
1350 
1351 	// compute the minimal divider
1352 	float divider = 0;
1353 	if (fLayoutData->label_width > 0) {
1354 		divider = fLayoutData->label_width
1355 			+ be_control_look->DefaultLabelSpacing();
1356 	}
1357 
1358 	// If we shan't do real layout, we let the current divider take influence.
1359 	if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
1360 		divider = std::max(divider, fDivider);
1361 
1362 	// get the minimal (== preferred) menu bar size
1363 	// TODO: BMenu::MinSize() is using the ResizeMode() to decide the
1364 	// minimum width. If the mode is B_FOLLOW_LEFT_RIGHT, it will use the
1365 	// parent's frame width or window's frame width. So at least the returned
1366 	// size is wrong, but apparantly it doesn't have much bad effect.
1367 	fLayoutData->menu_bar_min = fMenuBar->MinSize();
1368 
1369 	TRACE("menu bar min width: %.2f\n", fLayoutData->menu_bar_min.width);
1370 
1371 	// compute our minimal (== preferred) size
1372 	BSize min(fLayoutData->menu_bar_min);
1373 	min.width += 2 * kVMargin;
1374 	min.height += 2 * kVMargin;
1375 
1376 	if (divider > 0)
1377 		min.width += divider;
1378 
1379 	if (fLayoutData->label_height > min.height)
1380 		min.height = fLayoutData->label_height;
1381 
1382 	fLayoutData->min = min;
1383 
1384 	fLayoutData->valid = true;
1385 	ResetLayoutInvalidation();
1386 
1387 	TRACE("width: %.2f, height: %.2f\n", min.width, min.height);
1388 }
1389 
1390 
1391 float
1392 BMenuField::_MenuBarOffset() const
1393 {
1394 	return std::max(fDivider + kVMargin, kVMargin);
1395 }
1396 
1397 
1398 float
1399 BMenuField::_MenuBarWidth() const
1400 {
1401 	return Bounds().Width() - (_MenuBarOffset() + kVMargin);
1402 }
1403 
1404 
1405 // #pragma mark - BMenuField::LabelLayoutItem
1406 
1407 
1408 BMenuField::LabelLayoutItem::LabelLayoutItem(BMenuField* parent)
1409 	:
1410 	fParent(parent),
1411 	fFrame()
1412 {
1413 }
1414 
1415 
1416 BMenuField::LabelLayoutItem::LabelLayoutItem(BMessage* from)
1417 	:
1418 	BAbstractLayoutItem(from),
1419 	fParent(NULL),
1420 	fFrame()
1421 {
1422 	from->FindRect(kFrameField, &fFrame);
1423 }
1424 
1425 
1426 BRect
1427 BMenuField::LabelLayoutItem::FrameInParent() const
1428 {
1429 	return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
1430 }
1431 
1432 
1433 bool
1434 BMenuField::LabelLayoutItem::IsVisible()
1435 {
1436 	return !fParent->IsHidden(fParent);
1437 }
1438 
1439 
1440 void
1441 BMenuField::LabelLayoutItem::SetVisible(bool visible)
1442 {
1443 	// not allowed
1444 }
1445 
1446 
1447 BRect
1448 BMenuField::LabelLayoutItem::Frame()
1449 {
1450 	return fFrame;
1451 }
1452 
1453 
1454 void
1455 BMenuField::LabelLayoutItem::SetFrame(BRect frame)
1456 {
1457 	fFrame = frame;
1458 	fParent->_UpdateFrame();
1459 }
1460 
1461 
1462 void
1463 BMenuField::LabelLayoutItem::SetParent(BMenuField* parent)
1464 {
1465 	fParent = parent;
1466 }
1467 
1468 
1469 BView*
1470 BMenuField::LabelLayoutItem::View()
1471 {
1472 	return fParent;
1473 }
1474 
1475 
1476 BSize
1477 BMenuField::LabelLayoutItem::BaseMinSize()
1478 {
1479 	fParent->_ValidateLayoutData();
1480 
1481 	if (fParent->Label() == NULL)
1482 		return BSize(-1, -1);
1483 
1484 	return BSize(fParent->fLayoutData->label_width
1485 			+ be_control_look->DefaultLabelSpacing(),
1486 		fParent->fLayoutData->label_height);
1487 }
1488 
1489 
1490 BSize
1491 BMenuField::LabelLayoutItem::BaseMaxSize()
1492 {
1493 	return BaseMinSize();
1494 }
1495 
1496 
1497 BSize
1498 BMenuField::LabelLayoutItem::BasePreferredSize()
1499 {
1500 	return BaseMinSize();
1501 }
1502 
1503 
1504 BAlignment
1505 BMenuField::LabelLayoutItem::BaseAlignment()
1506 {
1507 	return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
1508 }
1509 
1510 
1511 status_t
1512 BMenuField::LabelLayoutItem::Archive(BMessage* into, bool deep) const
1513 {
1514 	BArchiver archiver(into);
1515 	status_t err = BAbstractLayoutItem::Archive(into, deep);
1516 
1517 	if (err == B_OK)
1518 		err = into->AddRect(kFrameField, fFrame);
1519 
1520 	return archiver.Finish(err);
1521 }
1522 
1523 
1524 BArchivable*
1525 BMenuField::LabelLayoutItem::Instantiate(BMessage* from)
1526 {
1527 	if (validate_instantiation(from, "BMenuField::LabelLayoutItem"))
1528 		return new LabelLayoutItem(from);
1529 
1530 	return NULL;
1531 }
1532 
1533 
1534 // #pragma mark - BMenuField::MenuBarLayoutItem
1535 
1536 
1537 BMenuField::MenuBarLayoutItem::MenuBarLayoutItem(BMenuField* parent)
1538 	:
1539 	fParent(parent),
1540 	fFrame()
1541 {
1542 	// by default the part right of the divider shall have an unlimited maximum
1543 	// width
1544 	SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
1545 }
1546 
1547 
1548 BMenuField::MenuBarLayoutItem::MenuBarLayoutItem(BMessage* from)
1549 	:
1550 	BAbstractLayoutItem(from),
1551 	fParent(NULL),
1552 	fFrame()
1553 {
1554 	from->FindRect(kFrameField, &fFrame);
1555 }
1556 
1557 
1558 BRect
1559 BMenuField::MenuBarLayoutItem::FrameInParent() const
1560 {
1561 	return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
1562 }
1563 
1564 
1565 bool
1566 BMenuField::MenuBarLayoutItem::IsVisible()
1567 {
1568 	return !fParent->IsHidden(fParent);
1569 }
1570 
1571 
1572 void
1573 BMenuField::MenuBarLayoutItem::SetVisible(bool visible)
1574 {
1575 	// not allowed
1576 }
1577 
1578 
1579 BRect
1580 BMenuField::MenuBarLayoutItem::Frame()
1581 {
1582 	return fFrame;
1583 }
1584 
1585 
1586 void
1587 BMenuField::MenuBarLayoutItem::SetFrame(BRect frame)
1588 {
1589 	fFrame = frame;
1590 	fParent->_UpdateFrame();
1591 }
1592 
1593 
1594 void
1595 BMenuField::MenuBarLayoutItem::SetParent(BMenuField* parent)
1596 {
1597 	fParent = parent;
1598 }
1599 
1600 
1601 BView*
1602 BMenuField::MenuBarLayoutItem::View()
1603 {
1604 	return fParent;
1605 }
1606 
1607 
1608 BSize
1609 BMenuField::MenuBarLayoutItem::BaseMinSize()
1610 {
1611 	fParent->_ValidateLayoutData();
1612 
1613 	BSize size = fParent->fLayoutData->menu_bar_min;
1614 	size.width += 2 * kVMargin;
1615 	size.height += 2 * kVMargin;
1616 
1617 	return size;
1618 }
1619 
1620 
1621 BSize
1622 BMenuField::MenuBarLayoutItem::BaseMaxSize()
1623 {
1624 	BSize size(BaseMinSize());
1625 	size.width = B_SIZE_UNLIMITED;
1626 
1627 	return size;
1628 }
1629 
1630 
1631 BSize
1632 BMenuField::MenuBarLayoutItem::BasePreferredSize()
1633 {
1634 	return BaseMinSize();
1635 }
1636 
1637 
1638 BAlignment
1639 BMenuField::MenuBarLayoutItem::BaseAlignment()
1640 {
1641 	return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
1642 }
1643 
1644 
1645 status_t
1646 BMenuField::MenuBarLayoutItem::Archive(BMessage* into, bool deep) const
1647 {
1648 	BArchiver archiver(into);
1649 	status_t err = BAbstractLayoutItem::Archive(into, deep);
1650 
1651 	if (err == B_OK)
1652 		err = into->AddRect(kFrameField, fFrame);
1653 
1654 	return archiver.Finish(err);
1655 }
1656 
1657 
1658 BArchivable*
1659 BMenuField::MenuBarLayoutItem::Instantiate(BMessage* from)
1660 {
1661 	if (validate_instantiation(from, "BMenuField::MenuBarLayoutItem"))
1662 		return new MenuBarLayoutItem(from);
1663 	return NULL;
1664 }
1665 
1666 
1667 extern "C" void
1668 B_IF_GCC_2(InvalidateLayout__10BMenuFieldb, _ZN10BMenuField16InvalidateLayoutEb)(
1669 	BMenuField* field, bool descendants)
1670 {
1671 	perform_data_layout_invalidated data;
1672 	data.descendants = descendants;
1673 
1674 	field->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
1675 }
1676