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