#include 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(CalendarClock::MODE_DATE) {} DropDate& DropDate::SetDate(int y, int m, int d) { EditDate::SetData(Date(y, m, d)); return *this; } DropTime::DropTime() : DateTimeCtrl(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; } }