JAL-3878 Create proper menu for interactive services.
[jalview.git] / src / jalview / ws2 / operations / AnnotationOperation.java
1 package jalview.ws2.operations;
2
3 import java.util.Collections;
4 import java.util.List;
5 import java.util.concurrent.CompletionStage;
6
7 import javax.swing.JCheckBoxMenuItem;
8 import javax.swing.JMenu;
9 import javax.swing.JMenuItem;
10 import javax.swing.event.MenuEvent;
11 import javax.swing.event.MenuListener;
12
13 import jalview.api.AlignCalcManagerI2;
14 import jalview.api.AlignmentViewPanel;
15 import jalview.datamodel.AlignmentAnnotation;
16 import jalview.gui.AlignFrame;
17 import jalview.gui.AlignViewport;
18 import jalview.gui.WsJobParameters;
19 import jalview.io.FeaturesFile;
20 import jalview.util.Format;
21 import jalview.util.MessageManager;
22 import jalview.ws.params.ArgumentI;
23 import jalview.ws.params.WsParamSetI;
24 import jalview.ws2.MenuEntryProviderI;
25 import jalview.ws2.ResultSupplier;
26 import jalview.ws2.PollingTaskExecutor;
27 import jalview.ws2.WebServiceI;
28
29 /**
30  *
31  * @author mmwarowny
32  *
33  */
34 public class AnnotationOperation implements Operation
35 {
36   final WebServiceI service;
37
38   final String typeName;
39
40   final ResultSupplier<List<AlignmentAnnotation>> annotationSupplier;
41
42   final ResultSupplier<FeaturesFile> featuresSupplier;
43
44   boolean alignmentAnalysis = false;
45
46   boolean interactive = false;
47
48   /*
49    * Is it fine to get rid of AlignAnalysisUIText?
50    */
51
52   public AnnotationOperation(WebServiceI service,
53       ResultSupplier<List<AlignmentAnnotation>> annotSupplier,
54       ResultSupplier<FeaturesFile> featSupplier, String operationName)
55   {
56     this.service = service;
57     this.annotationSupplier = annotSupplier;
58     this.featuresSupplier = featSupplier;
59     this.typeName = operationName;
60   }
61
62   @Override
63   public String getName()
64   {
65     return service.getName();
66   }
67
68   @Override
69   public String getTypeName()
70   {
71     return typeName;
72   }
73
74   @Override
75   public String getHostName()
76   {
77     return service.getHostName();
78   }
79
80   @Override
81   public int getMinSequences()
82   {
83     return 0;
84   }
85
86   @Override
87   public int getMaxSequences()
88   {
89     return Integer.MAX_VALUE;
90   }
91
92   @Override
93   public boolean canSubmitGaps()
94   {
95     return false;
96   }
97
98   @Override
99   public boolean isProteinOperation()
100   {
101     return true;
102   }
103
104   @Override
105   public boolean isNucleotideOperation()
106   {
107     return true;
108   }
109
110   @Override
111   public boolean isInteractive()
112   {
113     return interactive;
114   }
115
116   public void setInteractive(boolean value)
117   {
118     this.interactive = value;
119   }
120
121   @Override
122   public boolean isAlignmentAnalysis()
123   {
124     return alignmentAnalysis;
125   }
126
127   public void setAlignmentAnalysis(boolean value)
128   {
129     this.alignmentAnalysis = value;
130   }
131
132   @Override
133   public boolean getFilterNonStandardSymbols()
134   {
135     return false;
136   }
137
138   @Override
139   public boolean getNeedsAlignedSequences()
140   {
141     return false;
142   }
143
144   @Override
145   public MenuEntryProviderI getMenuBuilder()
146   {
147     if (isInteractive())
148       return this::buildInteractiveMenu;
149     else
150       return this::buildClassicMenu;
151   }
152
153   protected void buildClassicMenu(JMenu parent, AlignFrame frame)
154   {
155     final var calcName = service.getName();
156     PollingTaskExecutor wsExecutor = frame.getViewport().getWSExecutor();
157     final var calcManager = frame.getViewport().getCalcManager();
158     {
159       var item = new JMenuItem(MessageManager.formatMessage(
160           "label.calcname_with_default_settings", calcName));
161       item.addActionListener((event) -> {
162         var worker = createWorker(Collections.emptyList(), frame, calcManager);
163         calcManager.startWorker(worker);
164       });
165       parent.add(item);
166     }
167     if (service.hasParameters())
168     {
169       var item = new JMenuItem(
170           MessageManager.getString("label.edit_settings_and_run"));
171       item.setToolTipText(MessageManager.getString(
172           "label.view_and_change_parameters_before_running_calculation"));
173       item.addActionListener((event) -> {
174         openEditParamsDialog(service, null, null)
175             .thenAcceptAsync((arguments) -> {
176               if (arguments != null)
177               {
178                 var worker = createWorker(arguments, frame, calcManager);
179                 calcManager.startWorker(worker);
180               }
181             });
182       });
183       parent.add(item);
184     }
185   }
186
187   protected void buildInteractiveMenu(JMenu parent, AlignFrame frame)
188   {
189     final var calcName = service.getName();
190     final var calcManager = frame.getViewport().getCalcManager();
191     final JCheckBoxMenuItem runItem;
192     {
193       // TODO use MessageManager and set tool tip text
194       runItem = new JCheckBoxMenuItem(
195           String.format("%s calculations", calcName));
196       runItem.addActionListener((event) -> {
197         calcManager.removeWorkersForName(calcName);
198         var worker = createWorker(Collections.emptyList(), frame, calcManager);
199         calcManager.registerWorker(worker);
200       });
201       parent.add(runItem);
202     }
203     JMenuItem _editItem = null;
204     if (service.hasParameters())
205     {
206       // TODO use MessageManager and set tool tip text
207       _editItem = new JMenuItem(
208           String.format("Edit %s settings", calcName));
209       _editItem.addActionListener((event) -> {
210         openEditParamsDialog(service, null, null)
211             .thenAcceptAsync((arguments) -> {
212               if (arguments != null)
213               {
214                 calcManager.removeWorkersForName(calcName);
215                 var worker = createWorker(arguments, frame, calcManager);
216                 calcManager.registerWorker(worker);
217               }
218             });
219       });
220       parent.add(_editItem);
221     }
222     final var editItem = _editItem;
223
224     parent.addMenuListener(new MenuListener()
225       {
226         @Override
227         public void menuSelected(MenuEvent e)
228         {
229           var isNuc = frame.getViewport().getAlignment().isNucleotide();
230           var menuEnabled = (isNuc && isNucleotideOperation()) ||
231               (!isNuc && isProteinOperation());
232           runItem.setEnabled(menuEnabled);
233           if (editItem != null)
234             editItem.setEnabled(menuEnabled);
235           boolean currentlyRunning = calcManager.getWorkersForName(calcName).size() > 0;
236           runItem.setSelected(currentlyRunning);
237         }
238
239         @Override
240         public void menuDeselected(MenuEvent e) {}
241
242         @Override
243         public void menuCanceled(MenuEvent e) {}
244       });
245   }
246
247   private AnnotationServiceWorker createWorker(
248       List<ArgumentI> arguments, AlignFrame frame, AlignCalcManagerI2 calcManager)
249   {
250     /* What is the purpose of AlignViewport and AlignmentViewPanel? */
251     AlignViewport viewport = frame.getCurrentView();
252     AlignmentViewPanel alignPanel = frame.alignPanel;
253     return new AnnotationServiceWorker(this, service,
254         arguments, viewport, alignPanel, frame, frame,
255         calcManager);
256   }
257
258   private CompletionStage<List<ArgumentI>> openEditParamsDialog(
259       WebServiceI service, WsParamSetI preset, List<ArgumentI> arguments)
260   {
261     WsJobParameters jobParams;
262     if (preset == null && arguments != null && arguments.size() > 0)
263       jobParams = new WsJobParameters(service.getParamStore(), preset,
264           arguments);
265     else
266       jobParams = new WsJobParameters(service.getParamStore(), preset,
267           null);
268     if (preset != null)
269     {
270       jobParams.setName(MessageManager.getString(
271           "label.adjusting_parameters_for_calculation"));
272     }
273     var stage = jobParams.showRunDialog();
274     return stage.thenApply((startJob) -> {
275       if (startJob)
276       {
277         if (jobParams.getPreset() == null)
278           return jobParams.getJobParams();
279         else
280           return jobParams.getPreset().getArguments();
281       }
282       else
283       {
284         return null;
285       }
286     });
287   }
288
289 }