import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.TreeMap;
import java.util.concurrent.CompletionStage;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.ToolTipManager;
+import javax.swing.border.EmptyBorder;
import jalview.gui.AlignFrame;
import jalview.gui.Desktop;
import jalview.ws2.api.WebService;
import jalview.ws2.client.api.WebServiceProviderI;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNullElse;
+
public class WebServicesMenuManager
{
private final JMenu menu;
noServicesItem.setEnabled(false);
}
+ private Map<String, WeakReference<TaskI<?>>> interactiveTasks = new HashMap<>();
+
public WebServicesMenuManager(String name, AlignFrame frame)
{
this.frame = frame;
{
if (oneshot != null)
categoryMenu.addSeparator();
- // addInteractiveEntries(interactive, categoryMenu);
+ addInteractiveEntries(interactive, categoryMenu);
}
menu.add(categoryMenu);
}
{
String text = itemName;
if (datastore.hasParameters() || datastore.hasPresets())
- text += "with defaults";
+ text += " with defaults";
JMenuItem item = new JMenuItem(text);
item.addActionListener(e -> {
runAction(action, frame.getCurrentView(), Collections.emptyList(),
}
}
+ private void addInteractiveEntries(List<WebService<?>> services, JMenu menu)
+ {
+ Map<String, List<WebService<?>>> byServiceName = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ for (var service : services)
+ {
+ byServiceName.computeIfAbsent(service.getName(), k -> new ArrayList<>())
+ .add(service);
+ }
+ for (var entry : byServiceName.entrySet())
+ {
+ var group = new InteractiveServiceEntryGroup(entry.getKey(), entry.getValue());
+ group.appendTo(menu);
+ }
+ }
+
+ private class InteractiveServiceEntryGroup
+ {
+ JLabel serviceLabel;
+
+ JMenuItem urlItem = new JMenuItem();
+
+ JCheckBoxMenuItem serviceItem = new JCheckBoxMenuItem();
+
+ JMenuItem editParamsItem = new JMenuItem("Edit parameters...");
+
+ JMenu presetsMenu = new JMenu("Change preset");
+
+ JMenu alternativesMenu = new JMenu("Choose action");
+ {
+ urlItem.setForeground(Color.BLUE);
+ urlItem.setVisible(false);
+ serviceItem.setVisible(false);
+ editParamsItem.setVisible(false);
+ presetsMenu.setVisible(false);
+ }
+
+ InteractiveServiceEntryGroup(String name, List<WebService<?>> services)
+ {
+ serviceLabel = new JLabel(name);
+ serviceLabel.setBorder(new EmptyBorder(0, 6, 0, 6));
+ buildAlternativesMenu(services);
+ }
+
+ private void buildAlternativesMenu(List<WebService<?>> services)
+ {
+ var menu = alternativesMenu;
+ services.sort(Comparator
+ .<WebService<?>, String> comparing(s -> s.getUrl().toString())
+ .thenComparing(s -> s.getName()));
+ URL lastHost = null;
+ for (var service : services)
+ {
+ // Adding url "separator" before each group
+ URL host = service.getUrl();
+ if (!host.equals(lastHost))
+ {
+ if (lastHost != null)
+ menu.addSeparator();
+ var item = new JMenuItem(host.toString());
+ item.setForeground(Color.BLUE);
+ item.addActionListener(e -> Desktop.showUrl(host.toString()));
+ menu.add(item);
+ lastHost = host;
+ }
+ menu.addSeparator();
+ var actionsByCategory = new TreeMap<String, List<ActionI<?>>>();
+ for (ActionI<?> action : service.getActions())
+ {
+ actionsByCategory
+ .computeIfAbsent(
+ requireNonNullElse(action.getSubcategory(), ""),
+ k -> new ArrayList<>())
+ .add(action);
+ }
+ actionsByCategory.forEach((key, actions) -> {
+ var atMenu = key.isEmpty() ? menu : new JMenu(key + " with " + service.getName());
+ boolean topLevel = atMenu == menu;
+ if (!topLevel)
+ menu.add(atMenu);
+ actions.sort(Comparator.comparing(
+ a -> a.getName(),
+ Comparator.nullsFirst(Comparator.naturalOrder())));
+ for (ActionI<?> action : actions)
+ {
+ var item = new JMenuItem(action.getFullName());
+ item.addActionListener(e -> setAlternative(action));
+ atMenu.add(item);
+ }
+ });
+ }
+ }
+
+ private void setAlternative(ActionI<?> action)
+ {
+ final var arguments = new ArrayList<ArgumentI>();
+ final WsParamSetI[] lastPreset = { null };
+
+ // update selected url menu item
+ String url = action.getWebService().getUrl().toString();
+ urlItem.setText(url);
+ urlItem.setVisible(true);
+ for (var l : urlItem.getActionListeners())
+ urlItem.removeActionListener(l);
+ urlItem.addActionListener(e -> Desktop.showUrl(url));
+
+ // update selected service menu item
+ serviceItem.setText(action.getFullName());
+ serviceItem.setVisible(true);
+ for (var l : serviceItem.getActionListeners())
+ serviceItem.removeActionListener(l);
+ WebService<?> service = action.getWebService();
+ serviceItem.addActionListener(e -> {
+ if (serviceItem.getState())
+ {
+ cancelAndRunInteractive(action, frame.getCurrentView(), arguments,
+ Credentials.empty());
+ }
+ else
+ {
+ cancelInteractive(service.getName());
+ }
+ });
+ serviceItem.setSelected(true);
+
+ // update edit parameters menu item
+ var datastore = service.getParamDatastore();
+ editParamsItem.setVisible(datastore.hasParameters());
+ for (var l : editParamsItem.getActionListeners())
+ editParamsItem.removeActionListener(l);
+ if (datastore.hasParameters())
+ {
+ editParamsItem.addActionListener(e -> {
+ openEditParamsDialog(service.getParamDatastore(), lastPreset[0], arguments)
+ .thenAccept(args -> {
+ if (args != null)
+ {
+ lastPreset[0] = null;
+ arguments.clear();
+ arguments.addAll(args);
+ cancelAndRunInteractive(action, frame.getCurrentView(),
+ arguments, Credentials.empty());
+ }
+ });
+ });
+ }
+
+ // update presets menu
+ presetsMenu.removeAll();
+ presetsMenu.setEnabled(datastore.hasPresets());
+ if (datastore.hasPresets())
+ {
+ for (WsParamSetI preset : datastore.getPresets())
+ {
+ var item = new JMenuItem(preset.getName());
+ item.addActionListener(e -> {
+ lastPreset[0] = preset;
+ cancelAndRunInteractive(action, frame.getCurrentView(),
+ preset.getArguments(), Credentials.empty());
+ });
+ presetsMenu.add(item);
+ }
+ }
+
+ cancelAndRunInteractive(action, frame.getCurrentView(), arguments,
+ Credentials.empty());
+ }
+
+ void appendTo(JMenu menu)
+ {
+ menu.add(serviceLabel);
+ menu.add(urlItem);
+ menu.add(serviceItem);
+ menu.add(editParamsItem);
+ menu.add(presetsMenu);
+ menu.add(alternativesMenu);
+ }
+ }
+
+ private void cancelInteractive(String wsName)
+ {
+ var taskRef = interactiveTasks.get(wsName);
+ if (taskRef != null && taskRef.get() != null)
+ taskRef.get().cancel();
+ interactiveTasks.put(wsName, null);
+ }
+
+ private void cancelAndRunInteractive(ActionI<?> action,
+ AlignmentViewport viewport, List<ArgumentI> args, Credentials credentials)
+ {
+ var wsName = action.getWebService().getName();
+ cancelInteractive(wsName);
+ var task = runAction(action, viewport, args, credentials);
+ interactiveTasks.put(wsName, new WeakReference<>(task));
+ }
+
private TaskI<?> runAction(ActionI<?> action, AlignmentViewport viewport,
List<ArgumentI> args, Credentials credentials)
{