1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2002, OpenBeOS 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: BMenuItem.cpp 23 // Author: Marc Flerackers (mflerackers@androme.be) 24 // Bill Hayden (haydentech@users.sourceforge.net) 25 // Description: Display item for BMenu class 26 // 27 //------------------------------------------------------------------------------ 28 29 // Standard Includes ----------------------------------------------------------- 30 #include <string.h> 31 #include <stdlib.h> 32 33 // System Includes ------------------------------------------------------------- 34 #include <MenuItem.h> 35 #include <String.h> 36 #include <Message.h> 37 #include <Window.h> 38 #include <Bitmap.h> 39 40 // Project Includes ------------------------------------------------------------ 41 42 // Local Includes -------------------------------------------------------------- 43 44 // Local Defines --------------------------------------------------------------- 45 46 // Globals --------------------------------------------------------------------- 47 48 //------------------------------------------------------------------------------ 49 BMenuItem::BMenuItem(const char *label, BMessage *message, char shortcut, 50 uint32 modifiers) 51 { 52 InitData(); 53 fLabel = strdup(label); 54 SetMessage(message); 55 56 fShortcutChar = shortcut; 57 58 if (shortcut != 0) 59 fModifiers = modifiers | B_COMMAND_KEY; 60 else 61 fModifiers = 0; 62 } 63 //------------------------------------------------------------------------------ 64 BMenuItem::BMenuItem(BMenu *menu, BMessage *message) 65 { 66 InitData(); 67 SetMessage(message); 68 InitMenuData(menu); 69 } 70 //------------------------------------------------------------------------------ 71 BMenuItem::BMenuItem(BMessage *data) 72 { 73 InitData(); 74 75 if (data->HasString("_label")) 76 { 77 const char *string; 78 79 data->FindString("_label", &string); 80 SetLabel(string); 81 } 82 83 bool disable; 84 if (data->FindBool("_disable", &disable) == B_OK) 85 SetEnabled(!disable); 86 87 bool marked; 88 if (data->FindBool("_marked", &marked) == B_OK) 89 SetMarked(marked); 90 91 if (data->HasInt32("_user_trig")) 92 { 93 int32 user_trig; 94 95 data->FindInt32("_user_trig", &user_trig); 96 97 SetTrigger(user_trig); 98 } 99 100 if (data->HasInt32("_shortcut")) 101 { 102 int32 shortcut, mods; 103 104 data->FindInt32("_shortcut", &shortcut); 105 data->FindInt32("_mods", &mods); 106 107 SetShortcut(shortcut, mods); 108 } 109 110 if (data->HasMessage("_msg")) 111 { 112 BMessage *msg = new BMessage; 113 114 data->FindMessage("_msg", msg); 115 SetMessage(msg); 116 } 117 118 BMessage subMessage; 119 120 if (data->FindMessage("_submenu", &subMessage) == B_OK) 121 { 122 BArchivable *object = instantiate_object(&subMessage); 123 124 if (object) 125 { 126 BMenu *menu = dynamic_cast<BMenu*>(object); 127 128 if (menu) 129 InitMenuData(menu); 130 } 131 } 132 } 133 //------------------------------------------------------------------------------ 134 BArchivable *BMenuItem::Instantiate(BMessage *data) 135 { 136 if (validate_instantiation(data, "BMenuItem")) 137 return new BMenuItem(data); 138 else 139 return NULL; 140 } 141 //------------------------------------------------------------------------------ 142 status_t BMenuItem::Archive(BMessage *data, bool deep) const 143 { 144 if (fLabel) 145 data->AddString("_label", Label()); 146 147 if (!IsEnabled()) 148 data->AddBool("_disable", true); 149 150 if (IsMarked()) 151 data->AddBool("_marked", true); 152 153 if (fUserTrigger) 154 data->AddInt32("_user_trig", fUserTrigger); 155 156 if (fShortcutChar) 157 { 158 data->AddInt32("_shortcut", fShortcutChar); 159 data->AddInt32("_mods", fModifiers); 160 } 161 162 if (Message()) 163 data->AddMessage("_msg", Message()); 164 165 if (deep && fSubmenu) 166 { 167 BMessage submenu; 168 169 if (fSubmenu->Archive(&submenu, true) == B_OK) 170 data->AddMessage("_submenu", &submenu); 171 } 172 173 return B_OK; 174 } 175 //------------------------------------------------------------------------------ 176 BMenuItem::~BMenuItem() 177 { 178 if (fLabel) 179 free(fLabel); 180 181 if (fSubmenu) 182 delete fSubmenu; 183 } 184 //------------------------------------------------------------------------------ 185 void BMenuItem::SetLabel(const char *string) 186 { 187 if (fLabel) 188 free(fLabel); 189 190 if (string) 191 fLabel = strdup(string); 192 else 193 string = NULL; 194 195 if (fSuper) 196 { 197 fSuper->InvalidateLayout(); 198 199 if (fSuper->LockLooper()) 200 { 201 fSuper->Invalidate(); 202 fSuper->UnlockLooper(); 203 } 204 } 205 } 206 //------------------------------------------------------------------------------ 207 void BMenuItem::SetEnabled(bool state) 208 { 209 if (fSubmenu) 210 fSubmenu->SetEnabled(state); 211 212 fEnabled = state; 213 BMenu *menu = Menu(); 214 215 if (menu && menu->LockLooper()) 216 { 217 menu->Invalidate(fBounds); 218 menu->UnlockLooper(); 219 } 220 } 221 //------------------------------------------------------------------------------ 222 void BMenuItem::SetMarked(bool state) 223 { 224 fMark = state; 225 226 if (state && Menu()) 227 Menu()->ItemMarked(this); 228 } 229 //------------------------------------------------------------------------------ 230 void BMenuItem::SetTrigger(char ch) 231 { 232 fUserTrigger = ch; 233 234 if (strchr(fLabel, ch) != 0) 235 fSysTrigger = ch; 236 else 237 fSysTrigger = -1; 238 239 if (fSuper) 240 fSuper->InvalidateLayout(); 241 } 242 //------------------------------------------------------------------------------ 243 void BMenuItem::SetShortcut(char ch, uint32 modifiers) 244 { 245 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow) 246 fWindow->RemoveShortcut(fShortcutChar, fModifiers); 247 248 fShortcutChar = ch; 249 250 if (ch != 0) 251 fModifiers = modifiers | B_COMMAND_KEY; 252 else 253 fModifiers = 0; 254 255 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow) 256 fWindow->AddShortcut(fShortcutChar, fModifiers, this); 257 258 if (fSuper) 259 { 260 fSuper->InvalidateLayout(); 261 262 if (fSuper->LockLooper()) 263 { 264 fSuper->Invalidate(); 265 fSuper->UnlockLooper(); 266 } 267 } 268 } 269 //------------------------------------------------------------------------------ 270 const char *BMenuItem::Label() const 271 { 272 return fLabel; 273 } 274 //------------------------------------------------------------------------------ 275 bool BMenuItem::IsEnabled() const 276 { 277 if (fSubmenu) 278 return fSubmenu->IsEnabled(); 279 280 if (!fEnabled) 281 return false; 282 283 return fSuper ? fSuper->IsEnabled() : true; 284 } 285 //------------------------------------------------------------------------------ 286 bool BMenuItem::IsMarked() const 287 { 288 return fMark; 289 } 290 //------------------------------------------------------------------------------ 291 char BMenuItem::Trigger() const 292 { 293 return fUserTrigger; 294 } 295 //------------------------------------------------------------------------------ 296 char BMenuItem::Shortcut(uint32 *modifiers) const 297 { 298 if (modifiers) 299 *modifiers = fModifiers; 300 301 return fShortcutChar; 302 } 303 //------------------------------------------------------------------------------ 304 BMenu *BMenuItem::Submenu() const 305 { 306 return fSubmenu; 307 } 308 //------------------------------------------------------------------------------ 309 BMenu *BMenuItem::Menu() const 310 { 311 return fSuper; 312 } 313 //------------------------------------------------------------------------------ 314 BRect BMenuItem::Frame() const 315 { 316 return fBounds; 317 } 318 //------------------------------------------------------------------------------ 319 void BMenuItem::GetContentSize(float *width, float *height) 320 { 321 fSuper->CacheFontInfo(); 322 323 fCachedWidth = fSuper->StringWidth(fLabel); 324 325 *width = (float)ceil(fCachedWidth); 326 *height = fSuper->fFontHeight; 327 } 328 //------------------------------------------------------------------------------ 329 void BMenuItem::TruncateLabel(float maxWidth, char *newLabel) 330 { 331 // ToDo: implement me! 332 } 333 //------------------------------------------------------------------------------ 334 void BMenuItem::DrawContent() 335 { 336 fSuper->MovePenBy(0, fSuper->fAscent); 337 fSuper->DrawString(fLabel); 338 339 // ToDo: label truncation is missing 340 // ToDo: draw trigger is missing! 341 } 342 //------------------------------------------------------------------------------ 343 void BMenuItem::Draw() 344 { 345 bool enabled = IsEnabled(); 346 347 fSuper->CacheFontInfo(); 348 349 if (IsSelected() && (enabled || Submenu())/* && fSuper->fRedrawAfterSticky*/) 350 { 351 fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 352 B_DARKEN_2_TINT)); 353 fSuper->SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 354 B_DARKEN_2_TINT)); 355 fSuper->FillRect(Frame()); 356 } 357 358 if (IsEnabled()) 359 fSuper->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR)); 360 else if (IsSelected()) 361 fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 362 B_LIGHTEN_1_TINT)); 363 else 364 fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 365 B_DISABLED_LABEL_TINT)); 366 367 fSuper->MovePenTo(ContentLocation()); 368 DrawContent(); 369 370 if (fSuper->Layout() == B_ITEMS_IN_COLUMN) 371 { 372 if (IsMarked()) 373 DrawMarkSymbol(); 374 375 if (fShortcutChar) 376 DrawShortcutSymbol(); 377 378 if (Submenu()) 379 DrawSubmenuSymbol(); 380 } 381 } 382 //------------------------------------------------------------------------------ 383 void BMenuItem::Highlight(bool flag) 384 { 385 Menu()->Draw(Frame()); 386 } 387 //------------------------------------------------------------------------------ 388 bool BMenuItem::IsSelected() const 389 { 390 return fSelected; 391 } 392 //------------------------------------------------------------------------------ 393 BPoint BMenuItem::ContentLocation() const 394 { 395 return BPoint(fBounds.left + Menu()->fPad.left, 396 fBounds.top + Menu()->fPad.top); 397 } 398 //------------------------------------------------------------------------------ 399 void BMenuItem::_ReservedMenuItem1() {} 400 void BMenuItem::_ReservedMenuItem2() {} 401 void BMenuItem::_ReservedMenuItem3() {} 402 void BMenuItem::_ReservedMenuItem4() {} 403 //------------------------------------------------------------------------------ 404 BMenuItem::BMenuItem(const BMenuItem &) 405 { 406 } 407 //------------------------------------------------------------------------------ 408 BMenuItem &BMenuItem::operator=(const BMenuItem &) 409 { 410 return *this; 411 } 412 //------------------------------------------------------------------------------ 413 void BMenuItem::InitData() 414 { 415 fLabel = NULL; 416 fSubmenu = 0; 417 fWindow = NULL; 418 fSuper = NULL; 419 fModifiers = 0; 420 fCachedWidth = 0; 421 fTriggerIndex = -1; 422 fUserTrigger = 0; 423 fSysTrigger = 0; 424 fShortcutChar = 0; 425 fMark = false; 426 fEnabled = true; 427 fSelected = false; 428 } 429 //------------------------------------------------------------------------------ 430 void BMenuItem::InitMenuData(BMenu *menu) 431 { 432 fSubmenu = menu; 433 fSubmenu->fSuperitem = this; 434 435 BMenuItem *item; 436 437 if (menu->IsRadioMode() && menu->IsLabelFromMarked() && 438 (item = menu->FindMarked()) != NULL) 439 SetLabel(item->Label()); 440 else 441 SetLabel(menu->Name()); 442 } 443 //------------------------------------------------------------------------------ 444 void BMenuItem::Install(BWindow *window) 445 { 446 if (fSubmenu) 447 fSubmenu->Install(window); 448 449 fWindow = window; 450 451 if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow) 452 window->AddShortcut(fShortcutChar, fModifiers, this); 453 454 if (!Messenger().IsValid()) 455 SetTarget(window); 456 } 457 //------------------------------------------------------------------------------ 458 status_t BMenuItem::Invoke(BMessage *message) 459 { 460 if (!IsEnabled()) 461 return B_ERROR; 462 463 if (fSuper->IsRadioMode()) 464 SetMarked(true); 465 466 bool notify = false; 467 uint32 kind = InvokeKind(¬ify); 468 469 BMessage clone(kind); 470 status_t err = B_BAD_VALUE; 471 472 if (!message && !notify) 473 message = Message(); 474 475 if (!message) 476 { 477 if (!fSuper->IsWatched()) 478 return err; 479 } 480 else 481 clone = *message; 482 483 clone.AddInt32("index", Menu()->IndexOf(this)); 484 clone.AddInt64("when", (int64)system_time()); 485 clone.AddPointer("source", this); 486 clone.AddMessenger("be:sender", BMessenger(fSuper)); 487 488 if (message) 489 err = BInvoker::Invoke(&clone); 490 491 // TODO: assynchronous messaging 492 // SendNotices(kind, &clone); 493 494 return err; 495 } 496 //------------------------------------------------------------------------------ 497 void BMenuItem::Uninstall() 498 { 499 if (fSubmenu) 500 fSubmenu->Uninstall(); 501 502 if (Target() == fWindow) 503 SetTarget(BMessenger()); 504 505 if (0x6c != 0 && fModifiers & B_COMMAND_KEY && fWindow) 506 fWindow->RemoveShortcut(fShortcutChar, fModifiers); 507 508 fWindow = NULL; 509 } 510 //------------------------------------------------------------------------------ 511 void BMenuItem::SetSuper(BMenu *super) 512 { 513 if (fSuper != NULL && super != NULL) 514 { 515 debugger("Error - can't add menu or menu item to more than 1 container (either menu or menubar)."); 516 return; 517 } 518 519 fSuper = super; 520 521 if (fSubmenu) 522 fSubmenu->fSuper = super; 523 } 524 //------------------------------------------------------------------------------ 525 void BMenuItem::Select(bool on) 526 { 527 if (Submenu()) 528 { 529 fSelected = on; 530 Highlight(on); 531 } 532 else if (IsEnabled()) 533 { 534 fSelected = on; 535 Highlight(on); 536 } 537 538 } 539 //------------------------------------------------------------------------------ 540 void BMenuItem::DrawMarkSymbol() 541 { 542 fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 543 B_DARKEN_1_TINT)); 544 fSuper->StrokeLine(BPoint(fBounds.left + 6.0f, fBounds.bottom - 3.0f), 545 BPoint(fBounds.left + 10.0f, fBounds.bottom - 12.0f)); 546 fSuper->StrokeLine(BPoint(fBounds.left + 7.0f, fBounds.bottom - 3.0f), 547 BPoint(fBounds.left + 11.0f, fBounds.bottom - 12.0f)); 548 549 fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 550 B_DARKEN_4_TINT)); 551 552 fSuper->StrokeLine(BPoint(fBounds.left + 6.0f, fBounds.bottom - 4.0f), 553 BPoint(fBounds.left + 10.0f, fBounds.bottom - 13.0f)); 554 fSuper->StrokeLine(BPoint(fBounds.left + 5.0f, fBounds.bottom - 4.0f), 555 BPoint(fBounds.left + 9.0f, fBounds.bottom - 13.0f)); 556 fSuper->StrokeLine(BPoint(fBounds.left + 5.0f, fBounds.bottom - 3.0f), 557 BPoint(fBounds.left + 3.0f, fBounds.bottom - 9.0f)); 558 fSuper->StrokeLine(BPoint(fBounds.left + 4.0f, fBounds.bottom - 4.0f), 559 BPoint(fBounds.left + 2.0f, fBounds.bottom - 9.0f)); 560 } 561 //------------------------------------------------------------------------------ 562 void BMenuItem::DrawShortcutSymbol() 563 { 564 BString shortcut(""); 565 566 if (fModifiers & B_CONTROL_KEY) 567 shortcut += "ctl+"; 568 569 shortcut += fShortcutChar; 570 571 fSuper->DrawString(shortcut.String(), ContentLocation() + 572 BPoint(fBounds.Width() - 14.0f - 32.0f, fBounds.Height() - 4.0f)); 573 } 574 //------------------------------------------------------------------------------ 575 void BMenuItem::DrawSubmenuSymbol() 576 { 577 fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 578 B_LIGHTEN_MAX_TINT)); 579 fSuper->FillTriangle(BPoint(fBounds.right - 14.0f, fBounds.bottom - 4.0f), 580 BPoint(fBounds.right - 14.0f, fBounds.bottom - 12.0f), 581 BPoint(fBounds.right - 5.0f, fBounds.bottom - 8.0f)); 582 583 fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 584 B_DARKEN_2_TINT)); 585 fSuper->StrokeLine(BPoint(fBounds.right - 14.0f, fBounds.bottom - 5), 586 BPoint(fBounds.right - 9.0f, fBounds.bottom - 7)); 587 fSuper->StrokeLine(BPoint(fBounds.right - 7.0f, fBounds.bottom - 8), 588 BPoint(fBounds.right - 7.0f, fBounds.bottom - 8)); 589 590 fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 591 B_DARKEN_3_TINT)); 592 fSuper->StrokeTriangle(BPoint(fBounds.right - 14.0f, fBounds.bottom - 4.0f), 593 BPoint(fBounds.right - 14.0f, fBounds.bottom - 12.0f), 594 BPoint(fBounds.right - 5.0f, fBounds.bottom - 8.0f)); 595 596 fSuper->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 597 B_LIGHTEN_1_TINT)); 598 fSuper->StrokeTriangle(BPoint(fBounds.right - 12.0f, fBounds.bottom - 7.0f), 599 BPoint(fBounds.right - 12.0f, fBounds.bottom - 9.0f), 600 BPoint(fBounds.right - 9.0f, fBounds.bottom - 8.0f)); 601 fSuper->FillTriangle(BPoint(fBounds.right - 12.0f, fBounds.bottom - 7.0f), 602 BPoint(fBounds.right - 12.0f, fBounds.bottom - 9.0f), 603 BPoint(fBounds.right - 9.0f, fBounds.bottom - 8.0f)); 604 } 605 //------------------------------------------------------------------------------ 606 void BMenuItem::DrawControlChar(const char *control) 607 { 608 } 609 //------------------------------------------------------------------------------ 610 void BMenuItem::SetSysTrigger(char ch) 611 { 612 fSysTrigger = ch; 613 } 614 //------------------------------------------------------------------------------ 615 BSeparatorItem::BSeparatorItem() 616 : BMenuItem(NULL, NULL, 0, 0) 617 { 618 } 619 //------------------------------------------------------------------------------ 620 BSeparatorItem::BSeparatorItem(BMessage *data) 621 : BMenuItem(data) 622 { 623 } 624 //------------------------------------------------------------------------------ 625 BSeparatorItem::~BSeparatorItem() 626 { 627 } 628 //------------------------------------------------------------------------------ 629 status_t BSeparatorItem::Archive(BMessage *data, bool deep) const 630 { 631 return BMenuItem::Archive(data, deep); 632 } 633 //------------------------------------------------------------------------------ 634 BArchivable *BSeparatorItem::Instantiate(BMessage *data) 635 { 636 if (validate_instantiation(data, "BSeparatorItem")) 637 return new BSeparatorItem(data); 638 else 639 return NULL; 640 } 641 //------------------------------------------------------------------------------ 642 void BSeparatorItem::SetEnabled(bool state) 643 { 644 } 645 //------------------------------------------------------------------------------ 646 void BSeparatorItem::GetContentSize(float *width, float *height) 647 { 648 *width = 2.0f; 649 *height = 8.0f; 650 } 651 //------------------------------------------------------------------------------ 652 void BSeparatorItem::Draw() 653 { 654 BRect bounds = Frame(); 655 656 Menu()->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 657 B_DARKEN_2_TINT)); 658 Menu()->StrokeLine(BPoint(bounds.left + 1.0f, bounds.top + 4.0f), 659 BPoint(bounds.right - 1.0f, bounds.top + 4.0f)); 660 Menu()->SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 661 B_LIGHTEN_2_TINT)); 662 Menu()->StrokeLine(BPoint(bounds.left + 1.0f, bounds.top + 5.0f), 663 BPoint(bounds.right - 1.0f, bounds.top + 5.0f)); 664 665 Menu()->SetHighColor(0, 0, 0); 666 } 667 //------------------------------------------------------------------------------ 668 void BSeparatorItem::_ReservedSeparatorItem1() {} 669 void BSeparatorItem::_ReservedSeparatorItem2() {} 670 //------------------------------------------------------------------------------ 671 BSeparatorItem &BSeparatorItem::operator=(const BSeparatorItem &) 672 { 673 return *this; 674 } 675 //------------------------------------------------------------------------------ 676