/* * Copyright (c) 2013 by Gerrit Grunwald * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package eu.hansolo.enzo.lcd; import eu.hansolo.enzo.lcd.skin.LcdClockSkin; import javafx.animation.AnimationTimer; import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.css.PseudoClass; import javafx.scene.control.Control; import javafx.scene.control.Skin; import java.time.Clock; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; public class LcdClock extends Control { public static final String STYLE_CLASS_BEIGE = "lcd-beige"; public static final String STYLE_CLASS_BLUE = "lcd-blue"; public static final String STYLE_CLASS_ORANGE = "lcd-orange"; public static final String STYLE_CLASS_RED = "lcd-red"; public static final String STYLE_CLASS_YELLOW = "lcd-yellow"; public static final String STYLE_CLASS_WHITE = "lcd-white"; public static final String STYLE_CLASS_GRAY = "lcd-gray"; public static final String STYLE_CLASS_BLACK = "lcd-black"; public static final String STYLE_CLASS_GREEN = "lcd-green"; public static final String STYLE_CLASS_GREEN_DARKGREEN = "lcd-green-darkgreen"; public static final String STYLE_CLASS_BLUE2 = "lcd-blue2"; public static final String STYLE_CLASS_BLUE_BLACK = "lcd-blue-black"; public static final String STYLE_CLASS_BLUE_DARKBLUE = "lcd-blue-darkblue"; public static final String STYLE_CLASS_BLUE_LIGHTBLUE = "lcd-blue-lightblue"; public static final String STYLE_CLASS_BLUE_GRAY = "lcd-blue-gray"; public static final String STYLE_CLASS_STANDARD = "lcd-standard"; public static final String STYLE_CLASS_LIGHTGREEN = "lcd-lightgreen"; public static final String STYLE_CLASS_STANDARD_GREEN = "lcd-standard-green"; public static final String STYLE_CLASS_BLUE_BLUE = "lcd-blue-blue"; public static final String STYLE_CLASS_RED_DARKRED = "lcd-red-darkred"; public static final String STYLE_CLASS_DARKBLUE = "lcd-darkblue"; public static final String STYLE_CLASS_PURPLE = "lcd-purple"; public static final String STYLE_CLASS_BLACK_RED = "lcd-black-red"; public static final String STYLE_CLASS_DARKGREEN = "lcd-darkgreen"; public static final String STYLE_CLASS_AMBER = "lcd-amber"; public static final String STYLE_CLASS_LIGHTBLUE = "lcd-lightblue"; public static final String STYLE_CLASS_GREEN_BLACK = "lcd-green-black"; public static final String STYLE_CLASS_YELLOW_BLACK = "lcd-yellow-black"; public static final String STYLE_CLASS_BLACK_YELLOW = "lcd-black-yellow"; public static final String STYLE_CLASS_LIGHTGREEN_BLACK = "lcd-lightgreen-black"; public static final String STYLE_CLASS_DARKPURPLE = "lcd-darkpurple"; public static final String STYLE_CLASS_DARKAMBER = "lcd-darkamber"; public static final String STYLE_CLASS_BLUE_LIGHTBLUE2 = "lcd-blue-lightblue2"; public static final String STYLE_CLASS_GRAY_PURPLE = "lcd-gray-purple"; public static final String STYLE_CLASS_SECTIONS = "lcd-sections"; public static final String STYLE_CLASS_YOCTOPUCE = "lcd-yoctopuce"; public static final String STYLE_CLASS_FLAT_TURQOISE = "lcd-flat-turqoise"; public static final String STYLE_CLASS_FLAT_GREEN_SEA = "lcd-flat-green-sea"; public static final String STYLE_CLASS_FLAT_EMERLAND = "lcd-flat-emerland"; public static final String STYLE_CLASS_FLAT_NEPHRITIS = "lcd-flat-nephritis"; public static final String STYLE_CLASS_FLAT_PETER_RIVER = "lcd-flat-peter-river"; public static final String STYLE_CLASS_FLAT_BELIZE_HOLE = "lcd-flat-belize-hole"; public static final String STYLE_CLASS_FLAT_AMETHYST = "lcd-flat-amethyst"; public static final String STYLE_CLASS_FLAT_WISTERIA = "lcd-flat-wisteria"; public static final String STYLE_CLASS_FLAT_SUNFLOWER = "lcd-flat-sunflower"; public static final String STYLE_CLASS_FLAT_ORANGE = "lcd-flat-orange"; public static final String STYLE_CLASS_FLAT_CARROT = "lcd-flat-carrot"; public static final String STYLE_CLASS_FLAT_PUMPKIN = "lcd-flat-pumpkin"; public static final String STYLE_CLASS_FLAT_ALIZARIN = "lcd-flat-alizarin"; public static final String STYLE_CLASS_FLAT_POMEGRANATE = "lcd-flat-pomegranate"; public static final String STYLE_CLASS_FLAT_CLOUDS = "lcd-flat-clouds"; public static final String STYLE_CLASS_FLAT_SILVER = "lcd-flat-silver"; public static final String STYLE_CLASS_FLAT_CONCRETE = "lcd-flat-concrete"; public static final String STYLE_CLASS_FLAT_ASBESTOS = "lcd-flat-asbestos"; public static final String STYLE_CLASS_FLAT_WET_ASPHALT = "lcd-flat-wet-asphalt"; public static final String STYLE_CLASS_FLAT_MIDNIGHT_BLUE = "lcd-flat-midnight-blue"; public static enum LcdFont { STANDARD, LCD, DIGITAL, DIGITAL_BOLD, ELEKTRA } public static enum DateFormat { YEAR_MONTH_DAY, DAY_MONTH_YEAR; } public static enum DateSeparator { DOT("."), SLASH("/"), MINUS("-"); public final String chr; private DateSeparator(final String CHR) { chr = CHR; } } // CSS pseudo classes private static final PseudoClass NO_FRAME_PSEUDO_CLASS = PseudoClass.getPseudoClass("no-frame"); private BooleanProperty noFrame; private boolean initialized; private boolean firstTime; private boolean keepAspect; private String _title = ""; private StringProperty title; private String _titleFont = "Open Sans"; private StringProperty titleFont; private LcdFont _timeFont = LcdFont.LCD; private ObjectProperty<LcdFont> timeFont; private String _smallFont = "Open Sans"; private StringProperty smallFont; private boolean _backgroundVisible = true; private BooleanProperty backgroundVisible; private boolean _crystalOverlayVisible = false; private BooleanProperty crystalOverlayVisible; private boolean _mainInnerShadowVisible = false; private BooleanProperty mainInnerShadowVisible; private boolean _foregroundShadowVisible = false; private BooleanProperty foregroundShadowVisible; private ObjectProperty<Locale> locale; private ObservableList<Alarm> alarms; private List<Alarm> alarmsToRemove; private ObjectProperty<Clock> clock; private ObjectProperty<LocalDateTime> time; private ObjectProperty<DateFormat> dateFormat; private ObjectProperty<DateSeparator> dateSeparator; private long lastTimerCall; private AnimationTimer timer; // ******************** Constructors ************************************** public LcdClock() { getStyleClass().add("lcd-clock"); clock = new SimpleObjectProperty<>(this, "clock", Clock.systemDefaultZone()); time = new SimpleObjectProperty<>(this, "time", LocalDateTime.now(clock.get())); alarms = FXCollections.observableArrayList(); alarmsToRemove = new ArrayList<>(); initialized = false; firstTime = true; keepAspect = true; lastTimerCall = System.nanoTime(); timer = new AnimationTimer() { @Override public void handle(final long NOW) { if (NOW > lastTimerCall + 1_000_000_000l) { time.set(LocalDateTime.now(clock.get())); lastTimerCall = NOW; } } }; init(); initialized = true; timer.start(); } // ******************** Initialization ************************************ private void init() { timeProperty().addListener(observable -> { alarmsToRemove.clear(); for (Alarm alarm : alarms) { switch (alarm.getRepetition()) { case ONCE: if (getTime().isAfter(alarm.getTime())) { if (alarm.isArmed()) { fireEvent(new Alarm.AlarmEvent(alarm, this, this, Alarm.AlarmEvent.ALARM)); alarm.executeCommand(); } alarmsToRemove.add(alarm); } break; case HOURLY: if (alarm.getTime().getHour() == getTime().getMinute() && alarm.getTime().getMinute() == getTime().getSecond()) { if (alarm.isArmed()) { fireEvent(new Alarm.AlarmEvent(alarm, this, this, Alarm.AlarmEvent.ALARM)); alarm.executeCommand(); } } break; case DAILY: if (alarm.getTime().getHour() == getTime().getHour() && alarm.getTime().getMinute() == getTime().getMinute() && alarm.getTime().getSecond() == getTime().getSecond()) { if (alarm.isArmed()) { fireEvent(new Alarm.AlarmEvent(alarm, this, this, Alarm.AlarmEvent.ALARM)); alarm.executeCommand(); } } break; case WEEKLY: if (alarm.getTime().getDayOfWeek() == getTime().getDayOfWeek() && alarm.getTime().getHour() == getTime().getHour() && alarm.getTime().getMinute() == getTime().getMinute() && alarm.getTime().getSecond() == getTime().getSecond()) { if (alarm.isArmed()) { fireEvent(new Alarm.AlarmEvent(alarm, this, this, Alarm.AlarmEvent.ALARM)); alarm.executeCommand(); } } break; } } for (Alarm alarm : alarmsToRemove) { removeAlarm(alarm); } }); } // ******************** Methods ******************************************* public final boolean isKeepAspect() { return keepAspect; } public final void setKeepAspect(final boolean KEEP_ASPECT) { keepAspect = KEEP_ASPECT; } @Override public boolean isResizable() { return true; } public final Clock getClock() { return clock.get(); } public final void setClock(final Clock CLOCK) { clock.set(CLOCK); } public final ObjectProperty<Clock> clockProperty() { return clock; } public final LocalDateTime getTime() { return time.get(); } public final ReadOnlyObjectProperty timeProperty() { return time; } public final String getTitle() { return null == title ? _title : title.get(); } public final void setTitle(final String TITLE) { if (null == title) { _title = TITLE; } else { title.set(TITLE); } } public final StringProperty titleProperty() { if (null == title) { title = new SimpleStringProperty(this, "title", _title); } return title; } public final boolean isNoFrame() { return null == noFrame ? true : noFrame.get(); } public final void setNoFrame(final boolean NO_FRAME) { noFrameProperty().set(NO_FRAME); } public final BooleanProperty noFrameProperty() { if (null == noFrame) { noFrame = new BooleanPropertyBase(false) { @Override protected void invalidated() { pseudoClassStateChanged(NO_FRAME_PSEUDO_CLASS, get()); } @Override public Object getBean() { return this; } @Override public String getName() { return "noFrame"; } }; } return noFrame; } public final boolean isBackgroundVisible() { return null == backgroundVisible ? _backgroundVisible : backgroundVisible.get(); } public final void setBackgroundVisible(final boolean BACKGROUND_VISIBLE) { if (null == backgroundVisible) { _backgroundVisible = BACKGROUND_VISIBLE; } else { backgroundVisible.set(BACKGROUND_VISIBLE); } } public final BooleanProperty backgroundVisibleProperty() { if (null == backgroundVisible) { backgroundVisible = new SimpleBooleanProperty(this, "backgroundVisible", _backgroundVisible); } return backgroundVisible; } public final boolean isCrystalOverlayVisible() { return null == crystalOverlayVisible ? _crystalOverlayVisible : crystalOverlayVisible.get(); } public final void setCrystalOverlayVisible(final boolean CRYSTAL_OVERLAY_VISIBLE) { if (null == crystalOverlayVisible) { _crystalOverlayVisible = CRYSTAL_OVERLAY_VISIBLE; } else { crystalOverlayVisible.set(CRYSTAL_OVERLAY_VISIBLE); } } public final BooleanProperty crystalOverlayVisibleProperty() { if (null == crystalOverlayVisible) { crystalOverlayVisible = new SimpleBooleanProperty(this, "crystalOverlayVisible", _crystalOverlayVisible); } return crystalOverlayVisible; } public final boolean isMainInnerShadowVisible() { return null == mainInnerShadowVisible ? _mainInnerShadowVisible : mainInnerShadowVisible.get(); } public final void setMainInnerShadowVisible(final boolean MAIN_INNER_SHADOW_VISIBLE) { if (null == mainInnerShadowVisible) { _mainInnerShadowVisible = MAIN_INNER_SHADOW_VISIBLE; } else { mainInnerShadowVisible.set(MAIN_INNER_SHADOW_VISIBLE); } } public final BooleanProperty mainInnerShadowVisibleProperty() { if (null == mainInnerShadowVisible) { mainInnerShadowVisible = new SimpleBooleanProperty(this, "mainInnerShadowVisible", _mainInnerShadowVisible); } return mainInnerShadowVisible; } public final boolean isForegroundShadowVisible() { return null == foregroundShadowVisible ? _foregroundShadowVisible : foregroundShadowVisible.get(); } public final void setForegroundShadowVisible(final boolean FOREGROUND_SHADOW_VISIBLE) { if (null == foregroundShadowVisible) { _foregroundShadowVisible = FOREGROUND_SHADOW_VISIBLE; } else { foregroundShadowVisible.set(FOREGROUND_SHADOW_VISIBLE); } } public final BooleanProperty foregroundShadowVisibleProperty() { if (null == foregroundShadowVisible) { foregroundShadowVisible = new SimpleBooleanProperty(this, "foregroundShadowVisible", _foregroundShadowVisible); } return foregroundShadowVisible; } public final String getTitleFont() { return null == titleFont ? _titleFont : titleFont.get(); } public final void setTitleFont(final String TITLE_FONT) { if (null == titleFont) { _titleFont = TITLE_FONT; } else { titleFont.set(TITLE_FONT); } } public final StringProperty titleFontProperty() { if (null == titleFont) { titleFont = new SimpleStringProperty(this, "titleFont", _titleFont); } return titleFont; } public final LcdFont getTimeFont() { return null == timeFont ? _timeFont : timeFont.get(); } public final void setTimeFont(final LcdFont TIME_FONT) { if (null == timeFont) { _timeFont = TIME_FONT; } else { timeFont.set(TIME_FONT); } } public final ObjectProperty<LcdFont> timeFontProperty() { if (null == timeFont) { timeFont = new SimpleObjectProperty<>(this, "timeFont", _timeFont); } return timeFont; } public final String getSmallFont() { return null == smallFont ? _smallFont : smallFont.get(); } public final void setSmallFont(final String SMALL_FONT) { if (null == smallFont) { _smallFont = SMALL_FONT; } else { smallFont.set(SMALL_FONT); } } public final StringProperty smallFontProperty() { if (null == smallFont) { smallFont = new SimpleStringProperty(this, "smallFont", _smallFont); } return smallFont; } public final Locale getLocale() { return null == locale ? Locale.US : locale.get(); } public final void setLocale(final Locale LOCALE) { localeProperty().set(LOCALE); } public final ObjectProperty<Locale> localeProperty() { if (null == locale) { locale = new SimpleObjectProperty<>(this, "locale", Locale.US); } return locale; } public final DateFormat getDateFormat() { return null == dateFormat ? DateFormat.YEAR_MONTH_DAY : dateFormat.get(); } public final void setDateFormat(final DateFormat DATE_FORMAT) { dateFormatProperty().set(DATE_FORMAT); } public final ObjectProperty<DateFormat> dateFormatProperty() { if (null == dateFormat) { dateFormat = new SimpleObjectProperty<>(this, "dateFormat", DateFormat.YEAR_MONTH_DAY); } return dateFormat; } public final DateSeparator getDateSeparator() { return null == dateSeparator ? DateSeparator.SLASH : dateSeparator.get(); } public final void setDateSeparator(final DateSeparator DATE_SEPARATOR) { dateSeparatorProperty().set(DATE_SEPARATOR); } public final ObjectProperty<DateSeparator> dateSeparatorProperty() { if (null == dateSeparator) { dateSeparator = new SimpleObjectProperty<>(this, "dateSeparator", DateSeparator.SLASH); } return dateSeparator; } public final ObservableList<Alarm> getAlarms() { return alarms; } public final void setAlarms(final List<Alarm> ALARMS) { alarms.setAll(ALARMS); } public final void setAlarms(final Alarm... ALARMS) { setAlarms(Arrays.asList(ALARMS)); } public final void addAlarm(final Alarm ALARM) { if (!alarms.contains(ALARM)) alarms.add(ALARM); } public final void removeAlarm(final Alarm ALARM) { if (alarms.contains(ALARM)) alarms.remove(ALARM); } // ******************** Utility Methods *********************************** public static double clamp(final double MIN, final double MAX, final double VALUE) { if (VALUE < MIN) return MIN; if (VALUE > MAX) return MAX; return VALUE; } // ******************** Style related ************************************* @Override protected Skin createDefaultSkin() { return new LcdClockSkin(this); } @Override protected String getUserAgentStylesheet() { return getClass().getResource(getClass().getSimpleName().toLowerCase() + ".css").toExternalForm(); } }