public class XCalendar extends Canvas {
	//
	protected final XCalendarModel model;
	protected final XCalendarEventBus bus;
	protected final XCalendarReactor reactor;
	protected volatile boolean editable = true;
	protected final Map<Object, Object> cookies;
	protected Map<XCalendarState, List<XCalendarWidget>> widgets;
	
	//
	public final XCalendarEventBus getBus() { return this.bus; }
	public final XCalendarModel getModel() { return this.model; }
	public final XCalendarReactor getReactor() {return this.reactor; }

	/**
	 * 
	 */
	public XCalendar(Composite parent, int style) {
		//
		super(shell(parent), checkStyle(style | SWT.DOUBLE_BUFFERED));
		this.cookies = new HashMap<>();
		this.model = new XCalendarModel(style);
		this.bus = new XCalendarDefaultEventBus(this);
		this.reactor = new XCalendarDefaultRector(this);
		this.bus.addHandler(new XCalendarDefaultEventHandler(this));
		
		//
		getShell().setLayout(new FillLayout());
		addPaintListener(new XPaintListener());
		this.addListener(SWT.MouseUp, new XListener());
		this.addListener(SWT.MouseDown, new XListener());
		this.addListener(SWT.MouseMove, new XListener());
		this.addListener(SWT.MouseExit, new XListener());
		this.addListener(SWT.MouseEnter, new XListener());
		getShell().addListener(SWT.Deactivate, new XDeactivateListener());
	}
	
	/**
	 * 
	 */
	@Override
	public boolean setFocus() {
		super.forceFocus(); 
		return true;
	}
	
	public boolean isFocused() {
		return isFocusControl();
	}

	public boolean getEditable() {
		return editable;
	}
	
	public void setEditable(boolean editable) {
		this.editable = editable; redraw();
	}
	
	/**
	 * Cookie
	 */
	public final <T> T getCookie() {
		return getCookie("$DEFAULT");
	}
	
	public final void setCookie(Object value) {
		setCookie("$DEFAULT", value);
	}
	
	public final <T> T getCookie(final Object key) {
		return cast(this.cookies.get(key));
	}
	
	public void setCookie(Object key, Object value) {
		this.cookies.put(key, value);
	}
	
	/**
	 * 
	 */
	public void hide() {
		if(!isDisposed()) getShell().dispose(); 
	}
	
	public void show(Composite parent, Rectangle bounds) {
		this.widgets = XCalendarWidgetFactory.create(this);
		XCalendarLayout layout = model.getConfig().getLayout();
		layout.layout(this, parent, bounds); getShell().open();
	}
	
	/**
	 * 
	 */
	public void feed(Date date) {
		this.reactor.submit(new XCalendarFeedAction(date));
	}
	
	public void setup(Predicate<Date> predicate) {
		this.reactor.submit(new XCalendarSetupAction(predicate, false));
	}
	
	public void resize(Composite parent, Rectangle bounds) {
		this.reactor.submit(new XCalendarResizeAction(parent, bounds));
	}
	
	public void setup(Predicate<Date> predicate, boolean nullable) {
		this.reactor.submit(new XCalendarSetupAction(predicate, nullable));
	}
	
	/**
	 * 
	 */
	public List<XCalendarWidget> getWidgets() {
		return this.widgets.get(getModel().getState());
	}

	public XCalendarWidget getWidget(int x, int y) {
		return filter((v) -> v.getBounds().contains(x, y));
	}
	
	public void addXCalendarEventListener(XCalendarEventListener listener) {
		this.getBus().addListener(listener);
	}
	
	public void delXCalendarEventListener(XCalendarEventListener listener) {
		this.getBus().delListener(listener);
	}
	
	public XCalendarWidget filter(Predicate<XCalendarWidget> test) {
		for(XCalendarWidget v : getWidgets()) if(test.test(v)) return v; return null;
	}
	
	/**
	 * 
	 */
	protected void handle(Event event) {
		this.bus.handle(event);
	}

	protected void paint(final GC gc) {
		this.model.getConfig().getRender().render(this, gc);
	}

