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