haiku/src/kits/shared/CalendarView.cpp
Humdinger 9d30b95ab3 BCalendarView: change visuals for 'today'
Only using the keyboard navigation colour to indicate the current day isn't that
nice in all situations. When using custom system colours, like white on blue
for selected items, it can become barely readable.

Now we
* use bold type face for 'today',
* tint 'today's background (lighten for dark, darken for bright bgColor),
* use B_LIST_SELECTED_BACKGROUND_COLOR and B_LIST_SELECTED_ITEM_TEXT_COLOR
  for the selected day,
* keyboard navigation colour only for the frame and text when doing keyboard
  navigation

Fixes #13714
2017-09-22 18:27:43 +02:00

1361 lines
28 KiB
C++

/*
* Copyright 2007-2011, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Julun <host.haiku@gmx.de>
*/
#include "CalendarView.h"
#include <stdlib.h>
#include <DateFormat.h>
#include <LayoutUtils.h>
#include <Window.h>
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(&notify);
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<BDateFormatStyle>(static_cast<int>(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