	protected static Composite shell(Composite parent) {
		return new Shell(parent.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
	}
	
	protected final class XListener implements Listener {
		@Override public void handleEvent(Event event) { handle(event); }
	}

	protected final class XPaintListener implements PaintListener {
		@Override public final void paintControl(final PaintEvent event) { paint(event.gc); }
	}
	
	protected final class XDeactivateListener implements Listener {
		@Override public void handleEvent(Event event) { SwtUtils.async(null, () -> { handle(event); }); }
	}

	protected static int checkStyle(int style) {
		if((style & SWT.TIME) != 0 || (style & SWT.DATE) != 0 || (style & SWT.LONG) != 0 ||
				(style & SWT.SHORT) != 0 || (style & SWT.MEDIUM) != 0) return style; else throw new SWTException(SWT.ERROR_INVALID_ARGUMENT);
	}
} public class XCalendarModel {
	//
	protected int style;
	protected Date date;
	protected ZoneId zoneId;
	protected boolean nullable;
	protected XCalendarTheme theme;
	protected XCalendarConfig config;
	protected XVirtualCalendar calendar;
	protected XCalendarStateMachine stateMachine;

	/**
	 * 
	 */
	public XCalendarModel(int style) {
		//
		this.style = style;
		this.config = new XCalendarConfig();
		this.zoneId = ZoneId.systemDefault();
		this.calendar = new XVirtualCalendar(this.zoneId, isDateTime());
		this.theme = new XCalendarThemeFactory().create(Locale.getDefault());
		
		//
		if (isDateTime()) {
			this.stateMachine = new XCalendarStateMachine(DATE, TIME);
		} else if (isDate()) {
			this.stateMachine = new XCalendarStateMachine(DATE, null);
		} else if (isYear()) {
			this.stateMachine = new XCalendarStateMachine(YEAR, null);
		} else if (isTime()) {
			this.stateMachine = new XCalendarStateMachine(TIME, null);
		} else if (isYearMonth()) {
			this.stateMachine = new XCalendarStateMachine(MONTH, null);
		} 
	}

	/**
	 * 
	 */
	public boolean isTime() {
		return (style & SWT.TIME) != 0;
	}

	public boolean isDate() {
		return (style & SWT.DATE) != 0;
	}

	public boolean isYear() {
		return (style & SWT.SHORT) != 0;
	}

	public boolean isDateTime() {
		return (style & SWT.LONG) != 0;
	}
	
	public boolean isYearMonth() {
		return (style & SWT.MEDIUM) != 0;
	}

	/**
	 * 
	 */
	public Date getDate() {
		return date;
	}
	
	public void setDate(Date date) {
		this.date = date;
	}
	
	public ZoneId getZoneId() {
		return zoneId;
	}

	public void setZoneId(ZoneId zoneId) {
		this.zoneId = zoneId;
	}
	
	public boolean isNullable() {
		return nullable;
	}

	public void setNullable(boolean nullable) {
		this.nullable = nullable;
	}

	public XCalendarState getState() {
		return this.stateMachine.current();
	}
	
	public XCalendarStateMachine getStateMachine() {
		return this.stateMachine;
	}

	public final XCalendarTheme getTheme() {
		return theme;
	}

	public final void setTheme(XCalendarTheme theme) {
		this.theme = theme;
	}

	public XVirtualCalendar getCalendar() {
		return calendar;
	}

	public void setCalendar(XVirtualCalendar calendar) {
		this.calendar = calendar;
	}
	
	public final XCalendarConfig getConfig() {
		return config;
	}

	public final void setConfig(XCalendarConfig config) {
		this.config = config;
	}
	
	/**
	 * 
	 */
	public Date now() {
		if(!isDateTime() && !isTime()) {
			return toDate(truncatedTo(XCalendarUtils.now(getZoneId()), ChronoUnit.DAYS));
		} else {
			return toDate(truncatedTo(XCalendarUtils.now(getZoneId()), ChronoUnit.SECONDS));
		}
	}
} public class XCalendarConfig {
	//
	protected XCalendarLayout layout;
	protected XCalendarRender render;
	
	/**
	 * 
	 */
	public XCalendarConfig() {
		this.layout = new XCalendarDefaultLayout();
		this.render = new XCalendarDefaultRender();
	}

	/**
	 * 
	 */
	public final XCalendarLayout getLayout() {
		return layout;
	}

	public final void setLayout(XCalendarLayout layout) {
		this.layout = layout;
	}

	public final XCalendarRender getRender() {
		return render;
	}

	public final void setRender(XCalendarRender render) {
		this.render = render;
	}
} public class XCalendarFrame implements AutoCloseable {
	//
	private GC gc;
	private Transform transform;
	
	/**
	 * 
	 */
	public XCalendarFrame(GC gc, Transform transform) {
		this.gc = gc; this.transform = transform;
	}

	/**
	 * 
	 */
	public final GC getGc() {
		return gc;
	}
	
	public Transform getTransform() {
		return transform;
	}

	/**
	 * 
	 */
	@Override
	public void close() {
		if(this.gc.isDisposed()) return;
		gc.setClipping((Rectangle)null);
		if(transform != null && !transform.isDisposed()) transform.dispose();
	}
} public abstract class AbstractXCalendarWidget implements XCalendarWidget {
	//
	protected Point margin;
	protected Rectangle bounds;
	protected boolean disposed;
	protected final XCalendar popup;
	protected boolean enabled = true;
	protected XCalendarMouseTracker mouse;
	
	/**
	 * 
	 */
	public AbstractXCalendarWidget(XCalendar popup) {
		this.popup = popup;
		this.disposed = false;
		this.margin = new Point(1, 1);
		this.bounds = new Rectangle(0, 0, 0, 0);
		this.mouse = new XCalendarMouseTracker();
	}
	
	/**
	 * 
	 */
	@Override
	public void dispose() {
		this.disposed = true;
	}

	@Override
	public boolean isDisposed() {
		return this.disposed;
	}

	@Override
	public final Point getMargin() {
		return this.margin;
	}
	
	@Override
	public final Rectangle getBounds() {
		return this.bounds;
	}
	
	@Override
	public void setMargin(Point margin) {
		this.margin = margin;
	}

	@Override
	public XCalendarMouseTracker getMouse() {
		return this.mouse;
	}
	@Override
	public void setBounds(int x, int y, int w, int h) {
		int mx = margin.x, my = margin.y;
		bounds = new Rectangle(x + mx, y + my, w - (mx << 1), h - (my << 1));
	}
	
	/**
	 * 
	 */
	protected void showCursor() {
		this.popup.setCursor(Cursors.CURSOR_HAND);
	}
	
	protected void hideCursor() {
		this.popup.setCursor(Cursors.CURSOR_ARROW);
	}
	
	protected boolean submit(final XCalendarAction action) {
		this.popup.getReactor().submit(action); return true;
	}
	
	protected <T> T query(XVirtualCalendar v, ZoneId z, int col, int row) {
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Event
	 */
	protected boolean onMouseUp(XCalendarMouseUpEvent event) {
		return false;
	}
	
	protected boolean onMouseDown(XCalendarMouseDownEvent event) {
		return false;
	}
	
	protected boolean onMouseMove(XCalendarMouseMoveEvent event) {
		return false;
	}
	
	protected boolean onMouseEnter(XCalendarMouseEnterEvent event) {
		return false;
	}
	
	protected boolean onMouseLeave(XCalendarMouseLeaveEvent event) {
		return false;
	}
	
	@Override
	public boolean handle(final XCalendarEvent event) {
		//
		if(!popup.getEnabled() || !popup.getEditable()) return false; 
		
		//
		this.mouse.track(event);
		if(event instanceof XCalendarMouseUpEvent) return onMouseUp(cast(event));
		else if(event instanceof XCalendarMouseDownEvent) return onMouseDown(cast(event));
		else if(event instanceof XCalendarMouseMoveEvent) return onMouseMove(cast(event));
		else if(event instanceof XCalendarMouseEnterEvent) return onMouseEnter(cast(event));
		else if(event instanceof XCalendarMouseLeaveEvent) return onMouseLeave(cast(event));
		return false;
	}
} public abstract class XCalendarTheme {
	
	private static final String[][] HOUR = { { "00", "01", "02", "03" }, 
			                                 { "04", "05", "06", "07" }, 
			                                 { "08", "09", "10", "11" }, 
			                                 { "12", "13", "14", "15" }, 
			                                 { "16", "17", "18", "19" }, 
			                                 { "20", "21", "22", "23" } };

	private static final String[][] MINUTE = { { "00", "05", "10", "15" }, 
                                               { "20", "25", "30", "35" }, 
                                               { "40", "45", "50", "55" } };

	private static final String[][] SECOND = { { "00", "05", "10", "15" }, 
                                               { "20", "25", "30", "35" }, 
                                               { "40", "45", "50", "55" } };
	
	//
	protected int arc = 8;
	protected int margin = 5;
	protected int width = 220;
	protected Font font = Fonts.getSystemFont();
	protected Font bold = Fonts.getFont(XCalendarTheme.class, "bold", () -> Fonts.bold(this.font));
	
	protected Color[] grid = new Color[] { getColor(0xc8, 0xc8, 0xc8), getColor(0x8f, 0xbc, 0xee) };
	protected Color[] toolbar = new Color[] { getColor(0x00, 0x00, 0x00), getColor(0x40, 0x87, 0xcc) };
	protected Color[] foreground = new Color[] { getColor(0x00, 0x00, 0x00), getColor(0x9f, 0x9f, 0x9f), getColor(0xff, 0xff, 0xff) };
	protected Color[] background = new Color[] { getColor(0xff, 0xff, 0xff), getColor(0xe0, 0xe0, 0xe0), getColor(0x00, 0x69, 0xcf), getColor(0xfe, 0xde, 0x00) };
	
	/**
	 * 
	 */
	public abstract String[][] getMonthTheme();
	public abstract String[] getDayOfWeekTheme();
	public abstract String[] getMonthOfYearTheme();
	public abstract String header(int month, int year);
	
	public String[][] getHourTheme(){ return HOUR; }
	public String[][] getMinuteTheme(){ return MINUTE; }
	public String[][] getSecondTheme(){ return SECOND; }

	/**
	 * 
	 */
	public int getArc() {
		return arc;
	}
	
	public Font getBold() {
		return bold;
	}
	
	public Font getFont() {
		return font;
	}

	public int getWidth() {
		return width;
	}
	
	public int getMargin() {
		return margin;
	}
	
	public Color[] getToolbar() {
		return toolbar;
	}
	
	public Color[] getForeground() {
		return foreground;
	}
	
	public Color[] getBackground() {
		return background;
	}
	
	/**
	 * 
	 */
	public void setArc(int arc) {
		this.arc = arc;
	}
	
	public void setBold(Font bold) {
		this.bold = bold;
	}
	
	public void setFont(Font font) {
		this.font = font;
	}
	
	public void setWidth(int width) {
		this.width = width;
	}
	
	public void setMargin(int margin) {
		this.margin = margin;
	}

	public void setToolbar(Color[] toolbar) {
		this.toolbar = toolbar;
	}
	
	public final void setGrid(Color[] colors) {
		this.grid = colors;
	}
	
	public void setForeground(Color[] foreground) {
		this.foreground = foreground;
	}

	public void setBackground(Color[] background) {
		this.background = background;
	}

	/**
	 * 
	 */
	public Color getGrid(boolean focused) {
		return focused ? grid[1] : grid[0];
	}
	
	public Color getToolBarBackground(boolean hovered){
		return hovered ? toolbar[0] : toolbar[1];
	}
	
	public Color getForeground(boolean enabled, boolean selected, boolean same) {
		return !enabled? foreground[1] : selected ? foreground[2] : same ? foreground[0] : foreground[1];
	}
	
	public Color getBackground(boolean enabled, boolean selected, boolean same, boolean hovered) {
		return !enabled ? background[0] : selected ? background[2] : same ? background[3] : hovered? background[1] : background[0];
	}
} public class XVirtualCalendar {
	//
	private int decade;
	private int century;
	private Date prevDate;
	private int depth = 400;
	private final boolean time;
	private final ZoneId zoneId;
	private ZonedDateTime travel;
	private ZonedDateTime calendar;
	private Predicate<Date> predicate;
	
	/**
	 * 
	 */
	public XVirtualCalendar(ZoneId zoneId, boolean time) {
		this.zoneId = zoneId; this.time = time; this.predicate = (v) -> true; 
		adjust(toDate(truncatedTo(now(zoneId), DAYS))); this.prevDate = null;
	}
	
	/**
	 * 
	 */
	private void update() { updateDecade(); updateCentury(); }
	private void updateDecade() { decade = travel.getYear() / 10 * 10; }
	private void updateCentury() { this.century = this.decade / 100 * 100; }
	
	/**
	 * 
	 */
	public void prevCentury() { century -= 100; }
	public void prevDecade() { decade -= 10; updateCentury(); }
	public void prevYear() { travel = travel.minusYears(1); update(); }
	public void prevMonth() { travel = travel.minusMonths(1); update(); }
	public void prevHour() { this.calendar = this.calendar.minusHours(1); }
	public void prevMinute() { this.calendar = this.calendar.minusMinutes(1); }
	public void prevSecond() { this.calendar = this.calendar.minusSeconds(1); }
	
	/**
	 * 
	 */
	public void nextCentury() { century += 100; }
	public void nextDecade() { decade += 10; updateCentury(); }
	public void nextYear() { travel = travel.plusYears(1); update(); }
	public void nextMonth() { travel = travel.plusMonths(1); update(); }
	public void nextHour() { this.calendar = this.calendar.plusHours(1); }
	public void nextMinute() { this.calendar = this.calendar.plusMinutes(1); }
	public void nextSecond() { this.calendar = this.calendar.plusSeconds(1); }
	
	/**
	 * 
	 */
	public Date getDate() { return toDate(calendar); }
	public Date getTravel() { return toDate(travel); }
	public ZonedDateTime getTravelDateTime() { return travel; }
	public ZonedDateTime getCalendarDateTime() { return calendar; }
	public boolean isValid(Date date) { return predicate.test(date); }
	public void setFilter(Predicate<Date> filter) { this.predicate = filter; }
	
	/**
	 * 
	 */
	public int getYear() { return travel.getYear(); }
	public int getMonth() { return travel.getMonthValue(); }
	public int getHour() { return this.calendar.getHour(); }
	public int getMinute() { return this.calendar.getMinute(); }
	public int getSecond() { return this.calendar.getSecond(); }
	public Range<Integer> getDecade() { return closed(decade, decade + 11); }
	public Range<Integer> getCentury() { return closed(century, century + 107); }
	
	/**
	 * 
	 */
	public void setCentury(int century) { this.century = century; }
	public void setDecade(int decade) { this.decade = decade; updateCentury(); }
	public void setYear(int year) { travel = travel.withYear(year); update(); }
	public void setMonth(int month) { travel = travel.withMonth(month); update(); }
	public void setHour(int hour ) { this.calendar = this.calendar.withHour(hour); }
	public void setMinute(int minute) { this.calendar = this.calendar.withMinute(minute); }
	public void setSecond(int second) { this.calendar = this.calendar.withSecond(second); }
	
	/**
	 * 
	 */
	public Date adjust(Date date) { 
		return adjust(date, false); 
	}
	
	public Date adjust(Date date, boolean clear) {
		//
		Date currDate = null;
		if (!this.time || clear) this.prevDate = null;
		this.travel = toDateTime(truncatedTo(date, ChronoUnit.SECONDS), zoneId);
		this.calendar = toDateTime(truncatedTo(date, ChronoUnit.SECONDS), zoneId);
		
		// +1 direction
		ZonedDateTime next = this.travel;
		for (int i = 0; i < depth && !isValid(currDate = toDate(next)); i++) 
			next = next.plusDays(prevDate == null ? 1 : !prevDate.after(currDate) ? 1 : -1);
		if (isValid(toDate(next))) {
			this.travel = next; this.calendar = next; prevDate = currDate; update(); return toDate(calendar);
		}
		
		// -1 direction
		ZonedDateTime prev = this.travel;
		for (int i = 0; i < depth && !isValid(currDate = toDate(prev)); i++) 
			prev = prev.plusDays(prevDate == null ? -1 : !prevDate.after(currDate) ? 1 : -1);
		if (isValid(toDate(prev))) {
			this.travel = prev; this.calendar = prev; prevDate = currDate; update(); return toDate(calendar);
		}
		return adjust(prevDate);
	}
} public enum XCalendarTime {
	//
	HOUR, MINUTE, SECOND;
	
	/**
	 * 
	 */
	public void decr(XVirtualCalendar calendar) {
		switch(this) {
		case HOUR: calendar.prevHour(); break;
		case MINUTE: calendar.prevMinute(); break;
		case SECOND: calendar.prevSecond(); break;
		default: throw new SWTException("invalid calendar time: " + this);
		}
	}
	
	public void incr(XVirtualCalendar calendar) {
		switch(this) {
		case HOUR: calendar.nextHour(); break;
		case MINUTE: calendar.nextMinute(); break;
		case SECOND: calendar.nextSecond(); break;
		default: throw new SWTException("invalid calendar time: " + this);
		}
	}

	/**
	 * 
	 */
	public String text(XCalendarTheme theme, XVirtualCalendar calendar) {
		switch(this) {
		case HOUR: return lappend(calendar.getHour(), 2, '0');
		case MINUTE: return lappend(calendar.getMinute(), 2, '0');
		case SECOND: return lappend(calendar.getSecond(), 2, '0');
		default: throw new SWTException("invalid calendar time: " + this);
		}
	}
} public final class Pair<T> {
	//
	private T v1;
	private T v2;
	
	/**
	 * 
	 */
	public Pair() {
	}
	
	public Pair(T v1, T v2) {
		this.v1 = v1;
		this.v2 = v2;
	}

	@Override
	public String toString() {
		return "Pair{" +
				"v1=" + v1 +
				", v2=" + v2 +
				'}';
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;

		Pair<?> pair = (Pair<?>) o;

		if (v1 != null ? !v1.equals(pair.v1) : pair.v1 != null) return false;
		return v2 != null ? v2.equals(pair.v2) : pair.v2 == null;
	}

	@Override
	public int hashCode() {
		int result = v1 != null ? v1.hashCode() : 0;
		result = 31 * result + (v2 != null ? v2.hashCode() : 0);
		return result;
	}

	/**
	 * 
	 */
	public T getV1() {
		return v1;
	}

	public void setV1(T v1) {
		this.v1 = v1;
	}
	
	public T getV2() {
		return v2;
	}

	public void setV2(T v2) {
		this.v2 = v2;
	}

	/**
	 * 
	 */
	public synchronized void swap() {
		T backup = this.v1;
		this.v1 = this.v2;
		this.v2 = backup;
	}
	
	/**
	 * 
	 */
	public static void swap(Pair<?> p) {
		doSwap(p); // Nothing but capture the <?>
	}
	
	private static <T> void doSwap(Pair<T> p) {
		synchronized(p) {
			T backup = p.v1;
			p.v1 = p.v2;
			p.v2 = backup;
		}
	}

} public static Point extent(GC gc, String text) {
	return extent(gc, text, getExtents(gc, 20480));
}
import cn.nextop.thorin.rcp.support.swt.widget.xcalendar.XCalendar;
import cn.nextop.thorin.rcp.support.swt.widget.xcalendar.event.bus.internal.*;
import cn.nextop.thorin.rcp.support.swt.widget.xcalendar.model.XCalendarModel;
import cn.nextop.thorin.rcp.support.swt.widget.xcalendar.model.config.XCalendarConfig;
import cn.nextop.thorin.rcp.support.swt.widget.xcalendar.model.config.render.XCalendarFrame;
import cn.nextop.thorin.rcp.support.swt.widget.xcalendar.model.config.widget.AbstractXCalendarWidget;
import cn.nextop.thorin.rcp.support.swt.widget.xcalendar.model.theme.XCalendarTheme;
import cn.nextop.thorin.rcp.support.swt.widget.xcalendar.support.XVirtualCalendar;
import cn.nextop.thorin.rcp.support.swt.widget.xcalendar.support.glossary.XCalendarTime;
import cn.nextop.thorin.rcp.support.util.type.Pair;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import static cn.nextop.thorin.rcp.support.swt.utility.graphics.Extents.extent;
import static cn.nextop.thorin.rcp.support.swt.widget.xcalendar.support.glossary.XCalendarTime.SECOND;
package cn.nextop.thorin.rcp.support.swt.widget.xcalendar.model.config.widget.impl;



/**
 * @author chenby
 *
 */
public class XCalendarTimeWidget extends AbstractXCalendarWidget {
	//
	private final XCalendarTime type;

	/**
	 * 
	 */
	public XCalendarTimeWidget(XCalendar popup, XCalendarTime type) {
		super(popup); this.type = type; setMargin(new Point(10, 10));
	}
	
	/**
	 * 
	 */
	public XCalendarTime getType() {
		return type;
	}
	
	/**
	 * 
	 */
	@Override
	public Pair<Point> locate(int x, int y, int w, int m) {
		int unit = w / 3;
		if(type == SECOND) { x = m; y += unit; } else x += unit;
		return new Pair<>(new Point(unit, unit), new Point(x, y));
	}

	/**
	 * Event
	 */
	@Override
	protected boolean onMouseMove(XCalendarMouseMoveEvent event) {
		return true;
	}

	@Override
	protected boolean onMouseDown(XCalendarMouseDownEvent event) {
		return true;
	}

	@Override
	protected boolean onMouseEnter(XCalendarMouseEnterEvent event) {
		showCursor(); popup.redraw(); return true;
	}

	@Override
	protected boolean onMouseLeave(XCalendarMouseLeaveEvent event) {
		hideCursor(); popup.redraw(); return true;
	}

	@Override
	protected boolean onMouseUp(final XCalendarMouseUpEvent event) {
		if (!mouse.isEntered()) { popup.redraw(); return true; }
		final XCalendarModel model = popup.getModel();