1 /* 2 * Copyright 2001-2005, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Stefano Ceccherini (burton666@libero.it) 8 */ 9 10 #include <string.h> 11 12 #include <Debug.h> 13 #include <File.h> 14 #include <FindDirectory.h> 15 #include <Menu.h> 16 #include <MenuItem.h> 17 #include <Path.h> 18 #include <PropertyInfo.h> 19 #include <Screen.h> 20 #include <Window.h> 21 22 #include <MenuPrivate.h> 23 #include <MenuWindow.h> 24 25 26 class _ExtraMenuData_ { 27 public: 28 menu_tracking_hook trackingHook; 29 void *trackingState; 30 31 _ExtraMenuData_(menu_tracking_hook func, void *state) 32 { 33 trackingHook = func; 34 trackingState = state; 35 }; 36 }; 37 38 39 menu_info BMenu::sMenuInfo; 40 41 42 static property_info 43 sPropList[] = { 44 { "Enabled", { B_GET_PROPERTY, 0 }, 45 { B_DIRECT_SPECIFIER, 0 }, "Returns true if menu or menu item is enabled; false " 46 "otherwise." }, 47 48 { "Enabled", { B_SET_PROPERTY, 0 }, 49 { B_DIRECT_SPECIFIER, 0 }, "Enables or disables menu or menu item." }, 50 51 { "Label", { B_GET_PROPERTY, 0 }, 52 { B_DIRECT_SPECIFIER, 0 }, "Returns the string label of the menu or menu item." }, 53 54 { "Label", { B_SET_PROPERTY, 0 }, 55 { B_DIRECT_SPECIFIER, 0 }, "Sets the string label of the menu or menu item." }, 56 57 { "Mark", { B_GET_PROPERTY, 0 }, 58 { B_DIRECT_SPECIFIER, 0 }, "Returns true if the menu item or the menu's superitem " 59 "is marked; false otherwise." }, 60 61 { "Mark", { B_SET_PROPERTY, 0 }, 62 { B_DIRECT_SPECIFIER, 0 }, "Marks or unmarks the menu item or the menu's superitem." }, 63 64 { "Menu", { B_CREATE_PROPERTY, 0 }, 65 { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 }, 66 "Adds a new menu item at the specified index with the text label found in \"data\" " 67 "and the int32 command found in \"what\" (used as the what field in the CMessage " 68 "sent by the item)." }, 69 70 { "Menu", { B_DELETE_PROPERTY, 0 }, 71 { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 }, 72 "Removes the selected menu or menus." }, 73 74 { "Menu", { B_GET_PROPERTY, B_SET_PROPERTY, 0 }, 75 { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 }, 76 "Directs scripting message to the specified menu, first popping the current " 77 "specifier off the stack." }, 78 79 { "MenuItem", { B_COUNT_PROPERTIES, 0 }, 80 { B_DIRECT_SPECIFIER, 0 }, "Counts the number of menu items in the specified menu." }, 81 82 { "MenuItem", { B_CREATE_PROPERTY, 0 }, 83 { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 }, 84 "Adds a new menu item at the specified index with the text label found in \"data\" " 85 "and the int32 command found in \"what\" (used as the what field in the CMessage " 86 "sent by the item)." }, 87 88 { "MenuItem", { B_DELETE_PROPERTY, 0 }, 89 { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 }, 90 "Removes the specified menu item from its parent menu." }, 91 92 { "MenuItem", { B_EXECUTE_PROPERTY, 0 }, 93 { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 }, 94 "Invokes the specified menu item." }, 95 96 { "MenuItem", { B_GET_PROPERTY, B_SET_PROPERTY, 0 }, 97 { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 }, 98 "Directs scripting message to the specified menu, first popping the current " 99 "specifier off the stack." }, 100 {0} 101 }; 102 103 104 BMenu::BMenu(const char *name, menu_layout layout) 105 // : BView(BRect(), name, 0, B_WILL_DRAW), 106 : BView(BRect(0.0, 0.0, 5.0, 5.0), name, 0, B_WILL_DRAW), 107 fChosenItem(NULL), 108 fPad(14.0f, 2.0f, 20.0f, 0.0f), 109 fSelected(NULL), 110 fCachedMenuWindow(NULL), 111 fSuper(NULL), 112 fSuperitem(NULL), 113 fAscent(-1.0f), 114 fDescent(-1.0f), 115 fFontHeight(-1.0f), 116 fState(0), 117 fLayout(layout), 118 fExtraRect(NULL), 119 fMaxContentWidth(0.0f), 120 fInitMatrixSize(NULL), 121 fExtraMenuData(NULL), 122 fTrigger(0), 123 fResizeToFit(true), 124 fUseCachedMenuLayout(true), 125 fEnabled(true), 126 fDynamicName(false), 127 fRadioMode(false), 128 fTrackNewBounds(false), 129 fStickyMode(false), 130 fIgnoreHidden(true), 131 fTriggerEnabled(true), 132 fRedrawAfterSticky(false), 133 fAttachAborted(false) 134 { 135 InitData(NULL); 136 } 137 138 139 BMenu::BMenu(const char *name, float width, float height) 140 : BView(BRect(0.0f, width, 0.0f, height), name, 0, B_WILL_DRAW), 141 fChosenItem(NULL), 142 fSelected(NULL), 143 fCachedMenuWindow(NULL), 144 fSuper(NULL), 145 fSuperitem(NULL), 146 fAscent(-1.0f), 147 fDescent(-1.0f), 148 fFontHeight(-1.0f), 149 fState(0), 150 fLayout(B_ITEMS_IN_MATRIX), 151 fExtraRect(NULL), 152 fMaxContentWidth(0.0f), 153 fInitMatrixSize(NULL), 154 fExtraMenuData(NULL), 155 fTrigger(0), 156 fResizeToFit(true), 157 fUseCachedMenuLayout(true), 158 fEnabled(true), 159 fDynamicName(false), 160 fRadioMode(false), 161 fTrackNewBounds(false), 162 fStickyMode(false), 163 fIgnoreHidden(true), 164 fTriggerEnabled(true), 165 fRedrawAfterSticky(false), 166 fAttachAborted(false) 167 { 168 InitData(NULL); 169 } 170 171 172 BMenu::~BMenu() 173 { 174 RemoveItems(0, CountItems(), true); 175 176 DeleteMenuWindow(); 177 178 delete fInitMatrixSize; 179 delete fExtraMenuData; 180 } 181 182 183 BMenu::BMenu(BMessage *archive) 184 : BView(archive) 185 { 186 InitData(archive); 187 } 188 189 190 BArchivable * 191 BMenu::Instantiate(BMessage *data) 192 { 193 if (validate_instantiation(data, "BMenu")) 194 return new BMenu(data); 195 else 196 return NULL; 197 } 198 199 200 status_t 201 BMenu::Archive(BMessage *data, bool deep) const 202 { 203 status_t err = BView::Archive(data, deep); 204 205 if (err < B_OK) 206 return err; 207 208 if (Layout() != B_ITEMS_IN_ROW) { 209 err = data->AddInt32("_layout", Layout()); 210 211 if (err < B_OK) 212 return err; 213 } 214 215 err = data->AddBool("_rsize_to_fit", fResizeToFit); 216 217 if (err < B_OK) 218 return err; 219 220 err = data->AddBool("_disable", !IsEnabled()); 221 222 if (err < B_OK) 223 return err; 224 225 err = data->AddBool("_radio", IsRadioMode()); 226 227 if (err < B_OK) 228 return err; 229 230 err = data->AddBool("_trig_disabled", AreTriggersEnabled()); 231 232 if (err < B_OK) 233 return err; 234 235 err = data->AddBool("_dyn_label", fDynamicName); 236 237 if (err < B_OK) 238 return err; 239 240 err = data->AddFloat("_maxwidth", fMaxContentWidth); 241 242 if (err < B_OK) 243 return err; 244 245 if (deep) { 246 // TODO store items and rects 247 } 248 249 return err; 250 } 251 252 253 void 254 BMenu::AttachedToWindow() 255 { 256 BView::AttachedToWindow(); 257 258 bool aborted = false; 259 260 if (AddDynamicItem(B_INITIAL_ADD)) { 261 do { 262 if (!OkToProceed(NULL)) { 263 AddDynamicItem(B_ABORT); 264 aborted = true; 265 break; 266 } 267 } while (AddDynamicItem(B_PROCESSING)); 268 } 269 270 if (!aborted) 271 InvalidateLayout(); 272 } 273 274 275 void 276 BMenu::DetachedFromWindow() 277 { 278 BView::DetachedFromWindow(); 279 } 280 281 282 bool 283 BMenu::AddItem(BMenuItem *item) 284 { 285 return AddItem(item, CountItems()); 286 } 287 288 289 bool 290 BMenu::AddItem(BMenuItem *item, int32 index) 291 { 292 if (fLayout == B_ITEMS_IN_MATRIX) 293 debugger("BMenu::AddItem(BMenuItem *, int32) this method can only" 294 "be called if the menu layout is not B_ITEMS_IN_MATRIX"); 295 296 return _AddItem(item, index); 297 } 298 299 300 bool 301 BMenu::AddItem(BMenuItem *item, BRect frame) 302 { 303 if (fLayout != B_ITEMS_IN_MATRIX) 304 debugger("BMenu::AddItem(BMenuItem *, BRect) this method can only" 305 " be called if the menu layout is B_ITEMS_IN_MATRIX"); 306 307 if (!item) 308 return false; 309 310 item->fBounds = frame; 311 312 return _AddItem(item, CountItems()); 313 } 314 315 316 bool 317 BMenu::AddItem(BMenu *submenu) 318 { 319 BMenuItem *item = new BMenuItem(submenu); 320 if (!item) 321 return false; 322 323 return _AddItem(item, CountItems()); 324 } 325 326 327 bool 328 BMenu::AddItem(BMenu *submenu, int32 index) 329 { 330 if (fLayout == B_ITEMS_IN_MATRIX) 331 debugger("BMenu::AddItem(BMenuItem *, int32) this method can only" 332 "be called if the menu layout is not B_ITEMS_IN_MATRIX"); 333 334 BMenuItem *item = new BMenuItem(submenu); 335 if (!item) 336 return false; 337 338 return _AddItem(item, index); 339 } 340 341 342 bool 343 BMenu::AddItem(BMenu *submenu, BRect frame) 344 { 345 if (fLayout != B_ITEMS_IN_MATRIX) 346 debugger("BMenu::AddItem(BMenu *, BRect) this method can only" 347 " be called if the menu layout is B_ITEMS_IN_MATRIX"); 348 349 BMenuItem *item = new BMenuItem(submenu); 350 item->fBounds = frame; 351 352 return _AddItem(item, CountItems()); 353 } 354 355 356 bool 357 BMenu::AddList(BList *list, int32 index) 358 { 359 // TODO: test this function, it's not documented in the bebook. 360 if (list == NULL) 361 return false; 362 363 int32 numItems = list->CountItems(); 364 for (int32 i = 0; i < numItems; i++) { 365 BMenuItem *item = static_cast<BMenuItem *>(list->ItemAt(i)); 366 if (item != NULL) 367 _AddItem(item, index + i); 368 } 369 370 return true; 371 } 372 373 374 bool 375 BMenu::AddSeparatorItem() 376 { 377 BMenuItem *item = new BSeparatorItem(); 378 return _AddItem(item, CountItems()); 379 } 380 381 382 bool 383 BMenu::RemoveItem(BMenuItem *item) 384 { 385 // TODO: Check if item is also deleted 386 return RemoveItems(0, 0, item, false); 387 } 388 389 390 BMenuItem * 391 BMenu::RemoveItem(int32 index) 392 { 393 BMenuItem *item = ItemAt(index); 394 RemoveItems(0, 0, item, false); 395 return item; 396 } 397 398 399 bool 400 BMenu::RemoveItems(int32 index, int32 count, bool del) 401 { 402 return RemoveItems(index, count, NULL, del); 403 } 404 405 406 bool 407 BMenu::RemoveItem(BMenu *submenu) 408 { 409 for (int32 i = 0; i < fItems.CountItems(); i++) 410 if (static_cast<BMenuItem *>(fItems.ItemAt(i))->Submenu() == submenu) 411 return RemoveItems(i, 1, NULL, false); 412 413 return false; 414 } 415 416 417 int32 418 BMenu::CountItems() const 419 { 420 return fItems.CountItems(); 421 } 422 423 424 BMenuItem * 425 BMenu::ItemAt(int32 index) const 426 { 427 return static_cast<BMenuItem *>(fItems.ItemAt(index)); 428 } 429 430 431 BMenu * 432 BMenu::SubmenuAt(int32 index) const 433 { 434 BMenuItem *item = static_cast<BMenuItem *>(fItems.ItemAt(index)); 435 return (item != NULL) ? item->Submenu() : NULL; 436 } 437 438 439 int32 440 BMenu::IndexOf(BMenuItem *item) const 441 { 442 return fItems.IndexOf(item); 443 } 444 445 446 int32 447 BMenu::IndexOf(BMenu *submenu) const 448 { 449 for (int32 i = 0; i < fItems.CountItems(); i++) 450 if (ItemAt(i)->Submenu() == submenu) 451 return i; 452 453 return -1; 454 } 455 456 457 BMenuItem * 458 BMenu::FindItem(const char *label) const 459 { 460 BMenuItem *item = NULL; 461 462 for (int32 i = 0; i < CountItems(); i++) { 463 item = ItemAt(i); 464 465 if (item->Label() && strcmp(item->Label(), label) == 0) 466 return item; 467 468 if (item->Submenu() != NULL) { 469 item = item->Submenu()->FindItem(label); 470 if (item != NULL) 471 return item; 472 } 473 } 474 475 return NULL; 476 } 477 478 479 BMenuItem * 480 BMenu::FindItem(uint32 command) const 481 { 482 BMenuItem *item = NULL; 483 484 for (int32 i = 0; i < CountItems(); i++) { 485 item = ItemAt(i); 486 487 if (item->Command() == command) 488 return item; 489 490 if (item->Submenu() != NULL) { 491 item = item->Submenu()->FindItem(command); 492 if (item != NULL) 493 return item; 494 } 495 } 496 497 return NULL; 498 } 499 500 501 status_t 502 BMenu::SetTargetForItems(BHandler *handler) 503 { 504 status_t status = B_OK; 505 for (int32 i = 0; i < fItems.CountItems(); i++) { 506 status = ItemAt(i)->SetTarget(handler); 507 if (status < B_OK) 508 break; 509 } 510 511 return status; 512 } 513 514 515 status_t 516 BMenu::SetTargetForItems(BMessenger messenger) 517 { 518 status_t status = B_OK; 519 for (int32 i = 0; i < fItems.CountItems(); i++) { 520 status = ItemAt(i)->SetTarget(messenger); 521 if (status < B_OK) 522 break; 523 } 524 525 return status; 526 } 527 528 529 void 530 BMenu::SetEnabled(bool enabled) 531 { 532 fEnabled = enabled; 533 534 for (int32 i = 0; i < CountItems(); i++) 535 ItemAt(i)->SetEnabled(enabled); 536 } 537 538 539 void 540 BMenu::SetRadioMode(bool flag) 541 { 542 fRadioMode = flag; 543 } 544 545 546 void 547 BMenu::SetTriggersEnabled(bool flag) 548 { 549 fTriggerEnabled = flag; 550 } 551 552 553 void 554 BMenu::SetMaxContentWidth(float width) 555 { 556 fMaxContentWidth = width; 557 } 558 559 560 void 561 BMenu::SetLabelFromMarked(bool flag) 562 { 563 fDynamicName = flag; 564 } 565 566 567 bool 568 BMenu::IsLabelFromMarked() 569 { 570 return fDynamicName; 571 } 572 573 574 bool 575 BMenu::IsEnabled() const 576 { 577 return fEnabled; 578 } 579 580 581 bool 582 BMenu::IsRadioMode() const 583 { 584 return fRadioMode; 585 } 586 587 588 bool 589 BMenu::AreTriggersEnabled() const 590 { 591 return fTriggerEnabled; 592 } 593 594 595 bool 596 BMenu::IsRedrawAfterSticky() const 597 { 598 return fRedrawAfterSticky; 599 } 600 601 602 float 603 BMenu::MaxContentWidth() const 604 { 605 return fMaxContentWidth; 606 } 607 608 609 BMenuItem * 610 BMenu::FindMarked() 611 { 612 for (int32 i = 0; i < fItems.CountItems(); i++) { 613 BMenuItem *item = ItemAt(i); 614 if (item->IsMarked()) 615 return item; 616 } 617 618 return NULL; 619 } 620 621 622 BMenu * 623 BMenu::Supermenu() const 624 { 625 return fSuper; 626 } 627 628 629 BMenuItem * 630 BMenu::Superitem() const 631 { 632 return fSuperitem; 633 } 634 635 636 void 637 BMenu::MessageReceived(BMessage *msg) 638 { 639 BView::MessageReceived(msg); 640 } 641 642 643 void 644 BMenu::KeyDown(const char *bytes, int32 numBytes) 645 { 646 switch (bytes[0]) { 647 case B_UP_ARROW: 648 { 649 if (fSelected) { 650 fSelected->fSelected = false; 651 652 if (fSelected == fItems.FirstItem()) 653 fSelected = static_cast<BMenuItem *>(fItems.LastItem()); 654 else 655 fSelected = ItemAt(IndexOf(fSelected) - 1); 656 } else 657 fSelected = static_cast<BMenuItem *>(fItems.LastItem()); 658 659 fSelected->fSelected = true; 660 661 break; 662 } 663 case B_DOWN_ARROW: 664 { 665 if (fSelected) { 666 fSelected->fSelected = false; 667 668 if (fSelected == fItems.LastItem()) 669 fSelected = static_cast<BMenuItem *>(fItems.FirstItem()); 670 else 671 fSelected = ItemAt(IndexOf(fSelected) + 1); 672 } else 673 fSelected = static_cast<BMenuItem *>(fItems.FirstItem()); 674 675 fSelected->fSelected = true; 676 677 break; 678 } 679 case B_HOME: 680 { 681 if (fSelected) 682 fSelected->fSelected = false; 683 684 fSelected = static_cast<BMenuItem *>(fItems.FirstItem()); 685 fSelected->fSelected = true; 686 687 break; 688 } 689 case B_END: 690 { 691 if (fSelected) 692 fSelected->fSelected = false; 693 694 fSelected = static_cast<BMenuItem *>(fItems.LastItem()); 695 fSelected->fSelected = true; 696 697 break; 698 } 699 case B_ENTER: 700 case B_SPACE: 701 { 702 if (fSelected) 703 InvokeItem(fSelected); 704 705 break; 706 } 707 default: 708 BView::KeyDown(bytes, numBytes); 709 } 710 } 711 712 713 void 714 BMenu::Draw(BRect updateRect) 715 { 716 DrawBackground(updateRect); 717 DrawItems(updateRect); 718 } 719 720 721 void 722 BMenu::GetPreferredSize(float *_width, float *_height) 723 { 724 ComputeLayout(0, true, false, _width, _height); 725 } 726 727 728 void 729 BMenu::ResizeToPreferred() 730 { 731 BView::ResizeToPreferred(); 732 } 733 734 735 void 736 BMenu::FrameMoved(BPoint new_position) 737 { 738 BView::FrameMoved(new_position); 739 } 740 741 742 void 743 BMenu::FrameResized(float new_width, float new_height) 744 { 745 BView::FrameResized(new_width, new_height); 746 } 747 748 749 void 750 BMenu::InvalidateLayout() 751 { 752 CacheFontInfo(); 753 LayoutItems(0); 754 Invalidate(); 755 } 756 757 758 BHandler * 759 BMenu::ResolveSpecifier(BMessage *msg, int32 index, 760 BMessage *specifier, int32 form, 761 const char *property) 762 { 763 BPropertyInfo propInfo(sPropList); 764 BHandler *target = NULL; 765 766 switch (propInfo.FindMatch(msg, 0, specifier, form, property)) { 767 case B_ERROR: 768 break; 769 770 case 0: 771 case 1: 772 case 2: 773 case 3: 774 case 4: 775 case 5: 776 case 6: 777 case 7: 778 target = this; 779 break; 780 case 8: 781 // TODO: redirect to menu 782 target = this; 783 break; 784 case 9: 785 case 10: 786 case 11: 787 case 12: 788 target = this; 789 break; 790 case 13: 791 // TODO: redirect to menuitem 792 target = this; 793 break; 794 } 795 796 if (!target) 797 target = BView::ResolveSpecifier(msg, index, specifier, form, 798 property); 799 800 return target; 801 } 802 803 804 status_t 805 BMenu::GetSupportedSuites(BMessage *data) 806 { 807 if (data == NULL) 808 return B_BAD_VALUE; 809 810 status_t err = data->AddString("suites", "suite/vnd.Be-menu"); 811 812 if (err < B_OK) 813 return err; 814 815 BPropertyInfo prop_info(sPropList); 816 err = data->AddFlat("messages", &prop_info); 817 818 if (err < B_OK) 819 return err; 820 821 return BView::GetSupportedSuites(data); 822 } 823 824 825 status_t 826 BMenu::Perform(perform_code d, void *arg) 827 { 828 return BView::Perform(d, arg); 829 } 830 831 832 void 833 BMenu::MakeFocus(bool focused) 834 { 835 BView::MakeFocus(focused); 836 } 837 838 839 void 840 BMenu::AllAttached() 841 { 842 BView::AllAttached(); 843 } 844 845 846 void 847 BMenu::AllDetached() 848 { 849 BView::AllDetached(); 850 } 851 852 853 BMenu::BMenu(BRect frame, const char *name, uint32 resizingMode, uint32 flags, 854 menu_layout layout, bool resizeToFit) 855 : BView(frame, name, resizingMode, flags), 856 fChosenItem(NULL), 857 fSelected(NULL), 858 fCachedMenuWindow(NULL), 859 fSuper(NULL), 860 fSuperitem(NULL), 861 fAscent(-1.0f), 862 fDescent(-1.0f), 863 fFontHeight(-1.0f), 864 fState(0), 865 fLayout(layout), 866 fExtraRect(NULL), 867 fMaxContentWidth(0.0f), 868 fInitMatrixSize(NULL), 869 fExtraMenuData(NULL), 870 fTrigger(0), 871 fResizeToFit(resizeToFit), 872 fUseCachedMenuLayout(true), 873 fEnabled(true), 874 fDynamicName(false), 875 fRadioMode(false), 876 fTrackNewBounds(false), 877 fStickyMode(false), 878 fIgnoreHidden(true), 879 fTriggerEnabled(true), 880 fRedrawAfterSticky(false), 881 fAttachAborted(false) 882 { 883 InitData(NULL); 884 } 885 886 887 void 888 BMenu::SetItemMargins(float left, float top, float right, float bottom) 889 { 890 fPad.Set(left, top, right, bottom); 891 } 892 893 894 void 895 BMenu::GetItemMargins(float *left, float *top, float *right, 896 float *bottom) const 897 { 898 if (left != NULL) 899 *left = fPad.left; 900 if (top != NULL) 901 *top = fPad.top; 902 if (right != NULL) 903 *right = fPad.right; 904 if (bottom != NULL) 905 *bottom = fPad.bottom; 906 } 907 908 909 menu_layout 910 BMenu::Layout() const 911 { 912 return fLayout; 913 } 914 915 916 void 917 BMenu::Show() 918 { 919 Show(false); 920 } 921 922 923 void 924 BMenu::Show(bool selectFirst) 925 { 926 _show(selectFirst); 927 } 928 929 930 void 931 BMenu::Hide() 932 { 933 _hide(); 934 } 935 936 937 BMenuItem * 938 BMenu::Track(bool openAnyway, BRect *clickToOpenRect) 939 { 940 if (IsStickyPrefOn()) 941 openAnyway = false; 942 943 SetStickyMode(openAnyway); 944 945 if (LockLooper()) { 946 RedrawAfterSticky(Bounds()); 947 UnlockLooper(); 948 } 949 950 if (clickToOpenRect != NULL && LockLooper()) { 951 fExtraRect = clickToOpenRect; 952 ConvertFromScreen(fExtraRect); 953 UnlockLooper(); 954 } 955 956 int action; 957 BMenuItem *menuItem = _track(&action, -1); 958 959 SetStickyMode(false); 960 fExtraRect = NULL; 961 962 return menuItem; 963 } 964 965 966 bool 967 BMenu::AddDynamicItem(add_state s) 968 { 969 // Implemented in subclasses 970 return false; 971 } 972 973 974 void 975 BMenu::DrawBackground(BRect update) 976 { 977 BRect rect = Bounds() & update; 978 rgb_color oldColor = HighColor(); 979 980 SetHighColor(sMenuInfo.background_color); 981 FillRect(rect, B_SOLID_HIGH); 982 983 SetHighColor(oldColor); 984 } 985 986 987 void 988 BMenu::SetTrackingHook(menu_tracking_hook func, void *state) 989 { 990 delete fExtraMenuData; 991 fExtraMenuData = new _ExtraMenuData_(func, state); 992 } 993 994 995 void BMenu::_ReservedMenu3() {} 996 void BMenu::_ReservedMenu4() {} 997 void BMenu::_ReservedMenu5() {} 998 void BMenu::_ReservedMenu6() {} 999 1000 1001 BMenu & 1002 BMenu::operator=(const BMenu &) 1003 { 1004 return *this; 1005 } 1006 1007 1008 void 1009 BMenu::InitData(BMessage *data) 1010 { 1011 // TODO: Get _color, _fname, _fflt from the message, if present 1012 BFont font; 1013 font.SetFamilyAndStyle(sMenuInfo.f_family, sMenuInfo.f_style); 1014 font.SetSize(sMenuInfo.font_size); 1015 SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE); 1016 1017 SetLowColor(sMenuInfo.background_color); 1018 SetViewColor(sMenuInfo.background_color); 1019 1020 if (data != NULL) { 1021 data->FindInt32("_layout", (int32 *)&fLayout); 1022 data->FindBool("_rsize_to_fit", &fResizeToFit); 1023 data->FindBool("_disable", &fEnabled); 1024 data->FindBool("_radio", &fRadioMode); 1025 1026 bool disableTrigger = false; 1027 data->FindBool("_trig_disabled", &disableTrigger); 1028 fTriggerEnabled = !disableTrigger; 1029 1030 data->FindBool("_dyn_label", &fDynamicName); 1031 data->FindFloat("_maxwidth", &fMaxContentWidth); 1032 } 1033 } 1034 1035 1036 bool 1037 BMenu::_show(bool selectFirstItem) 1038 { 1039 // Menu windows get the BMenu's handler name 1040 fCachedMenuWindow = new BMenuWindow(Name()); 1041 fCachedMenuWindow->ChildAt(0)->AddChild(this); 1042 1043 // We're doing this here because ConvertToScreen() needs: 1044 // 1. The BView to be attached (see the above line). 1045 // 2. The looper locked or not running (the Show() call below starts the looper) 1046 if (fSuper != NULL) 1047 fSuperbounds = fSuper->ConvertToScreen(fSuper->Bounds()); 1048 1049 UpdateWindowViewSize(); 1050 fCachedMenuWindow->Show(); 1051 1052 return true; 1053 } 1054 1055 1056 void 1057 BMenu::_hide() 1058 { 1059 if (fCachedMenuWindow != NULL) { 1060 fCachedMenuWindow->Lock(); 1061 fCachedMenuWindow->ChildAt(0)->RemoveChild(this); 1062 fCachedMenuWindow->Quit(); 1063 fCachedMenuWindow = NULL; 1064 } 1065 } 1066 1067 1068 BMenuItem * 1069 BMenu::_track(int *action, long start) 1070 { 1071 // TODO: Take Sticky mode into account 1072 BPoint location; 1073 ulong buttons; 1074 BMenuItem *item = NULL; 1075 int localAction = MENU_ACT_NONE; 1076 do { 1077 if (!LockLooper()) 1078 break; 1079 1080 GetMouse(&location, &buttons); 1081 if (OverSuper(location)) { 1082 UnlockLooper(); 1083 break; 1084 } 1085 1086 item = HitTestItems(location, B_ORIGIN); 1087 1088 if (item != NULL) { 1089 if (item != fSelected) 1090 SelectItem(item); 1091 } else if (fSelected != NULL) { 1092 BPoint screenLocation = location; 1093 ConvertToScreen(&screenLocation); 1094 if (!OverSubmenu(fSelected, screenLocation)) 1095 SelectItem(NULL); 1096 } 1097 1098 if (fSelected != NULL && fSelected->Submenu() != NULL) { 1099 UnlockLooper(); 1100 1101 int submenuAction = MENU_ACT_NONE; 1102 BMenuItem *submenuItem = fSelected->Submenu()->_track(&submenuAction); 1103 if (submenuAction == MENU_ACT_CLOSE) { 1104 item = submenuItem; 1105 localAction = submenuAction; 1106 break; 1107 } 1108 1109 if (!LockLooper()) 1110 break; 1111 } 1112 1113 UnlockLooper(); 1114 1115 snooze(50000); 1116 } while (buttons != 0); 1117 1118 if (localAction == MENU_ACT_NONE) { 1119 if (buttons != 0) 1120 localAction = MENU_ACT_NONE; 1121 else 1122 localAction = MENU_ACT_CLOSE; 1123 } 1124 1125 if (action != NULL) 1126 *action = localAction; 1127 1128 if (LockLooper()) { 1129 SelectItem(NULL); 1130 UnlockLooper(); 1131 } 1132 1133 return item; 1134 } 1135 1136 1137 bool 1138 BMenu::_AddItem(BMenuItem *item, int32 index) 1139 { 1140 ASSERT(item != NULL); 1141 1142 bool locked = LockLooper(); 1143 1144 if (!fItems.AddItem(item, index)) { 1145 if (locked) 1146 UnlockLooper(); 1147 return false; 1148 } 1149 1150 item->SetSuper(this); 1151 1152 // Make sure we update the layout in case we are already attached. 1153 if (fResizeToFit && locked && Window() != NULL /*&& !Window()->IsHidden()*/) { 1154 LayoutItems(index); 1155 //UpdateWindowViewSize(); 1156 Invalidate(); 1157 } 1158 1159 // Find the root menu window, so we can install this item. 1160 // ToDo: this shouldn't be necessary - the first supermenu is 1161 // already initialized to the same window 1162 BMenu* root = this; 1163 while (root->Supermenu()) 1164 root = root->Supermenu(); 1165 1166 BWindow* window = root->Window(); 1167 1168 if (locked) 1169 UnlockLooper(); 1170 1171 // if we need to install the item in another window, we don't 1172 // want to keep our lock to prevent deadlocks 1173 1174 if (window && window->Lock()) { 1175 item->Install(window); 1176 window->Unlock(); 1177 } 1178 1179 return true; 1180 } 1181 1182 1183 bool 1184 BMenu::RemoveItems(int32 index, int32 count, BMenuItem *item, bool del) 1185 { 1186 bool success = false; 1187 bool invalidateLayout = false; 1188 1189 // The plan is simple: If we're given a BMenuItem directly, we use it 1190 // and ignore index and count. Otherwise, we use them instead. 1191 if (item != NULL) { 1192 if (fItems.RemoveItem(item)) { 1193 item->SetSuper(NULL); 1194 item->Uninstall(); 1195 if (del) 1196 delete item; 1197 success = invalidateLayout = true; 1198 } 1199 } else { 1200 // We iterate backwards because it's simpler 1201 int32 i = min_c(index + count - 1, fItems.CountItems() - 1); 1202 // NOTE: the range check for "index" is done after 1203 // calculating the last index to be removed, so 1204 // that the range is not "shifted" unintentionally 1205 index = max_c(0, index); 1206 for (; i >= index; i--) { 1207 item = static_cast<BMenuItem*>(fItems.ItemAt(i)); 1208 if (item != NULL) { 1209 if (fItems.RemoveItem(item)) { 1210 item->SetSuper(NULL); 1211 item->Uninstall(); 1212 if (del) 1213 delete item; 1214 success = true; 1215 invalidateLayout = true; 1216 } else { 1217 // operation not entirely successful 1218 success = false; 1219 break; 1220 } 1221 } 1222 } 1223 } 1224 1225 if (invalidateLayout && Window() != NULL && fResizeToFit) 1226 InvalidateLayout(); 1227 1228 return success; 1229 } 1230 1231 1232 void 1233 BMenu::LayoutItems(int32 index) 1234 { 1235 CalcTriggers(); 1236 1237 float width, height; 1238 ComputeLayout(index, true, true, &width, &height); 1239 1240 ResizeTo(width, height); 1241 1242 // Move the BMenu to 1, 1, if it's attached to a BMenuWindow, 1243 // (that means it's a BMenu, BMenuBars are attached to regular BWindows). 1244 // This is needed to be able to draw the frame around the BMenu. 1245 if (dynamic_cast<BMenuWindow *>(Window()) != NULL) 1246 MoveTo(1, 1); 1247 } 1248 1249 1250 void 1251 BMenu::ComputeLayout(int32 index, bool bestFit, bool moveItems, 1252 float* _width, float* _height) 1253 { 1254 // TODO: Take "bestFit", "moveItems", "index" into account, 1255 // Recalculate only the needed items, 1256 // not the whole layout every time 1257 1258 BRect frame(0, 0, 0, 0); 1259 float iWidth, iHeight; 1260 BMenuItem *item = NULL; 1261 1262 switch (fLayout) { 1263 case B_ITEMS_IN_COLUMN: 1264 { 1265 for (int32 i = 0; i < fItems.CountItems(); i++) { 1266 item = ItemAt(i); 1267 if (item != NULL) { 1268 item->GetContentSize(&iWidth, &iHeight); 1269 1270 if (item->fModifiers && item->fShortcutChar) 1271 iWidth += 25.0f; 1272 1273 item->fBounds.left = 0.0f; 1274 item->fBounds.top = frame.bottom; 1275 item->fBounds.bottom = item->fBounds.top + iHeight + fPad.top + fPad.bottom; 1276 1277 frame.right = max_c(frame.right, iWidth + fPad.left + fPad.right + 20); 1278 frame.bottom = item->fBounds.bottom + 1.0f; 1279 } 1280 } 1281 1282 for (int32 i = 0; i < fItems.CountItems(); i++) 1283 ItemAt(i)->fBounds.right = frame.right; 1284 1285 frame.right = (float)ceil(frame.right); 1286 frame.bottom--; 1287 break; 1288 } 1289 1290 case B_ITEMS_IN_ROW: 1291 { 1292 font_height fh; 1293 GetFontHeight(&fh); 1294 frame = BRect(0.0f, 0.0f, 0.0f, 1295 (float)ceil(fh.ascent) + (float)ceil(fh.descent) + fPad.top + fPad.bottom); 1296 1297 for (int32 i = 0; i < fItems.CountItems(); i++) { 1298 item = ItemAt(i); 1299 if (item != NULL) { 1300 item->GetContentSize(&iWidth, &iHeight); 1301 1302 item->fBounds.left = frame.right; 1303 item->fBounds.top = 0.0f; 1304 item->fBounds.right = item->fBounds.left + iWidth + fPad.left + fPad.right; 1305 1306 frame.right = item->Frame().right + 1.0f; 1307 frame.bottom = max_c(frame.bottom, iHeight + fPad.top + fPad.bottom); 1308 } 1309 } 1310 1311 for (int32 i = 0; i < fItems.CountItems(); i++) 1312 ItemAt(i)->fBounds.bottom = frame.bottom; 1313 1314 frame.right = (float)ceil(frame.right) + 8.0f; 1315 break; 1316 } 1317 1318 case B_ITEMS_IN_MATRIX: 1319 { 1320 for (int32 i = 0; i < CountItems(); i++) { 1321 item = ItemAt(i); 1322 if (item != NULL) { 1323 frame.left = min_c(frame.left, item->Frame().left); 1324 frame.right = max_c(frame.right, item->Frame().right); 1325 frame.top = min_c(frame.top, item->Frame().top); 1326 frame.bottom = max_c(frame.bottom, item->Frame().bottom); 1327 } 1328 } 1329 break; 1330 } 1331 1332 default: 1333 break; 1334 } 1335 1336 // This is for BMenuBar. 1337 if ((ResizingMode() & B_FOLLOW_LEFT_RIGHT) == B_FOLLOW_LEFT_RIGHT) { 1338 if (Parent()) 1339 *_width = Parent()->Frame().Width() + 1; 1340 else if (Window()) 1341 *_width = Window()->Frame().Width() + 1; 1342 else 1343 *_width = Bounds().Width(); 1344 } else 1345 *_width = frame.Width(); 1346 1347 *_height = frame.Height(); 1348 } 1349 1350 1351 BRect 1352 BMenu::Bump(BRect current, BPoint extent, int32 index) const 1353 { 1354 // ToDo: what's this? 1355 return BRect(); 1356 } 1357 1358 1359 BPoint 1360 BMenu::ItemLocInRect(BRect frame) const 1361 { 1362 // ToDo: what's this? 1363 return BPoint(); 1364 } 1365 1366 1367 BPoint 1368 BMenu::ScreenLocation() 1369 { 1370 BMenu *superMenu = Supermenu(); 1371 BMenuItem *superItem = Superitem(); 1372 1373 if (superMenu == NULL && superItem == NULL) { 1374 debugger("BMenu can't determine where to draw." 1375 "Override BMenu::ScreenLocation() to determine location."); 1376 } 1377 1378 BPoint point; 1379 if (superMenu->Layout() == B_ITEMS_IN_COLUMN) 1380 point = superItem->Frame().RightTop() + BPoint(1.0f, 1.0f); 1381 else 1382 point = superItem->Frame().LeftBottom() + BPoint(1.0f, 1.0f); 1383 1384 superMenu->ConvertToScreen(&point); 1385 1386 return point; 1387 } 1388 1389 1390 BRect 1391 BMenu::CalcFrame(BPoint where, bool *scrollOn) 1392 { 1393 // TODO: Improve me 1394 BRect bounds = Bounds(); 1395 BRect frame = bounds.OffsetToCopy(where); 1396 1397 BScreen screen(Window()); 1398 BRect screenFrame = screen.Frame(); 1399 1400 BMenu *superMenu = Supermenu(); 1401 BMenuItem *superItem = Superitem(); 1402 1403 if (superMenu == NULL || superItem == NULL) { 1404 // just move the window on screen 1405 1406 if (frame.bottom > screenFrame.bottom) 1407 frame.OffsetBy(0, screenFrame.bottom - frame.bottom); 1408 1409 if (frame.right > screenFrame.right) 1410 frame.OffsetBy(screenFrame.right - frame.right, 0); 1411 1412 return frame; 1413 } 1414 1415 if (superMenu->Layout() == B_ITEMS_IN_COLUMN) { 1416 if (frame.right > screenFrame.right) 1417 frame.OffsetBy(-superItem->Frame().Width() - frame.Width() - 2, 0); 1418 1419 if (frame.bottom > screenFrame.bottom) 1420 frame.OffsetBy(0, screenFrame.bottom - frame.bottom); 1421 } else { 1422 if (frame.bottom > screenFrame.bottom) 1423 frame.OffsetBy(0, -superItem->Frame().Height() - frame.Height() - 3); 1424 1425 if (frame.right > screenFrame.right) 1426 frame.OffsetBy(screenFrame.right - frame.right, 0); 1427 } 1428 1429 return frame; 1430 } 1431 1432 1433 bool 1434 BMenu::ScrollMenu(BRect bounds, BPoint loc, bool *fast) 1435 { 1436 return false; 1437 } 1438 1439 1440 void 1441 BMenu::ScrollIntoView(BMenuItem *item) 1442 { 1443 } 1444 1445 1446 void 1447 BMenu::DrawItems(BRect updateRect) 1448 { 1449 for (int32 i = 0; i < fItems.CountItems(); i++) { 1450 BMenuItem *item = ItemAt(i); 1451 if (item->Frame().Intersects(updateRect)) 1452 item->Draw(); 1453 } 1454 } 1455 1456 1457 int 1458 BMenu::State(BMenuItem **item) const 1459 { 1460 return 0; 1461 } 1462 1463 1464 void 1465 BMenu::InvokeItem(BMenuItem *item, bool now) 1466 { 1467 if (item->Submenu()) 1468 item->Submenu()->Show(); 1469 else if (IsRadioMode()) 1470 item->SetMarked(true); 1471 1472 item->Invoke(); 1473 } 1474 1475 1476 bool 1477 BMenu::OverSuper(BPoint location) 1478 { 1479 if (!Supermenu()) 1480 return false; 1481 1482 ConvertToScreen(&location); 1483 1484 return fSuperbounds.Contains(location); 1485 } 1486 1487 1488 bool 1489 BMenu::OverSubmenu(BMenuItem *item, BPoint loc) 1490 { 1491 // we assume that loc is in screen coords 1492 BMenu *subMenu = item->Submenu(); 1493 if (subMenu == NULL || subMenu->Window() == NULL) 1494 return false; 1495 1496 if (subMenu->Window()->Frame().Contains(loc)) 1497 return true; 1498 1499 if (subMenu->fSelected == NULL) 1500 return false; 1501 1502 return subMenu->OverSubmenu(subMenu->fSelected, loc); 1503 } 1504 1505 1506 BMenuWindow * 1507 BMenu::MenuWindow() 1508 { 1509 return fCachedMenuWindow; 1510 } 1511 1512 1513 void 1514 BMenu::DeleteMenuWindow() 1515 { 1516 if (fCachedMenuWindow != NULL) { 1517 fCachedMenuWindow->Lock(); 1518 fCachedMenuWindow->Quit(); 1519 fCachedMenuWindow = NULL; 1520 } 1521 } 1522 1523 1524 BMenuItem * 1525 BMenu::HitTestItems(BPoint where, BPoint slop) const 1526 { 1527 // TODO: Take "slop" into account ? 1528 int32 itemCount = CountItems(); 1529 for (int32 i = 0; i < itemCount; i++) { 1530 BMenuItem *item = ItemAt(i); 1531 if (item->Frame().Contains(where)) 1532 return item; 1533 } 1534 1535 return NULL; 1536 } 1537 1538 1539 BRect 1540 BMenu::Superbounds() const 1541 { 1542 return fSuperbounds; 1543 } 1544 1545 1546 void 1547 BMenu::CacheFontInfo() 1548 { 1549 font_height fh; 1550 GetFontHeight(&fh); 1551 fAscent = fh.ascent; 1552 fDescent = fh.descent; 1553 fFontHeight = (float)ceil(fh.ascent + fh.descent + fh.leading); 1554 } 1555 1556 1557 void 1558 BMenu::ItemMarked(BMenuItem *item) 1559 { 1560 if (IsRadioMode()) { 1561 for (int32 i = 0; i < CountItems(); i++) 1562 if (ItemAt(i) != item) 1563 ItemAt(i)->SetMarked(false); 1564 } 1565 1566 if (IsLabelFromMarked() && Superitem()) 1567 Superitem()->SetLabel(item->Label()); 1568 } 1569 1570 1571 void 1572 BMenu::Install(BWindow *target) 1573 { 1574 for (int32 i = 0; i < CountItems(); i++) 1575 ItemAt(i)->Install(target); 1576 } 1577 1578 1579 void 1580 BMenu::Uninstall() 1581 { 1582 for (int32 i = 0; i < CountItems(); i++) 1583 ItemAt(i)->Uninstall(); 1584 } 1585 1586 1587 void 1588 BMenu::SelectItem(BMenuItem *menuItem, uint32 showSubmenu, bool selectFirstItem) 1589 { 1590 // TODO: make use of "selectFirstItem" 1591 if (fSelected != NULL) { 1592 fSelected->Select(false); 1593 BMenu *subMenu = fSelected->Submenu(); 1594 if (subMenu != NULL && subMenu->Window() != NULL) 1595 subMenu->_hide(); 1596 } 1597 1598 if (menuItem != NULL) 1599 menuItem->Select(true); 1600 1601 fSelected = menuItem; 1602 if (fSelected != NULL && showSubmenu == 0) { 1603 BMenu *subMenu = fSelected->Submenu(); 1604 if (subMenu != NULL && subMenu->Window() == NULL) 1605 subMenu->_show(); 1606 } 1607 } 1608 1609 1610 BMenuItem * 1611 BMenu::CurrentSelection() const 1612 { 1613 return fSelected; 1614 } 1615 1616 1617 bool 1618 BMenu::SelectNextItem(BMenuItem *item, bool forward) 1619 { 1620 BMenuItem *nextItem = NextItem(item, forward); 1621 if (nextItem == NULL) 1622 return false; 1623 1624 SelectItem(nextItem); 1625 return true; 1626 } 1627 1628 1629 BMenuItem * 1630 BMenu::NextItem(BMenuItem *item, bool forward) const 1631 { 1632 int32 index = fItems.IndexOf(item); 1633 if (forward) 1634 index++; 1635 else 1636 index--; 1637 1638 if (index < 0 || index >= fItems.CountItems()) 1639 return NULL; 1640 1641 return ItemAt(index); 1642 } 1643 1644 1645 bool 1646 BMenu::IsItemVisible(BMenuItem *item) const 1647 { 1648 BRect itemFrame = item->Frame(); 1649 ConvertToScreen(&itemFrame); 1650 1651 BRect visibilityFrame = Window()->Frame() & BScreen(Window()).Frame(); 1652 1653 return visibilityFrame.Intersects(itemFrame); 1654 } 1655 1656 1657 void 1658 BMenu::SetIgnoreHidden(bool on) 1659 { 1660 fIgnoreHidden = on; 1661 } 1662 1663 1664 void 1665 BMenu::SetStickyMode(bool on) 1666 { 1667 fStickyMode = on; 1668 } 1669 1670 1671 bool 1672 BMenu::IsStickyMode() const 1673 { 1674 return fStickyMode; 1675 } 1676 1677 1678 void 1679 BMenu::CalcTriggers() 1680 { 1681 BList triggersList; 1682 1683 // Gathers the existing triggers 1684 // TODO: Oh great, reinterpret_cast. 1685 for (int32 i = 0; i < CountItems(); i++) { 1686 char trigger = ItemAt(i)->Trigger(); 1687 if (trigger != 0) 1688 triggersList.AddItem(reinterpret_cast<void *>((uint32)trigger)); 1689 } 1690 1691 // Set triggers for items which don't have one yet 1692 for (int32 i = 0; i < CountItems(); i++) { 1693 BMenuItem *item = ItemAt(i); 1694 if (item->Trigger() == 0) { 1695 const char *newTrigger = ChooseTrigger(item->Label(), &triggersList); 1696 if (newTrigger != NULL) { 1697 item->SetSysTrigger(*newTrigger); 1698 // TODO: This is crap. I'd prefer to have 1699 // BMenuItem::SetSysTrigger(const char *) update fTriggerIndex. 1700 // This isn't the case on beos, but it will probably be like that on haiku. 1701 item->fTriggerIndex = newTrigger - item->Label(); 1702 } 1703 } 1704 } 1705 } 1706 1707 1708 const char * 1709 BMenu::ChooseTrigger(const char *title, BList *chars) 1710 { 1711 ASSERT(chars != NULL); 1712 1713 if (title == NULL) 1714 return NULL; 1715 1716 char *titlePtr = const_cast<char *>(title); 1717 1718 char trigger; 1719 // TODO: Oh great, reinterpret_cast all around 1720 while ((trigger = *titlePtr) != '\0') { 1721 if (!chars->HasItem(reinterpret_cast<void *>((uint32)trigger))) { 1722 chars->AddItem(reinterpret_cast<void *>((uint32)trigger)); 1723 return titlePtr; 1724 } 1725 1726 titlePtr++; 1727 } 1728 1729 return NULL; 1730 } 1731 1732 1733 void 1734 BMenu::UpdateWindowViewSize(bool upWind) 1735 { 1736 ASSERT(fCachedMenuWindow != NULL); 1737 1738 bool scroll; 1739 BRect frame = CalcFrame(ScreenLocation(), &scroll); 1740 ResizeTo(frame.Width(), frame.Height()); 1741 1742 fCachedMenuWindow->ResizeTo(Bounds().Width() + 2, Bounds().Height() + 2); 1743 fCachedMenuWindow->MoveTo(frame.LeftTop()); 1744 } 1745 1746 1747 bool 1748 BMenu::IsStickyPrefOn() 1749 { 1750 return sMenuInfo.click_to_open; 1751 } 1752 1753 1754 void 1755 BMenu::RedrawAfterSticky(BRect bounds) 1756 { 1757 } 1758 1759 1760 bool 1761 BMenu::OkToProceed(BMenuItem* item) 1762 { 1763 // ToDo: test if the window could be closed again already 1764 1765 // ToDo: for now 1766 return true; 1767 } 1768 1769 1770 status_t 1771 BMenu::ParseMsg(BMessage *msg, int32 *sindex, BMessage *spec, 1772 int32 *form, const char **prop, BMenu **tmenu, 1773 BMenuItem **titem, int32 *user_data, 1774 BMessage *reply) const 1775 { 1776 return B_ERROR; 1777 } 1778 1779 1780 status_t 1781 BMenu::DoMenuMsg(BMenuItem **next, BMenu *tar, BMessage *m, 1782 BMessage *r, BMessage *spec, int32 f) const 1783 { 1784 return B_ERROR; 1785 } 1786 1787 1788 status_t 1789 BMenu::DoMenuItemMsg(BMenuItem **next, BMenu *tar, BMessage *m, 1790 BMessage *r, BMessage *spec, int32 f) const 1791 { 1792 return B_ERROR; 1793 } 1794 1795 1796 status_t 1797 BMenu::DoEnabledMsg(BMenuItem *ti, BMenu *tm, BMessage *m, 1798 BMessage *r) const 1799 { 1800 return B_ERROR; 1801 } 1802 1803 1804 status_t 1805 BMenu::DoLabelMsg(BMenuItem *ti, BMenu *tm, BMessage *m, 1806 BMessage *r) const 1807 { 1808 return B_ERROR; 1809 } 1810 1811 1812 status_t 1813 BMenu::DoMarkMsg(BMenuItem *ti, BMenu *tm, BMessage *m, 1814 BMessage *r) const 1815 { 1816 return B_ERROR; 1817 } 1818 1819 1820 status_t 1821 BMenu::DoDeleteMsg(BMenuItem *ti, BMenu *tm, BMessage *m, 1822 BMessage *r) const 1823 { 1824 return B_ERROR; 1825 } 1826 1827 1828 status_t 1829 BMenu::DoCreateMsg(BMenuItem *ti, BMenu *tm, BMessage *m, 1830 BMessage *r, bool menu) const 1831 { 1832 return B_ERROR; 1833 } 1834 1835 1836 status_t 1837 set_menu_info(menu_info *info) 1838 { 1839 if (!info) 1840 return B_BAD_VALUE; 1841 1842 BPath path; 1843 1844 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 1845 return B_OK; 1846 1847 path.Append("menu_settings"); 1848 1849 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE); 1850 1851 if (file.InitCheck() != B_OK) 1852 return B_OK; 1853 1854 file.Write(info, sizeof(menu_info)); 1855 1856 BMenu::sMenuInfo = *info; 1857 1858 return B_OK; 1859 } 1860 1861 1862 status_t 1863 get_menu_info(menu_info *info) 1864 { 1865 if (!info) 1866 return B_BAD_VALUE; 1867 1868 *info = BMenu::sMenuInfo; 1869 1870 return B_OK; 1871 } 1872