2 * Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * McCall <mccall@@digitalparadise.co.uk>
7 * Mike Berg <mike@berg-net.us>
8 * Julun <host.haiku@gmx.de>
9 * Clemens <mail@Clemens-Zeidler.de>
10 * Adrien Destugues <pulkomandy@pulkomandy.cx>
11 * Hamish Morrison <hamish@lavabit.com>
15 #include "DateTimeEdit.h"
19 #include <DateFormat.h>
26 using BPrivate::B_LOCAL_TIME
;
29 TTimeEdit::TTimeEdit(const char* name
, uint32 sections
)
31 TSectionEdit(name
, sections
),
35 fFieldPositions(NULL
),
42 TTimeEdit::~TTimeEdit()
44 free(fFieldPositions
);
50 TTimeEdit::KeyDown(const char* bytes
, int32 numBytes
)
52 TSectionEdit::KeyDown(bytes
, numBytes
);
54 // only accept valid input
55 int32 number
= atoi(bytes
);
59 int32 section
= FocusIndex();
60 if (section
< 0 || section
> 2)
62 bigtime_t currentTime
= system_time();
63 if (currentTime
- fLastKeyDownTime
< 1000000) {
64 int32 doubleDigit
= number
+ fLastKeyDownInt
* 10;
65 if (_IsValidDoubleDigit(doubleDigit
))
69 fLastKeyDownTime
= currentTime
;
70 fLastKeyDownInt
= number
;
73 // update display value
78 // send message to change time
86 // make sure we call the base class method, as it
87 // will create the arrow bitmaps and the section list
88 fTime
= BDateTime::CurrentDateTime(B_LOCAL_TIME
);
94 TTimeEdit::DrawSection(uint32 index
, BRect bounds
, bool hasFocus
)
96 if (fFieldPositions
== NULL
|| index
* 2 + 1 >= (uint32
)fFieldPosCount
)
100 SetLowColor(mix_color(ui_color(B_CONTROL_HIGHLIGHT_COLOR
),
103 SetLowColor(ViewColor());
106 fText
.CopyCharsInto(field
, fFieldPositions
[index
* 2],
107 fFieldPositions
[index
* 2 + 1] - fFieldPositions
[index
* 2]);
109 BPoint
point(bounds
.LeftBottom());
110 point
.y
-= bounds
.Height() / 2.0 - 6.0;
111 point
.x
+= (bounds
.Width() - StringWidth(field
)) / 2;
112 SetHighUIColor(B_PANEL_TEXT_COLOR
);
113 FillRect(bounds
, B_SOLID_LOW
);
114 DrawString(field
, point
);
119 TTimeEdit::DrawSeparator(uint32 index
, BRect bounds
)
121 if (fFieldPositions
== NULL
|| index
* 2 + 2 >= (uint32
)fFieldPosCount
)
125 fText
.CopyCharsInto(field
, fFieldPositions
[index
* 2 + 1],
126 fFieldPositions
[index
* 2 + 2] - fFieldPositions
[index
* 2 + 1]);
128 BPoint
point(bounds
.LeftBottom());
129 point
.y
-= bounds
.Height() / 2.0 - 6.0;
130 point
.x
+= (bounds
.Width() - StringWidth(field
)) / 2;
131 SetHighUIColor(B_PANEL_TEXT_COLOR
);
132 DrawString(field
, point
);
137 TTimeEdit::SeparatorWidth()
144 TTimeEdit::MinSectionWidth()
146 return be_plain_font
->StringWidth("00");
151 TTimeEdit::SectionFocus(uint32 index
)
153 fLastKeyDownTime
= 0;
155 fHoldValue
= _SectionValue(index
);
161 TTimeEdit::SetTime(int32 hour
, int32 minute
, int32 second
)
163 // make sure to update date upon overflow
164 if (hour
== 0 && minute
== 0 && second
== 0)
165 fTime
= BDateTime::CurrentDateTime(B_LOCAL_TIME
);
167 fTime
.SetTime(BTime(hour
, minute
, second
));
174 Invalidate(Bounds());
179 TTimeEdit::DoUpPress()
184 // update displayed value
189 // send message to change time
195 TTimeEdit::DoDownPress()
200 // update display value
205 // send message to change time
212 TTimeEdit::BuildDispatch(BMessage
* message
)
214 if (fFocus
< 0 || fFocus
>= fFieldCount
)
217 message
->AddBool("time", true);
219 for (int32 index
= 0; index
< (int)fSectionCount
; ++index
) {
220 uint32 data
= _SectionValue(index
);
225 switch (fFields
[index
]) {
226 case B_DATE_ELEMENT_HOUR
:
227 message
->AddInt32("hour", data
);
230 case B_DATE_ELEMENT_MINUTE
:
231 message
->AddInt32("minute", data
);
234 case B_DATE_ELEMENT_SECOND
:
235 message
->AddInt32("second", data
);
246 TTimeEdit::_UpdateFields()
248 time_t time
= fTime
.Time_t();
250 if (fFieldPositions
!= NULL
) {
251 free(fFieldPositions
);
252 fFieldPositions
= NULL
;
254 fTimeFormat
.Format(fText
, fFieldPositions
, fFieldPosCount
, time
,
255 B_MEDIUM_TIME_FORMAT
);
257 if (fFields
!= NULL
) {
261 fTimeFormat
.GetTimeFields(fFields
, fFieldCount
, B_MEDIUM_TIME_FORMAT
);
266 TTimeEdit::_CheckRange()
268 if (fFocus
< 0 || fFocus
>= fFieldCount
)
271 int32 value
= fHoldValue
;
272 switch (fFields
[fFocus
]) {
273 case B_DATE_ELEMENT_HOUR
:
279 fTime
.SetTime(BTime(value
, fTime
.Time().Minute(),
280 fTime
.Time().Second()));
283 case B_DATE_ELEMENT_MINUTE
:
289 fTime
.SetTime(BTime(fTime
.Time().Hour(), value
,
290 fTime
.Time().Second()));
293 case B_DATE_ELEMENT_SECOND
:
299 fTime
.SetTime(BTime(fTime
.Time().Hour(), fTime
.Time().Minute(),
303 case B_DATE_ELEMENT_AM_PM
:
304 value
= fTime
.Time().Hour();
312 // modify hour value to reflect change in am/ pm
313 fTime
.SetTime(BTime(value
, fTime
.Time().Minute(),
314 fTime
.Time().Second()));
322 Invalidate(Bounds());
327 TTimeEdit::_IsValidDoubleDigit(int32 value
)
329 if (fFocus
< 0 || fFocus
>= fFieldCount
)
332 bool isInRange
= false;
333 switch (fFields
[fFocus
]) {
334 case B_DATE_ELEMENT_HOUR
:
339 case B_DATE_ELEMENT_MINUTE
:
344 case B_DATE_ELEMENT_SECOND
:
358 TTimeEdit::_SectionValue(int32 index
) const
360 if (index
< 0 || index
>= fFieldCount
)
364 switch (fFields
[index
]) {
365 case B_DATE_ELEMENT_HOUR
:
366 value
= fTime
.Time().Hour();
369 case B_DATE_ELEMENT_MINUTE
:
370 value
= fTime
.Time().Minute();
373 case B_DATE_ELEMENT_SECOND
:
374 value
= fTime
.Time().Second();
387 TTimeEdit::PreferredHeight()
389 font_height fontHeight
;
390 GetFontHeight(&fontHeight
);
391 return ceilf((fontHeight
.ascent
+ fontHeight
.descent
) * 1.4);
398 TDateEdit::TDateEdit(const char* name
, uint32 sections
)
400 TSectionEdit(name
, sections
),
403 fFieldPositions(NULL
),
410 TDateEdit::~TDateEdit()
412 free(fFieldPositions
);
418 TDateEdit::KeyDown(const char* bytes
, int32 numBytes
)
420 TSectionEdit::KeyDown(bytes
, numBytes
);
422 // only accept valid input
423 int32 number
= atoi(bytes
);
427 int32 section
= FocusIndex();
428 if (section
< 0 || section
> 2)
431 bigtime_t currentTime
= system_time();
432 if (currentTime
- fLastKeyDownTime
< 1000000) {
433 int32 doubleDigit
= number
+ fLastKeyDownInt
* 10;
434 if (_IsValidDoubleDigit(doubleDigit
))
435 number
= doubleDigit
;
436 fLastKeyDownTime
= 0;
438 fLastKeyDownTime
= currentTime
;
439 fLastKeyDownInt
= number
;
444 if (fFields
[section
] == B_DATE_ELEMENT_YEAR
) {
445 int32 oldCentury
= int32(fHoldValue
/ 100) * 100;
446 if (number
< 10 && oldCentury
== 1900)
448 number
+= oldCentury
;
451 // update display value
456 // send message to change time
462 TDateEdit::InitView()
464 // make sure we call the base class method, as it
465 // will create the arrow bitmaps and the section list
466 fDate
= BDate::CurrentDate(B_LOCAL_TIME
);
472 TDateEdit::DrawSection(uint32 index
, BRect bounds
, bool hasFocus
)
474 if (fFieldPositions
== NULL
|| index
* 2 + 1 >= (uint32
)fFieldPosCount
)
478 SetLowColor(mix_color(ui_color(B_CONTROL_HIGHLIGHT_COLOR
),
481 SetLowColor(ViewColor());
484 fText
.CopyCharsInto(field
, fFieldPositions
[index
* 2],
485 fFieldPositions
[index
* 2 + 1] - fFieldPositions
[index
* 2]);
487 BPoint
point(bounds
.LeftBottom());
488 point
.y
-= bounds
.Height() / 2.0 - 6.0;
489 point
.x
+= (bounds
.Width() - StringWidth(field
)) / 2;
490 SetHighUIColor(B_PANEL_TEXT_COLOR
);
491 FillRect(bounds
, B_SOLID_LOW
);
492 DrawString(field
, point
);
497 TDateEdit::DrawSeparator(uint32 index
, BRect bounds
)
502 if (fFieldPositions
== NULL
|| index
* 2 + 2 >= (uint32
)fFieldPosCount
)
506 fText
.CopyCharsInto(field
, fFieldPositions
[index
* 2 + 1],
507 fFieldPositions
[index
* 2 + 2] - fFieldPositions
[index
* 2 + 1]);
509 BPoint
point(bounds
.LeftBottom());
510 point
.y
-= bounds
.Height() / 2.0 - 6.0;
511 point
.x
+= (bounds
.Width() - StringWidth(field
)) / 2;
512 SetHighUIColor(B_PANEL_TEXT_COLOR
);
513 DrawString(field
, point
);
518 TDateEdit::SectionFocus(uint32 index
)
520 fLastKeyDownTime
= 0;
522 fHoldValue
= _SectionValue(index
);
528 TDateEdit::MinSectionWidth()
530 return be_plain_font
->StringWidth("00");
535 TDateEdit::SeparatorWidth()
542 TDateEdit::SetDate(int32 year
, int32 month
, int32 day
)
544 fDate
.SetDate(year
, month
, day
);
551 Invalidate(Bounds());
556 TDateEdit::DoUpPress()
561 // update displayed value
566 // send message to change Date
572 TDateEdit::DoDownPress()
577 // update display value
582 // send message to change Date
588 TDateEdit::BuildDispatch(BMessage
* message
)
590 if (fFocus
< 0 || fFocus
>= fFieldCount
)
593 message
->AddBool("time", false);
595 for (int32 index
= 0; index
< (int)fSectionCount
; ++index
) {
596 uint32 data
= _SectionValue(index
);
601 switch (fFields
[index
]) {
602 case B_DATE_ELEMENT_MONTH
:
603 message
->AddInt32("month", data
);
606 case B_DATE_ELEMENT_DAY
:
607 message
->AddInt32("day", data
);
610 case B_DATE_ELEMENT_YEAR
:
611 message
->AddInt32("year", data
);
622 TDateEdit::_UpdateFields()
624 time_t time
= BDateTime(fDate
, BTime()).Time_t();
626 if (fFieldPositions
!= NULL
) {
627 free(fFieldPositions
);
628 fFieldPositions
= NULL
;
631 fDateFormat
.Format(fText
, fFieldPositions
, fFieldPosCount
, time
,
632 B_SHORT_DATE_FORMAT
);
634 if (fFields
!= NULL
) {
638 fDateFormat
.GetFields(fFields
, fFieldCount
, B_SHORT_DATE_FORMAT
);
643 TDateEdit::_CheckRange()
645 if (fFocus
< 0 || fFocus
>= fFieldCount
)
648 int32 value
= fHoldValue
;
649 switch (fFields
[fFocus
]) {
650 case B_DATE_ELEMENT_DAY
:
652 int32 days
= fDate
.DaysInMonth();
658 fDate
.SetDate(fDate
.Year(), fDate
.Month(), value
);
662 case B_DATE_ELEMENT_MONTH
:
668 fDate
.SetDate(fDate
.Year(), value
, fDate
.Day());
671 case B_DATE_ELEMENT_YEAR
:
672 // 2037 is the end of 32-bit UNIX time
675 else if (value
< 1970)
678 fDate
.SetDate(value
, fDate
.Month(), fDate
.Day());
691 TDateEdit::_IsValidDoubleDigit(int32 value
)
693 if (fFocus
< 0 || fFocus
>= fFieldCount
)
696 bool isInRange
= false;
697 switch (fFields
[fFocus
]) {
698 case B_DATE_ELEMENT_DAY
:
700 int32 days
= fDate
.DaysInMonth();
701 if (value
>= 1 && value
<= days
)
706 case B_DATE_ELEMENT_MONTH
:
708 if (value
>= 1 && value
<= 12)
713 case B_DATE_ELEMENT_YEAR
:
715 int32 year
= int32(fHoldValue
/ 100) * 100 + value
;
716 if (year
<= 2037 && year
>= 1970)
730 TDateEdit::_SectionValue(int32 index
) const
732 if (index
< 0 || index
>= fFieldCount
)
736 switch (fFields
[index
]) {
737 case B_DATE_ELEMENT_YEAR
:
738 value
= fDate
.Year();
741 case B_DATE_ELEMENT_MONTH
:
742 value
= fDate
.Month();
745 case B_DATE_ELEMENT_DAY
:
758 TDateEdit::PreferredHeight()
760 font_height fontHeight
;
761 GetFontHeight(&fontHeight
);
762 return ceilf((fontHeight
.ascent
+ fontHeight
.descent
) * 1.4);