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