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