xref: /haiku/src/kits/interface/Menu.cpp (revision db10640de90f7f9519ba2da9577b7c1af3c64f6b)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
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 //	Author:			Marc Flerackers (mflerackers@androme.be)
24 //	Description:	BMenu display a menu of selectable items.
25 //------------------------------------------------------------------------------
26 
27 // Standard Includes -----------------------------------------------------------
28 
29 // System Includes -------------------------------------------------------------
30 #include <Menu.h>
31 #include <MenuItem.h>
32 #include <Window.h>
33 #include <PropertyInfo.h>
34 #include <Errors.h>
35 #include <File.h>
36 #include <FindDirectory.h>
37 #include <Path.h>
38 
39 // Project Includes ------------------------------------------------------------
40 
41 // Local Includes --------------------------------------------------------------
42 
43 // Local Defines ---------------------------------------------------------------
44 
45 // Globals ---------------------------------------------------------------------
46 
47 menu_info BMenu::sMenuInfo;
48 
49 static property_info prop_list[] =
50 {
51 	{ "Enabled", { B_GET_PROPERTY, 0 },
52 		{ B_DIRECT_SPECIFIER, 0 }, "Returns true if menu or menu item is enabled; false "
53 		"otherwise." },
54 	{ "Enabled", { B_SET_PROPERTY, 0 },
55 		{ B_DIRECT_SPECIFIER, 0 }, "Enables or disables menu or menu item." },
56 	{ "Label", { B_GET_PROPERTY, 0 },
57 		{ B_DIRECT_SPECIFIER, 0 }, "Returns the string label of the menu or menu item." },
58 	{ "Label", { B_SET_PROPERTY, 0 },
59 		{ B_DIRECT_SPECIFIER, 0 }, "Sets the string label of the menu or menu item." },
60 	{ "Mark", { B_GET_PROPERTY, 0 },
61 		{ B_DIRECT_SPECIFIER, 0 }, "Returns true if the menu item or the menu's superitem "
62 		"is marked; false otherwise." },
63 	{ "Mark", { B_SET_PROPERTY, 0 },
64 		{ B_DIRECT_SPECIFIER, 0 }, "Marks or unmarks the menu item or the menu's superitem." },
65 	{ "Menu", { B_CREATE_PROPERTY, 0 },
66 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
67 		"Adds a new menu item at the specified index with the text label found in \"data\" "
68 		"and the int32 command found in \"what\" (used as the what field in the CMessage "
69 		"sent by the item)." },
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 	{ "Menu", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
74 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
75 		"Directs scripting message to the specified menu, first popping the current "
76 		"specifier off the stack." },
77 	{ "MenuItem", { B_COUNT_PROPERTIES, 0 },
78 		{ B_DIRECT_SPECIFIER, 0 }, "Counts the number of menu items in the specified menu." },
79 	{ "MenuItem", { B_CREATE_PROPERTY, 0 },
80 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
81 		"Adds a new menu item at the specified index with the text label found in \"data\" "
82 		"and the int32 command found in \"what\" (used as the what field in the CMessage "
83 		"sent by the item)." },
84 	{ "MenuItem", { B_DELETE_PROPERTY, 0 },
85 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
86 		"Removes the specified menu item from its parent menu." },
87 	{ "MenuItem", { B_EXECUTE_PROPERTY, 0 },
88 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
89 		"Invokes the specified menu item." },
90 	{ "MenuItem", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
91 		{ B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
92 		"Directs scripting message to the specified menu, first popping the current "
93 		"specifier off the stack." },
94 	{}
95 };
96 
97 //------------------------------------------------------------------------------
98 class BMenuWindow : public BWindow
99 {
100 public:
101 	BMenuWindow(BRect frame, BMenu *menu) :
102 		BWindow(frame, "Menu", B_NO_BORDER_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
103 			B_NOT_ZOOMABLE)
104 	{
105 		fMenu = menu;
106 		AddChild(fMenu);
107 		ResizeTo(fMenu->Bounds().Width() + 1, fMenu->Bounds().Height() + 1);
108 		fMenu->MakeFocus(true);
109 	}
110 
111 	virtual ~BMenuWindow() {}
112 
113 	virtual void WindowActivated(bool active)
114 	{
115 		if (!active)
116 		{
117 			RemoveChild(fMenu);
118 
119 			if (Lock())
120 				Quit();
121 		}
122 	}
123 
124 private:
125 	BMenu *fMenu;
126 };
127 
128 //------------------------------------------------------------------------------
129 BMenu::BMenu(const char *name, menu_layout layout)
130 	:	BView(BRect(), name, B_FOLLOW_LEFT | B_FOLLOW_TOP,
131 			B_WILL_DRAW | B_NAVIGABLE),
132 		fChosenItem(NULL),
133 		fPad(14.0f, 2.0f, 20.0f, 0.0f),
134 		fSelected(NULL),
135 		fCachedMenuWindow(NULL),
136 		fSuper(NULL),
137 		fSuperitem(NULL),
138 		fAscent(-1.0f),
139 		fDescent(-1.0f),
140 		fFontHeight(-1.0f),
141 		fState(0),
142 		fLayout(layout),
143 		fExtraRect(NULL),
144 		fMaxContentWidth(0.0f),
145 		fInitMatrixSize(NULL),
146 		fExtraMenuData(NULL),
147 		fTrigger(0),
148 		fResizeToFit(true),
149 		fUseCachedMenuLayout(true),
150 		fEnabled(true),
151 		fDynamicName(false),
152 		fRadioMode(false),
153 		fTrackNewBounds(false),
154 		fStickyMode(false),
155 		fIgnoreHidden(true),
156 		fTriggerEnabled(true),
157 		fRedrawAfterSticky(true),
158 		fAttachAborted(false)
159 {
160 	SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
161 	SetFontSize(10);
162 }
163 //------------------------------------------------------------------------------
164 BMenu::BMenu(const char *name, float width, float height)
165 	:	BView(BRect(0.0f, width, 0.0f, height), name,
166 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE),
167 		fChosenItem(NULL),
168 		fSelected(NULL),
169 		fCachedMenuWindow(NULL),
170 		fSuper(NULL),
171 		fSuperitem(NULL),
172 		fAscent(-1.0f),
173 		fDescent(-1.0f),
174 		fFontHeight(-1.0f),
175 		fState(0),
176 		fLayout(B_ITEMS_IN_MATRIX),
177 		fExtraRect(NULL),
178 		fMaxContentWidth(0.0f),
179 		fInitMatrixSize(NULL),
180 		fExtraMenuData(NULL),
181 		fTrigger(0),
182 		fResizeToFit(true),
183 		fUseCachedMenuLayout(true),
184 		fEnabled(true),
185 		fDynamicName(false),
186 		fRadioMode(false),
187 		fTrackNewBounds(false),
188 		fStickyMode(false),
189 		fIgnoreHidden(true),
190 		fTriggerEnabled(true),
191 		fRedrawAfterSticky(true),
192 		fAttachAborted(false)
193 {
194 	SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
195 	SetFontSize(10);
196 }
197 //------------------------------------------------------------------------------
198 BMenu::~BMenu()
199 {
200 	for (int i = 0; i < CountItems(); i++)
201 		delete ItemAt(i);
202 }
203 //------------------------------------------------------------------------------
204 BMenu::BMenu(BMessage *archive)
205 	:	BView(archive)
206 {
207 	SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
208 }
209 //------------------------------------------------------------------------------
210 BArchivable *BMenu::Instantiate(BMessage *data)
211 {
212 	if (validate_instantiation(data, "BMenu"))
213 		return new BMenu(data);
214 	else
215 		return NULL;
216 }
217 //------------------------------------------------------------------------------
218 status_t BMenu::Archive(BMessage *data, bool deep) const
219 {
220 	status_t err = BView::Archive(data, deep);
221 
222 	if (err != B_OK)
223 		return err;
224 
225 	if (Layout() != B_ITEMS_IN_ROW)
226 	{
227 		err = data->AddInt32("_layout", Layout());
228 
229 		if (err != B_OK)
230 			return err;
231 	}
232 
233 	err = data->AddBool("_rsize_to_fit", fResizeToFit);
234 
235 	if (err != B_OK)
236 		return err;
237 
238 	err = data->AddBool("_disable", !IsEnabled());
239 
240 	if (err != B_OK)
241 		return err;
242 
243 	err = data->AddBool("_radio", IsRadioMode());
244 
245 	if (err != B_OK)
246 		return err;
247 
248 	err = data->AddBool("_trig_disabled", AreTriggersEnabled());
249 
250 	if (err != B_OK)
251 		return err;
252 
253 	err = data->AddBool("_dyn_label", fDynamicName);
254 
255 	if (err != B_OK)
256 		return err;
257 
258 	err = data->AddFloat("_maxwidth", fMaxContentWidth);
259 
260 	if (err != B_OK)
261 		return err;
262 
263 	if (deep)
264 	{
265 		// TODO store items and rects
266 	}
267 
268 	return err;
269 }
270 //------------------------------------------------------------------------------
271 void BMenu::AttachedToWindow()
272 {
273 	BView::AttachedToWindow();
274 
275 	LayoutItems(0);
276 }
277 //------------------------------------------------------------------------------
278 void BMenu::DetachedFromWindow()
279 {
280 }
281 //------------------------------------------------------------------------------
282 bool BMenu::AddItem(BMenuItem *item)
283 {
284 	return AddItem(item, CountItems());
285 }
286 //------------------------------------------------------------------------------
287 bool BMenu::AddItem(BMenuItem *item, int32 index)
288 {
289 	item->fSuper = this;
290 
291 	bool err = fItems.AddItem(item, index);
292 
293 	if (!err)
294 		return err;
295 
296 	// Make sure we update the layout in case we are already attached.
297 	if (Window() && fResizeToFit)
298 		LayoutItems(index);
299 
300 	// Find the root menu window, so we can install this item.
301 	BMenu *root = this;
302 	while (root->Supermenu())
303 		root = root->Supermenu();
304 
305 	if (root->Window())
306 		Install(root->Window());
307 
308 	return err;
309 }
310 //------------------------------------------------------------------------------
311 bool BMenu::AddItem(BMenuItem *item, BRect frame)
312 {
313 	item->fBounds = frame;
314 
315 	return AddItem(item, CountItems());
316 }
317 //------------------------------------------------------------------------------
318 bool BMenu::AddItem(BMenu *submenu)
319 {
320 	BMenuItem *item = new BMenuItem(submenu);
321 	submenu->fSuper = this;
322 
323 	return AddItem(item);
324 }
325 //------------------------------------------------------------------------------
326 bool BMenu::AddItem(BMenu *submenu, int32 index)
327 {
328 	BMenuItem *item = new BMenuItem(submenu);
329 	submenu->fSuper = this;
330 
331 	return AddItem(item, index);
332 }
333 //------------------------------------------------------------------------------
334 bool BMenu::AddItem(BMenu *submenu, BRect frame)
335 {
336 	BMenuItem *item = new BMenuItem(submenu);
337 	submenu->fSuper = this;
338 	item->fBounds = frame;
339 
340 	return AddItem(item);
341 }
342 //------------------------------------------------------------------------------
343 bool BMenu::AddList(BList *list, int32 index)
344 {
345 	return false;
346 }
347 //------------------------------------------------------------------------------
348 bool BMenu::AddSeparatorItem()
349 {
350 	BMenuItem *item = new BSeparatorItem();
351 	item->fSuper = this;
352 
353 	return fItems.AddItem(item);
354 }
355 //------------------------------------------------------------------------------
356 bool BMenu::RemoveItem(BMenuItem *item)
357 {
358 	return fItems.RemoveItem(item);
359 }
360 //------------------------------------------------------------------------------
361 BMenuItem *BMenu::RemoveItem(int32 index)
362 {
363 	return (BMenuItem*)fItems.RemoveItem ( index );
364 }
365 //------------------------------------------------------------------------------
366 bool BMenu::RemoveItems(int32 index, int32 count, bool del)
367 {
368 	return false;
369 }
370 //------------------------------------------------------------------------------
371 bool BMenu::RemoveItem(BMenu *submenu)
372 {
373 	for (int i =0; i < fItems.CountItems(); i++)
374 		if (((BMenuItem*)fItems.ItemAt(i))->Submenu() == submenu)
375 			return (BMenuItem*)fItems.RemoveItem(fItems.ItemAt(i));
376 
377 	return false;
378 }
379 //------------------------------------------------------------------------------
380 int32 BMenu::CountItems() const
381 {
382 	return fItems.CountItems();
383 }
384 //------------------------------------------------------------------------------
385 BMenuItem *BMenu::ItemAt(int32 index) const
386 {
387 	return (BMenuItem*)fItems.ItemAt(index);
388 }
389 //------------------------------------------------------------------------------
390 BMenu *BMenu::SubmenuAt(int32 index) const
391 {
392 	return ((BMenuItem*)fItems.ItemAt(index))->Submenu();
393 }
394 //------------------------------------------------------------------------------
395 int32 BMenu::IndexOf(BMenuItem *item) const
396 {
397 	return fItems.IndexOf(item);
398 }
399 //------------------------------------------------------------------------------
400 int32 BMenu::IndexOf(BMenu *submenu) const
401 {
402 	for (int32 i =0; i < fItems.CountItems(); i++)
403 		if (((BMenuItem*)fItems.ItemAt(i))->Submenu() == submenu)
404 			return i;
405 
406 	return -1;
407 }
408 //------------------------------------------------------------------------------
409 BMenuItem *BMenu::FindItem(const char *label) const
410 {
411 	BMenuItem *item;
412 
413 	for (int32 i =0; i < CountItems(); i++)
414 	{
415 		item = ItemAt(i);
416 
417 		if (item->Label() && strcmp(item->Label(), label) == 0)
418 			return (item);
419 
420 		if (item->Submenu())
421 		{
422 			item = item->Submenu()->FindItem(label);
423 			if (item)
424 				return item;
425 		}
426 	}
427 
428 	return NULL;
429 }
430 //------------------------------------------------------------------------------
431 BMenuItem *BMenu::FindItem(uint32 command) const
432 {
433 	return NULL;
434 }
435 //------------------------------------------------------------------------------
436 status_t BMenu::SetTargetForItems(BHandler *handler)
437 {
438 	for (int32 i = 0; i < fItems.CountItems(); i++)
439 		if (((BMenuItem*)fItems.ItemAt(i))->SetTarget(handler) != B_OK)
440 			return B_ERROR;
441 
442 	return B_OK;
443 }
444 //------------------------------------------------------------------------------
445 status_t BMenu::SetTargetForItems(BMessenger messenger)
446 {
447 	for (int32 i = 0; i < fItems.CountItems (); i++)
448 		if (((BMenuItem*)fItems.ItemAt(i))->SetTarget(messenger) != B_OK)
449 			return B_ERROR;
450 
451 	return B_OK;
452 }
453 //------------------------------------------------------------------------------
454 void BMenu::SetEnabled(bool enabled)
455 {
456 	fEnabled = enabled;
457 
458 	for (int32 i = 0; i < CountItems(); i++)
459 		ItemAt(i)->SetEnabled(enabled);
460 }
461 //------------------------------------------------------------------------------
462 void BMenu::SetRadioMode(bool flag)
463 {
464 	fRadioMode = flag;
465 }
466 //------------------------------------------------------------------------------
467 void BMenu::SetTriggersEnabled(bool flag)
468 {
469 	fTriggerEnabled = flag;
470 }
471 //------------------------------------------------------------------------------
472 void BMenu::SetMaxContentWidth(float width)
473 {
474 	fMaxContentWidth = width;
475 }
476 //------------------------------------------------------------------------------
477 void BMenu::SetLabelFromMarked(bool flag)
478 {
479 }
480 //------------------------------------------------------------------------------
481 bool BMenu::IsLabelFromMarked()
482 {
483 	return false;
484 }
485 //------------------------------------------------------------------------------
486 bool BMenu::IsEnabled() const
487 {
488 	return fEnabled;
489 }
490 //------------------------------------------------------------------------------
491 bool BMenu::IsRadioMode() const
492 {
493 	return fRadioMode;
494 }
495 //------------------------------------------------------------------------------
496 bool BMenu::AreTriggersEnabled() const
497 {
498 	return fTriggerEnabled;
499 }
500 //------------------------------------------------------------------------------
501 bool BMenu::IsRedrawAfterSticky() const
502 {
503 	return fRedrawAfterSticky;
504 }
505 //------------------------------------------------------------------------------
506 float BMenu::MaxContentWidth() const
507 {
508 	return fMaxContentWidth;
509 }
510 //------------------------------------------------------------------------------
511 BMenuItem *BMenu::FindMarked()
512 {
513 	for (int i =0; i < fItems.CountItems(); i++)
514 		if (((BMenuItem*)fItems.ItemAt(i))->IsMarked())
515 			return (BMenuItem*)fItems.ItemAt(i);
516 
517 	return NULL;
518 }
519 //------------------------------------------------------------------------------
520 BMenu *BMenu::Supermenu() const
521 {
522 	return fSuper;
523 }
524 //------------------------------------------------------------------------------
525 BMenuItem *BMenu::Superitem() const
526 {
527 	return fSuperitem;
528 }
529 //------------------------------------------------------------------------------
530 void BMenu::MessageReceived(BMessage *msg)
531 {
532 	BView::MessageReceived(msg);
533 }
534 //------------------------------------------------------------------------------
535 void BMenu::KeyDown(const char *bytes, int32 numBytes)
536 {
537 	switch (bytes[0])
538 	{
539 		case B_UP_ARROW:
540 		{
541 			if (fSelected)
542 			{
543 				fSelected->fSelected = false;
544 
545 				if ( fSelected == fItems.FirstItem())
546 					fSelected = (BMenuItem*)fItems.LastItem();
547 				else
548 					fSelected = ItemAt(IndexOf(fSelected) - 1);
549 			}
550 			else
551 				fSelected = (BMenuItem*)fItems.LastItem();
552 
553 			fSelected->fSelected = true;
554 
555 			break;
556 		}
557 		case B_DOWN_ARROW:
558 		{
559 			if (fSelected)
560 			{
561 				fSelected->fSelected = false;
562 
563 				if (fSelected == fItems.LastItem())
564 					fSelected = (BMenuItem*)fItems.FirstItem();
565 				else
566 					fSelected = ItemAt(IndexOf(fSelected) + 1);
567 			}
568 			else
569 				fSelected = (BMenuItem*)fItems.FirstItem();
570 
571 			fSelected->fSelected = true;
572 
573 			break;
574 		}
575 		case B_HOME:
576 		{
577 			if (fSelected)
578 				fSelected->fSelected = false;
579 
580 			fSelected = (BMenuItem*)fItems.FirstItem();
581 			fSelected->fSelected = true;
582 
583 			break;
584 		}
585 		case B_END:
586 		{
587 			if (fSelected)
588 				fSelected->fSelected = false;
589 
590 			fSelected = (BMenuItem*)fItems.LastItem();
591 			fSelected->fSelected = true;
592 
593 			break;
594 		}
595 		case B_ENTER:
596 		case B_SPACE:
597 		{
598 			if (fSelected)
599 				InvokeItem(fSelected);
600 
601 			break;
602 		}
603 		default:
604 			BView::KeyDown(bytes, numBytes);
605 	}
606 }
607 //------------------------------------------------------------------------------
608 void BMenu::Draw(BRect updateRect)
609 {
610 	BRect bounds(Bounds());
611 
612 	SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_4_TINT));
613 	StrokeRect(bounds);
614 	SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT));
615 	StrokeLine(BPoint(bounds.left + 2, bounds.bottom - 1),
616 		BPoint(bounds.right - 1, bounds.bottom - 1));
617 	StrokeLine(BPoint(bounds.right - 1, bounds.top + 1));
618 	SetHighColor(tint_color ( ui_color ( B_MENU_BACKGROUND_COLOR ), B_LIGHTEN_2_TINT));
619 	StrokeLine(BPoint(bounds.right - 2, bounds.top + 1),
620 		BPoint(bounds.left + 1, bounds.top + 1));
621 	StrokeLine(BPoint(bounds.left + 1, bounds.bottom - 2));
622 
623 	DrawItems(updateRect);
624 }
625 //------------------------------------------------------------------------------
626 void BMenu::GetPreferredSize(float *width, float *height)
627 {
628 }
629 //------------------------------------------------------------------------------
630 void BMenu::ResizeToPreferred()
631 {
632 }
633 //------------------------------------------------------------------------------
634 void BMenu::FrameMoved(BPoint new_position)
635 {
636 }
637 //------------------------------------------------------------------------------
638 void BMenu::FrameResized(float new_width, float new_height)
639 {
640 }
641 //------------------------------------------------------------------------------
642 void BMenu::InvalidateLayout()
643 {
644 	CacheFontInfo();
645 	LayoutItems(0);
646 }
647 //------------------------------------------------------------------------------
648 BHandler *BMenu::ResolveSpecifier(BMessage *msg, int32 index,
649 								  BMessage *specifier, int32 form,
650 								  const char *property)
651 {
652 	BPropertyInfo propInfo(prop_list);
653 	BHandler *target = NULL;
654 
655 	switch (propInfo.FindMatch(msg, 0, specifier, form, property))
656 	{
657 		case B_ERROR:
658 			break;
659 
660 		case 0:
661 		case 1:
662 		case 2:
663 		case 3:
664 		case 4:
665 		case 5:
666 		case 6:
667 		case 7:
668 			target = this;
669 			break;
670 		case 8:
671 			// TODO: redirect to menu
672 			target = this;
673 			break;
674 		case 9:
675 		case 10:
676 		case 11:
677 		case 12:
678 			target = this;
679 			break;
680 		case 13:
681 			// TODO: redirect to menuitem
682 			target = this;
683 			break;
684 	}
685 
686 	if (!target)
687 		target = BView::ResolveSpecifier(msg, index, specifier, form,
688 		property);
689 
690 	return target;
691 }
692 //------------------------------------------------------------------------------
693 status_t BMenu::GetSupportedSuites(BMessage *data)
694 {
695 	status_t err;
696 
697 	if (data == NULL)
698 		return B_BAD_VALUE;
699 
700 	err = data->AddString("suites", "suite/vnd.Be-menu");
701 
702 	if (err != B_OK)
703 		return err;
704 
705 	BPropertyInfo prop_info(prop_list);
706 	err = data->AddFlat("messages", &prop_info);
707 
708 	if (err != B_OK)
709 		return err;
710 
711 	return BView::GetSupportedSuites(data);
712 }
713 //------------------------------------------------------------------------------
714 status_t BMenu::Perform(perform_code d, void *arg)
715 {
716 	return B_ERROR;
717 }
718 //------------------------------------------------------------------------------
719 void BMenu::MakeFocus(bool focused)
720 {
721 	if (focused)
722 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
723 
724 	BView::MakeFocus(focused);
725 }
726 //------------------------------------------------------------------------------
727 void BMenu::AllAttached()
728 {
729 }
730 //------------------------------------------------------------------------------
731 void BMenu::AllDetached()
732 {
733 }
734 //------------------------------------------------------------------------------
735 BMenu::BMenu(BRect frame, const char *name, uint32 resizingMode, uint32 flags,
736 			 menu_layout layout, bool resizeToFit)
737 	:	BView(frame, name, resizingMode, flags),
738 		fChosenItem(NULL),
739 		fSelected(NULL),
740 		fCachedMenuWindow(NULL),
741 		fSuper(NULL),
742 		fSuperitem(NULL),
743 		fAscent(-1.0f),
744 		fDescent(-1.0f),
745 		fFontHeight(-1.0f),
746 		fState(0),
747 		fLayout(layout),
748 		fExtraRect(NULL),
749 		fMaxContentWidth(0.0f),
750 		fInitMatrixSize(NULL),
751 		fExtraMenuData(NULL),
752 		fTrigger(0),
753 		fResizeToFit(resizeToFit),
754 		fUseCachedMenuLayout(true),
755 		fEnabled(true),
756 		fDynamicName(false),
757 		fRadioMode(false),
758 		fTrackNewBounds(false),
759 		fStickyMode(false),
760 		fIgnoreHidden(true),
761 		fTriggerEnabled(true),
762 		fRedrawAfterSticky(true),
763 		fAttachAborted(false)
764 {
765 	SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
766 	SetFontSize(10);
767 }
768 //------------------------------------------------------------------------------
769 BPoint BMenu::ScreenLocation()
770 {
771 	BMenu *superMenu = Supermenu();
772 
773 	if (superMenu == NULL)
774 		return BPoint();
775 
776 	BMenuItem *superItem = Superitem();
777 
778 	if (superItem == NULL)
779 		return BPoint();
780 
781 	BPoint point;
782 
783 	if (superMenu->Layout() == B_ITEMS_IN_COLUMN)
784 		point = superItem->Frame().RightTop();
785 	else
786 		point = superItem->Frame().LeftBottom() + BPoint(0.0f, 1.0f);
787 
788 	superMenu->ConvertToScreen(&point);
789 
790 	return point;
791 }
792 //------------------------------------------------------------------------------
793 void BMenu::SetItemMargins(float left, float top, float right, float bottom)
794 {
795 	fPad.Set(left, top, right, bottom);
796 }
797 //------------------------------------------------------------------------------
798 void BMenu::GetItemMargins(float *left, float *top, float *right,
799 						   float *bottom) const
800 {
801 	*left = fPad.left;
802 	*top = fPad.top;
803 	*right = fPad.right;
804 	*bottom = fPad.bottom;
805 }
806 //------------------------------------------------------------------------------
807 menu_layout BMenu::Layout() const
808 {
809 	return fLayout;
810 }
811 //------------------------------------------------------------------------------
812 void BMenu::Show()
813 {
814 	Show(false);
815 }
816 //------------------------------------------------------------------------------
817 void BMenu::Show(bool selectFirst)
818 {
819 	_show(selectFirst);
820 }
821 //------------------------------------------------------------------------------
822 void BMenu::Hide()
823 {
824 	_hide();
825 }
826 //------------------------------------------------------------------------------
827 BMenuItem *BMenu::Track(bool openAnyway, BRect *clickToOpenRect)
828 {
829 	return NULL;
830 }
831 //------------------------------------------------------------------------------
832 bool BMenu::AddDynamicItem(add_state s)
833 {
834 	return false;
835 }
836 //------------------------------------------------------------------------------
837 void BMenu::DrawBackground(BRect update)
838 {
839 }
840 //------------------------------------------------------------------------------
841 void BMenu::SetTrackingHook(menu_tracking_hook func, void *state)
842 {
843 }
844 //------------------------------------------------------------------------------
845 void BMenu::_ReservedMenu3() {}
846 void BMenu::_ReservedMenu4() {}
847 void BMenu::_ReservedMenu5() {}
848 void BMenu::_ReservedMenu6() {}
849 //------------------------------------------------------------------------------
850 BMenu &BMenu::operator=(const BMenu &)
851 {
852 	return *this;
853 }
854 //------------------------------------------------------------------------------
855 void BMenu::InitData(BMessage *data)
856 {
857 }
858 //------------------------------------------------------------------------------
859 bool BMenu::_show(bool selectFirstItem)
860 {
861 	BPoint point = ScreenLocation();
862 
863 	BWindow *window = new BMenuWindow(BRect(point.x, point.y,
864 		point.x + 20, point.y + 200), this);
865 
866 	window->Show();
867 
868 	return true;
869 }
870 //------------------------------------------------------------------------------
871 void BMenu::_hide()
872 {
873 	BWindow *window = Window();
874 
875 	if (window)
876 	{
877 		window->RemoveChild(this);
878 
879 		window->Lock();
880 		window->Quit();
881 	}
882 }
883 //------------------------------------------------------------------------------
884 BMenuItem *BMenu::_track(int *action, long start)
885 {
886 	return NULL;
887 }
888 //------------------------------------------------------------------------------
889 bool BMenu::_AddItem(BMenuItem *item, int32 index)
890 {
891 	return false;
892 }
893 //------------------------------------------------------------------------------
894 bool BMenu::RemoveItems(int32 index, int32 count, BMenuItem *item, bool del)
895 {
896 	return false;
897 }
898 //------------------------------------------------------------------------------
899 void BMenu::LayoutItems(int32 index)
900 {
901 	CalcTriggers();
902 
903 	float width, height;
904 
905 	ComputeLayout(index, true, true, &width, &height);
906 
907 	ResizeTo(width, height);
908 }
909 //------------------------------------------------------------------------------
910 void BMenu::ComputeLayout(int32 index, bool bestFit, bool moveItems,
911 						  float* width, float* height)
912 {
913 	BRect frame;
914 	float iWidth, iHeight;
915 	BMenuItem *item;
916 
917 	if (fLayout == B_ITEMS_IN_COLUMN)
918 	{
919 		frame = BRect(0.0f, 0.0f, 0.0f, 2.0f);
920 
921 		for (int i = 0; i < fItems.CountItems(); i++)
922 		{
923 			item = (BMenuItem*)fItems.ItemAt(i);
924 
925 			item->GetContentSize(&iWidth, &iHeight);
926 
927 			if (item->fModifiers && item->fShortcutChar)
928 				iWidth += 25.0f;
929 
930 			item->fBounds.left = 2.0f;
931 			item->fBounds.top = frame.bottom;
932 			item->fBounds.bottom = item->fBounds.top + iHeight + fPad.top + fPad.bottom;
933 
934 			frame.right = max_c(frame.right, iWidth + fPad.left + fPad.right);
935 			frame.bottom = item->fBounds.bottom + 1.0f;
936 		}
937 
938 		for (int i = 0; i < fItems.CountItems(); i++)
939 			ItemAt(i)->fBounds.right = frame.right;
940 
941 		frame.right = (float)ceil(frame.right) + 2.0f;
942 		frame.bottom += 1.0f;
943 	}
944 	else if (fLayout == B_ITEMS_IN_ROW)
945 	{
946 		font_height fh;
947 		GetFontHeight(&fh);
948 		frame = BRect(0.0f, 0.0f, 0.0f,
949 			(float)ceil(fh.ascent) + (float)ceil(fh.descent) + fPad.top + fPad.bottom);
950 
951 		for (int i = 0; i < fItems.CountItems(); i++)
952 		{
953 			item = (BMenuItem*)fItems.ItemAt(i);
954 
955 			item->GetContentSize(&iWidth, &iHeight);
956 
957 			item->fBounds.left = frame.right;
958 			item->fBounds.top = 0.0f;
959 			item->fBounds.right = item->fBounds.left + iWidth + fPad.left + fPad.right;
960 
961 			frame.right = item->fBounds.right + 1.0f;
962 			frame.bottom = max_c(frame.bottom, iHeight + fPad.top + fPad.bottom);
963 		}
964 
965 		for (int i = 0; i < fItems.CountItems(); i++)
966 			ItemAt(i)->fBounds.bottom = frame.bottom;
967 
968 		frame.right = (float)ceil(frame.right) + 8.0f;
969 	}
970 
971 	if ((ResizingMode() & B_FOLLOW_LEFT_RIGHT) ==  B_FOLLOW_LEFT_RIGHT)
972 	{
973 		if (Parent())
974 			*width = Parent()->Frame().Width() + 1.0f;
975 		else
976 			*width = Window()->Frame().Width() + 1.0f;
977 
978 		*height = frame.Height();
979 	}
980 	else
981 	{
982 		*width = frame.Width();
983 		*height = frame.Height();
984 	}
985 }
986 //------------------------------------------------------------------------------
987 BRect BMenu::Bump(BRect current, BPoint extent, int32 index) const
988 {
989 	return BRect();
990 }
991 //------------------------------------------------------------------------------
992 BPoint BMenu::ItemLocInRect(BRect frame) const
993 {
994 	return BPoint();
995 }
996 //------------------------------------------------------------------------------
997 BRect BMenu::CalcFrame(BPoint where, bool *scrollOn)
998 {
999 	return BRect();
1000 }
1001 //------------------------------------------------------------------------------
1002 bool BMenu::ScrollMenu(BRect bounds, BPoint loc, bool *fast)
1003 {
1004 	return false;
1005 }
1006 //------------------------------------------------------------------------------
1007 void BMenu::ScrollIntoView(BMenuItem *item)
1008 {
1009 }
1010 //------------------------------------------------------------------------------
1011 void BMenu::DrawItems(BRect updateRect)
1012 {
1013 	for (int i =0; i < fItems.CountItems(); i++)
1014 	{
1015 		if (ItemAt(i)->Frame().Intersects(updateRect))
1016 			ItemAt(i)->Draw();
1017 	}
1018 }
1019 //------------------------------------------------------------------------------
1020 int BMenu::State(BMenuItem **item) const
1021 {
1022 	return 0;
1023 }
1024 //------------------------------------------------------------------------------
1025 void BMenu::InvokeItem(BMenuItem *item, bool now)
1026 {
1027 	if (item->Submenu())
1028 		item->Submenu()->Show();
1029 	else if (IsRadioMode())
1030 		item->SetMarked(true);
1031 
1032 	item->Invoke();
1033 }
1034 //------------------------------------------------------------------------------
1035 bool BMenu::OverSuper(BPoint loc)
1036 {
1037 	// TODO: we assume that loc is in screen coords
1038 	if (!Supermenu())
1039 		return false;
1040 
1041 	return Supermenu()->Window()->Frame().Contains(loc);
1042 }
1043 //------------------------------------------------------------------------------
1044 bool BMenu::OverSubmenu(BMenuItem *item, BPoint loc)
1045 {
1046 	// TODO: we assume that loc is in screen coords
1047 	if (!item->Submenu())
1048 		return false;
1049 
1050 	return item->Submenu()->Window()->Frame().Contains(loc);
1051 }
1052 //------------------------------------------------------------------------------
1053 BMenuWindow	*BMenu::MenuWindow()
1054 {
1055 	return fCachedMenuWindow;
1056 }
1057 //------------------------------------------------------------------------------
1058 void BMenu::DeleteMenuWindow()
1059 {
1060 	delete fCachedMenuWindow;
1061 	fCachedMenuWindow = NULL;
1062 }
1063 //------------------------------------------------------------------------------
1064 BMenuItem *BMenu::HitTestItems(BPoint where, BPoint slop) const
1065 {
1066 	for (int i =0; i < CountItems(); i++)
1067 		if (ItemAt(i)->fBounds.Contains(where))
1068 			return ItemAt(i);
1069 
1070 	return NULL;
1071 }
1072 //------------------------------------------------------------------------------
1073 BRect BMenu::Superbounds() const
1074 {
1075 	return BRect();
1076 }
1077 //------------------------------------------------------------------------------
1078 void BMenu::CacheFontInfo()
1079 {
1080 	font_height fh;
1081 	GetFontHeight(&fh);
1082 	fAscent = fh.ascent;
1083 	fDescent = fh.descent;
1084 	fFontHeight = (float)ceil(fh.ascent + fh.descent + 0.5f);
1085 }
1086 //------------------------------------------------------------------------------
1087 void BMenu::ItemMarked(BMenuItem *item)
1088 {
1089 	if (IsRadioMode())
1090 	{
1091 		for (int32 i = 0; i < CountItems(); i++)
1092 			if (ItemAt(i) != item)
1093 				ItemAt(i)->SetMarked(false);
1094 	}
1095 
1096 	if (IsLabelFromMarked() && Superitem())
1097 		Superitem()->SetLabel(item->Label());
1098 }
1099 //------------------------------------------------------------------------------
1100 void BMenu::Install(BWindow *target)
1101 {
1102 	for (int i =0; i < CountItems(); i++)
1103 		ItemAt(i)->Install(target);
1104 }
1105 //------------------------------------------------------------------------------
1106 void BMenu::Uninstall()
1107 {
1108 }
1109 //------------------------------------------------------------------------------
1110 void BMenu::SelectItem(BMenuItem *m, uint32 showSubmenu,bool selectFirstItem)
1111 {
1112 }
1113 //------------------------------------------------------------------------------
1114 BMenuItem *BMenu::CurrentSelection() const
1115 {
1116 	return fSelected;
1117 }
1118 //------------------------------------------------------------------------------
1119 bool BMenu::SelectNextItem(BMenuItem *item, bool forward)
1120 {
1121 	return false;
1122 }
1123 //------------------------------------------------------------------------------
1124 BMenuItem *BMenu::NextItem(BMenuItem *item, bool forward) const
1125 {
1126 	return NULL;
1127 }
1128 //------------------------------------------------------------------------------
1129 bool BMenu::IsItemVisible(BMenuItem *item) const
1130 {
1131 	return false;
1132 }
1133 //------------------------------------------------------------------------------
1134 void BMenu::SetIgnoreHidden(bool on)
1135 {
1136 	fIgnoreHidden = on;
1137 }
1138 //------------------------------------------------------------------------------
1139 void BMenu::SetStickyMode(bool on)
1140 {
1141 	fStickyMode = on;
1142 }
1143 //------------------------------------------------------------------------------
1144 bool BMenu::IsStickyMode() const
1145 {
1146 	return fStickyMode;
1147 }
1148 //------------------------------------------------------------------------------
1149 void BMenu::CalcTriggers()
1150 {
1151 }
1152 //------------------------------------------------------------------------------
1153 const char *BMenu::ChooseTrigger(const char *title, BList *chars)
1154 {
1155 	return NULL;
1156 }
1157 //------------------------------------------------------------------------------
1158 void BMenu::UpdateWindowViewSize(bool upWind)
1159 {
1160 }
1161 //------------------------------------------------------------------------------
1162 bool BMenu::IsStickyPrefOn()
1163 {
1164 	return false;
1165 }
1166 //------------------------------------------------------------------------------
1167 void BMenu::RedrawAfterSticky(BRect bounds)
1168 {
1169 }
1170 //------------------------------------------------------------------------------
1171 bool BMenu::OkToProceed(BMenuItem *)
1172 {
1173 	return false;
1174 }
1175 //------------------------------------------------------------------------------
1176 status_t BMenu::ParseMsg(BMessage *msg, int32 *sindex, BMessage *spec,
1177 						 int32 *form, const char **prop, BMenu **tmenu,
1178 						 BMenuItem **titem, int32 *user_data,
1179 						 BMessage *reply) const
1180 {
1181 	return B_ERROR;
1182 }
1183 //------------------------------------------------------------------------------
1184 status_t BMenu::DoMenuMsg(BMenuItem **next, BMenu *tar, BMessage *m,
1185 						  BMessage *r, BMessage *spec, int32 f) const
1186 {
1187 	return B_ERROR;
1188 }
1189 //------------------------------------------------------------------------------
1190 status_t BMenu::DoMenuItemMsg(BMenuItem **next, BMenu *tar, BMessage *m,
1191 							  BMessage *r, BMessage *spec, int32 f) const
1192 {
1193 	return B_ERROR;
1194 }
1195 //------------------------------------------------------------------------------
1196 status_t BMenu::DoEnabledMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1197 							 BMessage *r) const
1198 {
1199 	return B_ERROR;
1200 }
1201 //------------------------------------------------------------------------------
1202 status_t BMenu::DoLabelMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1203 						   BMessage *r) const
1204 {
1205 	return B_ERROR;
1206 }
1207 //------------------------------------------------------------------------------
1208 status_t BMenu::DoMarkMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1209 						  BMessage *r) const
1210 {
1211 	return B_ERROR;
1212 }
1213 //------------------------------------------------------------------------------
1214 status_t BMenu::DoDeleteMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1215 							BMessage *r) const
1216 {
1217 	return B_ERROR;
1218 }
1219 //------------------------------------------------------------------------------
1220 status_t BMenu::DoCreateMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
1221 							BMessage *r, bool menu) const
1222 {
1223 	return B_ERROR;
1224 }
1225 //------------------------------------------------------------------------------
1226 
1227 
1228 
1229 // Temporary mouse hooks
1230 #if 0
1231 //------------------------------------------------------------------------------
1232 void BMenu::MouseDown(BPoint point)
1233 {
1234 	BMenuItem *item = HitTestItems(point);
1235 
1236 	if (item)
1237 		InvokeItem(item);
1238 
1239 	Hide();
1240 }
1241 //------------------------------------------------------------------------------
1242 void BMenu::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
1243 {
1244 	BMenuItem *item = HitTestItems(point);
1245 
1246 	if (fSelected)
1247 	{
1248 		if (fSelected == item)
1249 			return;
1250 
1251 		fSelected->fSelected = false;
1252 		//if (fSelected->Submenu())
1253 		//	fSelected->Submenu()->Hide();
1254 		Invalidate(fSelected->Frame());
1255 	}
1256 
1257 	fSelected = item;
1258 
1259 	if (fSelected)
1260 	{
1261 		fSelected->fSelected = true;
1262 		//if (fSelected->Submenu())
1263 		//	fSelected->Submenu()->Show();
1264 		Invalidate(fSelected->Frame());
1265 	}
1266 }
1267 //------------------------------------------------------------------------------
1268 void BMenu::MouseUp(BPoint point)
1269 {
1270 
1271 }
1272 #endif
1273 //------------------------------------------------------------------------------
1274 status_t set_menu_info(menu_info *info)
1275 {
1276 	BPath path;
1277 
1278 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
1279 		return B_OK;
1280 
1281 	path.Append("menu_settings");
1282 
1283 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
1284 
1285 	if (file.InitCheck() != B_OK)
1286 		return B_OK;
1287 
1288 	file.Write(info, sizeof(menu_info));
1289 
1290 	return B_OK;
1291 }
1292 //------------------------------------------------------------------------------
1293 status_t get_menu_info(menu_info *info)
1294 {
1295 	info->font_size = 8;
1296 	memcpy(info->f_family, "Arial", 6);
1297 	memcpy(info->f_style, "Regular", 8);
1298 	info->background_color = ui_color(B_MENU_BACKGROUND_COLOR);
1299 	info->separator = 0;
1300 	info->click_to_open = true;
1301 	info->triggers_always_shown = false;
1302 
1303 	BPath path;
1304 
1305 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
1306 		return B_OK;
1307 
1308 	path.Append("menu_settings");
1309 
1310 	BFile file(path.Path(), B_READ_ONLY);
1311 
1312 	if (file.InitCheck() != B_OK)
1313 		return B_OK;
1314 
1315 	file.Read(info, sizeof(menu_info));
1316 
1317 	return B_OK;
1318 }
1319 //------------------------------------------------------------------------------
1320