001package jmri.jmrit.pragotronclock;
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 Pragotron clock.
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 Petr Sidlo Copyright (C) 2019
025 *
026 * Based on Nixie clock by Bob Jacobsen.
027 */
028public class PragotronClockFrame extends JmriJFrame implements java.beans.PropertyChangeListener {
029
030    // GUI member declarations
031    JLabel h24;  // hours
032    JLabel m1;  // msb of minutes
033    JLabel m2;
034    JLabel colon;
035
036    double aspect;
037    double iconAspect10;
038    double iconAspectDot;
039    double iconAspect24;
040    int runPauseButtonWidth;
041
042    Timebase clock;
043
044    JButton runPauseButton;
045
046    NamedIcon[] foldingSheets10 = new NamedIcon[10];
047    NamedIcon[] baseFoldingSheets10 = new NamedIcon[10];
048    NamedIcon[] foldingSheets24 = new NamedIcon[24];
049    NamedIcon[] baseFoldingSheets24 = new NamedIcon[24];
050    NamedIcon colonIcon;
051    NamedIcon baseColon;
052    //"base" variables used to hold original gifs, other variables used with scaled images
053
054    public PragotronClockFrame() {
055        super(Bundle.getMessage("MenuItemPragotronClock"));
056
057        this.getContentPane().setBackground(new Color(0x3D3D3D));    // set background to black
058
059        clock = InstanceManager.getDefault(jmri.Timebase.class);
060
061        //Load the images (these are now the larger version of the original gifs
062        for (int i = 0; i < 10; i++) {
063            baseFoldingSheets10[i] = new NamedIcon("resources/icons/misc/Pragotron/M" + i + ".png", "resources/icons/misc/Pragotron/M" + i + ".png");
064            foldingSheets10[i] = new NamedIcon("resources/icons/misc/Pragotron/M" + i + ".png", "resources/icons/misc/Pragotron/M" + i + ".png");
065        }
066        for (int i = 0; i < 24; i++) {
067            baseFoldingSheets24[i] = new NamedIcon("resources/icons/misc/Pragotron/H" + i + ".png", "resources/icons/misc/Pragotron/H" + i + ".png");
068            foldingSheets24[i] = new NamedIcon("resources/icons/misc/Pragotron/H" + i + ".png", "resources/icons/misc/Pragotron/H" + i + ".png");
069        }
070        colonIcon = new NamedIcon("resources/icons/misc/Pragotron/dot.png", "resources/icons/misc/Pragotron/dot.png");
071        baseColon = new NamedIcon("resources/icons/misc/Pragotron/dot.png", "resources/icons/misc/Pragotron/dot.png");
072        // set initial size the same as the original gifs
073        for (int i = 0; i < 10; i++) {
074            Image scaledImage = baseFoldingSheets10[i].getImage().getScaledInstance(32, 48, Image.SCALE_SMOOTH);  // 152 / 192
075            foldingSheets10[i].setImage(scaledImage);
076        }
077        for (int i = 0; i < 24; i++) {
078            Image scaledImage = baseFoldingSheets24[i].getImage().getScaledInstance(80, 48, Image.SCALE_SMOOTH);  // 320 / 192
079            foldingSheets24[i].setImage(scaledImage);
080        }
081        Image scaledImage = baseColon.getImage().getScaledInstance(10, 48, Image.SCALE_SMOOTH);  // 40 / 192
082        colonIcon.setImage(scaledImage);
083
084        // create the run/pause button and get it's size
085        runPauseButton = new JButton(Bundle.getMessage("ButtonPauseClock"));
086        runPauseButton.setText( Bundle.getMessage( "ButtonPauseClock") );
087        runPauseButtonWidth = runPauseButton.getPreferredSize().width;
088
089        // determine aspect ratio of a single digit graphic
090        iconAspect10 = 152.0 / 192.0;       // 152 : 192
091        iconAspect24 = 320.0 / 192.0;       // 320 : 192
092        iconAspectDot = 40.0 / 192.0;       // 40 : 192
093
094        // determine the aspect ratio of the 1 hour digit, dot and 2 minutes digit
095        if (!clock.getShowStopButton()) {
096            aspect = (320.0 + 40.0 + 2 * 152.0) / 192.0; // pick up clock prefs choice: no button
097        } else {
098            aspect = (320.0 + 40.0 + 2 * 152.0 + runPauseButtonWidth) / 192.0; // pick up clock prefs choice: add width of a stop/start button
099        }
100
101        // listen for changes to the Timebase parameters
102        clock.addPropertyChangeListener(this);
103
104        // init GUI
105        m1 = new JLabel(foldingSheets10[0]);
106        m2 = new JLabel(foldingSheets10[0]);
107        h24 = new JLabel(foldingSheets24[0]);
108        colon = new JLabel(colonIcon);
109
110        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.X_AXIS));
111        getContentPane().add(h24);
112        getContentPane().add(colon);
113        getContentPane().add(m1);
114        getContentPane().add(m2);
115
116        getContentPane().add(runPauseButton);
117        runPauseButton.addActionListener(new ButtonListener());
118        // since Run/Stop button looks crummy, user may turn it on in clock prefs
119        runPauseButton.setVisible(clock.getShowStopButton()); // pick up clock prefs choice
120        updateButtonText();
121        update();
122        pack();
123
124        // request callback to update time
125        clock.addMinuteChangeListener((java.beans.PropertyChangeEvent e) -> {
126            update();
127        });
128
129        // Add component listener to handle frame resizing event
130        this.addComponentListener(
131                new ComponentAdapter() {
132                    @Override
133                    public void componentResized(ComponentEvent e) {
134                        scaleImage();
135                    }
136                });
137
138    }
139
140    // Added method to scale the clock digit images to fit the
141    // size of the display window
142    public void scaleImage() {
143        int iconHeight10;
144        int iconWidth10;
145        int iconHeight24;
146        int iconWidth24;
147        int iconHeightDot;
148        int iconWidthDot;
149        int frameHeight = this.getContentPane().getSize().height;
150        int frameWidth = this.getContentPane().getSize().width;
151        if ((double) frameWidth / (double) frameHeight > aspect) {
152            iconHeight10 = frameHeight;
153            iconWidth10 = (int) (iconAspect10 * iconHeight10);
154            iconHeight24 = frameHeight;
155            iconWidth24 = (int) (iconAspect24 * iconHeight24);
156            iconHeightDot = frameHeight;
157            iconWidthDot = (int) (iconAspectDot * iconHeightDot);
158        } else {
159            // allow space in width for run stop button
160            int workingWidth = frameWidth;
161            if (clock.getShowStopButton()) workingWidth = frameWidth - runPauseButtonWidth;
162            iconWidth10 = (int) (workingWidth / 664.0 * 152.0);
163            iconHeight10 = (int) (iconWidth10 / iconAspect10);
164            iconWidth24 = (int) (workingWidth / 664.0 * 320.0);
165            iconHeight24 = (int) (iconWidth24 / iconAspect24);
166            iconWidthDot = (int) (workingWidth / 664.0 * 40.0);
167            iconHeightDot = (int) (iconWidthDot / iconAspectDot);
168        }
169        for (int i = 0; i < 10; i++) {
170            Image scaledImage = baseFoldingSheets10[i].getImage().getScaledInstance(Math.max(1,iconWidth10), Math.max(1,iconHeight10), Image.SCALE_SMOOTH);
171            foldingSheets10[i].setImage(scaledImage);
172        }
173        for (int i = 0; i < 24; i++) {
174            Image scaledImage = baseFoldingSheets24[i].getImage().getScaledInstance(Math.max(1,iconWidth24), Math.max(1,iconHeight24), Image.SCALE_SMOOTH);
175            foldingSheets24[i].setImage(scaledImage);
176        }
177        Image scaledImage = baseColon.getImage().getScaledInstance(Math.max(1,iconWidthDot) , Math.max(1,iconHeightDot), Image.SCALE_SMOOTH);
178        colonIcon.setImage(scaledImage);
179
180        // update the images on screen
181        this.getContentPane().revalidate();
182    }
183
184    @SuppressWarnings("deprecation") // Date.getHours, getMinutes, getSeconds
185    void update() {
186        Date now = clock.getTime();
187        int hours = now.getHours();
188        int minutes = now.getMinutes();
189
190        h24.setIcon(foldingSheets24[hours]);
191        m1.setIcon(foldingSheets10[minutes / 10]);
192        m2.setIcon(foldingSheets10[minutes - (minutes / 10) * 10]);
193    }
194
195    /**
196     * Handle a change to clock properties.
197     * @param e unused.
198     */
199    @Override
200    public void propertyChange(java.beans.PropertyChangeEvent e) {
201        updateButtonText();
202    }
203
204    /**
205     * Update clock button text.
206     */
207    private void updateButtonText(){
208        runPauseButton.setText( Bundle.getMessage( clock.getRun() ? "ButtonPauseClock" : "ButtonRunClock") );
209    }
210
211    private class ButtonListener implements ActionListener {
212        @Override
213        public void actionPerformed(ActionEvent a) {
214            clock.setRun(!clock.getRun());
215            updateButtonText();
216        }
217    }
218
219}