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