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(¬ify); 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