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