xref: /haiku/src/kits/interface/MenuItem.cpp (revision 24159a0c7d6d6dcba9f2a0c1a7c08d2c8167f21b)
1 /*
2  * Copyright 2001-2006, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Bill Hayden (haydentech@users.sourceforge.net)
8  *		Stefano Ceccherini (burton666@libero.it)
9  *		Olivier Milla
10  */
11 
12 //!	Display item for BMenu class
13 
14 #include <Bitmap.h>
15 #include <MenuItem.h>
16 #include <String.h>
17 #include <Window.h>
18 
19 #include <string.h>
20 #include <stdlib.h>
21 
22 
23 const uint32 kCtrlLength = 20*11;
24 const unsigned char kCtrlBits [] = {
25 	0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x14,0xff,0xff,0xff,
26 	0x1d,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
27 	0x1d,0x1a,0x13,0x04,0x04,0x13,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
28 	0x1d,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x04,0x04,0x1a,0x04,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
29 	0x1d,0x1a,0x04,0x1a,0x1a,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
30 	0x1d,0x1a,0x04,0x1a,0x1a,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
31 	0x1d,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
32 	0x1d,0x1a,0x13,0x04,0x04,0x13,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x04,0x04,0x1a,0x17,0x14,0xff,0xff,0xff,
33 	0x1d,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
34 	0x1d,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x14,0xff,0xff,0xff,
35 	0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0xff,0xff,0xff
36 /*	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
37 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
38 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
39 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
40 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff*/
41 };
42 
43 
44 const uint32 kAltLength = 20*11;
45 const unsigned char kAltBits [] = {
46 	0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x14,0xff,0xff,0xff,
47 	0x1d,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
48 	0x1d,0x1a,0x1a,0x13,0x04,0x04,0x13,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
49 	0x1d,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x1a,0x1a,0x04,0x04,0x04,0x1a,0x17,0x14,0xff,0xff,0xff,
50 	0x1d,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
51 	0x1d,0x1a,0x1a,0x04,0x04,0x04,0x04,0x1a,0x04,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
52 	0x1d,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
53 	0x1d,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x04,0x04,0x1a,0x04,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
54 	0x1d,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
55 	0x1d,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x14,0xff,0xff,0xff,
56 	0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0xff,0xff,0xff
57 /*	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
58 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
59 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
60 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
61 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff*/
62 };
63 
64 
65 const uint32 kShiftLength = 24*11;
66 const unsigned char kShiftBits [] = {
67 	0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x17,0xff,0xff,
68 	0x1d,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x17,0xff,0xff,
69 	0x1d,0x1b,0x1b,0x17,0x0d,0x0d,0x17,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x17,0xff,0xff,
70 	0x1d,0x1b,0x1b,0x0d,0x19,0x19,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x0d,0x0d,0x0d,0x1b,0x0d,0x0d,0x0d,0x1b,0x19,0x17,0xff,0xff,
71 	0x1d,0x1b,0x1b,0x17,0x0d,0x0d,0x19,0x1b,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x1b,0x1b,0x1b,0x0d,0x1b,0x1b,0x19,0x17,0xff,0xff,
72 	0x1d,0x1b,0x1b,0x1b,0x1b,0x1b,0x0d,0x1b,0x0d,0x0d,0x0d,0x1b,0x0d,0x0d,0x1b,0x1b,0x1b,0x0d,0x1b,0x1b,0x19,0x17,0xff,0xff,
73 	0x1d,0x1b,0x1b,0x0d,0x19,0x19,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x1b,0x1b,0x1b,0x0d,0x1b,0x1b,0x19,0x17,0xff,0xff,
74 	0x1d,0x1b,0x1b,0x17,0x0d,0x0d,0x17,0x1b,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x1b,0x1b,0x1b,0x0d,0x1b,0x1b,0x19,0x17,0xff,0xff,
75 	0x1d,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x17,0xff,0xff,
76 	0x1d,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x17,0xff,0xff,
77 	0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0xff,0xff
78 	/*0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
79 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
80 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
81 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
82 	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff*/
83 };
84 
85 
86 BMenuItem::BMenuItem(const char *label, BMessage *message, char shortcut,
87 					 uint32 modifiers)
88 {
89 	InitData();
90 	if (label != NULL)
91 		fLabel = strdup(label);
92 
93 	SetMessage(message);
94 
95 	fShortcutChar = shortcut;
96 
97 	if (shortcut != 0)
98 		fModifiers = modifiers | B_COMMAND_KEY;
99 	else
100 		fModifiers = 0;
101 }
102 
103 
104 BMenuItem::BMenuItem(BMenu *menu, BMessage *message)
105 {
106 	InitData();
107 	SetMessage(message);
108 	InitMenuData(menu);
109 }
110 
111 
112 BMenuItem::BMenuItem(BMessage *data)
113 {
114 	InitData();
115 
116 	if (data->HasString("_label")) {
117 		const char *string;
118 
119 		data->FindString("_label", &string);
120 		SetLabel(string);
121 	}
122 
123 	bool disable;
124 	if (data->FindBool("_disable", &disable) == B_OK)
125 		SetEnabled(!disable);
126 
127 	bool marked;
128 	if (data->FindBool("_marked", &marked) == B_OK)
129 		SetMarked(marked);
130 
131 	if (data->HasInt32("_user_trig")) {
132 		int32 user_trig;
133 
134 		data->FindInt32("_user_trig", &user_trig);
135 
136 		SetTrigger(user_trig);
137 	}
138 
139 	if (data->HasInt32("_shortcut")) {
140 		int32 shortcut, mods;
141 
142 		data->FindInt32("_shortcut", &shortcut);
143 		data->FindInt32("_mods", &mods);
144 
145 		SetShortcut(shortcut, mods);
146 	}
147 
148 	if (data->HasMessage("_msg")) {
149 		BMessage *msg = new BMessage;
150 
151 		data->FindMessage("_msg", msg);
152 		SetMessage(msg);
153 	}
154 
155 	BMessage subMessage;
156 	if (data->FindMessage("_submenu", &subMessage) == B_OK) {
157 		BArchivable *object = instantiate_object(&subMessage);
158 
159 		if (object != NULL) {
160 			BMenu *menu = dynamic_cast<BMenu *>(object);
161 
162 			if (menu != NULL)
163 				InitMenuData(menu);
164 		}
165 	}
166 }
167 
168 
169 BArchivable *
170 BMenuItem::Instantiate(BMessage *data)
171 {
172 	if (validate_instantiation(data, "BMenuItem"))
173 		return new BMenuItem(data);
174 	else
175 		return NULL;
176 }
177 
178 
179 status_t
180 BMenuItem::Archive(BMessage *data, bool deep) const
181 {
182 	if (fLabel)
183 		data->AddString("_label", Label());
184 
185 	if (!IsEnabled())
186 		data->AddBool("_disable", true);
187 
188 	if (IsMarked())
189 		data->AddBool("_marked", true);
190 
191 	if (fUserTrigger)
192 		data->AddInt32("_user_trig", fUserTrigger);
193 
194 	if (fShortcutChar) {
195 		data->AddInt32("_shortcut", fShortcutChar);
196 		data->AddInt32("_mods", fModifiers);
197 	}
198 
199 	if (Message())
200 		data->AddMessage("_msg", Message());
201 
202 	if (deep && fSubmenu) {
203 		BMessage submenu;
204 
205 		if (fSubmenu->Archive(&submenu, true) == B_OK)
206 			data->AddMessage("_submenu", &submenu);
207 	}
208 
209 	return B_OK;
210 }
211 
212 
213 BMenuItem::~BMenuItem()
214 {
215 	free(fLabel);
216 	delete fSubmenu;
217 }
218 
219 
220 void
221 BMenuItem::SetLabel(const char *string)
222 {
223 	if (fLabel != NULL) {
224 		free(fLabel);
225 		fLabel = NULL;
226 	}
227 
228 	if (string != NULL)
229 		fLabel = strdup(string);
230 
231 	if (fSuper != NULL) {
232 		fSuper->InvalidateLayout();
233 
234 		if (fSuper->LockLooper()) {
235 			fSuper->Invalidate();
236 			fSuper->UnlockLooper();
237 		}
238 	}
239 }
240 
241 
242 void
243 BMenuItem::SetEnabled(bool state)
244 {
245 	if (fEnabled == state)
246 		return;
247 
248 	fEnabled = state;
249 
250 	if (fSubmenu != NULL)
251 		fSubmenu->SetEnabled(state);
252 
253 	BMenu *menu = Menu();
254 	if (menu != NULL && menu->LockLooper()) {
255 		menu->Invalidate(fBounds);
256 		menu->UnlockLooper();
257 	}
258 }
259 
260 
261 void
262 BMenuItem::SetMarked(bool state)
263 {
264 	fMark = state;
265 
266 	if (state && Menu() != NULL)
267 		Menu()->ItemMarked(this);
268 }
269 
270 
271 void
272 BMenuItem::SetTrigger(char ch)
273 {
274 	fUserTrigger = ch;
275 
276 	if (strchr(fLabel, ch) != 0)
277 		fSysTrigger = ch;
278 	else
279 		fSysTrigger = -1;
280 
281 	if (fSuper != NULL)
282 		fSuper->InvalidateLayout();
283 }
284 
285 
286 void
287 BMenuItem::SetShortcut(char ch, uint32 modifiers)
288 {
289 	if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow)
290 		fWindow->RemoveShortcut(fShortcutChar, fModifiers);
291 
292 	fShortcutChar = ch;
293 
294 	if (ch != 0)
295 		fModifiers = modifiers | B_COMMAND_KEY;
296 	else
297 		fModifiers = 0;
298 
299 	if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow)
300 		fWindow->AddShortcut(fShortcutChar, fModifiers, this);
301 
302 	if (fSuper) {
303 		fSuper->InvalidateLayout();
304 
305 		if (fSuper->LockLooper()) {
306 			fSuper->Invalidate();
307 			fSuper->UnlockLooper();
308 		}
309 	}
310 }
311 
312 
313 const char *
314 BMenuItem::Label() const
315 {
316 	return fLabel;
317 }
318 
319 
320 bool
321 BMenuItem::IsEnabled() const
322 {
323 	if (fSubmenu)
324 		return fSubmenu->IsEnabled();
325 
326 	if (!fEnabled)
327 		return false;
328 
329 	return fSuper ? fSuper->IsEnabled() : true;
330 }
331 
332 
333 bool
334 BMenuItem::IsMarked() const
335 {
336 	return fMark;
337 }
338 
339 
340 char
341 BMenuItem::Trigger() const
342 {
343 	return fUserTrigger;
344 }
345 
346 
347 char
348 BMenuItem::Shortcut(uint32 *modifiers) const
349 {
350 	if (modifiers)
351 		*modifiers = fModifiers;
352 
353 	return fShortcutChar;
354 }
355 
356 
357 BMenu *
358 BMenuItem::Submenu() const
359 {
360 	return fSubmenu;
361 }
362 
363 
364 BMenu *
365 BMenuItem::Menu() const
366 {
367 	return fSuper;
368 }
369 
370 
371 BRect
372 BMenuItem::Frame() const
373 {
374 	return fBounds;
375 }
376 
377 
378 void
379 BMenuItem::GetContentSize(float *width, float *height)
380 {
381 	fSuper->CacheFontInfo();
382 
383 	fCachedWidth = fSuper->StringWidth(fLabel);
384 
385 	if (width)
386 		*width = (float)ceil(fCachedWidth);
387 	if (height)
388 		*height = fSuper->fFontHeight;
389 }
390 
391 
392 void
393 BMenuItem::TruncateLabel(float maxWidth, char *newLabel)
394 {
395 	BFont font;
396 	BString string(fLabel);
397 
398 	font.TruncateString(&string, B_TRUNCATE_MIDDLE, maxWidth);
399 
400 	string.CopyInto(newLabel, 0, string.Length());
401 	newLabel[string.Length()] = '\0';
402 }
403 
404 
405 void
406 BMenuItem::DrawContent()
407 {
408 	fSuper->CacheFontInfo();
409 
410 	fSuper->MovePenBy(0, fSuper->fAscent);
411 	BPoint lineStart = fSuper->PenLocation();
412 
413 	float labelWidth, labelHeight;
414 	GetContentSize(&labelWidth, &labelHeight);
415 
416 	// truncate if needed
417 	// TODO: Actually, this is still never triggered
418 	if (fBounds.Width() > labelWidth)
419 		fSuper->DrawString(fLabel);
420 	else {
421 		char *truncatedLabel = new char[strlen(fLabel) + 4];
422 		TruncateLabel(fBounds.Width(), truncatedLabel);
423 		fSuper->DrawString(truncatedLabel);
424 		delete[] truncatedLabel;
425 	}
426 
427 	if (fSuper->AreTriggersEnabled() && fTriggerIndex != -1) {
428 		float escapements[fTriggerIndex + 1];
429 		BFont font;
430 		fSuper->GetFont(&font);
431 
432 		font.GetEscapements(fLabel, fTriggerIndex + 1, escapements);
433 
434 		for (int32 i = 0; i < fTriggerIndex; i++)
435 			lineStart.x += escapements[i] * font.Size();
436 
437 		lineStart.x--;
438 		lineStart.y++;
439 
440 		BPoint lineEnd(lineStart);
441 		lineEnd.x += escapements[fTriggerIndex] * font.Size();
442 
443 		fSuper->StrokeLine(lineStart, lineEnd);
444 	}
445 }
446 
447 
448 void
449 BMenuItem::Draw()
450 {
451 	// TODO: Cleanup
452 	bool enabled = IsEnabled();
453 
454 	if (IsSelected() && (enabled || Submenu()) /*&& fSuper->fRedrawAfterSticky*/) {
455 		fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
456 			B_DARKEN_2_TINT));
457 		fSuper->SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
458 			B_DARKEN_2_TINT));
459 		fSuper->FillRect(Frame());
460 	}
461 
462 	if (IsEnabled())
463 		fSuper->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
464 	else if (IsSelected())
465 		fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
466 			B_LIGHTEN_1_TINT));
467 	else
468 		fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
469 			B_DISABLED_LABEL_TINT));
470 
471 	fSuper->MovePenTo(ContentLocation());
472 
473 	DrawContent();
474 
475 	if (fSuper->Layout() == B_ITEMS_IN_COLUMN) {
476 		if (IsMarked())
477 			DrawMarkSymbol();
478 
479 		if (fShortcutChar)
480 			DrawShortcutSymbol();
481 
482 		if (Submenu())
483 			DrawSubmenuSymbol();
484 	}
485 
486 	fSuper->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR));
487 }
488 
489 
490 void
491 BMenuItem::Highlight(bool flag)
492 {
493 	Menu()->Invalidate(Frame());
494 }
495 
496 
497 bool
498 BMenuItem::IsSelected() const
499 {
500 	return fSelected;
501 }
502 
503 
504 BPoint
505 BMenuItem::ContentLocation() const
506 {
507 	return BPoint(fBounds.left + Menu()->fPad.left,
508 		fBounds.top + Menu()->fPad.top);
509 }
510 
511 
512 void BMenuItem::_ReservedMenuItem1() {}
513 void BMenuItem::_ReservedMenuItem2() {}
514 void BMenuItem::_ReservedMenuItem3() {}
515 void BMenuItem::_ReservedMenuItem4() {}
516 
517 
518 BMenuItem::BMenuItem(const BMenuItem &)
519 {
520 }
521 
522 
523 BMenuItem &
524 BMenuItem::operator=(const BMenuItem &)
525 {
526 	return *this;
527 }
528 
529 
530 void
531 BMenuItem::InitData()
532 {
533 	fLabel = NULL;
534 	fSubmenu = NULL;
535 	fWindow = NULL;
536 	fSuper = NULL;
537 	fModifiers = 0;
538 	fCachedWidth = 0;
539 	fTriggerIndex = -1;
540 	fUserTrigger = 0;
541 	fSysTrigger = 0;
542 	fShortcutChar = 0;
543 	fMark = false;
544 	fEnabled = true;
545 	fSelected = false;
546 }
547 
548 
549 void
550 BMenuItem::InitMenuData(BMenu *menu)
551 {
552 	fSubmenu = menu;
553 	fSubmenu->fSuperitem = this;
554 
555 	BMenuItem *item = menu->FindMarked();
556 
557 	if (menu->IsRadioMode() && menu->IsLabelFromMarked() && item != NULL)
558 		SetLabel(item->Label());
559 	else
560 		SetLabel(menu->Name());
561 }
562 
563 
564 void
565 BMenuItem::Install(BWindow *window)
566 {
567 	if (fSubmenu)
568 		fSubmenu->Install(window);
569 
570 	fWindow = window;
571 
572 	if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow)
573 		window->AddShortcut(fShortcutChar, fModifiers, this);
574 
575 	if (!Messenger().IsValid())
576 		SetTarget(window);
577 }
578 
579 
580 status_t
581 BMenuItem::Invoke(BMessage *message)
582 {
583 	if (!IsEnabled())
584 		return B_ERROR;
585 
586 	if (fSuper->IsRadioMode())
587 		SetMarked(true);
588 
589 	bool notify = false;
590 	uint32 kind = InvokeKind(&notify);
591 
592 	BMessage clone(kind);
593 	status_t err = B_BAD_VALUE;
594 
595 	if (!message && !notify)
596 		message = Message();
597 
598 	if (!message) {
599 		if (!fSuper->IsWatched())
600 			return err;
601 	} else
602 		clone = *message;
603 
604 	clone.AddInt32("index", Menu()->IndexOf(this));
605 	clone.AddInt64("when", (int64)system_time());
606 	clone.AddPointer("source", this);
607 	clone.AddMessenger("be:sender", BMessenger(fSuper));
608 
609 	if (message)
610 		err = BInvoker::Invoke(&clone);
611 
612 //	TODO: assynchronous messaging
613 //	SendNotices(kind, &clone);
614 
615 	return err;
616 }
617 
618 
619 void
620 BMenuItem::Uninstall()
621 {
622 	if (fSubmenu != NULL)
623 		fSubmenu->Uninstall();
624 
625 	if (Target() == fWindow)
626 		SetTarget(BMessenger());
627 
628 	// TODO: I'm not sure about B_COMMAND_KEY
629 	if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow != NULL)
630 		fWindow->RemoveShortcut(fShortcutChar, fModifiers);
631 
632 	fWindow = NULL;
633 }
634 
635 
636 void
637 BMenuItem::SetSuper(BMenu *super)
638 {
639 	if (fSuper != NULL && super != NULL)
640 		debugger("Error - can't add menu or menu item to more than 1 container (either menu or menubar).");
641 
642 	fSuper = super;
643 
644 	if (fSubmenu != NULL)
645 		fSubmenu->fSuper = super;
646 }
647 
648 
649 void
650 BMenuItem::Select(bool selected)
651 {
652 	if (fSelected == selected)
653 		return;
654 
655 	if (Submenu()) {
656 		fSelected = selected;
657 		Highlight(selected);
658 	} else if (IsEnabled()) {
659 		fSelected = selected;
660 		Highlight(selected);
661 	}
662 }
663 
664 
665 void
666 BMenuItem::DrawMarkSymbol()
667 {
668 	fSuper->SetDrawingMode(B_OP_OVER);
669 
670 	fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
671 		B_DARKEN_1_TINT));
672 	fSuper->StrokeLine(BPoint(fBounds.left + 6.0f, fBounds.bottom - 3.0f),
673 		BPoint(fBounds.left + 10.0f, fBounds.bottom - 12.0f));
674 	fSuper->StrokeLine(BPoint(fBounds.left + 7.0f, fBounds.bottom - 3.0f),
675 		BPoint(fBounds.left + 11.0f, fBounds.bottom - 12.0f));
676 
677 	fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
678 		B_DARKEN_4_TINT));
679 
680 	fSuper->BeginLineArray(4);
681 	fSuper->StrokeLine(BPoint(fBounds.left + 6.0f, fBounds.bottom - 4.0f),
682 		BPoint(fBounds.left + 10.0f, fBounds.bottom - 13.0f));
683 	fSuper->StrokeLine(BPoint(fBounds.left + 5.0f, fBounds.bottom - 4.0f),
684 		BPoint(fBounds.left + 9.0f, fBounds.bottom - 13.0f));
685 	fSuper->StrokeLine(BPoint(fBounds.left + 5.0f, fBounds.bottom - 3.0f),
686 		BPoint(fBounds.left + 3.0f, fBounds.bottom - 9.0f));
687 	fSuper->StrokeLine(BPoint(fBounds.left + 4.0f, fBounds.bottom - 4.0f),
688 		BPoint(fBounds.left + 2.0f, fBounds.bottom - 9.0f));
689 	fSuper->EndLineArray();
690 
691 	fSuper->SetDrawingMode(B_OP_COPY);
692 }
693 
694 
695 void
696 BMenuItem::DrawShortcutSymbol()
697 {
698 	// TODO: Review this
699 	BPoint where(ContentLocation() + BPoint(fBounds.Width() - 32.0f, fBounds.Height() - 4.0f));
700 	if (fSubmenu)
701 		where.x -= 12;
702 
703 	// TODO: If the shortcut is one of B_DOWN_ARROW, B_UP_ARROW, B_ENTER, etc.
704 	// we can't just use DrawString(), as those aren't valid ascii/UTF8 charachters.
705 	// In that case we need to build a BBitmap for the given shortcut and draw it using DrawBitmap()
706 	fSuper->DrawChar(fShortcutChar, where);
707 
708 	where -= BPoint(20, 10);
709 
710 	// TODO: Do this in a better way
711 	bool altAsCommandKey = true;
712 	key_map *keys = NULL;
713 	char *chars;
714 	get_key_map(&keys, &chars);
715 	if (keys == NULL || keys->left_command_key != 0x5d || keys->right_command_key != 0x5f)
716 		altAsCommandKey = false;
717 	free(chars);
718 	free(keys);
719 
720 	if (fModifiers & B_COMMAND_KEY) {
721 		BRect rect(0,0,16,10);
722 		BBitmap control(rect, B_COLOR_8_BIT);
723 
724 		if (altAsCommandKey)
725 			control.SetBits(kAltBits, kAltLength, 0, B_COLOR_8_BIT);
726 		else
727 			control.SetBits(kCtrlBits, kCtrlLength, 0, B_COLOR_8_BIT);
728 		fSuper->DrawBitmap(&control, where);
729 
730 		where.x -= rect.Width() + 1;
731 	}
732 
733 	if (fModifiers & B_CONTROL_KEY) {
734 		BRect rect(0,0,16,10);
735 		BBitmap control(rect, B_COLOR_8_BIT);
736 
737 		if (altAsCommandKey)
738 			control.SetBits(kCtrlBits, kCtrlLength, 0, B_COLOR_8_BIT);
739 		else
740 			control.SetBits(kAltBits, kAltLength, 0, B_COLOR_8_BIT);
741 		fSuper->DrawBitmap(&control, where);
742 
743 		where.x -= rect.Width() + 1;
744 	}
745 
746 	if (fModifiers & B_SHIFT_KEY) {
747 		BRect rect(0,0,21,10);
748 		BBitmap shift(rect, B_COLOR_8_BIT);
749 		shift.SetBits(kShiftBits, kShiftLength, 0, B_COLOR_8_BIT);
750 		fSuper->DrawBitmap(&shift, where - BPoint(6, 0));
751 	}
752 }
753 
754 
755 void
756 BMenuItem::DrawSubmenuSymbol()
757 {
758 	fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
759 		B_LIGHTEN_MAX_TINT));
760 	fSuper->FillTriangle(BPoint(fBounds.right - 14.0f, fBounds.bottom - 4.0f),
761 		BPoint(fBounds.right - 14.0f, fBounds.bottom - 12.0f),
762 		BPoint(fBounds.right - 5.0f, fBounds.bottom - 8.0f));
763 
764 	fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
765 		B_DARKEN_2_TINT));
766 	fSuper->StrokeLine(BPoint(fBounds.right - 14.0f, fBounds.bottom - 5),
767 		BPoint(fBounds.right - 9.0f, fBounds.bottom - 7));
768 	fSuper->StrokeLine(BPoint(fBounds.right - 7.0f, fBounds.bottom - 8),
769 		BPoint(fBounds.right - 7.0f, fBounds.bottom - 8));
770 
771 	fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
772 		B_DARKEN_3_TINT));
773 	fSuper->StrokeTriangle(BPoint(fBounds.right - 14.0f, fBounds.bottom - 4.0f),
774 		BPoint(fBounds.right - 14.0f, fBounds.bottom - 12.0f),
775 		BPoint(fBounds.right - 5.0f, fBounds.bottom - 8.0f));
776 
777 	fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
778 		B_LIGHTEN_1_TINT));
779 	fSuper->StrokeTriangle(BPoint(fBounds.right - 12.0f, fBounds.bottom - 7.0f),
780 		BPoint(fBounds.right - 12.0f, fBounds.bottom - 9.0f),
781 		BPoint(fBounds.right - 9.0f, fBounds.bottom - 8.0f));
782 	fSuper->FillTriangle(BPoint(fBounds.right - 12.0f, fBounds.bottom - 7.0f),
783 		BPoint(fBounds.right - 12.0f, fBounds.bottom - 9.0f),
784 		BPoint(fBounds.right - 9.0f, fBounds.bottom - 8.0f));
785 }
786 
787 
788 void
789 BMenuItem::DrawControlChar(const char *control)
790 {
791 	// TODO: Implement
792 }
793 
794 
795 void
796 BMenuItem::SetSysTrigger(char ch)
797 {
798 	fSysTrigger = ch;
799 }
800