1
package org.helioviewer.jhv.gui.components;
3
import java.awt.BorderLayout;
5
import java.awt.Component;
6
import java.awt.Dimension;
7
import java.awt.EventQueue;
8
import java.awt.FlowLayout;
10
import java.awt.event.ActionEvent;
11
import java.text.ParseException;
12
import java.util.Date;
14
import javax.swing.AbstractAction;
15
import javax.swing.Action;
16
import javax.swing.BorderFactory;
17
import javax.swing.JButton;
18
import javax.swing.JLabel;
19
import javax.swing.JPanel;
20
import javax.swing.JScrollPane;
22
import org.helioviewer.base.logging.Log;
23
import org.helioviewer.jhv.Settings;
24
import org.helioviewer.jhv.gui.IconBank;
25
import org.helioviewer.jhv.gui.IconBank.JHVIcon;
26
import org.helioviewer.jhv.gui.ImageViewerGui;
27
import org.helioviewer.jhv.gui.components.layerTable.LayerTable;
28
import org.helioviewer.jhv.gui.components.layerTable.LayerTableContainer;
29
import org.helioviewer.jhv.gui.dialogs.ObservationDialog;
30
import org.helioviewer.jhv.layers.LayersListener;
31
import org.helioviewer.jhv.layers.LayersModel;
32
import org.helioviewer.viewmodel.view.MovieView;
33
import org.helioviewer.viewmodel.view.TimedMovieView;
34
import org.helioviewer.viewmodel.view.View;
35
import org.helioviewer.viewmodel.view.jp2view.datetime.ImmutableDateTime;
38
* Panel for displaying all layers including the layer specific controls.
40
* This panel plays are very central role: It manages all visible layers,
42
* Except the LayersListener this is a GUI class expecting to be run in the
46
* TODO: Low-Importance - Fix the issue that everything runs in the EventQueue
48
* At the moment, a lot of GUI calls are not fired on the EventDispatchThread
49
* According to the Java specifications, this IS NOT ALLOWED and could cause
52
* In order to fix this use SwingUtilities.invokeLater or
53
* Swingutilities.invokeAndWait to make GUI interactions
55
* Ludwig already tried to fix all of this "with one line of code" by adding
56
* invokeLater to the ViewListenerDistributor However, this calls TOO MUCH stuff
57
* on the event queue - and the GUI becomes unresponsive
59
* @author Markus Langenberg
62
public class ImageSelectorPanel extends JPanel implements LayersListener {
64
private static final long serialVersionUID = 1L;
66
* Action to add a new layer. If there is a current active layer which much
67
* different time, the dates will be updated.
69
private Action addLayerAction = new AbstractAction("Add Layer", IconBank.getIcon(JHVIcon.ADD)) {
73
private static final long serialVersionUID = 1L;
75
putValue(SHORT_DESCRIPTION, "Add a new Layer");
81
public void actionPerformed(ActionEvent arg0) {
82
// Check the dates if possible
83
final View activeView = LayersModel.getSingletonInstance().getActiveView();
86
* TODO: Code Simplification - Cleanup Date selection when clicking
87
* on "add images", e.g. use LayersModel.getLatestDate(...), ...
89
* Here are some more comments by Helge:
91
* If it is a local file, the timestamps are read from the parsed
92
* JPX movie, i.e. a call will pause until the whole movie has
95
* If it has been reading through the API the frame time stamps
96
* already have been returned and it is not bad. For the time being
97
* it will only update if its already loaded.
99
* I think there should be a better solution? Maybe a wait dialog?
103
if (activeView != null) {
104
MovieView tmv = activeView.getAdapter(TimedMovieView.class);
105
if (tmv != null && tmv.getMaximumAccessibleFrameNumber() == tmv.getMaximumFrameNumber()) {
106
final ImmutableDateTime start = LayersModel.getSingletonInstance().getStartDate(activeView);
107
final ImmutableDateTime end = LayersModel.getSingletonInstance().getEndDate(activeView);
108
if (start != null && end != null) {
110
Date startDate = start.getTime();
111
Date endDate = end.getTime();
112
Date obsStartDate = ObservationDialog.apiDateFormat.parse(observationDialog.getStartTime());
113
Date obsEndDate = ObservationDialog.apiDateFormat.parse(observationDialog.getEndTime());
114
// only updates if its really necessary with a
115
// tolerance of an hour
116
final int tolerance = 60 * 60 * 1000;
117
if (Math.abs(startDate.getTime() - obsStartDate.getTime()) > tolerance || Math.abs(endDate.getTime() - obsEndDate.getTime()) > tolerance) {
118
observationDialog.setStartDate(startDate);
119
observationDialog.setEndDate(endDate);
121
} catch (ParseException e) {
123
Log.error("Cannot update observation dialog", e);
129
observationDialog.setType(ObservationDialog.TYPE_DEFAULT);
130
observationDialog.showDialog();
134
* Button to add new layers
136
private JButton addLayerButton = new JButton(addLayerAction);
139
* Observation dialog to actually add new data
141
private ObservationDialog observationDialog = new ObservationDialog(ObservationDialog.TYPE_DEFAULT, Boolean.parseBoolean(Settings.getSingletonInstance().getProperty("startup.loadmovie")));
143
* Action to download the current layer. If there is no active layer, the
144
* action will do nothing.
146
* Should be activated accordingly in the class
148
private Action downloadLayerAction = new AbstractAction() {
152
private static final long serialVersionUID = 1L;
154
putValue(SHORT_DESCRIPTION, "Download the currently selected Layer");
155
putValue(SMALL_ICON, IconBank.getIcon(JHVIcon.DOWNLOAD));
161
public void actionPerformed(ActionEvent arg0) {
162
if (LayersModel.getSingletonInstance().getActiveView() != null) {
163
LayersModel.getSingletonInstance().downloadLayer(LayersModel.getSingletonInstance().getActiveView());
168
* Button to show {@link #downloadLayerAction}
170
private JButton downloadLayerButton = new JButton(downloadLayerAction);
172
private LayerTable layerTable;
174
* Action to move the current layer down. If there is no active layer, the
175
* action will do nothing.
177
* Should be activated accordingly in the class
179
private Action moveLayerDownAction = new AbstractAction() {
183
private static final long serialVersionUID = 1L;
185
putValue(SHORT_DESCRIPTION, "Move the currently selected Layer down");
186
putValue(SMALL_ICON, IconBank.getIcon(JHVIcon.DOWN));
192
public void actionPerformed(ActionEvent arg0) {
193
if (LayersModel.getSingletonInstance().getActiveView() != null) {
194
LayersModel.getSingletonInstance().moveLayerDown(LayersModel.getSingletonInstance().getActiveView());
199
* Button to show {@link #moveLayerDownAction}
201
private JButton moveLayerDownButton = new JButton(moveLayerDownAction);
203
* Action to move the current layer up. If there is no active layer, the
204
* action will do nothing.
206
* Should be activated accordingly in the class
208
private Action moveLayerUpAction = new AbstractAction() {
212
private static final long serialVersionUID = 1L;
214
putValue(SHORT_DESCRIPTION, "Move the currently selected Layer up");
215
putValue(SMALL_ICON, IconBank.getIcon(JHVIcon.UP));
221
public void actionPerformed(ActionEvent arg0) {
222
if (LayersModel.getSingletonInstance().getActiveView() != null) {
223
LayersModel.getSingletonInstance().moveLayerUp(LayersModel.getSingletonInstance().getActiveView());
228
* Button to show {@link #moveLayerUpAction}
230
private JButton moveLayerUpButton = new JButton(moveLayerUpAction);
232
* Action to show the meta data. If there is no active layer, the action
235
* Should be activated accordingly in the class
237
private Action showMetaAction = new AbstractAction() {
241
private static final long serialVersionUID = 1L;
243
putValue(SHORT_DESCRIPTION, "Show the Metainformation of the currently selected Layer");
244
putValue(SMALL_ICON, IconBank.getIcon(JHVIcon.INFO));
250
public void actionPerformed(ActionEvent arg0) {
251
if (LayersModel.getSingletonInstance().getActiveView() != null) {
252
LayersModel.getSingletonInstance().showMetaInfo(LayersModel.getSingletonInstance().getActiveView());
257
* Button to show {@link #showMetaAction}
259
private JButton showMetaButton = new JButton(showMetaAction);
262
* Default constructor.
264
public ImageSelectorPanel() {
265
JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
267
addLayerButton.setAlignmentX(Component.RIGHT_ALIGNMENT);
268
southPanel.add(moveLayerUpButton);
269
southPanel.add(moveLayerDownButton);
270
southPanel.add(showMetaButton);
271
southPanel.add(downloadLayerButton);
272
southPanel.add(addLayerButton);
274
layerTable = new LayerTable();
276
// Create the scroll pane and add the table to it.
277
JScrollPane scrollPane = new JScrollPane(layerTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
278
scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
280
JLabel emptyLabel = new JLabel("No Layers Added yet", JLabel.CENTER);
281
emptyLabel.setFont(emptyLabel.getFont().deriveFont(Font.ITALIC));
282
emptyLabel.setHorizontalTextPosition(JLabel.CENTER);
283
emptyLabel.setOpaque(true);
284
emptyLabel.setBackground(Color.WHITE);
286
// Create the scroll pane and add the table to it.
287
JScrollPane emptyScrollPane = new JScrollPane(emptyLabel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
288
emptyScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
290
LayerTableContainer layerTableContainer = new LayerTableContainer(scrollPane, emptyScrollPane);
292
// Add the scroll pane to this panel.
293
layerTableContainer.setPreferredSize(new Dimension(ImageViewerGui.SIDE_PANEL_WIDTH, LayerTable.ROW_HEIGHT * 4 + 2));
295
this.setLayout(new BorderLayout());
297
this.add(layerTableContainer, BorderLayout.CENTER);
298
this.add(southPanel, BorderLayout.SOUTH);
300
LayersModel.getSingletonInstance().addLayersListener(this);
305
* Checks if there is a current active layer and activates the buttons
308
* Since the events can come from different threads it takes care that this
309
* runs in the EventQueue.
311
private void activateActions() {
312
EventQueue.invokeLater(new Runnable() {
314
boolean e = LayersModel.getSingletonInstance().getActiveView() != null;
315
downloadLayerAction.setEnabled(e);
316
moveLayerDownAction.setEnabled(e);
317
moveLayerUpAction.setEnabled(e);
318
showMetaAction.setEnabled(e);
326
public void activeLayerChanged(int index) {
332
public void layerAdded(int newIndex) {
339
public void layerChanged(int index) {
345
public void layerRemoved(View oldView, int oldIndex) {
352
public void subImageDataChanged() {
358
public void timestampChanged(int idx) {
364
public void viewportGeometryChanged() {
370
public void layerDownloaded(int idx) {