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