haiku/src/kits/shared/CalendarView.cpp

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