001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.chooser;
030
031import ucar.unidata.ui.ChooserList;
032import ucar.unidata.ui.ChooserPanel;
033import ucar.unidata.util.GuiUtils;
034import ucar.unidata.util.Misc;
035
036import javax.swing.JLabel;
037import javax.swing.JPanel;
038import javax.swing.JRadioButton;
039import javax.swing.event.ChangeEvent;
040import javax.swing.event.ChangeListener;
041import javax.swing.event.ListSelectionEvent;
042import javax.swing.event.ListSelectionListener;
043
044import java.awt.Component;
045import java.awt.event.ActionEvent;
046import java.util.Vector;
047
048/**
049 *
050 * @author Unidata IDV Development Team
051 * @version $Revision$
052 */
053public abstract class FrameChooser extends ChooserPanel {
054
055    /** Property for new data selection */
056    public static String NEW_SELECTION = "FrameChooser.NEW_SELECTION";
057
058    /** Have connected */
059    protected static final int STATE_CONNECTED = 2;
060
061    /** flag for ignoring combobox changes */
062    protected boolean ignoreStateChangedEvents = false;
063
064    /**
065     * Public keys for frame numbers, request, and data name.
066     */
067    public final static String FRAME_NUMBERS_KEY = "frame numbers";
068    public final static String DATA_NAME_KEY = "data name";
069    public final static String REQUEST_HOST = "host";
070    public final static String REQUEST_PORT = "port";
071    public final static String REQUEST_KEY = "key";
072
073    /** Used to synchronize access to widgets (eg: disabling, setting state, etc). */
074    protected Object WIDGET_MUTEX = new Object();
075
076    /** frames list */
077    private ChooserList framesList;
078
079    /** Keep track of when are are doing a frame loop */
080    private boolean doLoop = false;
081
082    /** Frame loop radio button */
083    private JRadioButton loopRB;
084
085    /** Refresh current frame radio button */
086    private JRadioButton curRB;
087
088    /**
089     * Create me.
090     */
091    public FrameChooser() {}
092
093    /**
094     * Handle when the user presses the update button
095     *
096     * @throws Exception _more_
097     */
098    public void handleUpdate() throws Exception {}
099
100    /**
101     * Handle when the user presses the update button
102     */
103    public void handleUpdateFromThread() {
104        showWaitCursor();
105        try {
106            handleUpdate();
107        } catch (Exception exc) {
108        }
109        showNormalCursor();
110    }
111
112    /**
113     * Update the selector. Call handleUpdate in a thread
114     */
115    public final void doUpdate() {
116        Misc.run(this, "handleUpdateFromThread");
117    }
118
119    /**
120     * Handle the event
121     *
122     * @param ae The event
123     */
124    public void actionPerformed(ActionEvent ae) {
125        String cmd = ae.getActionCommand();
126        super.actionPerformed(ae);
127    }
128
129    /**
130     * Disable/enable any components that depend on the server.
131     * Try to update the status labelwith what we know here.
132     */
133    protected void updateStatus() {
134       setHaveData(getGoodToGo());
135    }
136
137    /**
138     * Are there any times in the times list.
139     *
140     * @return Do we have any times at all.
141     */
142    protected boolean haveAnyTimes() {
143        return framesList.getModel().getSize() > 0;
144    }
145
146    /**
147     * Are there more than one times in the times list.
148     *
149     * @return Do we have a series.
150     */
151    protected boolean haveASeries() {
152        return !getTimesList().getSelectedValuesList().isEmpty();
153    }
154
155    /**
156     * Create (if needed) and return the list that shows frames.
157     *
158     * @return The frames list.
159     */
160    public ChooserList getTimesList() {
161        if (framesList == null) {
162            framesList = new ChooserList();
163            framesList.setVisibleRowCount(getTimesListSize());
164            framesList.addListSelectionListener(new ListSelectionListener() {
165                public void valueChanged(ListSelectionEvent e) {
166                    updateStatus();
167                }
168            });
169        }
170        return framesList;
171    }
172
173    /**
174     * Get the size of the times list
175     *
176     * @return the times list size
177     */
178    protected int getTimesListSize() {
179        return 6;
180    }
181
182    /**
183     * Clear all times in the times list.
184     */
185    protected void clearFramesList() {
186        getTimesList().setListData(new Vector());
187    }
188
189    /**
190     *  Do what needs to be done to read in the times.  Subclasses
191     *  need to implement this.
192     */
193    protected abstract void readFrames();
194
195    /**
196     * Are we all set to load data.
197     *
198     * @return All set to load.
199     */
200    protected boolean getGoodToGo() {
201        if ( !haveFrameSelected()) {
202            return false;
203        }
204        return true;
205    }
206
207    /**
208     * Create the current frame / frame loop selector
209     *
210     * @return  the image list panel
211     */
212    protected JPanel makeFramesPanel() {
213
214        getTimesList().addListSelectionListener(new ListSelectionListener() {
215            public void valueChanged(ListSelectionEvent e) {
216                if ( !getDoFrameLoop()) {
217                    return;
218                }
219            }
220        });
221
222        ChangeListener listener = new ChangeListener() {
223            public void stateChanged(ChangeEvent ae) {
224                if (loopRB.isSelected() == getDoFrameLoop()) {
225                    return;
226                }
227                doLoop = loopRB.isSelected();
228                if (doLoop && !haveAnyTimes()) {
229                    readFrames();
230                } else {
231                    updateStatus();
232                }
233                enableWidgets();
234            }
235        };
236
237        loopRB = new JRadioButton("Select frames", getDoFrameLoop());
238        loopRB.addChangeListener(listener);
239        curRB = new JRadioButton("Refresh current frame", !getDoFrameLoop());
240        curRB.addChangeListener(listener);
241        GuiUtils.buttonGroup(loopRB, curRB);
242        JPanel panel = GuiUtils.doLayout(new Component[] {
243            curRB, loopRB, 
244            new JLabel(" "),getTimesList().getScroller() 
245        }, 2, GuiUtils.WT_N, GuiUtils.WT_NY);
246        return GuiUtils.wrap(panel);
247    }
248
249    /**
250     * Are there any frames selected.
251     *
252     * @return Any frames selected.
253     */
254    protected boolean haveFrameSelected() {
255        return !getDoFrameLoop() || getTimesList().haveDataSelected();
256    }
257
258    /**
259     * Do we do a frame loop or refresh current frame
260     *
261     * @return Do we do frame loop
262     */
263    protected boolean getDoFrameLoop() {
264        return doLoop;
265    }
266
267    /**
268     * Set whether we do a frame loop or refresh current frame
269     *
270     * @param yesorno true to do frame loop
271     */
272    protected void setDoFrameLoop(boolean yesorno) {
273        doLoop = yesorno;
274        // Should this be in 
275        if (curRB != null) {
276            curRB.setSelected(yesorno);
277        }
278    }
279
280    /**
281     * Did the user select current frame?
282     *
283     * @return Should we load current frame
284     */
285    protected boolean getDoCurrentFrame() {
286        return !getDoFrameLoop();
287    }
288
289    /**
290     * Enable or disable the GUI widgets based on what has been
291     * selected.
292     */
293    protected void enableWidgets() {
294        getTimesList().setEnabled(getDoFrameLoop());
295    }
296}