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