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