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