ultimatepp/uppsrc/CtrlLib/DateTimeCtrl.cpp
2025-03-06 12:04:11 +01:00

1555 lines
29 KiB
C++

#include <CtrlLib/CtrlLib.h>
namespace Upp {
const Point Calendar::nullday = Point(-1, -1);
void PopUpCtrl::PopUp(Ctrl *owner, const Rect& rt)
{
Close();
Reset();
SetRect(rt);
Ctrl::PopUp(owner, true, true, GUI_DropShadows());
}
void PopUpCtrl::Deactivate()
{
if(WhenDeactivate)
{
WhenDeactivate();
}
else if(IsOpen() && IsPopUp())
{
WhenPopDown();
IgnoreMouseClick();
Close();
}
}
bool PopUpCtrl::IsPopUp() const
{
return popup || Ctrl::IsPopUp();
}
void PopUpCtrl::SetPopUp(bool b /* = true*/)
{
popup = b;
}
Calendar::Calendar()
{
SetFrame(BlackFrame());
Add(spin_year);
Add(spin_month);
Add(spin_all);
spin_month.SetCallbacks(THISBACK(OnMonthLeft), THISBACK(OnMonthRight));
spin_month.SetTips(t_("Previous month"), t_("Next month"));
spin_year.SetCallbacks(THISBACK(OnYearLeft), THISBACK(OnYearRight));
spin_year.SetTips(t_("Previous year"), t_("Next year"));
spin_all.SetCallbacks(THISBACK(OnMonthLeft), THISBACK(OnMonthRight));
spin_all.SetTips(t_("Previous month"), t_("Next month"));
time_mode = false;
swap_month_year = false;
one_button = false;
first_day = MONDAY;
selall = true;
bs = 5;
SetStyle(StyleDefault());
Reset();
ComputeSize();
}
void Calendar::Reset()
{
today = GetSysTime();
view = today;
view.day = 0;
sel = today;
istoday = false;
wastoday = false;
newday = oldday = nullday;
newweek = oldweek = -1;
stoday = Format("%s: %s", t_("Today"), time_mode ? Format(today) : Format(Date(today)));
UpdateFields();
}
Calendar& Calendar::SetStyle(const Style& s)
{
style = &s;
nbg = Colorize(IsDarkTheme() ? DarkTheme(CtrlImg::Bg()) : CtrlImg::Bg(), s.header);
Refresh();
return *this;
}
void Calendar::OnMonthLeft()
{
if(--view.month < 1)
{
view.month = 12;
if(view.year > 0)
view.year--;
}
UpdateFields();
}
void Calendar::OnMonthRight()
{
if(++view.month > 12)
{
view.month = 1;
view.year++;
}
UpdateFields();
}
void Calendar::OnYearLeft()
{
if(view.year > 0) view.year--;
UpdateFields();
}
void Calendar::OnYearRight()
{
view.year++;
UpdateFields();
}
void Calendar::UpdateFields()
{
String month = MonthName(view.month - 1);
String year = AsString(view.year);
spin_month.SetText(month);
spin_year.SetText(year);
if(swap_month_year)
spin_all.SetText(month + " " + year);
else
spin_all.SetText(year + " " + month);
Refresh();
}
int Calendar::DayOfWeek(int day, int month, int year, int zelleroffset)
{
if(month < 3)
{
month += 12 ;
year -= 1 ;
}
int n1 = (26 * (month + 1)) / 10;
int n2 = (125 * year) / 100;
return ((day + (8 - first_day) + n1 + n2 - (year / 100) + (year / 400) - zelleroffset) % 7);
}
int Calendar::WeekOfYear(int day, int month, int year) /* ISO-8601 */
{
const static int mnth[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
int weeknum;
bool cleapyear = IsLeapYear(year);
bool bleapyear = IsLeapYear(year - 1);
int daynum = day + mnth[month - 1];
if(cleapyear && month > 2)
daynum += 1;
int yy = (year - 1) % 100;
int c = (year - 1) - yy;
int g = yy + yy / 4;
int jan1weekday = 1 + (((((c / 100) % 4) * 5) + g) % 7);
int h = daynum + (jan1weekday - 1);
int weekday = 1 + ((h -1) % 7);
int yearnum;
if(daynum <= (8 - jan1weekday) && jan1weekday > 4)
{
yearnum = year - 1;
if(jan1weekday == 5 || (jan1weekday == 6 && bleapyear))
weeknum = 53;
else
weeknum = 52;
}
else
yearnum = year;
if(yearnum == year)
{
int yeardays = cleapyear ? 366 : 365;
if((yeardays - daynum) < (4 - weekday))
{
yearnum = year + 1;
weeknum = 1;
}
}
if(yearnum == year)
{
int fday = daynum + (7 - weekday) + (jan1weekday -1);
weeknum = fday / 7;
if(jan1weekday > 4)
weeknum -= 1;
}
return weeknum;
}
void Calendar::LeftDown(Point p, dword keyflags)
{
if(WhenWeek && newweek >= 0) {
if(PopUpCtrl::IsPopUp())
Deactivate();
Date d = view;
int wday = days[newweek][0];
d.day = wday;
if(wday < 0) {
d.day = -wday;
if(newweek == 0)
d.month--;
else
d.month++;
if(d.month < 1) {
d.month = 12;
d.year--;
}
if(d.month > 12) {
d.month = 1;
d.year++;
}
}
WhenWeek(d);
return;
}
bool isnewday = newday != nullday;
int day = 1;
bool refall = false;
if(isnewday)
{
day = Day(newday);
if(!selall && day < 0) return;
if(day < 0)
{
view.day = -day;
if(newday.y == 0)
view.month--;
else
view.month++;
if(view.month < 1)
{
view.month = 12;
view.year--;
}
if(view.month > 12)
{
view.month = 1;
view.year++;
}
refall = true;
}
}
else if(!istoday)
return;
Time tm = istoday ? today : view;
if(time_mode)
WhenTime(tm);
else
{
tm.hour = 0;
tm.minute = 0;
tm.second = 0;
}
if(PopUpCtrl::IsPopUp())
{
sel = tm;
Deactivate();
WhenAction();
}
else
{
sel = tm;
WhenAction();
if(refall)
{
view.day = 0;
Refresh();
Sync();
oldday = nullday;
MouseMove(p, 0);
return;
}
Refresh();
if(istoday)
SetDate(tm);
else
Refresh();
}
WhenSelect();
}
void Calendar::MouseMove(Point p, dword keyflags)
{
newday = GetDay(p);
if(newday != oldday)
{
if(oldday != nullday)
{
bool b = (selall == false ? Day(oldday) > 0 : true);
if(b)
{
view.day = 0;
Refresh();
}
}
if(newday != nullday)
{
bool b = (selall == false ? Day(newday) > 0 : true);
if(b)
{
view.day = Day(newday);
Refresh();
}
}
oldday = newday;
}
newweek = GetWeek(p);
if(newweek != oldweek && WhenWeek) {
Refresh();
oldweek = newweek;
}
istoday = MouseOnToday(p);
if(istoday != wastoday)
{
wastoday = istoday;
Refresh();
}
}
Image Calendar::CursorImage(Point p, dword keyflags)
{
bool b = (selall == false ? Day(newday) > 0 : true);
if((newday != nullday && b) || istoday)
return Image::Hand();
else
return Image::Arrow();
}
bool Calendar::MouseOnToday(Point p)
{
Size sz = GetSize();
int x0 = (sz.cx - sztoday.cx) / 2;
int x1 = x0 + sztoday.cx;
int y0 = sz.cy - (rh + sztoday.cy) / 2;
int y1 = y0 + sztoday.cy;
return (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1);
}
Point Calendar::GetDay(Point p)
{
for(int i = 0; i < rows; i++)
{
int y0 = 2 + (int)((i + 1) * rowh + hs);
int y1 = y0 + rh;
if(p.y >= y0 && p.y < y1) {
for(int j = 0; j < cols; j++)
{
int x0 = bs + 2 + (int)((j + 1) * colw);
int x1 = x0 + cw;
if(p.x >= x0 && p.x < x1)
return Point(j, i);
}
}
}
return Point(-1, -1);
}
int Calendar::GetWeek(Point p)
{
for(int i = 0; i < rows; i++)
{
int y0 = 2 + (int)((i + 1) * rowh + hs);
int y1 = y0 + rh;
if(p.y >= y0 && p.y < y1 && p.x < bs + 2 + colw)
return i;
}
return -1;
}
Size Calendar::ComputeSize()
{
const Style &st = *style;
Font fnt = st.font;
spin_all.SetFont(fnt);
spin_month.SetFont(fnt);
spin_year.SetFont(fnt);
spin_all.SetLeftImage(st.spinleftimg);
spin_all.SetRightImage(st.spinrightimg);
spin_month.SetLeftImage(st.spinleftimg);
spin_month.SetRightImage(st.spinrightimg);
spin_year.SetLeftImage(st.spinleftimg);
spin_year.SetRightImage(st.spinrightimg);
spin_all.SetHighlight(st.spinhighlight);
spin_month.SetHighlight(st.spinhighlight);
spin_year.SetHighlight(st.spinhighlight);
Size sz = IsPopUp() ? Size(-1, -1) : GetSize();
Size tsz = GetTextSize("WW", fnt.NoBold().NoUnderline());
Size rsz;
fh = tsz.cy;
colw = (float)(tsz.cx + 6);
rowh = (float)(tsz.cy + 4);
hs = /*spin_all.GetHeight()*/tsz.cy + 4;
rsz.cx = bs * 2 + 2 + (int)(colw * (cols + 1));
rsz.cy = (int)(rowh * (rows + 2) + hs);
if(sz.cx > rsz.cx)
{
colw = (sz.cx - bs * 2 + 2) / (float) (cols + 1);
rsz.cx = sz.cx;
}
if(sz.cy > rsz.cy)
{
rowh = (sz.cy - hs) / (float) (rows + 2);
rsz.cy = sz.cy;
}
cw = (int)(colw);
rh = (int)(rowh);
ts = rh;
return rsz;
}
void Calendar::Paint(Draw &w)
{
const Style &st = *style;
Font fnt = st.font;
Size sz = GetSize();
Size tsz;
String str;
int d = 1;
int m = view.month;
int y = view.year;
if(w.IsPainting(0, 0, sz.cx, hs))
{
w.DrawImage(0, 0, sz.cx, hs, nbg);
curdate = Format("%s %d", MonthName(view.month - 1), view.year);
}
w.DrawRect(0, hs, sz.cx, sz.cy - ts - hs, st.bgmain);
if(w.IsPainting(0, hs, sz.cx, rh))
{
int y = (int) (hs + ((double)rowh - fh) / 2.0);
fnt.NoBold().NoUnderline();
tsz = GetTextSize(t_("Wk"), fnt);
w.DrawText(bs + (cw - tsz.cx) / 2, y, t_("Wk"), fnt, st.week);
for(int i = 0; i < cols; i++)
{
const String &s = DyName((i + first_day) % 7);
tsz = GetTextSize(s, fnt);
w.DrawText(bs + 2 + (int)((i + 1) * colw) + (cw - tsz.cx) / 2, y, s, fnt, st.dayname);
}
}
if(w.IsPainting(0, sz.cy - ts, sz.cx, ts))
{
sztoday = GetTextSize(stoday, fnt.Bold().Underline(istoday));
w.DrawRect(0, sz.cy - ts, sz.cx, ts, st.bgtoday);
w.DrawText((sz.cx - sztoday.cx) / 2, sz.cy - (ts + sztoday.cy) / 2, stoday, fnt, istoday ? st.selecttoday : st.today);
}
int fd = DayOfWeek(1, view.month, view.year);
if(fd > 0)
{
if(--m < 1)
{
m = 12;
y -= 1;
}
d = GetDaysOfMonth(m, y) - fd + 1;
}
for(int i = 0; i < rows; i++)
{
int yp = 2 + (int) ((i + 1) * rowh + hs);
int yc = (rh - fh) / 2;
str = AsString(WeekOfYear(d, m, y));
Font wfnt = fnt;
wfnt.NoBold().NoUnderline();
Color wcolor = st.week;
if(newweek >= 0 && WhenWeek && newweek == i) {
wfnt.Bold().Underline();
wcolor = st.selectday;
}
w.DrawText(bs + (cw - GetTextSize(str, wfnt).cx) / 2, yp + yc, str, wfnt, wcolor);
for(int j = 0; j < cols; j++)
{
int xp = bs + 2 + (int)((j + 1) * colw);
if(w.IsPainting(xp, yp, cw, rh))
{
Color fg = st.fgmain;
Color bg = st.bgmain;
fnt.NoBold().NoUnderline();
bool special = false;
int sd = d;
if(m == view.month)
{
Day(j, i) = d;
fg = SBlack;
if(j == 6)
{
fg = SRed;
}
if(d == 1)
{
firstday.x = j;
firstday.y = i;
}
if(d == today.day && m == today.month && y == today.year)
{
fg = st.fgtoday;
bg = st.bgtoday;
fnt.Bold();
special = true;
curday.x = j;
curday.y = i;
}
if(d == sel.day && m == sel.month && y == sel.year)
{
fg = st.fgselect;
bg = st.bgselect;
fnt.Bold();
special = true;
prevday.x = j;
prevday.y = i;
}
}
else
{
fg = st.outofmonth;
sd = d > 0 ? -d : d;
Day(j, i) = sd;
}
if(sd == char(view.day))
{
if(sd < 0 && selall)
{
fg = st.outofmonth;
fnt.Bold().Underline(!special);
}
if(sd > 0 && view.month == m)
{
fg = st.selectday;
fnt.Bold().Underline(!special);
}
}
w.DrawRect(xp, yp, cw, rh, bg);
if(special)
{
DrawFrame(w, xp + 1, yp + 1, cw - 2, rh - 2, SBlack);
DrawFrame(w, xp, yp, cw, rh, st.bgmain);
}
str = AsString(abs(d));
w.DrawText(xp + (cw - GetTextSize(str, fnt).cx) / 2, yp + yc , str, fnt, fg);
}
if(++d > GetDaysOfMonth(m, y))
{
d = 1;
if(++m > 12)
{
m = 1;
y += 1;
}
}
}
}
w.DrawRect(bs, (int) (hs + rowh) + 1, sz.cx - bs * 2, 1, st.line);
w.DrawRect(bs + cw + 1, hs + bs, 1, sz.cy - hs - ts - bs * 1, st.line);
lastrow = row;
}
void Calendar::State(int reason)
{
if(reason == OPEN)
{
ComputeSize();
int yw = 0;
int mw = 0;
for(int i = 0; i < 12; i++)
mw = max(mw, spin_month.GetWidth(MonthName(i)));
if(one_button)
{
yw = spin_year.GetWidth(" 0000", false);
spin_all.HCenterPos(mw + yw, 0).TopPos(0, hs);
}
else
{
yw = spin_year.GetWidth("0000");
spin_month.TopPos(0, hs);
spin_year.TopPos(0, hs);
if(swap_month_year)
{
spin_month.RightPos(0, mw);
spin_year.LeftPos(0, yw);
}
else
{
spin_month.LeftPos(0, mw);
spin_year.RightPos(0, yw);
}
}
}
}
bool Calendar::Key(dword key, int count)
{
if(key == K_ESCAPE)
{
Deactivate();
return true;
}
return Ctrl::Key(key, count);
}
void Calendar::MouseLeave()
{
if(istoday)
{
istoday = false;
wastoday = false;
Refresh();
}
else
MouseMove(Point(-1, -1), 0);
}
Date Calendar::GetDate() const
{
return sel;
}
void Calendar::SetDate(int y, int m, int d)
{
SetDate(Date(y, m, d));
}
void Calendar::SetDate(const Date &dt)
{
sel = !IsNull(dt) && dt.IsValid() ? Time(dt.year, dt.month, dt.day, 0, 0, 0) : today;
view = sel;
view.day = 0;
UpdateFields();
}
Date Calendar::GetTime() const
{
return sel;
}
void Calendar::SetTime(int y, int m, int d, int h, int n, int s)
{
SetTime(Time(y, m, d, h, n, s));
}
void Calendar::SetTime(const Time &tm)
{
sel = !IsNull(tm) && tm.IsValid() ? tm : today;
view = sel;
view.day = 0;
UpdateFields();
}
void Calendar::PopUp(Ctrl *owner, Rect &rt)
{
Close();
Reset();
SetPopUp(true);
SetRect(rt);
Ctrl::PopUp(owner, true, true, GUI_DropShadows());
SetFocus();
}
void Calendar::SetView(const Time &v)
{
view = v;
view.day = 0;
UpdateFields();
}
// Clock
void LineCtrl::Paint(Draw& w)
{
Size sz = GetSize();
w.DrawRect(sz, Color(240, 240, 240));
if(real_pos < 0)
return;
if(sz.cx > sz.cy)
w.DrawRect(Rect(0, 0, min(sz.cx, real_pos), sz.cy), Blend(SColorHighlight(), White, 200));
else
w.DrawRect(Rect(0, max(0, real_pos), sz.cx, sz.cy), Blend(SColorHighlight(), White, 200));
}
void LineCtrl::SetPos(Point p)
{
Size sz = GetSize();
bool vh = sz.cy > sz.cx;
int mp = max(sz.cx, sz.cy);
real_pos = vh ? p.y : p.x;
if(real_pos < 0)
real_pos = 0;
else if(real_pos > mp)
real_pos = mp;
pos = (vh ? mp - real_pos : real_pos) * 100 / mp;
Refresh();
WhenAction();
}
int LineCtrl::GetPos()
{
return pos;
}
void LineCtrl::SetPos(int p)
{
Size sz = GetSize();
pos = p;
real_pos = p * max(sz.cx, sz.cy) / 100;
if(sz.cy > sz.cx)
real_pos = sz.cy - real_pos;
Refresh();
}
void LineCtrl::LeftDown(Point p, dword keyflags)
{
SetCapture();
SetPos(p);
}
void LineCtrl::MouseMove(Point p, dword keyflags)
{
if(HasCapture())
SetPos(p);
}
void LineCtrl::LeftUp(Point p, dword keyflags)
{
ReleaseCapture();
}
LineCtrl::LineCtrl()
{
pos = 0;
real_pos = -1;
BackPaint();
}
//Clock
Clock& Clock::SetStyle(const Style& s)
{
style = &s;
nbg = Colorize(IsDarkTheme() ? DarkTheme(CtrlImg::Bg()) : CtrlImg::Bg(), s.header);
Refresh();
return *this;
}
void Clock::PaintPtr(int n, Draw& w, Pointf p, double pos, double m, double rd, int d, Color color, Point cf)
{
double dx = m * sin(pos * 2 * M_PI);
double dy = m * cos(pos * 2 * M_PI);
lines[n].s.x = p.x - dx * 35 / 2.0;
lines[n].s.y = p.y + dy * 35 / 2.0;
lines[n].e.x = p.x + dx * (cf.x - rd);
lines[n].e.y = p.y - dy * (cf.y - rd);
w.DrawLine(lines[n].s, lines[n].e, d, color);
}
void Clock::PaintCenteredText(Draw& w, int x, int y, const char *text, const Font& fnt, Color c)
{
Size tsz = GetTextSize(text, fnt);
w.DrawText(x - tsz.cx / 2, y - tsz.cy / 2, text, fnt, c);
}
void Clock::PaintCenteredImage(Draw &w, int x, int y, const Image& img)
{
Size isz = img.GetSize();
w.DrawImage(x - isz.cx / 2, y - isz.cy / 2, img);
}
bool Clock::Key(dword key, int count)
{
if(key == K_TAB || key == K_SHIFT_TAB)
{
if(ed_minute.HasFocus())
ed_hour.SetFocus();
else if(ed_hour.HasFocus())
ed_minute.SetFocus();
else
ed_hour.SetFocus();
return true;
}
return Ctrl::Key(key, count);
}
void Clock::CalcSizes()
{
int margin = 3;
sz = GetSize();
int cx = sz.cx - 7 * 2;
int cy = sz.cy - 2 * margin - hs;
cm.x = cx / 2 + 7;
cm.y = cy / 2 + margin + hs;
cf.x = min(cy / 2, cx / 2);
cf.y = cf.x;
}
void Clock::Paint(Draw& w)
{
const Style &st = *style;
CalcSizes();
w.DrawRect(sz, st.bgmain);
w.DrawImage(0, 0, sz.cx, hs, nbg);
w.Clip(0, hs, sz.cx, sz.cy - hs);
if(colon)
PaintCenteredText(w, sz.cx / 2, hs / 2 - 1, " : ", StdFont().Bold(), SColorHighlightText());
//w.DrawEllipse(cm.x - r / 2, cm.y - r / 2, cf.x, cf.x, Blend(st.header, White, 250), PEN_NULL, Black);
Font fnt = st.font;
fnt.Height(max(6, min(16, fnt.GetHeight() * int(cf.x) / Ctrl::VertLayoutZoom(65))));
for(int i = 1; i <= 12; i++) {
PaintCenteredText(w,
int(cm.x + 0.8 * sin(i * M_PI / 6.0) * cf.x),
int(cm.y - 0.8 * cos(i * M_PI / 6.0) * cf.y),
AsString(i), fnt.Bold(i % 3 == 0), SColorText());
}
for(int i = 1; i <= 60; i++) {
int x = int(cm.x + 0.95 * sin(i * M_PI / 30.0) * cf.x);
int y = int(cm.y - 0.95 * cos(i * M_PI / 30.0) * cf.y);
PaintCenteredImage(w, x, y,
cur_point == i ? CtrlImg::BigDotH()
: i % 5 == 0 ? CtrlImg::BigDot() : CtrlImg::SmallDot());
}
PaintPtr(0, w, cm, cur_time / 3600.0 / 12.0, 0.5, 3.0, 5, cur_line == 0 ? st.arrowhl : st.arrowhour, cf);
PaintPtr(1, w, cm, cur_time / 3600.0, 0.6, 3.0, 3, cur_line == 1 ? st.arrowhl : st.arrowminute, cf);
if(seconds)
PaintPtr(2, w, cm, cur_time / 60.0, 0.75, 5.0, 2, cur_line == 2 ? st.arrowhl : st.arrowsecond, cf);
w.End();
}
int Clock::GetDir(int pp, int cp)
{
dir = 0;
if(cp >= 1 && cp <= 30 && pp >= 1 && pp <= 30 ||
cp > 30 && cp <= 60 && pp > 30 && pp <= 60)
{
dir = pp < cp ? 1 : -1;
}
else //there is a small problem on 30 when direction is opposite to "correct dir". Fortunatelly it won't hurt us hour/minute change is on 60
{
if(pp <= 60 && pp > 30 && cp >= 1)
dir = 1;
else if(pp >= 1 && cp <= 60 && cp > 30)
dir = -1;
}
return dir;
}
Clock::MinMax Clock::SetMinMax(int v, int min, int max)
{
MinMax mm;
if(v > max)
{
mm.diff = 1;
mm.value = min;
}
else if(v < min)
{
mm.diff = -1;
mm.value = max;
}
else
{
mm.diff = 0;
mm.value = v;
}
return mm;
}
int Clock::IncFactor(int dir, int pp, int cp)
{
if(dir > 0 && (pp < 60 && pp > 45 && (cp == 60 || (cp >= 1 && cp < 15))))
return 1;
if(dir < 0 && ((pp == 60 || (pp >= 1 && pp < 15)) && cp < 60 && cp > 45))
return -1;
return 0;
}
void Clock::MouseMove(Point p, dword keyflags)
{
if(!HasCapture())
{
cur_line = GetPointedLine(p);
if(cur_line != prv_line)
{
Refresh();
prv_line = cur_line;
}
if(cur_line < 0)
accept_time = !IsCircle(p, cm, cf.x / 2);
}
else
{
cur_point = GetPoint(p);
if(cur_point != prv_point && cur_point >= 0)
{
int dir = GetDir(prv_point, cur_point);
int inc = IncFactor(dir, prv_point, cur_point);
int cp = cur_point == 60 ? 0 : cur_point;
if(cur_line == 0)
{
int h = cp / 5;
if(sel.hour >= 12)
h += 12;
if(inc > 0)
h += 12;
else if(inc < 0)
h -= 12;
sel.hour = SetMinMax(h, 0, 23).value;
}
else if(cur_line == 1)
{
sel.hour = SetMinMax(sel.hour + inc, 0, 23).value;
sel.minute = cp;
}
else
{
MinMax mm = SetMinMax(sel.minute + inc, 0, 59);
int hour = int(sel.hour) + mm.diff;
sel.hour = SetMinMax(hour, 0, 23).value;
sel.minute = mm.value;
sel.second = cp;
}
UpdateTime();
prv_point = cur_point;
}
}
}
void Clock::LeftDown(Point p, dword keyflags)
{
if(cur_line >= 0)
SetCapture();
else if(accept_time)
{
Deactivate();
WhenAction();
}
}
void Clock::LeftUp(Point p, dword keyflags)
{
ReleaseCapture();
if(cur_point >= 0)
{
cur_point = -1;
prv_point = -1;
Refresh();
}
}
Image Clock::CursorImage(Point p, dword keyflags)
{
if(cur_line >= 0 || accept_time)
return CtrlImg::HandCursor();
else
return Image::Arrow();
}
int Clock::GetPointedLine(Point p)
{
for(int i = 0; i < 2 + int(seconds); i++)
if(IsLine(lines[i].s, lines[i].e, p))
return i;
return -1;
}
bool Clock::IsLine(Pointf s, Pointf e, Pointf p, double tolerance)
{
double minx = min(s.x, e.x);
double maxx = max(s.x, e.x);
double miny = min(s.y, e.y);
double maxy = max(s.y, e.y);
if(p.x < minx - tolerance) return false;
if(p.x > maxx + tolerance) return false;
if(p.y < miny - tolerance) return false;
if(p.y > maxy + tolerance) return false;
double dx = e.x - s.x;
double dy = e.y - s.y;
double u = (p.x - s.x) * dy - (p.y - s.y) * dx;
u *= u;
double l = dx * dx + dy * dy;
if(l == 0.0)
return false;
return u / l < tolerance * tolerance;
}
bool Clock::IsCircle(Pointf p, Pointf s, double r)
{
double dx = p.x - s.x;
double dy = p.y - s.y;
return dx * dx + dy * dy < r * r;
}
int Clock::GetPoint(Pointf p, double tolerance /* = 4.0*/)
{
double tcx = 0.95 * cf.x;
double tcy = 0.95 * cf.y;
double dx = p.x - cm.x;
double dy = p.y - cm.y;
double r = sqrt(double(dx * dx + dy * dy));
double sa = r != 0 ? dx / r : 0;
double ca = r != 0 ? dy / r : 0;
p.x = cm.x + tcx * sa;
p.y = cm.y + tcy * ca;
for(int i = 1; i <= 60; i++) {
double x = cm.x + tcx * sin(i * M_PI / 30);
double y = cm.y - tcy * cos(i * M_PI / 30);
if(fabs(p.x - x) + fabs(p.y - y) < tolerance)
return i;
}
return -1;
}
void Clock::Timer()
{
Refresh();
}
void Clock::UpdateFields()
{
ed_hour <<= sel.hour;
ed_minute <<= sel.minute;
ln_minute.SetPos(sel.minute * 100 / 59);
ln_hour.SetPos(sel.hour * 100 / 23);
spin_hour.SetText(Format("%.2d", sel.hour));
spin_minute.SetText(Format("%.2d", sel.minute));
}
void Clock::SetHourEdit()
{
sel.hour = int(~ed_hour);
UpdateTime();
}
void Clock::SetMinuteEdit()
{
sel.minute = int(~ed_minute);
UpdateTime();
}
void Clock::SetHourLine()
{
sel.hour = ln_hour.GetPos() * 23 / 100;
UpdateTime();
}
void Clock::SetMinuteLine()
{
sel.minute = ln_minute.GetPos() * 59 / 100;
UpdateTime();
}
void Clock::SetHourLeft()
{
sel.hour = SetMinMax(sel.hour - 1, 0, 23).value;
UpdateTime();
}
void Clock::SetHourRight()
{
sel.hour = SetMinMax(sel.hour + 1, 0, 23).value;
UpdateTime();
}
void Clock::SetMinuteLeft()
{
MinMax mm = SetMinMax(sel.minute - 1, 0, 59);
sel.minute = mm.value;
sel.hour = SetMinMax(sel.hour + mm.diff, 0, 23).value;
UpdateTime();
}
void Clock::SetMinuteRight()
{
MinMax mm = SetMinMax(sel.minute + 1, 0, 59);
sel.minute = mm.value;
sel.hour = SetMinMax(sel.hour + mm.diff, 0, 23).value;
UpdateTime();
}
void Clock::UpdateTime()
{
cur_time = int(sel.hour) * 3600 + int(sel.minute) * 60 + int(sel.second);
UpdateFields();
Refresh();
}
Size Clock::ComputeSize()
{
const Style &st = *style;
spin_hour.SetFont(st.font);
spin_minute.SetFont(st.font);
Font fnt = st.font;
Size tsz = GetTextSize("W", st.font);
hs = tsz.cy + 4;
//TODO: Find better scaling method.
double d = fnt == StdFont() ? 1 : tsz.cy / double(tsz.cx);
return Size(int(150.0 * d), int(157.0 * d));
}
void Clock::State(int reason)
{
if(reason == OPEN)
{
ComputeSize();
//spin_hour.LeftPos(0, spin_hour.GetWidth("00")).TopPos(0, hs);
//spin_minute.RightPos(0, spin_minute.GetWidth("00")).TopPos(0, hs);
int shw = spin_hour.GetWidth("00");
int smw = spin_minute.GetWidth("00");
spin_hour.HCenterPos(shw, -shw / 2).TopPos(0, hs);
spin_minute.HCenterPos(smw, smw / 2).TopPos(0, hs);
ln_minute.RightPos(0, 5).VSizePos(hs, 0);
ln_hour.LeftPos(0, 5).VSizePos(hs, 0);
}
}
Value Clock::GetData() const
{
return sel;
}
void Clock::SetData(const Value& v)
{
SetTime((Time) v);
}
Time Clock::GetTime() const
{
return sel;
}
void Clock::SetTime(const Time& tm)
{
sel = !IsNull(tm) && tm.IsValid() ? tm : GetSysTime();
UpdateTime();
}
void Clock::SetTime(int h, int n, int s)
{
sel.hour = h;
sel.minute = n;
sel.second = s;
UpdateTime();
}
void Clock::Reset()
{
sel = GetSysTime();
UpdateTime();
}
Clock::Clock()
{
sel = GetSysTime();
UpdateTime();
Add(ed_hour);
Add(ed_minute);
Add(ln_hour);
Add(ln_minute);
Add(spin_hour);
Add(spin_minute);
ed_hour.Min(0).Max(23);
ed_minute.Min(0).Max(59);
ed_hour <<= THISBACK(SetHourEdit);
ed_minute <<= THISBACK(SetMinuteEdit);
ln_hour <<= THISBACK(SetHourLine);
ln_minute <<= THISBACK(SetMinuteLine);
spin_hour.SetCallbacks(THISBACK(SetHourLeft), THISBACK(SetHourRight));
spin_hour.SetTips(t_("Previous hour"), t_("Next hour"));
spin_minute.SetCallbacks(THISBACK(SetMinuteLeft), THISBACK(SetMinuteRight));
spin_minute.SetTips(t_("Previous minute"), t_("Next minute"));
cur_line = -1;
prv_line = -1;
cur_hour = -1;
cur_minute = -1;
cur_second = -1;
prv_point = -1;
cur_point = -1;
SetStyle(StyleDefault());
seconds = true;
colon = false;
accept_time = false;
SetFrame(BlackFrame());
BackPaint();
ComputeSize();
}
// CalendarClock
CalendarClock::CalendarClock(int m) : mode(m)
{
if(mode == MODE_TIME)
{
Add(clock);
clock.SetFrame(NullFrame());
calendar.SetFrame(NullFrame());
SetFrame(BlackFrame());
calendar.TimeMode();
calendar.WhenTime = THISBACK(UpdateTime);
}
Add(calendar);
calendar.WhenDeactivate = THISBACK(Deactivate);
clock.WhenDeactivate = THISBACK(Deactivate);
}
void CalendarClock::UpdateTime(Time &tm)
{
tm.hour = clock.GetHour();
tm.minute = clock.GetMinute();
tm.second = clock.GetSecond();
}
void CalendarClock::PopUp(Ctrl *owner, const Rect& rt)
{
Close();
calendar.Reset();
clock.Reset();
calendar.SetPopUp(true);
clock.SetPopUp(true);
SetRect(rt);
Ctrl::PopUp(owner, true, true, GUI_DropShadows());
SetFocus();
}
Size CalendarClock::ComputeSize()
{
calendar_size = calendar.GetPopUpSize();
clock_size = clock.GetPopUpSize();
return mode == MODE_DATE ? calendar_size
: Size(calendar_size.cx + clock_size.cx + 2, max(calendar_size.cy, clock_size.cy));
}
void CalendarClock::Layout()
{
sz = ComputeSize();
calendar.LeftPos(0, calendar_size.cx).VSizePos(0, 0);
clock.RightPos(0, clock_size.cx).VSizePos(0, 0);
}
void CalendarClock::Deactivate()
{
if(IsOpen() && IsPopUp())
{
WhenPopDown();
IgnoreMouseUp();
Close();
calendar.SetPopUp(false);
clock.SetPopUp(false);
}
}
bool CalendarClock::Key(dword key, int count)
{
if(key == K_ESCAPE)
{
Deactivate();
return true;
}
return Ctrl::Key(key, count);
}
DropDate::DropDate() : DateTimeCtrl<EditDate>(CalendarClock::MODE_DATE)
{}
DropDate& DropDate::SetDate(int y, int m, int d)
{
EditDate::SetData(Date(y, m, d));
return *this;
}
DropTime::DropTime() : DateTimeCtrl<EditTime>(CalendarClock::MODE_TIME)
{}
DropTime& DropTime::SetTime(int y, int m, int d, int h, int n, int s)
{
EditTime::SetData(Time(y, m, d, h, n, s));
return *this;
}
// FlatButton
FlatButton::FlatButton()
{
hl = Blend(SColorHighlight, White, 200);
bg = Blend(SColorHighlight, White, 50);
fg = SColorPaper;
left = true;
highlight = true;
Transparent();
}
void FlatButton::DrawFrame(Draw &w, const Rect& r, Color lc, Color tc, Color rc, Color bc)
{
w.DrawRect(r.left, r.top, r.left + 1, r.bottom, lc);
w.DrawRect(r.right - 1, r.top, r.right, r.bottom, rc);
w.DrawRect(r.left, r.top, r.right, r.top + 1, tc);
w.DrawRect(r.left, r.bottom - 1, r.right, r.bottom, bc);
}
void FlatButton::Paint(Draw &w)
{
Size sz = GetSize();
Size isz = img.GetSize();
int dx = IsPush() * (left ? -1 : 1);
int dy = IsPush();
Point p((sz.cx - isz.cx) / 2 + dx, (sz.cy - isz.cy) / 2 + dy);
if(highlight)
w.DrawImage(p.x, p.y, img, HasMouse() ? SColorHighlightText() : Black);
else
w.DrawImage(p.x, p.y, img);
}
FlatSpin::FlatSpin()
{
tsz.Clear();
left.SetImage(CtrlImg::smallleft());
right.SetImage(CtrlImg::smallright());
Add(left);
Add(right);
left.SetLeft();
right.SetRight();
Transparent();
font = StdFont().Bold();
selected = false;
selectable = false;
}
FlatSpin& FlatSpin::Selectable(bool b /* = true*/)
{
selectable = b;
return *this;
}
void FlatSpin::SetFont(const Font& fnt)
{
font = fnt;
font.Bold();
}
int FlatSpin::GetWidth(const String& s, bool with_buttons)
{
Size sl = left.GetImage().GetSize();
Size sr = right.GetImage().GetSize();
int width = GetTextSize(s, font).cx;
if(with_buttons)
width += sl.cx + max(8, sl.cx / 2) + sr.cx + max(8, sr.cx / 2);
return width;
}
int FlatSpin::GetHeight()
{
int ih = max(left.GetImage().GetSize().cy + 8, right.GetImage().GetSize().cy + 8);
return max(font.GetHeight(), ih);
}
void FlatSpin::SetText(const String& s)
{
text = s;
tsz = GetTextSize(text, font);
Refresh();
}
void FlatSpin::SetTips(const char *tipl, const char *tipr)
{
left.Tip(tipl);
right.Tip(tipr);
}
void FlatSpin::SetCallbacks(const Event<>& cbl, const Event<>& cbr)
{
left.WhenAction = cbl;
right.WhenAction = cbr;
}
void FlatSpin::Layout()
{
left.LeftPos(0, left.GetImage().GetSize().cx + 8).VSizePos();
right.RightPos(0, right.GetImage().GetSize().cx + 8).VSizePos();
}
void FlatSpin::Paint(Draw& w)
{
Size sz = GetSize();
w.DrawText((sz.cx - tsz.cx) / 2, (sz.cy - tsz.cy) / 2, text, font.Underline(selected), SColorHighlightText());
}
Image FlatSpin::CursorImage(Point p, dword keyflags)
{
if(selectable && selected)
return CtrlImg::HandCursor();
else
return Image::Arrow();
}
void FlatSpin::MouseLeave()
{
selected = false;
Refresh();
}
void FlatSpin::MouseMove(Point p, dword keyflags)
{
if(!selectable)
return;
Size sz = GetSize();
bool s = selected;
selected = p.x >= (sz.cx - tsz.cx) / 2 && p.x <= (sz.cx + tsz.cx) / 2;
if(s != selected)
Refresh();
}
void FlatSpin::LeftDown(Point p, dword keyflags)
{
if(selected)
WhenAction();
}
CH_STYLE(Clock, Style, StyleDefault)
{
header = SColorHighlight;
bgmain = SColorPaper;
fgmain = SColorText;
arrowhl = SGreen;
arrowhour = SRed;
arrowminute = SBlue;
arrowsecond = SBlack;
font = StdFont();
}
CH_STYLE(Calendar, Style, StyleDefault)
{
header = SColorHighlight;
bgmain = SColorPaper;
bgtoday = SColorPaper;
bgselect = AColor(255, 254, 220);
fgmain = SColorText;
fgtoday = SBlack;
fgselect = SBlack;
outofmonth = AColor(180, 180, 180);
curdate = SWhite;
today = SColorText;
selecttoday = SColorMark;
cursorday = SColorText;
selectday = SColorMark;
line = SGray;
dayname = SColorText;
week = SColorText;
font = StdFont();
spinleftimg = CtrlImg::smallleft();
spinrightimg = CtrlImg::smallright();
spinhighlight = true;
}
}