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