Author: zooey Date: 2010-08-16 22:30:37 +0200 (Mon, 16 Aug 2010) New Revision: 38160 Changeset: http://dev.haiku-os.org/changeset/38160 Modified: haiku/trunk/src/preferences/time/DateTimeEdit.cpp haiku/trunk/src/preferences/time/DateTimeEdit.h Log: Largish overhaul of the DateTimeEdit-view in the Time preflet: * instead of fetching the field data every time something needs to be drawn, we now fetch all the data whenever the date or time changes - simpler and faster, too * adjusted date layout to match time layout, i.e. left-adjusted * lots of cleanup Modified: haiku/trunk/src/preferences/time/DateTimeEdit.cpp =================================================================== --- haiku/trunk/src/preferences/time/DateTimeEdit.cpp 2010-08-16 20:28:02 UTC (rev 38159) +++ haiku/trunk/src/preferences/time/DateTimeEdit.cpp 2010-08-16 20:30:37 UTC (rev 38160) @@ -10,6 +10,7 @@ * Adrien Destugues <pulkomandy@xxxxxxxxxxxxx> */ + #include "DateTimeEdit.h" #include <stdlib.h> @@ -27,15 +28,20 @@ TTimeEdit::TTimeEdit(BRect frame, const char* name, uint32 sections) : TSectionEdit(frame, name, sections), - fLastKeyDownTime(0) + fLastKeyDownTime(0), + fFields(NULL), + fFieldCount(0), + fFieldPositions(NULL), + fFieldPosCount(0) { InitView(); - fTime = BDateTime::CurrentDateTime(B_LOCAL_TIME); } TTimeEdit::~TTimeEdit() { + free(fFieldPositions); + free(fFields); } @@ -55,9 +61,9 @@ bigtime_t currentTime = system_time(); if (currentTime - fLastKeyDownTime < 1000000) { - int32 doubleDigi = number + fLastKeyDownInt * 10; - if (_IsValidDoubleDigi(doubleDigi)) - number = doubleDigi; + int32 doubleDigit = number + fLastKeyDownInt * 10; + if (_IsValidDoubleDigit(doubleDigit)) + number = doubleDigit; fLastKeyDownTime = 0; } else { fLastKeyDownTime = currentTime; @@ -80,6 +86,10 @@ // make sure we call the base class method, as it // will create the arrow bitmaps and the section list TSectionEdit::InitView(); + + fTime = BDateTime::CurrentDateTime(B_LOCAL_TIME); + _UpdateFields(); + SetSections(fSectionArea); } @@ -87,44 +97,28 @@ void TTimeEdit::DrawSection(uint32 index, bool hasFocus) { - TSection* section = NULL; - section = static_cast<TSection*> (fSectionList->ItemAt(index)); - + TSection* section = static_cast<TSection*>(fSectionList->ItemAt(index)); if (!section) return; + if (fFieldPositions == NULL || index * 2 + 1 > (uint32)fFieldPosCount) + return; + BRect bounds = section->Frame(); - time_t time = fTime.Time_t(); SetLowColor(ViewColor()); - BString field; if (hasFocus) SetLowColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); - BString text; - int* fieldPositions; - int fieldCount; + BString field; + fText.CopyCharsInto(field, fFieldPositions[index * 2], + fFieldPositions[index * 2 + 1] - fFieldPositions[index * 2]); - BLocale locale; - be_locale_roster->GetDefaultLocale(&locale); - locale.FormatTime(&text, fieldPositions, fieldCount, time, true); - // TODO : this should be cached somehow to not redo it for each field - - if (index * 2 + 1 > (uint32)fieldCount) { - free(fieldPositions); - return; - } - - text.CopyCharsInto(field, fieldPositions[index * 2], - fieldPositions[index * 2 + 1] - fieldPositions[index * 2]); - - free(fieldPositions); - // calc and center text in section rect float width = be_plain_font->StringWidth(field); - BPoint offset(-((bounds.Width()- width) / 2.0) -1.0, - bounds.Height() / 2.0 -6.0); + BPoint offset(-((bounds.Width()- width) / 2.0) - 1.0, + bounds.Height() / 2.0 - 6.0); SetHighColor(0, 0, 0, 255); FillRect(bounds, B_SOLID_LOW); @@ -135,38 +129,22 @@ void TTimeEdit::DrawSeparator(uint32 index) { - TSection* section = NULL; - section = static_cast<TSection*> (fSectionList->ItemAt(index)); - + TSection* section = static_cast<TSection*>(fSectionList->ItemAt(index)); if (!section) return; - BRect bounds = section->Frame(); - float sepWidth = SeparatorWidth(); - - BString text; - int* fieldPositions; - int fieldCount; - - BLocale locale; - be_locale_roster->GetDefaultLocale(&locale); - time_t time = fTime.Time_t(); - locale.FormatTime(&text, fieldPositions, fieldCount, time, true); - // TODO : this should be cached somehow to not redo it for each field - - if (index * 2 + 2 > (uint32)fieldCount) { - free(fieldPositions); + if (fFieldPositions == NULL || index * 2 + 2 > (uint32)fFieldPosCount) return; - } BString field; - text.CopyCharsInto(field, fieldPositions[index * 2 + 1], - fieldPositions[index * 2 + 2] - fieldPositions[index * 2 + 1]); + fText.CopyCharsInto(field, fFieldPositions[index * 2 + 1], + fFieldPositions[index * 2 + 2] - fFieldPositions[index * 2 + 1]); - free(fieldPositions); - + float sepWidth = SeparatorWidth(); + BRect bounds = section->Frame(); float width = be_plain_font->StringWidth(field); - BPoint offset(-((sepWidth - width) / 2.0) -1.0, bounds.Height() / 2.0 -6.0); + BPoint offset(-((sepWidth - width) / 2.0) - 1.0, + bounds.Height() / 2.0 - 6.0); DrawString(field, bounds.RightBottom() - offset); } @@ -179,16 +157,16 @@ float sepWidth = SeparatorWidth(); - float sep_2 = ceil(sepWidth / fSectionCount +1); - float width = bounds.Width() / fSectionCount -sep_2; - bounds.right = bounds.left + (width -sepWidth / fSectionCount); + float sep_2 = ceil(sepWidth / fSectionCount + 1); + float width = bounds.Width() / fSectionCount - sep_2; + bounds.right = bounds.left + (width - sepWidth / fSectionCount); for (uint32 idx = 0; idx < fSectionCount; idx++) { fSectionList->AddItem(new TSection(bounds)); bounds.left = bounds.right + sepWidth; - if (idx == fSectionCount -2) - bounds.right = area.right -1; + if (idx == fSectionCount - 2) + bounds.right = area.right - 1; else bounds.right = bounds.left + (width - sep_2); } @@ -215,11 +193,17 @@ void TTimeEdit::SetTime(int32 hour, int32 minute, int32 second) { - if (fTime.Time().Hour() == hour && fTime.Time().Minute() == minute - && fTime.Time().Second() == second) - return; + // make sure to update date upon overflow + if (hour == 0 && minute == 0 && second == 0) + fTime = BDateTime::CurrentDateTime(B_LOCAL_TIME); fTime.SetTime(BTime(hour, minute, second)); + + if (LockLooper()) { + _UpdateFields(); + UnlockLooper(); + } + Invalidate(Bounds()); } @@ -259,58 +243,57 @@ void TTimeEdit::BuildDispatch(BMessage* message) { - const char* fields[3] = { "hour", "minute", "second" }; + if (fFocus < 0 || fFocus >= fFieldCount) + return; message->AddBool("time", true); - BDateElement* dateFormat; - int fieldCount; - BLocale here; - be_locale_roster->GetDefaultLocale(&here); - here.GetTimeFields(dateFormat, fieldCount, true); - if (fFocus > fieldCount) { - free(dateFormat); - return; - } - - for (int32 index = 0; index < fSectionList->CountItems() -1; ++index) { + for (int32 index = 0; index < fSectionList->CountItems() - 1; ++index) { uint32 data = _SectionValue(index); if (fFocus == index) data = fHoldValue; - switch(dateFormat[index]) { + switch (fFields[index]) { case B_DATE_ELEMENT_HOUR: - message->AddInt32(fields[0], data); + message->AddInt32("hour", data); break; + case B_DATE_ELEMENT_MINUTE: - message->AddInt32(fields[1], data); + message->AddInt32("minute", data); break; + case B_DATE_ELEMENT_SECOND: - message->AddInt32(fields[2], data); + message->AddInt32("second", data); + break; + default: break; } } +} - free(dateFormat); + +void +TTimeEdit::_UpdateFields() +{ + BLocale locale; + be_locale_roster->GetDefaultLocale(&locale); + + time_t time = fTime.Time_t(); + locale.FormatTime(&fText, fFieldPositions, fFieldPosCount, time, true); + locale.GetTimeFields(fFields, fFieldCount, true); } void TTimeEdit::_CheckRange() { - int32 value = fHoldValue; - BDateElement* fields; - int fieldCount; - BLocale here; - be_locale_roster->GetDefaultLocale(&here); - here.GetTimeFields(fields, fieldCount, true); - if (fFocus > fieldCount) { - free(fields); + if (fFocus < 0 || fFocus >= fFieldCount) return; - } - switch (fields[fFocus]) { + + int32 value = fHoldValue; + switch (fFields[fFocus]) { case B_DATE_ELEMENT_HOUR: if (value > 23) value = 0; @@ -356,31 +339,22 @@ break; default: - free(fields); return; } - free(fields); - fHoldValue = value; Invalidate(Bounds()); } bool -TTimeEdit::_IsValidDoubleDigi(int32 value) +TTimeEdit::_IsValidDoubleDigit(int32 value) { - bool isInRange = false; - BDateElement* fields; - int fieldCount; - BLocale here; - be_locale_roster->GetDefaultLocale(&here); - here.GetTimeFields(fields, fieldCount, true); - if (fFocus > fieldCount) { - free(fields); + if (fFocus < 0 || fFocus >= fFieldCount) return false; - } - switch (fields[fFocus]) { + + bool isInRange = false; + switch (fFields[fFocus]) { case B_DATE_ELEMENT_HOUR: if (value <= 23) isInRange = true; @@ -397,11 +371,9 @@ break; default: - free(fields); - return isInRange; + break; } - free(fields); return isInRange; } @@ -409,17 +381,11 @@ int32 TTimeEdit::_SectionValue(int32 index) const { - int32 value; - BDateElement* fields; - int fieldCount; - BLocale here; - be_locale_roster->GetDefaultLocale(&here); - here.GetTimeFields(fields, fieldCount, true); - if (index > fieldCount) { - free(fields); + if (index < 0 || index >= fFieldCount) return 0; - } - switch (fields[index]) { + + int32 value; + switch (fFields[index]) { case B_DATE_ELEMENT_HOUR: value = fTime.Time().Hour(); break; @@ -437,7 +403,6 @@ break; } - free(fields); return value; } @@ -446,15 +411,21 @@ TDateEdit::TDateEdit(BRect frame, const char* name, uint32 sections) - : TSectionEdit(frame, name, sections) + : + TSectionEdit(frame, name, sections), + fFields(NULL), + fFieldCount(0), + fFieldPositions(NULL), + fFieldPosCount(0) { InitView(); - fDate = BDate::CurrentDate(B_LOCAL_TIME); } TDateEdit::~TDateEdit() { + free(fFieldPositions); + free(fFields); } @@ -474,9 +445,9 @@ bigtime_t currentTime = system_time(); if (currentTime - fLastKeyDownTime < 1000000) { - int32 doubleDigi = number + fLastKeyDownInt * 10; - if (_IsValidDoubleDigi(doubleDigi)) - number = doubleDigi; + int32 doubleDigit = number + fLastKeyDownInt * 10; + if (_IsValidDoubleDigit(doubleDigit)) + number = doubleDigit; fLastKeyDownTime = 0; } else { fLastKeyDownTime = currentTime; @@ -485,21 +456,13 @@ // if year add 2000 - BDateElement* dateFormat; - int fieldCount; - BLocale here; - be_locale_roster->GetDefaultLocale(&here); - here.GetDateFields(dateFormat, fieldCount, false); - - if (dateFormat[section] == B_DATE_ELEMENT_YEAR) { + if (fFields[section] == B_DATE_ELEMENT_YEAR) { int32 oldCentury = int32(fHoldValue / 100) * 100; if (number < 10 && oldCentury == 1900) number += 70; number += oldCentury; } - free(dateFormat); - // update display value fHoldValue = number; @@ -516,6 +479,10 @@ // make sure we call the base class method, as it // will create the arrow bitmaps and the section list TSectionEdit::InitView(); + + fDate = BDate::CurrentDate(B_LOCAL_TIME); + _UpdateFields(); + SetSections(fSectionArea); } @@ -523,42 +490,23 @@ void TDateEdit::DrawSection(uint32 index, bool hasFocus) { - TSection* section = NULL; - section = static_cast<TSection*> (fSectionList->ItemAt(index)); - + TSection* section = static_cast<TSection*>(fSectionList->ItemAt(index)); if (!section) return; - BRect bounds = section->Frame(); - BDateTime dateTime(fDate, BTime()); + if (fFieldPositions == NULL || index * 2 + 1 > (uint32)fFieldPosCount) + return; SetLowColor(ViewColor()); - BString field; if (hasFocus) SetLowColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); - BString text; - int* fieldPositions; - int fieldCount; + BString field; + fText.CopyCharsInto(field, fFieldPositions[index * 2], + fFieldPositions[index * 2 + 1] - fFieldPositions[index * 2]); - BLocale locale; - be_locale_roster->GetDefaultLocale(&locale); - locale.FormatDate(&text, fieldPositions, fieldCount, dateTime.Time_t(), - false); - // TODO : this should be cached somehow to not redo it for each field - - if (index * 2 + 1 > (uint32)fieldCount) { - free(fieldPositions); - return; - } - - text.CopyCharsInto(field, fieldPositions[index * 2], - fieldPositions[index * 2 + 1] - fieldPositions[index * 2]); - - free(fieldPositions); - - // calc and center text in section rect + BRect bounds = section->Frame(); float width = StringWidth(field); BPoint offset(-(bounds.Width() - width) / 2.0 - 1.0, (bounds.Height() / 2.0 - 6.0)); @@ -572,41 +520,27 @@ void TDateEdit::DrawSeparator(uint32 index) { - if (index == 3) + if (index < 0 || index >= 2) return; - TSection* section = NULL; - section = static_cast<TSection*> (fSectionList->ItemAt(index)); - BRect bounds = section->Frame(); + TSection* section = static_cast<TSection*>(fSectionList->ItemAt(index)); + if (!section) + return; - float sepWidth = SeparatorWidth(); - - SetHighColor(0, 0, 0, 255); - - BString text; - int* fieldPositions; - int fieldCount; - - BLocale locale; - be_locale_roster->GetDefaultLocale(&locale); - BDateTime dateTime(fDate, BTime()); - locale.FormatDate(&text, fieldPositions, fieldCount, dateTime.Time_t(), - false); - // TODO : this should be cached somehow to not redo it for each field - - if (index * 2 + 2 > (uint32)fieldCount) { - free(fieldPositions); + if (fFieldPositions == NULL || index * 2 + 2 > (uint32)fFieldPosCount) return; - } BString field; - text.CopyCharsInto(field, fieldPositions[index * 2 + 1], - fieldPositions[index * 2 + 2] - fieldPositions[index * 2 + 1]); + fText.CopyCharsInto(field, fFieldPositions[index * 2 + 1], + fFieldPositions[index * 2 + 2] - fFieldPositions[index * 2 + 1]); - free(fieldPositions); - + BRect bounds = section->Frame(); float width = be_plain_font->StringWidth(field); - BPoint offset(-((sepWidth - width) / 2.0) -1.0, bounds.Height() / 2.0 -6.0); + float sepWidth = SeparatorWidth(); + BPoint offset(-((sepWidth - width) / 2.0) - 1.0, + bounds.Height() / 2.0 - 6.0); + + SetHighColor(0, 0, 0, 255); DrawString(field, bounds.RightBottom() - offset); } @@ -615,7 +549,7 @@ TDateEdit::SetSections(BRect area) { // TODO : we have to be more clever here, as the fields can move and have - // different sizes dependin on the locale + // different sizes depending on the locale // create sections for (uint32 idx = 0; idx < fSectionCount; idx++) @@ -624,25 +558,22 @@ BRect bounds(area); float sepWidth = SeparatorWidth(); - // year - TSection* section = NULL; - float width = be_plain_font->StringWidth("0000") +6; - bounds.right = area.right; - bounds.left = bounds.right -width; - section = static_cast<TSection*> (fSectionList->ItemAt(2)); + TSection* section = static_cast<TSection*>(fSectionList->ItemAt(0)); + float width = be_plain_font->StringWidth("0000") + 10; + bounds.left = area.left; + bounds.right = bounds.left + width; section->SetFrame(bounds); - // day - width = be_plain_font->StringWidth("00") +6; - bounds.right = bounds.left -sepWidth; - bounds.left = bounds.right -width; - section = static_cast<TSection*> (fSectionList->ItemAt(1)); + section = static_cast<TSection*>(fSectionList->ItemAt(1)); + width = be_plain_font->StringWidth("0000") + 10; + bounds.left = bounds.right + sepWidth; + bounds.right = bounds.left + width; section->SetFrame(bounds); - // month - bounds.right = bounds.left - sepWidth; - bounds.left = area.left; - section = static_cast<TSection*> (fSectionList->ItemAt(0)); + section = static_cast<TSection*>(fSectionList->ItemAt(2)); + width = be_plain_font->StringWidth("0000") + 10; + bounds.left = bounds.right + sepWidth; + bounds.right = bounds.left + width; section->SetFrame(bounds); } @@ -667,10 +598,13 @@ void TDateEdit::SetDate(int32 year, int32 month, int32 day) { - if (year == fDate.Year() && month == fDate.Month() && day == fDate.Day()) - return; - fDate.SetDate(year, month, day); + + if (LockLooper()) { + _UpdateFields(); + UnlockLooper(); + } + Invalidate(Bounds()); } @@ -710,68 +644,57 @@ void TDateEdit::BuildDispatch(BMessage* message) { - const char* fields[3] = { "month", "day", "year" }; + if (fFocus < 0 || fFocus >= fFieldCount) + return; message->AddBool("time", false); - BDateElement* dateFormat; - int fieldCount; - BLocale here; - be_locale_roster->GetDefaultLocale(&here); - here.GetDateFields(dateFormat, fieldCount, false); - if (fFocus > fieldCount) { - free(dateFormat); - return; - } for (int32 index = 0; index < fSectionList->CountItems(); ++index) { uint32 data = _SectionValue(index); if (fFocus == index) data = fHoldValue; - switch(dateFormat[index]) { + switch (fFields[index]) { case B_DATE_ELEMENT_MONTH: - message->AddInt32(fields[0], data); + message->AddInt32("month", data); break; + case B_DATE_ELEMENT_DAY: - message->AddInt32(fields[1], data); + message->AddInt32("day", data); break; + case B_DATE_ELEMENT_YEAR: - message->AddInt32(fields[2], data); + message->AddInt32("year", data); break; + default: break; } } +} - free(dateFormat); + +void +TDateEdit::_UpdateFields() +{ + BLocale locale; + be_locale_roster->GetDefaultLocale(&locale); + + time_t time = BDateTime(fDate, BTime()).Time_t(); + locale.FormatDate(&fText, fFieldPositions, fFieldPosCount, time, false); + locale.GetDateFields(fFields, fFieldCount, false); } void TDateEdit::_CheckRange() { - int32 value = fHoldValue; - BDateElement* fields; - int fieldCount; - BLocale here; - be_locale_roster->GetDefaultLocale(&here); - here.GetDateFields(fields, fieldCount, false); - if (fFocus > fieldCount) { - free(fields); + if (fFocus < 0 || fFocus >= fFieldCount) return; - } - switch (fields[fFocus]) { - case B_DATE_ELEMENT_MONTH: - if (value > 12) - value = 1; - else if (value < 1) - value = 12; - - fDate.SetDate(fDate.Year(), value, fDate.Day()); - break; - + int32 value = fHoldValue; + switch (fFields[fFocus]) { case B_DATE_ELEMENT_DAY: { int32 days = fDate.DaysInMonth(); @@ -784,6 +707,15 @@ break; } + case B_DATE_ELEMENT_MONTH: + if (value > 12) + value = 1; + else if (value < 1) + value = 12; + + fDate.SetDate(fDate.Year(), value, fDate.Day()); + break; + case B_DATE_ELEMENT_YEAR: // 2037 is the end of 32-bit UNIX time if (value > 2037) @@ -795,53 +727,49 @@ break; default: - free(fields); return; } - free(fields); fHoldValue = value; Draw(Bounds()); } bool -TDateEdit::_IsValidDoubleDigi(int32 value) +TDateEdit::_IsValidDoubleDigit(int32 value) { - bool isInRange = false; - BDateElement* fields; - int fieldCount; - BLocale here; - be_locale_roster->GetDefaultLocale(&here); - here.GetDateFields(fields, fieldCount, false); - if (fFocus > fieldCount) { - free(fields); + if (fFocus < 0 || fFocus >= fFieldCount) return false; - } - int32 year = 0; - switch (fields[fFocus]) { + + bool isInRange = false; + switch (fFields[fFocus]) { case B_DATE_ELEMENT_DAY: { int32 days = fDate.DaysInMonth(); - if (value <= days) + if (value >= 1 && value <= days) isInRange = true; break; } + case B_DATE_ELEMENT_MONTH: + { + if (value >= 1 && value <= 12) + isInRange = true; + break; + } + case B_DATE_ELEMENT_YEAR: { - year = int32(fHoldValue / 100) * 100 + value; + int32 year = int32(fHoldValue / 100) * 100 + value; if (year <= 2037 && year >= 1970) isInRange = true; break; } default: - free(fields); - return isInRange; + break; } - free(fields); return isInRange; } @@ -849,17 +777,15 @@ int32 TDateEdit::_SectionValue(int32 index) const { - int32 value = 0; - BDateElement* fields; - int fieldCount; - BLocale here; - be_locale_roster->GetDefaultLocale(&here); - here.GetDateFields(fields, fieldCount, false); - if (index > fieldCount) { - free(fields); + if (index < 0 || index >= fFieldCount) return 0; - } - switch (fields[index]) { + + int32 value = 0; + switch (fFields[index]) { + case B_DATE_ELEMENT_YEAR: + value = fDate.Year(); + break; + case B_DATE_ELEMENT_MONTH: value = fDate.Month(); break; @@ -869,7 +795,6 @@ break; default: - value = fDate.Year(); break; } Modified: haiku/trunk/src/preferences/time/DateTimeEdit.h =================================================================== --- haiku/trunk/src/preferences/time/DateTimeEdit.h 2010-08-16 20:28:02 UTC (rev 38159) +++ haiku/trunk/src/preferences/time/DateTimeEdit.h 2010-08-16 20:30:37 UTC (rev 38160) @@ -15,6 +15,8 @@ #include "SectionEdit.h" #include <DateTime.h> +#include <Locale.h> +#include <String.h> class TTimeEdit : public TSectionEdit { @@ -41,14 +43,23 @@ void SetTime(int32 hour, int32 minute, int32 second); private: + void _UpdateFields(); void _CheckRange(); - bool _IsValidDoubleDigi(int32 value); + bool _IsValidDoubleDigit(int32 value); int32 _SectionValue(int32 index) const; private: BDateTime fTime; bigtime_t fLastKeyDownTime; int32 fLastKeyDownInt; + + BString fText; + + // TODO: morph the following into a proper class + BDateElement* fFields; + int fFieldCount; + int* fFieldPositions; + int fFieldPosCount; }; @@ -75,14 +86,23 @@ void SetDate(int32 year, int32 month, int32 day); private: + void _UpdateFields(); void _CheckRange(); - bool _IsValidDoubleDigi(int32 value); + bool _IsValidDoubleDigit(int32 value); int32 _SectionValue(int32 index) const; private: BDate fDate; bigtime_t fLastKeyDownTime; int32 fLastKeyDownInt; + + BString fText; + + // TODO: morph the following into a proper class + BDateElement* fFields; + int fFieldCount; + int* fFieldPositions; + int fFieldPosCount; };