xref: /haiku/src/kits/interface/Menu.cpp (revision db4227c6acc95a8a72df4dab8cfb592983e529d3)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2005, Haiku, Inc.
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		Menu.cpp
23 //	Authors:		Marc Flerackers (mflerackers@androme.be)
24 //					Stefano Ceccherini (burton666@libero.it)
25 //	Description:	BMenu display a menu of selectable items.
26 //------------------------------------------------------------------------------
27 #include <Debug.h>
28 #include <File.h>
29 #include <FindDirectory.h>
30 #include <Menu.h>
31 #include <MenuItem.h>
32 #include <Path.h>
33 #include <PropertyInfo.h>
34 #include <Window.h>
35 
36 #include <MenuWindow.h>
37 
38 #ifndef COMPILE_FOR_R5
39 menu_info BMenu::sMenuInfo;
40 #endif
41 
42 class _ExtraMenuData_ {
43 public:
44 	menu_tracking_hook trackingHook;
45 	void *trackingState;
46 
47 	_ExtraMenuData_(menu_tracking_hook func, void *state)
48 	{
49 		trackingHook = func;
50 		trackingState = state;
51 	};
52 };
53 
54 
55 static property_info
56 sPropList[] = {
57 	{ "Enabled", { B_GET_PROPERTY, 0 },
58 	{ B_DIRECT_SPECIFIER, 0 }, "Returns true if menu or menu item is enabled; false "
59 		"otherwise." },
60 
61 	{ "Enabled", { B_SET_PROPERTY, 0 },
62 	{ B_DIRECT_SPECIFIER, 0 }, "Enables or disables menu or menu item." },
63 
64 	{ "Label", { B_GET_PROPERTY, 0 },
65 	{ B_DIRECT_SPECIFIER, 0 }, "Returns the string label of the menu or menu item." },
66 
67 	{ "Label", { B_SET_PROPERTY, 0 },
68 	{ B_DIRECT_SPECIFIER, 0 }, "Sets the string label of the menu or menu item." },
69 
70 	{ "Mark", { B_GET_PROPERTY, 0 },
71 	{ B_DIRECT_SPECIFIER, 0 }, "Returns true if the menu item or the menu's superitem "
72 		"is marked; false otherwise." },
73 
74 	{ "Mark", { B_SET_PROPERTY, 0 },
75 	{ B_DIRECT_SPECIFIER, 0 }, "Marks or unmarks the menu item or the menu's superitem." },
76 
77 	{ "Menu", { B_CREATE_PROPERTY, 0 },
78 	{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
79 		"Adds a new menu item at the specified index with the text label found in \"data\" "
80 		"and the int32 command found in \"what\" (used as the what field in the CMessage "
81 		"sent by the item)." },
82 
83 	{ "Menu", { B_DELETE_PROPERTY, 0 },
84 	{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
85 		"Removes the selected menu or menus." },
86 
87 	{ "Menu", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
88 	{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
89 		"Directs scripting message to the specified menu, first popping the current "
90 		"specifier off the stack." },
91 
92 	{ "MenuItem", { B_COUNT_PROPERTIES, 0 },
93 	{ B_DIRECT_SPECIFIER, 0 }, "Counts the number of menu items in the specified menu." },
94 
95 	{ "MenuItem", { B_CREATE_PROPERTY, 0 },
96 	{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
97 		"Adds a new menu item at the specified index with the text label found in \"data\" "
98 		"and the int32 command found in \"what\" (used as the what field in the CMessage "
99 		"sent by the item)." },
100 
101 	{ "MenuItem", { B_DELETE_PROPERTY, 0 },
102 	{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
103 		"Removes the specified menu item from its parent menu." },
104 
105 	{ "MenuItem", { B_EXECUTE_PROPERTY, 0 },
106 	{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
107 		"Invokes the specified menu item." },
108 
109 	{ "MenuItem", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
110 	{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
111 		"Directs scripting message to the specified menu, first popping the current "
112 		"specifier off the stack." },
113 	{0}
114 };
115 
116 
117 BMenu::BMenu(const char *name, menu_layout layout)
118 	:	BView(BRect(), name, 0,	B_WILL_DRAW),
119 		fChosenItem(NULL),
120 		fPad(14.0f, 2.0f, 20.0f, 0.0f),
121 		fSelected(NULL),
122 		fCachedMenuWindow(NULL),
123 		fSuper(NULL),
124 		fSuperitem(NULL),
125 		fAscent(-1.0f),
126 		fDescent(-1.0f),
127 		fFontHeight(-1.0f),
128 		fState(0),
129 		fLayout(layout),
130 		fExtraRect(NULL),
131 		fMaxContentWidth(0.0f),
132 		fInitMatrixSize(NULL),
133 		fExtraMenuData(NULL),
134 		fTrigger(0),
135 		fResizeToFit(true),
136 		fUseCachedMenuLayout(true),
137 		fEnabled(true),
138 		fDynamicName(false),
139 		fRadioMode(false),
140 		fTrackNewBounds(false),
141 		fStickyMode(false),
142 		fIgnoreHidden(true),
143 		fTriggerEnabled(true),
144 		fRedrawAfterSticky(false),
145 		fAttachAborted(false)
146 {
147 	InitData(NULL);
148 }
149 
150 
151 BMenu::BMenu(const char *name, float width, float height)
152 	:	BView(BRect(0.0f, width, 0.0f, height), name, 0, B_WILL_DRAW),
153 		fChosenItem(NULL),
154 		fSelected(NULL),
155 		fCachedMenuWindow(NULL),
156 		fSuper(NULL),
157 		fSuperitem(NULL),
158 		fAscent(-1.0f),
159 		fDescent(-1.0f),
160 		fFontHeight(-1.0f),
161 		fState(0),
162 		fLayout(B_ITEMS_IN_MATRIX),
163 		fExtraRect(NULL),
164 		fMaxContentWidth(0.0f),
165 		fInitMatrixSize(NULL),
166 		fExtraMenuData(NULL),
167 		fTrigger(0),
168 		fResizeToFit(true),
169 		fUseCachedMenuLayout(true),
170 		fEnabled(true),
171 		fDynamicName(false),
172 		fRadioMode(false),
173 		fTrackNewBounds(false),
174 		fStickyMode(false),
175 		fIgnoreHidden(true),
176 		fTriggerEnabled(true),
177 		fRedrawAfterSticky(false),
178 		fAttachAborted(false)
179 {
180 	InitData(NULL);
181 }
182 
183 
184 BMenu::~BMenu()
185 {
186 	RemoveItems(0, CountItems(), true);
187 
188 	DeleteMenuWindow();
189 
190 	delete fInitMatrixSize;
191 	delete fExtraMenuData;
192 }
193 
194 
195 BMenu::BMenu(BMessage *archive)
196 	:	BView(archive)
197 {
198 	InitData(archive);
199 }
200 
201 
202 BArchivable *
203 BMenu::Instantiate(BMessage *data)
204 {
205 	if (validate_instantiation(data, "BMenu"))
206 		return new BMenu(data);
207 	else
208 		return NULL;
209 }
210 
211 
212 status_t
213 BMenu::Archive(BMessage *data, bool deep) const
214 {
215 	status_t err = BView::Archive(data, deep);
216 
217 	if (err < B_OK)
218 		return err;
219 
220 	if (Layout() != B_ITEMS_IN_ROW) {
221 		err = data->AddInt32("_layout", Layout());
222 
223 		if (err < B_OK)
224 			return err;
225 	}
226 
227 	err = data->AddBool("_rsize_to_fit", fResizeToFit);
228 
229 	if (err < B_OK)
230 		return err;
231 
232 	err = data->AddBool("_disable", !IsEnabled());
233 
234 	if (err < B_OK)
235 		return err;
236 
237 	err = data->AddBool("_radio", IsRadioMode());
238 
239 	if (err < B_OK)
240 		return err;
241 
242 	err = data->AddBool("_trig_disabled", AreTriggersEnabled());
243 
244 	if (err < B_OK)
245 		return err;
246 
247 	err = data->AddBool("_dyn_label", fDynamicName);
248 
249 	if (err < B_OK)
250 		return err;
251 
252 	err = data->AddFloat("_maxwidth", fMaxContentWidth);
253 
254 	if (err < B_OK)
255 		return err;
256 
257 	if (deep) {
258 		// TODO store items and rects
259 	}
260 
261 	return err;
262 }
263 
264 
265 void
266 BMenu::AttachedToWindow()
267 {
268 	BView::AttachedToWindow();
269 
270 	InvalidateLayout();
271 }
272 
273 
274 void
275 BMenu::DetachedFromWindow()
276 {
277 	BView::DetachedFromWindow();
278 }
279 
280 
281 bool
282 BMenu::AddItem(BMenuItem *item)
283 {
284 	return AddItem(item, CountItems());
285 }
286 
287 
288 bool
289 BMenu::AddItem(BMenuItem *item, int32 index)
290 {
291 	if (fLayout == B_ITEMS_IN_MATRIX)
292 		debugger("BMenu::AddItem(BMenuItem *, int32) this method can only"
293 				"be called if the menu layout is not B_ITEMS_IN_MATRIX");
294 
295 	return _AddItem(item, index);
296 }
297 
298 
299 bool
300 BMenu::AddItem(BMenuItem *item, BRect frame)
301 {
302 	if (fLayout != B_ITEMS_IN_MATRIX)
303 		debugger("BMenu::AddItem(BMenuItem *, BRect) this method can only"
304 			" be called if the menu layout is B_ITEMS_IN_MATRIX");
305 
306 	if (!item)
307 		return false;
308 
309 	item->fBounds = frame;
310 
311 	return _AddItem(item, CountItems());
312 }
313 
314 
315 bool
316 BMenu::AddItem(BMenu *submenu)
317 {
318 	BMenuItem *item = new BMenuItem(submenu);
319 	if (!item)
320 		return false;
321 
322 	return _AddItem(item, CountItems());
323 }
324 
325 
326 bool
327 BMenu::AddItem(BMenu *submenu, int32 index)
328 {
329 	if (fLayout == B_ITEMS_IN_MATRIX)
330 		debugger("BMenu::AddItem(BMenuItem *, int32) this method can only"
331 				"be called if the menu layout is not B_ITEMS_IN_MATRIX");
332 
333 	BMenuItem *item = new BMenuItem(submenu);
334 	if (!item)
335 		return false;
336 
337 	return _AddItem(item, index);
338 }
339 
340 
341 bool
342 BMenu::AddItem(BMenu *submenu, BRect frame)
343 {
344 	if (fLayout != B_ITEMS_IN_MATRIX)
345 		debugger("BMenu::AddItem(BMenu *, BRect) this method can only"
346 			" be called if the menu layout is B_ITEMS_IN_MATRIX");
347 
348 	BMenuItem *item = new BMenuItem(submenu);
349 	item->fBounds = frame;
350 
351 	return _AddItem(item, CountItems());
352 }
353 
354 
355 bool
356 BMenu::AddList(BList *list, int32 index)
357 {
358 	// TODO: test this function, it's not documented in the bebook.
359 	int32 numItems = 0;
360 	if (list != NULL)
361 		numItems = list->CountItems();
362 
363 	for (int32 i = 0; i < numItems; i++) {
364 		BMenuItem *item = static_cast<BMenuItem *>(list->ItemAt(i));
365 		if (item != NULL)
366 			_AddItem(item, index + i);
367 	}
368 
369 	// TODO: return false if needed
370 	return true;
371 }
372 
373 
374 bool
375 BMenu::AddSeparatorItem()
376 {
377 	BMenuItem *item = new BSeparatorItem();
378 	return _AddItem(item, CountItems());
379 }
380 
381 
382 bool
383 BMenu::RemoveItem(BMenuItem *item)
384 {
385 	// TODO: Check if item is also deleted
386 	return RemoveItems(0, 0, item, false);
387 }
388 
389 
390 BMenuItem *
391 BMenu::RemoveItem(int32 index)
392 {
393 	BMenuItem *item = ItemAt(index);
394 	RemoveItems(index, 1, NULL, false);
395 	return item;
396 }
397 
398 
399 bool
400 BMenu::RemoveItems(int32 index, int32 count, bool del)
401 {
402 	return RemoveItems(index, count, NULL, del);
403 }
404 
405 
406 bool
407 BMenu::RemoveItem(BMenu *submenu)
408 {
409 	for (int i = 0; i < fItems.CountItems(); i++)
410 		if (static_cast<BMenuItem *>(fItems.ItemAt(i))->Submenu() == submenu)
411 			return RemoveItems(i, 1, NULL, false);
412 
413 	return false;
414 }
415 
416 
417 int32
418 BMenu::CountItems() const
419 {
420 	return fItems.CountItems();
421 }
422 
423 
424 BMenuItem *
425 BMenu::ItemAt(int32 index) const
426 {
427 	return static_cast<BMenuItem *>(fItems.ItemAt(index));
428 }
429 
430 
431 BMenu *
432 BMenu::SubmenuAt(int32 index) const
433 {
434 	BMenuItem *item = static_cast<BMenuItem *>(fItems.ItemAt(index));
435 	return (item != NULL) ? item->Submenu() : NULL;
436 }
437 
438 
439 int32
440 BMenu::IndexOf(BMenuItem *item) const
441 {
442 	return fItems.IndexOf(item);
443 }
444 
445 
446 int32
447 BMenu::IndexOf(BMenu *submenu) const
448 {
449 	for (int32 i = 0; i < fItems.CountItems(); i++)
450 		if (ItemAt(i)->Submenu() == submenu)
451 			return i;
452 
453 	return -1;
454 }
455 
456 
457 BMenuItem *
458 BMenu::FindItem(const char *label) const
459 {
460 	BMenuItem *item = NULL;
461 
462 	for (int32 i = 0; i < CountItems(); i++) {
463 		item = ItemAt(i);
464 
465 		if (item->Label() && strcmp(item->Label(), label) == 0)
466 			break;
467 
468 		if (item->Submenu()) {
469 			item = item->Submenu()->FindItem(label);
470 			if (item)
471 				break;
472 		}
473 	}
474 
475 	return item;
476 }
477 
478 
479 BMenuItem *
480 BMenu::FindItem(uint32 command) const
481 {
482 	BMenuItem *item = NULL;
483 
484 	for (int32 i = 0; i < CountItems(); i++) {
485 		item = ItemAt(i);
486 
487 		if (item->Command() == command)
488 			break;
489 
490 		if (item->Submenu()) {
491 			item = item->Submenu()->FindItem(command);
492 			if (item)
493 				break;
494 		}
495 	}
496 
497 	return item;
498 }
499 
500 
501 status_t
502 BMenu::SetTargetForItems(BHandler *handler)
503 {
504 	for (int32 i = 0; i < fItems.CountItems(); i++)
505 		if (ItemAt(i)->SetTarget(handler) < B_OK)
506 			return B_ERROR;
507 
508 	return B_OK;
509 }
510 
511 
512 status_t
513 BMenu::SetTargetForItems(BMessenger messenger)
514 {
515 	for (int32 i = 0; i < fItems.CountItems(); i++)
516 		if (ItemAt(i)->SetTarget(messenger) < B_OK)
517 			return B_ERROR;
518 
519 	return B_OK;
520 }
521 
522 
523 void
524 BMenu::SetEnabled(bool enabled)
525 {
526 	fEnabled = enabled;
527 
528 	for (int32 i = 0; i < CountItems(); i++)
529 		ItemAt(i)->SetEnabled(enabled);
530 }
531 
532 
533 void
534 BMenu::SetRadioMode(bool flag)
535 {
536 	fRadioMode = flag;
537 }
538 
539 
540 void
541 BMenu::SetTriggersEnabled(bool flag)
542 {
543 	fTriggerEnabled = flag;
544 }
545 
546 
547 void
548 BMenu::SetMaxContentWidth(float width)
549 {
550 	fMaxContentWidth = width;
551 }
552 
553 
554 void
555 BMenu::SetLabelFromMarked(bool flag)
556 {
557 	fDynamicName = flag;
558 }
559 
560 
561 bool
562 BMenu::IsLabelFromMarked()
563 {
564 	return fDynamicName;
565 }
566 
567 
568 bool
569 BMenu::IsEnabled() const
570 {
571 	return fEnabled;
572 }
573 
574 
575 bool
576 BMenu::IsRadioMode() const
577 {
578 	return fRadioMode;
579 }
580 
581 
582 bool
583 BMenu::AreTriggersEnabled() const
584 {
585 	return fTriggerEnabled;
586 }
587 
588 
589 bool
590 BMenu::IsRedrawAfterSticky() const
591 {
592 	return fRedrawAfterSticky;
593 }
594 
595 
596 float
597 BMenu::MaxContentWidth() const
598 {
599 	return fMaxContentWidth;
600 }
601 
602 
603 BMenuItem *
604 BMenu::FindMarked()
605 {
606 	for (int i = 0; i < fItems.CountItems(); i++)
607 		if (((BMenuItem*)fItems.ItemAt(i))->IsMarked())
608 			return (BMenuItem*)fItems.ItemAt(i);
609 
610 	return NULL;
611 }
612 
613 
614 BMenu *
615 BMenu::Supermenu() const
616 {
617 	return fSuper;
618 }
619 
620 
621 BMenuItem *
622 BMenu::Superitem() const
623 {
624 	return fSuperitem;
625 }
626 
627 
628 void
629 BMenu::MessageReceived(BMessage *msg)
630 {
631 	BView::MessageReceived(msg);
632 }
633 
634 
635 void
636 BMenu::KeyDown(const char *bytes, int32 numBytes)
637 {
638 	switch (bytes[0]) {
639 		case B_UP_ARROW:
640 		{
641 			if (fSelected) 	{
642 				fSelected->fSelected = false;
643 
644 				if (fSelected == fItems.FirstItem())
645 					fSelected = static_cast<BMenuItem *>(fItems.LastItem());
646 				else
647 					fSelected = ItemAt(IndexOf(fSelected) - 1);
648 			} else
649 				fSelected = static_cast<BMenuItem *>(fItems.LastItem());
650 
651 			fSelected->fSelected = true;
652 
653 			break;
654 		}
655 		case B_DOWN_ARROW:
656 		{
657 			if (fSelected) {
658 				fSelected->fSelected = false;
659 
660 				if (fSelected == fItems.LastItem())
661 					fSelected = static_cast<BMenuItem *>(fItems.FirstItem());
662 				else
663 					fSelected = ItemAt(IndexOf(fSelected) + 1);
664 			} else
665 				fSelected = static_cast<BMenuItem *>(fItems.FirstItem());
666 
667 			fSelected->fSelected = true;
668 
669 			break;
670 		}
671 		case B_HOME:
672 		{
673 			if (fSelected)
674 				fSelected->fSelected = false;
675 
676 			fSelected = static_cast<BMenuItem *>(fItems.FirstItem());
677 			fSelected->fSelected = true;
678 
679 			break;
680 		}
681 		case B_END:
682 		{
683 			if (fSelected)
684 				fSelected->fSelected = false;
685 
686 			fSelected = static_cast<BMenuItem *>(fItems.LastItem());
687 			fSelected->fSelected = true;
688 
689 			break;
690 		}
691 		case B_ENTER:
692 		case B_SPACE:
693 		{
694 			if (fSelected)
695 				InvokeItem(fSelected);
696 
697 			break;
698 		}
699 		default:
700 			BView::KeyDown(bytes, numBytes);
701 	}
702 }
703 
704 
705 void
706 BMenu::Draw(BRect updateRect)
707 {
708 	DrawBackground(updateRect);
709 	DrawItems(updateRect);
710 }
711 
712 
713 void
714 BMenu::GetPreferredSize(float *width, float *height)
715 {
716 	ComputeLayout(0, true, false, width, height);
717 }
718 
719 
720 void
721 BMenu::ResizeToPreferred()
722 {
723 	BView::ResizeToPreferred();
724 }
725 
726 
727 void
728 BMenu::FrameMoved(BPoint new_position)
729 {
730 	BView::FrameMoved(new_position);
731 }
732 
733 
734 void
735 BMenu::FrameResized(float new_width, float new_height)
736 {
737 	BView::FrameResized(new_width, new_height);
738 }
739 
740 
741 void
742 BMenu::InvalidateLayout()
743 {
744 	CacheFontInfo();
745 	LayoutItems(0);
746 	Invalidate();
747 }
748 
749 
750 BHandler *
751 BMenu::ResolveSpecifier(BMessage *msg, int32 index,
752 								  BMessage *specifier, int32 form,
753 								  const char *property)
754 {
755 	BPropertyInfo propInfo(sPropList);
756 	BHandler *target = NULL;
757 
758 	switch (propInfo.FindMatch(msg, 0, specifier, form, property)) {
759 		case B_ERROR:
760 			break;
761 
762 		case 0:
763 		case 1:
764 		case 2:
765 		case 3:
766 		case 4:
767 		case 5:
768 		case 6:
769 		case 7:
770 			target = this;
771 			break;
772 		case 8:
773 			// TODO: redirect to menu
774 			target = this;
775 			break;
776 		case 9:
777 		case 10:
778 		case 11:
779 		case 12:
780 			target = this;
781 			break;
782 		case 13:
783 			// TODO: redirect to menuitem
784 			target = this;
785 			break;
786 	}
787 
788 	if (!target)
789 		target = BView::ResolveSpecifier(msg, index, specifier, form,
790 		property);
791 
792 	return target;
793 }
794 
795 
796 status_t
797 BMenu::GetSupportedSuites(BMessage *data)
798 {
799 	status_t err;
800 
801 	if (data == NULL)
802 		return B_BAD_VALUE;
803 
804 	err = data->AddString("suites", "suite/vnd.Be-menu");
805 
806 	if (err < B_OK)
807 		return err;
808 
809 	BPropertyInfo prop_info(sPropList);
810 	err = data->AddFlat("messages", &prop_info);
811 
812 	if (err < B_OK)
813 		return err;
814 
815 	return BView::GetSupportedSuites(data);
816 }
817 
818 
819 status_t
820 BMenu::Perform(perform_code d, void *arg)
821 {
822 	return BView::Perform(d, arg);
823 }
824 
825 
826 void
827 BMenu::MakeFocus(bool focused)
828 {
829 	BView::MakeFocus(focused);
830 }
831 
832 
833 void
834 BMenu::AllAttached()
835 {
836 	BView::AllAttached();
837 }
838 
839 
840 void
841 BMenu::AllDetached()
842 {
843 	BView::AllDetached();
844 }
845 
846 
847 BMenu::BMenu(BRect frame, const char *name, uint32 resizingMode, uint32 flags,
848 			 menu_layout layout, bool resizeToFit)
849 	:	BView(frame, name, resizingMode, flags),
850 		fChosenItem(NULL),
851 		fSelected(NULL),
852 		fCachedMenuWindow(NULL),
853 		fSuper(NULL),
854 		fSuperitem(NULL),
855 		fAscent(-1.0f),
856 		fDescent(-1.0f),
857 		fFontHeight(-1.0f),
858 		fState(0),
859 		fLayout(layout),
860 		fExtraRect(NULL),
861 		fMaxContentWidth(0.0f),
862 		fInitMatrixSize(NULL),
863 		fExtraMenuData(NULL),
864 		fTrigger(0),
865 		fResizeToFit(resizeToFit),
866 		fUseCachedMenuLayout(true),
867 		fEnabled(true),
868 		fDynamicName(false),
869 		fRadioMode(false),
870 		fTrackNewBounds(false),
871 		fStickyMode(false),
872 		fIgnoreHidden(true),
873 		fTriggerEnabled(true),
874 		fRedrawAfterSticky(false),
875 		fAttachAborted(false)
876 {
877 	InitData(NULL);
878 }
879 
880 
881 BPoint
882 BMenu::ScreenLocation()
883 {
884 	BMenu *superMenu = Supermenu();
885 	BMenuItem *superItem = Superitem();
886 
887 	if (superMenu == NULL && superItem == NULL) {
888 		debugger("BMenu can't determine where to draw."
889 			"Override BMenu::ScreenLocation() to determine location.");
890 	}
891 
892 	BPoint point;
893 	if (superMenu->Layout() == B_ITEMS_IN_COLUMN)
894 		point = superItem->Frame().RightTop();
895 	else
896 		point = superItem->Frame().LeftBottom() + BPoint(1.0f, 1.0f);
897 
898 	superMenu->ConvertToScreen(&point);
899 
900 	return point;
901 }
902 
903 
904 void
905 BMenu::SetItemMargins(float left, float top, float right, float bottom)
906 {
907 	fPad.Set(left, top, right, bottom);
908 }
909 
910 
911 void
912 BMenu::GetItemMargins(float *left, float *top, float *right,
913 						   float *bottom) const
914 {
915 	if (left != NULL)
916 		*left = fPad.left;
917 	if (top != NULL)
918 		*top = fPad.top;
919 	if (right != NULL)
920 		*right = fPad.right;
921 	if (bottom != NULL)
922 		*bottom = fPad.bottom;
923 }
924 
925 
926 menu_layout
927 BMenu::Layout() const
928 {
929 	return fLayout;
930 }
931 
932 
933 void
934 BMenu::Show()
935 {
936 	Show(false);
937 }
938 
939 
940 void
941 BMenu::Show(bool selectFirst)
942 {
943 	_show(selectFirst);
944 }
945 
946 
947 void
948 BMenu::Hide()
949 {
950 	_hide();
951 }
952 
953 
954 BMenuItem *
955 BMenu::Track(bool openAnyway, BRect *clickToOpenRect)
956 {
957 	if (IsStickyPrefOn())
958 		openAnyway = false;
959 
960 	SetStickyMode(openAnyway);
961 
962 	if (LockLooper()) {
963 		RedrawAfterSticky(Bounds());
964 		UnlockLooper();
965 	}
966 
967 	if (clickToOpenRect != NULL && LockLooper()) {
968 		fExtraRect = clickToOpenRect;
969 		ConvertFromScreen(fExtraRect);
970 		UnlockLooper();
971 	}
972 
973 	int action;
974 	BMenuItem *menuItem = _track(&action, -1);
975 
976 	SetStickyMode(false);
977 	fExtraRect = NULL;
978 
979 	return menuItem;
980 }
981 
982 
983 bool
984 BMenu::AddDynamicItem(add_state s)
985 {
986 	// Implemented in subclasses
987 	return false;
988 }
989 
990 
991 void
992 BMenu::DrawBackground(BRect update)
993 {
994 	BRect rect = Bounds() & update;
995 	rgb_color oldColor = HighColor();
996 
997 	SetHighColor(sMenuInfo.background_color);
998 	FillRect(rect, B_SOLID_HIGH);
999 
1000 	SetHighColor(oldColor);
1001 }
1002 
1003 
1004 void
1005 BMenu::SetTrackingHook(menu_tracking_hook func, void *state)
1006 {
1007 	delete fExtraMenuData;
1008 	fExtraMenuData = new _ExtraMenuData_(func, state);
1009 }
1010 
1011 
1012 void BMenu::_ReservedMenu3() {}
1013 void BMenu::_ReservedMenu4() {}
1014 void BMenu::_ReservedMenu5() {}
1015 void BMenu::_ReservedMenu6() {}
1016 
1017 
1018 BMenu &
1019 BMenu::operator=(const BMenu &)
1020 {
1021 	return *this;
1022 }
1023 
1024 
1025 void
1026 BMenu::InitData(BMessage *data)
1027 {
1028 	// TODO: Get _color, _fname, _fflt from the message, if present
1029 	BFont font;
1030 	font.SetFamilyAndStyle(sMenuInfo.f_family, sMenuInfo.f_style);
1031 	font.SetSize(sMenuInfo.font_size);
1032 	SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE);
1033 
1034 	SetLowColor(sMenuInfo.background_color);
1035 	SetViewColor(sMenuInfo.background_color);
1036 
1037 	if (data != NULL) {
1038 		data->FindInt32("_layout", (int32 *)&fLayout);
1039 		data->FindBool("_rsize_to_fit", &fResizeToFit);
1040 		data->FindBool("_disable", &fEnabled);
1041 		data->FindBool("_radio", &fRadioMode);
1042 
1043 		bool disableTrigger = false;
1044 		data->FindBool("_trig_disabled", &disableTrigger);
1045 		fTriggerEnabled = !disableTrigger;
1046 
1047 		data->FindBool("_dyn_label", &fDynamicName);
1048 		data->FindFloat("_maxwidth", &fMaxContentWidth);
1049 	}
1050 }
1051 
1052 
1053 bool
1054 BMenu::_show(bool selectFirstItem)
1055 {
1056 	BWindow *menuWindow = MenuWindow();
1057 
1058 	menuWindow->Lock();
1059 	menuWindow->ChildAt(0)->AddChild(this);
1060 
1061 	// We're doing this here because ConvertToScreen() needs:
1062 	// 1. The BView to be attached (see the above line).
1063 	// 2. The looper locked or not running (the Show() call below starts the looper)
1064 	if (fSuper != NULL)
1065 		fSuperbounds = fSuper->ConvertToScreen(fSuper->Bounds());
1066 
1067 	UpdateWindowViewSize();
1068 
1069 	menuWindow->Unlock();
1070 	menuWindow->Show();
1071 
1072 	return true;
1073 }
1074 
1075 
1076 void
1077 BMenu::_hide()
1078 {
1079 	if (fCachedMenuWindow != NULL) {
1080 		fCachedMenuWindow->Lock();
1081 		fCachedMenuWindow->ChildAt(0)->RemoveChild(this);
1082 		fCachedMenuWindow->Hide();
1083 		fCachedMenuWindow->Unlock();
1084 	}
1085 
1086 }
1087 
1088 
1089 BMenuItem *
1090 BMenu::_track(int *action, long start)
1091 {
1092 	// TODO: Take Sticky mode into account
1093 	BPoint location;
1094 	ulong buttons;
1095 	BMenuItem *item = NULL;
1096 	int localAction = 0;
1097 	do {
1098 		if (LockLooper()) {
1099 			GetMouse(&location, &buttons);
1100 			if (OverSuper(location)) {
1101 				UnlockLooper();
1102 				break;
1103 			}
1104 
1105 			item = HitTestItems(location, B_ORIGIN);
1106 
1107 			// TODO: Sometimes the menu flickers a bit.
1108 			// try to be smarter and suggest an update area,
1109 			// instead of invalidating the whole view.
1110 			if (item != NULL) {
1111 				if (item != fSelected) {
1112 					SelectItem(item);
1113 					Invalidate();
1114 				}
1115 
1116 				int submenuAction = 0;
1117 				BMenuItem *submenuItem = NULL;
1118 				// TODO: Review this as it doesn't work very well,
1119 				// BMenu::_track() isn't always called when needed.
1120 				if (item->Submenu() != NULL) {
1121 					UnlockLooper();
1122 
1123 					submenuItem = item->Submenu()->_track(&submenuAction);
1124 					if (submenuAction == 5) {
1125 						item = submenuItem;
1126 						localAction = submenuAction;
1127 						break;
1128 					}
1129 
1130 					if (!LockLooper())
1131 						break;
1132 				}
1133 			}
1134 
1135 			UnlockLooper();
1136 		}
1137 
1138 		snooze(50000);
1139 	} while (buttons != 0);
1140 
1141 	// TODO: A deeper investigation of actions
1142 	// would be nice. Consider building an enum
1143 	// with the possible actions, and putting it in a
1144 	// private, shared header (BMenuBar needs to know about them too).
1145 	if (localAction == 0) {
1146 		if (buttons != 0)
1147 			localAction = 0;
1148 		else
1149 			localAction = 5;
1150 	}
1151 
1152 	if (action != NULL)
1153 		*action = localAction;
1154 
1155 	if (LockLooper()) {
1156 		SelectItem(NULL);
1157 		UnlockLooper();
1158 	}
1159 
1160 	return item;
1161 }
1162 
1163 
1164 bool
1165 BMenu::_AddItem(BMenuItem *item, int32 index)
1166 {
1167 	ASSERT(item != NULL);
1168 
1169 	bool err = fItems.AddItem(item, index);
1170 
1171 	if (!err)
1172 		return err;
1173 
1174 	item->SetSuper(this);
1175 
1176 	// Make sure we update the layout in case we are already attached.
1177 	if (Window() && fResizeToFit) {
1178 		LayoutItems(index);
1179 		Invalidate();
1180 	}
1181 
1182 	// Find the root menu window, so we can install this item.
1183 	BMenu *root = this;
1184 	while (root->Supermenu())
1185 		root = root->Supermenu();
1186 
1187 	if (root->Window())
1188 		Install(root->Window());
1189 
1190 	return err;
1191 }
1192 
1193 
1194 bool
1195 BMenu::RemoveItems(int32 index, int32 count, BMenuItem *_item, bool del)
1196 {
1197 	bool result = false;
1198 
1199 	// The plan is simple: If we're given a BMenuItem directly, we use it
1200 	// and ignore index and count. Otherwise, we use them instead.
1201 	if (_item != NULL) {
1202 		fItems.RemoveItem(_item);
1203 		_item->SetSuper(NULL);
1204 		_item->Uninstall();
1205 		if (del)
1206 			delete _item;
1207 		result = true;
1208 	} else {
1209 		BMenuItem *item = NULL;
1210 		// We iterate backwards because it's simpler
1211 		// TODO: We should check if index and count are in bounds.
1212 		for (int32 i = index + count - 1; i >= index; i--) {
1213 			item = static_cast<BMenuItem *>(fItems.ItemAt(index));
1214 			if (item != NULL) {
1215 				fItems.RemoveItem(item);
1216 				item->SetSuper(NULL);
1217 				item->Uninstall();
1218 				if (del)
1219 					delete item;
1220 				if (!result)
1221 					result = true;
1222 			}
1223 		}
1224 	}
1225 
1226 	InvalidateLayout();
1227 
1228 	return result;
1229 }
1230 
1231 
1232 void
1233 BMenu::LayoutItems(int32 index)
1234 {
1235 	CalcTriggers();
1236 
1237 	float width, height;
1238 	ComputeLayout(index, true, true, &width, &height);
1239 
1240 	ResizeTo(width, height);
1241 
1242 	// Move the BMenu to 1, 1, if it's attached to a BMenuWindow,
1243 	// (that means it's a BMenu, BMenuBars are attached to regular BWindows).
1244 	// This is needed to be able to draw the frame around the BMenu.
1245 	if (dynamic_cast<BMenuWindow *>(Window()) != NULL)
1246 		MoveTo(1, 1);
1247 }
1248 
1249 
1250 void
1251 BMenu::ComputeLayout(int32 index, bool bestFit, bool moveItems,
1252 						  float* width, float* height)
1253 {
1254 	// TODO: Take "bestFit", "moveItems", "index" into account.
1255 	BRect frame(0, 0, 0, 0);
1256 	float iWidth, iHeight;
1257 	BMenuItem *item = NULL;
1258 
1259 	switch (fLayout) {
1260 		case B_ITEMS_IN_COLUMN:
1261 		{
1262 			for (int32 i = 0; i < fItems.CountItems(); i++) {
1263 				item = ItemAt(i);
1264 				if (item != NULL) {
1265 					item->GetContentSize(&iWidth, &iHeight);
1266 
1267 					if (item->fModifiers && item->fShortcutChar)
1268 						iWidth += 25.0f;
1269 
1270 					item->fBounds.left = 0.0f;
1271 					item->fBounds.top = frame.bottom;
1272 					item->fBounds.bottom = item->fBounds.top + iHeight + fPad.top + fPad.bottom;
1273 
1274 					frame.right = max_c(frame.right, iWidth + fPad.left + fPad.right) + 20;
1275 					frame.bottom = item->fBounds.bottom + 1.0f;
1276 				}
1277 			}
1278 
1279 			for (int32 i = 0; i < fItems.CountItems(); i++)
1280 				ItemAt(i)->fBounds.right = frame.right;
1281 
1282 			frame.right = (float)ceil(frame.right);
1283 			frame.bottom--;
1284 			break;
1285 		}
1286 
1287 		case B_ITEMS_IN_ROW:
1288 		{
1289 			font_height fh;
1290 			GetFontHeight(&fh);
1291 			frame = BRect(0.0f, 0.0f, 0.0f,
1292 				(float)ceil(fh.ascent) + (float)ceil(fh.descent) + fPad.top + fPad.bottom);
1293 
1294 			for (int32 i = 0; i < fItems.CountItems(); i++) {
1295 				item = ItemAt(i);
1296 				if (item != NULL) {
1297 					item->GetContentSize(&iWidth, &iHeight);
1298 
1299 					item->fBounds.left = frame.right;
1300 					item->fBounds.top = 0.0f;
1301 					item->fBounds.right = item->fBounds.left + iWidth + fPad.left + fPad.right;
1302 
1303 					frame.right = item->fBounds.right + 1.0f;
1304 					frame.bottom = max_c(frame.bottom, iHeight + fPad.top + fPad.bottom);
1305 				}
1306 			}
1307 
1308 			for (int32 i = 0; i < fItems.CountItems(); i++)
1309 				ItemAt(i)->fBounds.bottom = frame.bottom;
1310 
1311 			frame.right = (float)ceil(frame.right) + 8.0f;
1312 			break;
1313 		}
1314 
1315 		case B_ITEMS_IN_MATRIX:
1316 		{
1317 			for (int32 i = 0; i < CountItems(); i++) {
1318 				item = ItemAt(i);
1319 				if (item != NULL) {
1320 					frame.left = min_c(frame.left, item->Frame().left);
1321 					frame.right = max_c(frame.right, item->Frame().right);
1322 					frame.top = min_c(frame.top, item->Frame().top);
1323 					frame.bottom = max_c(frame.bottom, item->Frame().bottom);
1324 				}
1325 			}
1326 			break;
1327 		}
1328 
1329 		default:
1330 			break;
1331 	}
1332 
1333 	// This is for BMenuBar.
1334 	if ((ResizingMode() & B_FOLLOW_LEFT_RIGHT) == B_FOLLOW_LEFT_RIGHT) {
1335 		if (Parent())
1336 			*width = Parent()->Frame().Width() + 1;
1337 		else
1338 			*width = Window()->Frame().Width() + 1;
1339 
1340 		*height = frame.Height();
1341 	} else {
1342 		*width = frame.Width();
1343 		*height = frame.Height();
1344 	}
1345 }
1346 
1347 
1348 BRect
1349 BMenu::Bump(BRect current, BPoint extent, int32 index) const
1350 {
1351 	return BRect();
1352 }
1353 
1354 
1355 BPoint
1356 BMenu::ItemLocInRect(BRect frame) const
1357 {
1358 	return BPoint();
1359 }
1360 
1361 
1362 BRect
1363 BMenu::CalcFrame(BPoint where, bool *scrollOn)
1364 {
1365 	return BRect();
1366 }
1367 
1368 
1369 bool
1370 BMenu::ScrollMenu(BRect bounds, BPoint loc, bool *fast)
1371 {
1372 	return false;
1373 }
1374 
1375 
1376 void
1377 BMenu::ScrollIntoView(BMenuItem *item)
1378 {
1379 }
1380 
1381 
1382 void
1383 BMenu::DrawItems(BRect updateRect)
1384 {
1385 	for (int32 i = 0; i < fItems.CountItems(); i++) {
1386 		if (ItemAt(i)->Frame().Intersects(updateRect))
1387 			ItemAt(i)->Draw();
1388 	}
1389 }
1390 
1391 
1392 int
1393 BMenu::State(BMenuItem **item) const
1394 {
1395 	return 0;
1396 }
1397 
1398 
1399 void
1400 BMenu::InvokeItem(BMenuItem *item, bool now)
1401 {
1402 	if (item->Submenu())
1403 		item->Submenu()->Show();
1404 	else if (IsRadioMode())
1405 		item->SetMarked(true);
1406 
1407 	item->Invoke();
1408 }
1409 
1410 
1411 bool
1412 BMenu::OverSuper(BPoint location)
1413 {
1414 	if (!Supermenu())
1415 		return false;
1416 
1417 	ConvertToScreen(&location);
1418 
1419 	return fSuperbounds.Contains(location);
1420 }
1421 
1422 
1423 bool
1424 BMenu::OverSubmenu(BMenuItem *item, BPoint loc)
1425 {
1426 	// TODO: we assume that loc is in screen coords
1427 	if (!item->Submenu())
1428 		return false;
1429 
1430 	return item->Submenu()->Window()->Frame().Contains(loc);
1431 }
1432 
1433 
1434 BMenuWindow	*
1435 BMenu::MenuWindow()
1436 {
1437 	if (fCachedMenuWindow == NULL) {
1438 		// Menu windows get the BMenu's handler name
1439 		fCachedMenuWindow = new BMenuWindow(Name());
1440 		UpdateWindowViewSize();
1441 	}
1442 
1443 	return fCachedMenuWindow;
1444 }
1445 
1446 
1447 void
1448 BMenu::DeleteMenuWindow()
1449 {
1450 	if (fCachedMenuWindow != NULL) {
1451 		fCachedMenuWindow->Lock();
1452 		fCachedMenuWindow->Quit();
1453 		fCachedMenuWindow = NULL;
1454 	}
1455 }
1456 
1457 
1458 BMenuItem *
1459 BMenu::HitTestItems(BPoint where, BPoint slop) const
1460 {
1461 	// TODO: Take "slop" into account ?
1462 	int32 itemCount = CountItems();
1463 	for (int32 i = 0; i < itemCount; i++) {
1464 		BMenuItem *item = ItemAt(i);
1465 		if (item->Frame().Contains(where))
1466 			return item;
1467 	}
1468 
1469 	return NULL;
1470 }
1471 
1472 
1473 BRect
1474 BMenu::Superbounds() const
1475 {
1476 	return fSuperbounds;
1477 }
1478 
1479 
1480 void
1481 BMenu::CacheFontInfo()
1482 {
1483 	font_height fh;
1484 	GetFontHeight(&fh);
1485 	fAscent = fh.ascent;
1486 	fDescent = fh.descent;
1487 	fFontHeight = (float)ceil(fh.ascent + fh.descent + fh.leading);
1488 }
1489 
1490 
1491 void
1492 BMenu::ItemMarked(BMenuItem *item)
1493 {
1494 	if (IsRadioMode()) {
1495 		for (int32 i = 0; i < CountItems(); i++)
1496 			if (ItemAt(i) != item)
1497 				ItemAt(i)->SetMarked(false);
1498 	}
1499 
1500 	if (IsLabelFromMarked() && Superitem())
1501 		Superitem()->SetLabel(item->Label());
1502 }
1503 
1504 
1505 void
1506 BMenu::Install(BWindow *target)
1507 {
1508 	for (int32 i = 0; i < CountItems(); i++)
1509 		ItemAt(i)->Install(target);
1510 }
1511 
1512 
1513 void
1514 BMenu::Uninstall()
1515 {
1516 	for (int32 i = 0; i < CountItems(); i++)
1517 		ItemAt(i)->Uninstall();
1518 }
1519 
1520 
1521 void
1522 BMenu::SelectItem(BMenuItem *menuItem, uint32 showSubmenu, bool selectFirstItem)
1523 {
1524 	// TODO: make use of "showSubmenu" and "selectFirstItem".
1525 	if (fSelected != NULL) {
1526 		fSelected->Select(false);
1527 		if (fSelected->Submenu() != NULL)
1528 			fSelected->Submenu()->_hide();
1529 	}
1530 
1531 	if (menuItem != NULL)
1532 		menuItem->Select(true);
1533 
1534 	fSelected = menuItem;
1535 	if (fSelected != NULL && fSelected->Submenu() != NULL)
1536 		fSelected->Submenu()->_show();
1537 
1538 }
1539 
1540 
1541 BMenuItem *
1542 BMenu::CurrentSelection() const
1543 {
1544 	return fSelected;
1545 }
1546 
1547 
1548 bool
1549 BMenu::SelectNextItem(BMenuItem *item, bool forward)
1550 {
1551 	return false;
1552 }
1553 
1554 
1555 BMenuItem *
1556 BMenu::NextItem(BMenuItem *item, bool forward) const
1557 {
1558 	return NULL;
1559 }
1560 
1561 
1562 bool
1563 BMenu::IsItemVisible(BMenuItem *item) const
1564 {
1565 	return false;
1566 }
1567 
1568 
1569 void
1570 BMenu::SetIgnoreHidden(bool on)
1571 {
1572 	fIgnoreHidden = on;
1573 }
1574 
1575 
1576 void
1577 BMenu::SetStickyMode(bool on)
1578 {
1579 	fStickyMode = on;
1580 }
1581 
1582 
1583 bool
1584 BMenu::IsStickyMode() const
1585 {
1586 	return fStickyMode;
1587 }
1588 
1589 
1590 void
1591 BMenu::CalcTriggers()
1592 {
1593 	BList triggersList;
1594 
1595 	// Gathers the existing triggers
1596 	// TODO: Oh great, reinterpret_cast.
1597 	for (int32 i = 0; i < CountItems(); i++) {
1598 		char trigger = ItemAt(i)->Trigger();
1599 		if (trigger != 0)
1600 			triggersList.AddItem(reinterpret_cast<void *>((uint32)trigger));
1601 	}
1602 
1603 	// Set triggers for items which don't have one yet
1604 	for (int32 i = 0; i < CountItems(); i++) {
1605 		BMenuItem *item = ItemAt(i);
1606 		if (item->Trigger() == 0) {
1607 			const char *newTrigger = ChooseTrigger(item->Label(), &triggersList);
1608 			if (newTrigger != NULL) {
1609 				item->SetSysTrigger(*newTrigger);
1610 				// TODO: This is crap. I'd prefer to have
1611 				// BMenuItem::SetSysTrigger(const char *) update fTriggerIndex.
1612 				// This isn't the case on beos, but it will probably be like that on haiku.
1613 				item->fTriggerIndex = newTrigger - item->Label();
1614 			}
1615 		}
1616 	}
1617 }
1618 
1619 
1620 const char *
1621 BMenu::ChooseTrigger(const char *title, BList *chars)
1622 {
1623 	ASSERT(chars != NULL);
1624 
1625 	if (title == NULL)
1626 		return NULL;
1627 
1628 	char *titlePtr = const_cast<char *>(title);
1629 
1630 	char trigger;
1631 	// TODO: Oh great, reinterpret_cast all around
1632 	while ((trigger = *titlePtr) != '\0') {
1633 		if (!chars->HasItem(reinterpret_cast<void *>((uint32)trigger)))	{
1634 			chars->AddItem(reinterpret_cast<void *>((uint32)trigger));
1635 			return titlePtr;
1636 		}
1637 
1638 		titlePtr++;
1639 	}
1640 
1641 	return NULL;
1642 }
1643 
1644 
1645 void
1646 BMenu::UpdateWindowViewSize(bool upWind)
1647 {
1648 	ASSERT(fCachedMenuWindow != NULL);
1649 	fCachedMenuWindow->ResizeTo(Bounds().Width() + 2, Bounds().Height() + 2);
1650 	fCachedMenuWindow->MoveTo(ScreenLocation());
1651 }
1652 
1653 
1654 bool
1655 BMenu::IsStickyPrefOn()
1656 {
1657 	return sMenuInfo.click_to_open;
1658 }
1659 
1660 
1661 void
1662 BMenu::RedrawAfterSticky(BRect bounds)
1663 {
1664 }
1665 
1666 
1667 bool
1668 BMenu::OkToProceed(BMenuItem *)
1669 {
1670 	return false;
1671 }
1672 
1673 
1674 status_t
1675 BMenu::ParseMsg(BMessage *msg, int32 *sindex, BMessage *spec,
1676 						 int32 *form, const char **prop, BMenu **tmenu,
1677 						 BMenuItem **titem, int32 *user_data,
1678 						 BMessage *reply) const
1679 {
1680 	return B_ERROR;
1681 }
1682 
1683 
1684 status_t
1685 BMenu::DoMenuMsg(BMenuItem **next, BMenu *tar, BMessage *m,
1686 						  BMessage *r, BMessage *spec, int32 f) const
1687 {
1688 	return B_ERROR;
1689 }
1690 
1691 
1692 status_t
1693 BMenu::DoMenuItemMsg(BMenuItem **next, BMenu *tar, BMessage *m,
1694 							  BMessage *r, BMessage *spec, int32 f) const
1695 {
1696 	return B_ERROR;
1697 }
1698 
1699 
1700 status_t
1701 BMenu::DoEnabledMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1702 							 BMessage *r) const
1703 {
1704 	return B_ERROR;
1705 }
1706 
1707 
1708 status_t
1709 BMenu::DoLabelMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1710 						   BMessage *r) const
1711 {
1712 	return B_ERROR;
1713 }
1714 
1715 
1716 status_t
1717 BMenu::DoMarkMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1718 						  BMessage *r) const
1719 {
1720 	return B_ERROR;
1721 }
1722 
1723 
1724 status_t
1725 BMenu::DoDeleteMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1726 							BMessage *r) const
1727 {
1728 	return B_ERROR;
1729 }
1730 
1731 
1732 status_t
1733 BMenu::DoCreateMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1734 							BMessage *r, bool menu) const
1735 {
1736 	return B_ERROR;
1737 }
1738 
1739 
1740 status_t
1741 set_menu_info(menu_info *info)
1742 {
1743 	if (!info)
1744 		return B_BAD_VALUE;
1745 
1746 	BPath path;
1747 
1748 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
1749 		return B_OK;
1750 
1751 	path.Append("menu_settings");
1752 
1753 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
1754 
1755 	if (file.InitCheck() != B_OK)
1756 		return B_OK;
1757 
1758 	file.Write(info, sizeof(menu_info));
1759 
1760 	BMenu::sMenuInfo = *info;
1761 
1762 	return B_OK;
1763 }
1764 
1765 
1766 status_t
1767 get_menu_info(menu_info *info)
1768 {
1769 	if (!info)
1770 		return B_BAD_VALUE;
1771 
1772 	*info = BMenu::sMenuInfo;
1773 
1774 	return B_OK;
1775 }
1776