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