/* * Copyright 2003 Association for Universities for Research in Astronomy, Inc., * Observatory Control System, Gemini Telescopes Project. * * $Id: CatalogNavigator.java,v 1.12 2009/04/14 10:56:02 abrighto Exp $ */ package jsky.catalog.gui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Desktop; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.io.FileNotFoundException; import java.net.URL; import java.net.URLConnection; import java.util.*; import javax.swing.*; import javax.swing.event.*; import jsky.catalog.Catalog; import jsky.catalog.CatalogDirectory; import jsky.catalog.HTMLQueryResultHandler; import jsky.catalog.QueryResult; import jsky.catalog.TableQueryResult; import jsky.catalog.URLQueryResult; import jsky.catalog.astrocat.AstroCatConfig; import jsky.util.*; import jsky.util.SwingWorker; import jsky.util.gui.DialogUtil; import jsky.util.gui.GenericToolBarTarget; import jsky.util.gui.ProgressException; import jsky.util.gui.ProgressPanel; import jsky.util.gui.SwingUtil; import uk.ac.starlink.table.StarTable; /** * Used to navigate the catalog hierarchy. This class displays a tree of catalogs in one panel and the interface for * searching the catalog, or the query results in the other panel. *

* The tree display is based on a top level catalog directory. The details must be defined in a derived class. * * ** This class has been imported into Osp Project from Jsky library. */ public abstract class CatalogNavigator extends JPanel implements QueryResultDisplay, GenericToolBarTarget, HTMLQueryResultHandler { // Used to access internationalized strings (see i18n/gui*.proprties) private static final I18N _I18N = I18N.getInstance(CatalogNavigator.class); // // True if this is the main application window (enables exit menu item) // private static boolean _mainWindowFlag = false; // Set to true to query catalogs automatically when selected private boolean _autoQuery = false; // Displays the catalog tree and the catalog query widgets private JPanel _queryPanel; // Displays query results, such as tabular data. private JPanel _resultPanel; // Tree displaying catalog hierarchy private CatalogTree _catalogTree; // Query panel currently being displayed private JComponent _queryComponent; // Result panel currently being displayed private JComponent _resultComponent; // The original URL for the display component's data (for history list) private URL _origURL; // reuse file chooser widget for open private static JFileChooser _fileChooser; // reuse file chooser widget for saveAs private static JFileChooser _saveFileChooser; // Panel used to display download progress information private ProgressPanel _progressPanel; // list of listeners for change events private EventListenerList _listenerList = new EventListenerList(); // Stack of CatalogHistoryItems, used to go back to a previous panel private Stack _backStack = new Stack(); // Stack of CatalogHistoryItems, used to go forward to the next panel private Stack _forwStack = new Stack(); // Set when the back or forward actions are active to avoid the normal history stack handling private boolean _noStack = false; // Saved query result (set in background thread) private QueryResult _queryResult; // Optional object to use to plot table data private TablePlotter _plotter; // Utility object used to control background thread private SwingWorker _worker; // // Top level frame for viewing an HTML page // private HTMLViewerFrame _htmlViewerFrame; // Hash table associating each panel with a tree node private Hashtable _panelTreeNodeTable = new Hashtable(10); // Manages a list of previously viewed catalogs or query results. private CatalogHistoryList _historyList; // Manages a list of settings for stored querries, so that you can repeat the query later on private CatalogQueryList _queryList; // Maps query components to their corresponding result components private Hashtable _queryResultComponentMap = new Hashtable(); // Maps table ids and url strings to their corresponding query components private Hashtable _queryComponentMap = new Hashtable(); // The pane dividing the catalog tree and the query panel private JSplitPane _querySplitPane; // The pane dividing the query and the results panel private JSplitPane _resultSplitPane; // Action to use for the "Open..." menu and toolbar items private AbstractAction _openAction = new AbstractAction( _I18N.getString("open"), Resources.getIcon("Open24.gif")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("catalogOpenTip")); } @Override public void actionPerformed(ActionEvent evt) { open(); } }; // Action to use for the "Open URL..." menu and toolbar items private AbstractAction _openUrlAction = new AbstractAction( _I18N.getString("openURL")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("openURLTip")); } @Override public void actionPerformed(ActionEvent evt) { openURL(); } }; // Action to use for the "Clear" menu and toolbar items private AbstractAction _clearAction = new AbstractAction( _I18N.getString("clear")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("clearTip")); } @Override public void actionPerformed(ActionEvent evt) { clear(); } }; // May be set to override the default _openAction private AbstractAction _openActionOverride; // Action to use for the "Save as..." menu and toolbar items private AbstractAction _saveAsAction = new AbstractAction( _I18N.getString("saveAs"), Resources.getIcon("SaveAs24.gif")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("saveAsTip")); } @Override public void actionPerformed(ActionEvent evt) { saveAs(); } }; // Action to use for the "Save With Image..." menu and toolbar items private AbstractAction _saveWithImageAction = new AbstractAction( _I18N.getString("saveCatalogWithImage")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("saveCatalogWithImageTip")); } @Override public void actionPerformed(ActionEvent evt) { saveWithImage(); } }; // Action to use for the "Save as HTML..." menu and toolbar items private AbstractAction _saveAsHTMLAction = new AbstractAction( _I18N.getString("saveAsHTML")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("saveAsHTMLTip")); } @Override public void actionPerformed(ActionEvent evt) { saveAsHTML(); } }; // Action to use for the "Print..." menu and toolbar items private AbstractAction _printAction = new AbstractAction( _I18N.getString("print") + "...", Resources.getIcon("Print24.gif")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("printTip")); } @Override public void actionPerformed(ActionEvent evt) { print(); } }; // Action to use for the "Back" menu and toolbar items private AbstractAction _backAction = new AbstractAction( _I18N.getString("back"), Resources.getIcon("Back24.gif")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("catalogBackTip")); } @Override public void actionPerformed(ActionEvent evt) { back(); } }; // Action to use for the "Forward" menu and toolbar items private AbstractAction _forwAction = new AbstractAction( _I18N.getString("forward"), Resources.getIcon("Forward24.gif")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("catalogForwardTip")); } @Override public void actionPerformed(ActionEvent evt) { forward(); } }; // Action to use for the "Add Row" menu item private AbstractAction _addRowAction = new AbstractAction( _I18N.getString("addRow")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("addRowTip")); } @Override public void actionPerformed(ActionEvent evt) { addRow(); } }; // Action to use for the "Delete Rows..." menu item private AbstractAction _deleteSelectedRowsAction = new AbstractAction( _I18N.getString("deleteSelectedRows")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("deleteSelectedRowsTip")); } @Override public void actionPerformed(ActionEvent evt) { deleteSelectedRows(); } }; // Action to use for the "Query => Store => New Query..." menu item private AbstractAction _storeNewQueryAction = new AbstractAction( _I18N.getString("new")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("newQueryTip")); } @Override public void actionPerformed(ActionEvent evt) { storeNewQuery(); } }; // Action to use for the "Query => Delete => All" menu item private AbstractAction _deleteAllQueryAction = new AbstractAction( _I18N.getString("all")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("deleteAllQueryTip")); } @Override public void actionPerformed(ActionEvent evt) { clearQueryList(); } }; // Action to use for the "File => Close" menu item private AbstractAction _closeAction = new AbstractAction( _I18N.getString("close")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("closeTip")); } @Override public void actionPerformed(ActionEvent evt) { close(); } }; // Action to use for the "File => Exit" menu item private AbstractAction _exitAction = new AbstractAction( _I18N.getString("exit")) { { putValue(SHORT_DESCRIPTION, _I18N.getString("exitTip")); } @Override public void actionPerformed(ActionEvent evt) { exit(); } }; /** * Returns the top level catalog directory to use The contents of this are determined by the AstroCat.xml file found * either under ~/.jsky3 or as a resource (jsky-catalog/src/main/resources). * * @param load if true, load the catalog directory (which can be slow) if not already loaded, otherwise an empty * catalog directory is returned if not already initialized. This is just a placeholder that a listener * can be added to, to be notified later when the directory has been initialized. * @return the top level catalog directory */ public static CatalogDirectory getCatalogDirectory(boolean load) { return AstroCatConfig.getConfigFile(load); } /** * Returns the top level catalog directory to use The contents of this are determined by the AstroCat.xml file found * either under ~/.jsky3 or as a resource (jsky-catalog/src/main/resources). * * @return the top level catalog directory */ public static CatalogDirectory getCatalogDirectory() { return AstroCatConfig.getConfigFile(); } /** * Construct a CatalogNavigator using the given CatalogTree widget (Call setQueryResult to set the root catalog to * display). * * @param catalogTree a CatalogTree (normally a subclass of CatalogTree that knows about certain types of catalogs) */ public CatalogNavigator(CatalogTree catalogTree) { setLayout(new BorderLayout()); _catalogTree = catalogTree; catalogTree.setQueryResultDisplay(this); catalogTree.setHTMLQueryResultHandler(this); catalogTree.setPreferredSize(new Dimension(300, 0)); // Turn autoQuery off when the user selects the catalog in the tree // (Its turned on when selected from the main menu) catalogTree.getTree().getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent treeSelectionEvent) { _autoQuery = false; } }); _queryPanel = new JPanel(); _queryPanel.setLayout(new BorderLayout()); _resultPanel = new JPanel(); _resultPanel.setLayout(new BorderLayout()); _querySplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, catalogTree, _queryPanel); _querySplitPane.setOneTouchExpandable(false); _querySplitPane.setDividerLocation(385); _querySplitPane.setBorder(null); _resultSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, _querySplitPane, _resultPanel); _resultSplitPane.setOneTouchExpandable(false); _resultSplitPane.setDividerLocation(320); _resultSplitPane.setBorder(null); add(_resultSplitPane, BorderLayout.CENTER); _queryList = new CatalogQueryList(); _historyList = new CatalogHistoryList(); setQueryComponent(new EmptyPanel()); setResultComponent(new EmptyPanel()); } /** * Construct a CatalogNavigator using the given CatalogTree widget and TablePlotter (Call setQueryResult to set the * root catalog to display). * * @param catalogTree a CatalogTree (normally a subclass of CatalogTree that knows about certain types of catalogs) * @param plotter the object to use to plot catalog table data (when the plot button is pressed) */ public CatalogNavigator(CatalogTree catalogTree, TablePlotter plotter) { this(catalogTree); _plotter = plotter; } /** * @return the object displaying the catalog tree */ public CatalogTree getCatalogTree() { return _catalogTree; } /** * @return the pane dividing the catalog tree and the query panel */ protected JSplitPane getQuerySplitPane() { return _querySplitPane; } /** * @return the pane dividing the query and the results panel */ public JSplitPane getResultSplitPane() { return _resultSplitPane; } /** * @param b Set to true to query catalogs automatically when selected */ public void setAutoQuery(boolean b) { _autoQuery = b; } /** * @return the object used to plot table data, or null if none was defined. */ public TablePlotter getPlotter() { return _plotter; } /** * @param tp the object used to plot table data. */ public void setPlotter(TablePlotter tp) { _plotter = tp; } /** * Set the query or result component to display. The choice is made based on which interfaces the component * implements. If the component implements QueryResultDisplay, it is considered a result component. * * @param component the query or result component to display */ public void setComponent(JComponent component) { if (component instanceof QueryResultDisplay) { setResultComponent(component); } else { setQueryComponent(component); //System.out.println("XXX _autoQuery = " + _autoQuery + ", component is a " + component.getClass()); if ((component instanceof CatalogQueryTool) && (_autoQuery || ((CatalogQueryTool) component).getCatalog().isLocal())) { ((CatalogQueryTool) component).search(); } } } /** * @param component the query component to display */ public void setQueryComponent(JComponent component) { if (component == null || component == _queryComponent) { return; } if (_queryComponent != null) { addToHistory(); _queryPanel.remove(_queryComponent); _queryComponent = null; } _queryComponent = component; Catalog cat = _catalogTree.getSelectedCatalog(); if (cat != null) { _panelTreeNodeTable.put(_queryComponent, cat); } _queryPanel.add(_queryComponent, BorderLayout.CENTER); // restore the query result corresponding to this catalog, if known Object resultComp = _queryResultComponentMap.get(_queryComponent); if (resultComp == null) { setResultComponent(new EmptyPanel()); } else { setResultComponent((JComponent) resultComp); } update(); } /** * @return the panel currently being displayed */ public JComponent getQueryComponent() { return _queryComponent; } /** * @param component the result component to display */ public void setResultComponent(JComponent component) { if (component == null || component == _resultComponent) { return; } if (_resultComponent != null) { // if (_resultComponent instanceof TableDisplayTool) { // if we're not reusing the current table window, tell it to hide any related popup // windows before replacing it (It might be needed again later though, if the user // goes back to it). //((TableDisplayTool)_resultComponent).hidePopups(); // } _resultPanel.remove(_resultComponent); _resultComponent = null; } _resultComponent = component; if (_queryComponent != null) { _queryResultComponentMap.put(_queryComponent, _resultComponent); } _resultPanel.add(_resultComponent, BorderLayout.CENTER); update(); _resultComponentChanged(); // try to display the right amount of the query window // SwingUtilities.invokeLater(new Runnable() { // public void run() { // _resultSplitPane.resetToPreferredSizes(); // } // }); } /** * @return the panel currently being displayed */ public JComponent getResultComponent() { return _resultComponent; } /** * Called whenever the display component is changed */ protected void _resultComponentChanged() { // set the state of the "Save As..." menu item _saveAsAction.setEnabled(_resultComponent instanceof Saveable); _printAction.setEnabled(_resultComponent instanceof PrintableWithDialog); boolean isTable = (_resultComponent instanceof TableDisplayTool); _saveWithImageAction.setEnabled(isTable); _deleteSelectedRowsAction.setEnabled(isTable); _addRowAction.setEnabled(isTable); fireChange(new ChangeEvent(this)); } /** * Register to receive change events from this object whenever a new query result is displayed. * * @param l the listener */ public void addChangeListener(ChangeListener l) { _listenerList.add(ChangeListener.class, l); } /** * Stop receiving change events from this object. * * @param l the listener */ public void removeChangeListener(ChangeListener l) { _listenerList.remove(ChangeListener.class, l); } /** * Notify any listeners that a new query result is being displayed. * * @param e the event */ protected void fireChange(ChangeEvent e) { Object[] listeners = _listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { ((ChangeListener) listeners[i + 1]).stateChanged(e); } } } /** * Add the current catalog to the history stack, removing duplicates. */ protected void addToHistory() { if (_queryComponent == null) { return; } CatalogHistoryItem historyItem = makeCatalogHistoryItem(); if (historyItem == null) { return; } if (!_noStack) { _backStack.push(historyItem); _backAction.setEnabled(true); if (_forwStack.size() != 0) { _cleanupHistoryStack(_forwStack); _forwStack.clear(); _forwAction.setEnabled(false); } } _historyList.add(historyItem); } /** * @return a new CatalogHistoryItem for the currently displayed catalog. */ protected CatalogHistoryItem makeCatalogHistoryItem() { String s = _queryComponent.getName(); if (s != null) { return new CatalogHistoryItem(s, _origURL, _queryComponent); } return null; } /** * Add history items (for previously displayed components) to the given menu * * @param menu the menu to add the items to */ public void addHistoryMenuItems(JMenu menu) { Iterator it = _historyList.iterator(); while (it.hasNext()) { menu.add(it.next()); } } // This method may be redefined in subclasses to do cleanup work before components are // removed from the given history stack (_backStack or _forwStack). private void _cleanupHistoryStack(Stack stack) { unplot(stack); } /** * Clear out the history and back/forward stacks */ protected void clearHistory() { _backAction.setEnabled(false); _backStack.clear(); _forwAction.setEnabled(false); _forwStack.clear(); _historyList.clear(); } /** * Set the original URL for the current catalog or table. * * @param url the URL of the catalog, table or FITS file */ public void setOrigURL(URL url) { _origURL = url; } /** * Returns the original URL for the current catalog or table. * * @return the URL of the catalog, table or FITS file */ public URL getOrigURL() { return _origURL; } /** * Remove any plot symbols or graphics managed by any of the display components in the given stack * * @param stack history stack */ protected void unplot(Stack stack) { // Unplot any catalog symbols before loosing the information int n = stack.size(); for (int i = 0; i < n; i++) { CatalogHistoryItem item = (CatalogHistoryItem) (stack.get(i)); Object resultComp = _queryResultComponentMap.get(item.getQueryComponent()); if (resultComp instanceof TableDisplayTool) { ((TableDisplayTool) resultComp).unplot(); } } } /** * Remove any plot symbols or graphics managed by any of the display components */ public void unplot() { Enumeration e = _queryResultComponentMap.elements(); while (e.hasMoreElements()) { JComponent comp = (JComponent) e.nextElement(); if (comp instanceof TableDisplayTool) { ((TableDisplayTool) comp).unplot(); } } } /** * Update the layout after a new component has been inserted */ protected void update() { _queryPanel.revalidate(); _resultPanel.revalidate(); JFrame parent = SwingUtil.getFrame(this); if (parent != null) { parent.repaint(); } } /** * Select the node in the catalog directory tree corresponding to the current display component */ protected void updateTreeSelection() { if (_queryComponent instanceof CatalogQueryTool) { _catalogTree.setSelectedCatalog(((CatalogQueryTool) _queryComponent).getCatalog(), true); _updateTitle(((CatalogQueryTool) _queryComponent).getCatalog()); } else if (_queryComponent instanceof TableDisplayTool) { _catalogTree.setSelectedCatalog(((TableDisplayTool) _queryComponent).getTable(), true); } } public QueryResult getQueryResult() { return _queryResult; } /** * Display the given query result. */ @Override public void setQueryResult(QueryResult queryResult) { if (queryResult == null) { return; } if (_worker != null) { // shouldn't happen if user interface disables it DialogUtil.error(_I18N.getString("queryInProgress")); return; } _queryResult = queryResult; // Use a background thread for remote catalog access, local catalogs are handled in this thread boolean isLocal = true; if (queryResult instanceof URLQueryResult) { URLQueryResult uqr = (URLQueryResult) queryResult; URL url = uqr.getURL(); isLocal = (url.getProtocol().equals("file")); } else if (queryResult instanceof Catalog) { isLocal = ((Catalog) queryResult).isLocal(); } if (isLocal) { setComponent(makeQueryResultComponent(queryResult)); } else { // remote catalog: run in a separate thread, so the user can monitor progress makeProgressPanel(); _worker = new SwingWorker() { @Override public Object construct() { try { return makeQueryResultComponent(_queryResult); } catch (Exception e) { return e; } } @Override public void finished() { _worker = null; _progressPanel.stop(); Object o = getValue(); if (o instanceof Exception) { DialogUtil.error((Exception) o); } else if (o instanceof JComponent) { setComponent((JComponent) o); } } }; _worker.start(); } } /** * Update the frame's title to display the name of the given catalog * * @param catalog the given catalog */ private void _updateTitle(Catalog catalog) { String title = _I18N.getString("catalogNavigator"); String s = catalog.getTitle(); if (s != null && s.length() > 0) { title += " - " + s; } JFrame parent = SwingUtil.getFrame(this); if (parent != null) { parent.setTitle(title); } } /** * If it does not already exist, make the panel used to display the progress of network access. */ protected void makeProgressPanel() { if (_progressPanel == null) { JFrame parent = SwingUtil.getFrame(this); _progressPanel = ProgressPanel.makeProgressPanel(_I18N.getString("accessingCatalogServer"), parent); _progressPanel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (_worker != null) { _worker.interrupt(); _worker = null; } } }); } } /** * Create and return a JComponent displaying the given query result. * * @param queryResult result of a catalog query * @return a JComponent displaying the given query result */ protected JComponent makeQueryResultComponent(QueryResult queryResult) { _origURL = null; try { // See if there is a user interface handler for the query result if (queryResult instanceof CatalogUIHandler) { JComponent c = ((CatalogUIHandler) queryResult).makeComponent(this); if (c != null) { return c; } } // No UI handler, return the default component for the query result if (queryResult instanceof CatalogDirectory) { return makeCatalogDirectoryComponent((CatalogDirectory) queryResult); } if (queryResult instanceof TableQueryResult) { return makeTableQueryResultComponent((TableQueryResult) queryResult); } if (queryResult instanceof Catalog) { return makeCatalogComponent((Catalog) queryResult); } if (queryResult instanceof URLQueryResult) { return makeURLComponent((URLQueryResult) queryResult); } } catch (Exception e) { if (_progressPanel != null) { _progressPanel.stop(); } DialogUtil.error(e); } return new EmptyPanel(); } /** * @param catalogDirectory a catalog directory * @return a new JComponent displaying the contents of the given catalog directory */ protected JComponent makeCatalogDirectoryComponent(CatalogDirectory catalogDirectory) { // get the number of catalogs in the directory int numCatalogs = catalogDirectory.getNumCatalogs(); if (numCatalogs == 0) { return makeCatalogComponent(catalogDirectory); } if (numCatalogs == 1) { return makeCatalogComponent(catalogDirectory.getCatalog(0)); } return new EmptyPanel(); } /** * Return a new JComponent displaying the contents of the given table query result. * * @param tableQueryResult a table as the result of a query * @return a component displaying the table */ protected JComponent makeTableQueryResultComponent(TableQueryResult tableQueryResult) { if (_resultComponent instanceof TableDisplayTool) { TableDisplayTool tdt = (TableDisplayTool) _resultComponent; if (tdt.getTable().getName().equals(tableQueryResult.getName())) { tdt.setQueryResult(tableQueryResult); return tdt; } } TableDisplayTool t = new TableDisplayTool(tableQueryResult, this, _plotter); // add a popup menu to the table makeTablePopupMenu(t); return t; } /** * Add a popup menu to the given TableDisplayTool * * @param t component for displaying tables */ protected void makeTablePopupMenu(TableDisplayTool t) { final JPopupMenu m = new JPopupMenu(); m.add(_addRowAction); m.add(_deleteSelectedRowsAction); t.getTableDisplay().getTable().addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) { m.show(e.getComponent(), e.getX(), e.getY()); } } @Override public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) { m.show(e.getComponent(), e.getX(), e.getY()); } } }); } /** * @param catalog a catalog * @return a new JComponent displaying the contents of (or the interface for searching) the given catalog */ protected JComponent makeCatalogComponent(Catalog catalog) { // catalog may contain multiple tables and implement the CatalogDirectory interface if (catalog instanceof CatalogDirectory) { CatalogDirectory catalogDirectory = (CatalogDirectory) catalog; int numCatalogs = catalogDirectory.getNumCatalogs(); if (numCatalogs == 1) { Catalog c = catalogDirectory.getCatalog(0); if (c instanceof TableQueryResult) { return makeTableQueryResultComponent((TableQueryResult) c); } else { DialogUtil.error(_I18N.getString("subCatalogError") + ": " + c); return new EmptyPanel(); } } else if (numCatalogs > 1) { return makeTableQueryResultComponent(catalogDirectory.getCatalogList()); } } if (catalog instanceof TableQueryResult) { return makeTableQueryResultComponent((TableQueryResult) catalog); } // Default to normal catalog query component return makeCatalogQueryTool(catalog); } /** * Make a panel for querying a catalog * * @param catalog the catalog * @return the query panel to display */ protected CatalogQueryTool makeCatalogQueryTool(Catalog catalog) { return new CatalogQueryTool(catalog, this); } /** * Makes a component to display the contents of a URL * * @param queryResult contains the URL are related inforation * @return a new JComponent displaying the contents of the URL. * @throws java.io.IOException if the URL can't be read */ protected JComponent makeURLComponent(URLQueryResult queryResult) throws IOException { try { URL url = queryResult.getURL(); URLConnection connection; if (url.getProtocol().equals("file")) { connection = url.openConnection(); } else { connection = _progressPanel.openConnection(url); } if (connection == null) { return _queryComponent; } String format = queryResult.getFormat(); String contentType = connection.getContentType(); if (contentType == null) { contentType = "unknown"; } return makeURLComponent(url, contentType, format); } catch (ProgressException e) { // ignore: user pressed the stop button in the progress panel } catch (FileNotFoundException e) { DialogUtil.error(_I18N.getString("fileNotFound", e.getMessage())); } catch (Exception e) { DialogUtil.error(e); } if (_resultComponent != null) { return _resultComponent; } return new EmptyPanel(); } /** * @param url the URL to read * @param contentType the content type from the http server * @param format a format string from the table row, or null if not defined * @return a new JComponent displaying the contents of the given URL. * @throws java.io.IOException on read error */ protected JComponent makeURLComponent(URL url, String contentType, String format) throws IOException { String filename = url.getFile(); if (contentType.startsWith("text/plain")) { displayPlainText(url); return _resultComponent; } if (contentType.startsWith("text/")) { // assume HTML or something that a browser can display... displayHTMLPage(url); return _resultComponent; } // If it is not one of the known content types, call a method that may be // redefined in a derived class to handle that type return makeUnknownURLComponent(url, contentType); } // /* // * Attempt to show a URL in the default web browser and return true if successful. // */ // protected boolean displayHTMLPageWithDefaultBrowser(URL url) { // } /** * Display the given HTML URL in a popup window containing a JEditorPane. */ @Override public void displayHTMLPage(URL url) { // if (_htmlViewerFrame != null) { // _htmlViewerFrame.getHTMLViewer().setPage(url); // _htmlViewerFrame.setState(Frame.NORMAL); // _htmlViewerFrame.setVisible(true); // return; // } // _htmlViewerFrame = new HTMLViewerFrame(); // _htmlViewerFrame.getHTMLViewer().setPage(url); try { Desktop.getDesktop().browse(url.toURI()); } catch (Exception e) { DialogUtil.error(e); } } /** * Display the text pointed to by the given URL. * * @param url the URL to read */ public void displayPlainText(URL url) { try { String msg = FileUtil.getURL(url); if (_progressPanel != null) { _progressPanel.stop(); } if (msg.length() < 256) { DialogUtil.error(msg); } else { displayHTMLPage(url); } } catch (IOException e) { DialogUtil.error(e); } } /** * @param url the URL to read * @param contentType the content type from the http server * @return a new JComponent displaying the contents of the given URL. A null return value causes an empty panel to * be displayed. Returning the current component (_resultComponent) will cause no change. This should be * done if the URL is displayed in a separate window. */ @SuppressWarnings({"UnusedDeclaration"}) protected JComponent makeUnknownURLComponent(URL url, String contentType) { if (_resultComponent != null) { return _resultComponent; } return new EmptyPanel(); } /** * Display a file chooser to select a local catalog file to open */ public void open() { if (_openActionOverride != null) { _openActionOverride.actionPerformed(null); return; } if (_fileChooser == null) { _fileChooser = makeFileChooser(); } int option = _fileChooser.showOpenDialog(this); if (option == JFileChooser.APPROVE_OPTION && _fileChooser.getSelectedFile() != null) { open(_fileChooser.getSelectedFile().getAbsolutePath()); } } /** * Create and return a new file chooser to be used to select a local file to open. * * @return a new file chooser */ protected JFileChooser makeFileChooser() { return new JFileChooser(new File(".")); } /** * @return the existing file chooser, or a new one if none exists */ public JFileChooser getFileChooser() { if (_fileChooser == null) { _fileChooser = makeFileChooser(); } return _fileChooser; } /** * Create and return a new file chooser to be used for saving to a file. * * @return a new file chooser */ protected JFileChooser makeSaveFileChooser() { return new JFileChooser(new File(".")); } /** * Open the given file or URL * * @param fileOrUrl a file or URL */ public void open(String fileOrUrl) { try { setQueryComponent(new EmptyPanel()); URL url = FileUtil.makeURL(null, fileOrUrl); URLQueryResult _queryResult = new URLQueryResult(url); setQueryResult(_queryResult); } catch (Exception e) { DialogUtil.error(e); } } /** * Exit the application with the given status. */ public void exit() { System.exit(0); } /** * Close the window */ public void close() { JFrame parent = SwingUtil.getFrame(this); if (parent != null) { parent.setVisible(false); } } /** * Go back to the previous component in the history list */ public void back() { if (_backStack.size() == 0) { return; } if (_queryComponent != null) { _queryPanel.remove(_queryComponent); URL url = _origURL; // save and restore this CatalogHistoryItem item = makeCatalogHistoryItem(); _origURL = url; if (item != null) { _forwStack.push(item); _forwAction.setEnabled(true); } } CatalogHistoryItem historyItem = _backStack.pop(); if (_backStack.size() == 0) { _backAction.setEnabled(false); } // select the related tree node if (historyItem.getQueryComponent() != null) { Catalog cat = _panelTreeNodeTable.get(historyItem.getQueryComponent()); if (cat != null) { _catalogTree.setSelectedCatalog(cat, true); } } CatalogNavigatorMenuBar.setCurrentCatalogNavigator(this); _noStack = true; try { historyItem.actionPerformed(null); } catch (Exception e) { DialogUtil.error(e); } _noStack = false; update(); } /** * Go forward to the next component in the history list */ public void forward() { if (_forwStack.size() == 0) { return; } if (_queryComponent != null) { _queryPanel.remove(_queryComponent); URL url = _origURL; // save and restore this CatalogHistoryItem item = makeCatalogHistoryItem(); _origURL = url; if (item != null) { _backStack.push(item); _backAction.setEnabled(true); } } CatalogHistoryItem historyItem = _forwStack.pop(); if (_forwStack.size() == 0) { _forwAction.setEnabled(false); } // select the related tree node if (historyItem.getQueryComponent() != null) { Catalog cat = _panelTreeNodeTable.get(historyItem.getQueryComponent()); if (cat != null) { _catalogTree.setSelectedCatalog(cat, true); } } CatalogNavigatorMenuBar.setCurrentCatalogNavigator(this); _noStack = true; try { historyItem.actionPerformed(null); } catch (Exception e) { DialogUtil.error(e); } _noStack = false; update(); } // These are for the GenericToolBarTarget interface @Override public AbstractAction getOpenAction() { return _openAction; } public AbstractAction getOpenUrlAction() { return _openUrlAction; } public AbstractAction getClearAction() { return _clearAction; } /** * Override the default Open action. * * @param openAction the new action */ public void setOpenAction(AbstractAction openAction) { _openActionOverride = openAction; } public AbstractAction getSaveAsAction() { return _saveAsAction; } public AbstractAction getSaveAsHTMLAction() { return _saveAsHTMLAction; } public AbstractAction getSaveWithImageAction() { return _saveWithImageAction; } public AbstractAction getPrintAction() { return _printAction; } @Override public AbstractAction getBackAction() { return _backAction; } @Override public AbstractAction getForwAction() { return _forwAction; } public AbstractAction getAddRowAction() { return _addRowAction; } public AbstractAction getDeleteSelectedRowsAction() { return _deleteSelectedRowsAction; } public AbstractAction getStoreNewQueryAction() { return _storeNewQueryAction; } public AbstractAction getDeleteAllQueryAction() { return _deleteAllQueryAction; } public AbstractAction getCloseAction() { return _closeAction; } public AbstractAction getExitAction() { return _exitAction; } /** * Display a dialog to enter a URL to display */ public void openURL() { String urlStr = DialogUtil.input(_I18N.getString("enterURLDisplay") + ":"); if (urlStr != null) { URL url; try { url = new URL(urlStr); } catch (Exception e) { DialogUtil.error(e); return; } setQueryResult(new URLQueryResult(url)); } } /** * Clear the display. */ public void clear() { setQueryComponent(new EmptyPanel()); _origURL = null; } /** * Pop up a dialog to ask the user for a file name, and then save the current query result to the selected file. */ public void saveAs() { if (_resultComponent instanceof SaveableWithDialog) { ((SaveableWithDialog) _resultComponent).saveAs(); } else { DialogUtil.error(_I18N.getString("saveNotSupportedForObjType")); } } /** * Save the current query result to the selected file. * * @param filename the file name */ public void saveAs(String filename) { if (_resultComponent instanceof Saveable) { try { ((Saveable) _resultComponent).saveAs(filename); } catch (Exception e) { DialogUtil.error(e); } } else { DialogUtil.error(_I18N.getString("saveNotSupportedForObjType")); } } /** * Save the current table as a FITS table in the current FITS image (Should be defined in a derived class). */ public void saveWithImage() { } /** * Pop up a dialog to ask the user for a file name, and then save the current query result to the selected file in * HTML format. */ public void saveAsHTML() { if (_resultComponent instanceof SaveableAsHTML) { if (_saveFileChooser == null) { _saveFileChooser = makeSaveFileChooser(); } int option = _saveFileChooser.showSaveDialog(this); if (option == JFileChooser.APPROVE_OPTION && _saveFileChooser.getSelectedFile() != null) { saveAsHTML(_saveFileChooser.getSelectedFile().getAbsolutePath()); } } else { DialogUtil.error(_I18N.getString("htmlOutputNotSupportedForObjType")); } } /** * Save the current query result to the selected file in HTML format. * * @param filename the file name */ public void saveAsHTML(String filename) { if (_resultComponent instanceof SaveableAsHTML) { try { ((SaveableAsHTML) _resultComponent).saveAsHTML(filename); } catch (Exception e) { DialogUtil.error(e); } } else { DialogUtil.error(_I18N.getString("htmlOutputNotSupportedForObjType")); } } /** * Pop up a dialog for printing the query results. */ public void print() { if (_resultComponent instanceof PrintableWithDialog) { try { ((PrintableWithDialog) _resultComponent).print(); } catch (Exception e) { DialogUtil.error(e); } } else { DialogUtil.error(_I18N.getString("printingNotSupportedForObjType")); } } /** * If a table is being displayed, add an empty row in the table. */ public void addRow() { if (_resultComponent instanceof TableDisplayTool) { ((TableDisplayTool) _resultComponent).addRow(); } } /** * If a table is being displayed, delete the selected rows. */ public void deleteSelectedRows() { if (_resultComponent instanceof TableDisplayTool) { ((TableDisplayTool) _resultComponent).deleteSelectedRows(); } } /** * Set the editable state of the cells in the displayed table. * * @param b set to true if editable */ public void setTableCellsEditable(boolean b) { if (_resultComponent instanceof TableDisplayTool) { ((TableDisplayTool) _resultComponent).setTableCellsEditable(b); } } // /** // * @return true if this is the main application window (enables exit menu item) // */ // public static boolean isMainWindow() { // return _mainWindowFlag; // } // /** // * @param b Set to true if this is the main application window (enables exit menu item) // */ // public static void setMainWindow(boolean b) { // _mainWindowFlag = b; // } /** * Used to identify an empty query or result panel */ public class EmptyPanel extends JPanel implements QueryResultDisplay { public EmptyPanel() { // There is no border by default on Mac OS X, so add one if ("Mac OS X".equals(UIManager.getLookAndFeel().getName())) { setBorder(BorderFactory.createEtchedBorder()); } } @Override public void setQueryResult(QueryResult queryResult) { throw new RuntimeException(_I18N.getString("queryResultDisplayError")); } } /** * @return the panel used to display download progress information */ protected ProgressPanel getProgressPanel() { return _progressPanel; } /** * @return the stack of CatalogHistoryItems, used to go back to a previous panel */ protected Stack getBackStack() { return _backStack; } /** * @return the stack of CatalogHistoryItems, used to go forward to the next panel */ protected Stack getForwStack() { return _forwStack; } /** * Ask the user for a name, and then store the current query and display settings under that name for later use. */ public void storeNewQuery() { String name = DialogUtil.input(this, _I18N.getString("enterNameForQuery")); if (name == null || name.length() == 0) { return; } storeQuery(name); } /** * Store the current query and display settings under the given name for later use. * * @param name the query name */ public void storeQuery(String name) { if (_queryComponent instanceof Storeable) { try { Object queryInfo = ((Storeable) _queryComponent).storeSettings(); Object resultInfo = null; if (_resultComponent instanceof Storeable) { resultInfo = ((Storeable) _resultComponent).storeSettings(); } CatalogQueryItem item = new CatalogQueryItem(name, queryInfo, resultInfo); _queryList.add(item); } catch (Exception e) { DialogUtil.error(e); } } } /** * Delete the named query and display settings. * * @param name the query name */ public void deleteQuery(String name) { _queryList.remove(name); } /** * Remove all items from the query list. */ public void clearQueryList() { _queryList.clear(); } /** * Add Query items (for previously stored queries) to the given menu, using the given listener, if supplied, * otherwise the default (restore the query). * * @param menu the menu to add to * @param l the listener */ public void addQueryMenuItems(JMenu menu, ActionListener l) { Iterator it = _queryList.iterator(); if (l == null) { while (it.hasNext()) { menu.add((CatalogQueryItem) it.next()); } } else { while (it.hasNext()) { CatalogQueryItem item = (CatalogQueryItem) it.next(); JMenuItem menuItem = new JMenuItem(item.getName()); menuItem.addActionListener(l); menu.add(menuItem); } } } /** * @return a StarTable for the currently displayed table, or null if no table is displayed */ public StarTable getStarTable() { JComponent c = getResultComponent(); if (c instanceof TableDisplayTool) { TableDisplayTool t = (TableDisplayTool) c; TableQueryResult tqr = t.getTable(); if (tqr != null) { return tqr.getStarTable(); } } return null; } /** * Register the currently displayed query component with the given URL and table id for later reference in the * {@link #selectTableRows} method. * * @param url the table URL * @param tableId a unique value used to identify the table */ public void registerTable(URL url, String tableId) { if (_queryComponent != null) { if (tableId != null) { _queryComponentMap.put(tableId, _queryComponent); } if (url != null) { _queryComponentMap.put(url.toString(), _queryComponent); } } } /** * Selects a single row of an identified table by row index. The table to operate on is identified by one or both of * the table-id or url arguments. At least one of these must be supplied; if both are given they should refer to the * same thing. Exactly what highlighting means is left to the receiving application. * * @param tableId identifier associated with a table * @param url URL of a table * @param rows array of row indexes to highlight */ public void selectTableRows(String tableId, URL url, int[] rows) { JComponent queryComp = null; if (tableId != null) { queryComp = _queryComponentMap.get(tableId); } if (queryComp == null && url != null) { queryComp = _queryComponentMap.get(url.toString()); } if (queryComp != null) { JComponent resultComp = _queryResultComponentMap.get(queryComp); if (resultComp instanceof TableDisplayTool) { TableDisplay tableDisplay = ((TableDisplayTool) resultComp).getTableDisplay(); tableDisplay.getTable().clearSelection(); for (int row : rows) { tableDisplay.selectRow(row); } // Display the catalog and table, if not already displayed CatalogHistoryItem historyItem = _historyList.getItemForQueryComponent(queryComp); if (historyItem != null) { historyItem.actionPerformed(null); } } } } }