1 /* 2 * Copyright 2001-2013 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 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 enable) 211 { 212 if (fEnabled == enable) 213 return; 214 215 fEnabled = enable; 216 217 if (fSubmenu != NULL) 218 fSubmenu->SetEnabled(enable); 219 220 BMenu* menu = fSuper; 221 if (menu != NULL && menu->LockLooper()) { 222 menu->Invalidate(fBounds); 223 menu->UnlockLooper(); 224 } 225 } 226 227 228 void 229 BMenuItem::SetMarked(bool mark) 230 { 231 fMark = mark; 232 233 if (mark && fSuper != NULL) { 234 MenuPrivate priv(fSuper); 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 shortcut, uint32 modifiers) 270 { 271 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) != 0 272 && fWindow != NULL) { 273 fWindow->RemoveShortcut(fShortcutChar, fModifiers); 274 } 275 276 fShortcutChar = shortcut; 277 278 if (shortcut != 0) 279 fModifiers = modifiers | B_COMMAND_KEY; 280 else 281 fModifiers = 0; 282 283 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow) 284 fWindow->AddShortcut(fShortcutChar, fModifiers, this); 285 286 if (fSuper) { 287 fSuper->InvalidateLayout(); 288 289 if (fSuper->LockLooper()) { 290 fSuper->Invalidate(); 291 fSuper->UnlockLooper(); 292 } 293 } 294 } 295 296 297 const char* 298 BMenuItem::Label() const 299 { 300 return fLabel; 301 } 302 303 304 bool 305 BMenuItem::IsEnabled() const 306 { 307 if (fSubmenu) 308 return fSubmenu->IsEnabled(); 309 310 if (!fEnabled) 311 return false; 312 313 return fSuper != NULL ? fSuper->IsEnabled() : true; 314 } 315 316 317 bool 318 BMenuItem::IsMarked() const 319 { 320 return fMark; 321 } 322 323 324 char 325 BMenuItem::Trigger() const 326 { 327 return fUserTrigger; 328 } 329 330 331 char 332 BMenuItem::Shortcut(uint32* modifiers) const 333 { 334 if (modifiers) 335 *modifiers = fModifiers; 336 337 return fShortcutChar; 338 } 339 340 341 BMenu* 342 BMenuItem::Submenu() const 343 { 344 return fSubmenu; 345 } 346 347 348 BMenu* 349 BMenuItem::Menu() const 350 { 351 return fSuper; 352 } 353 354 355 BRect 356 BMenuItem::Frame() const 357 { 358 return fBounds; 359 } 360 361 362 void 363 BMenuItem::GetContentSize(float* _width, float* _height) 364 { 365 // TODO: Get rid of this. BMenu should handle this 366 // automatically. Maybe it's not even needed, since our 367 // BFont::Height() caches the value locally 368 MenuPrivate(fSuper).CacheFontInfo(); 369 370 fCachedWidth = fSuper->StringWidth(fLabel); 371 372 if (_width) 373 *_width = (float)ceil(fCachedWidth); 374 if (_height) 375 *_height = MenuPrivate(fSuper).FontHeight(); 376 } 377 378 379 void 380 BMenuItem::TruncateLabel(float maxWidth, char* newLabel) 381 { 382 BFont font; 383 fSuper->GetFont(&font); 384 385 BString string(fLabel); 386 387 font.TruncateString(&string, B_TRUNCATE_MIDDLE, maxWidth); 388 389 string.CopyInto(newLabel, 0, string.Length()); 390 newLabel[string.Length()] = '\0'; 391 } 392 393 394 void 395 BMenuItem::DrawContent() 396 { 397 MenuPrivate menuPrivate(fSuper); 398 menuPrivate.CacheFontInfo(); 399 400 fSuper->MovePenBy(0, menuPrivate.Ascent()); 401 BPoint lineStart = fSuper->PenLocation(); 402 403 fSuper->SetDrawingMode(B_OP_OVER); 404 405 float labelWidth; 406 float labelHeight; 407 GetContentSize(&labelWidth, &labelHeight); 408 409 const BRect& padding = menuPrivate.Padding(); 410 float maxContentWidth = fSuper->MaxContentWidth(); 411 float frameWidth = maxContentWidth > 0 ? maxContentWidth 412 : fSuper->Frame().Width() - padding.left - padding.right; 413 414 if (roundf(frameWidth) >= roundf(labelWidth)) 415 fSuper->DrawString(fLabel); 416 else { 417 // truncate label to fit 418 char* truncatedLabel = new char[strlen(fLabel) + 4]; 419 TruncateLabel(frameWidth, truncatedLabel); 420 fSuper->DrawString(truncatedLabel); 421 delete[] truncatedLabel; 422 } 423 424 if (fSuper->AreTriggersEnabled() && fTriggerIndex != -1) { 425 float escapements[fTriggerIndex + 1]; 426 BFont font; 427 fSuper->GetFont(&font); 428 429 font.GetEscapements(fLabel, fTriggerIndex + 1, escapements); 430 431 for (int32 i = 0; i < fTriggerIndex; i++) 432 lineStart.x += escapements[i] * font.Size(); 433 434 lineStart.x--; 435 lineStart.y++; 436 437 BPoint lineEnd(lineStart); 438 lineEnd.x += escapements[fTriggerIndex] * font.Size(); 439 440 fSuper->StrokeLine(lineStart, lineEnd); 441 } 442 } 443 444 445 void 446 BMenuItem::Draw() 447 { 448 const rgb_color lowColor = fSuper->LowColor(); 449 const rgb_color highColor = fSuper->HighColor(); 450 451 bool enabled = IsEnabled(); 452 bool selected = IsSelected(); 453 bool activated = selected && (enabled || Submenu() != NULL); 454 455 // set low color 456 if (activated) { 457 fSuper->SetLowColor(ui_color(B_MENU_SELECTED_BACKGROUND_COLOR)); 458 // fill in the background 459 BRect rect(Frame()); 460 be_control_look->DrawMenuItemBackground(fSuper, rect, Frame(), 461 fSuper->LowColor(), BControlLook::B_ACTIVATED); 462 } else 463 fSuper->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR)); 464 465 // set high color 466 if (activated && enabled) 467 fSuper->SetHighColor(ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR)); 468 else if (enabled) 469 fSuper->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR)); 470 else { 471 rgb_color bgColor = fSuper->LowColor(); 472 if (bgColor.red + bgColor.green + bgColor.blue > 128 * 3) 473 fSuper->SetHighColor(tint_color(bgColor, B_DISABLED_LABEL_TINT)); 474 else 475 fSuper->SetHighColor(tint_color(bgColor, B_LIGHTEN_2_TINT)); 476 } 477 478 // draw content 479 fSuper->MovePenTo(ContentLocation()); 480 DrawContent(); 481 482 // draw extra symbols 483 const menu_layout layout = MenuPrivate(fSuper).Layout(); 484 if (layout == B_ITEMS_IN_COLUMN) { 485 if (IsMarked()) 486 _DrawMarkSymbol(); 487 488 if (fShortcutChar) 489 _DrawShortcutSymbol(); 490 491 if (Submenu()) 492 _DrawSubmenuSymbol(); 493 } 494 495 // restore the parent menu's low color and high color 496 fSuper->SetLowColor(lowColor); 497 fSuper->SetHighColor(highColor); 498 } 499 500 501 void 502 BMenuItem::Highlight(bool highlight) 503 { 504 fSuper->Invalidate(Frame()); 505 } 506 507 508 bool 509 BMenuItem::IsSelected() const 510 { 511 return fSelected; 512 } 513 514 515 BPoint 516 BMenuItem::ContentLocation() const 517 { 518 const BRect& padding = MenuPrivate(fSuper).Padding(); 519 520 return BPoint(fBounds.left + padding.left, fBounds.top + padding.top); 521 } 522 523 524 void BMenuItem::_ReservedMenuItem1() {} 525 void BMenuItem::_ReservedMenuItem2() {} 526 void BMenuItem::_ReservedMenuItem3() {} 527 void BMenuItem::_ReservedMenuItem4() {} 528 529 530 BMenuItem::BMenuItem(const BMenuItem &) 531 { 532 } 533 534 535 BMenuItem& 536 BMenuItem::operator=(const BMenuItem &) 537 { 538 return *this; 539 } 540 541 542 void 543 BMenuItem::_InitData() 544 { 545 fLabel = NULL; 546 fSubmenu = NULL; 547 fWindow = NULL; 548 fSuper = NULL; 549 fModifiers = 0; 550 fCachedWidth = 0; 551 fTriggerIndex = -1; 552 fUserTrigger = 0; 553 fTrigger = 0; 554 fShortcutChar = 0; 555 fMark = false; 556 fEnabled = true; 557 fSelected = false; 558 } 559 560 561 void 562 BMenuItem::_InitMenuData(BMenu* menu) 563 { 564 fSubmenu = menu; 565 566 MenuPrivate(fSubmenu).SetSuperItem(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 != NULL) 581 MenuPrivate(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 == NULL && !notify) 609 message = Message(); 610 611 if (message == NULL) { 612 if (!fSuper->IsWatched()) 613 return err; 614 } else 615 clone = *message; 616 617 clone.AddInt32("index", fSuper->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 != NULL) 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 MenuPrivate(fSubmenu).Uninstall(); 637 638 if (Target() == fWindow) 639 SetTarget(BMessenger()); 640 641 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) != 0 642 && fWindow != NULL) { 643 fWindow->RemoveShortcut(fShortcutChar, fModifiers); 644 } 645 646 fWindow = NULL; 647 } 648 649 650 void 651 BMenuItem::SetSuper(BMenu* super) 652 { 653 if (fSuper != NULL && super != NULL) { 654 debugger("Error - can't add menu or menu item to more than 1 container" 655 " (either menu or menubar)."); 656 } 657 658 if (fSubmenu != NULL) 659 MenuPrivate(fSubmenu).SetSuper(super); 660 661 fSuper = super; 662 } 663 664 665 void 666 BMenuItem::Select(bool selected) 667 { 668 if (fSelected == selected) 669 return; 670 671 if (Submenu() != NULL || IsEnabled()) { 672 fSelected = selected; 673 Highlight(selected); 674 } 675 } 676 677 678 void 679 BMenuItem::_DrawMarkSymbol() 680 { 681 fSuper->PushState(); 682 683 BRect r(fBounds); 684 float leftMargin; 685 MenuPrivate(fSuper).GetItemMargins(&leftMargin, NULL, NULL, NULL); 686 r.right = r.left + leftMargin - 3; 687 r.left += 1; 688 689 BPoint center(floorf((r.left + r.right) / 2.0), 690 floorf((r.top + r.bottom) / 2.0)); 691 692 float size = min_c(r.Height() - 2, r.Width()); 693 r.top = floorf(center.y - size / 2 + 0.5); 694 r.bottom = floorf(center.y + size / 2 + 0.5); 695 r.left = floorf(center.x - size / 2 + 0.5); 696 r.right = floorf(center.x + size / 2 + 0.5); 697 698 BShape arrowShape; 699 center.x += 0.5; 700 center.y += 0.5; 701 size *= 0.3; 702 arrowShape.MoveTo(BPoint(center.x - size, center.y - size * 0.25)); 703 arrowShape.LineTo(BPoint(center.x - size * 0.25, center.y + size)); 704 arrowShape.LineTo(BPoint(center.x + size, center.y - size)); 705 706 fSuper->SetDrawingMode(B_OP_OVER); 707 fSuper->SetPenSize(2.0); 708 // NOTE: StrokeShape() offsets the shape by the current pen position, 709 // it is not documented in the BeBook, but it is true! 710 fSuper->MovePenTo(B_ORIGIN); 711 fSuper->StrokeShape(&arrowShape); 712 713 fSuper->PopState(); 714 } 715 716 717 void 718 BMenuItem::_DrawShortcutSymbol() 719 { 720 BMenu* menu = fSuper; 721 BFont font; 722 menu->GetFont(&font); 723 BPoint where = ContentLocation(); 724 where.x = fBounds.right - font.Size(); 725 726 if (fSubmenu) 727 where.x -= fBounds.Height() - 3; 728 729 const float ascent = MenuPrivate(fSuper).Ascent(); 730 if (fShortcutChar < B_SPACE && kUTF8ControlMap[(int)fShortcutChar]) 731 _DrawControlChar(fShortcutChar, where + BPoint(0, ascent)); 732 else 733 fSuper->DrawChar(fShortcutChar, where + BPoint(0, ascent)); 734 735 where.y += (fBounds.Height() - 11) / 2 - 1; 736 where.x -= 4; 737 738 // TODO: It would be nice to draw these taking into account the text (low) 739 // color. 740 if (fModifiers & B_COMMAND_KEY) { 741 const BBitmap *command = MenuPrivate::MenuItemCommand(); 742 const BRect &rect = command->Bounds(); 743 where.x -= rect.Width() + 1; 744 fSuper->DrawBitmap(command, where); 745 } 746 747 if (fModifiers & B_CONTROL_KEY) { 748 const BBitmap *control = MenuPrivate::MenuItemControl(); 749 const BRect &rect = control->Bounds(); 750 where.x -= rect.Width() + 1; 751 fSuper->DrawBitmap(control, where); 752 } 753 754 if (fModifiers & B_OPTION_KEY) { 755 const BBitmap *option = MenuPrivate::MenuItemOption(); 756 const BRect &rect = option->Bounds(); 757 where.x -= rect.Width() + 1; 758 fSuper->DrawBitmap(option, where); 759 } 760 761 if (fModifiers & B_SHIFT_KEY) { 762 const BBitmap *shift = MenuPrivate::MenuItemShift(); 763 const BRect &rect = shift->Bounds(); 764 where.x -= rect.Width() + 1; 765 fSuper->DrawBitmap(shift, where); 766 } 767 } 768 769 770 void 771 BMenuItem::_DrawSubmenuSymbol() 772 { 773 fSuper->PushState(); 774 775 BRect r(fBounds); 776 float rightMargin; 777 MenuPrivate(fSuper).GetItemMargins(NULL, NULL, &rightMargin, NULL); 778 r.left = r.right - rightMargin + 3; 779 r.right -= 1; 780 781 BPoint center(floorf((r.left + r.right) / 2.0), 782 floorf((r.top + r.bottom) / 2.0)); 783 784 float size = min_c(r.Height() - 2, r.Width()); 785 r.top = floorf(center.y - size / 2 + 0.5); 786 r.bottom = floorf(center.y + size / 2 + 0.5); 787 r.left = floorf(center.x - size / 2 + 0.5); 788 r.right = floorf(center.x + size / 2 + 0.5); 789 790 BShape arrowShape; 791 center.x += 0.5; 792 center.y += 0.5; 793 size *= 0.25; 794 float hSize = size * 0.7; 795 arrowShape.MoveTo(BPoint(center.x - hSize, center.y - size)); 796 arrowShape.LineTo(BPoint(center.x + hSize, center.y)); 797 arrowShape.LineTo(BPoint(center.x - hSize, center.y + size)); 798 799 fSuper->SetDrawingMode(B_OP_OVER); 800 fSuper->SetPenSize(ceilf(size * 0.4)); 801 // NOTE: StrokeShape() offsets the shape by the current pen position, 802 // it is not documented in the BeBook, but it is true! 803 fSuper->MovePenTo(B_ORIGIN); 804 fSuper->StrokeShape(&arrowShape); 805 806 fSuper->PopState(); 807 } 808 809 810 void 811 BMenuItem::_DrawControlChar(char shortcut, BPoint where) 812 { 813 // TODO: If needed, take another font for the control characters 814 // (or have font overlays in the app_server!) 815 const char* symbol = " "; 816 if (kUTF8ControlMap[(int)fShortcutChar]) 817 symbol = kUTF8ControlMap[(int)fShortcutChar]; 818 819 fSuper->DrawString(symbol, where); 820 } 821 822 823 void 824 BMenuItem::SetAutomaticTrigger(int32 index, uint32 trigger) 825 { 826 fTriggerIndex = index; 827 fTrigger = trigger; 828 } 829