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