1 /* 2 * Copyright 2004-2011, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * McCall <mccall@@digitalparadise.co.uk> 7 * Mike Berg <mike@berg-net.us> 8 * Julun <host.haiku@gmx.de> 9 * Clemens <mail@Clemens-Zeidler.de> 10 * Adrien Destugues <pulkomandy@pulkomandy.cx> 11 * Hamish Morrison <hamish@lavabit.com> 12 */ 13 14 15 #include "DateTimeEdit.h" 16 17 #include <stdlib.h> 18 19 #include <ControlLook.h> 20 #include <DateFormat.h> 21 #include <LayoutUtils.h> 22 #include <List.h> 23 #include <Locale.h> 24 #include <String.h> 25 #include <Window.h> 26 27 28 namespace BPrivate { 29 30 31 const uint32 kArrowAreaWidth = 16; 32 33 34 TimeEdit::TimeEdit(const char* name, uint32 sections, BMessage* message) 35 : 36 SectionEdit(name, sections, message), 37 fLastKeyDownTime(0), 38 fFields(NULL), 39 fFieldCount(0), 40 fFieldPositions(NULL), 41 fFieldPosCount(0) 42 { 43 InitView(); 44 } 45 46 47 TimeEdit::~TimeEdit() 48 { 49 free(fFieldPositions); 50 free(fFields); 51 } 52 53 54 void 55 TimeEdit::KeyDown(const char* bytes, int32 numBytes) 56 { 57 if (IsEnabled() == false) 58 return; 59 SectionEdit::KeyDown(bytes, numBytes); 60 61 // only accept valid input 62 int32 number = atoi(bytes); 63 if (number < 0 || bytes[0] < '0') 64 return; 65 66 int32 section = FocusIndex(); 67 if (section < 0 || section > 2) 68 return; 69 70 bigtime_t currentTime = system_time(); 71 if (currentTime - fLastKeyDownTime < 1000000) { 72 int32 doubleDigit = number + fLastKeyDownInt * 10; 73 if (_IsValidDoubleDigit(doubleDigit)) 74 number = doubleDigit; 75 fLastKeyDownTime = 0; 76 } else { 77 fLastKeyDownTime = currentTime; 78 fLastKeyDownInt = number; 79 } 80 81 // update display value 82 fHoldValue = number; 83 _CheckRange(); 84 _UpdateFields(); 85 86 // send message to change time 87 Invoke(); 88 } 89 90 91 void 92 TimeEdit::InitView() 93 { 94 // make sure we call the base class method, as it 95 // will create the arrow bitmaps and the section list 96 fTime = BDateTime::CurrentDateTime(B_LOCAL_TIME); 97 _UpdateFields(); 98 } 99 100 101 void 102 TimeEdit::DrawSection(uint32 index, BRect bounds, bool hasFocus) 103 { 104 if (fFieldPositions == NULL || index * 2 + 1 >= (uint32)fFieldPosCount) 105 return; 106 107 if (hasFocus) 108 SetLowColor(mix_color(ui_color(B_CONTROL_HIGHLIGHT_COLOR), 109 ViewColor(), 192)); 110 else 111 SetLowColor(ViewColor()); 112 113 BString field; 114 fText.CopyCharsInto(field, fFieldPositions[index * 2], 115 fFieldPositions[index * 2 + 1] - fFieldPositions[index * 2]); 116 117 BPoint point(bounds.LeftBottom()); 118 point.y -= bounds.Height() / 2.0 - 6.0; 119 point.x += (bounds.Width() - StringWidth(field)) / 2; 120 SetHighUIColor(B_PANEL_TEXT_COLOR); 121 FillRect(bounds, B_SOLID_LOW); 122 DrawString(field, point); 123 } 124 125 126 void 127 TimeEdit::DrawSeparator(uint32 index, BRect bounds) 128 { 129 if (fFieldPositions == NULL || index * 2 + 2 >= (uint32)fFieldPosCount) 130 return; 131 132 BString field; 133 fText.CopyCharsInto(field, fFieldPositions[index * 2 + 1], 134 fFieldPositions[index * 2 + 2] - fFieldPositions[index * 2 + 1]); 135 136 BPoint point(bounds.LeftBottom()); 137 point.y -= bounds.Height() / 2.0 - 6.0; 138 point.x += (bounds.Width() - StringWidth(field)) / 2; 139 SetHighUIColor(B_PANEL_TEXT_COLOR); 140 DrawString(field, point); 141 } 142 143 144 float 145 TimeEdit::SeparatorWidth() 146 { 147 return 10.0f; 148 } 149 150 151 float 152 TimeEdit::MinSectionWidth() 153 { 154 return be_plain_font->StringWidth("00"); 155 } 156 157 158 void 159 TimeEdit::SectionFocus(uint32 index) 160 { 161 fLastKeyDownTime = 0; 162 fFocus = index; 163 fHoldValue = _SectionValue(index); 164 Draw(Bounds()); 165 } 166 167 168 void 169 TimeEdit::SetTime(int32 hour, int32 minute, int32 second) 170 { 171 // make sure to update date upon overflow 172 if (hour == 0 && minute == 0 && second == 0) 173 fTime = BDateTime::CurrentDateTime(B_LOCAL_TIME); 174 175 fTime.SetTime(BTime(hour, minute, second)); 176 177 if (LockLooper()) { 178 _UpdateFields(); 179 UnlockLooper(); 180 } 181 182 Invalidate(Bounds()); 183 } 184 185 186 BTime 187 TimeEdit::GetTime() 188 { 189 return fTime.Time(); 190 } 191 192 193 void 194 TimeEdit::DoUpPress() 195 { 196 if (fFocus == -1) 197 SectionFocus(0); 198 199 // update displayed value 200 fHoldValue += 1; 201 202 _CheckRange(); 203 _UpdateFields(); 204 205 // send message to change time 206 Invoke(); 207 } 208 209 210 void 211 TimeEdit::DoDownPress() 212 { 213 if (fFocus == -1) 214 SectionFocus(0); 215 216 // update display value 217 fHoldValue -= 1; 218 219 _CheckRange(); 220 _UpdateFields(); 221 222 Invoke(); 223 } 224 225 226 void 227 TimeEdit::PopulateMessage(BMessage* message) 228 { 229 if (fFocus < 0 || fFocus >= fFieldCount) 230 return; 231 232 message->AddBool("time", true); 233 message->AddInt32("hour", fTime.Time().Hour()); 234 message->AddInt32("minute", fTime.Time().Minute()); 235 message->AddInt32("second", fTime.Time().Second()); 236 } 237 238 239 void 240 TimeEdit::_UpdateFields() 241 { 242 time_t time = fTime.Time_t(); 243 244 if (fFieldPositions != NULL) { 245 free(fFieldPositions); 246 fFieldPositions = NULL; 247 } 248 fTimeFormat.Format(fText, fFieldPositions, fFieldPosCount, time, 249 B_MEDIUM_TIME_FORMAT); 250 251 if (fFields != NULL) { 252 free(fFields); 253 fFields = NULL; 254 } 255 fTimeFormat.GetTimeFields(fFields, fFieldCount, B_MEDIUM_TIME_FORMAT); 256 } 257 258 259 void 260 TimeEdit::_CheckRange() 261 { 262 if (fFocus < 0 || fFocus >= fFieldCount) 263 return; 264 265 int32 value = fHoldValue; 266 switch (fFields[fFocus]) { 267 case B_DATE_ELEMENT_HOUR: 268 if (value > 23) 269 value = 0; 270 else if (value < 0) 271 value = 23; 272 273 fTime.SetTime(BTime(value, fTime.Time().Minute(), 274 fTime.Time().Second())); 275 break; 276 277 case B_DATE_ELEMENT_MINUTE: 278 if (value> 59) 279 value = 0; 280 else if (value < 0) 281 value = 59; 282 283 fTime.SetTime(BTime(fTime.Time().Hour(), value, 284 fTime.Time().Second())); 285 break; 286 287 case B_DATE_ELEMENT_SECOND: 288 if (value > 59) 289 value = 0; 290 else if (value < 0) 291 value = 59; 292 293 fTime.SetTime(BTime(fTime.Time().Hour(), fTime.Time().Minute(), 294 value)); 295 break; 296 297 case B_DATE_ELEMENT_AM_PM: 298 value = fTime.Time().Hour(); 299 if (value < 13) 300 value += 12; 301 else 302 value -= 12; 303 if (value == 24) 304 value = 0; 305 306 // modify hour value to reflect change in am/ pm 307 fTime.SetTime(BTime(value, fTime.Time().Minute(), 308 fTime.Time().Second())); 309 break; 310 311 default: 312 return; 313 } 314 315 316 fHoldValue = value; 317 Invalidate(Bounds()); 318 } 319 320 321 bool 322 TimeEdit::_IsValidDoubleDigit(int32 value) 323 { 324 if (fFocus < 0 || fFocus >= fFieldCount) 325 return false; 326 327 bool isInRange = false; 328 switch (fFields[fFocus]) { 329 case B_DATE_ELEMENT_HOUR: 330 if (value <= 23) 331 isInRange = true; 332 break; 333 334 case B_DATE_ELEMENT_MINUTE: 335 if (value <= 59) 336 isInRange = true; 337 break; 338 339 case B_DATE_ELEMENT_SECOND: 340 if (value <= 59) 341 isInRange = true; 342 break; 343 344 default: 345 break; 346 } 347 348 return isInRange; 349 } 350 351 352 int32 353 TimeEdit::_SectionValue(int32 index) const 354 { 355 if (index < 0 || index >= fFieldCount) 356 return 0; 357 358 int32 value; 359 switch (fFields[index]) { 360 case B_DATE_ELEMENT_HOUR: 361 value = fTime.Time().Hour(); 362 break; 363 364 case B_DATE_ELEMENT_MINUTE: 365 value = fTime.Time().Minute(); 366 break; 367 368 case B_DATE_ELEMENT_SECOND: 369 value = fTime.Time().Second(); 370 break; 371 372 default: 373 value = 0; 374 break; 375 } 376 377 return value; 378 } 379 380 381 float 382 TimeEdit::PreferredHeight() 383 { 384 font_height fontHeight; 385 GetFontHeight(&fontHeight); 386 return ceilf((fontHeight.ascent + fontHeight.descent) * 1.4); 387 } 388 389 390 // #pragma mark - 391 392 393 DateEdit::DateEdit(const char* name, uint32 sections, BMessage* message) 394 : 395 SectionEdit(name, sections, message), 396 fFields(NULL), 397 fFieldCount(0), 398 fFieldPositions(NULL), 399 fFieldPosCount(0) 400 { 401 InitView(); 402 } 403 404 405 DateEdit::~DateEdit() 406 { 407 free(fFieldPositions); 408 free(fFields); 409 } 410 411 412 void 413 DateEdit::KeyDown(const char* bytes, int32 numBytes) 414 { 415 if (IsEnabled() == false) 416 return; 417 SectionEdit::KeyDown(bytes, numBytes); 418 419 // only accept valid input 420 int32 number = atoi(bytes); 421 if (number < 0 || bytes[0] < '0') 422 return; 423 424 int32 section = FocusIndex(); 425 if (section < 0 || section > 2) 426 return; 427 428 bigtime_t currentTime = system_time(); 429 if (currentTime - fLastKeyDownTime < 1000000) { 430 int32 doubleDigit = number + fLastKeyDownInt * 10; 431 if (_IsValidDoubleDigit(doubleDigit)) 432 number = doubleDigit; 433 fLastKeyDownTime = 0; 434 } else { 435 fLastKeyDownTime = currentTime; 436 fLastKeyDownInt = number; 437 } 438 439 // if year add 2000 440 441 if (fFields[section] == B_DATE_ELEMENT_YEAR) { 442 int32 oldCentury = int32(fHoldValue / 100) * 100; 443 if (number < 10 && oldCentury == 1900) 444 number += 70; 445 number += oldCentury; 446 } 447 fHoldValue = number; 448 449 // update display value 450 _CheckRange(); 451 _UpdateFields(); 452 453 // send message to change time 454 Invoke(); 455 } 456 457 458 void 459 DateEdit::InitView() 460 { 461 // make sure we call the base class method, as it 462 // will create the arrow bitmaps and the section list 463 fDate = BDate::CurrentDate(B_LOCAL_TIME); 464 _UpdateFields(); 465 } 466 467 468 void 469 DateEdit::DrawSection(uint32 index, BRect bounds, bool hasFocus) 470 { 471 if (fFieldPositions == NULL || index * 2 + 1 >= (uint32)fFieldPosCount) 472 return; 473 474 if (hasFocus) 475 SetLowColor(mix_color(ui_color(B_CONTROL_HIGHLIGHT_COLOR), 476 ViewColor(), 192)); 477 else 478 SetLowColor(ViewColor()); 479 480 BString field; 481 fText.CopyCharsInto(field, fFieldPositions[index * 2], 482 fFieldPositions[index * 2 + 1] - fFieldPositions[index * 2]); 483 484 BPoint point(bounds.LeftBottom()); 485 point.y -= bounds.Height() / 2.0 - 6.0; 486 point.x += (bounds.Width() - StringWidth(field)) / 2; 487 SetHighUIColor(B_PANEL_TEXT_COLOR); 488 FillRect(bounds, B_SOLID_LOW); 489 DrawString(field, point); 490 } 491 492 493 void 494 DateEdit::DrawSeparator(uint32 index, BRect bounds) 495 { 496 if (index >= 2) 497 return; 498 499 if (fFieldPositions == NULL || index * 2 + 2 >= (uint32)fFieldPosCount) 500 return; 501 502 BString field; 503 fText.CopyCharsInto(field, fFieldPositions[index * 2 + 1], 504 fFieldPositions[index * 2 + 2] - fFieldPositions[index * 2 + 1]); 505 506 BPoint point(bounds.LeftBottom()); 507 point.y -= bounds.Height() / 2.0 - 6.0; 508 point.x += (bounds.Width() - StringWidth(field)) / 2; 509 SetHighUIColor(B_PANEL_TEXT_COLOR); 510 DrawString(field, point); 511 } 512 513 514 void 515 DateEdit::SectionFocus(uint32 index) 516 { 517 fLastKeyDownTime = 0; 518 fFocus = index; 519 fHoldValue = _SectionValue(index); 520 Draw(Bounds()); 521 } 522 523 524 float 525 DateEdit::MinSectionWidth() 526 { 527 return be_plain_font->StringWidth("00"); 528 } 529 530 531 float 532 DateEdit::SeparatorWidth() 533 { 534 return 10.0f; 535 } 536 537 538 void 539 DateEdit::SetDate(int32 year, int32 month, int32 day) 540 { 541 fDate.SetDate(year, month, day); 542 543 if (LockLooper()) { 544 _UpdateFields(); 545 UnlockLooper(); 546 } 547 548 Invalidate(Bounds()); 549 } 550 551 552 BDate 553 DateEdit::GetDate() 554 { 555 return fDate; 556 } 557 558 559 void 560 DateEdit::DoUpPress() 561 { 562 if (fFocus == -1) 563 SectionFocus(0); 564 565 // update displayed value 566 fHoldValue += 1; 567 568 _CheckRange(); 569 _UpdateFields(); 570 571 // send message to change Date 572 Invoke(); 573 } 574 575 576 void 577 DateEdit::DoDownPress() 578 { 579 if (fFocus == -1) 580 SectionFocus(0); 581 582 // update display value 583 fHoldValue -= 1; 584 585 _CheckRange(); 586 _UpdateFields(); 587 588 // send message to change Date 589 Invoke(); 590 } 591 592 593 void 594 DateEdit::PopulateMessage(BMessage* message) 595 { 596 if (fFocus < 0 || fFocus >= fFieldCount) 597 return; 598 599 message->AddBool("time", false); 600 message->AddInt32("year", fDate.Year()); 601 message->AddInt32("month", fDate.Month()); 602 message->AddInt32("day", fDate.Day()); 603 } 604 605 606 void 607 DateEdit::_UpdateFields() 608 { 609 time_t time = BDateTime(fDate, BTime()).Time_t(); 610 611 if (fFieldPositions != NULL) { 612 free(fFieldPositions); 613 fFieldPositions = NULL; 614 } 615 616 fDateFormat.Format(fText, fFieldPositions, fFieldPosCount, time, 617 B_SHORT_DATE_FORMAT); 618 619 if (fFields != NULL) { 620 free(fFields); 621 fFields = NULL; 622 } 623 fDateFormat.GetFields(fFields, fFieldCount, B_SHORT_DATE_FORMAT); 624 } 625 626 627 void 628 DateEdit::_CheckRange() 629 { 630 if (fFocus < 0 || fFocus >= fFieldCount) 631 return; 632 633 int32 value = fHoldValue; 634 switch (fFields[fFocus]) { 635 case B_DATE_ELEMENT_DAY: 636 { 637 int32 days = fDate.DaysInMonth(); 638 if (value > days) 639 value = 1; 640 else if (value < 1) 641 value = days; 642 643 fDate.SetDate(fDate.Year(), fDate.Month(), value); 644 break; 645 } 646 647 case B_DATE_ELEMENT_MONTH: 648 { 649 if (value > 12) 650 value = 1; 651 else if (value < 1) 652 value = 12; 653 654 int32 day = fDate.Day(); 655 fDate.SetDate(fDate.Year(), value, 1); 656 657 // changing between months with differing amounts of days 658 while (day > fDate.DaysInMonth()) 659 day--; 660 fDate.SetDate(fDate.Year(), value, day); 661 break; 662 } 663 664 case B_DATE_ELEMENT_YEAR: 665 fDate.SetDate(value, fDate.Month(), fDate.Day()); 666 break; 667 668 default: 669 return; 670 } 671 672 fHoldValue = value; 673 Invalidate(Bounds()); 674 } 675 676 677 bool 678 DateEdit::_IsValidDoubleDigit(int32 value) 679 { 680 if (fFocus < 0 || fFocus >= fFieldCount) 681 return false; 682 683 bool isInRange = false; 684 switch (fFields[fFocus]) { 685 case B_DATE_ELEMENT_DAY: 686 { 687 int32 days = fDate.DaysInMonth(); 688 if (value >= 1 && value <= days) 689 isInRange = true; 690 break; 691 } 692 693 case B_DATE_ELEMENT_MONTH: 694 { 695 if (value >= 1 && value <= 12) 696 isInRange = true; 697 break; 698 } 699 700 case B_DATE_ELEMENT_YEAR: 701 { 702 int32 year = int32(fHoldValue / 100) * 100 + value; 703 if (year >= 2000) 704 isInRange = true; 705 break; 706 } 707 708 default: 709 break; 710 } 711 712 return isInRange; 713 } 714 715 716 int32 717 DateEdit::_SectionValue(int32 index) const 718 { 719 if (index < 0 || index >= fFieldCount) 720 return 0; 721 722 int32 value = 0; 723 switch (fFields[index]) { 724 case B_DATE_ELEMENT_YEAR: 725 value = fDate.Year(); 726 break; 727 728 case B_DATE_ELEMENT_MONTH: 729 value = fDate.Month(); 730 break; 731 732 case B_DATE_ELEMENT_DAY: 733 value = fDate.Day(); 734 break; 735 736 default: 737 break; 738 } 739 740 return value; 741 } 742 743 744 float 745 DateEdit::PreferredHeight() 746 { 747 font_height fontHeight; 748 GetFontHeight(&fontHeight); 749 return ceilf((fontHeight.ascent + fontHeight.descent) * 1.4); 750 } 751 752 753 // #pragma mark - 754 755 756 SectionEdit::SectionEdit(const char* name, uint32 sections, BMessage* message) 757 : 758 BControl(name, NULL, message, B_WILL_DRAW | B_NAVIGABLE), 759 fFocus(-1), 760 fSectionCount(sections), 761 fHoldValue(0) 762 { 763 } 764 765 766 SectionEdit::~SectionEdit() 767 { 768 } 769 770 771 void 772 SectionEdit::AttachedToWindow() 773 { 774 AdoptParentColors(); 775 BControl::AttachedToWindow(); 776 } 777 778 779 void 780 SectionEdit::Draw(BRect updateRect) 781 { 782 DrawBorder(updateRect); 783 784 for (uint32 idx = 0; idx < fSectionCount; idx++) { 785 DrawSection(idx, FrameForSection(idx), 786 ((uint32)fFocus == idx) && IsFocus()); 787 if (idx < fSectionCount - 1) 788 DrawSeparator(idx, FrameForSeparator(idx)); 789 } 790 } 791 792 793 void 794 SectionEdit::MouseDown(BPoint where) 795 { 796 if (IsEnabled() == false) 797 return; 798 799 MakeFocus(true); 800 801 if (fUpRect.Contains(where)) 802 DoUpPress(); 803 else if (fDownRect.Contains(where)) 804 DoDownPress(); 805 else if (fSectionCount > 0) { 806 for (uint32 idx = 0; idx < fSectionCount; idx++) { 807 if (FrameForSection(idx).Contains(where)) { 808 SectionFocus(idx); 809 return; 810 } 811 } 812 } 813 } 814 815 816 BSize 817 SectionEdit::MaxSize() 818 { 819 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 820 BSize(B_SIZE_UNLIMITED, PreferredHeight())); 821 } 822 823 824 BSize 825 SectionEdit::MinSize() 826 { 827 BSize minSize; 828 minSize.height = PreferredHeight(); 829 minSize.width = (SeparatorWidth() + MinSectionWidth()) 830 * fSectionCount; 831 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 832 minSize); 833 } 834 835 836 BSize 837 SectionEdit::PreferredSize() 838 { 839 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 840 MinSize()); 841 } 842 843 844 BRect 845 SectionEdit::FrameForSection(uint32 index) 846 { 847 BRect area = SectionArea(); 848 float sepWidth = SeparatorWidth(); 849 850 float width = (area.Width() - 851 sepWidth * (fSectionCount - 1)) 852 / fSectionCount; 853 area.left += index * (width + sepWidth); 854 area.right = area.left + width; 855 856 return area; 857 } 858 859 860 BRect 861 SectionEdit::FrameForSeparator(uint32 index) 862 { 863 BRect area = SectionArea(); 864 float sepWidth = SeparatorWidth(); 865 866 float width = (area.Width() - 867 sepWidth * (fSectionCount - 1)) 868 / fSectionCount; 869 area.left += (index + 1) * width + index * sepWidth; 870 area.right = area.left + sepWidth; 871 872 return area; 873 } 874 875 876 void 877 SectionEdit::MakeFocus(bool focused) 878 { 879 if (focused == IsFocus()) 880 return; 881 882 BControl::MakeFocus(focused); 883 884 if (fFocus == -1) 885 SectionFocus(0); 886 else 887 SectionFocus(fFocus); 888 } 889 890 891 void 892 SectionEdit::KeyDown(const char* bytes, int32 numbytes) 893 { 894 if (IsEnabled() == false) 895 return; 896 if (fFocus == -1) 897 SectionFocus(0); 898 899 switch (bytes[0]) { 900 case B_LEFT_ARROW: 901 fFocus -= 1; 902 if (fFocus < 0) 903 fFocus = fSectionCount - 1; 904 SectionFocus(fFocus); 905 break; 906 907 case B_RIGHT_ARROW: 908 fFocus += 1; 909 if ((uint32)fFocus >= fSectionCount) 910 fFocus = 0; 911 SectionFocus(fFocus); 912 break; 913 914 case B_UP_ARROW: 915 DoUpPress(); 916 break; 917 918 case B_DOWN_ARROW: 919 DoDownPress(); 920 break; 921 922 default: 923 BControl::KeyDown(bytes, numbytes); 924 break; 925 } 926 Draw(Bounds()); 927 } 928 929 930 status_t 931 SectionEdit::Invoke(BMessage* message) 932 { 933 if (message == NULL) 934 message = Message(); 935 if (message == NULL) 936 return BControl::Invoke(NULL); 937 938 BMessage clone(*message); 939 PopulateMessage(&clone); 940 return BControl::Invoke(&clone); 941 } 942 943 944 uint32 945 SectionEdit::CountSections() const 946 { 947 return fSectionCount; 948 } 949 950 951 int32 952 SectionEdit::FocusIndex() const 953 { 954 return fFocus; 955 } 956 957 958 BRect 959 SectionEdit::SectionArea() const 960 { 961 BRect sectionArea = Bounds().InsetByCopy(2, 2); 962 sectionArea.right -= kArrowAreaWidth; 963 return sectionArea; 964 } 965 966 967 void 968 SectionEdit::DrawBorder(const BRect& updateRect) 969 { 970 BRect bounds(Bounds()); 971 bool showFocus = (IsFocus() && Window() && Window()->IsActive()); 972 973 be_control_look->DrawBorder(this, bounds, updateRect, ViewColor(), 974 B_FANCY_BORDER, showFocus ? BControlLook::B_FOCUSED : 0); 975 976 // draw up/down control 977 978 bounds.left = bounds.right - kArrowAreaWidth; 979 bounds.right = Bounds().right - 2; 980 fUpRect.Set(bounds.left + 3, bounds.top + 2, bounds.right, 981 bounds.bottom / 2.0); 982 fDownRect = fUpRect.OffsetByCopy(0, fUpRect.Height() + 2); 983 984 BPoint middle(floorf(fUpRect.left + fUpRect.Width() / 2), 985 fUpRect.top + 1); 986 BPoint left(fUpRect.left + 3, fUpRect.bottom - 1); 987 BPoint right(left.x + 2 * (middle.x - left.x), fUpRect.bottom - 1); 988 989 SetPenSize(2); 990 SetLowColor(ViewColor()); 991 992 if (updateRect.Intersects(fUpRect)) { 993 FillRect(fUpRect, B_SOLID_LOW); 994 BeginLineArray(2); 995 AddLine(left, middle, HighColor()); 996 AddLine(middle, right, HighColor()); 997 EndLineArray(); 998 } 999 if (updateRect.Intersects(fDownRect)) { 1000 middle.y = fDownRect.bottom - 1; 1001 left.y = right.y = fDownRect.top + 1; 1002 1003 FillRect(fDownRect, B_SOLID_LOW); 1004 BeginLineArray(2); 1005 AddLine(left, middle, HighColor()); 1006 AddLine(middle, right, HighColor()); 1007 EndLineArray(); 1008 } 1009 1010 SetPenSize(1); 1011 } 1012 1013 1014 } // namespace BPrivate 1015