1 package jalview.ws2.gui;
3 import static java.lang.String.format;
5 import java.awt.event.MouseAdapter;
6 import java.awt.event.MouseEvent;
7 import java.util.ArrayList;
8 import java.util.Collections;
10 import java.util.Objects;
11 import java.util.concurrent.CompletionStage;
12 import java.util.function.Consumer;
14 import javax.swing.JMenu;
15 import javax.swing.JMenuItem;
16 import javax.swing.ToolTipManager;
18 import jalview.datamodel.Alignment;
19 import jalview.datamodel.AlignmentI;
20 import jalview.datamodel.AlignmentOrder;
21 import jalview.datamodel.AlignmentView;
22 import jalview.datamodel.HiddenColumns;
23 import jalview.gui.AlignFrame;
24 import jalview.gui.AlignViewport;
25 import jalview.gui.Desktop;
26 import jalview.gui.JvOptionPane;
27 import jalview.gui.JvSwingUtils;
28 import jalview.gui.WebserviceInfo;
29 import jalview.gui.WsJobParameters;
30 import jalview.util.MathUtils;
31 import jalview.util.MessageManager;
32 import jalview.ws.params.ArgumentI;
33 import jalview.ws.params.ParamDatastoreI;
34 import jalview.ws.params.WsParamSetI;
35 import jalview.ws2.MenuEntryProviderI;
36 import jalview.ws2.PollingTaskExecutor;
37 import jalview.ws2.WSJob;
38 import jalview.ws2.WebServiceInfoUpdater;
39 import jalview.ws2.WebServiceWorkerI;
40 import jalview.ws2.WebServiceWorkerListener;
41 import jalview.ws2.operations.AlignmentOperation;
42 import jalview.ws2.operations.AlignmentOperation.AlignmentResult;
43 import jalview.ws2.operations.AlignmentOperation.AlignmentWorker;
46 public class AlignmentMenuBuilder implements MenuEntryProviderI
48 AlignmentOperation operation;
50 public AlignmentMenuBuilder(AlignmentOperation operation)
52 this.operation = operation;
55 public void buildMenu(JMenu parent, AlignFrame frame)
57 if (operation.canSubmitGaps())
59 var alignSubmenu = new JMenu(operation.getName());
60 buildMenu(alignSubmenu, frame, false);
61 parent.add(alignSubmenu);
62 var realignSubmenu = new JMenu(MessageManager.formatMessage(
63 "label.realign_with_params", operation.getName()));
64 realignSubmenu.setToolTipText(MessageManager
65 .getString("label.align_sequences_to_existing_alignment"));
66 buildMenu(realignSubmenu, frame, true);
67 parent.add(realignSubmenu);
71 buildMenu(parent, frame, false);
75 protected void buildMenu(JMenu parent, AlignFrame frame,
78 final String action = submitGaps ? "Align" : "Realign";
79 final var calcName = operation.getName();
82 var item = new JMenuItem(MessageManager.formatMessage(
83 "label.calcname_with_default_settings", calcName));
84 item.setToolTipText(MessageManager
85 .formatMessage("label.action_with_default_settings", action));
86 item.addActionListener((event) -> {
87 final AlignmentView msa = frame.gatherSequencesForAlignment();
90 startWorker(frame, msa, Collections.emptyList(), submitGaps);
96 if (operation.hasParameters())
98 var item = new JMenuItem(
99 MessageManager.getString("label.edit_settings_and_run"));
100 item.setToolTipText(MessageManager.getString(
101 "label.view_and_change_parameters_before_alignment"));
102 item.addActionListener((event) -> {
103 AlignmentView msa = frame.gatherSequencesForAlignment();
106 openEditParamsDialog(operation.getParamStore(), null, null)
107 .thenAcceptAsync((arguments) -> {
108 if (arguments != null)
110 startWorker(frame, msa, arguments, submitGaps);
118 var presets = operation.getParamStore().getPresets();
119 if (presets != null && presets.size() > 0)
121 final var presetList = new JMenu(MessageManager
122 .formatMessage("label.run_with_preset_params", calcName));
123 final var showToolTipFor = ToolTipManager.sharedInstance()
125 for (final var preset : presets)
127 var item = new JMenuItem(preset.getName());
128 final int QUICK_TOOLTIP = 1500;
129 item.addMouseListener(new MouseAdapter()
132 public void mouseEntered(MouseEvent e)
134 ToolTipManager.sharedInstance().setDismissDelay(QUICK_TOOLTIP);
138 public void mouseExited(MouseEvent e)
140 ToolTipManager.sharedInstance().setDismissDelay(showToolTipFor);
143 String tooltip = JvSwingUtils.wrapTooltip(true,
144 format("<strong>%s</strong><br/>%s",
145 MessageManager.getString(
146 preset.isModifiable() ? "label.user_preset"
147 : "label.service_preset"),
148 preset.getDescription()));
149 item.setToolTipText(tooltip);
150 item.addActionListener((event) -> {
151 AlignmentView msa = frame.gatherSequencesForAlignment();
152 startWorker(frame, msa, preset.getArguments(), submitGaps);
154 presetList.add(item);
156 parent.add(presetList);
160 private CompletionStage<List<ArgumentI>> openEditParamsDialog(
161 ParamDatastoreI paramStore, WsParamSetI preset,
162 List<ArgumentI> arguments)
164 WsJobParameters jobParams;
165 if (preset == null && arguments != null && arguments.size() > 0)
166 jobParams = new WsJobParameters(paramStore, preset, arguments);
168 jobParams = new WsJobParameters(paramStore, preset, null);
171 jobParams.setName(MessageManager.getString(
172 "label.adjusting_parameters_for_calculation"));
174 var stage = jobParams.showRunDialog();
175 return stage.thenApply((startJob) -> {
178 if (jobParams.getPreset() == null)
180 return jobParams.getJobParams();
184 return jobParams.getPreset().getArguments();
194 private void startWorker(AlignFrame frame, AlignmentView msa,
195 List<ArgumentI> arguments, boolean submitGaps)
197 AlignViewport viewport = frame.getViewport();
198 PollingTaskExecutor executor = frame.getViewport().getWSExecutor();
202 String panelInfo = String.format("%s using service hosted at %s%n%s",
203 operation.getName(), operation.getHostName(),
204 Objects.requireNonNullElse(operation.getDescription(), ""));
205 var wsInfo = new WebserviceInfo(operation.getName(), panelInfo, false);
207 final String alnTitle = frame.getTitle();
208 AlignmentWorker worker = operation.new AlignmentWorker(msa,
209 arguments, frame.getTitle(), submitGaps, true,
211 String outputHeader = String.format("%s of %s%nJob details%n",
212 submitGaps ? "Re-alignment" : "Alignment", alnTitle);
214 var awl = new AlignmentWorkerListener(worker, wsInfo, frame,
216 worker.setResultConsumer(awl);
217 worker.addListener(awl);
219 executor.submit(worker);
224 private class AlignmentWorkerListener
225 implements WebServiceWorkerListener, Consumer<AlignmentResult>
228 final WebServiceWorkerI worker;
229 final WebserviceInfo wsInfo;
230 final AlignFrame frame;
231 WebServiceInfoUpdater updater;
233 final long progbarId = MathUtils.getUID();
235 private AlignmentWorkerListener(WebServiceWorkerI worker, WebserviceInfo wsInfo,
236 AlignFrame frame, String header)
238 this.worker = worker;
239 this.wsInfo = wsInfo;
241 this.outputHeader = header;
242 this.updater = new WebServiceInfoUpdater(worker, wsInfo);
243 updater.setOutputHeader(outputHeader);
247 public void workerStarted(WebServiceWorkerI source)
249 // wsInfo.setThisService() should happen here
250 wsInfo.setVisible(true);
254 public void workerNotStarted(WebServiceWorkerI source)
256 wsInfo.setVisible(false);
257 // TODO show notification dialog.
258 JvOptionPane.showMessageDialog(frame,
259 MessageManager.getString("info.invalid_msa_input_mininfo"),
260 MessageManager.getString("info.invalid_msa_notenough"),
261 JvOptionPane.INFORMATION_MESSAGE);
265 public void jobCreated(WebServiceWorkerI source, WSJob job)
267 int tabIndex = wsInfo.addJobPane();
268 wsInfo.setProgressName(String.format("region %d", job.getJobNum()),
270 wsInfo.setProgressText(tabIndex, outputHeader);
271 job.addPropertyChangeListener(updater);
275 public void pollException(WebServiceWorkerI source, WSJob job, Exception e)
277 wsInfo.appendProgressText(job.getJobNum(),
278 MessageManager.formatMessage("info.server_exception",
279 operation.getName(), e.getMessage()));
283 public void workerCompleting(WebServiceWorkerI source)
285 // TODO Auto-generated method stub
286 wsInfo.setProgressBar(
287 MessageManager.getString("status.collecting_job_results"),
292 public void workerCompleted(WebServiceWorkerI source)
294 wsInfo.removeProgressBar(progbarId);
299 public void accept(AlignmentResult out)
303 wsInfo.showResultsNewFrame.addActionListener(evt -> displayNewFrame(
304 new Alignment(out.getAln()), out.getAlorders(), out.getHidden()));
305 wsInfo.setResultsReady();
309 wsInfo.setFinishedNoResults();
313 private void displayNewFrame(AlignmentI aln,
314 List<AlignmentOrder> alorders, HiddenColumns hidden)
316 AlignFrame frame = new AlignFrame(aln, hidden,
317 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
318 // TODO store feature renderer settings in worker object
319 // frame.getFeatureRenderer().transferSettings(featureSettings);
320 var regions = sortOrders(alorders);
321 if (alorders.size() == 1)
323 frame.addSortByOrderMenuItem(
324 format("%s Ordering", operation.getName()), alorders.get(0));
328 for (int i = 0; i < alorders.size(); i++)
331 Iterable<String> iter = () -> regions.get(j).stream()
332 .map(it -> Integer.toString(it)).iterator();
333 var orderName = format("%s Region %s Ordering", operation.getName(),
334 String.join(",", iter));
335 frame.addSortByOrderMenuItem(orderName, alorders.get(i));
340 * If alignment was requested from one half of a SplitFrame, show in a
341 * SplitFrame with the other pane similarly aligned.
344 Desktop.addInternalFrame(frame, frame.getTitle(), AlignFrame.DEFAULT_WIDTH,
345 AlignFrame.DEFAULT_HEIGHT);
349 private List<List<Integer>> sortOrders(List<?> alorders)
351 List<List<Integer>> regions = new ArrayList<>();
352 for (int i = 0; i < alorders.size(); i++)
354 List<Integer> regs = new ArrayList<>();
357 while (j < alorders.size())
359 if (alorders.get(i).equals(alorders.get(j)))