Mercurial > hg > release > thermostat-1.0
changeset 1294:cf49a3a699bd
Add context actions to new VMTree
review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-October/008596.html
reviewed-by: omajid
line wrap: on
line diff
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/HostContextAction.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/HostContextAction.java Thu Oct 31 16:37:07 2013 +0100 @@ -63,7 +63,7 @@ * @see VMContextAction */ @ExtensionPoint -public interface HostContextAction extends ContextAction { +public interface HostContextAction extends ReferenceContextAction<HostRef> { /** * A user-visible name for this {@code HostContextAction}. This should be @@ -78,19 +78,5 @@ */ @Override LocalizedString getDescription(); - - /** - * Invoked when the user selects this {@code HostContextAction}. - * - * @param reference the host on which this {@code HostContextAction} was - * invoked on. - */ - void execute(HostRef reference); - - /** - * The {@link Filter} returned by this method is used to select what VMs - * this {@code HostContextAction} is applicable to. - */ - Filter<HostRef> getFilter(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/ReferenceContextAction.java Thu Oct 31 16:37:07 2013 +0100 @@ -0,0 +1,61 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.ui; + +import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.storage.core.Ref; + +/** + * A common interface for {@link ContextAction} that can execute commands + * based on {@link Ref}erences. + */ +public interface ReferenceContextAction<R extends Ref> extends ContextAction { + + /** + * Invoked when the user selects this {@code ReferenceAction}. + * + * @param reference the host on which this {@code ReferenceAction} was + * invoked on. + */ + void execute(R reference); + + /** + * The {@link Filter} returned by this method is used to select what + * {@link Ref} this {@code ReferenceAction} is applicable to. + */ + Filter<R> getFilter(); +}
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/VMContextAction.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/VMContextAction.java Thu Oct 31 16:37:07 2013 +0100 @@ -64,7 +64,7 @@ * @see HostContextAction */ @ExtensionPoint -public interface VMContextAction extends ContextAction { +public interface VMContextAction extends ReferenceContextAction<VmRef> { /** * A user-visible name for this {@code VMContextAction}. Should be @@ -79,19 +79,5 @@ */ @Override public LocalizedString getDescription(); - - /** - * Invoked when the user selects this {@code VMContextAction}. - * - * @param reference specifies the vm that this {@code VMContextAction} was - * invoked on. - */ - void execute(VmRef reference); - - /** - * The {@link Filter} returned by this method is used to select what VMs - * this {@code VMContextAction} is applicable to. - */ - Filter<VmRef> getFilter(); }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainView.java Thu Oct 31 16:37:07 2013 +0100 @@ -36,17 +36,13 @@ package com.redhat.thermostat.client.swing.internal; -import java.awt.event.MouseEvent; -import java.util.List; - import javax.swing.JFrame; import com.redhat.thermostat.client.core.progress.ProgressNotifier; import com.redhat.thermostat.client.core.views.BasicView; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextActionController; import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController; -import com.redhat.thermostat.client.ui.ContextAction; import com.redhat.thermostat.client.ui.MenuAction; -import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.shared.locale.LocalizedString; import com.redhat.thermostat.storage.core.Ref; @@ -60,8 +56,6 @@ SHOW_CLIENT_CONFIG, SHOW_ABOUT_DIALOG, SHUTDOWN, - SHOW_HOST_VM_CONTEXT_MENU, - HOST_VM_CONTEXT_ACTION, } void addActionListener(ActionListener<Action> capture); @@ -92,21 +86,7 @@ * non-zero length) and the menu already exists. */ void removeMenu(MenuAction action); - - /** - * Shows a popup context menu created from the list of supplied context - * actions. When an item in the popup menu is selected, an - * {@link ActionEvent} is fired with the id - * {@link Action#HOST_VM_CONTEXT_ACTION} and the user-selected - * {@link ContextAction} as the payload. - * - * @param actions the {@link ContextAction}s available to the user. - * Normally classes implementing sub-interfaces of {@link ContextAction} are used here. - * @param e the mouse event that triggered the context action. Used to - * position the context menu. - */ - void showContextActions(List<ContextAction> actions, MouseEvent e); - + JFrame getTopFrame(); /** @@ -114,5 +94,11 @@ * object tracked by this UI Client. */ HostTreeController getHostTreeController(); + + /** + * Returns the {@link ContextActionController} that handles the context + * actions in the UI Client. + */ + ContextActionController getContextActionController(); }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java Thu Oct 31 16:37:07 2013 +0100 @@ -56,7 +56,6 @@ import java.util.List; import javax.swing.BorderFactory; -import javax.swing.JCheckBoxMenuItem; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; @@ -74,7 +73,6 @@ import com.redhat.thermostat.client.swing.MenuHelper; import com.redhat.thermostat.client.swing.SwingComponent; import com.redhat.thermostat.client.swing.components.OverlayPanel; -import com.redhat.thermostat.client.swing.components.ThermostatPopupMenu; import com.redhat.thermostat.client.swing.internal.accordion.Accordion; import com.redhat.thermostat.client.swing.internal.components.ThermostatGlassPane; import com.redhat.thermostat.client.swing.internal.components.ThermostatGlassPaneLayout; @@ -86,9 +84,9 @@ import com.redhat.thermostat.client.swing.internal.sidepane.ThermostatSidePanel; import com.redhat.thermostat.client.swing.internal.splitpane.ThermostatSplitPane; import com.redhat.thermostat.client.swing.internal.vmlist.HostTreeComponentFactory; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextActionController; import com.redhat.thermostat.client.swing.internal.vmlist.controller.DecoratorManager; import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController; -import com.redhat.thermostat.client.ui.ContextAction; import com.redhat.thermostat.client.ui.MenuAction; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; @@ -115,13 +113,13 @@ private ActionNotifier<Action> actionNotifier = new ActionNotifier<>(this); - private ThermostatPopupMenu contextMenu; private StatusBar statusBar; private ThermostatSidePanel navigationPanel; private Accordion<HostRef, VmRef> hostTree; private HostTreeController hostTreeController; + private ContextActionController contextActionController; public MainWindow() { super(); @@ -241,18 +239,6 @@ editMenu.add(configureClientMenuItem); editMenu.addSeparator(); - - // FIXME: re-add this when ready -// JMenuItem historyModeMenuItem = new JCheckBoxMenuItem(translator.localize(LocaleResources.MENU_EDIT_ENABLE_HISTORY_MODE).getContents()); -// historyModeMenuItem.setName("historyModeSwitch"); -// historyModeMenuItem.setSelected(false); -// historyModeMenuItem.addActionListener(new java.awt.event.ActionListener() { -// @Override -// public void actionPerformed(java.awt.event.ActionEvent e) { -// fireViewAction(Action.SWITCH_HISTORY_MODE); -// } -// }); -// editMenu.add(historyModeMenuItem); JMenu viewMenu = new JMenu(translator.localize(LocaleResources.MENU_VIEW).getContents()); mainMenuBar.add(viewMenu); @@ -294,8 +280,10 @@ splitPane.setLeftComponent(navigationPanel); DecoratorManager decoratorManager = new DecoratorManager(); + contextActionController = new ContextActionController(); - HostTreeComponentFactory hostFactory = new HostTreeComponentFactory(decoratorManager); + HostTreeComponentFactory hostFactory = + new HostTreeComponentFactory(decoratorManager, contextActionController); hostTree = new Accordion<>(hostFactory); hostTreeController = new HostTreeController(hostTree, decoratorManager, hostFactory); @@ -352,51 +340,6 @@ }); } - // TODO -// private void registerContextActionListener(JTree agentVmTree2) { -// contextMenu = new ThermostatPopupMenu(); -// agentVmTree2.addMouseListener(new MouseAdapter() { -// @Override -// public void mousePressed(MouseEvent e) { -// if (e.isPopupTrigger()) { -// Ref ref = getSelectedHostOrVm(); -// fireViewAction(Action.SHOW_HOST_VM_CONTEXT_MENU, e); -// } -// } -// }); -// } - - @Override - public void showContextActions(final List<ContextAction> actions, final MouseEvent e) { - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - contextMenu.removeAll(); - - for (final ContextAction action: actions) { - JMenuItem contextAction = new JMenuItem(); - contextAction.setText(action.getName().getContents()); - contextAction.setToolTipText(action.getDescription().getContents()); - - contextAction.addActionListener(new java.awt.event.ActionListener() { - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - fireViewAction(Action.HOST_VM_CONTEXT_ACTION, action); - } - }); - - // the component name is for unit tests only - contextAction.setName(action.getName().getContents()); - - contextMenu.add(contextAction); - } - - contextMenu.show((Component)e.getSource(), e.getX(), e.getY()); - } - }); - } - private JPanel createDetailsPanel() { JPanel result = new JPanel(new BorderLayout()); result.add(contentArea, BorderLayout.CENTER); @@ -444,11 +387,7 @@ private void fireViewAction(Action action) { actionNotifier.fireAction(action); } - - private void fireViewAction(Action action, Object payload) { - actionNotifier.fireAction(action, payload); - } - + @SuppressWarnings("unused") // Used for debugging but not in production code. private static void printTree(PrintStream out, TreeNode node, int depth) { out.println(StringUtils.repeat(" ", depth) + node.toString()); @@ -528,5 +467,10 @@ public HostTreeController getHostTreeController() { return hostTreeController; } + + @Override + public ContextActionController getContextActionController() { + return contextActionController; + } }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImpl.java Thu Oct 31 16:37:07 2013 +0100 @@ -56,10 +56,11 @@ import com.redhat.thermostat.client.core.views.HostInformationViewProvider; import com.redhat.thermostat.client.core.views.SummaryViewProvider; import com.redhat.thermostat.client.core.views.VmInformationViewProvider; -import com.redhat.thermostat.client.swing.internal.MainView.Action; import com.redhat.thermostat.client.swing.internal.osgi.HostContextActionServiceTracker; import com.redhat.thermostat.client.swing.internal.osgi.InformationServiceTracker; import com.redhat.thermostat.client.swing.internal.osgi.VMContextActionServiceTracker; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextActionController; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextHandler; import com.redhat.thermostat.client.swing.internal.vmlist.controller.DecoratorProviderExtensionListener; import com.redhat.thermostat.client.swing.internal.vmlist.controller.FilterManager; import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController; @@ -343,12 +344,6 @@ case SHOW_ABOUT_DIALOG: showAboutDialog(); break; - case SHOW_HOST_VM_CONTEXT_MENU: - showContextMenu(evt); - break; - case HOST_VM_CONTEXT_ACTION: - handleVMHooks(evt); - break; case SHUTDOWN: // Main will call shutdownApplication shutdown.countDown(); @@ -400,8 +395,20 @@ vmInfoRegistry.addActionListener(vmInfoRegistryListener); vmInfoRegistry.start(); + + setUpActionControllers(); } + private void setUpActionControllers() { + ContextActionController contextController = + view.getContextActionController(); + ContextHandler handler = + new ContextHandler(hostContextActionTracker, + vmContextActionTracker); + contextController.addContextActionListener(handler); + handler.addContextHandlerActionListener(contextController); + } + private void registerProgressNotificator(BundleContext context) { ProgressNotifier notifier = view.getNotifier(); context.registerService(ProgressNotifier.class, notifier, null); @@ -431,51 +438,6 @@ vmInfoRegistry.stop(); } - private void showContextMenu(ActionEvent<Action> evt) { - // TODO -// List<ContextAction> toShow = new ArrayList<>(); -// -// Ref ref = view.getSelectedHostOrVm(); -// if (ref instanceof HostRef) { -// HostRef vm = (HostRef) ref; -// -// logger.log(Level.INFO, "registering applicable HostContextActions actions to show"); -// -// for (HostContextAction action : hostContextActionTracker.getHostContextActions()) { -// if (action.getFilter().matches(vm)) { -// toShow.add(action); -// } -// } -// } else if (ref instanceof VmRef) { -// VmRef vm = (VmRef) ref; -// -// logger.log(Level.INFO, "registering applicable VMContextActions actions to show"); -// -// for (VMContextAction action : vmContextActionTracker.getVmContextActions()) { -// if (action.getFilter().matches(vm)) { -// toShow.add(action); -// } -// } -// } -// -// view.showContextActions(toShow, (MouseEvent) evt.getPayload()); - } - - private void handleVMHooks(ActionEvent<MainView.Action> event) { -// Object payload = event.getPayload(); -// try { -// if (payload instanceof HostContextAction) { -// HostContextAction action = (HostContextAction) payload; -// action.execute((HostRef) view.getSelectedHostOrVm()); -// } else if (payload instanceof VMContextAction) { -// VMContextAction action = (VMContextAction) payload; -// action.execute((VmRef) view.getSelectedHostOrVm()); -// } -// } catch (Throwable error) { -// logger.log(Level.SEVERE, "error invocating context action", error); -// } - } - @Override public void showMainMainWindow() { try {
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/HostContextActionServiceTracker.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/HostContextActionServiceTracker.java Thu Oct 31 16:37:07 2013 +0100 @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; @@ -46,14 +47,14 @@ import com.redhat.thermostat.client.ui.HostContextAction; @SuppressWarnings("rawtypes") -public class HostContextActionServiceTracker extends ServiceTracker { +public class HostContextActionServiceTracker extends ServiceTracker implements ReferenceContextActionProvider { private List<HostContextAction> hostContextActions; @SuppressWarnings("unchecked") public HostContextActionServiceTracker(BundleContext context) { super(context, HostContextAction.class.getName(), null); - this.hostContextActions = new ArrayList<>(); + this.hostContextActions = new CopyOnWriteArrayList<>(); } @Override @@ -70,10 +71,10 @@ hostContextActions.remove((HostContextAction)service); super.removedService(reference, service); } - - public List<HostContextAction> getHostContextActions() { + + @Override + public List<HostContextAction> getActions() { return new ArrayList<>(hostContextActions); } - }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/ReferenceContextActionProvider.java Thu Oct 31 16:37:07 2013 +0100 @@ -0,0 +1,46 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.internal.osgi; + +import java.util.List; + +import com.redhat.thermostat.client.ui.ReferenceContextAction; + +public interface ReferenceContextActionProvider { + + public List<? extends ReferenceContextAction> getActions(); +}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/VMContextActionServiceTracker.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/osgi/VMContextActionServiceTracker.java Thu Oct 31 16:37:07 2013 +0100 @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; @@ -46,14 +47,14 @@ import com.redhat.thermostat.client.ui.VMContextAction; @SuppressWarnings("rawtypes") -public class VMContextActionServiceTracker extends ServiceTracker { +public class VMContextActionServiceTracker extends ServiceTracker implements ReferenceContextActionProvider { private List<VMContextAction> vmContextActions; @SuppressWarnings("unchecked") public VMContextActionServiceTracker(BundleContext context) { super(context, VMContextAction.class.getName(), null); - this.vmContextActions = new ArrayList<>(); + this.vmContextActions = new CopyOnWriteArrayList<>(); } @Override @@ -71,7 +72,8 @@ super.removedService(reference, service); } - public List<VMContextAction> getVmContextActions() { + @Override + public List<VMContextAction> getActions() { return new ArrayList<>(vmContextActions); } }
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/HostTreeComponentFactory.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/HostTreeComponentFactory.java Thu Oct 31 16:37:07 2013 +0100 @@ -42,6 +42,7 @@ import com.redhat.thermostat.client.swing.internal.accordion.AccordionComponent; import com.redhat.thermostat.client.swing.internal.accordion.AccordionComponentFactory; import com.redhat.thermostat.client.swing.internal.accordion.TitledPane; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextActionController; import com.redhat.thermostat.client.swing.internal.vmlist.controller.DecoratorManager; import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.VmRef; @@ -52,9 +53,14 @@ private Map<HostRef, ReferenceTitle> headers; private DecoratorManager decoratorManager; + private ContextActionController contextActionController; - public HostTreeComponentFactory(DecoratorManager decoratorManager) { + public HostTreeComponentFactory(DecoratorManager decoratorManager, + ContextActionController contextActionController) + { this.decoratorManager = decoratorManager; + this.contextActionController = contextActionController; + components = new HashMap<>(); headers = new HashMap<>(); } @@ -63,6 +69,7 @@ public TitledPane createHeader(HostRef header) { ReferenceTitle pane = new ReferenceTitle(header); decoratorManager.registerAndSetIcon(pane); + contextActionController.register(pane, pane); headers.put(header, pane); return pane; @@ -72,6 +79,7 @@ public AccordionComponent createComponent(HostRef header, VmRef component) { ReferenceComponent refComponent = new ReferenceComponent(component); decoratorManager.registerAndSetIcon(refComponent); + contextActionController.register(refComponent, refComponent); components.put(component, refComponent); return refComponent;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/ContextActionController.java Thu Oct 31 16:37:07 2013 +0100 @@ -0,0 +1,118 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.internal.vmlist.controller; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.client.swing.internal.accordion.AccordionComponent; +import com.redhat.thermostat.client.swing.internal.vmlist.ReferenceProvider; +import com.redhat.thermostat.client.ui.ReferenceContextAction; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ActionNotifier; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.storage.core.Ref; + +public class ContextActionController implements ActionListener<ContextHandler.ContextHandlerAction>{ + + public enum ContextAction { + SHOW_CONTEXT_MENU, + } + + public static class Payload { + public AccordionComponent component; + public int x; + public int y; + public Ref ref; + } + + private static final Logger logger = LoggingUtils.getLogger(ContextActionController.class); + + private ActionNotifier<ContextAction> notifier; + + public ContextActionController() { + notifier = new ActionNotifier<>(this); + } + + public void register(final AccordionComponent pane, + final ReferenceProvider provider) + { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + pane.getUiComponent().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + Payload payload = new Payload(); + payload.ref = provider.getReference(); + payload.component = pane; + payload.x = e.getX(); + payload.y = e.getY(); + notifier.fireAction(ContextAction.SHOW_CONTEXT_MENU, payload); + } + } + }); + } + }); + } + + public void addContextActionListener(ActionListener<ContextAction> l) { + notifier.addActionListener(l); + } + + public void removeContextActionListener(ActionListener<ContextAction> l) { + notifier.removeActionListener(l); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void actionPerformed(ActionEvent<ContextHandler.ContextHandlerAction> event) { + ContextHandler.Payload payload = (ContextHandler.Payload) event.getPayload(); + try { + ReferenceContextAction action = payload.action; + action.execute(payload.reference); + } catch (Throwable error) { + logger.log(Level.SEVERE, "error invocating context action", error); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/ContextHandler.java Thu Oct 31 16:37:07 2013 +0100 @@ -0,0 +1,159 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.internal.vmlist.controller; + +import java.util.List; + +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; + +import com.redhat.thermostat.client.swing.components.ThermostatPopupMenu; +import com.redhat.thermostat.client.swing.internal.osgi.HostContextActionServiceTracker; +import com.redhat.thermostat.client.swing.internal.osgi.VMContextActionServiceTracker; +import com.redhat.thermostat.client.ui.ReferenceContextAction; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ActionNotifier; +import com.redhat.thermostat.storage.core.HostRef; +import com.redhat.thermostat.storage.core.Ref; + +/** + * + */ +public class ContextHandler implements ActionListener<ContextActionController.ContextAction>{ + + public enum ContextHandlerAction { + ACTION_PERFORMED, + } + + public static class Payload<R extends Ref> { + ReferenceContextAction<R> action; + Ref reference; + } + + private ActionNotifier<ContextHandlerAction> notifier; + + private ThermostatPopupMenu contextMenu; + + private HostContextActionServiceTracker hostContextActionTracker; + private VMContextActionServiceTracker vmContextActionTracker; + + public ContextHandler(HostContextActionServiceTracker hostContextActionTracker, + VMContextActionServiceTracker vmContextActionTracker) + { + this.hostContextActionTracker = hostContextActionTracker; + this.vmContextActionTracker = vmContextActionTracker; + notifier = new ActionNotifier<>(this); + } + + @Override + public void actionPerformed(final ActionEvent<ContextActionController.ContextAction> actionEvent) { + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public void run() { + if (contextMenu == null) { + contextMenu = createContextPopMenu(); + } + + final ContextActionController.Payload payload = + (ContextActionController.Payload) actionEvent.getPayload(); + + List<? extends ReferenceContextAction> actions = null; + if (payload.ref instanceof HostRef) { + actions = hostContextActionTracker.getActions(); + } else { + actions = vmContextActionTracker.getActions(); + } + + contextMenu.removeAll(); + + boolean showPopup = false; + for (final ReferenceContextAction action : actions) { + + if (!action.getFilter().matches(payload.ref)) { + continue; + } + + showPopup = true; + + JMenuItem contextAction = createContextMenuItem(); + contextAction.setText(action.getName().getContents()); + contextAction.setToolTipText(action.getDescription().getContents()); + contextAction.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + Payload actionPayload = new Payload(); + actionPayload.reference = payload.ref; + actionPayload.action = action; + + notifier.fireAction(ContextHandlerAction.ACTION_PERFORMED, actionPayload); + } + }); + + // the component name is for unit tests only + contextAction.setName(action.getName().getContents()); + + contextMenu.add(contextAction); + } + + if (showPopup) { + contextMenu.show(payload.component.getUiComponent(), payload.x, payload.y); + } + } + }); + } + + // allow us to inject a mock for testing + ThermostatPopupMenu createContextPopMenu() { + return new ThermostatPopupMenu(); + } + + // allow us to inject a mock for testing + JMenuItem createContextMenuItem() { + return new JMenuItem(); + } + + public void addContextHandlerActionListener(ActionListener<ContextHandlerAction> l) { + notifier.addActionListener(l); + } + + public void removeContextHandlerActionListener(ActionListener<ContextHandlerAction> l) { + notifier.removeActionListener(l); + } +}
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImplTest.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImplTest.java Thu Oct 31 16:37:07 2013 +0100 @@ -37,14 +37,14 @@ package com.redhat.thermostat.client.swing.internal; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; + import static org.mockito.Matchers.isA; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.any; import java.util.concurrent.CountDownLatch; @@ -66,6 +66,8 @@ import com.redhat.thermostat.client.core.views.SummaryViewProvider; import com.redhat.thermostat.client.core.views.VmInformationView; import com.redhat.thermostat.client.core.views.VmInformationViewProvider; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextActionController; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextHandler; import com.redhat.thermostat.client.swing.internal.vmlist.controller.DecoratorProviderExtensionListener; import com.redhat.thermostat.client.swing.internal.vmlist.controller.HostTreeController; import com.redhat.thermostat.client.ui.HostContextAction; @@ -127,6 +129,8 @@ private DecoratorProviderExtensionListener<HostRef> hostDecorators; private DecoratorProviderExtensionListener<VmRef> vmDecorators; + private ContextActionController contextController; + @BeforeClass public static void setUpOnce() { // TODO remove when controller uses mocked objects rather than real swing objects @@ -137,7 +141,7 @@ @Before public void setUp() throws Exception { context = new StubBundleContext(); - + // Setup timers mainWindowTimer = mock(Timer.class); Timer otherTimer = mock(Timer.class); // FIXME needed for SummaryView; remove later @@ -189,6 +193,9 @@ ArgumentCaptor<ActionListener> grabListener = ArgumentCaptor.forClass(ActionListener.class); doNothing().when(view).addActionListener(grabListener.capture()); + contextController = mock(ContextActionController.class); + when(view.getContextActionController()).thenReturn(contextController); + hostDecorators = mock(DecoratorProviderExtensionListener.class); vmDecorators = mock(DecoratorProviderExtensionListener.class); @@ -307,6 +314,8 @@ assertEquals(hostDecorators, l1); assertEquals(vmDecorators, l2); + + verify(contextController).addContextActionListener(any(ContextHandler.class)); } @Test @@ -314,30 +323,7 @@ controller.showMainMainWindow(); verify(view).showMainWindow(); } - -// @Test -// public void verifyUpdateHostsVMsLoadsCorrectVMWithFilter() { -// -// VmRef ref1 = mock(VmRef.class); -// when(ref1.getStringID()).thenReturn("test1"); -// when(ref1.getName()).thenReturn("test1"); -// -// VmRef ref2 = mock(VmRef.class); -// when(ref2.getStringID()).thenReturn("test2"); -// when(ref2.getName()).thenReturn("test2"); -// -// controller.setHostVmTreeFilter("test1"); -// -// Filter<VmRef> filter = controller.getVmFilter(); -// assertTrue(filter.matches(ref1)); -// assertFalse(filter.matches(ref2)); -// } - -// private void assertEqualCollection(Collection<?> expected, Collection<?> actual) { -// assertEquals(expected.size(), actual.size()); -// assertTrue(expected.containsAll(actual)); -// } -// + // @Test // @Bug(id="954", // summary="Thermostat GUI client should remember my last panel selected", @@ -447,69 +433,6 @@ // // assertEquals(2, id); // } -// -// @Test -// public void verifyHostActionsAreShown() { -// HostRef host = mock(HostRef.class); -// when(view.getSelectedHostOrVm()).thenReturn(host); -// -// MouseEvent uiEvent = mock(MouseEvent.class); -// ActionEvent<MainView.Action> viewEvent = new ActionEvent<>(view, MainView.Action.SHOW_HOST_VM_CONTEXT_MENU); -// viewEvent.setPayload(uiEvent); -// -// l.actionPerformed(viewEvent); -// -// List<ContextAction> actions = new ArrayList<>(); -// actions.add(hostContextAction1); -// -// verify(view).showContextActions(actions, uiEvent); -// } -// -// @Test -// public void verityVMActionsAreShown() { -// VmInfo vmInfo = new VmInfo("foo", "123", 0, 1, 2, null, null, null, null, null, null, null, null, null, null, null, -1, null); -// when(mockVmsDAO.getVmInfo(isA(VmRef.class))).thenReturn(vmInfo); -// -// VmRef ref = mock(VmRef.class); -// when(view.getSelectedHostOrVm()).thenReturn(ref); -// -// MouseEvent uiEvent = mock(MouseEvent.class); -// ActionEvent<MainView.Action> viewEvent = new ActionEvent<>(view, MainView.Action.SHOW_HOST_VM_CONTEXT_MENU); -// viewEvent.setPayload(uiEvent); -// -// l.actionPerformed(viewEvent); -// -// List<ContextAction> actions = new ArrayList<>(); -// actions.add(vmContextAction1); -// -// verify(view).showContextActions(actions, uiEvent); -// } -// -// @Test -// public void verifyHostActionsAreExecuted() { -// HostRef hostRef = mock(HostRef.class); -// when(view.getSelectedHostOrVm()).thenReturn(hostRef); -// -// ActionEvent<MainView.Action> event = new ActionEvent<>(view, MainView.Action.HOST_VM_CONTEXT_ACTION); -// event.setPayload(hostContextAction1); -// l.actionPerformed(event); -// -// verify(hostContextAction1, times(1)).execute(hostRef); -// } -// -// @Test -// public void verityVMActionsAreExecuted() { -// -// VmRef vmRef = mock(VmRef.class); -// when(view.getSelectedHostOrVm()).thenReturn(vmRef); -// -// ActionEvent<MainView.Action> event = new ActionEvent<>(view, MainView.Action.HOST_VM_CONTEXT_ACTION); -// event.setPayload(vmContextAction1); -// l.actionPerformed(event); -// -// verify(vmContextAction1, times(1)).execute(any(VmRef.class)); -// verify(vmContextAction2, times(0)).execute(any(VmRef.class)); -// } @Test public void verifyMenuItems() {
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/osgi/HostContextActionServiceTrackerTest.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/osgi/HostContextActionServiceTrackerTest.java Thu Oct 31 16:37:07 2013 +0100 @@ -58,13 +58,13 @@ HostContextActionServiceTracker tracker = new HostContextActionServiceTracker(bundleContext); tracker.open(); - assertTrue(tracker.getHostContextActions().contains(hostAction)); + assertTrue(tracker.getActions().contains(hostAction)); registration.unregister(); tracker.close(); - assertFalse(tracker.getHostContextActions().contains(hostAction)); + assertFalse(tracker.getActions().contains(hostAction)); } }
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/osgi/VMContextActionServiceTrackerTest.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/osgi/VMContextActionServiceTrackerTest.java Thu Oct 31 16:37:07 2013 +0100 @@ -58,13 +58,13 @@ VMContextActionServiceTracker tracker = new VMContextActionServiceTracker(bundleContext); tracker.open(); - assertTrue(tracker.getVmContextActions().contains(vmAction)); + assertTrue(tracker.getActions().contains(vmAction)); registration.unregister(); tracker.close(); - assertFalse(tracker.getVmContextActions().contains(vmAction)); + assertFalse(tracker.getActions().contains(vmAction)); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/ContextActionControllerTest.java Thu Oct 31 16:37:07 2013 +0100 @@ -0,0 +1,159 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.internal.vmlist.controller; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertEquals; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.JComponent; +import javax.swing.RepaintManager; +import javax.swing.SwingUtilities; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.redhat.thermostat.client.swing.internal.accordion.AccordionComponent; +import com.redhat.thermostat.client.swing.internal.vmlist.ReferenceProvider; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextActionController.ContextAction; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextActionController.Payload; +import com.redhat.thermostat.client.ui.ReferenceContextAction; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.storage.core.HostRef; + +public class ContextActionControllerTest { + + private JComponent jcomponent; + private AccordionComponent component; + private ReferenceProvider provider; + + @BeforeClass + public static void setUpOnce() { + // This is needed because some other test may have installed the + // EDT violation checker repaint manager. + RepaintManager.setCurrentManager(new RepaintManager()); + } + + @Before + public void setUp() { + component = mock(AccordionComponent.class); + provider = mock(ReferenceProvider.class); + jcomponent = mock(JComponent.class); + } + + private void waitForSwing() { + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + // just wait :) + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Test + public void testRegister() { + + ArgumentCaptor<MouseListener> captor = ArgumentCaptor.forClass(MouseListener.class); + MouseEvent e = mock(MouseEvent.class); + + when(e.getX()).thenReturn(5); + when(e.getY()).thenReturn(10); + + HostRef ref = new HostRef("0", "0"); + + when(e.isPopupTrigger()).thenReturn(true); + when(component.getUiComponent()).thenReturn(jcomponent); + when(provider.getReference()).thenReturn(ref); + doNothing().when(jcomponent).addMouseListener(captor.capture()); + + final ContextActionController.Payload[] payload = new ContextActionController.Payload[1]; + ActionListener<ContextActionController.ContextAction> l = + new ActionListener<ContextActionController.ContextAction>() { + @Override + public void actionPerformed(ActionEvent<ContextAction> actionEvent) { + payload[0] = (Payload) actionEvent.getPayload(); + } + }; + + ContextActionController controller = new ContextActionController(); + controller.register(component, provider); + controller.addContextActionListener(l); + + waitForSwing(); + + MouseListener mouseListener = captor.getValue(); + mouseListener.mousePressed(e); + + assertNotNull(payload[0]); + assertEquals(ref, payload[0].ref); + assertEquals(component, payload[0].component); + assertEquals(5, payload[0].x); + assertEquals(10, payload[0].y); + } + + @Test + public void testExecute() { + ContextActionController controller = new ContextActionController(); + + ContextHandler.Payload payload = new ContextHandler.Payload(); + payload.action = mock(ReferenceContextAction.class); + payload.reference = new HostRef("0", "0"); + + ActionEvent<ContextHandler.ContextHandlerAction> event = + new ActionEvent<ContextHandler.ContextHandlerAction>(this, + ContextHandler.ContextHandlerAction.ACTION_PERFORMED); + event.setPayload(payload); + + controller.actionPerformed(event); + + verify(payload.action).execute(payload.reference); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/ContextHandlerTest.java Thu Oct 31 16:37:07 2013 +0100 @@ -0,0 +1,285 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.internal.vmlist.controller; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.doNothing; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.awt.Component; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.RepaintManager; +import javax.swing.SwingUtilities; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.redhat.thermostat.client.swing.components.ThermostatPopupMenu; +import com.redhat.thermostat.client.swing.internal.accordion.AccordionComponent; +import com.redhat.thermostat.client.swing.internal.osgi.HostContextActionServiceTracker; +import com.redhat.thermostat.client.swing.internal.osgi.VMContextActionServiceTracker; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextHandler.ContextHandlerAction; +import com.redhat.thermostat.client.swing.internal.vmlist.controller.ContextHandler.Payload; +import com.redhat.thermostat.client.ui.HostContextAction; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.Filter; +import com.redhat.thermostat.shared.locale.LocalizedString; +import com.redhat.thermostat.storage.core.HostRef; + +/** + * + */ +public class ContextHandlerTest { + + private HostContextActionServiceTracker hostContextActionTracker; + private VMContextActionServiceTracker vmContextActionTracker; + private ActionEvent actionEvent; + + @BeforeClass + public static void setUpOnce() { + // This is needed because some other test may have installed the + // EDT violation checker repaint manager. + RepaintManager.setCurrentManager(new RepaintManager()); + } + + private void waitForSwing() { + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + // just wait :) + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private class TestActionListener implements com.redhat.thermostat.common.ActionListener<ContextHandlerAction> { + + ActionEvent<ContextHandlerAction> actionEvent; + + @Override + public void actionPerformed(ActionEvent<ContextHandlerAction> actionEvent) { + this.actionEvent = actionEvent; + } + } + + @Before + public void setUp() { + hostContextActionTracker = mock(HostContextActionServiceTracker.class); + vmContextActionTracker = mock(VMContextActionServiceTracker.class); + actionEvent = mock(ActionEvent.class); + } + + @Test + public void testActions() { + + ArgumentCaptor<ActionListener> captor0 = ArgumentCaptor.forClass(ActionListener.class); + ArgumentCaptor<ActionListener> captor1 = ArgumentCaptor.forClass(ActionListener.class); + + final int[] invocations = new int[2]; + + final ThermostatPopupMenu popup = mock(ThermostatPopupMenu.class); + final JMenuItem menuItem0 = mock(JMenuItem.class); + final JMenuItem menuItem1 = mock(JMenuItem.class); + + doNothing().when(menuItem0).addActionListener(captor0.capture()); + doNothing().when(menuItem1).addActionListener(captor1.capture()); + + final JMenuItem[] items = new JMenuItem[2]; + items[0] = menuItem0; + items[1] = menuItem1; + + ContextActionController.Payload payload = new ContextActionController.Payload(); + when(actionEvent.getPayload()).thenReturn(payload); + + HostRef host0 = new HostRef("0", "0"); + payload.ref = host0; + + JComponent accordionComponent = mock(JComponent.class); + AccordionComponent accordion = mock(AccordionComponent.class); + when(accordion.getUiComponent()).thenReturn(accordionComponent); + payload.component = accordion; + payload.x = 10; + payload.y = 10; + + TestActionListener testListener = new TestActionListener(); + + ContextHandler handler = new ContextHandler(hostContextActionTracker, vmContextActionTracker) { + @Override + ThermostatPopupMenu createContextPopMenu() { + invocations[0]++; + return popup; + } + @Override + JMenuItem createContextMenuItem() { + return items[invocations[1]++ % 2]; + } + }; + handler.addContextHandlerActionListener(testListener); + + // *** test no action + + handler.actionPerformed(actionEvent); + + waitForSwing(); + + assertEquals(1, invocations[0]); + assertEquals(0, invocations[1]); + + verify(hostContextActionTracker).getActions(); + verify(vmContextActionTracker, times(0)).getActions(); + + // verify the popup is built from scratch + verify(popup).removeAll(); + + verify(popup, times(0)).show(any(Component.class), anyInt(), anyInt()); + + // *** test two actions, no filter + + // no reason to change the event, but add actions + Filter<HostRef> hostFilter = mock(Filter.class); + when(hostFilter.matches(host0)).thenReturn(true).thenReturn(true); + + LocalizedString name0 = new LocalizedString("actionName0"); + LocalizedString des0 = new LocalizedString("actionDesc0"); + + HostContextAction hostAction0 = mock(HostContextAction.class); + when(hostAction0.getFilter()).thenReturn(hostFilter); + when(hostAction0.getName()).thenReturn(name0); + when(hostAction0.getDescription()).thenReturn(des0); + + LocalizedString name1 = new LocalizedString("actionName1"); + LocalizedString des1 = new LocalizedString("actionDesc1"); + + HostContextAction hostAction1 = mock(HostContextAction.class); + when(hostAction1.getFilter()).thenReturn(hostFilter); + when(hostAction1.getName()).thenReturn(name1); + when(hostAction1.getDescription()).thenReturn(des1); + + List<HostContextAction> hostActions = new ArrayList<>(); + hostActions.add(hostAction0); + hostActions.add(hostAction1); + + when(hostContextActionTracker.getActions()).thenReturn(hostActions); + + handler.actionPerformed(actionEvent); + waitForSwing(); + + assertEquals(1, invocations[0]); + assertEquals(2, invocations[1]); + + verify(popup, times(2)).removeAll(); + + verify(popup).add(menuItem0); + verify(popup).add(menuItem1); + + verify(popup, times(1)).show(accordionComponent, 10, 10); + + verifyNoMoreInteractions(popup); + + verify(menuItem0).setText(name0.getContents()); + verify(menuItem1).setText(name1.getContents()); + + verify(menuItem0).setToolTipText(des0.getContents()); + verify(menuItem1).setToolTipText(des1.getContents()); + + // *** check that we are notified for the correct action + + ActionListener l0 = captor0.getValue(); + ActionListener l1 = captor1.getValue(); + + java.awt.event.ActionEvent fakeEvent = mock(java.awt.event.ActionEvent.class); + l0.actionPerformed(fakeEvent); + + assertNotNull(testListener.actionEvent); + + Payload eventPayload = (Payload) testListener.actionEvent.getPayload(); + assertNotNull(eventPayload); + assertEquals(host0, eventPayload.reference); + assertEquals(hostAction0, eventPayload.action); + + l1.actionPerformed(fakeEvent); + assertNotNull(testListener.actionEvent); + + eventPayload = (Payload) testListener.actionEvent.getPayload(); + assertNotNull(eventPayload); + assertEquals(host0, eventPayload.reference); + assertEquals(hostAction1, eventPayload.action); + + // *** now test again with one filtering + + invocations[1] = 0; + + when(hostFilter.matches(host0)).thenReturn(false).thenReturn(true); + handler.actionPerformed(actionEvent); + waitForSwing(); + + assertEquals(1, invocations[0]); + assertEquals(1, invocations[1]); + + // TODO: the numbers here take into account the previous + // invocation this should really be a separate test, but there's too + // much stubbing to duplicate, consider refactoring the test + + verify(popup, times(3)).removeAll(); + + verify(popup, times(2)).add(menuItem0); + verify(popup, times(2)).show(accordionComponent, 10, 10); + + verifyNoMoreInteractions(popup); + + verify(menuItem0).setText(name0.getContents()); + verify(menuItem0).setToolTipText(des0.getContents()); + } +}
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeControllerTest.java Wed Oct 30 14:32:31 2013 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/vmlist/controller/HostTreeControllerTest.java Thu Oct 31 16:37:07 2013 +0100 @@ -39,6 +39,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; @@ -59,7 +60,6 @@ import com.redhat.thermostat.client.swing.internal.vmlist.HostTreeComponentFactory; import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.VmRef; -import com.redhat.thermostat.test.Bug; public class HostTreeControllerTest {