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