BInvoker methods are now used, and input-handling is also tweaked. Change-Id: I120cca8df9f11c11aac80911108d62fb49488f8f Reviewed-on: https://review.haiku-os.org/c/haiku/+/4927 Tested-by: Commit checker robot <[email protected]> Reviewed-by: Jérôme Duval <[email protected]>
1015 lines
17 KiB
C++
1015 lines
17 KiB
C++
/*
|
|
* Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* McCall <mccall@@digitalparadise.co.uk>
|
|
* Mike Berg <[email protected]>
|
|
* Julun <[email protected]>
|
|
* Clemens <[email protected]>
|
|
* Adrien Destugues <[email protected]>
|
|
* Hamish Morrison <[email protected]>
|
|
*/
|
|
|
|
|
|
#include "DateTimeEdit.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <ControlLook.h>
|
|
#include <DateFormat.h>
|
|
#include <LayoutUtils.h>
|
|
#include <List.h>
|
|
#include <Locale.h>
|
|
#include <String.h>
|
|
#include <Window.h>
|
|
|
|
|
|
namespace BPrivate {
|
|
|
|
|
|
const uint32 kArrowAreaWidth = 16;
|
|
|
|
|
|
TimeEdit::TimeEdit(const char* name, uint32 sections, BMessage* message)
|
|
:
|
|
SectionEdit(name, sections, message),
|
|
fLastKeyDownTime(0),
|
|
fFields(NULL),
|
|
fFieldCount(0),
|
|
fFieldPositions(NULL),
|
|
fFieldPosCount(0)
|
|
{
|
|
InitView();
|
|
}
|
|
|
|
|
|
TimeEdit::~TimeEdit()
|
|
{
|
|
free(fFieldPositions);
|
|
free(fFields);
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::KeyDown(const char* bytes, int32 numBytes)
|
|
{
|
|
if (IsEnabled() == false)
|
|
return;
|
|
SectionEdit::KeyDown(bytes, numBytes);
|
|
|
|
// only accept valid input
|
|
int32 number = atoi(bytes);
|
|
if (number < 0 || bytes[0] < '0')
|
|
return;
|
|
|
|
int32 section = FocusIndex();
|
|
if (section < 0 || section > 2)
|
|
return;
|
|
|
|
bigtime_t currentTime = system_time();
|
|
if (currentTime - fLastKeyDownTime < 1000000) {
|
|
int32 doubleDigit = number + fLastKeyDownInt * 10;
|
|
if (_IsValidDoubleDigit(doubleDigit))
|
|
number = doubleDigit;
|
|
fLastKeyDownTime = 0;
|
|
} else {
|
|
fLastKeyDownTime = currentTime;
|
|
fLastKeyDownInt = number;
|
|
}
|
|
|
|
// update display value
|
|
fHoldValue = number;
|
|
_CheckRange();
|
|
_UpdateFields();
|
|
|
|
// send message to change time
|
|
Invoke();
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::InitView()
|
|
{
|
|
// make sure we call the base class method, as it
|
|
// will create the arrow bitmaps and the section list
|
|
fTime = BDateTime::CurrentDateTime(B_LOCAL_TIME);
|
|
_UpdateFields();
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::DrawSection(uint32 index, BRect bounds, bool hasFocus)
|
|
{
|
|
if (fFieldPositions == NULL || index * 2 + 1 >= (uint32)fFieldPosCount)
|
|
return;
|
|
|
|
if (hasFocus)
|
|
SetLowColor(mix_color(ui_color(B_CONTROL_HIGHLIGHT_COLOR),
|
|
ViewColor(), 192));
|
|
else
|
|
SetLowColor(ViewColor());
|
|
|
|
BString field;
|
|
fText.CopyCharsInto(field, fFieldPositions[index * 2],
|
|
fFieldPositions[index * 2 + 1] - fFieldPositions[index * 2]);
|
|
|
|
BPoint point(bounds.LeftBottom());
|
|
point.y -= bounds.Height() / 2.0 - 6.0;
|
|
point.x += (bounds.Width() - StringWidth(field)) / 2;
|
|
SetHighUIColor(B_PANEL_TEXT_COLOR);
|
|
FillRect(bounds, B_SOLID_LOW);
|
|
DrawString(field, point);
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::DrawSeparator(uint32 index, BRect bounds)
|
|
{
|
|
if (fFieldPositions == NULL || index * 2 + 2 >= (uint32)fFieldPosCount)
|
|
return;
|
|
|
|
BString field;
|
|
fText.CopyCharsInto(field, fFieldPositions[index * 2 + 1],
|
|
fFieldPositions[index * 2 + 2] - fFieldPositions[index * 2 + 1]);
|
|
|
|
BPoint point(bounds.LeftBottom());
|
|
point.y -= bounds.Height() / 2.0 - 6.0;
|
|
point.x += (bounds.Width() - StringWidth(field)) / 2;
|
|
SetHighUIColor(B_PANEL_TEXT_COLOR);
|
|
DrawString(field, point);
|
|
}
|
|
|
|
|
|
float
|
|
TimeEdit::SeparatorWidth()
|
|
{
|
|
return 10.0f;
|
|
}
|
|
|
|
|
|
float
|
|
TimeEdit::MinSectionWidth()
|
|
{
|
|
return be_plain_font->StringWidth("00");
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::SectionFocus(uint32 index)
|
|
{
|
|
fLastKeyDownTime = 0;
|
|
fFocus = index;
|
|
fHoldValue = _SectionValue(index);
|
|
Draw(Bounds());
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::SetTime(int32 hour, int32 minute, int32 second)
|
|
{
|
|
// 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());
|
|
}
|
|
|
|
|
|
BTime
|
|
TimeEdit::GetTime()
|
|
{
|
|
return fTime.Time();
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::DoUpPress()
|
|
{
|
|
if (fFocus == -1)
|
|
SectionFocus(0);
|
|
|
|
// update displayed value
|
|
fHoldValue += 1;
|
|
|
|
_CheckRange();
|
|
_UpdateFields();
|
|
|
|
// send message to change time
|
|
Invoke();
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::DoDownPress()
|
|
{
|
|
if (fFocus == -1)
|
|
SectionFocus(0);
|
|
|
|
// update display value
|
|
fHoldValue -= 1;
|
|
|
|
_CheckRange();
|
|
_UpdateFields();
|
|
|
|
Invoke();
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::PopulateMessage(BMessage* message)
|
|
{
|
|
if (fFocus < 0 || fFocus >= fFieldCount)
|
|
return;
|
|
|
|
message->AddBool("time", true);
|
|
message->AddInt32("hour", fTime.Time().Hour());
|
|
message->AddInt32("minute", fTime.Time().Minute());
|
|
message->AddInt32("second", fTime.Time().Second());
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::_UpdateFields()
|
|
{
|
|
time_t time = fTime.Time_t();
|
|
|
|
if (fFieldPositions != NULL) {
|
|
free(fFieldPositions);
|
|
fFieldPositions = NULL;
|
|
}
|
|
fTimeFormat.Format(fText, fFieldPositions, fFieldPosCount, time,
|
|
B_MEDIUM_TIME_FORMAT);
|
|
|
|
if (fFields != NULL) {
|
|
free(fFields);
|
|
fFields = NULL;
|
|
}
|
|
fTimeFormat.GetTimeFields(fFields, fFieldCount, B_MEDIUM_TIME_FORMAT);
|
|
}
|
|
|
|
|
|
void
|
|
TimeEdit::_CheckRange()
|
|
{
|
|
if (fFocus < 0 || fFocus >= fFieldCount)
|
|
return;
|
|
|
|
int32 value = fHoldValue;
|
|
switch (fFields[fFocus]) {
|
|
case B_DATE_ELEMENT_HOUR:
|
|
if (value > 23)
|
|
value = 0;
|
|
else if (value < 0)
|
|
value = 23;
|
|
|
|
fTime.SetTime(BTime(value, fTime.Time().Minute(),
|
|
fTime.Time().Second()));
|
|
break;
|
|
|
|
case B_DATE_ELEMENT_MINUTE:
|
|
if (value> 59)
|
|
value = 0;
|
|
else if (value < 0)
|
|
value = 59;
|
|
|
|
fTime.SetTime(BTime(fTime.Time().Hour(), value,
|
|
fTime.Time().Second()));
|
|
break;
|
|
|
|
case B_DATE_ELEMENT_SECOND:
|
|
if (value > 59)
|
|
value = 0;
|
|
else if (value < 0)
|
|
value = 59;
|
|
|
|
fTime.SetTime(BTime(fTime.Time().Hour(), fTime.Time().Minute(),
|
|
value));
|
|
break;
|
|
|
|
case B_DATE_ELEMENT_AM_PM:
|
|
value = fTime.Time().Hour();
|
|
if (value < 13)
|
|
value += 12;
|
|
else
|
|
value -= 12;
|
|
if (value == 24)
|
|
value = 0;
|
|
|
|
// modify hour value to reflect change in am/ pm
|
|
fTime.SetTime(BTime(value, fTime.Time().Minute(),
|
|
fTime.Time().Second()));
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
|
|
fHoldValue = value;
|
|
Invalidate(Bounds());
|
|
}
|
|
|
|
|
|
bool
|
|
TimeEdit::_IsValidDoubleDigit(int32 value)
|
|
{
|
|
if (fFocus < 0 || fFocus >= fFieldCount)
|
|
return false;
|
|
|
|
bool isInRange = false;
|
|
switch (fFields[fFocus]) {
|
|
case B_DATE_ELEMENT_HOUR:
|
|
if (value <= 23)
|
|
isInRange = true;
|
|
break;
|
|
|
|
case B_DATE_ELEMENT_MINUTE:
|
|
if (value <= 59)
|
|
isInRange = true;
|
|
break;
|
|
|
|
case B_DATE_ELEMENT_SECOND:
|
|
if (value <= 59)
|
|
isInRange = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return isInRange;
|
|
}
|
|
|
|
|
|
int32
|
|
TimeEdit::_SectionValue(int32 index) const
|
|
{
|
|
if (index < 0 || index >= fFieldCount)
|
|
return 0;
|
|
|
|
int32 value;
|
|
switch (fFields[index]) {
|
|
case B_DATE_ELEMENT_HOUR:
|
|
value = fTime.Time().Hour();
|
|
break;
|
|
|
|
case B_DATE_ELEMENT_MINUTE:
|
|
value = fTime.Time().Minute();
|
|
break;
|
|
|
|
case B_DATE_ELEMENT_SECOND:
|
|
value = fTime.Time().Second();
|
|
break;
|
|
|
|
default:
|
|
value = 0;
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
float
|
|
TimeEdit::PreferredHeight()
|
|
{
|
|
font_height fontHeight;
|
|
GetFontHeight(&fontHeight);
|
|
return ceilf((fontHeight.ascent + fontHeight.descent) * 1.4);
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
DateEdit::DateEdit(const char* name, uint32 sections, BMessage* message)
|
|
:
|
|
SectionEdit(name, sections, message),
|
|
fFields(NULL),
|
|
fFieldCount(0),
|
|
fFieldPositions(NULL),
|
|
fFieldPosCount(0)
|
|
{
|
|
InitView();
|
|
}
|
|
|
|
|
|
DateEdit::~DateEdit()
|
|
{
|
|
free(fFieldPositions);
|
|
free(fFields);
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::KeyDown(const char* bytes, int32 numBytes)
|
|
{
|
|
if (IsEnabled() == false)
|
|
return;
|
|
SectionEdit::KeyDown(bytes, numBytes);
|
|
|
|
// only accept valid input
|
|
int32 number = atoi(bytes);
|
|
if (number < 0 || bytes[0] < '0')
|
|
return;
|
|
|
|
int32 section = FocusIndex();
|
|
if (section < 0 || section > 2)
|
|
return;
|
|
|
|
bigtime_t currentTime = system_time();
|
|
if (currentTime - fLastKeyDownTime < 1000000) {
|
|
int32 doubleDigit = number + fLastKeyDownInt * 10;
|
|
if (_IsValidDoubleDigit(doubleDigit))
|
|
number = doubleDigit;
|
|
fLastKeyDownTime = 0;
|
|
} else {
|
|
fLastKeyDownTime = currentTime;
|
|
fLastKeyDownInt = number;
|
|
}
|
|
|
|
// if year add 2000
|
|
|
|
if (fFields[section] == B_DATE_ELEMENT_YEAR) {
|
|
int32 oldCentury = int32(fHoldValue / 100) * 100;
|
|
if (number < 10 && oldCentury == 1900)
|
|
number += 70;
|
|
number += oldCentury;
|
|
}
|
|
fHoldValue = number;
|
|
|
|
// update display value
|
|
_CheckRange();
|
|
_UpdateFields();
|
|
|
|
// send message to change time
|
|
Invoke();
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::InitView()
|
|
{
|
|
// make sure we call the base class method, as it
|
|
// will create the arrow bitmaps and the section list
|
|
fDate = BDate::CurrentDate(B_LOCAL_TIME);
|
|
_UpdateFields();
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::DrawSection(uint32 index, BRect bounds, bool hasFocus)
|
|
{
|
|
if (fFieldPositions == NULL || index * 2 + 1 >= (uint32)fFieldPosCount)
|
|
return;
|
|
|
|
if (hasFocus)
|
|
SetLowColor(mix_color(ui_color(B_CONTROL_HIGHLIGHT_COLOR),
|
|
ViewColor(), 192));
|
|
else
|
|
SetLowColor(ViewColor());
|
|
|
|
BString field;
|
|
fText.CopyCharsInto(field, fFieldPositions[index * 2],
|
|
fFieldPositions[index * 2 + 1] - fFieldPositions[index * 2]);
|
|
|
|
BPoint point(bounds.LeftBottom());
|
|
point.y -= bounds.Height() / 2.0 - 6.0;
|
|
point.x += (bounds.Width() - StringWidth(field)) / 2;
|
|
SetHighUIColor(B_PANEL_TEXT_COLOR);
|
|
FillRect(bounds, B_SOLID_LOW);
|
|
DrawString(field, point);
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::DrawSeparator(uint32 index, BRect bounds)
|
|
{
|
|
if (index >= 2)
|
|
return;
|
|
|
|
if (fFieldPositions == NULL || index * 2 + 2 >= (uint32)fFieldPosCount)
|
|
return;
|
|
|
|
BString field;
|
|
fText.CopyCharsInto(field, fFieldPositions[index * 2 + 1],
|
|
fFieldPositions[index * 2 + 2] - fFieldPositions[index * 2 + 1]);
|
|
|
|
BPoint point(bounds.LeftBottom());
|
|
point.y -= bounds.Height() / 2.0 - 6.0;
|
|
point.x += (bounds.Width() - StringWidth(field)) / 2;
|
|
SetHighUIColor(B_PANEL_TEXT_COLOR);
|
|
DrawString(field, point);
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::SectionFocus(uint32 index)
|
|
{
|
|
fLastKeyDownTime = 0;
|
|
fFocus = index;
|
|
fHoldValue = _SectionValue(index);
|
|
Draw(Bounds());
|
|
}
|
|
|
|
|
|
float
|
|
DateEdit::MinSectionWidth()
|
|
{
|
|
return be_plain_font->StringWidth("00");
|
|
}
|
|
|
|
|
|
float
|
|
DateEdit::SeparatorWidth()
|
|
{
|
|
return 10.0f;
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::SetDate(int32 year, int32 month, int32 day)
|
|
{
|
|
fDate.SetDate(year, month, day);
|
|
|
|
if (LockLooper()) {
|
|
_UpdateFields();
|
|
UnlockLooper();
|
|
}
|
|
|
|
Invalidate(Bounds());
|
|
}
|
|
|
|
|
|
BDate
|
|
DateEdit::GetDate()
|
|
{
|
|
return fDate;
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::DoUpPress()
|
|
{
|
|
if (fFocus == -1)
|
|
SectionFocus(0);
|
|
|
|
// update displayed value
|
|
fHoldValue += 1;
|
|
|
|
_CheckRange();
|
|
_UpdateFields();
|
|
|
|
// send message to change Date
|
|
Invoke();
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::DoDownPress()
|
|
{
|
|
if (fFocus == -1)
|
|
SectionFocus(0);
|
|
|
|
// update display value
|
|
fHoldValue -= 1;
|
|
|
|
_CheckRange();
|
|
_UpdateFields();
|
|
|
|
// send message to change Date
|
|
Invoke();
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::PopulateMessage(BMessage* message)
|
|
{
|
|
if (fFocus < 0 || fFocus >= fFieldCount)
|
|
return;
|
|
|
|
message->AddBool("time", false);
|
|
message->AddInt32("year", fDate.Year());
|
|
message->AddInt32("month", fDate.Month());
|
|
message->AddInt32("day", fDate.Day());
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::_UpdateFields()
|
|
{
|
|
time_t time = BDateTime(fDate, BTime()).Time_t();
|
|
|
|
if (fFieldPositions != NULL) {
|
|
free(fFieldPositions);
|
|
fFieldPositions = NULL;
|
|
}
|
|
|
|
fDateFormat.Format(fText, fFieldPositions, fFieldPosCount, time,
|
|
B_SHORT_DATE_FORMAT);
|
|
|
|
if (fFields != NULL) {
|
|
free(fFields);
|
|
fFields = NULL;
|
|
}
|
|
fDateFormat.GetFields(fFields, fFieldCount, B_SHORT_DATE_FORMAT);
|
|
}
|
|
|
|
|
|
void
|
|
DateEdit::_CheckRange()
|
|
{
|
|
if (fFocus < 0 || fFocus >= fFieldCount)
|
|
return;
|
|
|
|
int32 value = fHoldValue;
|
|
switch (fFields[fFocus]) {
|
|
case B_DATE_ELEMENT_DAY:
|
|
{
|
|
int32 days = fDate.DaysInMonth();
|
|
if (value > days)
|
|
value = 1;
|
|
else if (value < 1)
|
|
value = days;
|
|
|
|
fDate.SetDate(fDate.Year(), fDate.Month(), value);
|
|
break;
|
|
}
|
|
|
|
case B_DATE_ELEMENT_MONTH:
|
|
{
|
|
if (value > 12)
|
|
value = 1;
|
|
else if (value < 1)
|
|
value = 12;
|
|
|
|
int32 day = fDate.Day();
|
|
fDate.SetDate(fDate.Year(), value, 1);
|
|
|
|
// changing between months with differing amounts of days
|
|
while (day > fDate.DaysInMonth())
|
|
day--;
|
|
fDate.SetDate(fDate.Year(), value, day);
|
|
break;
|
|
}
|
|
|
|
case B_DATE_ELEMENT_YEAR:
|
|
fDate.SetDate(value, fDate.Month(), fDate.Day());
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
fHoldValue = value;
|
|
Invalidate(Bounds());
|
|
}
|
|
|
|
|
|
bool
|
|
DateEdit::_IsValidDoubleDigit(int32 value)
|
|
{
|
|
if (fFocus < 0 || fFocus >= fFieldCount)
|
|
return false;
|
|
|
|
bool isInRange = false;
|
|
switch (fFields[fFocus]) {
|
|
case B_DATE_ELEMENT_DAY:
|
|
{
|
|
int32 days = fDate.DaysInMonth();
|
|
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:
|
|
{
|
|
int32 year = int32(fHoldValue / 100) * 100 + value;
|
|
if (year >= 2000)
|
|
isInRange = true;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return isInRange;
|
|
}
|
|
|
|
|
|
int32
|
|
DateEdit::_SectionValue(int32 index) const
|
|
{
|
|
if (index < 0 || index >= fFieldCount)
|
|
return 0;
|
|
|
|
int32 value = 0;
|
|
switch (fFields[index]) {
|
|
case B_DATE_ELEMENT_YEAR:
|
|
value = fDate.Year();
|
|
break;
|
|
|
|
case B_DATE_ELEMENT_MONTH:
|
|
value = fDate.Month();
|
|
break;
|
|
|
|
case B_DATE_ELEMENT_DAY:
|
|
value = fDate.Day();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
float
|
|
DateEdit::PreferredHeight()
|
|
{
|
|
font_height fontHeight;
|
|
GetFontHeight(&fontHeight);
|
|
return ceilf((fontHeight.ascent + fontHeight.descent) * 1.4);
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
SectionEdit::SectionEdit(const char* name, uint32 sections, BMessage* message)
|
|
:
|
|
BControl(name, NULL, message, B_WILL_DRAW | B_NAVIGABLE),
|
|
fFocus(-1),
|
|
fSectionCount(sections),
|
|
fHoldValue(0)
|
|
{
|
|
}
|
|
|
|
|
|
SectionEdit::~SectionEdit()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
SectionEdit::AttachedToWindow()
|
|
{
|
|
AdoptParentColors();
|
|
BControl::AttachedToWindow();
|
|
}
|
|
|
|
|
|
void
|
|
SectionEdit::Draw(BRect updateRect)
|
|
{
|
|
DrawBorder(updateRect);
|
|
|
|
for (uint32 idx = 0; idx < fSectionCount; idx++) {
|
|
DrawSection(idx, FrameForSection(idx),
|
|
((uint32)fFocus == idx) && IsFocus());
|
|
if (idx < fSectionCount - 1)
|
|
DrawSeparator(idx, FrameForSeparator(idx));
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SectionEdit::MouseDown(BPoint where)
|
|
{
|
|
if (IsEnabled() == false)
|
|
return;
|
|
|
|
MakeFocus(true);
|
|
|
|
if (fUpRect.Contains(where))
|
|
DoUpPress();
|
|
else if (fDownRect.Contains(where))
|
|
DoDownPress();
|
|
else if (fSectionCount > 0) {
|
|
for (uint32 idx = 0; idx < fSectionCount; idx++) {
|
|
if (FrameForSection(idx).Contains(where)) {
|
|
SectionFocus(idx);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BSize
|
|
SectionEdit::MaxSize()
|
|
{
|
|
return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
|
|
BSize(B_SIZE_UNLIMITED, PreferredHeight()));
|
|
}
|
|
|
|
|
|
BSize
|
|
SectionEdit::MinSize()
|
|
{
|
|
BSize minSize;
|
|
minSize.height = PreferredHeight();
|
|
minSize.width = (SeparatorWidth() + MinSectionWidth())
|
|
* fSectionCount;
|
|
return BLayoutUtils::ComposeSize(ExplicitMinSize(),
|
|
minSize);
|
|
}
|
|
|
|
|
|
BSize
|
|
SectionEdit::PreferredSize()
|
|
{
|
|
return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
|
|
MinSize());
|
|
}
|
|
|
|
|
|
BRect
|
|
SectionEdit::FrameForSection(uint32 index)
|
|
{
|
|
BRect area = SectionArea();
|
|
float sepWidth = SeparatorWidth();
|
|
|
|
float width = (area.Width() -
|
|
sepWidth * (fSectionCount - 1))
|
|
/ fSectionCount;
|
|
area.left += index * (width + sepWidth);
|
|
area.right = area.left + width;
|
|
|
|
return area;
|
|
}
|
|
|
|
|
|
BRect
|
|
SectionEdit::FrameForSeparator(uint32 index)
|
|
{
|
|
BRect area = SectionArea();
|
|
float sepWidth = SeparatorWidth();
|
|
|
|
float width = (area.Width() -
|
|
sepWidth * (fSectionCount - 1))
|
|
/ fSectionCount;
|
|
area.left += (index + 1) * width + index * sepWidth;
|
|
area.right = area.left + sepWidth;
|
|
|
|
return area;
|
|
}
|
|
|
|
|
|
void
|
|
SectionEdit::MakeFocus(bool focused)
|
|
{
|
|
if (focused == IsFocus())
|
|
return;
|
|
|
|
BControl::MakeFocus(focused);
|
|
|
|
if (fFocus == -1)
|
|
SectionFocus(0);
|
|
else
|
|
SectionFocus(fFocus);
|
|
}
|
|
|
|
|
|
void
|
|
SectionEdit::KeyDown(const char* bytes, int32 numbytes)
|
|
{
|
|
if (IsEnabled() == false)
|
|
return;
|
|
if (fFocus == -1)
|
|
SectionFocus(0);
|
|
|
|
switch (bytes[0]) {
|
|
case B_LEFT_ARROW:
|
|
fFocus -= 1;
|
|
if (fFocus < 0)
|
|
fFocus = fSectionCount - 1;
|
|
SectionFocus(fFocus);
|
|
break;
|
|
|
|
case B_RIGHT_ARROW:
|
|
fFocus += 1;
|
|
if ((uint32)fFocus >= fSectionCount)
|
|
fFocus = 0;
|
|
SectionFocus(fFocus);
|
|
break;
|
|
|
|
case B_UP_ARROW:
|
|
DoUpPress();
|
|
break;
|
|
|
|
case B_DOWN_ARROW:
|
|
DoDownPress();
|
|
break;
|
|
|
|
default:
|
|
BControl::KeyDown(bytes, numbytes);
|
|
break;
|
|
}
|
|
Draw(Bounds());
|
|
}
|
|
|
|
|
|
status_t
|
|
SectionEdit::Invoke(BMessage* message)
|
|
{
|
|
if (message == NULL)
|
|
message = Message();
|
|
if (message == NULL)
|
|
return BControl::Invoke(NULL);
|
|
|
|
BMessage clone(*message);
|
|
PopulateMessage(&clone);
|
|
return BControl::Invoke(&clone);
|
|
}
|
|
|
|
|
|
uint32
|
|
SectionEdit::CountSections() const
|
|
{
|
|
return fSectionCount;
|
|
}
|
|
|
|
|
|
int32
|
|
SectionEdit::FocusIndex() const
|
|
{
|
|
return fFocus;
|
|
}
|
|
|
|
|
|
BRect
|
|
SectionEdit::SectionArea() const
|
|
{
|
|
BRect sectionArea = Bounds().InsetByCopy(2, 2);
|
|
sectionArea.right -= kArrowAreaWidth;
|
|
return sectionArea;
|
|
}
|
|
|
|
|
|
void
|
|
SectionEdit::DrawBorder(const BRect& updateRect)
|
|
{
|
|
BRect bounds(Bounds());
|
|
bool showFocus = (IsFocus() && Window() && Window()->IsActive());
|
|
|
|
be_control_look->DrawBorder(this, bounds, updateRect, ViewColor(),
|
|
B_FANCY_BORDER, showFocus ? BControlLook::B_FOCUSED : 0);
|
|
|
|
// draw up/down control
|
|
|
|
bounds.left = bounds.right - kArrowAreaWidth;
|
|
bounds.right = Bounds().right - 2;
|
|
fUpRect.Set(bounds.left + 3, bounds.top + 2, bounds.right,
|
|
bounds.bottom / 2.0);
|
|
fDownRect = fUpRect.OffsetByCopy(0, fUpRect.Height() + 2);
|
|
|
|
BPoint middle(floorf(fUpRect.left + fUpRect.Width() / 2),
|
|
fUpRect.top + 1);
|
|
BPoint left(fUpRect.left + 3, fUpRect.bottom - 1);
|
|
BPoint right(left.x + 2 * (middle.x - left.x), fUpRect.bottom - 1);
|
|
|
|
SetPenSize(2);
|
|
SetLowColor(ViewColor());
|
|
|
|
if (updateRect.Intersects(fUpRect)) {
|
|
FillRect(fUpRect, B_SOLID_LOW);
|
|
BeginLineArray(2);
|
|
AddLine(left, middle, HighColor());
|
|
AddLine(middle, right, HighColor());
|
|
EndLineArray();
|
|
}
|
|
if (updateRect.Intersects(fDownRect)) {
|
|
middle.y = fDownRect.bottom - 1;
|
|
left.y = right.y = fDownRect.top + 1;
|
|
|
|
FillRect(fDownRect, B_SOLID_LOW);
|
|
BeginLineArray(2);
|
|
AddLine(left, middle, HighColor());
|
|
AddLine(middle, right, HighColor());
|
|
EndLineArray();
|
|
}
|
|
|
|
SetPenSize(1);
|
|
}
|
|
|
|
|
|
} // namespace BPrivate
|