xref: /haiku/src/kits/interface/Menu.cpp (revision 6a45488dd93a920bccd174756ad44613d844189c)
1 /*
2  * Copyright 2001-2006, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Stefano Ceccherini (burton666@libero.it)
8  */
9 
10 #include <new>
11 #include <ctype.h>
12 #include <string.h>
13 
14 #include <Debug.h>
15 #include <File.h>
16 #include <FindDirectory.h>
17 #include <Layout.h>
18 #include <LayoutUtils.h>
19 #include <Menu.h>
20 #include <MenuBar.h>
21 #include <MenuItem.h>
22 #include <Path.h>
23 #include <PropertyInfo.h>
24 #include <Screen.h>
25 #include <Window.h>
26 
27 #include <AppServerLink.h>
28 #include <BMCPrivate.h>
29 #include <MenuPrivate.h>
30 #include <MenuWindow.h>
31 #include <ServerProtocol.h>
32 
33 using std::nothrow;
34 using BPrivate::BMenuWindow;
35 
36 
37 class _ExtraMenuData_ {
38 public:
39 	menu_tracking_hook trackingHook;
40 	void *trackingState;
41 
42 	_ExtraMenuData_(menu_tracking_hook func, void *state)
43 	{
44 		trackingHook = func;
45 		trackingState = state;
46 	};
47 };
48 
49 
50 menu_info BMenu::sMenuInfo;
51 bool BMenu::sAltAsCommandKey;
52 
53 
54 static property_info
55 sPropList[] = {
56 	{ "Enabled", { B_GET_PROPERTY, 0 },
57 		{ B_DIRECT_SPECIFIER, 0 }, "Returns true if menu or menu item is enabled; false "
58 		"otherwise.",
59 		0, { B_BOOL_TYPE }
60 	},
61 
62 	{ "Enabled", { B_SET_PROPERTY, 0 },
63 		{ B_DIRECT_SPECIFIER, 0 }, "Enables or disables menu or menu item.",
64 		0, { B_BOOL_TYPE }
65 	},
66 
67 	{ "Label", { B_GET_PROPERTY, 0 },
68 		{ B_DIRECT_SPECIFIER, 0 }, "Returns the string label of the menu or menu item.",
69 		0, { B_STRING_TYPE }
70 	},
71 
72 	{ "Label", { B_SET_PROPERTY, 0 },
73 		{ B_DIRECT_SPECIFIER, 0 }, "Sets the string label of the menu or menu item.",
74 		0, { B_STRING_TYPE }
75 	},
76 
77 	{ "Mark", { B_GET_PROPERTY, 0 },
78 		{ B_DIRECT_SPECIFIER, 0 }, "Returns true if the menu item or the menu's superitem "
79 		"is marked; false otherwise.",
80 		0, { B_BOOL_TYPE }
81 	},
82 
83 	{ "Mark", { B_SET_PROPERTY, 0 },
84 		{ B_DIRECT_SPECIFIER, 0 }, "Marks or unmarks the menu item or the menu's superitem.",
85 		0, { B_BOOL_TYPE }
86 	},
87 
88 	{ "Menu", { B_CREATE_PROPERTY, 0 },
89 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
90 		"Adds a new menu item at the specified index with the text label found in \"data\" "
91 		"and the int32 command found in \"what\" (used as the what field in the CMessage "
92 		"sent by the item)." , 0, {},
93 		{ 	{{{"data", B_STRING_TYPE}}}
94 		}
95 	},
96 
97 	{ "Menu", { B_DELETE_PROPERTY, 0 },
98 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
99 		"Removes the selected menu or menus.", 0, {}
100 	},
101 
102 	{ "Menu", { },
103 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
104 		"Directs scripting message to the specified menu, first popping the current "
105 		"specifier off the stack.", 0, {}
106 	},
107 
108 	{ "MenuItem", { B_COUNT_PROPERTIES, 0 },
109 		{ B_DIRECT_SPECIFIER, 0 }, "Counts the number of menu items in the specified menu.",
110 		0, { B_INT32_TYPE }
111 	},
112 
113 	{ "MenuItem", { B_CREATE_PROPERTY, 0 },
114 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
115 		"Adds a new menu item at the specified index with the text label found in \"data\" "
116 		"and the int32 command found in \"what\" (used as the what field in the CMessage "
117 		"sent by the item).", 0, {},
118 		{	{ {{"data", B_STRING_TYPE },
119 			{"be:invoke_message", B_MESSAGE_TYPE},
120 			{"what", B_INT32_TYPE},
121 			{"be:target", B_MESSENGER_TYPE}} }
122 		}
123 	},
124 
125 	{ "MenuItem", { B_DELETE_PROPERTY, 0 },
126 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
127 		"Removes the specified menu item from its parent menu."
128 	},
129 
130 	{ "MenuItem", { B_EXECUTE_PROPERTY, 0 },
131 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
132 		"Invokes the specified menu item."
133 	},
134 
135 	{ "MenuItem", { },
136 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
137 		"Directs scripting message to the specified menu, first popping the current "
138 		"specifier off the stack."
139 	},
140 
141 	{}
142 };
143 
144 
145 const char *kEmptyMenuLabel = "<empty>";
146 
147 
148 struct BMenu::LayoutData {
149 	BSize	preferred;
150 };
151 
152 
153 BMenu::BMenu(const char *name, menu_layout layout)
154 	:	BView(BRect(0, 0, 0, 0), name, 0, B_WILL_DRAW),
155 		fChosenItem(NULL),
156 		fPad(14.0f, 2.0f, 20.0f, 0.0f),
157 		fSelected(NULL),
158 		fCachedMenuWindow(NULL),
159 		fSuper(NULL),
160 		fSuperitem(NULL),
161 		fAscent(-1.0f),
162 		fDescent(-1.0f),
163 		fFontHeight(-1.0f),
164 		fState(0),
165 		fLayout(layout),
166 		fExtraRect(NULL),
167 		fMaxContentWidth(0.0f),
168 		fInitMatrixSize(NULL),
169 		fExtraMenuData(NULL),
170 		fSubmenus(0),
171 		fTrigger(0),
172 		fResizeToFit(true),
173 		fUseCachedMenuLayout(false),
174 		fEnabled(true),
175 		fDynamicName(false),
176 		fRadioMode(false),
177 		fTrackNewBounds(false),
178 		fStickyMode(false),
179 		fIgnoreHidden(true),
180 		fTriggerEnabled(true),
181 		fRedrawAfterSticky(false),
182 		fAttachAborted(false)
183 {
184 	InitData(NULL);
185 }
186 
187 
188 BMenu::BMenu(const char *name, float width, float height)
189 	:	BView(BRect(0.0f, width, 0.0f, height), name, 0, B_WILL_DRAW),
190 		fChosenItem(NULL),
191 		fSelected(NULL),
192 		fCachedMenuWindow(NULL),
193 		fSuper(NULL),
194 		fSuperitem(NULL),
195 		fAscent(-1.0f),
196 		fDescent(-1.0f),
197 		fFontHeight(-1.0f),
198 		fState(0),
199 		fLayout(B_ITEMS_IN_MATRIX),
200 		fExtraRect(NULL),
201 		fMaxContentWidth(0.0f),
202 		fInitMatrixSize(NULL),
203 		fExtraMenuData(NULL),
204 		fSubmenus(0),
205 		fTrigger(0),
206 		fResizeToFit(true),
207 		fUseCachedMenuLayout(false),
208 		fEnabled(true),
209 		fDynamicName(false),
210 		fRadioMode(false),
211 		fTrackNewBounds(false),
212 		fStickyMode(false),
213 		fIgnoreHidden(true),
214 		fTriggerEnabled(true),
215 		fRedrawAfterSticky(false),
216 		fAttachAborted(false)
217 {
218 	InitData(NULL);
219 }
220 
221 
222 BMenu::~BMenu()
223 {
224 	DeleteMenuWindow();
225 
226 	RemoveItems(0, CountItems(), true);
227 
228 	delete fInitMatrixSize;
229 	delete fExtraMenuData;
230 	delete fLayoutData;
231 }
232 
233 
234 BMenu::BMenu(BMessage *archive)
235 	:	BView(archive),
236 		fChosenItem(NULL),
237 		fPad(14.0f, 2.0f, 20.0f, 0.0f),
238 		fSelected(NULL),
239 		fCachedMenuWindow(NULL),
240 		fSuper(NULL),
241 		fSuperitem(NULL),
242 		fAscent(-1.0f),
243 		fDescent(-1.0f),
244 		fFontHeight(-1.0f),
245 		fState(0),
246 		fLayout(B_ITEMS_IN_ROW),
247 		fExtraRect(NULL),
248 		fMaxContentWidth(0.0f),
249 		fInitMatrixSize(NULL),
250 		fExtraMenuData(NULL),
251 		fSubmenus(0),
252 		fTrigger(0),
253 		fResizeToFit(true),
254 		fUseCachedMenuLayout(false),
255 		fEnabled(true),
256 		fDynamicName(false),
257 		fRadioMode(false),
258 		fTrackNewBounds(false),
259 		fStickyMode(false),
260 		fIgnoreHidden(true),
261 		fTriggerEnabled(true),
262 		fRedrawAfterSticky(false),
263 		fAttachAborted(false)
264 {
265 	InitData(archive);
266 }
267 
268 
269 BArchivable *
270 BMenu::Instantiate(BMessage *data)
271 {
272 	if (validate_instantiation(data, "BMenu"))
273 		return new (nothrow) BMenu(data);
274 
275 	return NULL;
276 }
277 
278 
279 status_t
280 BMenu::Archive(BMessage *data, bool deep) const
281 {
282 	status_t err = BView::Archive(data, deep);
283 
284 	if (err == B_OK && Layout() != B_ITEMS_IN_ROW)
285 		err = data->AddInt32("_layout", Layout());
286 	if (err == B_OK)
287 		err = data->AddBool("_rsize_to_fit", fResizeToFit);
288 	if (err == B_OK)
289 		err = data->AddBool("_disable", !IsEnabled());
290 	if (err ==  B_OK)
291 		err = data->AddBool("_radio", IsRadioMode());
292 	if (err == B_OK)
293 		err = data->AddBool("_trig_disabled", AreTriggersEnabled());
294 	if (err == B_OK)
295 		err = data->AddBool("_dyn_label", fDynamicName);
296 	if (err == B_OK)
297 		err = data->AddFloat("_maxwidth", fMaxContentWidth);
298 	if (err == B_OK && deep) {
299 		BMenuItem *item = NULL;
300 		int32 index = 0;
301 		while ((item = ItemAt(index++)) != NULL) {
302 			BMessage itemData;
303 			item->Archive(&itemData, deep);
304 			err = data->AddMessage("_items", &itemData);
305 			if (err != B_OK)
306 				break;
307 			if (fLayout == B_ITEMS_IN_MATRIX) {
308 				err = data->AddRect("_i_frames", item->fBounds);
309 			}
310 		}
311 	}
312 
313 	return err;
314 }
315 
316 
317 void
318 BMenu::AttachedToWindow()
319 {
320 	BView::AttachedToWindow();
321 
322 	sAltAsCommandKey = true;
323 	key_map *keys = NULL;
324 	char *chars = NULL;
325 	get_key_map(&keys, &chars);
326 	if (keys == NULL || keys->left_command_key != 0x5d || keys->right_command_key != 0x5f)
327 		sAltAsCommandKey = false;
328 	free(chars);
329 	free(keys);
330 
331 	BMenuItem *superItem = Superitem();
332 	BMenu *superMenu = Supermenu();
333 	if (AddDynamicItem(B_INITIAL_ADD)) {
334 		do {
335 			if (superMenu != NULL && !superMenu->OkToProceed(superItem)) {
336 				AddDynamicItem(B_ABORT);
337 				fAttachAborted = true;
338 				break;
339 			}
340 		} while (AddDynamicItem(B_PROCESSING));
341 	}
342 
343 	if (!fAttachAborted) {
344 		CacheFontInfo();
345 		LayoutItems(0);
346 		UpdateWindowViewSize(false);
347 	}
348 }
349 
350 
351 void
352 BMenu::DetachedFromWindow()
353 {
354 	BView::DetachedFromWindow();
355 }
356 
357 
358 bool
359 BMenu::AddItem(BMenuItem *item)
360 {
361 	return AddItem(item, CountItems());
362 }
363 
364 
365 bool
366 BMenu::AddItem(BMenuItem *item, int32 index)
367 {
368 	if (fLayout == B_ITEMS_IN_MATRIX)
369 		debugger("BMenu::AddItem(BMenuItem *, int32) this method can only "
370 				"be called if the menu layout is not B_ITEMS_IN_MATRIX");
371 
372 	if (!item || !_AddItem(item, index))
373 		return false;
374 
375 	InvalidateLayout();
376 	if (LockLooper()) {
377 		if (!Window()->IsHidden()) {
378 			LayoutItems(index);
379 			UpdateWindowViewSize(false);
380 			Invalidate();
381 		}
382 		UnlockLooper();
383 	}
384 	return true;
385 }
386 
387 
388 bool
389 BMenu::AddItem(BMenuItem *item, BRect frame)
390 {
391 	if (fLayout != B_ITEMS_IN_MATRIX)
392 		debugger("BMenu::AddItem(BMenuItem *, BRect) this method can only "
393 			"be called if the menu layout is B_ITEMS_IN_MATRIX");
394 
395 	if (!item)
396 		return false;
397 
398 	item->fBounds = frame;
399 
400 	int32 index = CountItems();
401 	if (!_AddItem(item, index)) {
402 		return false;
403 	}
404 
405 	if (LockLooper()) {
406 		if (!Window()->IsHidden()) {
407 			LayoutItems(index);
408 			Invalidate();
409 		}
410 		UnlockLooper();
411 	}
412 
413 	return true;
414 }
415 
416 
417 bool
418 BMenu::AddItem(BMenu *submenu)
419 {
420 	BMenuItem *item = new (nothrow) BMenuItem(submenu);
421 	if (!item)
422 		return false;
423 
424 	if (!AddItem(item, CountItems())) {
425 		item->fSubmenu = NULL;
426 		delete item;
427 		return false;
428 	}
429 
430 	return true;
431 }
432 
433 
434 bool
435 BMenu::AddItem(BMenu *submenu, int32 index)
436 {
437 	if (fLayout == B_ITEMS_IN_MATRIX)
438 		debugger("BMenu::AddItem(BMenuItem *, int32) this method can only "
439 				"be called if the menu layout is not B_ITEMS_IN_MATRIX");
440 
441 	BMenuItem *item = new (nothrow) BMenuItem(submenu);
442 	if (!item)
443 		return false;
444 
445 	if (!AddItem(item, index)) {
446 		item->fSubmenu = NULL;
447 		delete item;
448 		return false;
449 	}
450 
451 	return true;
452 }
453 
454 
455 bool
456 BMenu::AddItem(BMenu *submenu, BRect frame)
457 {
458 	if (fLayout != B_ITEMS_IN_MATRIX)
459 		debugger("BMenu::AddItem(BMenu *, BRect) this method can only "
460 			"be called if the menu layout is B_ITEMS_IN_MATRIX");
461 
462 	BMenuItem *item = new (nothrow) BMenuItem(submenu);
463 	if (!item)
464 		return false;
465 
466 	if (!AddItem(item, frame)) {
467 		item->fSubmenu = NULL;
468 		delete item;
469 		return false;
470 	}
471 
472 	return true;
473 }
474 
475 
476 bool
477 BMenu::AddList(BList *list, int32 index)
478 {
479 	// TODO: test this function, it's not documented in the bebook.
480 	if (list == NULL)
481 		return false;
482 
483 	bool locked = LockLooper();
484 
485 	int32 numItems = list->CountItems();
486 	for (int32 i = 0; i < numItems; i++) {
487 		BMenuItem *item = static_cast<BMenuItem *>(list->ItemAt(i));
488 		if (item != NULL) {
489 			if (!_AddItem(item, index + i))
490 				break;
491 		}
492 	}
493 
494 	InvalidateLayout();
495 	if (locked && Window() != NULL && !Window()->IsHidden()) {
496 		// Make sure we update the layout if needed.
497 		LayoutItems(index);
498 		UpdateWindowViewSize(false);
499 		Invalidate();
500 	}
501 
502 	if (locked)
503 		UnlockLooper();
504 
505 	return true;
506 }
507 
508 
509 bool
510 BMenu::AddSeparatorItem()
511 {
512 	BMenuItem *item = new (nothrow) BSeparatorItem();
513 	if (!item || !AddItem(item, CountItems())) {
514 		delete item;
515 		return false;
516 	}
517 
518 	return true;
519 }
520 
521 
522 bool
523 BMenu::RemoveItem(BMenuItem *item)
524 {
525 	// TODO: Check if item is also deleted
526 	return RemoveItems(0, 0, item, false);
527 }
528 
529 
530 BMenuItem *
531 BMenu::RemoveItem(int32 index)
532 {
533 	BMenuItem *item = ItemAt(index);
534 	if (item != NULL)
535 		RemoveItems(0, 0, item, false);
536 	return item;
537 }
538 
539 
540 bool
541 BMenu::RemoveItems(int32 index, int32 count, bool del)
542 {
543 	return RemoveItems(index, count, NULL, del);
544 }
545 
546 
547 bool
548 BMenu::RemoveItem(BMenu *submenu)
549 {
550 	for (int32 i = 0; i < fItems.CountItems(); i++) {
551 		if (static_cast<BMenuItem *>(fItems.ItemAtFast(i))->Submenu() == submenu)
552 			return RemoveItems(i, 1, NULL, false);
553 	}
554 
555 	return false;
556 }
557 
558 
559 int32
560 BMenu::CountItems() const
561 {
562 	return fItems.CountItems();
563 }
564 
565 
566 BMenuItem *
567 BMenu::ItemAt(int32 index) const
568 {
569 	return static_cast<BMenuItem *>(fItems.ItemAt(index));
570 }
571 
572 
573 BMenu *
574 BMenu::SubmenuAt(int32 index) const
575 {
576 	BMenuItem *item = static_cast<BMenuItem *>(fItems.ItemAt(index));
577 	return (item != NULL) ? item->Submenu() : NULL;
578 }
579 
580 
581 int32
582 BMenu::IndexOf(BMenuItem *item) const
583 {
584 	return fItems.IndexOf(item);
585 }
586 
587 
588 int32
589 BMenu::IndexOf(BMenu *submenu) const
590 {
591 	for (int32 i = 0; i < fItems.CountItems(); i++) {
592 		if (ItemAt(i)->Submenu() == submenu)
593 			return i;
594 	}
595 
596 	return -1;
597 }
598 
599 
600 BMenuItem *
601 BMenu::FindItem(const char *label) const
602 {
603 	BMenuItem *item = NULL;
604 
605 	for (int32 i = 0; i < CountItems(); i++) {
606 		item = ItemAt(i);
607 
608 		if (item->Label() && strcmp(item->Label(), label) == 0)
609 			return item;
610 
611 		if (item->Submenu() != NULL) {
612 			item = item->Submenu()->FindItem(label);
613 			if (item != NULL)
614 				return item;
615 		}
616 	}
617 
618 	return NULL;
619 }
620 
621 
622 BMenuItem *
623 BMenu::FindItem(uint32 command) const
624 {
625 	BMenuItem *item = NULL;
626 
627 	for (int32 i = 0; i < CountItems(); i++) {
628 		item = ItemAt(i);
629 
630 		if (item->Command() == command)
631 			return item;
632 
633 		if (item->Submenu() != NULL) {
634 			item = item->Submenu()->FindItem(command);
635 			if (item != NULL)
636 				return item;
637 		}
638 	}
639 
640 	return NULL;
641 }
642 
643 
644 status_t
645 BMenu::SetTargetForItems(BHandler *handler)
646 {
647 	status_t status = B_OK;
648 	for (int32 i = 0; i < fItems.CountItems(); i++) {
649 		status = ItemAt(i)->SetTarget(handler);
650 		if (status < B_OK)
651 			break;
652 	}
653 
654 	return status;
655 }
656 
657 
658 status_t
659 BMenu::SetTargetForItems(BMessenger messenger)
660 {
661 	status_t status = B_OK;
662 	for (int32 i = 0; i < fItems.CountItems(); i++) {
663 		status = ItemAt(i)->SetTarget(messenger);
664 		if (status < B_OK)
665 			break;
666 	}
667 
668 	return status;
669 }
670 
671 
672 void
673 BMenu::SetEnabled(bool enabled)
674 {
675 	if (fEnabled == enabled)
676 		return;
677 
678 	fEnabled = enabled;
679 
680 	if (fSuperitem)
681 		fSuperitem->SetEnabled(enabled);
682 }
683 
684 
685 void
686 BMenu::SetRadioMode(bool flag)
687 {
688 	fRadioMode = flag;
689 	if (!flag)
690 		SetLabelFromMarked(false);
691 }
692 
693 
694 void
695 BMenu::SetTriggersEnabled(bool flag)
696 {
697 	fTriggerEnabled = flag;
698 }
699 
700 
701 void
702 BMenu::SetMaxContentWidth(float width)
703 {
704 	fMaxContentWidth = width;
705 }
706 
707 
708 void
709 BMenu::SetLabelFromMarked(bool flag)
710 {
711 	fDynamicName = flag;
712 	if (flag)
713 		SetRadioMode(true);
714 }
715 
716 
717 bool
718 BMenu::IsLabelFromMarked()
719 {
720 	return fDynamicName;
721 }
722 
723 
724 bool
725 BMenu::IsEnabled() const
726 {
727 	if (!fEnabled)
728 		return false;
729 
730 	return fSuper ? fSuper->IsEnabled() : true ;
731 }
732 
733 
734 bool
735 BMenu::IsRadioMode() const
736 {
737 	return fRadioMode;
738 }
739 
740 
741 bool
742 BMenu::AreTriggersEnabled() const
743 {
744 	return fTriggerEnabled;
745 }
746 
747 
748 bool
749 BMenu::IsRedrawAfterSticky() const
750 {
751 	return fRedrawAfterSticky;
752 }
753 
754 
755 float
756 BMenu::MaxContentWidth() const
757 {
758 	return fMaxContentWidth;
759 }
760 
761 
762 BMenuItem *
763 BMenu::FindMarked()
764 {
765 	for (int32 i = 0; i < fItems.CountItems(); i++) {
766 		BMenuItem *item = ItemAt(i);
767 		if (item->IsMarked())
768 			return item;
769 	}
770 
771 	return NULL;
772 }
773 
774 
775 BMenu *
776 BMenu::Supermenu() const
777 {
778 	return fSuper;
779 }
780 
781 
782 BMenuItem *
783 BMenu::Superitem() const
784 {
785 	return fSuperitem;
786 }
787 
788 
789 void
790 BMenu::MessageReceived(BMessage *msg)
791 {
792 	BView::MessageReceived(msg);
793 }
794 
795 
796 void
797 BMenu::KeyDown(const char *bytes, int32 numBytes)
798 {
799 	// TODO: Test how it works on beos and implement it correctly
800 	switch (bytes[0]) {
801 		case B_UP_ARROW:
802 			if (fLayout == B_ITEMS_IN_COLUMN)
803 				SelectNextItem(fSelected, false);
804 			break;
805 
806 		case B_DOWN_ARROW:
807 			if (fLayout == B_ITEMS_IN_COLUMN)
808 				SelectNextItem(fSelected, true);
809 			break;
810 
811 		case B_LEFT_ARROW:
812 			if (fLayout == B_ITEMS_IN_ROW)
813 				SelectNextItem(fSelected, false);
814 			break;
815 
816 		case B_RIGHT_ARROW:
817 			if (fLayout == B_ITEMS_IN_ROW)
818 				SelectNextItem(fSelected, true);
819 			break;
820 
821 		case B_ENTER:
822 		case B_SPACE:
823 			if (fSelected)
824 				InvokeItem(fSelected);
825 
826 			break;
827 
828 		case B_ESCAPE:
829 			QuitTracking();
830 			break;
831 
832 		default:
833 			BView::KeyDown(bytes, numBytes);
834 	}
835 }
836 
837 
838 void
839 BMenu::Draw(BRect updateRect)
840 {
841 	if (RelayoutIfNeeded()) {
842 		Invalidate();
843 		return;
844 	}
845 
846 	DrawBackground(updateRect);
847 	DrawItems(updateRect);
848 }
849 
850 
851 BSize
852 BMenu::MinSize()
853 {
854 	_ValidatePreferredSize();
855 
856 	BSize size = (GetLayout() ? GetLayout()->MinSize()
857 		: fLayoutData->preferred);
858 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
859 }
860 
861 
862 BSize
863 BMenu::MaxSize()
864 {
865 	_ValidatePreferredSize();
866 
867 	BSize size = (GetLayout() ? GetLayout()->MaxSize()
868 		: fLayoutData->preferred);
869 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
870 }
871 
872 
873 BSize
874 BMenu::PreferredSize()
875 {
876 	_ValidatePreferredSize();
877 
878 	BSize size = (GetLayout() ? GetLayout()->PreferredSize()
879 		: fLayoutData->preferred);
880 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
881 }
882 
883 
884 void
885 BMenu::GetPreferredSize(float *_width, float *_height)
886 {
887 	_ValidatePreferredSize();
888 
889 	if (_width)
890 		*_width = fLayoutData->preferred.width;
891 	if (_height)
892 		*_height = fLayoutData->preferred.height;
893 }
894 
895 
896 void
897 BMenu::ResizeToPreferred()
898 {
899 	BView::ResizeToPreferred();
900 }
901 
902 
903 void
904 BMenu::DoLayout()
905 {
906 	// If the user set a layout, we let the base class version call its
907 	// hook.
908 	if (GetLayout()) {
909 		BView::DoLayout();
910 		return;
911 	}
912 
913 	if (RelayoutIfNeeded())
914 		Invalidate();
915 }
916 
917 
918 void
919 BMenu::FrameMoved(BPoint new_position)
920 {
921 	BView::FrameMoved(new_position);
922 }
923 
924 
925 void
926 BMenu::FrameResized(float new_width, float new_height)
927 {
928 	BView::FrameResized(new_width, new_height);
929 }
930 
931 
932 void
933 BMenu::InvalidateLayout()
934 {
935 	InvalidateLayout(false);
936 }
937 
938 
939 void
940 BMenu::InvalidateLayout(bool descendants)
941 {
942 	fUseCachedMenuLayout = false;
943 	fLayoutData->preferred.Set(B_SIZE_UNSET, B_SIZE_UNSET);
944 
945 	BView::InvalidateLayout(descendants);
946 }
947 
948 
949 BHandler *
950 BMenu::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
951 						int32 form, const char *property)
952 {
953 	BPropertyInfo propInfo(sPropList);
954 	BHandler *target = NULL;
955 
956 	switch (propInfo.FindMatch(msg, 0, specifier, form, property)) {
957 		case B_ERROR:
958 			break;
959 
960 		case 0:
961 		case 1:
962 		case 2:
963 		case 3:
964 		case 4:
965 		case 5:
966 		case 6:
967 		case 7:
968 			target = this;
969 			break;
970 		case 8:
971 			// TODO: redirect to menu
972 			target = this;
973 			break;
974 		case 9:
975 		case 10:
976 		case 11:
977 		case 12:
978 			target = this;
979 			break;
980 		case 13:
981 			// TODO: redirect to menuitem
982 			target = this;
983 			break;
984 	}
985 
986 	if (!target)
987 		target = BView::ResolveSpecifier(msg, index, specifier, form,
988 		property);
989 
990 	return target;
991 }
992 
993 
994 status_t
995 BMenu::GetSupportedSuites(BMessage *data)
996 {
997 	if (data == NULL)
998 		return B_BAD_VALUE;
999 
1000 	status_t err = data->AddString("suites", "suite/vnd.Be-menu");
1001 
1002 	if (err < B_OK)
1003 		return err;
1004 
1005 	BPropertyInfo propertyInfo(sPropList);
1006 	err = data->AddFlat("messages", &propertyInfo);
1007 
1008 	if (err < B_OK)
1009 		return err;
1010 
1011 	return BView::GetSupportedSuites(data);
1012 }
1013 
1014 
1015 status_t
1016 BMenu::Perform(perform_code d, void *arg)
1017 {
1018 	return BView::Perform(d, arg);
1019 }
1020 
1021 
1022 void
1023 BMenu::MakeFocus(bool focused)
1024 {
1025 	BView::MakeFocus(focused);
1026 }
1027 
1028 
1029 void
1030 BMenu::AllAttached()
1031 {
1032 	BView::AllAttached();
1033 }
1034 
1035 
1036 void
1037 BMenu::AllDetached()
1038 {
1039 	BView::AllDetached();
1040 }
1041 
1042 
1043 BMenu::BMenu(BRect frame, const char *name, uint32 resizingMode, uint32 flags,
1044 			 menu_layout layout, bool resizeToFit)
1045 	:	BView(frame, name, resizingMode, flags),
1046 		fChosenItem(NULL),
1047 		fSelected(NULL),
1048 		fCachedMenuWindow(NULL),
1049 		fSuper(NULL),
1050 		fSuperitem(NULL),
1051 		fAscent(-1.0f),
1052 		fDescent(-1.0f),
1053 		fFontHeight(-1.0f),
1054 		fState(0),
1055 		fLayout(layout),
1056 		fExtraRect(NULL),
1057 		fMaxContentWidth(0.0f),
1058 		fInitMatrixSize(NULL),
1059 		fExtraMenuData(NULL),
1060 		fSubmenus(0),
1061 		fTrigger(0),
1062 		fResizeToFit(resizeToFit),
1063 		fUseCachedMenuLayout(false),
1064 		fEnabled(true),
1065 		fDynamicName(false),
1066 		fRadioMode(false),
1067 		fTrackNewBounds(false),
1068 		fStickyMode(false),
1069 		fIgnoreHidden(true),
1070 		fTriggerEnabled(true),
1071 		fRedrawAfterSticky(false),
1072 		fAttachAborted(false)
1073 {
1074 	InitData(NULL);
1075 }
1076 
1077 
1078 void
1079 BMenu::SetItemMargins(float left, float top, float right, float bottom)
1080 {
1081 	fPad.Set(left, top, right, bottom);
1082 }
1083 
1084 
1085 void
1086 BMenu::GetItemMargins(float *left, float *top, float *right,
1087 						   float *bottom) const
1088 {
1089 	if (left != NULL)
1090 		*left = fPad.left;
1091 	if (top != NULL)
1092 		*top = fPad.top;
1093 	if (right != NULL)
1094 		*right = fPad.right;
1095 	if (bottom != NULL)
1096 		*bottom = fPad.bottom;
1097 }
1098 
1099 
1100 menu_layout
1101 BMenu::Layout() const
1102 {
1103 	return fLayout;
1104 }
1105 
1106 
1107 void
1108 BMenu::Show()
1109 {
1110 	Show(false);
1111 }
1112 
1113 
1114 void
1115 BMenu::Show(bool selectFirst)
1116 {
1117 	Install(NULL);
1118 	_show(selectFirst);
1119 }
1120 
1121 
1122 void
1123 BMenu::Hide()
1124 {
1125 	_hide();
1126 	Uninstall();
1127 }
1128 
1129 
1130 BMenuItem *
1131 BMenu::Track(bool sticky, BRect *clickToOpenRect)
1132 {
1133 	if (sticky && LockLooper()) {
1134 		RedrawAfterSticky(Bounds());
1135 		UnlockLooper();
1136 	}
1137 
1138 	if (clickToOpenRect != NULL && LockLooper()) {
1139 		fExtraRect = clickToOpenRect;
1140 		ConvertFromScreen(fExtraRect);
1141 		UnlockLooper();
1142 	}
1143 
1144 	int action;
1145 	BMenuItem *menuItem = _track(&action);
1146 
1147 	SetStickyMode(false);
1148 	fExtraRect = NULL;
1149 
1150 	return menuItem;
1151 }
1152 
1153 
1154 bool
1155 BMenu::AddDynamicItem(add_state s)
1156 {
1157 	// Implemented in subclasses
1158 	return false;
1159 }
1160 
1161 
1162 void
1163 BMenu::DrawBackground(BRect update)
1164 {
1165 	rgb_color oldColor = HighColor();
1166 	SetHighColor(sMenuInfo.background_color);
1167 	FillRect(Bounds() & update, B_SOLID_HIGH);
1168 	SetHighColor(oldColor);
1169 }
1170 
1171 
1172 void
1173 BMenu::SetTrackingHook(menu_tracking_hook func, void *state)
1174 {
1175 	delete fExtraMenuData;
1176 	fExtraMenuData = new (nothrow) _ExtraMenuData_(func, state);
1177 }
1178 
1179 
1180 void BMenu::_ReservedMenu3() {}
1181 void BMenu::_ReservedMenu4() {}
1182 void BMenu::_ReservedMenu5() {}
1183 void BMenu::_ReservedMenu6() {}
1184 
1185 
1186 BMenu &
1187 BMenu::operator=(const BMenu &)
1188 {
1189 	return *this;
1190 }
1191 
1192 
1193 void
1194 BMenu::InitData(BMessage *data)
1195 {
1196 	// TODO: Get _color, _fname, _fflt from the message, if present
1197 	BFont font;
1198 	font.SetFamilyAndStyle(sMenuInfo.f_family, sMenuInfo.f_style);
1199 	font.SetSize(sMenuInfo.font_size);
1200 	SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE);
1201 
1202 	fLayoutData = new LayoutData;
1203 
1204 	SetLowColor(sMenuInfo.background_color);
1205 	SetViewColor(sMenuInfo.background_color);
1206 
1207 	if (data != NULL) {
1208 		data->FindInt32("_layout", (int32 *)&fLayout);
1209 		data->FindBool("_rsize_to_fit", &fResizeToFit);
1210 		bool disabled;
1211 		if (data->FindBool("_disable", &disabled) == B_OK)
1212 			fEnabled = !disabled;
1213 		data->FindBool("_radio", &fRadioMode);
1214 
1215 		bool disableTrigger = false;
1216 		data->FindBool("_trig_disabled", &disableTrigger);
1217 		fTriggerEnabled = !disableTrigger;
1218 
1219 		data->FindBool("_dyn_label", &fDynamicName);
1220 		data->FindFloat("_maxwidth", &fMaxContentWidth);
1221 
1222 		BMessage msg;
1223         	for (int32 i = 0; data->FindMessage("_items", i, &msg) == B_OK; i++) {
1224 			BArchivable *object = instantiate_object(&msg);
1225 			if (BMenuItem *item = dynamic_cast<BMenuItem *>(object)) {
1226 				BRect bounds;
1227 				if ((fLayout == B_ITEMS_IN_MATRIX)
1228 					&& (data->FindRect("_i_frames", i, &bounds) == B_OK))
1229 						AddItem(item, bounds);
1230 				else
1231 					AddItem(item);
1232 			}
1233 		}
1234 	}
1235 }
1236 
1237 
1238 bool
1239 BMenu::_show(bool selectFirstItem)
1240 {
1241 	// See if the supermenu has a cached menuwindow,
1242 	// and use that one if possible.
1243 	BMenuWindow *window = NULL;
1244 	bool ourWindow = false;
1245 	if (fSuper != NULL) {
1246 		fSuperbounds = fSuper->ConvertToScreen(fSuper->Bounds());
1247 		window = fSuper->MenuWindow();
1248 	}
1249 
1250 	// Otherwise, create a new one
1251 	// This happens for "stand alone" BPopUpMenus
1252 	// (i.e. not within a BMenuField)
1253 	if (window == NULL) {
1254 		// Menu windows get the BMenu's handler name
1255 		window = new (nothrow) BMenuWindow(Name());
1256 		ourWindow = true;
1257 	}
1258 
1259 	if (window == NULL)
1260 		return false;
1261 
1262 	if (window->Lock()) {
1263 		fAttachAborted = false;
1264 		window->AttachMenu(this);
1265 
1266 		// Menu didn't have the time to add its items: aborting...
1267 		if (fAttachAborted) {
1268 			window->DetachMenu();
1269 			// TODO: Probably not needed, we can just let _hide() quit the window
1270 			if (ourWindow)
1271 				window->Quit();
1272 			else
1273 				window->Unlock();
1274 			return false;
1275 		}
1276 
1277 		// Move the BMenu to 1, 1, if it's attached to a BMenuWindow,
1278 		// (that means it's a BMenu, BMenuBars are attached to regular BWindows).
1279 		// This is needed to be able to draw the frame around the BMenu.
1280 		if (dynamic_cast<BMenuWindow *>(window) != NULL)
1281 			MoveTo(1, 1);
1282 
1283 		UpdateWindowViewSize(true);
1284 		window->Show();
1285 
1286 		if (selectFirstItem)
1287 			_SelectItem(ItemAt(0));
1288 
1289 		window->Unlock();
1290 	}
1291 
1292 	return true;
1293 }
1294 
1295 
1296 void
1297 BMenu::_hide()
1298 {
1299 	BMenuWindow *window = static_cast<BMenuWindow *>(Window());
1300 	if (window == NULL || !window->Lock())
1301 		return;
1302 
1303 	if (fSelected != NULL)
1304 		_SelectItem(NULL);
1305 
1306 	window->Hide();
1307 	window->DetachMenu();
1308 		// we don't want to be deleted when the window is removed
1309 
1310 	// Delete the menu window used by our submenus
1311 	DeleteMenuWindow();
1312 
1313 	if (fSuper != NULL)
1314 		window->Unlock();
1315 	else {
1316 		// it's our window, quit it
1317 		window->Quit();
1318 	}
1319 }
1320 
1321 
1322 const bigtime_t kHysteresis = 200000; // TODO: Test and reduce if needed.
1323 
1324 
1325 BMenuItem *
1326 BMenu::_track(int *action, long start)
1327 {
1328 	// TODO: cleanup
1329 	BMenuItem *item = NULL;
1330 	bigtime_t openTime = system_time();
1331 	bigtime_t closeTime = 0;
1332 
1333 	fState = MENU_STATE_TRACKING;
1334 	if (fSuper != NULL)
1335 		fSuper->fState = MENU_STATE_TRACKING_SUBMENU;
1336 
1337 	while (true) {
1338 		if (CustomTrackingWantsToQuit())
1339 			break;
1340 
1341 		bool locked = LockLooper();
1342 		if (!locked)
1343 			break;
1344 
1345 		bigtime_t snoozeAmount = 50000;
1346 		BPoint location;
1347 		ulong buttons;
1348 		GetMouse(&location, &buttons, true);
1349 
1350 		BMenuWindow *window = static_cast<BMenuWindow *>(Window());
1351 
1352 		BPoint screenLocation = ConvertToScreen(location);
1353 		if (window->CheckForScrolling(screenLocation)) {
1354 			item = NULL;
1355 		} else {
1356 			item = HitTestItems(location, B_ORIGIN);
1357 			if (item != NULL)
1358 				_UpdateStateOpenSelect(item, openTime, closeTime);
1359 		}
1360 
1361 		// Track the submenu
1362 		if (OverSubmenu(fSelected, screenLocation)) {
1363 			UnlockLooper();
1364 			locked = false;
1365 			int submenuAction = MENU_STATE_TRACKING;
1366 			BMenu *submenu = fSelected->Submenu();
1367 			bool wasSticky = IsStickyMode();
1368 			if (wasSticky)
1369 				submenu->SetStickyMode(true);
1370 			BMenuItem *submenuItem = submenu->_track(&submenuAction);
1371 
1372 			// check if the user started holding down a mouse button in a submenu
1373 			if (wasSticky && !IsStickyMode()) {
1374 				buttons = 1;
1375 					// buttons must have been pressed in the meantime
1376 			}
1377 
1378 			if (submenuAction == MENU_STATE_CLOSED) {
1379 				item = submenuItem;
1380 				fState = submenuAction;
1381 				break;
1382 			}
1383 
1384 			locked = LockLooper();
1385 			if (!locked)
1386 				break;
1387 
1388 		} else if (item == NULL) {
1389 			if (OverSuper(screenLocation)) {
1390 				fState = MENU_STATE_TRACKING;
1391 				UnlockLooper();
1392 				break;
1393 			}
1394 
1395 			if (!OverSubmenu(fSelected, screenLocation)
1396 				&& system_time() > closeTime + kHysteresis
1397 				&& fState != MENU_STATE_TRACKING_SUBMENU) {
1398 				_SelectItem(NULL);
1399 				fState = MENU_STATE_TRACKING;
1400 			}
1401 
1402 			if (fSuper != NULL) {
1403 				// Give supermenu the chance to continue tracking
1404 				*action = fState;
1405 				if (locked)
1406 					UnlockLooper();
1407 
1408 				return NULL;
1409 			}
1410 		}
1411 
1412 		if (locked)
1413 			UnlockLooper();
1414 
1415 		_UpdateStateClose(item, location, buttons);
1416 
1417 		if (fState == MENU_STATE_CLOSED)
1418 			break;
1419 
1420 		snooze(snoozeAmount);
1421 	}
1422 
1423 	if (action != NULL)
1424 		*action = fState;
1425 
1426 	if (fSelected != NULL && LockLooper()) {
1427 		_SelectItem(NULL);
1428 		UnlockLooper();
1429 	}
1430 
1431 	if (IsStickyMode())
1432 		SetStickyMode(false);
1433 
1434 	// delete the menu window recycled for all the child menus
1435 	DeleteMenuWindow();
1436 
1437 	return item;
1438 }
1439 
1440 
1441 void
1442 BMenu::_UpdateStateOpenSelect(BMenuItem *item, bigtime_t &openTime, bigtime_t &closeTime)
1443 {
1444 	if (fState == MENU_STATE_CLOSED)
1445 		return;
1446 
1447 	if (item != fSelected && system_time() > closeTime + kHysteresis) {
1448 		_SelectItem(item, false);
1449 		openTime = system_time();
1450 	} else if (system_time() > kHysteresis + openTime && item->Submenu() != NULL
1451 		&& item->Submenu()->Window() == NULL) {
1452 		// Open the submenu if it's not opened yet, but only if
1453 		// the mouse pointer stayed over there for some time
1454 		// (hysteresis)
1455 		_SelectItem(item);
1456 		closeTime = system_time();
1457 	}
1458 	if (fState != MENU_STATE_TRACKING)
1459 		fState = MENU_STATE_TRACKING;
1460 }
1461 
1462 
1463 void
1464 BMenu::_UpdateStateClose(BMenuItem *item, const BPoint &where, const uint32 &buttons)
1465 {
1466 	if (fState == MENU_STATE_CLOSED)
1467 		return;
1468 
1469 	if (buttons != 0 && IsStickyMode()) {
1470 		if (item == NULL)
1471 			fState = MENU_STATE_CLOSED;
1472 		else {
1473 			BMenu *supermenu = Supermenu();
1474 			for(; supermenu; supermenu = supermenu->Supermenu())
1475 				supermenu->SetStickyMode(false);
1476 			SetStickyMode(false);
1477 		}
1478 	} else if (buttons == 0 && !IsStickyMode()) {
1479 		if (fExtraRect != NULL && fExtraRect->Contains(where)) {
1480 			SetStickyMode(true);
1481 			fExtraRect = NULL;
1482 				// This code should be executed only once
1483 		} else
1484 			fState = MENU_STATE_CLOSED;
1485 	}
1486 }
1487 
1488 
1489 bool
1490 BMenu::_AddItem(BMenuItem *item, int32 index)
1491 {
1492 	ASSERT(item != NULL);
1493 	if (index < 0 || index > fItems.CountItems())
1494 		return false;
1495 
1496 	if (!fItems.AddItem(item, index))
1497 		return false;
1498 
1499 	// install the item on the supermenu's window
1500 	// or onto our window, if we are a root menu
1501 	BWindow* window = NULL;
1502 	if (Superitem() != NULL)
1503 		window = Superitem()->fWindow;
1504 	else
1505 		window = Window();
1506 	if (window != NULL)
1507 		item->Install(window);
1508 
1509 	item->SetSuper(this);
1510 
1511 	return true;
1512 }
1513 
1514 
1515 bool
1516 BMenu::RemoveItems(int32 index, int32 count, BMenuItem *item, bool deleteItems)
1517 {
1518 	bool success = false;
1519 	bool invalidateLayout = false;
1520 
1521 	bool locked = LockLooper();
1522 	BWindow *window = Window();
1523 
1524 	// The plan is simple: If we're given a BMenuItem directly, we use it
1525 	// and ignore index and count. Otherwise, we use them instead.
1526 	if (item != NULL) {
1527 		if (fItems.RemoveItem(item)) {
1528 			if (item == fSelected && window != NULL)
1529 				_SelectItem(NULL);
1530 			item->Uninstall();
1531 			item->SetSuper(NULL);
1532 			if (deleteItems)
1533 				delete item;
1534 			success = invalidateLayout = true;
1535 		}
1536 	} else {
1537 		// We iterate backwards because it's simpler
1538 		int32 i = min_c(index + count - 1, fItems.CountItems() - 1);
1539 		// NOTE: the range check for "index" is done after
1540 		// calculating the last index to be removed, so
1541 		// that the range is not "shifted" unintentionally
1542 		index = max_c(0, index);
1543 		for (; i >= index; i--) {
1544 			item = static_cast<BMenuItem*>(fItems.ItemAt(i));
1545 			if (item != NULL) {
1546 				if (fItems.RemoveItem(item)) {
1547 					if (item == fSelected && window != NULL)
1548 						_SelectItem(NULL);
1549 					item->Uninstall();
1550 					item->SetSuper(NULL);
1551 					if (deleteItems)
1552 						delete item;
1553 					success = true;
1554 					invalidateLayout = true;
1555 				} else {
1556 					// operation not entirely successful
1557 					success = false;
1558 					break;
1559 				}
1560 			}
1561 		}
1562 	}
1563 
1564 	if (invalidateLayout) {
1565 		InvalidateLayout();
1566 		if (locked && window != NULL) {
1567 			LayoutItems(0);
1568 			UpdateWindowViewSize(false);
1569 			Invalidate();
1570 		}
1571 	}
1572 
1573 	if (locked)
1574 		UnlockLooper();
1575 
1576 	return success;
1577 }
1578 
1579 
1580 bool
1581 BMenu::RelayoutIfNeeded()
1582 {
1583 	if (!fUseCachedMenuLayout) {
1584 		fUseCachedMenuLayout = true;
1585 		CacheFontInfo();
1586 		LayoutItems(0);
1587 		return true;
1588 	}
1589 	return false;
1590 }
1591 
1592 
1593 void
1594 BMenu::LayoutItems(int32 index)
1595 {
1596 	CalcTriggers();
1597 
1598 	float width, height;
1599 	ComputeLayout(index, fResizeToFit, true, &width, &height);
1600 
1601 	if (fResizeToFit)
1602 		ResizeTo(width, height);
1603 }
1604 
1605 
1606 BSize
1607 BMenu::_ValidatePreferredSize()
1608 {
1609 	if (!fLayoutData->preferred.IsWidthSet())
1610 		ComputeLayout(0, true, false, NULL, NULL);
1611 
1612 	return fLayoutData->preferred;
1613 }
1614 
1615 
1616 void
1617 BMenu::ComputeLayout(int32 index, bool bestFit, bool moveItems,
1618 	float* _width, float* _height)
1619 {
1620 	// TODO: Take "bestFit", "moveItems", "index" into account,
1621 	// Recalculate only the needed items,
1622 	// not the whole layout every time
1623 
1624 	BRect frame(0, 0, 0, 0);
1625 	switch (fLayout) {
1626 		case B_ITEMS_IN_COLUMN:
1627 			_ComputeColumnLayout(index, bestFit, moveItems, frame);
1628 			break;
1629 
1630 		case B_ITEMS_IN_ROW:
1631 			_ComputeRowLayout(index, bestFit, moveItems, frame);
1632 			break;
1633 
1634 		case B_ITEMS_IN_MATRIX:
1635 			_ComputeMatrixLayout(frame);
1636 			break;
1637 
1638 		default:
1639 			break;
1640 	}
1641 
1642 	// change width depending on resize mode
1643 	BSize size;
1644 	if ((ResizingMode() & B_FOLLOW_LEFT_RIGHT) == B_FOLLOW_LEFT_RIGHT) {
1645 		if (Parent())
1646 			size.width = Parent()->Frame().Width() + 1;
1647 		else if (Window())
1648 			size.width = Window()->Frame().Width() + 1;
1649 		else
1650 			size.width = Bounds().Width();
1651 	} else
1652 		size.width = frame.Width();
1653 
1654 	size.height = frame.Height();
1655 
1656 	if (_width)
1657 		*_width = size.width;
1658 
1659 	if (_height)
1660 		*_height = size.height;
1661 
1662 	if (bestFit)
1663 		fLayoutData->preferred = size;
1664 
1665 	if (moveItems)
1666 		fUseCachedMenuLayout = true;
1667 }
1668 
1669 
1670 void
1671 BMenu::_ComputeColumnLayout(int32 index, bool bestFit, bool moveItems, BRect &frame)
1672 {
1673 	BFont font;
1674 	GetFont(&font);
1675 	bool command = false;
1676 	bool control = false;
1677 	bool shift = false;
1678 	for (int32 i = 0; i < fItems.CountItems(); i++) {
1679 		BMenuItem *item = ItemAt(i);
1680 		if (item != NULL) {
1681 			float iWidth, iHeight;
1682 			item->GetContentSize(&iWidth, &iHeight);
1683 
1684 			if (item->fModifiers && item->fShortcutChar) {
1685 				iWidth += font.Size();
1686 				if (item->fModifiers & B_COMMAND_KEY)
1687 					command = true;
1688 				if (item->fModifiers & B_CONTROL_KEY)
1689 					control = true;
1690 				if (item->fModifiers & B_SHIFT_KEY)
1691 					shift = true;
1692 			}
1693 
1694 			item->fBounds.left = 0.0f;
1695 			item->fBounds.top = frame.bottom;
1696 			item->fBounds.bottom = item->fBounds.top + iHeight + fPad.top + fPad.bottom;
1697 
1698 			if (fSubmenus)
1699 				iWidth += item->Frame().Height();
1700 
1701 			frame.right = max_c(frame.right, iWidth + fPad.left + fPad.right);
1702 			frame.bottom = item->fBounds.bottom + 1.0f;
1703 		}
1704 	}
1705 
1706 	if (command)
1707 		frame.right += 17;
1708 	if (control)
1709 		frame.right += 17;
1710 	if (shift)
1711 		frame.right += 22;
1712 
1713 	if (fMaxContentWidth > 0)
1714 		frame.right = min_c(frame.right, fMaxContentWidth);
1715 
1716 	if (moveItems) {
1717 		for (int32 i = 0; i < fItems.CountItems(); i++)
1718 			ItemAt(i)->fBounds.right = frame.right;
1719 	}
1720 	frame.right = ceilf(frame.right);
1721 	frame.bottom--;
1722 }
1723 
1724 
1725 void
1726 BMenu::_ComputeRowLayout(int32 index, bool bestFit, bool moveItems, BRect &frame)
1727 {
1728 	font_height fh;
1729 	GetFontHeight(&fh);
1730 	frame = BRect(0.0f, 0.0f, 0.0f,	ceilf(fh.ascent + fh.descent + fPad.top + fPad.bottom));
1731 
1732 	for (int32 i = 0; i < fItems.CountItems(); i++) {
1733 		BMenuItem *item = ItemAt(i);
1734 		float iWidth, iHeight;
1735 		if (item != NULL) {
1736 			item->GetContentSize(&iWidth, &iHeight);
1737 
1738 			item->fBounds.left = frame.right;
1739 			item->fBounds.top = 0.0f;
1740 			item->fBounds.right = item->fBounds.left + iWidth + fPad.left + fPad.right;
1741 
1742 			frame.right = item->Frame().right + 1.0f;
1743 			frame.bottom = max_c(frame.bottom, iHeight + fPad.top + fPad.bottom);
1744 		}
1745 	}
1746 
1747 	if (moveItems) {
1748 		for (int32 i = 0; i < fItems.CountItems(); i++)
1749 			ItemAt(i)->fBounds.bottom = frame.bottom;
1750 	}
1751 
1752 	if (bestFit)
1753 		frame.right = ceilf(frame.right);
1754 	else
1755 		frame.right = Bounds().right;
1756 }
1757 
1758 
1759 void
1760 BMenu::_ComputeMatrixLayout(BRect &frame)
1761 {
1762 	for (int32 i = 0; i < CountItems(); i++) {
1763 		BMenuItem *item = ItemAt(i);
1764 		if (item != NULL) {
1765 			frame.left = min_c(frame.left, item->Frame().left);
1766 			frame.right = max_c(frame.right, item->Frame().right);
1767 			frame.top = min_c(frame.top, item->Frame().top);
1768 			frame.bottom = max_c(frame.bottom, item->Frame().bottom);
1769 		}
1770 	}
1771 }
1772 
1773 
1774 BRect
1775 BMenu::Bump(BRect current, BPoint extent, int32 index) const
1776 {
1777 	// ToDo: what's this?
1778 	return BRect();
1779 }
1780 
1781 
1782 BPoint
1783 BMenu::ItemLocInRect(BRect frame) const
1784 {
1785 	// ToDo: what's this?
1786 	return BPoint();
1787 }
1788 
1789 
1790 // Assumes the SuperMenu to be locked (due to calling ConvertToScreen())
1791 BPoint
1792 BMenu::ScreenLocation()
1793 {
1794 	BMenu *superMenu = Supermenu();
1795 	BMenuItem *superItem = Superitem();
1796 
1797 	if (superMenu == NULL || superItem == NULL) {
1798 		debugger("BMenu can't determine where to draw."
1799 			"Override BMenu::ScreenLocation() to determine location.");
1800 	}
1801 
1802 	BPoint point;
1803 	if (superMenu->Layout() == B_ITEMS_IN_COLUMN)
1804 		point = superItem->Frame().RightTop() + BPoint(1.0f, 1.0f);
1805 	else
1806 		point = superItem->Frame().LeftBottom() + BPoint(1.0f, 1.0f);
1807 
1808 	superMenu->ConvertToScreen(&point);
1809 
1810 	return point;
1811 }
1812 
1813 
1814 BRect
1815 BMenu::CalcFrame(BPoint where, bool *scrollOn)
1816 {
1817 	// TODO: Improve me
1818 	BRect bounds = Bounds();
1819 	BRect frame = bounds.OffsetToCopy(where);
1820 
1821 	BScreen screen(Window());
1822 	BRect screenFrame = screen.Frame();
1823 
1824 	BMenu *superMenu = Supermenu();
1825 	BMenuItem *superItem = Superitem();
1826 
1827 	if (scrollOn != NULL) {
1828 		// basically, if this returns false, it means
1829 		// that the menu frame won't fit completely inside the screen
1830 		*scrollOn = !screenFrame.Contains(bounds);
1831 	}
1832 
1833 	// TODO: Horrible hack:
1834 	// When added to a BMenuField, a BPopUpMenu is the child of
1835 	// a _BMCMenuBar_ to "fake" the menu hierarchy
1836 	if (superMenu == NULL || superItem == NULL
1837 		|| dynamic_cast<_BMCMenuBar_ *>(superMenu) != NULL) {
1838 		// just move the window on screen
1839 
1840 		if (frame.bottom > screenFrame.bottom)
1841 			frame.OffsetBy(0, screenFrame.bottom - frame.bottom);
1842 		else if (frame.top < screenFrame.top)
1843 			frame.OffsetBy(0, -frame.top);
1844 
1845 		if (frame.right > screenFrame.right)
1846 			frame.OffsetBy(screenFrame.right - frame.right, 0);
1847 		else if (frame.left < screenFrame.left)
1848 			frame.OffsetBy(-frame.left, 0);
1849 
1850 		return frame;
1851 	}
1852 
1853 	if (superMenu->Layout() == B_ITEMS_IN_COLUMN) {
1854 		if (frame.right > screenFrame.right)
1855 			frame.OffsetBy(-superItem->Frame().Width() - frame.Width() - 2, 0);
1856 
1857 		if (frame.left < 0)
1858 			frame.OffsetBy(-frame.left + 6, 0);
1859 
1860 		if (frame.bottom > screenFrame.bottom)
1861 			frame.OffsetBy(0, screenFrame.bottom - frame.bottom);
1862 	} else {
1863 		if (frame.bottom > screenFrame.bottom) {
1864 			if (scrollOn != NULL && superMenu != NULL &&
1865 				dynamic_cast<BMenuBar *>(superMenu) != NULL &&
1866 				frame.top < (screenFrame.bottom - 80)) {
1867 				*scrollOn = true;
1868 			} else {
1869 				frame.OffsetBy(0, -superItem->Frame().Height() - frame.Height() - 3);
1870 			}
1871 		}
1872 
1873 		if (frame.right > screenFrame.right)
1874 			frame.OffsetBy(screenFrame.right - frame.right, 0);
1875 	}
1876 
1877 	return frame;
1878 }
1879 
1880 
1881 bool
1882 BMenu::ScrollMenu(BRect bounds, BPoint loc, bool *fast)
1883 {
1884 	return false;
1885 }
1886 
1887 
1888 void
1889 BMenu::ScrollIntoView(BMenuItem *item)
1890 {
1891 }
1892 
1893 
1894 void
1895 BMenu::DrawItems(BRect updateRect)
1896 {
1897 	int32 itemCount = fItems.CountItems();
1898 	for (int32 i = 0; i < itemCount; i++) {
1899 		BMenuItem *item = ItemAt(i);
1900 		if (item->Frame().Intersects(updateRect))
1901 			item->Draw();
1902 	}
1903 }
1904 
1905 
1906 int
1907 BMenu::State(BMenuItem **item) const
1908 {
1909 	if (fState == MENU_STATE_TRACKING || fState == MENU_STATE_CLOSED)
1910 		return fState;
1911 
1912 	if (fSelected != NULL && fSelected->Submenu() != NULL)
1913 		return fSelected->Submenu()->State(item);
1914 
1915 	return fState;
1916 }
1917 
1918 
1919 void
1920 BMenu::InvokeItem(BMenuItem *item, bool now)
1921 {
1922 	if (!item->IsEnabled())
1923 		return;
1924 
1925 	// Do the "selected" animation
1926 	if (!item->Submenu() && LockLooper()) {
1927 		snooze(50000);
1928 		item->Select(true);
1929 		Sync();
1930 		snooze(50000);
1931 		item->Select(false);
1932 		Sync();
1933 		snooze(50000);
1934 		item->Select(true);
1935 		Sync();
1936 		snooze(50000);
1937 		item->Select(false);
1938 		Sync();
1939 		UnlockLooper();
1940 	}
1941 
1942 	item->Invoke();
1943 }
1944 
1945 
1946 bool
1947 BMenu::OverSuper(BPoint location)
1948 {
1949 	if (!Supermenu())
1950 		return false;
1951 
1952 	return fSuperbounds.Contains(location);
1953 }
1954 
1955 
1956 bool
1957 BMenu::OverSubmenu(BMenuItem *item, BPoint loc)
1958 {
1959 	if (item == NULL)
1960 		return false;
1961 
1962 	BMenu *subMenu = item->Submenu();
1963 	if (subMenu == NULL || subMenu->Window() == NULL)
1964 		return false;
1965 
1966 	// we assume that loc is in screen coords
1967 	if (subMenu->Window()->Frame().Contains(loc))
1968 		return true;
1969 
1970 	return subMenu->OverSubmenu(subMenu->fSelected, loc);
1971 }
1972 
1973 
1974 BMenuWindow *
1975 BMenu::MenuWindow()
1976 {
1977 	if (fCachedMenuWindow == NULL) {
1978 		char windowName[64];
1979 		snprintf(windowName, 64, "%s cached menu", Name());
1980 		fCachedMenuWindow = new (nothrow) BMenuWindow(windowName);
1981 	}
1982 
1983 	return fCachedMenuWindow;
1984 }
1985 
1986 
1987 void
1988 BMenu::DeleteMenuWindow()
1989 {
1990 	if (fCachedMenuWindow != NULL) {
1991 		fCachedMenuWindow->Lock();
1992 		fCachedMenuWindow->Quit();
1993 		fCachedMenuWindow = NULL;
1994 	}
1995 }
1996 
1997 
1998 BMenuItem *
1999 BMenu::HitTestItems(BPoint where, BPoint slop) const
2000 {
2001 	// TODO: Take "slop" into account ?
2002 
2003 	// if the point doesn't lie within the menu's
2004 	// bounds, bail out immediately
2005 	if (!Bounds().Contains(where))
2006 		return NULL;
2007 
2008 	int32 itemCount = CountItems();
2009 	for (int32 i = 0; i < itemCount; i++) {
2010 		BMenuItem *item = ItemAt(i);
2011 		if (item->Frame().Contains(where))
2012 			return item;
2013 	}
2014 
2015 	return NULL;
2016 }
2017 
2018 
2019 BRect
2020 BMenu::Superbounds() const
2021 {
2022 	return fSuperbounds;
2023 }
2024 
2025 
2026 void
2027 BMenu::CacheFontInfo()
2028 {
2029 	font_height fh;
2030 	GetFontHeight(&fh);
2031 	fAscent = fh.ascent;
2032 	fDescent = fh.descent;
2033 	fFontHeight = ceilf(fh.ascent + fh.descent + fh.leading);
2034 }
2035 
2036 
2037 void
2038 BMenu::ItemMarked(BMenuItem *item)
2039 {
2040 	if (IsRadioMode()) {
2041 		for (int32 i = 0; i < CountItems(); i++)
2042 			if (ItemAt(i) != item)
2043 				ItemAt(i)->SetMarked(false);
2044 		InvalidateLayout();
2045 	}
2046 
2047 	if (IsLabelFromMarked() && Superitem())
2048 		Superitem()->SetLabel(item->Label());
2049 }
2050 
2051 
2052 void
2053 BMenu::Install(BWindow *target)
2054 {
2055 	for (int32 i = 0; i < CountItems(); i++)
2056 		ItemAt(i)->Install(target);
2057 }
2058 
2059 
2060 void
2061 BMenu::Uninstall()
2062 {
2063 	for (int32 i = 0; i < CountItems(); i++)
2064 		ItemAt(i)->Uninstall();
2065 }
2066 
2067 
2068 void
2069 BMenu::_SelectItem(BMenuItem* menuItem, bool showSubmenu, bool selectFirstItem)
2070 {
2071 	// Avoid deselecting and then reselecting the same item
2072 	// which would cause flickering
2073 	if (menuItem != fSelected) {
2074 		if (fSelected != NULL) {
2075 			fSelected->Select(false);
2076 			BMenu *subMenu = fSelected->Submenu();
2077 			if (subMenu != NULL && subMenu->Window() != NULL)
2078 				subMenu->_hide();
2079 		}
2080 
2081 		fSelected = menuItem;
2082 		if (fSelected != NULL)
2083 			fSelected->Select(true);
2084 	}
2085 
2086 	if (fSelected != NULL && showSubmenu) {
2087 		BMenu *subMenu = fSelected->Submenu();
2088 		if (subMenu != NULL && subMenu->Window() == NULL) {
2089 			if (!subMenu->_show(selectFirstItem)) {
2090 				// something went wrong, deselect the item
2091 				fSelected->Select(false);
2092 				fSelected = NULL;
2093 			}
2094 		}
2095 	}
2096 }
2097 
2098 
2099 BMenuItem *
2100 BMenu::CurrentSelection() const
2101 {
2102 	return fSelected;
2103 }
2104 
2105 
2106 bool
2107 BMenu::SelectNextItem(BMenuItem *item, bool forward)
2108 {
2109 	BMenuItem *nextItem = NextItem(item, forward);
2110 	if (nextItem == NULL)
2111 		return false;
2112 
2113 	_SelectItem(nextItem);
2114 	return true;
2115 }
2116 
2117 
2118 BMenuItem *
2119 BMenu::NextItem(BMenuItem *item, bool forward) const
2120 {
2121 	if (item == NULL) {
2122 		if (forward)
2123 			return ItemAt(CountItems() - 1);
2124 		else
2125 			return ItemAt(0);
2126 	}
2127 
2128 	int32 index = fItems.IndexOf(item);
2129 	if (forward)
2130 		index++;
2131 	else
2132 		index--;
2133 
2134 	if (index < 0 || index >= fItems.CountItems())
2135 		return NULL;
2136 
2137 	return ItemAt(index);
2138 }
2139 
2140 
2141 bool
2142 BMenu::IsItemVisible(BMenuItem *item) const
2143 {
2144 	BRect itemFrame = item->Frame();
2145 	ConvertToScreen(&itemFrame);
2146 
2147 	BRect visibilityFrame = Window()->Frame() & BScreen(Window()).Frame();
2148 
2149 	return visibilityFrame.Intersects(itemFrame);
2150 }
2151 
2152 
2153 void
2154 BMenu::SetIgnoreHidden(bool on)
2155 {
2156 	fIgnoreHidden = on;
2157 }
2158 
2159 
2160 void
2161 BMenu::SetStickyMode(bool on)
2162 {
2163 	if (fStickyMode != on) {
2164 		// TODO: Ugly hack, but it needs to be done right here in this method
2165 		BMenuBar *menuBar = dynamic_cast<BMenuBar *>(this);
2166 		if (on && menuBar != NULL && menuBar->LockLooper()) {
2167 			// Steal the focus from the current focus view
2168 			// (needed to handle keyboard navigation)
2169 			menuBar->StealFocus();
2170 			menuBar->UnlockLooper();
2171 		}
2172 
2173 		fStickyMode = on;
2174 	}
2175 
2176 	// If we are switching to sticky mode, propagate the status
2177 	// back to the super menu
2178 	if (on && fSuper != NULL)
2179 		fSuper->SetStickyMode(on);
2180 }
2181 
2182 
2183 bool
2184 BMenu::IsStickyMode() const
2185 {
2186 	return fStickyMode;
2187 }
2188 
2189 
2190 void
2191 BMenu::CalcTriggers()
2192 {
2193 	BList triggersList;
2194 
2195 	// Gathers the existing triggers
2196 	// TODO: Oh great, reinterpret_cast.
2197 	for (int32 i = 0; i < CountItems(); i++) {
2198 		char trigger = ItemAt(i)->Trigger();
2199 		if (trigger != 0)
2200 			triggersList.AddItem(reinterpret_cast<void *>((uint32)trigger));
2201 	}
2202 
2203 	// Set triggers for items which don't have one yet
2204 	for (int32 i = 0; i < CountItems(); i++) {
2205 		BMenuItem *item = ItemAt(i);
2206 		if (item->Trigger() == 0) {
2207 			const char *newTrigger = ChooseTrigger(item->Label(), &triggersList);
2208 			if (newTrigger != NULL)
2209 				item->SetAutomaticTrigger(*newTrigger);
2210 		}
2211 	}
2212 }
2213 
2214 
2215 const char *
2216 BMenu::ChooseTrigger(const char *title, BList *chars)
2217 {
2218 	ASSERT(chars != NULL);
2219 
2220 	if (title == NULL)
2221 		return NULL;
2222 
2223 	char trigger;
2224 	// TODO: Oh great, reinterpret_cast all around
2225 	while ((trigger = title[0]) != '\0') {
2226 		if (isalpha(trigger)
2227 			&& !chars->HasItem(reinterpret_cast<void *>((uint32)trigger))) {
2228 			chars->AddItem(reinterpret_cast<void *>((uint32)trigger));
2229 			return title;
2230 		}
2231 
2232 		title++;
2233 	}
2234 
2235 	return NULL;
2236 }
2237 
2238 
2239 void
2240 BMenu::UpdateWindowViewSize(bool upWind)
2241 {
2242 	BWindow *window = Window();
2243 	if (window == NULL)
2244 		return;
2245 
2246 	if (dynamic_cast<BMenuBar *>(this) != NULL)
2247 		return;
2248 
2249 	if (!fResizeToFit)
2250 		return;
2251 
2252 	bool scroll;
2253 	const BPoint screenLocation = upWind ? ScreenLocation() : window->Frame().LeftTop();
2254 	BRect frame = CalcFrame(screenLocation, &scroll);
2255 	ResizeTo(frame.Width(), frame.Height());
2256 
2257 	if (fItems.CountItems() > 0) {
2258 		if (!scroll) {
2259 			window->ResizeTo(Bounds().Width() + 2, Bounds().Height() + 2);
2260 		} else {
2261 			BScreen screen(window);
2262 
2263 			// If we need scrolling, resize the window to fit the screen and
2264 			// attach scrollers to our cached MenuWindow.
2265 			if (dynamic_cast<BMenuBar *>(Supermenu()) == NULL) {
2266 				window->ResizeTo(Bounds().Width() + 2, screen.Frame().bottom);
2267 				frame.top = 0;
2268 			} else {
2269 				// Or, in case our parent was a BMenuBar enable scrolling with
2270 				// normal size.
2271 				window->ResizeTo(Bounds().Width() + 2, screen.Frame().bottom - frame.top);
2272 			}
2273 
2274 			static_cast<BMenuWindow *>(window)->AttachScrollers();
2275 		}
2276 	} else {
2277 		CacheFontInfo();
2278 		window->ResizeTo(StringWidth(kEmptyMenuLabel) + fPad.left + fPad.right,
2279 						fFontHeight + fPad.top + fPad.bottom);
2280 	}
2281 
2282 	if (upWind)
2283 		window->MoveTo(frame.LeftTop());
2284 }
2285 
2286 
2287 bool
2288 BMenu::IsStickyPrefOn()
2289 {
2290 	return true;
2291 }
2292 
2293 
2294 void
2295 BMenu::RedrawAfterSticky(BRect bounds)
2296 {
2297 }
2298 
2299 
2300 bool
2301 BMenu::OkToProceed(BMenuItem* item)
2302 {
2303 	bool proceed = true;
2304 	BPoint where;
2305 	ulong buttons;
2306 	GetMouse(&where, &buttons, false);
2307 	bool stickyMode = IsStickyMode();
2308 	// Quit if user clicks the mouse button in sticky mode
2309 	// or releases the mouse button in nonsticky mode
2310 	// or moves the pointer over another item
2311 	// TODO: I added the check for BMenuBar to solve a problem with Deskbar.
2312 	// Beos seems to do something similar. This could also be a bug in Deskbar, though.
2313 	if ((buttons != 0 && stickyMode)
2314 		|| (dynamic_cast<BMenuBar *>(this) == NULL && (buttons == 0 && !stickyMode)
2315 		|| HitTestItems(where) != item))
2316 		proceed = false;
2317 
2318 
2319 	return proceed;
2320 }
2321 
2322 
2323 bool
2324 BMenu::CustomTrackingWantsToQuit()
2325 {
2326 	if (fExtraMenuData != NULL && fExtraMenuData->trackingHook != NULL
2327 		&& fExtraMenuData->trackingState != NULL) {
2328 		return fExtraMenuData->trackingHook(this, fExtraMenuData->trackingState);
2329 	}
2330 
2331 	return false;
2332 }
2333 
2334 
2335 void
2336 BMenu::QuitTracking()
2337 {
2338 	_SelectItem(NULL);
2339 	if (BMenuBar *menuBar = dynamic_cast<BMenuBar *>(this))
2340 		menuBar->RestoreFocus();
2341 
2342 	fChosenItem = NULL;
2343 	fState = MENU_STATE_CLOSED;
2344 }
2345 
2346 
2347 status_t
2348 BMenu::ParseMsg(BMessage *msg, int32 *sindex, BMessage *spec,
2349 						 int32 *form, const char **prop, BMenu **tmenu,
2350 						 BMenuItem **titem, int32 *user_data,
2351 						 BMessage *reply) const
2352 {
2353 	return B_ERROR;
2354 }
2355 
2356 
2357 status_t
2358 BMenu::DoMenuMsg(BMenuItem **next, BMenu *menu, BMessage *message,
2359 						  BMessage *r, BMessage *spec, int32 f) const
2360 {
2361 	return B_ERROR;
2362 }
2363 
2364 
2365 status_t
2366 BMenu::DoMenuItemMsg(BMenuItem **next, BMenu *menu, BMessage *message,
2367 							  BMessage *r, BMessage *spec, int32 f) const
2368 {
2369 	return B_ERROR;
2370 }
2371 
2372 
2373 status_t
2374 BMenu::DoEnabledMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
2375 							 BMessage *r) const
2376 {
2377 	return B_ERROR;
2378 }
2379 
2380 
2381 status_t
2382 BMenu::DoLabelMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
2383 						   BMessage *r) const
2384 {
2385 	return B_ERROR;
2386 }
2387 
2388 
2389 status_t
2390 BMenu::DoMarkMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
2391 						  BMessage *r) const
2392 {
2393 	return B_ERROR;
2394 }
2395 
2396 
2397 status_t
2398 BMenu::DoDeleteMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
2399 							BMessage *r) const
2400 {
2401 	return B_ERROR;
2402 }
2403 
2404 
2405 status_t
2406 BMenu::DoCreateMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
2407 							BMessage *r, bool menu) const
2408 {
2409 	return B_ERROR;
2410 }
2411 
2412 
2413 // TODO: Maybe the following two methods would fit better into InterfaceDefs.cpp
2414 // In R5, they do all the work client side, we let the app_server handle the details.
2415 status_t
2416 set_menu_info(menu_info *info)
2417 {
2418 	if (!info)
2419 		return B_BAD_VALUE;
2420 
2421 	BPrivate::AppServerLink link;
2422 	link.StartMessage(AS_SET_MENU_INFO);
2423 	link.Attach<menu_info>(*info);
2424 
2425 	status_t status = B_ERROR;
2426 	if (link.FlushWithReply(status) == B_OK && status == B_OK)
2427 		BMenu::sMenuInfo = *info;
2428 		// Update also the local copy, in case anyone relies on it
2429 
2430 	return status;
2431 }
2432 
2433 
2434 status_t
2435 get_menu_info(menu_info *info)
2436 {
2437 	if (!info)
2438 		return B_BAD_VALUE;
2439 
2440 	BPrivate::AppServerLink link;
2441 	link.StartMessage(AS_GET_MENU_INFO);
2442 
2443 	status_t status = B_ERROR;
2444 	if (link.FlushWithReply(status) == B_OK && status == B_OK)
2445 		link.Read<menu_info>(info);
2446 
2447 	return status;
2448 }
2449