1 /* 2 * Copyright 2001-2015 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stefano Ceccherini, stefano.ceccherini@gmail.com 7 * Marc Flerackers, mflerackers@androme.be 8 * Bill Hayden, haydentech@users.sourceforge.net 9 * Olivier Milla 10 * John Scipione, jscipione@gmail.com 11 */ 12 13 14 #include <ctype.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include <Bitmap.h> 19 #include <ControlLook.h> 20 #include <MenuItem.h> 21 #include <Shape.h> 22 #include <String.h> 23 #include <Window.h> 24 25 #include <MenuPrivate.h> 26 27 #include "utf8_functions.h" 28 29 30 // map control key shortcuts to drawable Unicode characters 31 // cf. http://unicode.org/charts/PDF/U2190.pdf 32 const char* kUTF8ControlMap[] = { 33 NULL, 34 "\xe2\x86\xb8", /* B_HOME U+21B8 */ 35 NULL, NULL, 36 NULL, /* B_END */ 37 NULL, /* B_INSERT */ 38 NULL, NULL, 39 NULL, /* B_BACKSPACE */ 40 "\xe2\x86\xb9", /* B_TAB U+21B9 */ 41 "\xe2\x86\xb5", /* B_ENTER, U+21B5 */ 42 //"\xe2\x8f\x8e", /* B_ENTER, U+23CE it's the official one */ 43 NULL, /* B_PAGE_UP */ 44 NULL, /* B_PAGE_DOWN */ 45 NULL, NULL, NULL, 46 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 47 NULL, NULL, NULL, NULL, 48 "\xe2\x86\x90", /* B_LEFT_ARROW */ 49 "\xe2\x86\x92", /* B_RIGHT_ARROW */ 50 "\xe2\x86\x91", /* B_UP_ARROW */ 51 "\xe2\x86\x93", /* B_DOWN_ARROW */ 52 }; 53 54 55 using BPrivate::MenuPrivate; 56 57 BMenuItem::BMenuItem(const char* label, BMessage* message, char shortcut, 58 uint32 modifiers) 59 { 60 _InitData(); 61 if (label != NULL) 62 fLabel = strdup(label); 63 64 SetMessage(message); 65 66 fShortcutChar = shortcut; 67 68 if (shortcut != 0) 69 fModifiers = modifiers | B_COMMAND_KEY; 70 else 71 fModifiers = 0; 72 } 73 74 75 BMenuItem::BMenuItem(BMenu* menu, BMessage* message) 76 { 77 _InitData(); 78 SetMessage(message); 79 _InitMenuData(menu); 80 } 81 82 83 BMenuItem::BMenuItem(BMessage* data) 84 { 85 _InitData(); 86 87 if (data->HasString("_label")) { 88 const char* string; 89 90 data->FindString("_label", &string); 91 SetLabel(string); 92 } 93 94 bool disable; 95 if (data->FindBool("_disable", &disable) == B_OK) 96 SetEnabled(!disable); 97 98 bool marked; 99 if (data->FindBool("_marked", &marked) == B_OK) 100 SetMarked(marked); 101 102 int32 userTrigger; 103 if (data->FindInt32("_user_trig", &userTrigger) == B_OK) 104 SetTrigger(userTrigger); 105 106 if (data->HasInt32("_shortcut")) { 107 int32 shortcut, mods; 108 109 data->FindInt32("_shortcut", &shortcut); 110 data->FindInt32("_mods", &mods); 111 112 SetShortcut(shortcut, mods); 113 } 114 115 if (data->HasMessage("_msg")) { 116 BMessage* message = new BMessage; 117 data->FindMessage("_msg", message); 118 SetMessage(message); 119 } 120 121 BMessage subMessage; 122 if (data->FindMessage("_submenu", &subMessage) == B_OK) { 123 BArchivable* object = instantiate_object(&subMessage); 124 if (object != NULL) { 125 BMenu* menu = dynamic_cast<BMenu*>(object); 126 if (menu != NULL) 127 _InitMenuData(menu); 128 } 129 } 130 } 131 132 133 BArchivable* 134 BMenuItem::Instantiate(BMessage* data) 135 { 136 if (validate_instantiation(data, "BMenuItem")) 137 return new BMenuItem(data); 138 139 return NULL; 140 } 141 142 143 status_t 144 BMenuItem::Archive(BMessage* data, bool deep) const 145 { 146 status_t status = BArchivable::Archive(data, deep); 147 148 if (status == B_OK && fLabel) 149 status = data->AddString("_label", Label()); 150 151 if (status == B_OK && !IsEnabled()) 152 status = data->AddBool("_disable", true); 153 154 if (status == B_OK && IsMarked()) 155 status = data->AddBool("_marked", true); 156 157 if (status == B_OK && fUserTrigger) 158 status = data->AddInt32("_user_trig", fUserTrigger); 159 160 if (status == B_OK && fShortcutChar) { 161 status = data->AddInt32("_shortcut", fShortcutChar); 162 if (status == B_OK) 163 status = data->AddInt32("_mods", fModifiers); 164 } 165 166 if (status == B_OK && Message() != NULL) 167 status = data->AddMessage("_msg", Message()); 168 169 if (status == B_OK && deep && fSubmenu) { 170 BMessage submenu; 171 if (fSubmenu->Archive(&submenu, true) == B_OK) 172 status = data->AddMessage("_submenu", &submenu); 173 } 174 175 return status; 176 } 177 178 179 BMenuItem::~BMenuItem() 180 { 181 if (fSuper != NULL) 182 fSuper->RemoveItem(this); 183 184 free(fLabel); 185 delete fSubmenu; 186 } 187 188 189 void 190 BMenuItem::SetLabel(const char* string) 191 { 192 if (fLabel != NULL) { 193 free(fLabel); 194 fLabel = NULL; 195 } 196 197 if (string != NULL) 198 fLabel = strdup(string); 199 200 if (fSuper != NULL) { 201 fSuper->InvalidateLayout(); 202 203 if (fSuper->LockLooper()) { 204 fSuper->Invalidate(); 205 fSuper->UnlockLooper(); 206 } 207 } 208 } 209 210 211 void 212 BMenuItem::SetEnabled(bool enable) 213 { 214 if (fEnabled == enable) 215 return; 216 217 fEnabled = enable; 218 219 if (fSubmenu != NULL) 220 fSubmenu->SetEnabled(enable); 221 222 BMenu* menu = fSuper; 223 if (menu != NULL && menu->LockLooper()) { 224 menu->Invalidate(fBounds); 225 menu->UnlockLooper(); 226 } 227 } 228 229 230 void 231 BMenuItem::SetMarked(bool mark) 232 { 233 fMark = mark; 234 235 if (mark && fSuper != NULL) { 236 MenuPrivate priv(fSuper); 237 priv.ItemMarked(this); 238 } 239 } 240 241 242 void 243 BMenuItem::SetTrigger(char trigger) 244 { 245 fUserTrigger = trigger; 246 247 // try uppercase letters first 248 249 const char* pos = strchr(Label(), toupper(trigger)); 250 trigger = tolower(trigger); 251 252 if (pos == NULL) { 253 // take lowercase, too 254 pos = strchr(Label(), trigger); 255 } 256 257 if (pos != NULL) { 258 fTriggerIndex = UTF8CountChars(Label(), pos - Label()); 259 fTrigger = trigger; 260 } else { 261 fTrigger = 0; 262 fTriggerIndex = -1; 263 } 264 265 if (fSuper != NULL) 266 fSuper->InvalidateLayout(); 267 } 268 269 270 void 271 BMenuItem::SetShortcut(char shortcut, uint32 modifiers) 272 { 273 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) != 0 274 && fWindow != NULL) { 275 fWindow->RemoveShortcut(fShortcutChar, fModifiers); 276 } 277 278 fShortcutChar = shortcut; 279 280 if (shortcut != 0) 281 fModifiers = modifiers | B_COMMAND_KEY; 282 else 283 fModifiers = 0; 284 285 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow) 286 fWindow->AddShortcut(fShortcutChar, fModifiers, this); 287 288 if (fSuper != NULL) { 289 fSuper->InvalidateLayout(); 290 291 if (fSuper->LockLooper()) { 292 fSuper->Invalidate(); 293 fSuper->UnlockLooper(); 294 } 295 } 296 } 297 298 299 const char* 300 BMenuItem::Label() const 301 { 302 return fLabel; 303 } 304 305 306 bool 307 BMenuItem::IsEnabled() const 308 { 309 if (fSubmenu) 310 return fSubmenu->IsEnabled(); 311 312 if (!fEnabled) 313 return false; 314 315 return fSuper != NULL ? fSuper->IsEnabled() : true; 316 } 317 318 319 bool 320 BMenuItem::IsMarked() const 321 { 322 return fMark; 323 } 324 325 326 char 327 BMenuItem::Trigger() const 328 { 329 return fUserTrigger; 330 } 331 332 333 char 334 BMenuItem::Shortcut(uint32* modifiers) const 335 { 336 if (modifiers) 337 *modifiers = fModifiers; 338 339 return fShortcutChar; 340 } 341 342 343 BMenu* 344 BMenuItem::Submenu() const 345 { 346 return fSubmenu; 347 } 348 349 350 BMenu* 351 BMenuItem::Menu() const 352 { 353 return fSuper; 354 } 355 356 357 BRect 358 BMenuItem::Frame() const 359 { 360 return fBounds; 361 } 362 363 364 void 365 BMenuItem::GetContentSize(float* _width, float* _height) 366 { 367 // TODO: Get rid of this. BMenu should handle this 368 // automatically. Maybe it's not even needed, since our 369 // BFont::Height() caches the value locally 370 MenuPrivate(fSuper).CacheFontInfo(); 371 372 fCachedWidth = fSuper->StringWidth(fLabel); 373 374 if (_width) 375 *_width = (float)ceil(fCachedWidth); 376 if (_height) 377 *_height = MenuPrivate(fSuper).FontHeight(); 378 } 379 380 381 void 382 BMenuItem::TruncateLabel(float maxWidth, char* newLabel) 383 { 384 BFont font; 385 fSuper->GetFont(&font); 386 387 BString string(fLabel); 388 389 font.TruncateString(&string, B_TRUNCATE_MIDDLE, maxWidth); 390 391 string.CopyInto(newLabel, 0, string.Length()); 392 newLabel[string.Length()] = '\0'; 393 } 394 395 396 void 397 BMenuItem::DrawContent() 398 { 399 MenuPrivate menuPrivate(fSuper); 400 menuPrivate.CacheFontInfo(); 401 402 fSuper->MovePenBy(0, menuPrivate.Ascent()); 403 BPoint lineStart = fSuper->PenLocation(); 404 405 fSuper->SetDrawingMode(B_OP_OVER); 406 407 float labelWidth; 408 float labelHeight; 409 GetContentSize(&labelWidth, &labelHeight); 410 411 const BRect& padding = menuPrivate.Padding(); 412 float maxContentWidth = fSuper->MaxContentWidth(); 413 float frameWidth = maxContentWidth > 0 ? maxContentWidth 414 : fSuper->Frame().Width() - padding.left - padding.right; 415 416 if (roundf(frameWidth) >= roundf(labelWidth)) 417 fSuper->DrawString(fLabel); 418 else { 419 // truncate label to fit 420 char* truncatedLabel = new char[strlen(fLabel) + 4]; 421 TruncateLabel(frameWidth, truncatedLabel); 422 fSuper->DrawString(truncatedLabel); 423 delete[] truncatedLabel; 424 } 425 426 if (fSuper->AreTriggersEnabled() && fTriggerIndex != -1) { 427 float escapements[fTriggerIndex + 1]; 428 BFont font; 429 fSuper->GetFont(&font); 430 431 font.GetEscapements(fLabel, fTriggerIndex + 1, escapements); 432 433 for (int32 i = 0; i < fTriggerIndex; i++) 434 lineStart.x += escapements[i] * font.Size(); 435 436 lineStart.x--; 437 lineStart.y++; 438 439 BPoint lineEnd(lineStart); 440 lineEnd.x += escapements[fTriggerIndex] * font.Size(); 441 442 fSuper->StrokeLine(lineStart, lineEnd); 443 } 444 } 445 446 447 void 448 BMenuItem::Draw() 449 { 450 const color_which lowColor = fSuper->LowUIColor(); 451 const color_which highColor = fSuper->HighUIColor(); 452 453 bool enabled = IsEnabled(); 454 bool selected = IsSelected(); 455 bool activated = selected && (enabled || Submenu() != NULL); 456 457 // set low color 458 if (activated) { 459 fSuper->SetLowColor(ui_color(B_MENU_SELECTED_BACKGROUND_COLOR)); 460 // fill in the background 461 BRect rect(Frame()); 462 be_control_look->DrawMenuItemBackground(fSuper, rect, Frame(), 463 fSuper->LowColor(), BControlLook::B_ACTIVATED); 464 } else 465 fSuper->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR)); 466 467 // set high color 468 if (activated && enabled) 469 fSuper->SetHighColor(ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR)); 470 else if (enabled) 471 fSuper->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR)); 472 else { 473 rgb_color bgColor = fSuper->LowColor(); 474 if (bgColor.red + bgColor.green + bgColor.blue > 128 * 3) 475 fSuper->SetHighColor(tint_color(bgColor, B_DISABLED_LABEL_TINT)); 476 else 477 fSuper->SetHighColor(tint_color(bgColor, B_LIGHTEN_2_TINT)); 478 } 479 480 // draw content 481 fSuper->MovePenTo(ContentLocation()); 482 DrawContent(); 483 484 // draw extra symbols 485 const menu_layout layout = MenuPrivate(fSuper).Layout(); 486 if (layout == B_ITEMS_IN_COLUMN) { 487 if (IsMarked()) 488 _DrawMarkSymbol(); 489 490 if (fShortcutChar) 491 _DrawShortcutSymbol(); 492 493 if (Submenu() != NULL) 494 _DrawSubmenuSymbol(); 495 } 496 497 // restore the parent menu's low color and high color 498 fSuper->SetLowUIColor(lowColor); 499 fSuper->SetHighUIColor(highColor); 500 } 501 502 503 void 504 BMenuItem::Highlight(bool highlight) 505 { 506 fSuper->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 const BRect& padding = MenuPrivate(fSuper).Padding(); 521 522 return BPoint(fBounds.left + padding.left, fBounds.top + padding.top); 523 } 524 525 526 void BMenuItem::_ReservedMenuItem1() {} 527 void BMenuItem::_ReservedMenuItem2() {} 528 void BMenuItem::_ReservedMenuItem3() {} 529 void BMenuItem::_ReservedMenuItem4() {} 530 531 532 BMenuItem::BMenuItem(const BMenuItem &) 533 { 534 } 535 536 537 BMenuItem& 538 BMenuItem::operator=(const BMenuItem &) 539 { 540 return *this; 541 } 542 543 544 void 545 BMenuItem::_InitData() 546 { 547 fLabel = NULL; 548 fSubmenu = NULL; 549 fWindow = NULL; 550 fSuper = NULL; 551 fModifiers = 0; 552 fCachedWidth = 0; 553 fTriggerIndex = -1; 554 fUserTrigger = 0; 555 fTrigger = 0; 556 fShortcutChar = 0; 557 fMark = false; 558 fEnabled = true; 559 fSelected = false; 560 } 561 562 563 void 564 BMenuItem::_InitMenuData(BMenu* menu) 565 { 566 fSubmenu = menu; 567 568 MenuPrivate(fSubmenu).SetSuperItem(this); 569 570 BMenuItem* item = menu->FindMarked(); 571 572 if (menu->IsRadioMode() && menu->IsLabelFromMarked() && item != NULL) 573 SetLabel(item->Label()); 574 else 575 SetLabel(menu->Name()); 576 } 577 578 579 void 580 BMenuItem::Install(BWindow* window) 581 { 582 if (fSubmenu != NULL) 583 MenuPrivate(fSubmenu).Install(window); 584 585 fWindow = window; 586 587 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow) 588 window->AddShortcut(fShortcutChar, fModifiers, this); 589 590 if (!Messenger().IsValid()) 591 SetTarget(window); 592 } 593 594 595 status_t 596 BMenuItem::Invoke(BMessage* message) 597 { 598 if (!IsEnabled()) 599 return B_ERROR; 600 601 if (fSuper->IsRadioMode()) 602 SetMarked(true); 603 604 bool notify = false; 605 uint32 kind = InvokeKind(¬ify); 606 607 BMessage clone(kind); 608 status_t err = B_BAD_VALUE; 609 610 if (message == NULL && !notify) 611 message = Message(); 612 613 if (message == NULL) { 614 if (!fSuper->IsWatched()) 615 return err; 616 } else 617 clone = *message; 618 619 clone.AddInt32("index", fSuper->IndexOf(this)); 620 clone.AddInt64("when", (int64)system_time()); 621 clone.AddPointer("source", this); 622 clone.AddMessenger("be:sender", BMessenger(fSuper)); 623 624 if (message != NULL) 625 err = BInvoker::Invoke(&clone); 626 627 // TODO: assynchronous messaging 628 // SendNotices(kind, &clone); 629 630 return err; 631 } 632 633 634 void 635 BMenuItem::Uninstall() 636 { 637 if (fSubmenu != NULL) 638 MenuPrivate(fSubmenu).Uninstall(); 639 640 if (Target() == fWindow) 641 SetTarget(BMessenger()); 642 643 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) != 0 644 && fWindow != NULL) { 645 fWindow->RemoveShortcut(fShortcutChar, fModifiers); 646 } 647 648 fWindow = NULL; 649 } 650 651 652 void 653 BMenuItem::SetSuper(BMenu* super) 654 { 655 if (fSuper != NULL && super != NULL) { 656 debugger("Error - can't add menu or menu item to more than 1 container" 657 " (either menu or menubar)."); 658 } 659 660 if (fSubmenu != NULL) 661 MenuPrivate(fSubmenu).SetSuper(super); 662 663 fSuper = super; 664 } 665 666 667 void 668 BMenuItem::Select(bool selected) 669 { 670 if (fSelected == selected) 671 return; 672 673 if (Submenu() != NULL || IsEnabled()) { 674 fSelected = selected; 675 Highlight(selected); 676 } 677 } 678 679 680 void 681 BMenuItem::_DrawMarkSymbol() 682 { 683 fSuper->PushState(); 684 685 BRect r(fBounds); 686 float leftMargin; 687 MenuPrivate(fSuper).GetItemMargins(&leftMargin, NULL, NULL, NULL); 688 float gap = leftMargin / 4; 689 r.right = r.left + leftMargin - gap; 690 r.left += gap / 3; 691 692 BPoint center(floorf((r.left + r.right) / 2.0), 693 floorf((r.top + r.bottom) / 2.0)); 694 695 float size = min_c(r.Height() - 2, r.Width()); 696 r.top = floorf(center.y - size / 2 + 0.5); 697 r.bottom = floorf(center.y + size / 2 + 0.5); 698 r.left = floorf(center.x - size / 2 + 0.5); 699 r.right = floorf(center.x + size / 2 + 0.5); 700 701 BShape arrowShape; 702 center.x += 0.5; 703 center.y += 0.5; 704 size *= 0.3; 705 arrowShape.MoveTo(BPoint(center.x - size, center.y - size * 0.25)); 706 arrowShape.LineTo(BPoint(center.x - size * 0.25, center.y + size)); 707 arrowShape.LineTo(BPoint(center.x + size, center.y - size)); 708 709 fSuper->SetDrawingMode(B_OP_OVER); 710 fSuper->SetPenSize(2.0); 711 // NOTE: StrokeShape() offsets the shape by the current pen position, 712 // it is not documented in the BeBook, but it is true! 713 fSuper->MovePenTo(B_ORIGIN); 714 fSuper->StrokeShape(&arrowShape); 715 716 fSuper->PopState(); 717 } 718 719 720 void 721 BMenuItem::_DrawShortcutSymbol() 722 { 723 BMenu* menu = fSuper; 724 BFont font; 725 menu->GetFont(&font); 726 BPoint where = ContentLocation(); 727 where.x = fBounds.right - font.Size(); 728 729 if (fSubmenu) 730 where.x -= fBounds.Height() - 3; 731 732 const float ascent = MenuPrivate(fSuper).Ascent(); 733 if (fShortcutChar < B_SPACE && kUTF8ControlMap[(int)fShortcutChar]) 734 _DrawControlChar(fShortcutChar, where + BPoint(0, ascent)); 735 else 736 fSuper->DrawChar(fShortcutChar, where + BPoint(0, ascent)); 737 738 where.y += (fBounds.Height() - 11) / 2 - 1; 739 where.x -= 4; 740 741 // TODO: It would be nice to draw these taking into account the text (low) 742 // color. 743 if ((fModifiers & B_COMMAND_KEY) != 0) { 744 const BBitmap* command = MenuPrivate::MenuItemCommand(); 745 const BRect &rect = command->Bounds(); 746 where.x -= rect.Width() + 1; 747 fSuper->DrawBitmap(command, where); 748 } 749 750 if ((fModifiers & B_CONTROL_KEY) != 0) { 751 const BBitmap* control = MenuPrivate::MenuItemControl(); 752 const BRect &rect = control->Bounds(); 753 where.x -= rect.Width() + 1; 754 fSuper->DrawBitmap(control, where); 755 } 756 757 if ((fModifiers & B_OPTION_KEY) != 0) { 758 const BBitmap* option = MenuPrivate::MenuItemOption(); 759 const BRect &rect = option->Bounds(); 760 where.x -= rect.Width() + 1; 761 fSuper->DrawBitmap(option, where); 762 } 763 764 if ((fModifiers & B_SHIFT_KEY) != 0) { 765 const BBitmap* shift = MenuPrivate::MenuItemShift(); 766 const BRect &rect = shift->Bounds(); 767 where.x -= rect.Width() + 1; 768 fSuper->DrawBitmap(shift, where); 769 } 770 } 771 772 773 void 774 BMenuItem::_DrawSubmenuSymbol() 775 { 776 fSuper->PushState(); 777 778 BRect r(fBounds); 779 float rightMargin; 780 MenuPrivate(fSuper).GetItemMargins(NULL, NULL, &rightMargin, NULL); 781 r.left = r.right - rightMargin + 3; 782 r.right -= 1; 783 784 BPoint center(floorf((r.left + r.right) / 2.0), 785 floorf((r.top + r.bottom) / 2.0)); 786 787 float size = min_c(r.Height() - 2, r.Width()); 788 r.top = floorf(center.y - size / 2 + 0.5); 789 r.bottom = floorf(center.y + size / 2 + 0.5); 790 r.left = floorf(center.x - size / 2 + 0.5); 791 r.right = floorf(center.x + size / 2 + 0.5); 792 793 BShape arrowShape; 794 center.x += 0.5; 795 center.y += 0.5; 796 size *= 0.25; 797 float hSize = size * 0.7; 798 arrowShape.MoveTo(BPoint(center.x - hSize, center.y - size)); 799 arrowShape.LineTo(BPoint(center.x + hSize, center.y)); 800 arrowShape.LineTo(BPoint(center.x - hSize, center.y + size)); 801 802 fSuper->SetDrawingMode(B_OP_OVER); 803 fSuper->SetPenSize(ceilf(size * 0.4)); 804 // NOTE: StrokeShape() offsets the shape by the current pen position, 805 // it is not documented in the BeBook, but it is true! 806 fSuper->MovePenTo(B_ORIGIN); 807 fSuper->StrokeShape(&arrowShape); 808 809 fSuper->PopState(); 810 } 811 812 813 void 814 BMenuItem::_DrawControlChar(char shortcut, BPoint where) 815 { 816 // TODO: If needed, take another font for the control characters 817 // (or have font overlays in the app_server!) 818 const char* symbol = " "; 819 if (kUTF8ControlMap[(int)fShortcutChar]) 820 symbol = kUTF8ControlMap[(int)fShortcutChar]; 821 822 fSuper->DrawString(symbol, where); 823 } 824 825 826 void 827 BMenuItem::SetAutomaticTrigger(int32 index, uint32 trigger) 828 { 829 fTriggerIndex = index; 830 fTrigger = trigger; 831 } 832