tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / preferences / time / DateTimeEdit.cpp
blob0614a2bb30096edf0693088d73e424d1fe82310a
1 /*
2 * Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
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"
17 #include <stdlib.h>
19 #include <DateFormat.h>
20 #include <List.h>
21 #include <Locale.h>
22 #include <String.h>
23 #include <Window.h>
26 using BPrivate::B_LOCAL_TIME;
29 TTimeEdit::TTimeEdit(const char* name, uint32 sections)
31 TSectionEdit(name, sections),
32 fLastKeyDownTime(0),
33 fFields(NULL),
34 fFieldCount(0),
35 fFieldPositions(NULL),
36 fFieldPosCount(0)
38 InitView();
42 TTimeEdit::~TTimeEdit()
44 free(fFieldPositions);
45 free(fFields);
49 void
50 TTimeEdit::KeyDown(const char* bytes, int32 numBytes)
52 TSectionEdit::KeyDown(bytes, numBytes);
54 // only accept valid input
55 int32 number = atoi(bytes);
56 if (number - 1 < 0)
57 return;
59 int32 section = FocusIndex();
60 if (section < 0 || section > 2)
61 return;
62 bigtime_t currentTime = system_time();
63 if (currentTime - fLastKeyDownTime < 1000000) {
64 int32 doubleDigit = number + fLastKeyDownInt * 10;
65 if (_IsValidDoubleDigit(doubleDigit))
66 number = doubleDigit;
67 fLastKeyDownTime = 0;
68 } else {
69 fLastKeyDownTime = currentTime;
70 fLastKeyDownInt = number;
73 // update display value
74 fHoldValue = number;
76 _CheckRange();
78 // send message to change time
79 DispatchMessage();
83 void
84 TTimeEdit::InitView()
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);
89 _UpdateFields();
93 void
94 TTimeEdit::DrawSection(uint32 index, BRect bounds, bool hasFocus)
96 if (fFieldPositions == NULL || index * 2 + 1 >= (uint32)fFieldPosCount)
97 return;
98 SetLowColor(ViewColor());
99 if (hasFocus)
100 SetLowColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
102 BString field;
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);
115 void
116 TTimeEdit::DrawSeparator(uint32 index, BRect bounds)
118 if (fFieldPositions == NULL || index * 2 + 2 >= (uint32)fFieldPosCount)
119 return;
121 BString field;
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);
133 float
134 TTimeEdit::SeparatorWidth()
136 return 10.0f;
140 float
141 TTimeEdit::MinSectionWidth()
143 return be_plain_font->StringWidth("00");
147 void
148 TTimeEdit::SectionFocus(uint32 index)
150 fLastKeyDownTime = 0;
151 fFocus = index;
152 fHoldValue = _SectionValue(index);
153 Draw(Bounds());
157 void
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));
166 if (LockLooper()) {
167 _UpdateFields();
168 UnlockLooper();
171 Invalidate(Bounds());
175 void
176 TTimeEdit::DoUpPress()
178 if (fFocus == -1)
179 SectionFocus(0);
181 // update displayed value
182 fHoldValue += 1;
184 _CheckRange();
186 // send message to change time
187 DispatchMessage();
191 void
192 TTimeEdit::DoDownPress()
194 if (fFocus == -1)
195 SectionFocus(0);
197 // update display value
198 fHoldValue -= 1;
200 _CheckRange();
202 // send message to change time
203 DispatchMessage();
208 void
209 TTimeEdit::BuildDispatch(BMessage* message)
211 if (fFocus < 0 || fFocus >= fFieldCount)
212 return;
214 message->AddBool("time", true);
216 for (int32 index = 0; index < (int)fSectionCount; ++index) {
217 uint32 data = _SectionValue(index);
219 if (fFocus == index)
220 data = fHoldValue;
222 switch (fFields[index]) {
223 case B_DATE_ELEMENT_HOUR:
224 message->AddInt32("hour", data);
225 break;
227 case B_DATE_ELEMENT_MINUTE:
228 message->AddInt32("minute", data);
229 break;
231 case B_DATE_ELEMENT_SECOND:
232 message->AddInt32("second", data);
233 break;
235 default:
236 break;
242 void
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) {
255 free(fFields);
256 fFields = NULL;
258 fTimeFormat.GetTimeFields(fFields, fFieldCount, B_MEDIUM_TIME_FORMAT);
262 void
263 TTimeEdit::_CheckRange()
265 if (fFocus < 0 || fFocus >= fFieldCount)
266 return;
268 int32 value = fHoldValue;
269 switch (fFields[fFocus]) {
270 case B_DATE_ELEMENT_HOUR:
271 if (value > 23)
272 value = 0;
273 else if (value < 0)
274 value = 23;
276 fTime.SetTime(BTime(value, fTime.Time().Minute(),
277 fTime.Time().Second()));
278 break;
280 case B_DATE_ELEMENT_MINUTE:
281 if (value> 59)
282 value = 0;
283 else if (value < 0)
284 value = 59;
286 fTime.SetTime(BTime(fTime.Time().Hour(), value,
287 fTime.Time().Second()));
288 break;
290 case B_DATE_ELEMENT_SECOND:
291 if (value > 59)
292 value = 0;
293 else if (value < 0)
294 value = 59;
296 fTime.SetTime(BTime(fTime.Time().Hour(), fTime.Time().Minute(),
297 value));
298 break;
300 case B_DATE_ELEMENT_AM_PM:
301 value = fTime.Time().Hour();
302 if (value < 13)
303 value += 12;
304 else
305 value -= 12;
306 if (value == 24)
307 value = 0;
309 // modify hour value to reflect change in am/ pm
310 fTime.SetTime(BTime(value, fTime.Time().Minute(),
311 fTime.Time().Second()));
312 break;
314 default:
315 return;
318 fHoldValue = value;
319 Invalidate(Bounds());
323 bool
324 TTimeEdit::_IsValidDoubleDigit(int32 value)
326 if (fFocus < 0 || fFocus >= fFieldCount)
327 return false;
329 bool isInRange = false;
330 switch (fFields[fFocus]) {
331 case B_DATE_ELEMENT_HOUR:
332 if (value <= 23)
333 isInRange = true;
334 break;
336 case B_DATE_ELEMENT_MINUTE:
337 if (value <= 59)
338 isInRange = true;
339 break;
341 case B_DATE_ELEMENT_SECOND:
342 if (value <= 59)
343 isInRange = true;
344 break;
346 default:
347 break;
350 return isInRange;
354 int32
355 TTimeEdit::_SectionValue(int32 index) const
357 if (index < 0 || index >= fFieldCount)
358 return 0;
360 int32 value;
361 switch (fFields[index]) {
362 case B_DATE_ELEMENT_HOUR:
363 value = fTime.Time().Hour();
364 break;
366 case B_DATE_ELEMENT_MINUTE:
367 value = fTime.Time().Minute();
368 break;
370 case B_DATE_ELEMENT_SECOND:
371 value = fTime.Time().Second();
372 break;
374 default:
375 value = 0;
376 break;
379 return value;
383 float
384 TTimeEdit::PreferredHeight()
386 font_height fontHeight;
387 GetFontHeight(&fontHeight);
388 return ceilf((fontHeight.ascent + fontHeight.descent) * 1.4);
392 // #pragma mark -
395 TDateEdit::TDateEdit(const char* name, uint32 sections)
397 TSectionEdit(name, sections),
398 fFields(NULL),
399 fFieldCount(0),
400 fFieldPositions(NULL),
401 fFieldPosCount(0)
403 InitView();
407 TDateEdit::~TDateEdit()
409 free(fFieldPositions);
410 free(fFields);
414 void
415 TDateEdit::KeyDown(const char* bytes, int32 numBytes)
417 TSectionEdit::KeyDown(bytes, numBytes);
419 // only accept valid input
420 int32 number = atoi(bytes);
421 if (number - 1 < 0)
422 return;
424 int32 section = FocusIndex();
425 if (section < 0 || section > 2)
426 return;
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;
434 } else {
435 fLastKeyDownTime = currentTime;
436 fLastKeyDownInt = number;
439 // if year add 2000
441 if (fFields[section] == B_DATE_ELEMENT_YEAR) {
442 int32 oldCentury = int32(fHoldValue / 100) * 100;
443 if (number < 10 && oldCentury == 1900)
444 number += 70;
445 number += oldCentury;
448 // update display value
449 fHoldValue = number;
451 _CheckRange();
453 // send message to change time
454 DispatchMessage();
458 void
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);
464 _UpdateFields();
468 void
469 TDateEdit::DrawSection(uint32 index, BRect bounds, bool hasFocus)
471 if (fFieldPositions == NULL || index * 2 + 1 >= (uint32)fFieldPosCount)
472 return;
474 SetLowColor(ViewColor());
475 if (hasFocus)
476 SetLowColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
478 BString field;
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);
491 void
492 TDateEdit::DrawSeparator(uint32 index, BRect bounds)
494 if (index >= 2)
495 return;
497 if (fFieldPositions == NULL || index * 2 + 2 >= (uint32)fFieldPosCount)
498 return;
500 BString field;
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);
512 void
513 TDateEdit::SectionFocus(uint32 index)
515 fLastKeyDownTime = 0;
516 fFocus = index;
517 fHoldValue = _SectionValue(index);
518 Draw(Bounds());
522 float
523 TDateEdit::MinSectionWidth()
525 return be_plain_font->StringWidth("00");
529 float
530 TDateEdit::SeparatorWidth()
532 return 10.0f;
536 void
537 TDateEdit::SetDate(int32 year, int32 month, int32 day)
539 fDate.SetDate(year, month, day);
541 if (LockLooper()) {
542 _UpdateFields();
543 UnlockLooper();
546 Invalidate(Bounds());
550 void
551 TDateEdit::DoUpPress()
553 if (fFocus == -1)
554 SectionFocus(0);
556 // update displayed value
557 fHoldValue += 1;
559 _CheckRange();
561 // send message to change Date
562 DispatchMessage();
566 void
567 TDateEdit::DoDownPress()
569 if (fFocus == -1)
570 SectionFocus(0);
572 // update display value
573 fHoldValue -= 1;
575 _CheckRange();
577 // send message to change Date
578 DispatchMessage();
582 void
583 TDateEdit::BuildDispatch(BMessage* message)
585 if (fFocus < 0 || fFocus >= fFieldCount)
586 return;
588 message->AddBool("time", false);
590 for (int32 index = 0; index < (int)fSectionCount; ++index) {
591 uint32 data = _SectionValue(index);
593 if (fFocus == index)
594 data = fHoldValue;
596 switch (fFields[index]) {
597 case B_DATE_ELEMENT_MONTH:
598 message->AddInt32("month", data);
599 break;
601 case B_DATE_ELEMENT_DAY:
602 message->AddInt32("day", data);
603 break;
605 case B_DATE_ELEMENT_YEAR:
606 message->AddInt32("year", data);
607 break;
609 default:
610 break;
616 void
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) {
630 free(fFields);
631 fFields = NULL;
633 fDateFormat.GetFields(fFields, fFieldCount, B_SHORT_DATE_FORMAT);
637 void
638 TDateEdit::_CheckRange()
640 if (fFocus < 0 || fFocus >= fFieldCount)
641 return;
643 int32 value = fHoldValue;
644 switch (fFields[fFocus]) {
645 case B_DATE_ELEMENT_DAY:
647 int32 days = fDate.DaysInMonth();
648 if (value > days)
649 value = 1;
650 else if (value < 1)
651 value = days;
653 fDate.SetDate(fDate.Year(), fDate.Month(), value);
654 break;
657 case B_DATE_ELEMENT_MONTH:
658 if (value > 12)
659 value = 1;
660 else if (value < 1)
661 value = 12;
663 fDate.SetDate(fDate.Year(), value, fDate.Day());
664 break;
666 case B_DATE_ELEMENT_YEAR:
667 // 2037 is the end of 32-bit UNIX time
668 if (value > 2037)
669 value = 2037;
670 else if (value < 1970)
671 value = 1970;
673 fDate.SetDate(value, fDate.Month(), fDate.Day());
674 break;
676 default:
677 return;
680 fHoldValue = value;
681 Draw(Bounds());
685 bool
686 TDateEdit::_IsValidDoubleDigit(int32 value)
688 if (fFocus < 0 || fFocus >= fFieldCount)
689 return false;
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)
697 isInRange = true;
698 break;
701 case B_DATE_ELEMENT_MONTH:
703 if (value >= 1 && value <= 12)
704 isInRange = true;
705 break;
708 case B_DATE_ELEMENT_YEAR:
710 int32 year = int32(fHoldValue / 100) * 100 + value;
711 if (year <= 2037 && year >= 1970)
712 isInRange = true;
713 break;
716 default:
717 break;
720 return isInRange;
724 int32
725 TDateEdit::_SectionValue(int32 index) const
727 if (index < 0 || index >= fFieldCount)
728 return 0;
730 int32 value = 0;
731 switch (fFields[index]) {
732 case B_DATE_ELEMENT_YEAR:
733 value = fDate.Year();
734 break;
736 case B_DATE_ELEMENT_MONTH:
737 value = fDate.Month();
738 break;
740 case B_DATE_ELEMENT_DAY:
741 value = fDate.Day();
742 break;
744 default:
745 break;
748 return value;
752 float
753 TDateEdit::PreferredHeight()
755 font_height fontHeight;
756 GetFontHeight(&fontHeight);
757 return ceilf((fontHeight.ascent + fontHeight.descent) * 1.4);