Commit 84b3dbfc8f6fd5fb6645fc9148cded8de3c3cfd6

Authored by Nathanael Jourdane
1 parent d349949e
Exists in master

Use SwingWorkers to avoid EDT violations.

src/main/java/eu/omp/irap/vespa/epntapclient/EpnTapConnection.java
@@ -125,7 +125,7 @@ public class EpnTapConnection implements EpnTapInterface { @@ -125,7 +125,7 @@ public class EpnTapConnection implements EpnTapInterface {
125 @Override 125 @Override
126 public List<Granule> sendADQLQuery(String tapURL, String adqlQuery) throws VOTableException { 126 public List<Granule> sendADQLQuery(String tapURL, String adqlQuery) throws VOTableException {
127 VOTableCtrl voTableCtrl = new VOTableCtrl(); 127 VOTableCtrl voTableCtrl = new VOTableCtrl();
128 - voTableCtrl.newVOTable(tapURL, adqlQuery); 128 + voTableCtrl.acquireVOTable(tapURL, adqlQuery, false);
129 VOTableData data = voTableCtrl.getVOTableData(); 129 VOTableData data = voTableCtrl.getVOTableData();
130 130
131 List<Granule> granules; 131 List<Granule> granules;
@@ -139,7 +139,7 @@ public class EpnTapConnection implements EpnTapInterface { @@ -139,7 +139,7 @@ public class EpnTapConnection implements EpnTapInterface {
139 throws VOTableException { 139 throws VOTableException {
140 String query = String.format(enumeratedQuery.toString(), schemaName); 140 String query = String.format(enumeratedQuery.toString(), schemaName);
141 VOTableCtrl voTableCtrl = new VOTableCtrl(); 141 VOTableCtrl voTableCtrl = new VOTableCtrl();
142 - voTableCtrl.newVOTable(tapURL, query); 142 + voTableCtrl.acquireVOTable(tapURL, query, false);
143 VOTableData data = voTableCtrl.getVOTableData(); 143 VOTableData data = voTableCtrl.getVOTableData();
144 Debug.writeObject("data", data); 144 Debug.writeObject("data", data);
145 145
src/main/java/eu/omp/irap/vespa/epntapclient/EpnTapMainApp.java
@@ -18,6 +18,9 @@ package eu.omp.irap.vespa.epntapclient; @@ -18,6 +18,9 @@ package eu.omp.irap.vespa.epntapclient;
18 18
19 import java.util.logging.Logger; 19 import java.util.logging.Logger;
20 20
  21 +import javax.swing.JFrame;
  22 +import javax.swing.SwingUtilities;
  23 +
21 import com.google.gson.Gson; 24 import com.google.gson.Gson;
22 25
23 import eu.omp.irap.vespa.epntapclient.gui.mainpanel.MainPanelCtrl; 26 import eu.omp.irap.vespa.epntapclient.gui.mainpanel.MainPanelCtrl;
@@ -46,12 +49,27 @@ public class EpnTapMainApp { @@ -46,12 +49,27 @@ public class EpnTapMainApp {
46 * @param args The program arguments (not used). 49 * @param args The program arguments (not used).
47 */ 50 */
48 public static void main(final String[] args) { 51 public static void main(final String[] args) {
  52 + // RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
49 LOGGER.info("Lauching EPNTAP app with arguments: " + new Gson().toJson(args)); 53 LOGGER.info("Lauching EPNTAP app with arguments: " + new Gson().toJson(args));
50 if (args.length != 0) { 54 if (args.length != 0) {
51 System.console().writer().println(WRONG_COMMAND); 55 System.console().writer().println(WRONG_COMMAND);
52 return; 56 return;
53 } 57 }
54 - MainPanelCtrl guiCtrl = new MainPanelCtrl();  
55 - guiCtrl.readServices(); 58 +
  59 + SwingUtilities.invokeLater(new Runnable() {
  60 +
  61 + @Override
  62 + public void run() {
  63 + MainPanelCtrl guiCtrl = new MainPanelCtrl();
  64 + guiCtrl.acquireServices();
  65 + JFrame frame = new JFrame("EpnTAP client");
  66 + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  67 + frame.setContentPane(guiCtrl.getView());
  68 + frame.setVisible(true);
  69 + frame.setSize(800, 600);
  70 + frame.setLocationRelativeTo(null);
  71 +
  72 + }
  73 + });
56 } 74 }
57 } 75 }
src/main/java/eu/omp/irap/vespa/epntapclient/epntap/EpnTapController.java
@@ -55,6 +55,17 @@ public abstract class EpnTapController { @@ -55,6 +55,17 @@ public abstract class EpnTapController {
55 55
56 56
57 /** 57 /**
  58 + * Get the services from the XML path or the targetURL / query.
  59 + *
  60 + * @throws VOTableException Can not read the services.
  61 + */
  62 + public void acquireServices() throws VOTableException {
  63 + String query = String.format(Queries.SELECT_ALL_TAP_SERVICES_WHERE_CORE,
  64 + ServiceCore.EPNCORE);
  65 + getServicesCtrl().acquireVOTable(Consts.DEFAULT_REGISTRY_URL, query, true);
  66 + }
  67 +
  68 + /**
58 * @return The request controller. 69 * @return The request controller.
59 */ 70 */
60 public RequestCtrl getRequestCtrl() { 71 public RequestCtrl getRequestCtrl() {
@@ -83,37 +94,16 @@ public abstract class EpnTapController { @@ -83,37 +94,16 @@ public abstract class EpnTapController {
83 } 94 }
84 95
85 /** 96 /**
86 - * Get the services from the XML path or the targetURL / query.  
87 - *  
88 - * @throws VOTableException Can not read the services.  
89 - */  
90 - public void readServices() throws VOTableException {  
91 - // Here getServicesCtrl() is used instead of class field servicesCtrl to get the  
92 - // subclass field, since subclasses overrides getServicesCtrl().  
93 - String query = String.format(Queries.SELECT_ALL_TAP_SERVICES_WHERE_CORE,  
94 - ServiceCore.EPNCORE);  
95 - getServicesCtrl().newVOTable(Consts.DEFAULT_REGISTRY_URL, query);  
96 - }  
97 -  
98 - /**  
99 - * ... 97 + * Send all the queries.
100 * 98 *
101 * @param services The services to send the queries. 99 * @param services The services to send the queries.
102 - * @throws VOTableException Can not update the VOTable.  
103 */ 100 */
104 - public void sendQueries(ServicesList services) throws VOTableException {  
105 - // Here getRequestCtrl() and getResultsCtrl() are used instead of class fields requestCtrl  
106 - // and resultCtrl to get the subclass field, since subclasses overrides getRequestCtrl() and  
107 - // getResultsCtrl(). 101 + public void sendQueries(ServicesList services) {
108 List<String> servicesUrls = services.getTargetUrls(); 102 List<String> servicesUrls = services.getTargetUrls();
109 - LOGGER.info("Sending query(ies) on " + StringJoiner.join(servicesUrls)); 103 + LOGGER.info("Sending query(ies) at " + StringJoiner.join(servicesUrls));
110 for (int i = 0; i < servicesUrls.size(); i++) { 104 for (int i = 0; i < servicesUrls.size(); i++) {
111 String query = getRequestCtrl().getQuery(services.getTableNames().get(i)); 105 String query = getRequestCtrl().getQuery(services.getTableNames().get(i));
112 - if (i == 0) {  
113 - getResultsCtrl().newVOTable(servicesUrls.get(i), query);  
114 - } else {  
115 - getResultsCtrl().appendVOTable(servicesUrls.get(i), query);  
116 - } 106 + getResultsCtrl().acquireVOTable(servicesUrls.get(i), query, i != 0);
117 } 107 }
118 108
119 } 109 }
src/main/java/eu/omp/irap/vespa/epntapclient/epntap/service/ServiceCtrl.java
@@ -152,7 +152,7 @@ public class ServiceCtrl { @@ -152,7 +152,7 @@ public class ServiceCtrl {
152 */ 152 */
153 public static VOTABLE getVoTable(String query) throws VOTableException { 153 public static VOTABLE getVoTable(String query) throws VOTableException {
154 VOTableCtrl voTableCtrl = new VOTableCtrl(); 154 VOTableCtrl voTableCtrl = new VOTableCtrl();
155 - voTableCtrl.newVOTable(Consts.DEFAULT_REGISTRY_URL, query); 155 + voTableCtrl.acquireVOTable(Consts.DEFAULT_REGISTRY_URL, query, false);
156 return voTableCtrl.getVOTable(); 156 return voTableCtrl.getVOTable();
157 } 157 }
158 158
@@ -173,10 +173,10 @@ public class ServiceCtrl { @@ -173,10 +173,10 @@ public class ServiceCtrl {
173 * @throws VOTableException Can not get the VOTable. 173 * @throws VOTableException Can not get the VOTable.
174 */ 174 */
175 public static VOTableData getVoTableData(VOTABLE voTable) throws VOTableException { 175 public static VOTableData getVoTableData(VOTABLE voTable) throws VOTableException {
176 - VOTableCtrl.checkVOTable(voTable);  
177 - 176 + VOTableCtrl ctrl = new VOTableCtrl();
  177 + ctrl.acquireVOTable(voTable, false);
178 Table table = (Table) voTable.getRESOURCE().get(0).getLINKAndTABLEOrRESOURCE().get(0); 178 Table table = (Table) voTable.getRESOURCE().get(0).getLINKAndTABLEOrRESOURCE().get(0);
179 - VOTableDataParser dataParser = new VOTableDataParser(table); 179 + VOTableDataParser dataParser = new VOTableDataParser("Services list", table);
180 dataParser.parseData(); 180 dataParser.parseData();
181 return dataParser.getData(); 181 return dataParser.getData();
182 } 182 }
src/main/java/eu/omp/irap/vespa/epntapclient/gui/mainpanel/MainPanelCtrl.java
@@ -16,21 +16,19 @@ @@ -16,21 +16,19 @@
16 16
17 package eu.omp.irap.vespa.epntapclient.gui.mainpanel; 17 package eu.omp.irap.vespa.epntapclient.gui.mainpanel;
18 18
19 -import java.awt.Cursor;  
20 import java.io.File; 19 import java.io.File;
  20 +import java.io.IOException;
  21 +import java.nio.file.Files;
  22 +import java.nio.file.Paths;
21 import java.util.logging.Level; 23 import java.util.logging.Level;
22 import java.util.logging.Logger; 24 import java.util.logging.Logger;
23 25
24 -import javax.swing.JFrame;  
25 import javax.swing.JOptionPane; 26 import javax.swing.JOptionPane;
26 -import javax.swing.SwingUtilities;  
27 -import javax.swing.SwingWorker;  
28 27
29 import eu.omp.irap.vespa.epntapclient.epntap.EpnTapController; 28 import eu.omp.irap.vespa.epntapclient.epntap.EpnTapController;
30 import eu.omp.irap.vespa.epntapclient.gui.requestpanel.RequestPanelCtrl; 29 import eu.omp.irap.vespa.epntapclient.gui.requestpanel.RequestPanelCtrl;
31 import eu.omp.irap.vespa.epntapclient.gui.resultpanel.ResultPanelCtrl; 30 import eu.omp.irap.vespa.epntapclient.gui.resultpanel.ResultPanelCtrl;
32 import eu.omp.irap.vespa.epntapclient.gui.servicespanel.ServicesPanelCtrl; 31 import eu.omp.irap.vespa.epntapclient.gui.servicespanel.ServicesPanelCtrl;
33 -import eu.omp.irap.vespa.votable.votable.VOTableException;  
34 32
35 /** 33 /**
36 * @author N. Jourdane 34 * @author N. Jourdane
@@ -40,9 +38,6 @@ public class MainPanelCtrl extends EpnTapController implements MainPanelListener @@ -40,9 +38,6 @@ public class MainPanelCtrl extends EpnTapController implements MainPanelListener
40 /** The logger for the class MainPanelCtrl. */ 38 /** The logger for the class MainPanelCtrl. */
41 private static final Logger LOGGER = Logger.getLogger(MainPanelCtrl.class.getName()); 39 private static final Logger LOGGER = Logger.getLogger(MainPanelCtrl.class.getName());
42 40
43 - /** The swing worker for doing a send query. */  
44 - private SwingWorker<Void, Void> sw;  
45 -  
46 /** The controller of the request panel. */ 41 /** The controller of the request panel. */
47 RequestPanelCtrl requestPanelCtrl; 42 RequestPanelCtrl requestPanelCtrl;
48 43
@@ -64,40 +59,27 @@ public class MainPanelCtrl extends EpnTapController implements MainPanelListener @@ -64,40 +59,27 @@ public class MainPanelCtrl extends EpnTapController implements MainPanelListener
64 resultsPanelCtrl = new ResultPanelCtrl(this); 59 resultsPanelCtrl = new ResultPanelCtrl(this);
65 requestPanelCtrl = new RequestPanelCtrl(this); 60 requestPanelCtrl = new RequestPanelCtrl(this);
66 view = new MainPanelView(this); 61 view = new MainPanelView(this);
67 - SwingUtilities.invokeLater(run("EPN-TAP client", view));  
68 } 62 }
69 63
70 - /**  
71 - * Creates runnable used to run the application GUI.  
72 - *  
73 - * @param title The title of the application window.  
74 - * @param view The view of the VOTable, created by the VOTableController.  
75 - * @return The runnable.  
76 - */  
77 - private static Runnable run(final String title, final MainPanelView view) {  
78 - return new Runnable() {  
79 -  
80 - @Override  
81 - public void run() {  
82 - JFrame frame = new JFrame(title);  
83 - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
84 - frame.setContentPane(view);  
85 - frame.setVisible(true);  
86 - frame.pack();  
87 - frame.setLocationRelativeTo(null);  
88 - }  
89 - }; 64 + @Override
  65 + public void acquireServices() {
  66 + servicesPanelCtrl.acquire();
90 } 67 }
91 68
92 @Override 69 @Override
93 - public void displayError(String message, Exception e) {  
94 - LOGGER.log(Level.SEVERE, message, e);  
95 - JOptionPane.showMessageDialog(view, e.getMessage(), message, JOptionPane.ERROR_MESSAGE); 70 + public void displayError(String message, Exception error) {
  71 + LOGGER.log(Level.SEVERE, message, error);
  72 + JOptionPane.showMessageDialog(view, error.getMessage(), message, JOptionPane.ERROR_MESSAGE);
96 } 73 }
97 74
98 @Override 75 @Override
99 - public void displayInfo(String message) {  
100 - view.getResultsPanel().setStatusBarText(message); 76 + public void displayInfo(String shortMessage, String detailledMessage) {
  77 + if (detailledMessage == null) {
  78 + LOGGER.info(shortMessage);
  79 + } else {
  80 + LOGGER.info(shortMessage + ": " + detailledMessage);
  81 + }
  82 + view.getStatusBarPanelView().setStatusBarText(shortMessage);
101 } 83 }
102 84
103 /** 85 /**
@@ -132,57 +114,22 @@ public class MainPanelCtrl extends EpnTapController implements MainPanelListener @@ -132,57 +114,22 @@ public class MainPanelCtrl extends EpnTapController implements MainPanelListener
132 } 114 }
133 115
134 @Override 116 @Override
135 - public void readServices() { 117 + public void saveCurrentVOTable(File file) {
136 try { 118 try {
137 - super.readServices();  
138 - } catch (VOTableException e) {  
139 - displayError("Can not read services.", e); 119 + Files.copy(Paths.get(resultsPanelCtrl.getVOTablePath()),
  120 + Paths.get(file.getAbsolutePath()));
  121 + } catch (IOException e) {
  122 + displayError("Can not save the VOTable file.", e);
140 } 123 }
141 - view.getServicesPanel().fillTable(servicesPanelCtrl.getVOTableData());  
142 } 124 }
143 125
144 @Override 126 @Override
145 - public void sendQuery() {  
146 - // Avoid multiple queries at the same time: stop the current first, then launch a new one.  
147 - if (sw != null && !sw.isDone()) {  
148 - sw.cancel(true);  
149 - }  
150 - sw = createNewWorker();  
151 - view.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));  
152 - sw.execute(); 127 + public void sendQueries() {
  128 + sendQueries(servicesPanelCtrl.getServices());
153 } 129 }
154 130
155 @Override 131 @Override
156 public void updateQuery() { 132 public void updateQuery() {
157 requestPanelCtrl.updateQuery(); 133 requestPanelCtrl.updateQuery();
158 } 134 }
159 -  
160 - /**  
161 - * Create a new {@link SwingWorker} for doing a send query.  
162 - *  
163 - * @return The created {@link SwingWorker}.  
164 - */  
165 - private SwingWorker<Void, Void> createNewWorker() {  
166 - return new SwingWorker<Void, Void>() {  
167 -  
168 - @Override  
169 - protected Void doInBackground() throws Exception {  
170 - try {  
171 - sendQueries(servicesPanelCtrl.getServices());  
172 - } catch (VOTableException e) {  
173 - displayError("Can not update the VOTable.", e);  
174 - }  
175 - return null;  
176 - }  
177 -  
178 - @Override  
179 - protected void done() {  
180 - if (!isCancelled()) {  
181 - String fName = new File(resultsPanelCtrl.getVOTablePath()).getName();  
182 - resultsPanelCtrl.getView().addTable(fName, resultsPanelCtrl.getVOTableData());  
183 - }  
184 - view.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));  
185 - }  
186 - };  
187 - }  
188 } 135 }
src/main/java/eu/omp/irap/vespa/epntapclient/gui/mainpanel/MainPanelListener.java
@@ -16,33 +16,39 @@ @@ -16,33 +16,39 @@
16 16
17 package eu.omp.irap.vespa.epntapclient.gui.mainpanel; 17 package eu.omp.irap.vespa.epntapclient.gui.mainpanel;
18 18
  19 +import java.io.File;
  20 +
19 /** 21 /**
20 * @author N. Jourdane 22 * @author N. Jourdane
21 */ 23 */
22 public interface MainPanelListener { 24 public interface MainPanelListener {
23 25
24 /** 26 /**
25 - * Ask the main panel to open a pop-up and display the specified error. 27 + * Display an error message to the user.
26 * 28 *
27 - * @param message A short message describing the error, which will be the pop-up title.  
28 - * @param e The error itself. 29 + * @param message A short version of the message.
  30 + * @param error The exception related to the error.
29 */ 31 */
30 - void displayError(String message, Exception e); 32 + void displayError(String message, Exception error);
31 33
32 /** 34 /**
33 - * Display an informative message in the status bar. 35 + * Display an informative message to the user.
34 * 36 *
35 - * @param message The message to display. 37 + * @param shortMessage A short version of the message.
  38 + * @param detailledMessage The full message to display (optional, can be null).
36 */ 39 */
37 - void displayInfo(String message); 40 + void displayInfo(String shortMessage, String detailledMessage);
38 41
39 /** 42 /**
40 - * Ask the main panel to send the query. 43 + * Ask the main panel to save the VOTable displayed in the focused tab on the result panel.
  44 + *
  45 + * @param file The file specified by the user on the FileChooser popup.
41 */ 46 */
42 - void sendQuery(); 47 + void saveCurrentVOTable(File file);
43 48
44 - /**  
45 - * Ask the main panel to update the query.  
46 - */ 49 + /** Ask the main panel to send the query. */
  50 + void sendQueries();
  51 +
  52 + /** Ask the main panel to update the query. */
47 void updateQuery(); 53 void updateQuery();
48 } 54 }
src/main/java/eu/omp/irap/vespa/epntapclient/gui/mainpanel/MainPanelView.java
@@ -26,6 +26,7 @@ import javax.swing.JSplitPane; @@ -26,6 +26,7 @@ import javax.swing.JSplitPane;
26 import eu.omp.irap.vespa.epntapclient.gui.requestpanel.RequestPanelView; 26 import eu.omp.irap.vespa.epntapclient.gui.requestpanel.RequestPanelView;
27 import eu.omp.irap.vespa.epntapclient.gui.resultpanel.ResultPanelView; 27 import eu.omp.irap.vespa.epntapclient.gui.resultpanel.ResultPanelView;
28 import eu.omp.irap.vespa.epntapclient.gui.servicespanel.ServicesPanelView; 28 import eu.omp.irap.vespa.epntapclient.gui.servicespanel.ServicesPanelView;
  29 +import eu.omp.irap.vespa.epntapclient.gui.statusbarpanel.StatusBarPanelView;
29 30
30 /** 31 /**
31 * The main view of the application, which manage all the other views. 32 * The main view of the application, which manage all the other views.
@@ -34,7 +35,7 @@ import eu.omp.irap.vespa.epntapclient.gui.servicespanel.ServicesPanelView; @@ -34,7 +35,7 @@ import eu.omp.irap.vespa.epntapclient.gui.servicespanel.ServicesPanelView;
34 */ 35 */
35 public class MainPanelView extends JPanel { 36 public class MainPanelView extends JPanel {
36 37
37 - /** The serial version UID. */ 38 + /** The default serial version UID. */
38 private static final long serialVersionUID = 1L; 39 private static final long serialVersionUID = 1L;
39 40
40 /** The JPanel where the user build the query. */ 41 /** The JPanel where the user build the query. */
@@ -46,6 +47,9 @@ public class MainPanelView extends JPanel { @@ -46,6 +47,9 @@ public class MainPanelView extends JPanel {
46 /** The JPanel where the list of services is displayed. */ 47 /** The JPanel where the list of services is displayed. */
47 private ServicesPanelView servicesPanel; 48 private ServicesPanelView servicesPanel;
48 49
  50 + /** The JPanel where the list of services is displayed. */
  51 + private StatusBarPanelView statusBarPanel;
  52 +
49 53
50 /** 54 /**
51 * The main view constructor, which create all the panels. 55 * The main view constructor, which create all the panels.
@@ -57,6 +61,7 @@ public class MainPanelView extends JPanel { @@ -57,6 +61,7 @@ public class MainPanelView extends JPanel {
57 servicesPanel = mainPanelCtrl.getServicesCtrl().getView(); 61 servicesPanel = mainPanelCtrl.getServicesCtrl().getView();
58 resultPanel = mainPanelCtrl.getResultsCtrl().getView(); 62 resultPanel = mainPanelCtrl.getResultsCtrl().getView();
59 requestPanel = mainPanelCtrl.getRequestCtrl().getView(); 63 requestPanel = mainPanelCtrl.getRequestCtrl().getView();
  64 + statusBarPanel = new StatusBarPanelView();
60 buildMainView(); 65 buildMainView();
61 } 66 }
62 67
@@ -67,8 +72,7 @@ public class MainPanelView extends JPanel { @@ -67,8 +72,7 @@ public class MainPanelView extends JPanel {
67 * @param message The message of the error. 72 * @param message The message of the error.
68 */ 73 */
69 public void displayError(String title, String message) { 74 public void displayError(String title, String message) {
70 - JOptionPane.showMessageDialog(this, message, title,  
71 - JOptionPane.ERROR_MESSAGE); 75 + JOptionPane.showMessageDialog(this, message, title, JOptionPane.ERROR_MESSAGE);
72 } 76 }
73 77
74 /** 78 /**
@@ -93,6 +97,13 @@ public class MainPanelView extends JPanel { @@ -93,6 +97,13 @@ public class MainPanelView extends JPanel {
93 } 97 }
94 98
95 /** 99 /**
  100 + * @return The view of the status bar panel.
  101 + */
  102 + public StatusBarPanelView getStatusBarPanelView() {
  103 + return statusBarPanel;
  104 + }
  105 +
  106 + /**
96 * Build the panel and add GUI elements on it. 107 * Build the panel and add GUI elements on it.
97 */ 108 */
98 private void buildMainView() { 109 private void buildMainView() {
@@ -102,6 +113,7 @@ public class MainPanelView extends JPanel { @@ -102,6 +113,7 @@ public class MainPanelView extends JPanel {
102 requestPanel); 113 requestPanel);
103 JSplitPane mainPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, northPanel, resultPanel); 114 JSplitPane mainPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, northPanel, resultPanel);
104 add(mainPanel, BorderLayout.CENTER); 115 add(mainPanel, BorderLayout.CENTER);
  116 + add(statusBarPanel, BorderLayout.SOUTH);
105 117
106 setSizes(); 118 setSizes();
107 revalidate(); 119 revalidate();
src/main/java/eu/omp/irap/vespa/epntapclient/gui/requestpanel/RequestPanelCtrl.java
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 16
17 package eu.omp.irap.vespa.epntapclient.gui.requestpanel; 17 package eu.omp.irap.vespa.epntapclient.gui.requestpanel;
18 18
  19 +import java.io.File;
  20 +
19 import eu.omp.irap.vespa.epntapclient.epntap.request.RequestCtrl; 21 import eu.omp.irap.vespa.epntapclient.epntap.request.RequestCtrl;
20 import eu.omp.irap.vespa.epntapclient.gui.mainpanel.MainPanelListener; 22 import eu.omp.irap.vespa.epntapclient.gui.mainpanel.MainPanelListener;
21 23
@@ -62,9 +64,14 @@ public class RequestPanelCtrl extends RequestCtrl implements RequestPanelListene @@ -62,9 +64,14 @@ public class RequestPanelCtrl extends RequestCtrl implements RequestPanelListene
62 } 64 }
63 65
64 @Override 66 @Override
  67 + public void onSaveButtonClicked(File file) {
  68 + listener.saveCurrentVOTable(file);
  69 + }
  70 +
  71 + @Override
65 public void onSendButtonClicked() { 72 public void onSendButtonClicked() {
66 setQuery(view.getQueryArea().getText()); 73 setQuery(view.getQueryArea().getText());
67 - listener.sendQuery(); 74 + listener.sendQueries();
68 } 75 }
69 76
70 /** 77 /**
src/main/java/eu/omp/irap/vespa/epntapclient/gui/requestpanel/RequestPanelListener.java
@@ -16,20 +16,23 @@ @@ -16,20 +16,23 @@
16 16
17 package eu.omp.irap.vespa.epntapclient.gui.requestpanel; 17 package eu.omp.irap.vespa.epntapclient.gui.requestpanel;
18 18
  19 +import java.io.File;
  20 +
19 /** 21 /**
20 * @author N. Jourdane 22 * @author N. Jourdane
21 */ 23 */
22 public interface RequestPanelListener { 24 public interface RequestPanelListener {
23 25
24 /** 26 /**
25 - * Method called when the user remove a parameter with a parameter field. 27 + * This method is called when the user remove a parameter with a parameter field.
26 * 28 *
27 * @param paramName The name of the removed parameter 29 * @param paramName The name of the removed parameter
28 */ 30 */
29 void onParameterRemoved(String paramName); 31 void onParameterRemoved(String paramName);
30 32
31 /** 33 /**
32 - * Method called when the user change a parameter (add or update) with a parameter field. 34 + * This method is called when the user change a parameter (add or update) with a parameter
  35 + * field.
33 * 36 *
34 * @param paramName The name of the changed parameter. 37 * @param paramName The name of the changed parameter.
35 * @param paramValue The new value of the parameter. 38 * @param paramValue The new value of the parameter.
@@ -37,7 +40,15 @@ public interface RequestPanelListener { @@ -37,7 +40,15 @@ public interface RequestPanelListener {
37 void onParameterUpdated(String paramName, Object paramValue); 40 void onParameterUpdated(String paramName, Object paramValue);
38 41
39 /** 42 /**
40 - * Method called when the used click on the 'Send query' button. 43 + * This method is called when the user clicks on the download button.
  44 + *
  45 + * @param file The file selected by the user in the FileChooser pop-up, corresponding to the
  46 + * place where save the VOTable.
  47 + */
  48 + void onSaveButtonClicked(File file);
  49 +
  50 + /**
  51 + * This method is called when the used click on the 'Send query' button.
41 */ 52 */
42 void onSendButtonClicked(); 53 void onSendButtonClicked();
43 } 54 }
src/main/java/eu/omp/irap/vespa/epntapclient/gui/requestpanel/RequestPanelView.java
@@ -26,6 +26,7 @@ import java.util.List; @@ -26,6 +26,7 @@ import java.util.List;
26 import javax.swing.BorderFactory; 26 import javax.swing.BorderFactory;
27 import javax.swing.BoxLayout; 27 import javax.swing.BoxLayout;
28 import javax.swing.JButton; 28 import javax.swing.JButton;
  29 +import javax.swing.JFileChooser;
29 import javax.swing.JPanel; 30 import javax.swing.JPanel;
30 import javax.swing.JTextArea; 31 import javax.swing.JTextArea;
31 32
@@ -45,9 +46,12 @@ public class RequestPanelView extends JPanel { @@ -45,9 +46,12 @@ public class RequestPanelView extends JPanel {
45 /** The height of the buttons panel. */ 46 /** The height of the buttons panel. */
46 private static final int BUTTON_PANEL_HEIGHT = 20; 47 private static final int BUTTON_PANEL_HEIGHT = 20;
47 48
48 - /** The serial version UID. */ 49 + /** The default serial version UID. */
49 private static final long serialVersionUID = 1L; 50 private static final long serialVersionUID = 1L;
50 51
  52 + /** The GUI element of the button to move the file in the specified location. */
  53 + private JButton buttonSave;
  54 +
51 /** The GUI element of the button to send the query. */ 55 /** The GUI element of the button to send the query. */
52 private JButton buttonSend; 56 private JButton buttonSend;
53 57
@@ -86,7 +90,7 @@ public class RequestPanelView extends JPanel { @@ -86,7 +90,7 @@ public class RequestPanelView extends JPanel {
86 /** 90 /**
87 * Get the GUI element of the send button. If it doesn't exist, create it. 91 * Get the GUI element of the send button. If it doesn't exist, create it.
88 * 92 *
89 - * @return The button. 93 + * @return The button to download and parse the VOTable.
90 */ 94 */
91 public JButton getButtonSend() { 95 public JButton getButtonSend() {
92 if (buttonSend == null) { 96 if (buttonSend == null) {
@@ -160,9 +164,9 @@ public class RequestPanelView extends JPanel { @@ -160,9 +164,9 @@ public class RequestPanelView extends JPanel {
160 } 164 }
161 165
162 /** 166 /**
163 - * Get the GUI element of the target name field. If it doesn't exist, create it. 167 + * Get the field where the user enter the target name. If it doesn't exist, create it.
164 * 168 *
165 - * @return The target name field. 169 + * @return The TargetNameField object of the field.
166 */ 170 */
167 public TargetNameField getTargetNameField() { 171 public TargetNameField getTargetNameField() {
168 if (targetNameField == null) { 172 if (targetNameField == null) {
@@ -186,6 +190,27 @@ public class RequestPanelView extends JPanel { @@ -186,6 +190,27 @@ public class RequestPanelView extends JPanel {
186 } 190 }
187 191
188 /** 192 /**
  193 + * Get the button to save the VOTable in the specified location. If it doesn't exist, create it.
  194 + *
  195 + * @return The JButton object.
  196 + */
  197 + public JButton saveFileButton() {
  198 + if (buttonSave == null) {
  199 + buttonSave = new JButton("Save File");
  200 + buttonSave.addActionListener(new ActionListener() {
  201 +
  202 + @Override
  203 + public void actionPerformed(ActionEvent evt) {
  204 + final JFileChooser fc = new JFileChooser();
  205 + fc.showOpenDialog(RequestPanelView.this);
  206 + listener.onSaveButtonClicked(fc.getSelectedFile());
  207 + }
  208 + });
  209 + }
  210 + return buttonSave;
  211 + }
  212 +
  213 + /**
189 * Update the query in the JTextArea. 214 * Update the query in the JTextArea.
190 * 215 *
191 * @param query The string literal to put in the text-area, which will override the old content. 216 * @param query The string literal to put in the text-area, which will override the old content.
src/main/java/eu/omp/irap/vespa/epntapclient/gui/requestpanel/paramfield/TargetNameField.java
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 package eu.omp.irap.vespa.epntapclient.gui.requestpanel.paramfield; 17 package eu.omp.irap.vespa.epntapclient.gui.requestpanel.paramfield;
18 18
19 import java.awt.Dimension; 19 import java.awt.Dimension;
  20 +import java.io.File;
20 import java.util.logging.Level; 21 import java.util.logging.Level;
21 import java.util.logging.Logger; 22 import java.util.logging.Logger;
22 23
@@ -93,6 +94,11 @@ public class TargetNameField extends ParamField implements TextFieldListener { @@ -93,6 +94,11 @@ public class TargetNameField extends ParamField implements TextFieldListener {
93 } 94 }
94 95
95 @Override 96 @Override
  97 + public void onSaveButtonClicked(File file) {
  98 + /** No SaveButtonClicked event, we just want the field itself. */
  99 + }
  100 +
  101 + @Override
96 public void onSendButtonClicked() { 102 public void onSendButtonClicked() {
97 /** No SendButtonClicked event, we just want the field itself. */ 103 /** No SendButtonClicked event, we just want the field itself. */
98 } 104 }
src/main/java/eu/omp/irap/vespa/epntapclient/gui/resultpanel/ResultPanelCtrl.java
@@ -16,16 +16,12 @@ @@ -16,16 +16,12 @@
16 16
17 package eu.omp.irap.vespa.epntapclient.gui.resultpanel; 17 package eu.omp.irap.vespa.epntapclient.gui.resultpanel;
18 18
19 -import java.io.File;  
20 -import java.io.IOException;  
21 -import java.nio.file.Files;  
22 -import java.nio.file.Paths; 19 +import java.awt.Cursor;
23 import java.util.logging.Logger; 20 import java.util.logging.Logger;
24 21
25 import eu.omp.irap.vespa.epntapclient.gui.mainpanel.MainPanelListener; 22 import eu.omp.irap.vespa.epntapclient.gui.mainpanel.MainPanelListener;
26 import eu.omp.irap.vespa.votable.utils.StringJoiner; 23 import eu.omp.irap.vespa.votable.utils.StringJoiner;
27 import eu.omp.irap.vespa.votable.votable.VOTableCtrl; 24 import eu.omp.irap.vespa.votable.votable.VOTableCtrl;
28 -import eu.omp.irap.vespa.votable.votable.VOTableException;  
29 25
30 /** 26 /**
31 * @author N. Jourdane 27 * @author N. Jourdane
@@ -52,41 +48,38 @@ public class ResultPanelCtrl extends VOTableCtrl implements ResultPanelListener @@ -52,41 +48,38 @@ public class ResultPanelCtrl extends VOTableCtrl implements ResultPanelListener
52 view = new ResultPanelView(this); 48 view = new ResultPanelView(this);
53 } 49 }
54 50
55 - /** Download and parse a VOTable, then add the result to the current tab in the result panel. */  
56 @Override 51 @Override
57 - public void appendVOTable(String newTargetURL, String newQuery) {  
58 - try {  
59 - super.appendVOTable(newTargetURL, newQuery);  
60 - } catch (VOTableException e) {  
61 - listener.displayError("Can not update the VOTable.", e);  
62 - }  
63 - view.updateTable(voTableData); 52 + public void displayError(String message, Exception error) {
  53 + listener.displayError(message, error);
64 } 54 }
65 55
66 @Override 56 @Override
67 public void displayInfo(String shortMessage, String detailledMessage) { 57 public void displayInfo(String shortMessage, String detailledMessage) {
68 - super.displayInfo(shortMessage, detailledMessage);  
69 - listener.displayInfo(shortMessage); 58 + listener.displayInfo(shortMessage, detailledMessage);
  59 + }
  60 +
  61 + @Override
  62 + public void fillTable() {
  63 + view.addTable(voTableData);
70 } 64 }
71 65
72 /** 66 /**
73 - * @return The view of the result panel. Used in MainPanelCtrl to add panels in the main window. 67 + * @return The result panel view.
74 */ 68 */
75 public ResultPanelView getView() { 69 public ResultPanelView getView() {
76 return view; 70 return view;
77 } 71 }
78 72
79 @Override 73 @Override
80 - public void onDownloadButtonClicked(File file) {  
81 - try {  
82 - Files.copy(Paths.get(voTablePath), Paths.get(file.getAbsolutePath()));  
83 - } catch (IOException e) {  
84 - listener.displayError("Can not save the VOTable file.", e); 74 + public void onRowsSelected() {
  75 + if (view.getSelectedIndex() != -1) {
  76 + LOGGER.info("Selected row(s): " + StringJoiner.join(view.getSelectedRows()));
85 } 77 }
86 } 78 }
87 79
88 @Override 80 @Override
89 - public void onRowsSelected() {  
90 - LOGGER.info("Selected row: " + StringJoiner.join(view.getSelectedRows())); 81 + public void setWaitCursor(boolean enableWaitcursor) {
  82 + int cursor = enableWaitcursor ? Cursor.WAIT_CURSOR : Cursor.DEFAULT_CURSOR;
  83 + view.setCursor(Cursor.getPredefinedCursor(cursor));
91 } 84 }
92 } 85 }
src/main/java/eu/omp/irap/vespa/epntapclient/gui/resultpanel/ResultPanelListener.java
@@ -16,8 +16,6 @@ @@ -16,8 +16,6 @@
16 16
17 package eu.omp.irap.vespa.epntapclient.gui.resultpanel; 17 package eu.omp.irap.vespa.epntapclient.gui.resultpanel;
18 18
19 -import java.io.File;  
20 -  
21 import eu.omp.irap.vespa.votable.gui.VOTablePanelListener; 19 import eu.omp.irap.vespa.votable.gui.VOTablePanelListener;
22 20
23 /** 21 /**
@@ -25,11 +23,4 @@ import eu.omp.irap.vespa.votable.gui.VOTablePanelListener; @@ -25,11 +23,4 @@ import eu.omp.irap.vespa.votable.gui.VOTablePanelListener;
25 */ 23 */
26 public interface ResultPanelListener extends VOTablePanelListener { 24 public interface ResultPanelListener extends VOTablePanelListener {
27 25
28 - /**  
29 - * This method is called when the user clicks on the download button.  
30 - *  
31 - * @param file The file selected by the user in the FileChooser pop-up, corresponding to the  
32 - * place where save the VOTable.  
33 - */  
34 - public void onDownloadButtonClicked(File file);  
35 } 26 }
src/main/java/eu/omp/irap/vespa/epntapclient/gui/resultpanel/ResultPanelView.java
@@ -16,43 +16,21 @@ @@ -16,43 +16,21 @@
16 16
17 package eu.omp.irap.vespa.epntapclient.gui.resultpanel; 17 package eu.omp.irap.vespa.epntapclient.gui.resultpanel;
18 18
19 -import java.awt.BorderLayout;  
20 -import java.awt.event.ActionEvent;  
21 -import java.awt.event.ActionListener;  
22 -import java.util.ArrayList;  
23 import java.util.List; 19 import java.util.List;
24 20
25 -import javax.swing.JButton;  
26 -import javax.swing.JFileChooser;  
27 -import javax.swing.JLabel;  
28 -import javax.swing.JPanel;  
29 import javax.swing.JTabbedPane; 21 import javax.swing.JTabbedPane;
30 22
31 -import eu.omp.irap.vespa.votable.gui.VOTablePanelListener;  
32 import eu.omp.irap.vespa.votable.gui.VOTablePanelView; 23 import eu.omp.irap.vespa.votable.gui.VOTablePanelView;
33 import eu.omp.irap.vespa.votable.votabledata.VOTableData; 24 import eu.omp.irap.vespa.votable.votabledata.VOTableData;
34 25
35 /** 26 /**
36 * @author N. Jourdane 27 * @author N. Jourdane
37 */ 28 */
38 -public class ResultPanelView extends JPanel implements VOTablePanelListener { 29 +public class ResultPanelView extends JTabbedPane {
39 30
40 - /** The serial version UID. */ 31 + /** The default serial version UID. */
41 private static final long serialVersionUID = 1L; 32 private static final long serialVersionUID = 1L;
42 33
43 - /** The JPanel containing the buttons. */  
44 - private JPanel buttonsPanel;  
45 -  
46 - /** The GUI element of the button to save the result of the query. */  
47 - private JButton fileButton;  
48 -  
49 - /** A status bar, to display several informative messages. */  
50 - private JLabel statusBar;  
51 -  
52 - private JTabbedPane tabbedPane;  
53 -  
54 - private List<VOTablePanelView> tablePanels;  
55 -  
56 /** The listener of the result panel. */ 34 /** The listener of the result panel. */
57 ResultPanelListener listener; 35 ResultPanelListener listener;
58 36
@@ -65,7 +43,6 @@ public class ResultPanelView extends JPanel implements VOTablePanelListener { @@ -65,7 +43,6 @@ public class ResultPanelView extends JPanel implements VOTablePanelListener {
65 */ 43 */
66 public ResultPanelView(ResultPanelListener listener) { 44 public ResultPanelView(ResultPanelListener listener) {
67 this.listener = listener; 45 this.listener = listener;
68 - tablePanels = new ArrayList<>();  
69 buildResultPanel(); 46 buildResultPanel();
70 } 47 }
71 48
@@ -74,63 +51,24 @@ public class ResultPanelView extends JPanel implements VOTablePanelListener { @@ -74,63 +51,24 @@ public class ResultPanelView extends JPanel implements VOTablePanelListener {
74 * 51 *
75 * @param voTableData The VOTable data to add in a new tab. 52 * @param voTableData The VOTable data to add in a new tab.
76 */ 53 */
77 - public void addTable(String title, VOTableData voTableData) {  
78 - VOTablePanelView voTablePanel = new VOTablePanelView(this); 54 + public void addTable(VOTableData voTableData) {
  55 + VOTablePanelView voTablePanel = new VOTablePanelView(listener);
79 voTablePanel.fillTable(voTableData); 56 voTablePanel.fillTable(voTableData);
80 - tablePanels.add(voTablePanel);  
81 - tabbedPane.add(title, voTablePanel); 57 + addTab(voTableData.getTitle(), voTablePanel);
82 } 58 }
83 59
84 /** 60 /**
85 * Build the panel and add graphical elements to it. 61 * Build the panel and add graphical elements to it.
86 */ 62 */
87 public void buildResultPanel() { 63 public void buildResultPanel() {
88 - tabbedPane = new JTabbedPane();  
89 - JPanel bottomBar = new JPanel();  
90 - bottomBar.setLayout(new BorderLayout());  
91 - bottomBar.add(getStatusBar(), BorderLayout.CENTER);  
92 - bottomBar.add(getButtonsPanel(), BorderLayout.EAST);  
93 -  
94 - add(tabbedPane, BorderLayout.CENTER);  
95 - add(bottomBar, BorderLayout.SOUTH); 64 + // setLayout(new BorderLayout());
96 } 65 }
97 66
98 /** 67 /**
99 - * Create if necessary the buttons panel, then return it.  
100 - *  
101 - * @return the buttons panel. 68 + * @return The panel inside the current active tab.
102 */ 69 */
103 - public JPanel getButtonsPanel() {  
104 - if (buttonsPanel == null) {  
105 - buttonsPanel = new JPanel();  
106 - buttonsPanel.add(getFileButton());  
107 - }  
108 - return buttonsPanel;  
109 - }  
110 -  
111 public VOTablePanelView getCurrentTablePanel() { 70 public VOTablePanelView getCurrentTablePanel() {
112 - return tablePanels.get(getSelectedTab());  
113 - }  
114 -  
115 - /**  
116 - * Returns the button to save the VOTable, create it if doesn't exist.  
117 - *  
118 - * @return The button to save the VOTable.  
119 - */  
120 - public JButton getFileButton() {  
121 - if (fileButton == null) {  
122 - fileButton = new JButton("Get File");  
123 - fileButton.addActionListener(new ActionListener() {  
124 -  
125 - @Override  
126 - public void actionPerformed(ActionEvent evt) {  
127 - final JFileChooser fc = new JFileChooser();  
128 - fc.showOpenDialog(ResultPanelView.this);  
129 - listener.onDownloadButtonClicked(fc.getSelectedFile());  
130 - }  
131 - });  
132 - }  
133 - return fileButton; 71 + return (VOTablePanelView) getComponentAt(getSelectedTab());
134 } 72 }
135 73
136 /** 74 /**
@@ -144,31 +82,7 @@ public class ResultPanelView extends JPanel implements VOTablePanelListener { @@ -144,31 +82,7 @@ public class ResultPanelView extends JPanel implements VOTablePanelListener {
144 * @return The index of the selected tab. 82 * @return The index of the selected tab.
145 */ 83 */
146 public int getSelectedTab() { 84 public int getSelectedTab() {
147 - return tabbedPane.getSelectedIndex();  
148 - }  
149 -  
150 - /**  
151 - * Returns the status bar, create it if doesn't exist.  
152 - *  
153 - * @return The status bar.  
154 - */  
155 - public JLabel getStatusBar() {  
156 - if (statusBar == null) {  
157 - statusBar = new JLabel("");  
158 - }  
159 - return statusBar;  
160 - }  
161 -  
162 - @Override  
163 - public void onRowsSelected() {  
164 - // Do nothing yet when a row is selected.  
165 - }  
166 -  
167 - /**  
168 - * @param infoText The text to display in the status-bar, which will override the old one.  
169 - */  
170 - public void setStatusBarText(String infoText) {  
171 - getStatusBar().setText(infoText); 85 + return getSelectedIndex();
172 } 86 }
173 87
174 /** 88 /**
src/main/java/eu/omp/irap/vespa/epntapclient/gui/servicespanel/ServicesPanelCtrl.java
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 16
17 package eu.omp.irap.vespa.epntapclient.gui.servicespanel; 17 package eu.omp.irap.vespa.epntapclient.gui.servicespanel;
18 18
  19 +import java.awt.Cursor;
19 import java.util.ArrayList; 20 import java.util.ArrayList;
20 import java.util.Arrays; 21 import java.util.Arrays;
21 import java.util.List; 22 import java.util.List;
@@ -53,15 +54,31 @@ public class ServicesPanelCtrl extends VOTableCtrl implements ServicesPanelListe @@ -53,15 +54,31 @@ public class ServicesPanelCtrl extends VOTableCtrl implements ServicesPanelListe
53 public ServicesPanelCtrl(MainPanelListener listener) { 54 public ServicesPanelCtrl(MainPanelListener listener) {
54 this.listener = listener; 55 this.listener = listener;
55 services = new ServicesList(); 56 services = new ServicesList();
56 - targetUrl = Consts.DEFAULT_REGISTRY_URL;  
57 - query = String.format(Queries.SELECT_ALL_TAP_SERVICES_WHERE_CORE, ServiceCore.EPNCORE);  
58 view = new ServicesPanelView(this); 57 view = new ServicesPanelView(this);
59 } 58 }
60 59
  60 + /**
  61 + * Download and parse the list of services.
  62 + */
  63 + public void acquire() {
  64 + String getServicesQuery = String.format(Queries.SELECT_ALL_TAP_SERVICES_WHERE_CORE,
  65 + ServiceCore.EPNCORE);
  66 + acquireVOTable(Consts.DEFAULT_REGISTRY_URL, getServicesQuery, false);
  67 + }
  68 +
  69 + @Override
  70 + public void displayError(String message, Exception error) {
  71 + listener.displayError(message, error);
  72 + }
  73 +
61 @Override 74 @Override
62 public void displayInfo(String shortMessage, String detailledMessage) { 75 public void displayInfo(String shortMessage, String detailledMessage) {
63 - super.displayInfo(shortMessage, detailledMessage);  
64 - listener.displayInfo(shortMessage); 76 + listener.displayInfo(shortMessage, detailledMessage);
  77 + }
  78 +
  79 + @Override
  80 + public void fillTable() {
  81 + view.fillTable(voTableData);
65 } 82 }
66 83
67 /** 84 /**
@@ -99,8 +116,10 @@ public class ServicesPanelCtrl extends VOTableCtrl implements ServicesPanelListe @@ -99,8 +116,10 @@ public class ServicesPanelCtrl extends VOTableCtrl implements ServicesPanelListe
99 public void onServiceListUpdated() { 116 public void onServiceListUpdated() {
100 String newTableName = view.getTableNameTextField().getText(); 117 String newTableName = view.getTableNameTextField().getText();
101 String newTargetUrl = view.getServiceUrlTextField().getText(); 118 String newTargetUrl = view.getServiceUrlTextField().getText();
102 - List<String> customTableNames = Arrays.asList(newTableName.split(CUSTOM_SERVICES_SEPARATOR));  
103 - List<String> customServicesUrls = Arrays.asList(newTargetUrl.split(CUSTOM_SERVICES_SEPARATOR)); 119 + List<String> customTableNames = Arrays
  120 + .asList(newTableName.split(CUSTOM_SERVICES_SEPARATOR));
  121 + List<String> customServicesUrls = Arrays
  122 + .asList(newTargetUrl.split(CUSTOM_SERVICES_SEPARATOR));
104 123
105 if (!newTableName.isEmpty() && !newTargetUrl.isEmpty() 124 if (!newTableName.isEmpty() && !newTargetUrl.isEmpty()
106 && customTableNames.size() == customServicesUrls.size()) { 125 && customTableNames.size() == customServicesUrls.size()) {
@@ -108,4 +127,10 @@ public class ServicesPanelCtrl extends VOTableCtrl implements ServicesPanelListe @@ -108,4 +127,10 @@ public class ServicesPanelCtrl extends VOTableCtrl implements ServicesPanelListe
108 } 127 }
109 listener.updateQuery(); 128 listener.updateQuery();
110 } 129 }
  130 +
  131 + @Override
  132 + public void setWaitCursor(boolean enableWaitcursor) {
  133 + int cursor = enableWaitcursor ? Cursor.WAIT_CURSOR : Cursor.DEFAULT_CURSOR;
  134 + view.setCursor(Cursor.getPredefinedCursor(cursor));
  135 + }
111 } 136 }