/* * Copyright 2007-2011, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Julun */ #include "CalendarView.h" #include #include #include #include namespace BPrivate { static float FontHeight(const BView* view) { if (!view) return 0.0; BFont font; view->GetFont(&font); font_height fheight; font.GetHeight(&fheight); return ceilf(fheight.ascent + fheight.descent + fheight.leading); } // #pragma mark - BCalendarView::BCalendarView(BRect frame, const char* name, uint32 resizeMask, uint32 flags) : BView(frame, name, resizeMask, flags), BInvoker(), fSelectionMessage(NULL), fDate(), fCurrentDate(BDate::CurrentDate(B_LOCAL_TIME)), fFocusChanged(false), fSelectionChanged(false), fCurrentDayChanged(false), fStartOfWeek((int32)B_WEEKDAY_MONDAY), fDayNameHeaderVisible(true), fWeekNumberHeaderVisible(true) { _InitObject(); } BCalendarView::BCalendarView(const char* name, uint32 flags) : BView(name, flags), BInvoker(), fSelectionMessage(NULL), fDate(), fCurrentDate(BDate::CurrentDate(B_LOCAL_TIME)), fFocusChanged(false), fSelectionChanged(false), fCurrentDayChanged(false), fStartOfWeek((int32)B_WEEKDAY_MONDAY), fDayNameHeaderVisible(true), fWeekNumberHeaderVisible(true) { _InitObject(); } BCalendarView::~BCalendarView() { SetSelectionMessage(NULL); } BCalendarView::BCalendarView(BMessage* archive) : BView(archive), BInvoker(), fSelectionMessage(NULL), fDate(archive), fCurrentDate(BDate::CurrentDate(B_LOCAL_TIME)), fFocusChanged(false), fSelectionChanged(false), fCurrentDayChanged(false), fStartOfWeek((int32)B_WEEKDAY_MONDAY), fDayNameHeaderVisible(true), fWeekNumberHeaderVisible(true) { if (archive->HasMessage("_invokeMsg")) { BMessage* invokationMessage = new BMessage; archive->FindMessage("_invokeMsg", invokationMessage); SetInvocationMessage(invokationMessage); } if (archive->HasMessage("_selectMsg")) { BMessage* selectionMessage = new BMessage; archive->FindMessage("selectMsg", selectionMessage); SetSelectionMessage(selectionMessage); } if (archive->FindInt32("_weekStart", &fStartOfWeek) != B_OK) fStartOfWeek = (int32)B_WEEKDAY_MONDAY; if (archive->FindBool("_dayHeader", &fDayNameHeaderVisible) != B_OK) fDayNameHeaderVisible = true; if (archive->FindBool("_weekHeader", &fWeekNumberHeaderVisible) != B_OK) fWeekNumberHeaderVisible = true; _SetupDayNames(); _SetupDayNumbers(); _SetupWeekNumbers(); } BArchivable* BCalendarView::Instantiate(BMessage* archive) { if (validate_instantiation(archive, "BCalendarView")) return new BCalendarView(archive); return NULL; } status_t BCalendarView::Archive(BMessage* archive, bool deep) const { status_t status = BView::Archive(archive, deep); if (status == B_OK && InvocationMessage()) status = archive->AddMessage("_invokeMsg", InvocationMessage()); if (status == B_OK && SelectionMessage()) status = archive->AddMessage("_selectMsg", SelectionMessage()); if (status == B_OK) status = fDate.Archive(archive); if (status == B_OK) status = archive->AddInt32("_weekStart", fStartOfWeek); if (status == B_OK) status = archive->AddBool("_dayHeader", fDayNameHeaderVisible); if (status == B_OK) status = archive->AddBool("_weekHeader", fWeekNumberHeaderVisible); return status; } void BCalendarView::AttachedToWindow() { BView::AttachedToWindow(); if (!Messenger().IsValid()) SetTarget(Window(), NULL); SetViewUIColor(B_LIST_BACKGROUND_COLOR); } void BCalendarView::FrameResized(float width, float height) { _SetupDayNames(); Invalidate(Bounds()); } void BCalendarView::Draw(BRect updateRect) { if (LockLooper()) { if (fFocusChanged) { _DrawFocusRect(); UnlockLooper(); return; } if (fSelectionChanged) { _UpdateSelection(); UnlockLooper(); return; } if (fCurrentDayChanged) { _UpdateCurrentDay(); UnlockLooper(); return; } _DrawDays(); _DrawDayHeader(); _DrawWeekHeader(); rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); SetHighColor(tint_color(background, B_DARKEN_3_TINT)); StrokeRect(Bounds()); UnlockLooper(); } } void BCalendarView::DrawDay(BView* owner, BRect frame, const char* text, bool isSelected, bool isEnabled, bool focus, bool highlight) { _DrawItem(owner, frame, text, isSelected, isEnabled, focus, highlight); } void BCalendarView::DrawDayName(BView* owner, BRect frame, const char* text) { // we get the full rect, fake this as the internal function // shrinks the frame to work properly when drawing a day item _DrawItem(owner, frame.InsetByCopy(-1.0, -1.0), text, true); } void BCalendarView::DrawWeekNumber(BView* owner, BRect frame, const char* text) { // we get the full rect, fake this as the internal function // shrinks the frame to work properly when drawing a day item _DrawItem(owner, frame.InsetByCopy(-1.0, -1.0), text, true); } uint32 BCalendarView::SelectionCommand() const { if (SelectionMessage()) return SelectionMessage()->what; return 0; } BMessage* BCalendarView::SelectionMessage() const { return fSelectionMessage; } void BCalendarView::SetSelectionMessage(BMessage* message) { delete fSelectionMessage; fSelectionMessage = message; } uint32 BCalendarView::InvocationCommand() const { return BInvoker::Command(); } BMessage* BCalendarView::InvocationMessage() const { return BInvoker::Message(); } void BCalendarView::SetInvocationMessage(BMessage* message) { BInvoker::SetMessage(message); } void BCalendarView::MakeFocus(bool state) { if (IsFocus() == state) return; BView::MakeFocus(state); // TODO: solve this better fFocusChanged = true; Draw(_RectOfDay(fFocusedDay)); fFocusChanged = false; } status_t BCalendarView::Invoke(BMessage* message) { bool notify = false; uint32 kind = InvokeKind(¬ify); BMessage clone(kind); status_t status = B_BAD_VALUE; if (!message && !notify) message = Message(); if (!message) { if (!IsWatched()) return status; } else clone = *message; clone.AddPointer("source", this); clone.AddInt64("when", (int64)system_time()); clone.AddMessenger("be:sender", BMessenger(this)); int32 year; int32 month; _GetYearMonthForSelection(fSelectedDay, &year, &month); clone.AddInt32("year", fDate.Year()); clone.AddInt32("month", fDate.Month()); clone.AddInt32("day", fDate.Day()); if (message) status = BInvoker::Invoke(&clone); SendNotices(kind, &clone); return status; } void BCalendarView::MouseDown(BPoint where) { if (!IsFocus()) { MakeFocus(); Sync(); Window()->UpdateIfNeeded(); } BRect frame = Bounds(); if (fDayNameHeaderVisible) frame.top += frame.Height() / 7 - 1.0; if (fWeekNumberHeaderVisible) frame.left += frame.Width() / 8 - 1.0; if (!frame.Contains(where)) return; // try to set to new day frame = _SetNewSelectedDay(where); // on success if (fSelectedDay != fNewSelectedDay) { // update focus fFocusChanged = true; fNewFocusedDay = fNewSelectedDay; Draw(_RectOfDay(fFocusedDay)); fFocusChanged = false; // update selection fSelectionChanged = true; Draw(frame); Draw(_RectOfDay(fSelectedDay)); fSelectionChanged = false; // notify that selection changed InvokeNotify(SelectionMessage(), B_CONTROL_MODIFIED); } int32 clicks; // on double click invoke BMessage* message = Looper()->CurrentMessage(); if (message->FindInt32("clicks", &clicks) == B_OK && clicks > 1) Invoke(); } void BCalendarView::KeyDown(const char* bytes, int32 numBytes) { const int32 kRows = 6; const int32 kColumns = 7; int32 row = fFocusedDay.row; int32 column = fFocusedDay.column; switch (bytes[0]) { case B_LEFT_ARROW: column -= 1; if (column < 0) { column = kColumns - 1; row -= 1; if (row >= 0) fFocusChanged = true; } else fFocusChanged = true; break; case B_RIGHT_ARROW: column += 1; if (column == kColumns) { column = 0; row += 1; if (row < kRows) fFocusChanged = true; } else fFocusChanged = true; break; case B_UP_ARROW: row -= 1; if (row >= 0) fFocusChanged = true; break; case B_DOWN_ARROW: row += 1; if (row < kRows) fFocusChanged = true; break; case B_PAGE_UP: { BDate date(fDate); date.AddMonths(-1); SetDate(date); Invoke(); break; } case B_PAGE_DOWN: { BDate date(fDate); date.AddMonths(1); SetDate(date); Invoke(); break; } case B_RETURN: case B_SPACE: { fSelectionChanged = true; BPoint pt = _RectOfDay(fFocusedDay).LeftTop(); Draw(_SetNewSelectedDay(pt + BPoint(4.0, 4.0))); Draw(_RectOfDay(fSelectedDay)); fSelectionChanged = false; Invoke(); break; } default: BView::KeyDown(bytes, numBytes); break; } if (fFocusChanged) { fNewFocusedDay.SetTo(row, column); Draw(_RectOfDay(fFocusedDay)); Draw(_RectOfDay(fNewFocusedDay)); fFocusChanged = false; } } void BCalendarView::Pulse() { _UpdateCurrentDate(); } void BCalendarView::ResizeToPreferred() { float width; float height; GetPreferredSize(&width, &height); BView::ResizeTo(width, height); } void BCalendarView::GetPreferredSize(float* width, float* height) { _GetPreferredSize(width, height); } BSize BCalendarView::MaxSize() { return BLayoutUtils::ComposeSize(ExplicitMaxSize(), BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); } BSize BCalendarView::MinSize() { float width, height; _GetPreferredSize(&width, &height); return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(width, height)); } BSize BCalendarView::PreferredSize() { return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize()); } int32 BCalendarView::Day() const { return fDate.Day(); } int32 BCalendarView::Year() const { int32 year; _GetYearMonthForSelection(fSelectedDay, &year, NULL); return year; } int32 BCalendarView::Month() const { int32 month; _GetYearMonthForSelection(fSelectedDay, NULL, &month); return month; } bool BCalendarView::SetDay(int32 day) { BDate date = Date(); date.SetDay(day); if (!date.IsValid()) return false; SetDate(date); return true; } bool BCalendarView::SetMonth(int32 month) { if (month < 1 || month > 12) return false; BDate date = Date(); int32 oldDay = date.Day(); date.SetMonth(month); date.SetDay(1); // make sure the date is valid // We must make sure that the day in month fits inside the new month. if (oldDay > date.DaysInMonth()) date.SetDay(date.DaysInMonth()); else date.SetDay(oldDay); SetDate(date); return true; } bool BCalendarView::SetYear(int32 year) { BDate date = Date(); // This can fail when going from 29 feb. on a leap year to a non-leap year. if (date.Month() == 2 && date.Day() == 29 && !date.IsLeapYear(year)) date.SetDay(28); // TODO we should also handle the "hole" at the switch between Julian and // Gregorian calendars, which will result in an invalid date. date.SetYear(year); SetDate(date); return true; } BDate BCalendarView::Date() const { int32 year; int32 month; _GetYearMonthForSelection(fSelectedDay, &year, &month); return BDate(year, month, fDate.Day()); } bool BCalendarView::SetDate(const BDate& date) { if (!date.IsValid()) return false; if (fDate == date) return true; if (fDate.Year() == date.Year() && fDate.Month() == date.Month()) { fDate = date; _SetToDay(); // update focus fFocusChanged = true; Draw(_RectOfDay(fFocusedDay)); fFocusChanged = false; // update selection fSelectionChanged = true; Draw(_RectOfDay(fSelectedDay)); Draw(_RectOfDay(fNewSelectedDay)); fSelectionChanged = false; } else { fDate = date; _SetupDayNumbers(); _SetupWeekNumbers(); BRect frame = Bounds(); if (fDayNameHeaderVisible) frame.top += frame.Height() / 7 - 1.0; if (fWeekNumberHeaderVisible) frame.left += frame.Width() / 8 - 1.0; Draw(frame.InsetBySelf(4.0, 4.0)); } return true; } bool BCalendarView::SetDate(int32 year, int32 month, int32 day) { return SetDate(BDate(year, month, day)); } BWeekday BCalendarView::StartOfWeek() const { return BWeekday(fStartOfWeek); } void BCalendarView::SetStartOfWeek(BWeekday startOfWeek) { if (fStartOfWeek == (int32)startOfWeek) return; fStartOfWeek = (int32)startOfWeek; _SetupDayNames(); _SetupDayNumbers(); _SetupWeekNumbers(); Invalidate(Bounds().InsetBySelf(1.0, 1.0)); } bool BCalendarView::IsDayNameHeaderVisible() const { return fDayNameHeaderVisible; } void BCalendarView::SetDayNameHeaderVisible(bool visible) { if (fDayNameHeaderVisible == visible) return; fDayNameHeaderVisible = visible; Invalidate(Bounds().InsetBySelf(1.0, 1.0)); } void BCalendarView::UpdateDayNameHeader() { if (!fDayNameHeaderVisible) return; _SetupDayNames(); Invalidate(Bounds().InsetBySelf(1.0, 1.0)); } bool BCalendarView::IsWeekNumberHeaderVisible() const { return fWeekNumberHeaderVisible; } void BCalendarView::SetWeekNumberHeaderVisible(bool visible) { if (fWeekNumberHeaderVisible == visible) return; fWeekNumberHeaderVisible = visible; Invalidate(Bounds().InsetBySelf(1.0, 1.0)); } void BCalendarView::_InitObject() { fDate = BDate::CurrentDate(B_LOCAL_TIME); BDateFormat().GetStartOfWeek((BWeekday*)&fStartOfWeek); _SetupDayNames(); _SetupDayNumbers(); _SetupWeekNumbers(); } void BCalendarView::_SetToDay() { BDate date(fDate.Year(), fDate.Month(), 1); if (!date.IsValid()) return; const int32 firstDayOffset = (7 + date.DayOfWeek() - fStartOfWeek) % 7; int32 day = 1 - firstDayOffset; for (int32 row = 0; row < 6; ++row) { for (int32 column = 0; column < 7; ++column) { if (day == fDate.Day()) { fNewFocusedDay.SetTo(row, column); fNewSelectedDay.SetTo(row, column); return; } day++; } } fNewFocusedDay.SetTo(0, 0); fNewSelectedDay.SetTo(0, 0); } void BCalendarView::_SetToCurrentDay() { BDate date(fCurrentDate.Year(), fCurrentDate.Month(), 1); if (!date.IsValid()) return; if (fDate.Year() != date.Year() || fDate.Month() != date.Month()) { fNewCurrentDay.SetTo(-1, -1); return; } const int32 firstDayOffset = (7 + date.DayOfWeek() - fStartOfWeek) % 7; int32 day = 1 - firstDayOffset; for (int32 row = 0; row < 6; ++row) { for (int32 column = 0; column < 7; ++column) { if (day == fCurrentDate.Day()) { fNewCurrentDay.SetTo(row, column); return; } day++; } } fNewCurrentDay.SetTo(-1, -1); } void BCalendarView::_GetYearMonthForSelection(const Selection& selection, int32* year, int32* month) const { BDate startOfMonth(fDate.Year(), fDate.Month(), 1); const int32 firstDayOffset = (7 + startOfMonth.DayOfWeek() - fStartOfWeek) % 7; const int32 daysInMonth = startOfMonth.DaysInMonth(); BDate date(fDate); const int32 dayOffset = selection.row * 7 + selection.column; if (dayOffset < firstDayOffset) date.AddMonths(-1); else if (dayOffset >= firstDayOffset + daysInMonth) date.AddMonths(1); if (year != NULL) *year = date.Year(); if (month != NULL) *month = date.Month(); } void BCalendarView::_GetPreferredSize(float* _width, float* _height) { BFont font; GetFont(&font); font_height fontHeight; font.GetHeight(&fontHeight); const float height = FontHeight(this) + 4.0; int32 rows = 7; if (!fDayNameHeaderVisible) rows = 6; // height = font height * rows + 8 px border *_height = height * rows + 8.0; float width = 0.0; for (int32 column = 0; column < 7; ++column) { float tmp = StringWidth(fDayNames[column].String()) + 2.0; width = tmp > width ? tmp : width; } int32 columns = 8; if (!fWeekNumberHeaderVisible) columns = 7; // width = max width day name * 8 column + 8 px border *_width = width * columns + 8.0; } void BCalendarView::_SetupDayNames() { BDateFormatStyle style = B_LONG_DATE_FORMAT; float width, height; while (style != B_DATE_FORMAT_STYLE_COUNT) { _PopulateDayNames(style); GetPreferredSize(&width, &height); if (width < Bounds().Width()) return; style = static_cast(static_cast(style) + 1); } } void BCalendarView::_PopulateDayNames(BDateFormatStyle style) { for (int32 i = 0; i <= 6; ++i) { fDayNames[i] = ""; BDateFormat().GetDayName(1 + (fStartOfWeek - 1 + i) % 7, fDayNames[i], style); } } void BCalendarView::_SetupDayNumbers() { BDate startOfMonth(fDate.Year(), fDate.Month(), 1); if (!startOfMonth.IsValid()) return; fFocusedDay.SetTo(0, 0); fSelectedDay.SetTo(0, 0); fNewFocusedDay.SetTo(0, 0); fCurrentDay.SetTo(-1, -1); const int32 daysInMonth = startOfMonth.DaysInMonth(); const int32 firstDayOffset = (7 + startOfMonth.DayOfWeek() - fStartOfWeek) % 7; // calc the last day one month before BDate lastDayInMonthBefore(startOfMonth); lastDayInMonthBefore.AddDays(-1); const int32 lastDayBefore = lastDayInMonthBefore.DaysInMonth(); int32 counter = 0; int32 firstDayAfter = 1; for (int32 row = 0; row < 6; ++row) { for (int32 column = 0; column < 7; ++column) { int32 day = 1 + counter - firstDayOffset; if (counter < firstDayOffset) day += lastDayBefore; else if (counter >= firstDayOffset + daysInMonth) day = firstDayAfter++; else if (day == fDate.Day()) { fFocusedDay.SetTo(row, column); fSelectedDay.SetTo(row, column); fNewFocusedDay.SetTo(row, column); } if (day == fCurrentDate.Day() && counter >= firstDayOffset && counter < firstDayOffset + daysInMonth && fDate.Month() == fCurrentDate.Month() && fDate.Year() == fCurrentDate.Year()) fCurrentDay.SetTo(row, column); counter++; fDayNumbers[row][column].Truncate(0); fDayNumbers[row][column] << day; } } } void BCalendarView::_SetupWeekNumbers() { BDate date(fDate.Year(), fDate.Month(), 1); if (!date.IsValid()) return; for (int32 row = 0; row < 6; ++row) { fWeekNumbers[row].SetTo(""); fWeekNumbers[row] << date.WeekNumber(); date.AddDays(7); } } void BCalendarView::_DrawDay(int32 currRow, int32 currColumn, int32 row, int32 column, int32 counter, BRect frame, const char* text, bool focus, bool highlight) { BDate startOfMonth(fDate.Year(), fDate.Month(), 1); const int32 firstDayOffset = (7 + startOfMonth.DayOfWeek() - fStartOfWeek) % 7; const int32 daysMonth = startOfMonth.DaysInMonth(); bool enabled = true; bool selected = false; // check for the current date if (currRow == row && currColumn == column) { selected = true; // draw current date selected if (counter <= firstDayOffset || counter > firstDayOffset + daysMonth) { enabled = false; // days of month before or after selected = false; // not selected but able to get focus } } else { if (counter <= firstDayOffset || counter > firstDayOffset + daysMonth) enabled = false; // days of month before or after } DrawDay(this, frame, text, selected, enabled, focus, highlight); } void BCalendarView::_DrawDays() { BRect frame = _FirstCalendarItemFrame(); const int32 currRow = fSelectedDay.row; const int32 currColumn = fSelectedDay.column; const bool isFocus = IsFocus(); const int32 focusRow = fFocusedDay.row; const int32 focusColumn = fFocusedDay.column; const int32 highlightRow = fCurrentDay.row; const int32 highlightColumn = fCurrentDay.column; int32 counter = 0; for (int32 row = 0; row < 6; ++row) { BRect tmp = frame; for (int32 column = 0; column < 7; ++column) { counter++; const char* day = fDayNumbers[row][column].String(); bool focus = isFocus && focusRow == row && focusColumn == column; bool highlight = highlightRow == row && highlightColumn == column; _DrawDay(currRow, currColumn, row, column, counter, tmp, day, focus, highlight); tmp.OffsetBy(tmp.Width(), 0.0); } frame.OffsetBy(0.0, frame.Height()); } } void BCalendarView::_DrawFocusRect() { BRect frame = _FirstCalendarItemFrame(); const int32 currRow = fSelectedDay.row; const int32 currColumn = fSelectedDay.column; const int32 focusRow = fFocusedDay.row; const int32 focusColumn = fFocusedDay.column; const int32 highlightRow = fCurrentDay.row; const int32 highlightColumn = fCurrentDay.column; int32 counter = 0; for (int32 row = 0; row < 6; ++row) { BRect tmp = frame; for (int32 column = 0; column < 7; ++column) { counter++; if (fNewFocusedDay.row == row && fNewFocusedDay.column == column) { fFocusedDay.SetTo(row, column); bool focus = IsFocus() && true; bool highlight = highlightRow == row && highlightColumn == column; const char* day = fDayNumbers[row][column].String(); _DrawDay(currRow, currColumn, row, column, counter, tmp, day, focus, highlight); } else if (focusRow == row && focusColumn == column) { const char* day = fDayNumbers[row][column].String(); bool highlight = highlightRow == row && highlightColumn == column; _DrawDay(currRow, currColumn, row, column, counter, tmp, day, false, highlight); } tmp.OffsetBy(tmp.Width(), 0.0); } frame.OffsetBy(0.0, frame.Height()); } } void BCalendarView::_DrawDayHeader() { if (!fDayNameHeaderVisible) return; int32 offset = 1; int32 columns = 8; if (!fWeekNumberHeaderVisible) { offset = 0; columns = 7; } BRect frame = Bounds(); frame.right = frame.Width() / columns - 1.0; frame.bottom = frame.Height() / 7.0 - 2.0; frame.OffsetBy(4.0, 4.0); for (int32 i = 0; i < columns; ++i) { if (i == 0 && fWeekNumberHeaderVisible) { DrawDayName(this, frame, ""); frame.OffsetBy(frame.Width(), 0.0); continue; } DrawDayName(this, frame, fDayNames[i - offset].String()); frame.OffsetBy(frame.Width(), 0.0); } } void BCalendarView::_DrawWeekHeader() { if (!fWeekNumberHeaderVisible) return; int32 rows = 7; if (!fDayNameHeaderVisible) rows = 6; BRect frame = Bounds(); frame.right = frame.Width() / 8.0 - 2.0; frame.bottom = frame.Height() / rows - 1.0; float offsetY = 4.0; if (fDayNameHeaderVisible) offsetY += frame.Height(); frame.OffsetBy(4.0, offsetY); for (int32 row = 0; row < 6; ++row) { DrawWeekNumber(this, frame, fWeekNumbers[row].String()); frame.OffsetBy(0.0, frame.Height()); } } void BCalendarView::_DrawItem(BView* owner, BRect frame, const char* text, bool isSelected, bool isEnabled, bool focus, bool isHighlight) { rgb_color lColor = LowColor(); rgb_color highColor = HighColor(); rgb_color textColor = ui_color(B_LIST_ITEM_TEXT_COLOR); rgb_color bgColor = ui_color(B_LIST_BACKGROUND_COLOR); float tintDisabled = B_LIGHTEN_2_TINT; float tintHighlight = B_LIGHTEN_1_TINT; if (textColor.red + textColor.green + textColor.blue > 125 * 3) tintDisabled = B_DARKEN_2_TINT; if (bgColor.red + bgColor.green + bgColor.blue > 125 * 3) tintHighlight = B_DARKEN_1_TINT; if (isSelected) { SetHighColor(ui_color(B_LIST_SELECTED_BACKGROUND_COLOR)); textColor = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR); } else if (isHighlight) SetHighColor(tint_color(bgColor, tintHighlight)); else SetHighColor(bgColor); SetLowColor(HighColor()); FillRect(frame.InsetByCopy(1.0, 1.0)); if (focus) { rgb_color focusColor = keyboard_navigation_color(); SetHighColor(focusColor); StrokeRect(frame.InsetByCopy(1.0, 1.0)); if (!isSelected) textColor = focusColor; } SetHighColor(textColor); if (!isEnabled) SetHighColor(tint_color(textColor, tintDisabled)); float offsetH = frame.Width() / 2.0; float offsetV = frame.Height() / 2.0 + FontHeight(owner) / 2.0 - 2.0; BFont font(be_plain_font); if (isHighlight) font.SetFace(B_BOLD_FACE); else font.SetFace(B_REGULAR_FACE); SetFont(&font); DrawString(text, BPoint(frame.right - offsetH - StringWidth(text) / 2.0, frame.top + offsetV)); SetLowColor(lColor); SetHighColor(highColor); } void BCalendarView::_UpdateSelection() { BRect frame = _FirstCalendarItemFrame(); const int32 currRow = fSelectedDay.row; const int32 currColumn = fSelectedDay.column; const int32 focusRow = fFocusedDay.row; const int32 focusColumn = fFocusedDay.column; const int32 highlightRow = fCurrentDay.row; const int32 highlightColumn = fCurrentDay.column; int32 counter = 0; for (int32 row = 0; row < 6; ++row) { BRect tmp = frame; for (int32 column = 0; column < 7; ++column) { counter++; if (fNewSelectedDay.row == row && fNewSelectedDay.column == column) { fSelectedDay.SetTo(row, column); const char* day = fDayNumbers[row][column].String(); bool focus = IsFocus() && focusRow == row && focusColumn == column; bool highlight = highlightRow == row && highlightColumn == column; _DrawDay(row, column, row, column, counter, tmp, day, focus, highlight); } else if (currRow == row && currColumn == column) { const char* day = fDayNumbers[row][column].String(); bool focus = IsFocus() && focusRow == row && focusColumn == column; bool highlight = highlightRow == row && highlightColumn == column; _DrawDay(currRow, currColumn, -1, -1, counter, tmp, day, focus, highlight); } tmp.OffsetBy(tmp.Width(), 0.0); } frame.OffsetBy(0.0, frame.Height()); } } void BCalendarView::_UpdateCurrentDay() { BRect frame = _FirstCalendarItemFrame(); const int32 selectRow = fSelectedDay.row; const int32 selectColumn = fSelectedDay.column; const int32 focusRow = fFocusedDay.row; const int32 focusColumn = fFocusedDay.column; const int32 currRow = fCurrentDay.row; const int32 currColumn = fCurrentDay.column; int32 counter = 0; for (int32 row = 0; row < 6; ++row) { BRect tmp = frame; for (int32 column = 0; column < 7; ++column) { counter++; if (fNewCurrentDay.row == row && fNewCurrentDay.column == column) { fCurrentDay.SetTo(row, column); const char* day = fDayNumbers[row][column].String(); bool focus = IsFocus() && focusRow == row && focusColumn == column; bool isSelected = selectRow == row && selectColumn == column; if (isSelected) _DrawDay(row, column, row, column, counter, tmp, day, focus, true); else _DrawDay(row, column, -1, -1, counter, tmp, day, focus, true); } else if (currRow == row && currColumn == column) { const char* day = fDayNumbers[row][column].String(); bool focus = IsFocus() && focusRow == row && focusColumn == column; bool isSelected = selectRow == row && selectColumn == column; if(isSelected) _DrawDay(currRow, currColumn, row, column, counter, tmp, day, focus, false); else _DrawDay(currRow, currColumn, -1, -1, counter, tmp, day, focus, false); } tmp.OffsetBy(tmp.Width(), 0.0); } frame.OffsetBy(0.0, frame.Height()); } } void BCalendarView::_UpdateCurrentDate() { BDate date = BDate::CurrentDate(B_LOCAL_TIME); if (!date.IsValid()) return; if (date == fCurrentDate) return; fCurrentDate = date; _SetToCurrentDay(); fCurrentDayChanged = true; Draw(_RectOfDay(fCurrentDay)); Draw(_RectOfDay(fNewCurrentDay)); fCurrentDayChanged = false; return; } BRect BCalendarView::_FirstCalendarItemFrame() const { int32 rows = 7; int32 columns = 8; if (!fDayNameHeaderVisible) rows = 6; if (!fWeekNumberHeaderVisible) columns = 7; BRect frame = Bounds(); frame.right = frame.Width() / columns - 1.0; frame.bottom = frame.Height() / rows - 1.0; float offsetY = 4.0; if (fDayNameHeaderVisible) offsetY += frame.Height(); float offsetX = 4.0; if (fWeekNumberHeaderVisible) offsetX += frame.Width(); return frame.OffsetBySelf(offsetX, offsetY); } BRect BCalendarView::_SetNewSelectedDay(const BPoint& where) { BRect frame = _FirstCalendarItemFrame(); int32 counter = 0; for (int32 row = 0; row < 6; ++row) { BRect tmp = frame; for (int32 column = 0; column < 7; ++column) { counter++; if (tmp.Contains(where)) { fNewSelectedDay.SetTo(row, column); int32 year; int32 month; _GetYearMonthForSelection(fNewSelectedDay, &year, &month); if (month == fDate.Month()) { // only change date if a day in the current month has been // selected int32 day = atoi(fDayNumbers[row][column].String()); fDate.SetDate(year, month, day); } return tmp; } tmp.OffsetBy(tmp.Width(), 0.0); } frame.OffsetBy(0.0, frame.Height()); } return frame; } BRect BCalendarView::_RectOfDay(const Selection& selection) const { BRect frame = _FirstCalendarItemFrame(); int32 counter = 0; for (int32 row = 0; row < 6; ++row) { BRect tmp = frame; for (int32 column = 0; column < 7; ++column) { counter++; if (selection.row == row && selection.column == column) return tmp; tmp.OffsetBy(tmp.Width(), 0.0); } frame.OffsetBy(0.0, frame.Height()); } return frame; } } // namespace BPrivate