1 /* 2 * Copyright 2007-2011, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Julun <host.haiku@gmx.de> 7 */ 8 9 10 #include "CalendarView.h" 11 12 #include <stdlib.h> 13 14 #include <LayoutUtils.h> 15 #include <Window.h> 16 17 18 namespace BPrivate { 19 20 21 static float 22 FontHeight(const BView* view) 23 { 24 if (!view) 25 return 0.0; 26 27 BFont font; 28 view->GetFont(&font); 29 font_height fheight; 30 font.GetHeight(&fheight); 31 return ceilf(fheight.ascent + fheight.descent + fheight.leading); 32 } 33 34 35 // #pragma mark - 36 37 38 BCalendarView::BCalendarView(BRect frame, const char* name, uint32 resizeMask, 39 uint32 flags) 40 : 41 BView(frame, name, resizeMask, flags), 42 BInvoker(), 43 fSelectionMessage(NULL), 44 fDate(), 45 fFocusChanged(false), 46 fSelectionChanged(false), 47 fStartOfWeek((int32)B_WEEKDAY_MONDAY), 48 fDayNameHeaderVisible(true), 49 fWeekNumberHeaderVisible(true) 50 { 51 _InitObject(); 52 } 53 54 55 BCalendarView::BCalendarView(const char* name, uint32 flags) 56 : 57 BView(name, flags), 58 BInvoker(), 59 fSelectionMessage(NULL), 60 fDate(), 61 fFocusChanged(false), 62 fSelectionChanged(false), 63 fStartOfWeek((int32)B_WEEKDAY_MONDAY), 64 fDayNameHeaderVisible(true), 65 fWeekNumberHeaderVisible(true) 66 { 67 _InitObject(); 68 } 69 70 71 BCalendarView::~BCalendarView() 72 { 73 SetSelectionMessage(NULL); 74 } 75 76 77 BCalendarView::BCalendarView(BMessage* archive) 78 : 79 BView(archive), 80 BInvoker(), 81 fSelectionMessage(NULL), 82 fDate(archive), 83 fFocusChanged(false), 84 fSelectionChanged(false), 85 fStartOfWeek((int32)B_WEEKDAY_MONDAY), 86 fDayNameHeaderVisible(true), 87 fWeekNumberHeaderVisible(true) 88 { 89 if (archive->HasMessage("_invokeMsg")) { 90 BMessage* invokationMessage = new BMessage; 91 archive->FindMessage("_invokeMsg", invokationMessage); 92 SetInvocationMessage(invokationMessage); 93 } 94 95 if (archive->HasMessage("_selectMsg")) { 96 BMessage* selectionMessage = new BMessage; 97 archive->FindMessage("selectMsg", selectionMessage); 98 SetSelectionMessage(selectionMessage); 99 } 100 101 if (archive->FindInt32("_weekStart", &fStartOfWeek) != B_OK) 102 fStartOfWeek = (int32)B_WEEKDAY_MONDAY; 103 104 if (archive->FindBool("_dayHeader", &fDayNameHeaderVisible) != B_OK) 105 fDayNameHeaderVisible = true; 106 107 if (archive->FindBool("_weekHeader", &fWeekNumberHeaderVisible) != B_OK) 108 fWeekNumberHeaderVisible = true; 109 110 _SetupDayNames(); 111 _SetupDayNumbers(); 112 _SetupWeekNumbers(); 113 } 114 115 116 BArchivable* 117 BCalendarView::Instantiate(BMessage* archive) 118 { 119 if (validate_instantiation(archive, "BCalendarView")) 120 return new BCalendarView(archive); 121 122 return NULL; 123 } 124 125 126 status_t 127 BCalendarView::Archive(BMessage* archive, bool deep) const 128 { 129 status_t status = BView::Archive(archive, deep); 130 131 if (status == B_OK && InvocationMessage()) 132 status = archive->AddMessage("_invokeMsg", InvocationMessage()); 133 134 if (status == B_OK && SelectionMessage()) 135 status = archive->AddMessage("_selectMsg", SelectionMessage()); 136 137 if (status == B_OK) 138 status = fDate.Archive(archive); 139 140 if (status == B_OK) 141 status = archive->AddInt32("_weekStart", fStartOfWeek); 142 143 if (status == B_OK) 144 status = archive->AddBool("_dayHeader", fDayNameHeaderVisible); 145 146 if (status == B_OK) 147 status = archive->AddBool("_weekHeader", fWeekNumberHeaderVisible); 148 149 return status; 150 } 151 152 153 void 154 BCalendarView::AttachedToWindow() 155 { 156 BView::AttachedToWindow(); 157 158 if (!Messenger().IsValid()) 159 SetTarget(Window(), NULL); 160 161 SetViewColor(ui_color(B_LIST_BACKGROUND_COLOR)); 162 } 163 164 165 void 166 BCalendarView::FrameResized(float width, float height) 167 { 168 Invalidate(Bounds()); 169 } 170 171 172 void 173 BCalendarView::Draw(BRect updateRect) 174 { 175 if (LockLooper()) { 176 if (fFocusChanged) { 177 _DrawFocusRect(); 178 UnlockLooper(); 179 return; 180 } 181 182 if (fSelectionChanged) { 183 _UpdateSelection(); 184 UnlockLooper(); 185 return; 186 } 187 188 _DrawDays(); 189 _DrawDayHeader(); 190 _DrawWeekHeader(); 191 192 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 193 SetHighColor(tint_color(background, B_DARKEN_3_TINT)); 194 StrokeRect(Bounds()); 195 196 UnlockLooper(); 197 } 198 } 199 200 201 void 202 BCalendarView::DrawDay(BView* owner, BRect frame, const char* text, 203 bool isSelected, bool isEnabled, bool focus) 204 { 205 _DrawItem(owner, frame, text, isSelected, isEnabled, focus); 206 } 207 208 209 void 210 BCalendarView::DrawDayName(BView* owner, BRect frame, const char* text) 211 { 212 // we get the full rect, fake this as the internal function 213 // shrinks the frame to work properly when drawing a day item 214 _DrawItem(owner, frame.InsetByCopy(-1.0, -1.0), text, true); 215 } 216 217 218 void 219 BCalendarView::DrawWeekNumber(BView* owner, BRect frame, const char* text) 220 { 221 // we get the full rect, fake this as the internal function 222 // shrinks the frame to work properly when drawing a day item 223 _DrawItem(owner, frame.InsetByCopy(-1.0, -1.0), text, true); 224 } 225 226 227 uint32 228 BCalendarView::SelectionCommand() const 229 { 230 if (SelectionMessage()) 231 return SelectionMessage()->what; 232 233 return 0; 234 } 235 236 237 BMessage* 238 BCalendarView::SelectionMessage() const 239 { 240 return fSelectionMessage; 241 } 242 243 244 void 245 BCalendarView::SetSelectionMessage(BMessage* message) 246 { 247 delete fSelectionMessage; 248 fSelectionMessage = message; 249 } 250 251 252 uint32 253 BCalendarView::InvocationCommand() const 254 { 255 return BInvoker::Command(); 256 } 257 258 259 BMessage* 260 BCalendarView::InvocationMessage() const 261 { 262 return BInvoker::Message(); 263 } 264 265 266 void 267 BCalendarView::SetInvocationMessage(BMessage* message) 268 { 269 BInvoker::SetMessage(message); 270 } 271 272 273 void 274 BCalendarView::MakeFocus(bool state) 275 { 276 if (IsFocus() == state) 277 return; 278 279 BView::MakeFocus(state); 280 281 // TODO: solve this better 282 fFocusChanged = true; 283 Draw(_RectOfDay(fFocusedDay)); 284 fFocusChanged = false; 285 } 286 287 288 status_t 289 BCalendarView::Invoke(BMessage* message) 290 { 291 bool notify = false; 292 uint32 kind = InvokeKind(¬ify); 293 294 BMessage clone(kind); 295 status_t status = B_BAD_VALUE; 296 297 if (!message && !notify) 298 message = Message(); 299 300 if (!message) { 301 if (!IsWatched()) 302 return status; 303 } else 304 clone = *message; 305 306 clone.AddPointer("source", this); 307 clone.AddInt64("when", (int64)system_time()); 308 clone.AddMessenger("be:sender", BMessenger(this)); 309 310 int32 year; 311 int32 month; 312 _GetYearMonthForSelection(fSelectedDay, &year, &month); 313 314 clone.AddInt32("year", fDate.Year()); 315 clone.AddInt32("month", fDate.Month()); 316 clone.AddInt32("day", fDate.Day()); 317 318 if (message) 319 status = BInvoker::Invoke(&clone); 320 321 SendNotices(kind, &clone); 322 323 return status; 324 } 325 326 327 void 328 BCalendarView::MouseDown(BPoint where) 329 { 330 if (!IsFocus()) { 331 MakeFocus(); 332 Sync(); 333 Window()->UpdateIfNeeded(); 334 } 335 336 BRect frame = Bounds(); 337 if (fDayNameHeaderVisible) 338 frame.top += frame.Height() / 7 - 1.0; 339 340 if (fWeekNumberHeaderVisible) 341 frame.left += frame.Width() / 8 - 1.0; 342 343 if (!frame.Contains(where)) 344 return; 345 346 // try to set to new day 347 frame = _SetNewSelectedDay(where); 348 349 // on success 350 if (fSelectedDay != fNewSelectedDay) { 351 // update focus 352 fFocusChanged = true; 353 fNewFocusedDay = fNewSelectedDay; 354 Draw(_RectOfDay(fFocusedDay)); 355 fFocusChanged = false; 356 357 // update selection 358 fSelectionChanged = true; 359 Draw(frame); 360 Draw(_RectOfDay(fSelectedDay)); 361 fSelectionChanged = false; 362 363 // notify that selection changed 364 InvokeNotify(SelectionMessage(), B_CONTROL_MODIFIED); 365 } 366 367 int32 clicks; 368 // on double click invoke 369 BMessage* message = Looper()->CurrentMessage(); 370 if (message->FindInt32("clicks", &clicks) == B_OK && clicks > 1) 371 Invoke(); 372 } 373 374 375 void 376 BCalendarView::KeyDown(const char* bytes, int32 numBytes) 377 { 378 const int32 kRows = 6; 379 const int32 kColumns = 7; 380 381 int32 row = fFocusedDay.row; 382 int32 column = fFocusedDay.column; 383 384 switch (bytes[0]) { 385 case B_LEFT_ARROW: 386 column -= 1; 387 if (column < 0) { 388 column = kColumns - 1; 389 row -= 1; 390 if (row >= 0) 391 fFocusChanged = true; 392 } else 393 fFocusChanged = true; 394 break; 395 396 case B_RIGHT_ARROW: 397 column += 1; 398 if (column == kColumns) { 399 column = 0; 400 row += 1; 401 if (row < kRows) 402 fFocusChanged = true; 403 } else 404 fFocusChanged = true; 405 break; 406 407 case B_UP_ARROW: 408 row -= 1; 409 if (row >= 0) 410 fFocusChanged = true; 411 break; 412 413 case B_DOWN_ARROW: 414 row += 1; 415 if (row < kRows) 416 fFocusChanged = true; 417 break; 418 419 case B_PAGE_UP: 420 { 421 BDate date(fDate); 422 date.AddMonths(-1); 423 SetDate(date); 424 425 Invoke(); 426 break; 427 } 428 429 case B_PAGE_DOWN: 430 { 431 BDate date(fDate); 432 date.AddMonths(1); 433 SetDate(date); 434 435 Invoke(); 436 break; 437 } 438 439 case B_RETURN: 440 case B_SPACE: 441 { 442 fSelectionChanged = true; 443 BPoint pt = _RectOfDay(fFocusedDay).LeftTop(); 444 Draw(_SetNewSelectedDay(pt + BPoint(4.0, 4.0))); 445 Draw(_RectOfDay(fSelectedDay)); 446 fSelectionChanged = false; 447 448 Invoke(); 449 break; 450 } 451 452 default: 453 BView::KeyDown(bytes, numBytes); 454 break; 455 } 456 457 if (fFocusChanged) { 458 fNewFocusedDay.SetTo(row, column); 459 Draw(_RectOfDay(fFocusedDay)); 460 Draw(_RectOfDay(fNewFocusedDay)); 461 fFocusChanged = false; 462 } 463 } 464 465 466 void 467 BCalendarView::ResizeToPreferred() 468 { 469 float width; 470 float height; 471 472 GetPreferredSize(&width, &height); 473 BView::ResizeTo(width, height); 474 } 475 476 477 void 478 BCalendarView::GetPreferredSize(float* width, float* height) 479 { 480 _GetPreferredSize(width, height); 481 } 482 483 484 BSize 485 BCalendarView::MaxSize() 486 { 487 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 488 BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); 489 } 490 491 492 BSize 493 BCalendarView::MinSize() 494 { 495 float width, height; 496 _GetPreferredSize(&width, &height); 497 return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(width, height)); 498 } 499 500 501 BSize 502 BCalendarView::PreferredSize() 503 { 504 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize()); 505 } 506 507 508 int32 509 BCalendarView::Day() const 510 { 511 return fDate.Day(); 512 } 513 514 515 int32 516 BCalendarView::Year() const 517 { 518 int32 year; 519 _GetYearMonthForSelection(fSelectedDay, &year, NULL); 520 521 return year; 522 } 523 524 525 int32 526 BCalendarView::Month() const 527 { 528 int32 month; 529 _GetYearMonthForSelection(fSelectedDay, NULL, &month); 530 531 return month; 532 } 533 534 535 bool 536 BCalendarView::SetDay(int32 day) 537 { 538 BDate date = Date(); 539 date.SetDay(day); 540 if (!date.IsValid()) 541 return false; 542 SetDate(date); 543 return true; 544 } 545 546 547 bool 548 BCalendarView::SetMonth(int32 month) 549 { 550 if (month < 1 || month > 12) 551 return false; 552 BDate date = Date(); 553 int32 oldDay = date.Day(); 554 555 date.SetMonth(month); 556 date.SetDay(1); // make sure the date is valid 557 558 // We must make sure that the day in month fits inside the new month. 559 if (oldDay > date.DaysInMonth()) 560 date.SetDay(date.DaysInMonth()); 561 else 562 date.SetDay(oldDay); 563 SetDate(date); 564 return true; 565 } 566 567 568 bool 569 BCalendarView::SetYear(int32 year) 570 { 571 BDate date = Date(); 572 573 // This can fail when going from 29 feb. on a leap year to a non-leap year. 574 if (date.Month() == 2 && date.Day() == 29 && !date.IsLeapYear(year)) 575 date.SetDay(28); 576 577 // TODO we should also handle the "hole" at the switch between Julian and 578 // Gregorian calendars, which will result in an invalid date. 579 580 date.SetYear(year); 581 SetDate(date); 582 return true; 583 } 584 585 586 BDate 587 BCalendarView::Date() const 588 { 589 int32 year; 590 int32 month; 591 _GetYearMonthForSelection(fSelectedDay, &year, &month); 592 return BDate(year, month, fDate.Day()); 593 } 594 595 596 bool 597 BCalendarView::SetDate(const BDate& date) 598 { 599 if (!date.IsValid()) 600 return false; 601 602 if (fDate == date) 603 return true; 604 605 if (fDate.Year() == date.Year() && fDate.Month() == date.Month()) { 606 fDate = date; 607 608 _SetToDay(); 609 // update focus 610 fFocusChanged = true; 611 Draw(_RectOfDay(fFocusedDay)); 612 fFocusChanged = false; 613 614 // update selection 615 fSelectionChanged = true; 616 Draw(_RectOfDay(fSelectedDay)); 617 Draw(_RectOfDay(fNewSelectedDay)); 618 fSelectionChanged = false; 619 } else { 620 fDate = date; 621 622 _SetupDayNumbers(); 623 _SetupWeekNumbers(); 624 625 BRect frame = Bounds(); 626 if (fDayNameHeaderVisible) 627 frame.top += frame.Height() / 7 - 1.0; 628 629 if (fWeekNumberHeaderVisible) 630 frame.left += frame.Width() / 8 - 1.0; 631 632 Draw(frame.InsetBySelf(4.0, 4.0)); 633 } 634 635 return true; 636 } 637 638 639 bool 640 BCalendarView::SetDate(int32 year, int32 month, int32 day) 641 { 642 return SetDate(BDate(year, month, day)); 643 } 644 645 646 BWeekday 647 BCalendarView::StartOfWeek() const 648 { 649 return BWeekday(fStartOfWeek); 650 } 651 652 653 void 654 BCalendarView::SetStartOfWeek(BWeekday startOfWeek) 655 { 656 if (fStartOfWeek == (int32)startOfWeek) 657 return; 658 659 fStartOfWeek = (int32)startOfWeek; 660 661 _SetupDayNames(); 662 _SetupDayNumbers(); 663 _SetupWeekNumbers(); 664 665 Invalidate(Bounds().InsetBySelf(1.0, 1.0)); 666 } 667 668 669 bool 670 BCalendarView::IsDayNameHeaderVisible() const 671 { 672 return fDayNameHeaderVisible; 673 } 674 675 676 void 677 BCalendarView::SetDayNameHeaderVisible(bool visible) 678 { 679 if (fDayNameHeaderVisible == visible) 680 return; 681 682 fDayNameHeaderVisible = visible; 683 Invalidate(Bounds().InsetBySelf(1.0, 1.0)); 684 } 685 686 687 bool 688 BCalendarView::IsWeekNumberHeaderVisible() const 689 { 690 return fWeekNumberHeaderVisible; 691 } 692 693 694 void 695 BCalendarView::SetWeekNumberHeaderVisible(bool visible) 696 { 697 if (fWeekNumberHeaderVisible == visible) 698 return; 699 700 fWeekNumberHeaderVisible = visible; 701 Invalidate(Bounds().InsetBySelf(1.0, 1.0)); 702 } 703 704 705 void 706 BCalendarView::_InitObject() 707 { 708 fDate = BDate::CurrentDate(B_LOCAL_TIME); 709 710 BDateFormat().GetStartOfWeek((BWeekday*)&fStartOfWeek); 711 712 _SetupDayNames(); 713 _SetupDayNumbers(); 714 _SetupWeekNumbers(); 715 } 716 717 718 void 719 BCalendarView::_SetToDay() 720 { 721 BDate date(fDate.Year(), fDate.Month(), 1); 722 if (!date.IsValid()) 723 return; 724 725 const int32 firstDayOffset = (7 + date.DayOfWeek() - fStartOfWeek) % 7; 726 727 int32 day = 1 - firstDayOffset; 728 for (int32 row = 0; row < 6; ++row) { 729 for (int32 column = 0; column < 7; ++column) { 730 if (day == fDate.Day()) { 731 fNewFocusedDay.SetTo(row, column); 732 fNewSelectedDay.SetTo(row, column); 733 return; 734 } 735 day++; 736 } 737 } 738 739 fNewFocusedDay.SetTo(0, 0); 740 fNewSelectedDay.SetTo(0, 0); 741 } 742 743 744 void 745 BCalendarView::_GetYearMonthForSelection(const Selection& selection, 746 int32* year, int32* month) const 747 { 748 BDate startOfMonth(fDate.Year(), fDate.Month(), 1); 749 const int32 firstDayOffset 750 = (7 + startOfMonth.DayOfWeek() - fStartOfWeek) % 7; 751 const int32 daysInMonth = startOfMonth.DaysInMonth(); 752 753 BDate date(fDate); 754 const int32 dayOffset = selection.row * 7 + selection.column; 755 if (dayOffset < firstDayOffset) 756 date.AddMonths(-1); 757 else if (dayOffset >= firstDayOffset + daysInMonth) 758 date.AddMonths(1); 759 if (year != NULL) 760 *year = date.Year(); 761 if (month != NULL) 762 *month = date.Month(); 763 } 764 765 766 void 767 BCalendarView::_GetPreferredSize(float* _width, float* _height) 768 { 769 BFont font; 770 GetFont(&font); 771 font_height fontHeight; 772 font.GetHeight(&fontHeight); 773 774 const float height = FontHeight(this) + 4.0; 775 776 int32 rows = 7; 777 if (!fDayNameHeaderVisible) 778 rows = 6; 779 780 // height = font height * rows + 8 px border 781 *_height = height * rows + 8.0; 782 783 float width = 0.0; 784 for (int32 column = 0; column < 7; ++column) { 785 float tmp = StringWidth(fDayNames[column].String()) + 2.0; 786 width = tmp > width ? tmp : width; 787 } 788 789 int32 columns = 8; 790 if (!fWeekNumberHeaderVisible) 791 columns = 7; 792 793 // width = max width day name * 8 column + 8 px border 794 *_width = width * columns + 8.0; 795 } 796 797 798 void 799 BCalendarView::_SetupDayNames() 800 { 801 for (int32 i = 0; i <= 6; ++i) 802 fDayNames[i] = fDate.ShortDayName(1 + (fStartOfWeek - 1 + i) % 7); 803 } 804 805 806 void 807 BCalendarView::_SetupDayNumbers() 808 { 809 BDate startOfMonth(fDate.Year(), fDate.Month(), 1); 810 if (!startOfMonth.IsValid()) 811 return; 812 813 fFocusedDay.SetTo(0, 0); 814 fSelectedDay.SetTo(0, 0); 815 fNewFocusedDay.SetTo(0, 0); 816 817 const int32 daysInMonth = startOfMonth.DaysInMonth(); 818 const int32 firstDayOffset 819 = (7 + startOfMonth.DayOfWeek() - fStartOfWeek) % 7; 820 821 // calc the last day one month before 822 BDate lastDayInMonthBefore(startOfMonth); 823 lastDayInMonthBefore.AddDays(-1); 824 const int32 lastDayBefore = lastDayInMonthBefore.DaysInMonth(); 825 826 int32 counter = 0; 827 int32 firstDayAfter = 1; 828 for (int32 row = 0; row < 6; ++row) { 829 for (int32 column = 0; column < 7; ++column) { 830 int32 day = 1 + counter - firstDayOffset; 831 if (counter < firstDayOffset) 832 day += lastDayBefore; 833 else if (counter >= firstDayOffset + daysInMonth) 834 day = firstDayAfter++; 835 else if (day == fDate.Day()) { 836 fFocusedDay.SetTo(row, column); 837 fSelectedDay.SetTo(row, column); 838 fNewFocusedDay.SetTo(row, column); 839 } 840 counter++; 841 fDayNumbers[row][column].Truncate(0); 842 fDayNumbers[row][column] << day; 843 } 844 } 845 } 846 847 848 void 849 BCalendarView::_SetupWeekNumbers() 850 { 851 BDate date(fDate.Year(), fDate.Month(), 1); 852 if (!date.IsValid()) 853 return; 854 855 for (int32 row = 0; row < 6; ++row) { 856 fWeekNumbers[row].SetTo(""); 857 fWeekNumbers[row] << date.WeekNumber(); 858 date.AddDays(7); 859 } 860 } 861 862 863 void 864 BCalendarView::_DrawDay(int32 currRow, int32 currColumn, int32 row, 865 int32 column, int32 counter, BRect frame, const char* text, bool focus) 866 { 867 BDate startOfMonth(fDate.Year(), fDate.Month(), 1); 868 const int32 firstDayOffset 869 = (7 + startOfMonth.DayOfWeek() - fStartOfWeek) % 7; 870 const int32 daysMonth = startOfMonth.DaysInMonth(); 871 872 bool enabled = true; 873 bool selected = false; 874 // check for the current date 875 if (currRow == row && currColumn == column) { 876 selected = true; // draw current date selected 877 if (counter <= firstDayOffset || counter > firstDayOffset + daysMonth) { 878 enabled = false; // days of month before or after 879 selected = false; // not selected but able to get focus 880 } 881 } else { 882 if (counter <= firstDayOffset || counter > firstDayOffset + daysMonth) 883 enabled = false; // days of month before or after 884 } 885 886 DrawDay(this, frame, text, selected, enabled, focus); 887 } 888 889 890 void 891 BCalendarView::_DrawDays() 892 { 893 BRect frame = _FirstCalendarItemFrame(); 894 895 const int32 currRow = fSelectedDay.row; 896 const int32 currColumn = fSelectedDay.column; 897 898 const bool isFocus = IsFocus(); 899 const int32 focusRow = fFocusedDay.row; 900 const int32 focusColumn = fFocusedDay.column; 901 902 int32 counter = 0; 903 for (int32 row = 0; row < 6; ++row) { 904 BRect tmp = frame; 905 for (int32 column = 0; column < 7; ++column) { 906 counter++; 907 const char* day = fDayNumbers[row][column].String(); 908 bool focus = isFocus && focusRow == row && focusColumn == column; 909 _DrawDay(currRow, currColumn, row, column, counter, tmp, day, 910 focus); 911 912 tmp.OffsetBy(tmp.Width(), 0.0); 913 } 914 frame.OffsetBy(0.0, frame.Height()); 915 } 916 } 917 918 919 void 920 BCalendarView::_DrawFocusRect() 921 { 922 BRect frame = _FirstCalendarItemFrame(); 923 924 const int32 currRow = fSelectedDay.row; 925 const int32 currColumn = fSelectedDay.column; 926 927 const int32 focusRow = fFocusedDay.row; 928 const int32 focusColumn = fFocusedDay.column; 929 930 int32 counter = 0; 931 for (int32 row = 0; row < 6; ++row) { 932 BRect tmp = frame; 933 for (int32 column = 0; column < 7; ++column) { 934 counter++; 935 if (fNewFocusedDay.row == row && fNewFocusedDay.column == column) { 936 fFocusedDay.SetTo(row, column); 937 938 bool focus = IsFocus() && true; 939 const char* day = fDayNumbers[row][column].String(); 940 _DrawDay(currRow, currColumn, row, column, counter, tmp, day, 941 focus); 942 } else if (focusRow == row && focusColumn == column) { 943 const char* day = fDayNumbers[row][column].String(); 944 _DrawDay(currRow, currColumn, row, column, counter, tmp, day, 945 false); 946 } 947 tmp.OffsetBy(tmp.Width(), 0.0); 948 } 949 frame.OffsetBy(0.0, frame.Height()); 950 } 951 } 952 953 954 void 955 BCalendarView::_DrawDayHeader() 956 { 957 if (!fDayNameHeaderVisible) 958 return; 959 960 int32 offset = 1; 961 int32 columns = 8; 962 if (!fWeekNumberHeaderVisible) { 963 offset = 0; 964 columns = 7; 965 } 966 967 BRect frame = Bounds(); 968 frame.right = frame.Width() / columns - 1.0; 969 frame.bottom = frame.Height() / 7.0 - 2.0; 970 frame.OffsetBy(4.0, 4.0); 971 972 for (int32 i = 0; i < columns; ++i) { 973 if (i == 0 && fWeekNumberHeaderVisible) { 974 DrawDayName(this, frame, ""); 975 frame.OffsetBy(frame.Width(), 0.0); 976 continue; 977 } 978 DrawDayName(this, frame, fDayNames[i - offset].String()); 979 frame.OffsetBy(frame.Width(), 0.0); 980 } 981 } 982 983 984 void 985 BCalendarView::_DrawWeekHeader() 986 { 987 if (!fWeekNumberHeaderVisible) 988 return; 989 990 int32 rows = 7; 991 if (!fDayNameHeaderVisible) 992 rows = 6; 993 994 BRect frame = Bounds(); 995 frame.right = frame.Width() / 8.0 - 2.0; 996 frame.bottom = frame.Height() / rows - 1.0; 997 998 float offsetY = 4.0; 999 if (fDayNameHeaderVisible) 1000 offsetY += frame.Height(); 1001 1002 frame.OffsetBy(4.0, offsetY); 1003 1004 for (int32 row = 0; row < 6; ++row) { 1005 DrawWeekNumber(this, frame, fWeekNumbers[row].String()); 1006 frame.OffsetBy(0.0, frame.Height()); 1007 } 1008 } 1009 1010 1011 void 1012 BCalendarView::_DrawItem(BView* owner, BRect frame, const char* text, 1013 bool isSelected, bool isEnabled, bool focus) 1014 { 1015 rgb_color lColor = LowColor(); 1016 rgb_color highColor = HighColor(); 1017 1018 rgb_color textColor = ui_color(B_LIST_ITEM_TEXT_COLOR); 1019 float tintDisabled = B_LIGHTEN_2_TINT; 1020 1021 if (textColor.red + textColor.green + textColor.blue > 125 * 3) 1022 tintDisabled = B_DARKEN_2_TINT; 1023 1024 if (isSelected) { 1025 SetHighColor(ui_color(B_LIST_SELECTED_BACKGROUND_COLOR)); 1026 textColor = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR); 1027 } else 1028 SetHighColor(ui_color(B_LIST_BACKGROUND_COLOR)); 1029 1030 SetLowColor(HighColor()); 1031 1032 FillRect(frame.InsetByCopy(1.0, 1.0)); 1033 1034 if (focus) { 1035 SetHighColor(keyboard_navigation_color()); 1036 StrokeRect(frame.InsetByCopy(1.0, 1.0)); 1037 } 1038 1039 SetHighColor(textColor); 1040 if (!isEnabled) 1041 SetHighColor(tint_color(textColor, tintDisabled)); 1042 1043 float offsetH = frame.Width() / 2.0; 1044 float offsetV = frame.Height() / 2.0 + FontHeight(owner) / 2.0 - 2.0; 1045 1046 DrawString(text, BPoint(frame.right - offsetH - StringWidth(text) / 2.0, 1047 frame.top + offsetV)); 1048 1049 SetLowColor(lColor); 1050 SetHighColor(highColor); 1051 } 1052 1053 1054 void 1055 BCalendarView::_UpdateSelection() 1056 { 1057 BRect frame = _FirstCalendarItemFrame(); 1058 1059 const int32 currRow = fSelectedDay.row; 1060 const int32 currColumn = fSelectedDay.column; 1061 1062 const int32 focusRow = fFocusedDay.row; 1063 const int32 focusColumn = fFocusedDay.column; 1064 1065 int32 counter = 0; 1066 for (int32 row = 0; row < 6; ++row) { 1067 BRect tmp = frame; 1068 for (int32 column = 0; column < 7; ++column) { 1069 counter++; 1070 if (fNewSelectedDay.row == row 1071 && fNewSelectedDay.column == column) { 1072 fSelectedDay.SetTo(row, column); 1073 1074 const char* day = fDayNumbers[row][column].String(); 1075 bool focus = IsFocus() && focusRow == row 1076 && focusColumn == column; 1077 _DrawDay(row, column, row, column, counter, tmp, day, focus); 1078 } else if (currRow == row && currColumn == column) { 1079 const char* day = fDayNumbers[row][column].String(); 1080 bool focus = IsFocus() && focusRow == row 1081 && focusColumn == column; 1082 _DrawDay(currRow, currColumn, -1, -1, counter, tmp, day, focus); 1083 } 1084 tmp.OffsetBy(tmp.Width(), 0.0); 1085 } 1086 frame.OffsetBy(0.0, frame.Height()); 1087 } 1088 } 1089 1090 1091 BRect 1092 BCalendarView::_FirstCalendarItemFrame() const 1093 { 1094 int32 rows = 7; 1095 int32 columns = 8; 1096 1097 if (!fDayNameHeaderVisible) 1098 rows = 6; 1099 1100 if (!fWeekNumberHeaderVisible) 1101 columns = 7; 1102 1103 BRect frame = Bounds(); 1104 frame.right = frame.Width() / columns - 1.0; 1105 frame.bottom = frame.Height() / rows - 1.0; 1106 1107 float offsetY = 4.0; 1108 if (fDayNameHeaderVisible) 1109 offsetY += frame.Height(); 1110 1111 float offsetX = 4.0; 1112 if (fWeekNumberHeaderVisible) 1113 offsetX += frame.Width(); 1114 1115 return frame.OffsetBySelf(offsetX, offsetY); 1116 } 1117 1118 1119 BRect 1120 BCalendarView::_SetNewSelectedDay(const BPoint& where) 1121 { 1122 BRect frame = _FirstCalendarItemFrame(); 1123 1124 int32 counter = 0; 1125 for (int32 row = 0; row < 6; ++row) { 1126 BRect tmp = frame; 1127 for (int32 column = 0; column < 7; ++column) { 1128 counter++; 1129 if (tmp.Contains(where)) { 1130 fNewSelectedDay.SetTo(row, column); 1131 int32 year; 1132 int32 month; 1133 _GetYearMonthForSelection(fNewSelectedDay, &year, &month); 1134 if (month == fDate.Month()) { 1135 // only change date if a day in the current month has been 1136 // selected 1137 int32 day = atoi(fDayNumbers[row][column].String()); 1138 fDate.SetDate(year, month, day); 1139 } 1140 return tmp; 1141 } 1142 tmp.OffsetBy(tmp.Width(), 0.0); 1143 } 1144 frame.OffsetBy(0.0, frame.Height()); 1145 } 1146 1147 return frame; 1148 } 1149 1150 1151 BRect 1152 BCalendarView::_RectOfDay(const Selection& selection) const 1153 { 1154 BRect frame = _FirstCalendarItemFrame(); 1155 1156 int32 counter = 0; 1157 for (int32 row = 0; row < 6; ++row) { 1158 BRect tmp = frame; 1159 for (int32 column = 0; column < 7; ++column) { 1160 counter++; 1161 if (selection.row == row && selection.column == column) 1162 return tmp; 1163 tmp.OffsetBy(tmp.Width(), 0.0); 1164 } 1165 frame.OffsetBy(0.0, frame.Height()); 1166 } 1167 1168 return frame; 1169 } 1170 1171 1172 } // namespace BPrivate 1173