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