1 package org.lsst.ccs.plugin.jas3.trending.timeselection;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.Window;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.text.ParseException;
9 import java.util.Calendar;
10 import java.util.Date;
11 import java.util.regex.Matcher;
12 import java.util.regex.Pattern;
13 import javax.swing.BorderFactory;
14 import javax.swing.Box;
15 import javax.swing.BoxLayout;
16 import javax.swing.ButtonGroup;
17 import javax.swing.JButton;
18 import javax.swing.JCheckBox;
19 import javax.swing.JComponent;
20 import javax.swing.JLabel;
21 import javax.swing.JOptionPane;
22 import javax.swing.JPanel;
23 import javax.swing.JRadioButton;
24 import javax.swing.JSpinner;
25 import javax.swing.JTextField;
26 import javax.swing.SpinnerDateModel;
27 import javax.swing.SpinnerNumberModel;
28 import javax.swing.SwingUtilities;
29 import javax.swing.event.CaretEvent;
30 import javax.swing.event.CaretListener;
31 import javax.swing.event.ChangeEvent;
32 import javax.swing.event.ChangeListener;
33
34
35
36
37
38
39 public class TimeSelectionPanel extends JPanel {
40
41
42
43 final Pattern _nowPattern = Pattern.compile("\\s*now\\s*-?\\s*(\\d*)\\s*");
44 final int MAX_YEARS = 50;
45 final int SECONDS_IN_YEAR = 365 * 24 * 60 * 60;
46 final int SECONDS_IN_DAY = 24 * 60 * 60;
47 final int SECONDS_IN_HOUR = 60 * 60;
48 final int SECONDS_IN_MINUTE = 60;
49
50 private final int VSPACE = 5;
51 private final int HSPACE = 10;
52
53 private final TimePanel fromPanel;
54 private final TimePanel toPanel;
55 private final JTextField nameField;
56 private final JCheckBox saveCheck;
57
58 static private TimeSelection range;
59 static private JButton okButton;
60
61
62
63
64 private TimeSelectionPanel() {
65
66 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
67
68 fromPanel = new TimePanel(" Starting ");
69 add(fromPanel);
70
71 add(Box.createRigidArea(new Dimension(0, 2*VSPACE)));
72
73 toPanel = new TimePanel(" Ending ");
74 add(toPanel);
75
76 add(Box.createRigidArea(new Dimension(0, 2*VSPACE)));
77
78 Box saveBox = Box.createVerticalBox();
79 saveBox.setBorder(BorderFactory.createCompoundBorder(
80 BorderFactory.createTitledBorder(" Remember As "),
81 BorderFactory.createEmptyBorder(VSPACE, HSPACE, VSPACE, HSPACE)));
82 nameField = new JTextField();
83 nameField.setColumns(30);
84 nameField.addCaretListener(new CaretListener() {
85 public void caretUpdate(CaretEvent e) {
86 saveCheck.setEnabled(!nameField.getText().trim().isEmpty());
87 }
88 });
89 saveBox.add(nameField);
90 saveBox.add(Box.createRigidArea(new Dimension(0, VSPACE)));
91 saveCheck = new JCheckBox("Remember between sessions");
92 saveBox.add(saveCheck);
93 add(saveBox);
94
95 if (range == null) {
96 fromPanel.setTime("now - "+ SECONDS_IN_DAY);
97 toPanel.setTime("now");
98 saveCheck.setEnabled(false);
99 } else {
100 fromPanel.setTime(range.lowerEdgeString);
101 toPanel.setTime(range.upperEdgeString);
102 nameField.setText(range.name);
103 saveCheck.setSelected(range.isPersistent());
104 saveCheck.setEnabled(true);
105 }
106 }
107
108
109
110
111
112
113
114
115
116
117
118 static public TimeSelection editTimeWindow(JComponent parent, TimeSelection seed) {
119 range = seed;
120 okButton = new JButton(" OK ");
121 final TimeSelectionPanel tsp = new TimeSelectionPanel();
122 okButton.addActionListener(new ActionListener() {
123 public void actionPerformed(ActionEvent e) {
124 tsp.getRange();
125 Window w = SwingUtilities.getWindowAncestor(tsp);
126 if (w != null) w.setVisible(false);
127 }
128 });
129 JButton cancelButton = new JButton("Cancel");
130 cancelButton.addActionListener(new ActionListener() {
131 public void actionPerformed(ActionEvent e) {
132 range = null;
133 Window w = SwingUtilities.getWindowAncestor(tsp);
134 if (w != null) w.setVisible(false);
135 }
136 });
137 JOptionPane.showOptionDialog(parent, tsp, "Select time range", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, new Object[] {okButton,cancelButton}, null);
138 okButton = null;
139 return range;
140 }
141
142
143
144
145 private void checkValidy() {
146 okButton.setEnabled(fromPanel.isDataValid() && toPanel.isDataValid());
147 }
148
149 private TimeSelection getRange() {
150 String name = nameField.getText().trim();
151 boolean persistent = !name.isEmpty() && saveCheck.isSelected();
152 String start = fromPanel.getTime();
153 String end = toPanel.getTime();
154 if (range == null) {
155 range = new TimeSelection(name, start, end, persistent);
156 } else {
157 range.name = name;
158 range.lowerEdgeString = start;
159 range.upperEdgeString = end;
160 range.setPersistent(persistent);
161 }
162 return range;
163 }
164
165
166
167
168 private class TimePanel extends JPanel {
169
170 final JTextField _field;
171 final ButtonGroup _group;
172 final JRadioButton _absButton;
173 final JRadioButton _relButton;
174 final JSpinner _timeSpinner;
175 final JSpinner _yearSpinner, _daySpinner, _hourSpinner, _minSpinner, _secSpinner;
176
177
178 final SpinnerDateModel _timeSpinnerModel;
179 final SpinnerNumberModel _yearSpinnerModel, _daySpinnerModel, _hourSpinnerModel, _minSpinnerModel, _secSpinnerModel;
180
181 boolean _listen;
182
183 ChangeListener _absListener = new ChangeListener() {
184 public void stateChanged(ChangeEvent e) {
185 if (_listen) {
186 _listen = false;
187 updateFromAbsolute();
188 _listen = true;
189 }
190 }
191 };
192 ChangeListener _relListener = new ChangeListener() {
193 public void stateChanged(ChangeEvent e) {
194 if (_listen) {
195 _listen = false;
196 updateFromRelative();
197 _listen = true;
198 }
199 }
200 };
201
202 TimePanel(String title) {
203 _listen = false;
204
205 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
206 setBorder(BorderFactory.createCompoundBorder(
207 BorderFactory.createTitledBorder(title),
208 BorderFactory.createEmptyBorder(VSPACE, HSPACE, VSPACE, HSPACE)));
209
210 _field = new JTextField();
211 _field.addCaretListener(new CaretListener() {
212 public void caretUpdate(CaretEvent e) {
213 if (_listen) {
214 _listen = false;
215 updateFromField();
216 _listen = true;
217 }
218 }
219
220 });
221 add(_field);
222
223 add(Box.createRigidArea(new Dimension(0, VSPACE)));
224
225 Box box = Box.createHorizontalBox();
226 add(box);
227 _group = new ButtonGroup();
228 _absButton = new JRadioButton("Fixed: ");
229 _absButton.addActionListener(new ActionListener() {
230 public void actionPerformed(ActionEvent e) {
231 if (_listen) {
232 _listen = false;
233 enableRelative(false);
234 updateFromAbsolute();
235 _field.setForeground(Color.BLACK);
236 _listen = true;
237 }
238 }
239 });
240 _group.add(_absButton);
241 box.add(_absButton);
242 box.add(Box.createRigidArea(new Dimension(HSPACE, 0)));
243 _timeSpinnerModel = new SpinnerDateModel(new Date(), null, null, Calendar.HOUR);
244 _timeSpinner = new JSpinner(_timeSpinnerModel);
245 _timeSpinner.setEditor(new JSpinner.DateEditor(_timeSpinner, " "+ TimeSelection.DATE_PATTERN +" "));
246 _timeSpinner.addChangeListener(_absListener);
247 box.add(_timeSpinner);
248 box.add(Box.createRigidArea(new Dimension(HSPACE, 0)));
249 box.add(Box.createHorizontalGlue());
250
251 add(Box.createRigidArea(new Dimension(0, VSPACE)));
252
253 box = Box.createHorizontalBox();
254 add(box);
255 _relButton = new JRadioButton();
256 _relButton.addActionListener(new ActionListener() {
257 public void actionPerformed(ActionEvent e) {
258 if (_listen) {
259 _listen = false;
260 enableRelative(true);
261 updateFromRelative();
262 _field.setForeground(Color.BLACK);
263 _listen = true;
264 }
265 }
266 });
267 _group.add(_relButton);
268 box.add(_relButton);
269 _yearSpinnerModel = new SpinnerNumberModel(0, 0, MAX_YEARS, 1);
270 _yearSpinner = new JSpinner(_yearSpinnerModel);
271 _yearSpinner.addChangeListener(_relListener);
272 box.add(_yearSpinner);
273 box.add(Box.createRigidArea(new Dimension(HSPACE/2, 0)));
274 box.add(new JLabel("years"));
275 box.add(Box.createRigidArea(new Dimension(HSPACE/2, 0)));
276 _daySpinnerModel = new SpinnerNumberModel(0, 0, 999, 1);
277 _daySpinner = new JSpinner(_daySpinnerModel);
278 _daySpinner.addChangeListener(_relListener);
279 box.add(_daySpinner);
280 box.add(Box.createRigidArea(new Dimension(HSPACE/2, 0)));
281 box.add(new JLabel("days"));
282 box.add(Box.createRigidArea(new Dimension(HSPACE/2, 0)));
283 _hourSpinnerModel = new SpinnerNumberModel(0, 0, 99, 1);
284 _hourSpinner = new JSpinner(_hourSpinnerModel);
285 _hourSpinner.addChangeListener(_relListener);
286 box.add(_hourSpinner);
287 box.add(Box.createRigidArea(new Dimension(HSPACE/2, 0)));
288 box.add(new JLabel("hours"));
289 box.add(Box.createRigidArea(new Dimension(HSPACE/2, 0)));
290 _minSpinnerModel = new SpinnerNumberModel(0, 0, 99, 1);
291 _minSpinner = new JSpinner(_minSpinnerModel);
292 _minSpinner.addChangeListener(_relListener);
293 box.add(_minSpinner);
294 box.add(Box.createRigidArea(new Dimension(HSPACE/2, 0)));
295 box.add(new JLabel("minutes"));
296 box.add(Box.createRigidArea(new Dimension(HSPACE/2, 0)));
297 _secSpinnerModel = new SpinnerNumberModel(0, 0, 9999, 1);
298 _secSpinner = new JSpinner(_secSpinnerModel);
299 _secSpinner.addChangeListener(_relListener);
300 box.add(_secSpinner);
301 box.add(Box.createRigidArea(new Dimension(HSPACE/2, 0)));
302 box.add(new JLabel("seconds in the past"));
303 box.add(Box.createRigidArea(new Dimension(HSPACE, 0)));
304 box.add(Box.createHorizontalGlue());
305
306 add(Box.createRigidArea(new Dimension(0, VSPACE)));
307
308 _listen = true;
309 }
310
311 boolean isDataValid() {
312 return _field.getForeground() == Color.BLACK;
313 }
314
315 String getTime() {
316 if (_absButton.isSelected()) {
317 updateFromAbsolute();
318 } else {
319 updateFromRelative();
320 }
321 return _field.getText().trim();
322 }
323
324 void setTime(String time) {
325 _field.setText(time);
326 updateFromField();
327 }
328
329 private void setValid(boolean isValid) {
330 if (isValid) {
331 _field.setForeground(Color.BLACK);
332 } else {
333 _field.setForeground(Color.RED);
334 }
335 TimeSelectionPanel.this.checkValidy();
336 }
337
338 private void updateFromAbsolute() {
339 _field.setText(TimeSelection.DATE_FORMAT.format(_timeSpinnerModel.getDate()));
340 }
341
342 private void updateFromRelative() {
343 int seconds = SECONDS_IN_YEAR * _yearSpinnerModel.getNumber().intValue() +
344 SECONDS_IN_DAY * _daySpinnerModel.getNumber().intValue() +
345 SECONDS_IN_HOUR * _hourSpinnerModel.getNumber().intValue() +
346 SECONDS_IN_MINUTE * _minSpinnerModel.getNumber().intValue() +
347 _secSpinnerModel.getNumber().intValue();
348 _field.setText("now"+ (seconds > 0 ? (" - "+ seconds) : ""));
349 }
350
351 private void updateFromField() {
352 String data = _field.getText();
353 Matcher m = _nowPattern.matcher(data);
354 long seconds = -1L;
355 if (m.matches()) {
356 try {
357 String ddd = m.group(1);
358 seconds = ddd.isEmpty() ? 0 : Long.parseLong(ddd);
359 if (seconds / SECONDS_IN_YEAR > MAX_YEARS) {
360 seconds = -1;
361 }
362 } catch (NumberFormatException x) {
363 }
364 if (seconds < 0L) {
365 setValid(false);
366 } else {
367 setValid(true);
368 _yearSpinnerModel.setValue(seconds / SECONDS_IN_YEAR);
369 seconds %= SECONDS_IN_YEAR;
370 _daySpinnerModel.setValue(seconds / SECONDS_IN_DAY);
371 seconds %= SECONDS_IN_DAY;
372 _hourSpinnerModel.setValue(seconds / SECONDS_IN_HOUR);
373 seconds %= SECONDS_IN_HOUR;
374 _minSpinnerModel.setValue(seconds / SECONDS_IN_MINUTE);
375 seconds %= SECONDS_IN_MINUTE;
376 _secSpinnerModel.setValue(seconds);
377 enableRelative(true);
378 }
379 } else {
380 try {
381 Date date = TimeSelection.DATE_FORMAT.parse(data);
382 _timeSpinnerModel.setValue(date);
383 enableRelative(false);
384 setValid(true);
385 } catch (ParseException x) {
386 setValid(false);
387 }
388 }
389 }
390
391 private void enableRelative(boolean relative) {
392 _timeSpinner.setEnabled(!relative);
393 _yearSpinner.setEnabled(relative);
394 _daySpinner.setEnabled(relative);
395 _hourSpinner.setEnabled(relative);
396 _minSpinner.setEnabled(relative);
397 _secSpinner.setEnabled(relative);
398 if (relative) {
399 _relButton.setSelected(true);
400 } else {
401 _absButton.setSelected(true);
402 }
403 }
404
405 }
406
407 }