1db937cbeSAxel Dörfler/*
2ac9ec47dSAxel Dörfler * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3db937cbeSAxel Dörfler * Distributed under the terms of the MIT License.
4db937cbeSAxel Dörfler *
5db937cbeSAxel Dörfler * Authors:
6d5c51ba6SJohn Scipione *		Stefano Ceccherini, stefano.ceccherini@gmail.com
7d5c51ba6SJohn Scipione *		Marc Flerackers, mflerackers@androme.be
8d5c51ba6SJohn Scipione *		Bill Hayden, haydentech@users.sourceforge.net
9db937cbeSAxel Dörfler *		Olivier Milla
10d5c51ba6SJohn Scipione *		John Scipione, jscipione@gmail.com
11db937cbeSAxel Dörfler */
12db937cbeSAxel Dörfler
1330fd5147Shaydentech
14083de48aSAxel Dörfler#include <ctype.h>
15083de48aSAxel Dörfler#include <stdlib.h>
16083de48aSAxel Dörfler#include <string.h>
17083de48aSAxel Dörfler
185f603da0SAdrien Destugues#include <algorithm>
195f603da0SAdrien Destugues
204e876d12SStefano Ceccherini#include <Bitmap.h>
217a8567e6SStefano Ceccherini#include <ControlLook.h>
2230fd5147Shaydentech#include <MenuItem.h>
23306e9208SStephan Aßmus#include <Shape.h>
2430fd5147Shaydentech#include <String.h>
2530fd5147Shaydentech#include <Window.h>
2630fd5147Shaydentech
2710f4d067SStefano Ceccherini#include <MenuPrivate.h>
2810f4d067SStefano Ceccherini
29083de48aSAxel Dörfler#include "utf8_functions.h"
30db937cbeSAxel Dörfler
315f71cfd4SJérôme Duval
325f603da0SAdrien Destuguesstatic const float kMarkTint = 0.75f;
335f603da0SAdrien Destugues
3404780702SFrançois Revol// map control key shortcuts to drawable Unicode characters
3504780702SFrançois Revol// cf. http://unicode.org/charts/PDF/U2190.pdf
3661dfdec4SJohn Scipioneconst char* kUTF8ControlMap[] = {
37bae33cdaSMichael Lotz	NULL,
3804780702SFrançois Revol	"\xe2\x86\xb8", /* B_HOME U+21B8 */
39bae33cdaSMichael Lotz	NULL, NULL,
4004780702SFrançois Revol	NULL, /* B_END */
4104780702SFrançois Revol	NULL, /* B_INSERT */
4204780702SFrançois Revol	NULL, NULL,
4304780702SFrançois Revol	NULL, /* B_BACKSPACE */
4404780702SFrançois Revol	"\xe2\x86\xb9", /* B_TAB U+21B9 */
4504780702SFrançois Revol	"\xe2\x86\xb5", /* B_ENTER, U+21B5 */
4604780702SFrançois Revol	//"\xe2\x8f\x8e", /* B_ENTER, U+23CE it's the official one */
4704780702SFrançois Revol	NULL, /* B_PAGE_UP */
4804780702SFrançois Revol	NULL, /* B_PAGE_DOWN */
4904780702SFrançois Revol	NULL, NULL, NULL,
5004780702SFrançois Revol	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
51bae33cdaSMichael Lotz	NULL, NULL, NULL, NULL,
5204780702SFrançois Revol	"\xe2\x86\x90", /* B_LEFT_ARROW */
5304780702SFrançois Revol	"\xe2\x86\x92", /* B_RIGHT_ARROW */
5404780702SFrançois Revol	"\xe2\x86\x91", /* B_UP_ARROW */
5504780702SFrançois Revol	"\xe2\x86\x93", /* B_DOWN_ARROW */
5604780702SFrançois Revol};
5704780702SFrançois Revol
5861dfdec4SJohn Scipione
5910f4d067SStefano Ceccheriniusing BPrivate::MenuPrivate;
60bd780d99SAxel Dörfler
61d97b4340SJohn ScipioneBMenuItem::BMenuItem(const char* label, BMessage* message, char shortcut,
62d97b4340SJohn Scipione	uint32 modifiers)
6330fd5147Shaydentech{
640f11085cSAxel Dörfler	_InitData();
650c14fc6dSStefano Ceccherini	if (label != NULL)
660c14fc6dSStefano Ceccherini		fLabel = strdup(label);
67bae33cdaSMichael Lotz
68b7fdea42SMarc Flerackers	SetMessage(message);
69b7fdea42SMarc Flerackers
70b7fdea42SMarc Flerackers	fShortcutChar = shortcut;
71b7fdea42SMarc Flerackers
72b7fdea42SMarc Flerackers	if (shortcut != 0)
73b7fdea42SMarc Flerackers		fModifiers = modifiers | B_COMMAND_KEY;
74b7fdea42SMarc Flerackers	else
75b7fdea42SMarc Flerackers		fModifiers = 0;
7630fd5147Shaydentech}
7736d4f059SStefano Ceccherini
7836d4f059SStefano Ceccherini
79d97b4340SJohn ScipioneBMenuItem::BMenuItem(BMenu* menu, BMessage* message)
80b7fdea42SMarc Flerackers{
810f11085cSAxel Dörfler	_InitData();
82b7fdea42SMarc Flerackers	SetMessage(message);
830f11085cSAxel Dörfler	_InitMenuData(menu);
8430fd5147Shaydentech}
8536d4f059SStefano Ceccherini
8636d4f059SStefano Ceccherini
87d97b4340SJohn ScipioneBMenuItem::BMenuItem(BMessage* data)
8830fd5147Shaydentech{
890f11085cSAxel Dörfler	_InitData();
90b7fdea42SMarc Flerackers
9136d4f059SStefano Ceccherini	if (data->HasString("_label")) {
9261dfdec4SJohn Scipione		const char* string;
93b7fdea42SMarc Flerackers
94b7fdea42SMarc Flerackers		data->FindString("_label", &string);
95b7fdea42SMarc Flerackers		SetLabel(string);
96b7fdea42SMarc Flerackers	}
97b7fdea42SMarc Flerackers
98b7fdea42SMarc Flerackers	bool disable;
99b7fdea42SMarc Flerackers	if (data->FindBool("_disable", &disable) == B_OK)
100b7fdea42SMarc Flerackers		SetEnabled(!disable);
101b7fdea42SMarc Flerackers
102b7fdea42SMarc Flerackers	bool marked;
103b7fdea42SMarc Flerackers	if (data->FindBool("_marked", &marked) == B_OK)
104b7fdea42SMarc Flerackers		SetMarked(marked);
105b7fdea42SMarc Flerackers
106083de48aSAxel Dörfler	int32 userTrigger;
107083de48aSAxel Dörfler	if (data->FindInt32("_user_trig", &userTrigger) == B_OK)
108083de48aSAxel Dörfler		SetTrigger(userTrigger);
109b7fdea42SMarc Flerackers
11036d4f059SStefano Ceccherini	if (data->HasInt32("_shortcut")) {
111b7fdea42SMarc Flerackers		int32 shortcut, mods;
112b7fdea42SMarc Flerackers
113b7fdea42SMarc Flerackers		data->FindInt32("_shortcut", &shortcut);
114b7fdea42SMarc Flerackers		data->FindInt32("_mods", &mods);
115b7fdea42SMarc Flerackers
116b7fdea42SMarc Flerackers		SetShortcut(shortcut, mods);
117b7fdea42SMarc Flerackers	}
118b7fdea42SMarc Flerackers
11936d4f059SStefano Ceccherini	if (data->HasMessage("_msg")) {
12061dfdec4SJohn Scipione		BMessage* message = new BMessage;
12161dfdec4SJohn Scipione		data->FindMessage("_msg", message);
12261dfdec4SJohn Scipione		SetMessage(message);
123b7fdea42SMarc Flerackers	}
124b7fdea42SMarc Flerackers
125b7fdea42SMarc Flerackers	BMessage subMessage;
12636d4f059SStefano Ceccherini	if (data->FindMessage("_submenu", &subMessage) == B_OK) {
127d97b4340SJohn Scipione		BArchivable* object = instantiate_object(&subMessage);
1284e876d12SStefano Ceccherini		if (object != NULL) {
12961dfdec4SJohn Scipione			BMenu* menu = dynamic_cast<BMenu*>(object);
1304e876d12SStefano Ceccherini			if (menu != NULL)
1310f11085cSAxel Dörfler				_InitMenuData(menu);
132b7fdea42SMarc Flerackers		}
133b7fdea42SMarc Flerackers	}
13430fd5147Shaydentech}
13536d4f059SStefano Ceccherini
13636d4f059SStefano Ceccherini
137d97b4340SJohn ScipioneBArchivable*
138d97b4340SJohn ScipioneBMenuItem::Instantiate(BMessage* data)
13930fd5147Shaydentech{
14030fd5147Shaydentech	if (validate_instantiation(data, "BMenuItem"))
14130fd5147Shaydentech		return new BMenuItem(data);
1420f11085cSAxel Dörfler
1430f11085cSAxel Dörfler	return NULL;
14430fd5147Shaydentech}
14536d4f059SStefano Ceccherini
14636d4f059SStefano Ceccherini
14736d4f059SStefano Ceccherinistatus_t
148d97b4340SJohn ScipioneBMenuItem::Archive(BMessage* data, bool deep) const
14930fd5147Shaydentech{
15061dfdec4SJohn Scipione	status_t status = BArchivable::Archive(data, deep);
1519fcdf0e9SJérôme Duval
15261dfdec4SJohn Scipione	if (status == B_OK && fLabel)
15361dfdec4SJohn Scipione		status = data->AddString("_label", Label());
15430fd5147Shaydentech
15561dfdec4SJohn Scipione	if (status == B_OK && !IsEnabled())
15661dfdec4SJohn Scipione		status = data->AddBool("_disable", true);
15730fd5147Shaydentech
15861dfdec4SJohn Scipione	if (status == B_OK && IsMarked())
15961dfdec4SJohn Scipione		status = data->AddBool("_marked", true);
16030fd5147Shaydentech
16161dfdec4SJohn Scipione	if (status == B_OK && fUserTrigger)
16261dfdec4SJohn Scipione		status = data->AddInt32("_user_trig", fUserTrigger);
16330fd5147Shaydentech
16461dfdec4SJohn Scipione	if (status == B_OK && fShortcutChar) {
16561dfdec4SJohn Scipione		status = data->AddInt32("_shortcut", fShortcutChar);
16661dfdec4SJohn Scipione		if (status == B_OK)
16761dfdec4SJohn Scipione			status = data->AddInt32("_mods", fModifiers);
16830fd5147Shaydentech	}
16930fd5147Shaydentech
17061dfdec4SJohn Scipione	if (status == B_OK && Message() != NULL)
17161dfdec4SJohn Scipione		status = data->AddMessage("_msg", Message());
17230fd5147Shaydentech
17361dfdec4SJohn Scipione	if (status == B_OK && deep && fSubmenu) {
17430fd5147Shaydentech		BMessage submenu;
175b7fdea42SMarc Flerackers		if (fSubmenu->Archive(&submenu, true) == B_OK)
17661dfdec4SJohn Scipione			status = data->AddMessage("_submenu", &submenu);
17730fd5147Shaydentech	}
17830fd5147Shaydentech
17961dfdec4SJohn Scipione	return status;
18030fd5147Shaydentech}
18136d4f059SStefano Ceccherini
18236d4f059SStefano Ceccherini
18330fd5147ShaydentechBMenuItem::~BMenuItem()
184bae33cdaSMichael Lotz{
185c3ac0a72SAugustin Cavalier	if (fSuper != NULL)
186c3ac0a72SAugustin Cavalier		fSuper->RemoveItem(this);
187c3ac0a72SAugustin Cavalier
18836d4f059SStefano Ceccherini	free(fLabel);
18936d4f059SStefano Ceccherini	delete fSubmenu;
19030fd5147Shaydentech}
19136d4f059SStefano Ceccherini
19236d4f059SStefano Ceccherini
19336d4f059SStefano Ceccherinivoid
194fa412b5fSJohn ScipioneBMenuItem::SetLabel(const char* string)
19530fd5147Shaydentech{
1964e876d12SStefano Ceccherini	if (fLabel != NULL) {
19730fd5147Shaydentech		free(fLabel);
19836d4f059SStefano Ceccherini		fLabel = NULL;
19936d4f059SStefano Ceccherini	}
200bae33cdaSMichael Lotz
2014e876d12SStefano Ceccherini	if (string != NULL)
202b7fdea42SMarc Flerackers		fLabel = strdup(string);
203b7fdea42SMarc Flerackers
2044e876d12SStefano Ceccherini	if (fSuper != NULL) {
205b7fdea42SMarc Flerackers		fSuper->InvalidateLayout();
20630fd5147Shaydentech
20736d4f059SStefano Ceccherini		if (fSuper->LockLooper()) {
208b7fdea42SMarc Flerackers			fSuper->Invalidate();
209b7fdea42SMarc Flerackers			fSuper->UnlockLooper();
210b7fdea42SMarc Flerackers		}
211b7fdea42SMarc Flerackers	}
21230fd5147Shaydentech}
21336d4f059SStefano Ceccherini
21436d4f059SStefano Ceccherini
21536d4f059SStefano Ceccherinivoid
216fa412b5fSJohn ScipioneBMenuItem::SetEnabled(bool enable)
21730fd5147Shaydentech{
218fa412b5fSJohn Scipione	if (fEnabled == enable)
2199e8d2dd2SStefano Ceccherini		return;
22030fd5147Shaydentech
221fa412b5fSJohn Scipione	fEnabled = enable;
2220f11085cSAxel Dörfler
2239e8d2dd2SStefano Ceccherini	if (fSubmenu != NULL)
224fa412b5fSJohn Scipione		fSubmenu->SetEnabled(enable);
2259e8d2dd2SStefano Ceccherini
226d97b4340SJohn Scipione	BMenu* menu = fSuper;
2274e876d12SStefano Ceccherini	if (menu != NULL && menu->LockLooper()) {
228b7fdea42SMarc Flerackers		menu->Invalidate(fBounds);
229b7fdea42SMarc Flerackers		menu->UnlockLooper();
230b7fdea42SMarc Flerackers	}
23130fd5147Shaydentech}
23236d4f059SStefano Ceccherini
23336d4f059SStefano Ceccherini
23436d4f059SStefano Ceccherinivoid
235fa412b5fSJohn ScipioneBMenuItem::SetMarked(bool mark)
23630fd5147Shaydentech{
237fa412b5fSJohn Scipione	fMark = mark;
23830fd5147Shaydentech
239fa412b5fSJohn Scipione	if (mark && fSuper != NULL) {
240d97b4340SJohn Scipione		MenuPrivate priv(fSuper);
24110f4d067SStefano Ceccherini		priv.ItemMarked(this);
24210f4d067SStefano Ceccherini	}
24330fd5147Shaydentech}
24436d4f059SStefano Ceccherini
24536d4f059SStefano Ceccherini
24636d4f059SStefano Ceccherinivoid
2470f11085cSAxel DörflerBMenuItem::SetTrigger(char trigger)
24830fd5147Shaydentech{
2490f11085cSAxel Dörfler	fUserTrigger = trigger;
250b7fdea42SMarc Flerackers
251083de48aSAxel Dörfler	// try uppercase letters first
252083de48aSAxel Dörfler
253083de48aSAxel Dörfler	const char* pos = strchr(Label(), toupper(trigger));
2541da48c41SMichael Lotz	trigger = tolower(trigger);
2551da48c41SMichael Lotz
256083de48aSAxel Dörfler	if (pos == NULL) {
257083de48aSAxel Dörfler		// take lowercase, too
258083de48aSAxel Dörfler		pos = strchr(Label(), trigger);
259083de48aSAxel Dörfler	}
2601da48c41SMichael Lotz
261cb2998afSAxel Dörfler	if (pos != NULL) {
262f1be4682SAxel Dörfler		fTriggerIndex = UTF8CountChars(Label(), pos - Label());
2631da48c41SMichael Lotz		fTrigger = trigger;
264cb2998afSAxel Dörfler	} else {
265083de48aSAxel Dörfler		fTrigger = 0;
266cb2998afSAxel Dörfler		fTriggerIndex = -1;
267cb2998afSAxel Dörfler	}
268b7fdea42SMarc Flerackers
2694e876d12SStefano Ceccherini	if (fSuper != NULL)
270b7fdea42SMarc Flerackers		fSuper->InvalidateLayout();
27130fd5147Shaydentech}
27236d4f059SStefano Ceccherini
27336d4f059SStefano Ceccherini
27436d4f059SStefano Ceccherinivoid
275fa412b5fSJohn ScipioneBMenuItem::SetShortcut(char shortcut, uint32 modifiers)
27630fd5147Shaydentech{
277fa412b5fSJohn Scipione	if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) != 0
278fa412b5fSJohn Scipione		&& fWindow != NULL) {
279b7fdea42SMarc Flerackers		fWindow->RemoveShortcut(fShortcutChar, fModifiers);
280fa412b5fSJohn Scipione	}
281b7fdea42SMarc Flerackers
282fa412b5fSJohn Scipione	fShortcutChar = shortcut;
283b7fdea42SMarc Flerackers
284fa412b5fSJohn Scipione	if (shortcut != 0)
285b7fdea42SMarc Flerackers		fModifiers = modifiers | B_COMMAND_KEY;
286b7fdea42SMarc Flerackers	else
287b7fdea42SMarc Flerackers		fModifiers = 0;
288b7fdea42SMarc Flerackers
289b7fdea42SMarc Flerackers	if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow)
290b7fdea42SMarc Flerackers		fWindow->AddShortcut(fShortcutChar, fModifiers, this);
291b7fdea42SMarc Flerackers
292bd4ea656SJohn Scipione	if (fSuper != NULL) {
293b7fdea42SMarc Flerackers		fSuper->InvalidateLayout();
294b7fdea42SMarc Flerackers
29536d4f059SStefano Ceccherini		if (fSuper->LockLooper()) {
296b7fdea42SMarc Flerackers			fSuper->Invalidate();
297b7fdea42SMarc Flerackers			fSuper->UnlockLooper();
298b7fdea42SMarc Flerackers		}
299b7fdea42SMarc Flerackers	}
30030fd5147Shaydentech}
30136d4f059SStefano Ceccherini
30236d4f059SStefano Ceccherini
303d97b4340SJohn Scipioneconst char*
30436d4f059SStefano CeccheriniBMenuItem::Label() const
30530fd5147Shaydentech{
30630fd5147Shaydentech	return fLabel;
30730fd5147Shaydentech}
30836d4f059SStefano Ceccherini
30936d4f059SStefano Ceccherini
31036d4f059SStefano Ceccherinibool
31136d4f059SStefano CeccheriniBMenuItem::IsEnabled() const
31230fd5147Shaydentech{
313b7fdea42SMarc Flerackers	if (fSubmenu)
314b7fdea42SMarc Flerackers		return fSubmenu->IsEnabled();
315b7fdea42SMarc Flerackers
316b7fdea42SMarc Flerackers	if (!fEnabled)
317b7fdea42SMarc Flerackers		return false;
318b7fdea42SMarc Flerackers
319d97b4340SJohn Scipione	return fSuper != NULL ? fSuper->IsEnabled() : true;
32030fd5147Shaydentech}
32136d4f059SStefano Ceccherini
32236d4f059SStefano Ceccherini
32336d4f059SStefano Ceccherinibool
32436d4f059SStefano CeccheriniBMenuItem::IsMarked() const
32530fd5147Shaydentech{
32630fd5147Shaydentech	return fMark;
32730fd5147Shaydentech}
32836d4f059SStefano Ceccherini
32936d4f059SStefano Ceccherini
33036d4f059SStefano Ceccherinichar
33136d4f059SStefano CeccheriniBMenuItem::Trigger() const
33230fd5147Shaydentech{
333b7fdea42SMarc Flerackers	return fUserTrigger;
33430fd5147Shaydentech}
33536d4f059SStefano Ceccherini
33636d4f059SStefano Ceccherini
33736d4f059SStefano Ceccherinichar
338d97b4340SJohn ScipioneBMenuItem::Shortcut(uint32* modifiers) const
33930fd5147Shaydentech{
34030fd5147Shaydentech	if (modifiers)
34130fd5147Shaydentech		*modifiers = fModifiers;
34230fd5147Shaydentech
34330fd5147Shaydentech	return fShortcutChar;
34430fd5147Shaydentech}
34536d4f059SStefano Ceccherini
34636d4f059SStefano Ceccherini
347d97b4340SJohn ScipioneBMenu*
34836d4f059SStefano CeccheriniBMenuItem::Submenu() const
34930fd5147Shaydentech{
35030fd5147Shaydentech	return fSubmenu;
35130fd5147Shaydentech}
35236d4f059SStefano Ceccherini
35336d4f059SStefano Ceccherini
354d97b4340SJohn ScipioneBMenu*
35536d4f059SStefano CeccheriniBMenuItem::Menu() const
35630fd5147Shaydentech{
35730fd5147Shaydentech	return fSuper;
35830fd5147Shaydentech}
35936d4f059SStefano Ceccherini
36036d4f059SStefano Ceccherini
36136d4f059SStefano CeccheriniBRect
36236d4f059SStefano CeccheriniBMenuItem::Frame() const
36330fd5147Shaydentech{
36430fd5147Shaydentech	return fBounds;
36530fd5147Shaydentech}
36636d4f059SStefano Ceccherini
36736d4f059SStefano Ceccherini
36836d4f059SStefano Ceccherinivoid
369379ede58SJohn ScipioneBMenuItem::GetContentSize(float* _width, float* _height)
37030fd5147Shaydentech{
37110f4d067SStefano Ceccherini	// TODO: Get rid of this. BMenu should handle this
37210f4d067SStefano Ceccherini	// automatically. Maybe it's not even needed, since our
37310f4d067SStefano Ceccherini	// BFont::Height() caches the value locally
37410f4d067SStefano Ceccherini	MenuPrivate(fSuper).CacheFontInfo();
375083de48aSAxel Dörfler
37643775be6SIthamar R. Adema	fCachedWidth = fSuper->StringWidth(fLabel);
377083de48aSAxel Dörfler
378379ede58SJohn Scipione	if (_width)
379379ede58SJohn Scipione		*_width = (float)ceil(fCachedWidth);
380379ede58SJohn Scipione	if (_height)
381379ede58SJohn Scipione		*_height = MenuPrivate(fSuper).FontHeight();
38230fd5147Shaydentech}
38336d4f059SStefano Ceccherini
38436d4f059SStefano Ceccherini
38536d4f059SStefano Ceccherinivoid
386ddd60371SJohn ScipioneBMenuItem::TruncateLabel(float maxWidth, char* newLabel)
38730fd5147Shaydentech{
388cab8c090SStefano Ceccherini	BFont font;
389bab4f5adSRyan Leavengood	fSuper->GetFont(&font);
390bab4f5adSRyan Leavengood
391cab8c090SStefano Ceccherini	BString string(fLabel);
392083de48aSAxel Dörfler
393cab8c090SStefano Ceccherini	font.TruncateString(&string, B_TRUNCATE_MIDDLE, maxWidth);
394083de48aSAxel Dörfler
395cab8c090SStefano Ceccherini	string.CopyInto(newLabel, 0, string.Length());
396cab8c090SStefano Ceccherini	newLabel[string.Length()] = '\0';
39730fd5147Shaydentech}
39836d4f059SStefano Ceccherini
39936d4f059SStefano Ceccherini
40036d4f059SStefano Ceccherinivoid
40136d4f059SStefano CeccheriniBMenuItem::DrawContent()
40230fd5147Shaydentech{
4032bcccf5aSStephan Aßmus	MenuPrivate menuPrivate(fSuper);
4042bcccf5aSStephan Aßmus	menuPrivate.CacheFontInfo();
405797d1a66SStefano Ceccherini
4062bcccf5aSStephan Aßmus	fSuper->MovePenBy(0, menuPrivate.Ascent());
4076c857bb9SStefano Ceccherini	BPoint lineStart = fSuper->PenLocation();
408cab8c090SStefano Ceccherini
40936759a6bSStephan Aßmus	fSuper->SetDrawingMode(B_OP_OVER);
410083de48aSAxel Dörfler
411c1a7e89fSJohn Scipione	float labelWidth;
412c1a7e89fSJohn Scipione	float labelHeight;
413c1a7e89fSJohn Scipione	GetContentSize(&labelWidth, &labelHeight);
414c1a7e89fSJohn Scipione
415c1a7e89fSJohn Scipione	const BRect& padding = menuPrivate.Padding();
416d926be8aSJohn Scipione	float maxContentWidth = fSuper->MaxContentWidth();
417d926be8aSJohn Scipione	float frameWidth = maxContentWidth > 0 ? maxContentWidth
418d926be8aSJohn Scipione		: fSuper->Frame().Width() - padding.left - padding.right;
419c1a7e89fSJohn Scipione
420a61d468cSJohn Scipione	if (roundf(frameWidth) >= roundf(labelWidth))
421c1a7e89fSJohn Scipione		fSuper->DrawString(fLabel);
422c1a7e89fSJohn Scipione	else {
423c1a7e89fSJohn Scipione		// truncate label to fit
424c1a7e89fSJohn Scipione		char* truncatedLabel = new char[strlen(fLabel) + 4];
425c1a7e89fSJohn Scipione		TruncateLabel(frameWidth, truncatedLabel);
426c1a7e89fSJohn Scipione		fSuper->DrawString(truncatedLabel);
427c1a7e89fSJohn Scipione		delete[] truncatedLabel;
428c1a7e89fSJohn Scipione	}
429083de48aSAxel Dörfler
4306c857bb9SStefano Ceccherini	if (fSuper->AreTriggersEnabled() && fTriggerIndex != -1) {
431cab8c090SStefano Ceccherini		float escapements[fTriggerIndex + 1];
4326c857bb9SStefano Ceccherini		BFont font;
4336c857bb9SStefano Ceccherini		fSuper->GetFont(&font);
434083de48aSAxel Dörfler
4356c857bb9SStefano Ceccherini		font.GetEscapements(fLabel, fTriggerIndex + 1, escapements);
436cab8c090SStefano Ceccherini
4376c857bb9SStefano Ceccherini		for (int32 i = 0; i < fTriggerIndex; i++)
4386c857bb9SStefano Ceccherini			lineStart.x += escapements[i] * font.Size();
439cab8c090SStefano Ceccherini
4406c857bb9SStefano Ceccherini		lineStart.x--;
4416c857bb9SStefano Ceccherini		lineStart.y++;
442083de48aSAxel Dörfler
4436c857bb9SStefano Ceccherini		BPoint lineEnd(lineStart);
4446c857bb9SStefano Ceccherini		lineEnd.x += escapements[fTriggerIndex] * font.Size();
445083de48aSAxel Dörfler
4466c857bb9SStefano Ceccherini		fSuper->StrokeLine(lineStart, lineEnd);
447bae33cdaSMichael Lotz	}
44830fd5147Shaydentech}
44936d4f059SStefano Ceccherini
45036d4f059SStefano Ceccherini
45136d4f059SStefano Ceccherinivoid
45236d4f059SStefano CeccheriniBMenuItem::Draw()
45330fd5147Shaydentech{
4547a96554cSlooncraz	const color_which lowColor = fSuper->LowUIColor();
4557a96554cSlooncraz	const color_which highColor = fSuper->HighUIColor();
4566a03786cSRyan Leavengood
4575f603da0SAdrien Destugues	fSuper->SetLowColor(_LowColor());
4585f603da0SAdrien Destugues	fSuper->SetHighColor(_HighColor());
459b7fdea42SMarc Flerackers
4605f603da0SAdrien Destugues	if (_IsActivated()) {
461db85cbe0SJohn Scipione		// fill in the background
4625f603da0SAdrien Destugues		BRect frame(Frame());
4635f603da0SAdrien Destugues		be_control_look->DrawMenuItemBackground(fSuper, frame, frame,
464db85cbe0SJohn Scipione			fSuper->LowColor(), BControlLook::B_ACTIVATED);
4652f86ba45SStephan Aßmus	}
466b7fdea42SMarc Flerackers
467306e9208SStephan Aßmus	// draw content
468b7fdea42SMarc Flerackers	fSuper->MovePenTo(ContentLocation());
469c38a7df2SStefano Ceccherini	DrawContent();
470306e9208SStephan Aßmus
471306e9208SStephan Aßmus	// draw extra symbols
47210f4d067SStefano Ceccherini	const menu_layout layout = MenuPrivate(fSuper).Layout();
47310f4d067SStefano Ceccherini	if (layout == B_ITEMS_IN_COLUMN) {
47430fd5147Shaydentech		if (IsMarked())
4756a03786cSRyan Leavengood			_DrawMarkSymbol();
47630fd5147Shaydentech
477b7fdea42SMarc Flerackers		if (fShortcutChar)
4780f11085cSAxel Dörfler			_DrawShortcutSymbol();
479b7fdea42SMarc Flerackers
48061dfdec4SJohn Scipione		if (Submenu() != NULL)
4816a03786cSRyan Leavengood			_DrawSubmenuSymbol();
48230fd5147Shaydentech	}
483bd57c145SStephan Aßmus
484db85cbe0SJohn Scipione	// restore the parent menu's low color and high color
4857a96554cSlooncraz	fSuper->SetLowUIColor(lowColor);
4867a96554cSlooncraz	fSuper->SetHighUIColor(highColor);
48730fd5147Shaydentech}
48836d4f059SStefano Ceccherini
48936d4f059SStefano Ceccherini
49036d4f059SStefano Ceccherinivoid
491fa412b5fSJohn ScipioneBMenuItem::Highlight(bool highlight)
49230fd5147Shaydentech{
493d97b4340SJohn Scipione	fSuper->Invalidate(Frame());
49430fd5147Shaydentech}
49536d4f059SStefano Ceccherini
49636d4f059SStefano Ceccherini
49736d4f059SStefano Ceccherinibool
49836d4f059SStefano CeccheriniBMenuItem::IsSelected() const
49930fd5147Shaydentech{
50030fd5147Shaydentech	return fSelected;
50130fd5147Shaydentech}
50236d4f059SStefano Ceccherini
50336d4f059SStefano Ceccherini
50436d4f059SStefano CeccheriniBPoint
50536d4f059SStefano CeccheriniBMenuItem::ContentLocation() const
506bae33cdaSMichael Lotz{
507d97b4340SJohn Scipione	const BRect& padding = MenuPrivate(fSuper).Padding();
508bae33cdaSMichael Lotz
509d97b4340SJohn Scipione	return BPoint(fBounds.left + padding.left, fBounds.top + padding.top);
51030fd5147Shaydentech}
51136d4f059SStefano Ceccherini
51236d4f059SStefano Ceccherini
51330fd5147Shaydentechvoid BMenuItem::_ReservedMenuItem1() {}
51430fd5147Shaydentechvoid BMenuItem::_ReservedMenuItem2() {}
51530fd5147Shaydentechvoid BMenuItem::_ReservedMenuItem3() {}
51630fd5147Shaydentechvoid BMenuItem::_ReservedMenuItem4() {}
51736d4f059SStefano Ceccherini
51836d4f059SStefano Ceccherini
51930fd5147ShaydentechBMenuItem::BMenuItem(const BMenuItem &)
52030fd5147Shaydentech{
52130fd5147Shaydentech}
52236d4f059SStefano Ceccherini
52336d4f059SStefano Ceccherini
524d97b4340SJohn ScipioneBMenuItem&
52536d4f059SStefano CeccheriniBMenuItem::operator=(const BMenuItem &)
52630fd5147Shaydentech{
52730fd5147Shaydentech	return *this;
52830fd5147Shaydentech}
52936d4f059SStefano Ceccherini
53036d4f059SStefano Ceccherini
53136d4f059SStefano Ceccherinivoid
5320f11085cSAxel DörflerBMenuItem::_InitData()
53330fd5147Shaydentech{
534b7fdea42SMarc Flerackers	fLabel = NULL;
5350c14fc6dSStefano Ceccherini	fSubmenu = NULL;
536b7fdea42SMarc Flerackers	fWindow = NULL;
537b7fdea42SMarc Flerackers	fSuper = NULL;
538b7fdea42SMarc Flerackers	fModifiers = 0;
539b7fdea42SMarc Flerackers	fCachedWidth = 0;
540b7fdea42SMarc Flerackers	fTriggerIndex = -1;
541b7fdea42SMarc Flerackers	fUserTrigger = 0;
542083de48aSAxel Dörfler	fTrigger = 0;
543b7fdea42SMarc Flerackers	fShortcutChar = 0;
544b7fdea42SMarc Flerackers	fMark = false;
545b7fdea42SMarc Flerackers	fEnabled = true;
546b7fdea42SMarc Flerackers	fSelected = false;
54730fd5147Shaydentech}
54836d4f059SStefano Ceccherini
54936d4f059SStefano Ceccherini
55036d4f059SStefano Ceccherinivoid
551d97b4340SJohn ScipioneBMenuItem::_InitMenuData(BMenu* menu)
55230fd5147Shaydentech{
553b7fdea42SMarc Flerackers	fSubmenu = menu;
554bae33cdaSMichael Lotz
55510f4d067SStefano Ceccherini	MenuPrivate(fSubmenu).SetSuperItem(this);
556b7fdea42SMarc Flerackers
557d97b4340SJohn Scipione	BMenuItem* item = menu->FindMarked();
558b7fdea42SMarc Flerackers
5596c857bb9SStefano Ceccherini	if (menu->IsRadioMode() && menu->IsLabelFromMarked() && item != NULL)
560b7fdea42SMarc Flerackers		SetLabel(item->Label());
561b7fdea42SMarc Flerackers	else
562b7fdea42SMarc Flerackers		SetLabel(menu->Name());
56330fd5147Shaydentech}
56436d4f059SStefano Ceccherini
56536d4f059SStefano Ceccherini
56636d4f059SStefano Ceccherinivoid
567d97b4340SJohn ScipioneBMenuItem::Install(BWindow* window)
56830fd5147Shaydentech{
569d97b4340SJohn Scipione	if (fSubmenu != NULL)
57010f4d067SStefano Ceccherini		MenuPrivate(fSubmenu).Install(window);
571bae33cdaSMichael Lotz
572b7fdea42SMarc Flerackers	fWindow = window;
57330fd5147Shaydentech
574b7fdea42SMarc Flerackers	if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow)
575b7fdea42SMarc Flerackers		window->AddShortcut(fShortcutChar, fModifiers, this);
576b7fdea42SMarc Flerackers
577b7fdea42SMarc Flerackers	if (!Messenger().IsValid())
57830fd5147Shaydentech		SetTarget(window);
57930fd5147Shaydentech}
58036d4f059SStefano Ceccherini
58136d4f059SStefano Ceccherini
58236d4f059SStefano Ceccherinistatus_t
583d97b4340SJohn ScipioneBMenuItem::Invoke(BMessage* message)
58430fd5147Shaydentech{
585b7fdea42SMarc Flerackers	if (!IsEnabled())
586b7fdea42SMarc Flerackers		return B_ERROR;
58730fd5147Shaydentech
588b7fdea42SMarc Flerackers	if (fSuper->IsRadioMode())
589b7fdea42SMarc Flerackers		SetMarked(true);
59030fd5147Shaydentech
591b7fdea42SMarc Flerackers	bool notify = false;
592b7fdea42SMarc Flerackers	uint32 kind = InvokeKind(&notify);
59330fd5147Shaydentech
594b7fdea42SMarc Flerackers	BMessage clone(kind);
595b7fdea42SMarc Flerackers	status_t err = B_BAD_VALUE;
596b7fdea42SMarc Flerackers
597d97b4340SJohn Scipione	if (message == NULL && !notify)
598b7fdea42SMarc Flerackers		message = Message();
599083de48aSAxel Dörfler
600d97b4340SJohn Scipione	if (message == NULL) {
601b7fdea42SMarc Flerackers		if (!fSuper->IsWatched())
602b7fdea42SMarc Flerackers			return err;
60336d4f059SStefano Ceccherini	} else
604b7fdea42SMarc Flerackers		clone = *message;
605b7fdea42SMarc Flerackers
606d97b4340SJohn Scipione	clone.AddInt32("index", fSuper->IndexOf(this));
607b7fdea42SMarc Flerackers	clone.AddInt64("when", (int64)system_time());
608b7fdea42SMarc Flerackers	clone.AddPointer("source", this);
609b7fdea42SMarc Flerackers	clone.AddMessenger("be:sender", BMessenger(fSuper));
610083de48aSAxel Dörfler
611d97b4340SJohn Scipione	if (message != NULL)
612b7fdea42SMarc Flerackers		err = BInvoker::Invoke(&clone);
613b7fdea42SMarc Flerackers
614b7fdea42SMarc Flerackers//	TODO: assynchronous messaging
615b7fdea42SMarc Flerackers//	SendNotices(kind, &clone);
616b7fdea42SMarc Flerackers
617b7fdea42SMarc Flerackers	return err;
61830fd5147Shaydentech}
61936d4f059SStefano Ceccherini
62036d4f059SStefano Ceccherini
62136d4f059SStefano Ceccherinivoid
62236d4f059SStefano CeccheriniBMenuItem::Uninstall()
62330fd5147Shaydentech{
624d97b4340SJohn Scipione	if (fSubmenu != NULL)
62510f4d067SStefano Ceccherini		MenuPrivate(fSubmenu).Uninstall();
626bae33cdaSMichael Lotz
627b7fdea42SMarc Flerackers	if (Target() == fWindow)
628b7fdea42SMarc Flerackers		SetTarget(BMessenger());
629b7fdea42SMarc Flerackers
630083de48aSAxel Dörfler	if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) != 0
631d97b4340SJohn Scipione		&& fWindow != NULL) {
632b7fdea42SMarc Flerackers		fWindow->RemoveShortcut(fShortcutChar, fModifiers);
633d97b4340SJohn Scipione	}
634b7fdea42SMarc Flerackers
635b7fdea42SMarc Flerackers	fWindow = NULL;
63630fd5147Shaydentech}
63736d4f059SStefano Ceccherini
63836d4f059SStefano Ceccherini
63936d4f059SStefano Ceccherinivoid
640d97b4340SJohn ScipioneBMenuItem::SetSuper(BMenu* super)
64130fd5147Shaydentech{
642d97b4340SJohn Scipione	if (fSuper != NULL && super != NULL) {
643d97b4340SJohn Scipione		debugger("Error - can't add menu or menu item to more than 1 container"
644d97b4340SJohn Scipione			" (either menu or menubar).");
645d97b4340SJohn Scipione	}
646b7fdea42SMarc Flerackers
647d97b4340SJohn Scipione	if (fSubmenu != NULL)
64810f4d067SStefano Ceccherini		MenuPrivate(fSubmenu).SetSuper(super);
649083de48aSAxel Dörfler
65037ddfd14SStefano Ceccherini	fSuper = super;
65130fd5147Shaydentech}
65236d4f059SStefano Ceccherini
65336d4f059SStefano Ceccherini
65436d4f059SStefano Ceccherinivoid
6552c11ec31SStefano CeccheriniBMenuItem::Select(bool selected)
65630fd5147Shaydentech{
6572c11ec31SStefano Ceccherini	if (fSelected == selected)
6582c11ec31SStefano Ceccherini		return;
6592c11ec31SStefano Ceccherini
660d97b4340SJohn Scipione	if (Submenu() != NULL || IsEnabled()) {
6612c11ec31SStefano Ceccherini		fSelected = selected;
6622c11ec31SStefano Ceccherini		Highlight(selected);
663bae33cdaSMichael Lotz	}
66430fd5147Shaydentech}
66536d4f059SStefano Ceccherini
66636d4f059SStefano Ceccherini
6675f603da0SAdrien Destuguesbool
6685f603da0SAdrien DestuguesBMenuItem::_IsActivated()
6695f603da0SAdrien Destugues{
6705f603da0SAdrien Destugues	return IsSelected() && (IsEnabled() || fSubmenu != NULL);
6715f603da0SAdrien Destugues}
6725f603da0SAdrien Destugues
6735f603da0SAdrien Destugues
6745f603da0SAdrien Destuguesrgb_color
6755f603da0SAdrien DestuguesBMenuItem::_LowColor()
6765f603da0SAdrien Destugues{
6775f603da0SAdrien Destugues	return _IsActivated() ? ui_color(B_MENU_SELECTED_BACKGROUND_COLOR)
6785f603da0SAdrien Destugues		: ui_color(B_MENU_BACKGROUND_COLOR);
6795f603da0SAdrien Destugues}
6805f603da0SAdrien Destugues
6815f603da0SAdrien Destugues
6825f603da0SAdrien Destuguesrgb_color
6835f603da0SAdrien DestuguesBMenuItem::_HighColor()
6845f603da0SAdrien Destugues{
6855f603da0SAdrien Destugues	rgb_color highColor;
6865f603da0SAdrien Destugues
6875f603da0SAdrien Destugues	bool isEnabled = IsEnabled();
6885f603da0SAdrien Destugues	bool isSelected = IsSelected();
6895f603da0SAdrien Destugues
6905f603da0SAdrien Destugues	if (isEnabled && isSelected)
6915f603da0SAdrien Destugues		highColor = ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR);
6925f603da0SAdrien Destugues	else if (isEnabled)
6935f603da0SAdrien Destugues		highColor = ui_color(B_MENU_ITEM_TEXT_COLOR);
6945f603da0SAdrien Destugues	else {
6955f603da0SAdrien Destugues		rgb_color bgColor = fSuper->LowColor();
6965f603da0SAdrien Destugues		if (bgColor.red + bgColor.green + bgColor.blue > 128 * 3)
6975f603da0SAdrien Destugues			highColor = tint_color(bgColor, B_DISABLED_LABEL_TINT);
6985f603da0SAdrien Destugues		else
6995f603da0SAdrien Destugues			highColor = tint_color(bgColor, B_LIGHTEN_2_TINT);
7005f603da0SAdrien Destugues	}
7015f603da0SAdrien Destugues
7025f603da0SAdrien Destugues	return highColor;
7035f603da0SAdrien Destugues}
7045f603da0SAdrien Destugues
7055f603da0SAdrien Destugues
70636d4f059SStefano Ceccherinivoid
7076a03786cSRyan LeavengoodBMenuItem::_DrawMarkSymbol()
70830fd5147Shaydentech{
709306e9208SStephan Aßmus	fSuper->PushState();
7107dc436d8SStephan Aßmus
711306e9208SStephan Aßmus	BRect r(fBounds);
7125b06c4aeSStephan Aßmus	float leftMargin;
71310f4d067SStefano Ceccherini	MenuPrivate(fSuper).GetItemMargins(&leftMargin, NULL, NULL, NULL);
714ac9ec47dSAxel Dörfler	float gap = leftMargin / 4;
715ac9ec47dSAxel Dörfler	r.right = r.left + leftMargin - gap;
716ac9ec47dSAxel Dörfler	r.left += gap / 3;
7175b06c4aeSStephan Aßmus
7185b06c4aeSStephan Aßmus	BPoint center(floorf((r.left + r.right) / 2.0),
7195b06c4aeSStephan Aßmus		floorf((r.top + r.bottom) / 2.0));
7205b06c4aeSStephan Aßmus
7215f603da0SAdrien Destugues	float size = std::min(r.Height() - 2, r.Width());
7225b06c4aeSStephan Aßmus	r.top = floorf(center.y - size / 2 + 0.5);
7235b06c4aeSStephan Aßmus	r.bottom = floorf(center.y + size / 2 + 0.5);
7245b06c4aeSStephan Aßmus	r.left = floorf(center.x - size / 2 + 0.5);
7255b06c4aeSStephan Aßmus	r.right = floorf(center.x + size / 2 + 0.5);
72630fd5147Shaydentech
727306e9208SStephan Aßmus	BShape arrowShape;
7285b06c4aeSStephan Aßmus	center.x += 0.5;
7295b06c4aeSStephan Aßmus	center.y += 0.5;
7305b06c4aeSStephan Aßmus	size *= 0.3;
7315b06c4aeSStephan Aßmus	arrowShape.MoveTo(BPoint(center.x - size, center.y - size * 0.25));
7325b06c4aeSStephan Aßmus	arrowShape.LineTo(BPoint(center.x - size * 0.25, center.y + size));
7335b06c4aeSStephan Aßmus	arrowShape.LineTo(BPoint(center.x + size, center.y - size));
7347dc436d8SStephan Aßmus
7355f603da0SAdrien Destugues	fSuper->SetHighColor(tint_color(_HighColor(), kMarkTint));
736306e9208SStephan Aßmus	fSuper->SetDrawingMode(B_OP_OVER);
737306e9208SStephan Aßmus	fSuper->SetPenSize(2.0);
738306e9208SStephan Aßmus	// NOTE: StrokeShape() offsets the shape by the current pen position,
739306e9208SStephan Aßmus	// it is not documented in the BeBook, but it is true!
740306e9208SStephan Aßmus	fSuper->MovePenTo(B_ORIGIN);
741306e9208SStephan Aßmus	fSuper->StrokeShape(&arrowShape);
742306e9208SStephan Aßmus
743306e9208SStephan Aßmus	fSuper->PopState();
74430fd5147Shaydentech}
74536d4f059SStefano Ceccherini
74636d4f059SStefano Ceccherini
74736d4f059SStefano Ceccherinivoid
7480f11085cSAxel DörflerBMenuItem::_DrawShortcutSymbol()
74930fd5147Shaydentech{
750d97b4340SJohn Scipione	BMenu* menu = fSuper;
751fbe2f227SStefano Ceccherini	BFont font;
75237ddfd14SStefano Ceccherini	menu->GetFont(&font);
75376b895fbSStefano Ceccherini	BPoint where = ContentLocation();
754bd57c145SStephan Aßmus	where.x = fBounds.right - font.Size();
755bae33cdaSMichael Lotz
7565f603da0SAdrien Destugues	if (fSubmenu != NULL)
7575f603da0SAdrien Destugues		where.x -= fBounds.Height() / 2;
7588152e712SStefano Ceccherini
75910f4d067SStefano Ceccherini	const float ascent = MenuPrivate(fSuper).Ascent();
7606eb09230SMichael Lotz	if (fShortcutChar < B_SPACE && kUTF8ControlMap[(int)fShortcutChar])
76104780702SFrançois Revol		_DrawControlChar(fShortcutChar, where + BPoint(0, ascent));
76204780702SFrançois Revol	else
76304780702SFrançois Revol		fSuper->DrawChar(fShortcutChar, where + BPoint(0, ascent));
764bd780d99SAxel Dörfler
765fbe2f227SStefano Ceccherini	where.y += (fBounds.Height() - 11) / 2 - 1;
766bae33cdaSMichael Lotz	where.x -= 4;
767bae33cdaSMichael Lotz
7686a03786cSRyan Leavengood	// TODO: It would be nice to draw these taking into account the text (low)
7696a03786cSRyan Leavengood	// color.
770bd4ea656SJohn Scipione	if ((fModifiers & B_COMMAND_KEY) != 0) {
771bd4ea656SJohn Scipione		const BBitmap* command = MenuPrivate::MenuItemCommand();
772c3d7cee2SStefano Ceccherini		const BRect &rect = command->Bounds();
7732c11ec31SStefano Ceccherini		where.x -= rect.Width() + 1;
774c3d7cee2SStefano Ceccherini		fSuper->DrawBitmap(command, where);
7752c11ec31SStefano Ceccherini	}
7762c11ec31SStefano Ceccherini
777bd4ea656SJohn Scipione	if ((fModifiers & B_CONTROL_KEY) != 0) {
778bd4ea656SJohn Scipione		const BBitmap* control = MenuPrivate::MenuItemControl();
779c3d7cee2SStefano Ceccherini		const BRect &rect = control->Bounds();
7802c11ec31SStefano Ceccherini		where.x -= rect.Width() + 1;
781c3d7cee2SStefano Ceccherini		fSuper->DrawBitmap(control, where);
7825f71cfd4SJérôme Duval	}
7832c11ec31SStefano Ceccherini
784bd4ea656SJohn Scipione	if ((fModifiers & B_OPTION_KEY) != 0) {
785bd4ea656SJohn Scipione		const BBitmap* option = MenuPrivate::MenuItemOption();
786c3d7cee2SStefano Ceccherini		const BRect &rect = option->Bounds();
787a48d16a0SStefano Ceccherini		where.x -= rect.Width() + 1;
788