001package jmri.jmrit.lcdclock;
002
003import java.awt.Color;
004import java.awt.Image;
005import java.awt.event.ActionEvent;
006import java.awt.event.ActionListener;
007import java.awt.event.ComponentAdapter;
008import java.awt.event.ComponentEvent;
009import java.beans.PropertyChangeEvent;
010import java.beans.PropertyChangeListener;
011import java.util.Date;
012
013import javax.swing.BoxLayout;
014import javax.swing.JButton;
015import javax.swing.JLabel;
016
017import jmri.InstanceManager;
018import jmri.Timebase;
019import jmri.jmrit.catalog.NamedIcon;
020import jmri.util.JmriJFrame;
021
022/**
023 * Frame providing a simple clock showing Lcd tubes.
024 * <p>
025 * A Run/Stop button is built into this, but because I don't like the way it
026 * looks, it's not currently displayed in the GUI.
027 *
028 * @author Ken Cameron Copyright (C) 2007
029 *
030 * This was a very direct steal from the Nixie clock code, ver 1.12. Thank you
031 * Bob Jacobson.
032 *
033 */
034public class LcdClockFrame extends JmriJFrame implements PropertyChangeListener {
035
036    // GUI member declarations
037    JLabel h1;  // msb of hours
038    JLabel h2;
039    JLabel m1;  // msb of minutes
040    JLabel m2;
041    JLabel colon;
042
043    double aspect;
044    double iconAspect;
045    int runPauseButtonWidth;
046
047    JButton runPauseButton;
048
049    Timebase clock;
050    private final PropertyChangeListener minuteListener = (PropertyChangeEvent evt) -> update();
051
052    NamedIcon tubes[] = new NamedIcon[10];
053    NamedIcon baseTubes[] = new NamedIcon[10];
054    NamedIcon colonIcon;
055    NamedIcon baseColon;
056    //"base" variables used to hold original gifs, other variables used with scaled images
057
058    public LcdClockFrame() {
059        super(Bundle.getMessage("MenuItemLcdClock"));
060
061        clock = InstanceManager.getDefault(jmri.Timebase.class);
062
063        //Load the images (these are now the larger version of the original gifs
064        for (int i = 0; i < 10; i++) {
065            baseTubes[i] = new NamedIcon("resources/icons/misc/LCD/Lcd_" + i + "b.GIF", "resources/icons/misc/LCD/Lcd_" + i + "b.GIF");
066            tubes[i] = new NamedIcon("resources/icons/misc/LCD/Lcd_" + i + "b.GIF", "resources/icons/misc/LCD/Lcd_" + i + "b.GIF");
067        }
068        colonIcon = new NamedIcon("resources/icons/misc/LCD/Lcd_Colonb.GIF", "resources/icons/misc/LCD/Lcd_Colonb.GIF");
069        baseColon = new NamedIcon("resources/icons/misc/LCD/Lcd_Colonb.GIF", "resources/icons/misc/LCD/Lcd_Colonb.GIF");
070        // set initial size the same as the original gifs
071        for (int i = 0; i < 10; i++) {
072            Image scaledImage = baseTubes[i].getImage().getScaledInstance(23, 32, Image.SCALE_SMOOTH);
073            tubes[i].setImage(scaledImage);
074        }
075        Image scaledImage = baseColon.getImage().getScaledInstance(12, 32, Image.SCALE_SMOOTH);
076        colonIcon.setImage(scaledImage);
077
078        // create the run/pause button and get it's size
079        runPauseButton = new JButton(Bundle.getMessage("ButtonPauseClock"));
080        runPauseButton.setText( Bundle.getMessage( "ButtonPauseClock") );
081        runPauseButtonWidth = runPauseButton.getPreferredSize().width;
082        
083        // determine aspect ratio of a single digit graphic
084        iconAspect = 24. / 32.;
085
086        // determine the aspect ratio of the 4 digit base graphic plus a half digit for the colon
087        if (!clock.getShowStopButton()) {
088            aspect = (4.5 * 24.) / 32.; // pick up clock prefs choice: no button
089        } else {
090            aspect = (4.5 * 24. + runPauseButtonWidth) / 32.; // pick up clock prefs choice: add size of a stop/start button
091        }
092
093        // listen for changes to the Timebase parameters
094        clock.addPropertyChangeListener(this);
095
096        // init GUI
097        m1 = new JLabel(tubes[0]);
098        m2 = new JLabel(tubes[0]);
099        h1 = new JLabel(tubes[0]);
100        h2 = new JLabel(tubes[0]);
101        colon = new JLabel(colonIcon);
102
103        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.X_AXIS));
104        getContentPane().add(h1);
105        getContentPane().add(h2);
106        getContentPane().add(colon);
107        getContentPane().add(m1);
108        getContentPane().add(m2);
109        this.getContentPane().setBackground(new Color(0xFFFFFF)); // set background to white to match lcd
110        getContentPane().add(runPauseButton);
111        runPauseButton.addActionListener(new ButtonListener());
112        // since Run/Stop button looks crummy, user may turn it on in clock prefs
113        runPauseButton.setVisible(clock.getShowStopButton()); // pick up clock prefs choice
114        updateButtonText();
115        update();
116        pack();
117
118        // request callback to update time
119        clock.addMinuteChangeListener(minuteListener);
120
121        // Add component listener to handle frame resizing event
122        this.addComponentListener(
123                new ComponentAdapter() {
124                    @Override
125                    public void componentResized(ComponentEvent e) {
126                        scaleImage();
127                    }
128                });
129
130    }
131
132    // Added method to scale the clock digit images to fit the
133    // size of the display window
134    public void scaleImage() {
135        int iconHeight;
136        int iconWidth;
137        int frameHeight = this.getContentPane().getSize().height;
138        int frameWidth = this.getContentPane().getSize().width;
139        if ((double) frameWidth / (double) frameHeight > aspect) {
140            iconHeight = frameHeight;
141            iconWidth = (int) (iconAspect * iconHeight);
142        } else {
143            // allow space in width for run stop button
144            int workingWidth = frameWidth;
145            if (clock.getShowStopButton()) workingWidth = frameWidth - runPauseButtonWidth;
146            iconWidth = (int) (workingWidth / 4.5);
147            iconHeight = (int) (iconWidth / iconAspect);
148        }
149        for (int i = 0; i < 10; i++) {
150            Image scaledImage = baseTubes[i].getImage().getScaledInstance(Math.max(1,iconWidth), Math.max(1,iconHeight), Image.SCALE_SMOOTH);
151            tubes[i].setImage(scaledImage);
152        }
153        Image scaledImage = baseColon.getImage().getScaledInstance(Math.max(1,iconWidth / 2), Math.max(1,iconHeight), Image.SCALE_SMOOTH);
154        colonIcon.setImage(scaledImage);
155        // update the images on screen
156        this.getContentPane().revalidate();
157    }
158
159    @SuppressWarnings("deprecation") // Date.getHours, getMinutes, getSeconds
160    void update() {
161        Date now = clock.getTime();
162        int hours = now.getHours();
163        int minutes = now.getMinutes();
164
165        h1.setIcon(tubes[hours / 10]);
166        h2.setIcon(tubes[hours - (hours / 10) * 10]);
167        m1.setIcon(tubes[minutes / 10]);
168        m2.setIcon(tubes[minutes - (minutes / 10) * 10]);
169    }
170
171    /**
172     * Handle a change to clock properties.
173     * @param e unused.
174     */
175    @Override
176    public void propertyChange( PropertyChangeEvent e) {
177        updateButtonText();
178    }
179
180    /**
181     * Update clock button text.
182     */
183    private void updateButtonText(){
184        runPauseButton.setText( Bundle.getMessage( clock.getRun() ? "ButtonPauseClock" : "ButtonRunClock") );
185    }
186
187    private class ButtonListener implements ActionListener {
188        @Override
189        public void actionPerformed(ActionEvent a) {
190            clock.setRun(!clock.getRun());
191            updateButtonText();
192        }
193    }
194
195    @Override
196    public void dispose() {
197        clock.removeMinuteChangeListener(minuteListener);
198        clock.removePropertyChangeListener(this);
199        super.dispose();
200    }
201
202}