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
)
98 SetLowColor(ViewColor());
100 SetLowColor(tint_color(ViewColor(), B_DARKEN_1_TINT
));
103 fText
.CopyCharsInto(field
, fFieldPositions
[index
* 2],
104 fFieldPositions
[index
* 2 + 1] - fFieldPositions
[index
* 2]);
106 BPoint
point(bounds
.LeftBottom());
107 point
.y
-= bounds
.Height() / 2.0 - 6.0;
108 point
.x
+= (bounds
.Width() - StringWidth(field
)) / 2;
109 SetHighColor(0, 0, 0, 255);
110 FillRect(bounds
, B_SOLID_LOW
);
111 DrawString(field
, point
);
116 TTimeEdit::DrawSeparator(uint32 index
, BRect bounds
)
118 if (fFieldPositions
== NULL
|| index
* 2 + 2 >= (uint32
)fFieldPosCount
)
122 fText
.CopyCharsInto(field
, fFieldPositions
[index
* 2 + 1],
123 fFieldPositions
[index
* 2 + 2] - fFieldPositions
[index
* 2 + 1]);
125 BPoint
point(bounds
.LeftBottom());
126 point
.y
-= bounds
.Height() / 2.0 - 6.0;
127 point
.x
+= (bounds
.Width() - StringWidth(field
)) / 2;
128 SetHighColor(0, 0, 0, 255);
129 DrawString(field
, point
);
134 TTimeEdit::SeparatorWidth()
141 TTimeEdit::MinSectionWidth()
143 return be_plain_font
->StringWidth("00");
148 TTimeEdit::SectionFocus(uint32 index
)
150 fLastKeyDownTime
= 0;
152 fHoldValue
= _SectionValue(index
);
158 TTimeEdit::SetTime(int32 hour
, int32 minute
, int32 second
)
160 // make sure to update date upon overflow
161 if (hour
== 0 && minute
== 0 && second
== 0)
162 fTime
= BDateTime::CurrentDateTime(B_LOCAL_TIME
);
164 fTime
.SetTime(BTime(hour
, minute
, second
));
171 Invalidate(Bounds());
176 TTimeEdit::DoUpPress()
181 // update displayed value
186 // send message to change time
192 TTimeEdit::DoDownPress()
197 // update display value
202 // send message to change time
209 TTimeEdit::BuildDispatch(BMessage
* message
)
211 if (fFocus
< 0 || fFocus
>= fFieldCount
)
214 message
->AddBool("time", true);
216 for (int32 index
= 0; index
< (int)fSectionCount
; ++index
) {
217 uint32 data
= _SectionValue(index
);
222 switch (fFields
[index
]) {
223 case B_DATE_ELEMENT_HOUR
:
224 message
->AddInt32("hour", data
);
227 case B_DATE_ELEMENT_MINUTE
:
228 message
->AddInt32("minute", data
);
231 case B_DATE_ELEMENT_SECOND
:
232 message
->AddInt32("second", data
);
243 TTimeEdit::_UpdateFields()
245 time_t time
= fTime
.Time_t();
247 if (fFieldPositions
!= NULL
) {
248 free(fFieldPositions
);
249 fFieldPositions
= NULL
;
251 fTimeFormat
.Format(fText
, fFieldPositions
, fFieldPosCount
, time
,
252 B_MEDIUM_TIME_FORMAT
);
254 if (fFields
!= NULL
) {
258 fTimeFormat
.GetTimeFields(fFields
, fFieldCount
, B_MEDIUM_TIME_FORMAT
);
263 TTimeEdit::_CheckRange()
265 if (fFocus
< 0 || fFocus
>= fFieldCount
)
268 int32 value
= fHoldValue
;
269 switch (fFields
[fFocus
]) {
270 case B_DATE_ELEMENT_HOUR
:
276 fTime
.SetTime(BTime(value
, fTime
.Time().Minute(),
277 fTime
.Time().Second()));
280 case B_DATE_ELEMENT_MINUTE
:
286 fTime
.SetTime(BTime(fTime
.Time().Hour(), value
,
287 fTime
.Time().Second()));
290 case B_DATE_ELEMENT_SECOND
:
296 fTime
.SetTime(BTime(fTime
.Time().Hour(), fTime
.Time().Minute(),
300 case B_DATE_ELEMENT_AM_PM
:
301 value
= fTime
.Time().Hour();
309 // modify hour value to reflect change in am/ pm
310 fTime
.SetTime(BTime(value
, fTime
.Time().Minute(),
311 fTime
.Time().Second()));
319 Invalidate(Bounds());
324 TTimeEdit::_IsValidDoubleDigit(int32 value
)
326 if (fFocus
< 0 || fFocus
>= fFieldCount
)
329 bool isInRange
= false;
330 switch (fFields
[fFocus
]) {
331 case B_DATE_ELEMENT_HOUR
:
336 case B_DATE_ELEMENT_MINUTE
:
341 case B_DATE_ELEMENT_SECOND
:
355 TTimeEdit::_SectionValue(int32 index
) const
357 if (index
< 0 || index
>= fFieldCount
)
361 switch (fFields
[index
]) {
362 case B_DATE_ELEMENT_HOUR
:
363 value
= fTime
.Time().Hour();
366 case B_DATE_ELEMENT_MINUTE
:
367 value
= fTime
.Time().Minute();
370 case B_DATE_ELEMENT_SECOND
:
371 value
= fTime
.Time().Second();
384 TTimeEdit::PreferredHeight()
386 font_height fontHeight
;
387 GetFontHeight(&fontHeight
);
388 return ceilf((fontHeight
.ascent
+ fontHeight
.descent
) * 1.4);
395 TDateEdit::TDateEdit(const char* name
, uint32 sections
)
397 TSectionEdit(name
, sections
),
400 fFieldPositions(NULL
),
407 TDateEdit::~TDateEdit()
409 free(fFieldPositions
);
415 TDateEdit::KeyDown(const char* bytes
, int32 numBytes
)
417 TSectionEdit::KeyDown(bytes
, numBytes
);
419 // only accept valid input
420 int32 number
= atoi(bytes
);
424 int32 section
= FocusIndex();
425 if (section
< 0 || section
> 2)
428 bigtime_t currentTime
= system_time();
429 if (currentTime
- fLastKeyDownTime
< 1000000) {
430 int32 doubleDigit
= number
+ fLastKeyDownInt
* 10;
431 if (_IsValidDoubleDigit(doubleDigit
))
432 number
= doubleDigit
;
433 fLastKeyDownTime
= 0;
435 fLastKeyDownTime
= currentTime
;
436 fLastKeyDownInt
= number
;
441 if (fFields
[section
] == B_DATE_ELEMENT_YEAR
) {
442 int32 oldCentury
= int32(fHoldValue
/ 100) * 100;
443 if (number
< 10 && oldCentury
== 1900)
445 number
+= oldCentury
;
448 // update display value
453 // send message to change time
459 TDateEdit::InitView()
461 // make sure we call the base class method, as it
462 // will create the arrow bitmaps and the section list
463 fDate
= BDate::CurrentDate(B_LOCAL_TIME
);
469 TDateEdit::DrawSection(uint32 index
, BRect bounds
, bool hasFocus
)
471 if (fFieldPositions
== NULL
|| index
* 2 + 1 >= (uint32
)fFieldPosCount
)
474 SetLowColor(ViewColor());
476 SetLowColor(tint_color(ViewColor(), B_DARKEN_1_TINT
));
479 fText
.CopyCharsInto(field
, fFieldPositions
[index
* 2],
480 fFieldPositions
[index
* 2 + 1] - fFieldPositions
[index
* 2]);
482 BPoint
point(bounds
.LeftBottom());
483 point
.y
-= bounds
.Height() / 2.0 - 6.0;
484 point
.x
+= (bounds
.Width() - StringWidth(field
)) / 2;
485 SetHighColor(0, 0, 0, 255);
486 FillRect(bounds
, B_SOLID_LOW
);
487 DrawString(field
, point
);
492 TDateEdit::DrawSeparator(uint32 index
, BRect bounds
)
497 if (fFieldPositions
== NULL
|| index
* 2 + 2 >= (uint32
)fFieldPosCount
)
501 fText
.CopyCharsInto(field
, fFieldPositions
[index
* 2 + 1],
502 fFieldPositions
[index
* 2 + 2] - fFieldPositions
[index
* 2 + 1]);
504 BPoint
point(bounds
.LeftBottom());
505 point
.y
-= bounds
.Height() / 2.0 - 6.0;
506 point
.x
+= (bounds
.Width() - StringWidth(field
)) / 2;
507 SetHighColor(0, 0, 0, 255);
508 DrawString(field
, point
);
513 TDateEdit::SectionFocus(uint32 index
)
515 fLastKeyDownTime
= 0;
517 fHoldValue
= _SectionValue(index
);
523 TDateEdit::MinSectionWidth()
525 return be_plain_font
->StringWidth("00");
530 TDateEdit::SeparatorWidth()
537 TDateEdit::SetDate(int32 year
, int32 month
, int32 day
)
539 fDate
.SetDate(year
, month
, day
);
546 Invalidate(Bounds());
551 TDateEdit::DoUpPress()
556 // update displayed value
561 // send message to change Date
567 TDateEdit::DoDownPress()
572 // update display value
577 // send message to change Date
583 TDateEdit::BuildDispatch(BMessage
* message
)
585 if (fFocus
< 0 || fFocus
>= fFieldCount
)
588 message
->AddBool("time", false);
590 for (int32 index
= 0; index
< (int)fSectionCount
; ++index
) {
591 uint32 data
= _SectionValue(index
);
596 switch (fFields
[index
]) {
597 case B_DATE_ELEMENT_MONTH
:
598 message
->AddInt32("month", data
);
601 case B_DATE_ELEMENT_DAY
:
602 message
->AddInt32("day", data
);
605 case B_DATE_ELEMENT_YEAR
:
606 message
->AddInt32("year", data
);
617 TDateEdit::_UpdateFields()
619 time_t time
= BDateTime(fDate
, BTime()).Time_t();
621 if (fFieldPositions
!= NULL
) {
622 free(fFieldPositions
);
623 fFieldPositions
= NULL
;
626 fDateFormat
.Format(fText
, fFieldPositions
, fFieldPosCount
, time
,
627 B_SHORT_DATE_FORMAT
);
629 if (fFields
!= NULL
) {
633 fDateFormat
.GetFields(fFields
, fFieldCount
, B_SHORT_DATE_FORMAT
);
638 TDateEdit::_CheckRange()
640 if (fFocus
< 0 || fFocus
>= fFieldCount
)
643 int32 value
= fHoldValue
;
644 switch (fFields
[fFocus
]) {
645 case B_DATE_ELEMENT_DAY
:
647 int32 days
= fDate
.DaysInMonth();
653 fDate
.SetDate(fDate
.Year(), fDate
.Month(), value
);
657 case B_DATE_ELEMENT_MONTH
:
663 fDate
.SetDate(fDate
.Year(), value
, fDate
.Day());
666 case B_DATE_ELEMENT_YEAR
:
667 // 2037 is the end of 32-bit UNIX time
670 else if (value
< 1970)
673 fDate
.SetDate(value
, fDate
.Month(), fDate
.Day());
686 TDateEdit::_IsValidDoubleDigit(int32 value
)
688 if (fFocus
< 0 || fFocus
>= fFieldCount
)
691 bool isInRange
= false;
692 switch (fFields
[fFocus
]) {
693 case B_DATE_ELEMENT_DAY
:
695 int32 days
= fDate
.DaysInMonth();
696 if (value
>= 1 && value
<= days
)
701 case B_DATE_ELEMENT_MONTH
:
703 if (value
>= 1 && value
<= 12)
708 case B_DATE_ELEMENT_YEAR
:
710 int32 year
= int32(fHoldValue
/ 100) * 100 + value
;
711 if (year
<= 2037 && year
>= 1970)
725 TDateEdit::_SectionValue(int32 index
) const
727 if (index
< 0 || index
>= fFieldCount
)
731 switch (fFields
[index
]) {
732 case B_DATE_ELEMENT_YEAR
:
733 value
= fDate
.Year();
736 case B_DATE_ELEMENT_MONTH
:
737 value
= fDate
.Month();
740 case B_DATE_ELEMENT_DAY
:
753 TDateEdit::PreferredHeight()
755 font_height fontHeight
;
756 GetFontHeight(&fontHeight
);
757 return ceilf((fontHeight
.ascent
+ fontHeight
.descent
) * 1.4);