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