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 */ 8 9 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 14 #include <MenuBar.h> 15 #include <MenuField.h> 16 #include <Message.h> 17 #include <BMCPrivate.h> 18 #include <Window.h> 19 20 21 static float kVMargin = 2.0f; 22 23 24 BMenuField::BMenuField(BRect frame, const char *name, const char *label, 25 BMenu *menu, uint32 resize, uint32 flags) 26 : BView(frame, name, resize, flags) 27 { 28 InitObject(label); 29 SetFont(be_plain_font); 30 fMenu = menu; 31 InitMenu(menu); 32 33 frame.OffsetTo(0.0f, 0.0f); 34 fMenuBar = new _BMCMenuBar_(BRect(frame.left + fDivider + 1.0, 35 frame.top + kVMargin, frame.right - 2.0f, frame.bottom - kVMargin), 36 false, this); 37 38 AddChild(fMenuBar); 39 fMenuBar->AddItem(menu); 40 41 fMenuBar->SetFont(be_plain_font); 42 43 InitObject2(); 44 } 45 46 47 BMenuField::BMenuField(BRect frame, const char *name, const char *label, 48 BMenu *menu, bool fixedSize, uint32 resize, uint32 flags) 49 : BView(frame, name, resize, flags) 50 { 51 InitObject(label); 52 SetFont(be_plain_font); 53 fMenu = menu; 54 InitMenu(menu); 55 56 fFixedSizeMB = fixedSize; 57 58 frame.OffsetTo(0.0f, 0.0f); 59 fMenuBar = new _BMCMenuBar_(BRect(frame.left + fDivider + 1.0, 60 frame.top + kVMargin, frame.right - 2.0f, frame.bottom - kVMargin), 61 fixedSize, this); 62 63 AddChild(fMenuBar); 64 fMenuBar->AddItem(new _BMCItem_(menu)); 65 66 fMenuBar->SetFont(be_plain_font); 67 68 InitObject2(); 69 } 70 71 72 BMenuField::BMenuField(BMessage *data) 73 : BView(data) 74 { 75 const char *label = NULL; 76 data->FindString("_label", &label); 77 78 InitObject(label); 79 80 fMenuBar = (BMenuBar*)FindView("_mc_mb_"); 81 fMenu = fMenuBar->SubmenuAt(0); 82 83 InitObject2(); 84 85 bool disable; 86 if (data->FindBool("_disable", &disable) == B_OK) 87 SetEnabled(!disable); 88 89 int32 align; 90 data->FindInt32("_align", &align); 91 SetAlignment((alignment)align); 92 93 data->FindFloat("_divide", &fDivider); 94 95 bool fixed; 96 if (data->FindBool("be:fixeds", &fixed) == B_OK) 97 fFixedSizeMB = fixed; 98 99 BMenuItem *item = fMenuBar->ItemAt(0); 100 if (!item) 101 return; 102 103 _BMCItem_ *bmcitem = dynamic_cast<_BMCItem_*>(item); 104 if (!bmcitem) 105 return; 106 107 bool dmark; 108 if (data->FindBool("be:dmark", &dmark)) 109 bmcitem->fShowPopUpMarker = dmark; 110 } 111 112 113 BMenuField::~BMenuField() 114 { 115 free(fLabel); 116 117 if (fMenuTaskID >= 0) 118 kill_thread(fMenuTaskID); 119 } 120 121 122 BArchivable * 123 BMenuField::Instantiate(BMessage *data) 124 { 125 if (validate_instantiation(data, "BMenuField")) 126 return new BMenuField(data); 127 128 return NULL; 129 } 130 131 132 status_t 133 BMenuField::Archive(BMessage *data, bool deep) const 134 { 135 BView::Archive(data, deep); 136 137 if (Label()) 138 data->AddString("_label", Label()); 139 140 if (!IsEnabled()) 141 data->AddBool("_disable", true); 142 143 data->AddInt32("_align", Alignment()); 144 data->AddFloat("_divide", Divider()); 145 146 if (fFixedSizeMB) 147 data->AddBool("be:fixeds", true); 148 149 BMenuItem *item = fMenuBar->ItemAt(0); 150 if (!item) 151 return B_OK; 152 153 _BMCItem_ *bmcitem = dynamic_cast<_BMCItem_*>(item); 154 if (bmcitem && !bmcitem->fShowPopUpMarker) 155 data->AddBool("be:dmark", false); 156 157 return B_OK; 158 } 159 160 161 void 162 BMenuField::Draw(BRect update) 163 { 164 BRect bounds(Bounds()); 165 bool active = false; 166 167 if (IsFocus()) 168 active = Window()->IsActive(); 169 170 DrawLabel(bounds, update); 171 172 BRect frame(fMenuBar->Frame()); 173 174 if (frame.InsetByCopy(-2, -2).Intersects(update)) { 175 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT)); 176 StrokeLine(BPoint(frame.left - 1.0f, frame.top - 1.0f), 177 BPoint(frame.left - 1.0f, frame.bottom - 1.0f)); 178 StrokeLine(BPoint(frame.left - 1.0f, frame.top - 1.0f), 179 BPoint(frame.right - 1.0f, frame.top - 1.0f)); 180 181 StrokeLine(BPoint(frame.left + 1.0f, frame.bottom + 1.0f), 182 BPoint(frame.right + 1.0f, frame.bottom + 1.0f)); 183 StrokeLine(BPoint(frame.right + 1.0f, frame.top + 1.0f)); 184 185 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_4_TINT)); 186 StrokeLine(BPoint(frame.left - 1.0f, frame.bottom), 187 BPoint(frame.left - 1.0f, frame.bottom)); 188 StrokeLine(BPoint(frame.right, frame.top - 1.0f), 189 BPoint(frame.right, frame.top - 1.0f)); 190 } 191 192 if (active || fTransition) { 193 SetHighColor(active ? ui_color(B_KEYBOARD_NAVIGATION_COLOR) : 194 ViewColor()); 195 StrokeRect(frame.InsetByCopy(-kVMargin, -kVMargin)); 196 197 fTransition = false; 198 } 199 } 200 201 202 void 203 BMenuField::AttachedToWindow() 204 { 205 if (Parent()) { 206 SetViewColor(Parent()->ViewColor()); 207 SetLowColor(Parent()->ViewColor()); 208 } 209 210 if (fLabel) 211 fStringWidth = StringWidth(fLabel); 212 } 213 214 215 void 216 BMenuField::AllAttached() 217 { 218 ResizeTo(Bounds().Width(), 219 fMenuBar->Bounds().Height() + kVMargin + kVMargin); 220 } 221 222 223 void 224 BMenuField::MouseDown(BPoint where) 225 { 226 if (where.x > fDivider && !fMenuBar->Frame().Contains(where)) 227 return; 228 229 BRect bounds = fMenuBar->ConvertFromParent(Bounds()); 230 231 fMenuBar->StartMenuBar(0, false, true, &bounds); 232 233 fMenuTaskID = spawn_thread((thread_func)MenuTask, "_m_task_", 234 B_NORMAL_PRIORITY, this); 235 if (fMenuTaskID) 236 resume_thread(fMenuTaskID); 237 } 238 239 240 void 241 BMenuField::KeyDown(const char *bytes, int32 numBytes) 242 { 243 switch (bytes[0]) { 244 case B_SPACE: 245 case B_RIGHT_ARROW: 246 case B_DOWN_ARROW: 247 { 248 BRect bounds = fMenuBar->ConvertFromParent(Bounds()); 249 250 fMenuBar->StartMenuBar(0, true, true, &bounds); 251 252 fSelected = true; 253 fTransition = true; 254 255 bounds = Bounds(); 256 bounds.right = fDivider; 257 258 Invalidate(bounds); 259 } 260 261 default: 262 BView::KeyDown(bytes, numBytes); 263 } 264 } 265 266 267 void 268 BMenuField::MakeFocus(bool state) 269 { 270 if (IsFocus() == state) 271 return; 272 273 BView::MakeFocus(state); 274 275 if (Window()) 276 Invalidate(); // TODO: use fStringWidth 277 } 278 279 280 void 281 BMenuField::MessageReceived(BMessage *msg) 282 { 283 BView::MessageReceived(msg); 284 } 285 286 287 void 288 BMenuField::WindowActivated(bool state) 289 { 290 BView::WindowActivated(state); 291 292 if (IsFocus()) 293 Invalidate(); 294 } 295 296 297 void 298 BMenuField::MouseUp(BPoint point) 299 { 300 BView::MouseUp(point); 301 } 302 303 304 void 305 BMenuField::MouseMoved(BPoint point, uint32 code, const BMessage *message) 306 { 307 BView::MouseMoved(point, code, message); 308 } 309 310 311 void 312 BMenuField::DetachedFromWindow() 313 { 314 BView::DetachedFromWindow(); 315 } 316 317 318 void 319 BMenuField::AllDetached() 320 { 321 BView::AllDetached(); 322 } 323 324 325 void 326 BMenuField::FrameMoved(BPoint newPosition) 327 { 328 BView::FrameMoved(newPosition); 329 } 330 331 332 void 333 BMenuField::FrameResized(float newWidth, float newHeight) 334 { 335 BView::FrameResized(newWidth, newHeight); 336 } 337 338 339 BMenu * 340 BMenuField::Menu() const 341 { 342 return fMenu; 343 } 344 345 346 BMenuBar * 347 BMenuField::MenuBar() const 348 { 349 return fMenuBar; 350 } 351 352 353 BMenuItem * 354 BMenuField::MenuItem() const 355 { 356 return fMenuBar->ItemAt(0); 357 } 358 359 360 void 361 BMenuField::SetLabel(const char *label) 362 { 363 if (fLabel) { 364 if (label && strcmp(fLabel, label) == 0) 365 return; 366 367 free(fLabel); 368 } 369 370 fLabel = strdup(label); 371 372 if (Window()) { 373 Invalidate(); 374 if (fLabel) 375 fStringWidth = StringWidth(fLabel); 376 } 377 } 378 379 380 const char * 381 BMenuField::Label() const 382 { 383 return fLabel; 384 } 385 386 387 void 388 BMenuField::SetEnabled(bool on) 389 { 390 if (fEnabled == on) 391 return; 392 393 fEnabled = on; 394 fMenuBar->SetEnabled(on); 395 396 if (Window()) { 397 fMenuBar->Invalidate(fMenuBar->Bounds()); 398 Invalidate(Bounds()); 399 } 400 } 401 402 403 bool 404 BMenuField::IsEnabled() const 405 { 406 return fEnabled; 407 } 408 409 410 void 411 BMenuField::SetAlignment(alignment label) 412 { 413 fAlign = label; 414 } 415 416 417 alignment 418 BMenuField::Alignment() const 419 { 420 return fAlign; 421 } 422 423 424 void 425 BMenuField::SetDivider(float divider) 426 { 427 divider = floorf(divider + 0.5); 428 429 float dx = fDivider - divider; 430 431 if (dx == 0.0f) 432 return; 433 434 fDivider = divider; 435 436 MenuBar()->MoveBy(-dx, 0.0f); 437 MenuBar()->ResizeBy(dx, 0.0f); 438 } 439 440 441 float 442 BMenuField::Divider() const 443 { 444 return fDivider; 445 } 446 447 448 void 449 BMenuField::ShowPopUpMarker() 450 { 451 // TODO: 452 } 453 454 455 void 456 BMenuField::HidePopUpMarker() 457 { 458 // TODO: 459 } 460 461 462 BHandler * 463 BMenuField::ResolveSpecifier(BMessage *message, int32 index, 464 BMessage *specifier, int32 form, const char *property) 465 { 466 return BView::ResolveSpecifier(message, index, specifier, form, property); 467 } 468 469 470 status_t 471 BMenuField::GetSupportedSuites(BMessage *data) 472 { 473 return BView::GetSupportedSuites(data); 474 } 475 476 477 void 478 BMenuField::ResizeToPreferred() 479 { 480 BView::ResizeToPreferred(); 481 } 482 483 484 void 485 BMenuField::GetPreferredSize(float *_width, float *_height) 486 { 487 BView::GetPreferredSize(_width, _height); 488 489 if (!fFixedSizeMB) { 490 BMenu* menu = Menu(); 491 float width = 0; 492 493 if (menu != NULL) { 494 if (menu->IsLabelFromMarked()) { 495 // find the width of the largest item 496 for (int32 i = menu->CountItems(); i-- > 0;) { 497 BMenuItem* item = menu->ItemAt(i); 498 499 if (item != NULL && item->Label() != NULL) { 500 float labelWidth = StringWidth(item->Label()); 501 if (labelWidth > width) 502 width = labelWidth; 503 } 504 } 505 } else if (menu->Superitem() != NULL) 506 width = StringWidth(menu->Superitem()->Label()); 507 } 508 509 // TODO: fix these values (they should match the visual appearance) 510 *_width = width + 45 + fDivider; 511 512 if (Label()) 513 *_width += StringWidth(Label()) + 5; 514 } 515 516 *_height = fMenuBar->Bounds().Height(); 517 } 518 519 520 status_t 521 BMenuField::Perform(perform_code d, void *arg) 522 { 523 return BView::Perform(d, arg); 524 } 525 526 527 void BMenuField::_ReservedMenuField1() {} 528 void BMenuField::_ReservedMenuField2() {} 529 void BMenuField::_ReservedMenuField3() {} 530 531 532 BMenuField & 533 BMenuField::operator=(const BMenuField &) 534 { 535 return *this; 536 } 537 538 539 void 540 BMenuField::InitObject(const char *label) 541 { 542 fLabel = NULL; 543 fMenu = NULL; 544 fMenuBar = NULL; 545 fAlign = B_ALIGN_LEFT; 546 fStringWidth = 0; 547 fEnabled = true; 548 fSelected = false; 549 fTransition = false; 550 fFixedSizeMB = false; 551 fMenuTaskID = -1; 552 553 SetLabel(label); 554 555 if (label) 556 fDivider = (float)floor(Frame().Width() / 2.0f); 557 else 558 fDivider = 0; 559 } 560 561 562 void 563 BMenuField::InitObject2() 564 { 565 // TODO set filter 566 567 font_height fontHeight; 568 GetFontHeight(&fontHeight); 569 570 // TODO: fix this calculation 571 float height = floorf(fontHeight.ascent + fontHeight.descent 572 + fontHeight.leading) + 7; 573 574 fMenuBar->ResizeTo(Bounds().Width() - fDivider, height); 575 } 576 577 578 void 579 BMenuField::DrawLabel(BRect bounds, BRect update) 580 { 581 font_height fh; 582 GetFontHeight(&fh); 583 584 if (Label()) { 585 SetLowColor(ViewColor()); 586 587 float y = (float)ceil(fh.ascent + fh.descent + fh.leading) + 2.0f; 588 float x; 589 590 switch (fAlign) { 591 case B_ALIGN_RIGHT: 592 x = fDivider - StringWidth(Label()) - 3.0f; 593 break; 594 595 case B_ALIGN_CENTER: 596 x = fDivider - StringWidth(Label()) / 2.0f; 597 break; 598 599 default: 600 x = 3.0f; 601 break; 602 } 603 604 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 605 IsEnabled() ? B_DARKEN_MAX_TINT : B_DISABLED_LABEL_TINT)); 606 DrawString(Label(), BPoint(x, y)); 607 } 608 } 609 610 611 void 612 BMenuField::InitMenu(BMenu *menu) 613 { 614 menu->SetFont(be_plain_font); 615 616 int32 index = 0; 617 BMenu *subMenu; 618 619 while ((subMenu = menu->SubmenuAt(index++)) != NULL) 620 InitMenu(subMenu); 621 } 622 623 624 long 625 BMenuField::MenuTask(void *arg) 626 { 627 BMenuField *menuField = (BMenuField*)arg; 628 629 if (!menuField->LockLooper()) 630 return 0; 631 632 menuField->fSelected = true; 633 menuField->fTransition = true; 634 menuField->Invalidate(); 635 636 menuField->UnlockLooper(); 637 638 bool tracking; 639 640 do { 641 snooze(20000); 642 643 if (!menuField->LockLooper()) 644 return 0; 645 646 tracking = menuField->fMenuBar->fTracking; 647 648 menuField->UnlockLooper(); 649 } while (tracking); 650 651 if (!menuField->LockLooper()) 652 return 0; 653 654 menuField->fSelected = false; 655 menuField->fTransition = true; 656 menuField->Invalidate(); 657 658 menuField->UnlockLooper(); 659 return 0; 660 } 661 662