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