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