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}