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