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