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