# HG changeset patch # User Andrew Azores # Date 1444754885 14400 # Node ID ffd37dfffbdda1a0a581cef3f0876805117c239e # Parent 747a32443e86f72fb9f1e74f19bfa09d1733f868 Redesign ActionToggleButton states. Use in Profiling, Threads, Notifications Reviewed-by: neugens, jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-September/016403.html http://icedtea.classpath.org/pipermail/thermostat/2015-October/016608.html PR2671 diff -r 747a32443e86 -r ffd37dfffbdd client/core/src/main/java/com/redhat/thermostat/client/core/ToggleActionState.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/ToggleActionState.java Tue Oct 13 12:48:05 2015 -0400 @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2015 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 + * . + * + * 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.core; + +public interface ToggleActionState { + boolean isTransitionState(); + boolean isActionEnabled(); + boolean isButtonEnabled(); +} diff -r 747a32443e86 -r ffd37dfffbdd client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ActionButtonUI.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ActionButtonUI.java Tue Oct 13 11:44:25 2015 -0400 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ActionButtonUI.java Tue Oct 13 12:48:05 2015 -0400 @@ -59,13 +59,13 @@ class ActionButtonUI extends MetalButtonUI { - private BufferedImage sourceIcon; - private BufferedImage rollOverIcon; - private Image disabledIcon; + protected BufferedImage sourceIcon; + protected BufferedImage rollOverIcon; + protected Image disabledIcon; ActionButtonUI() {} - private BufferedImage getBrighterImage(BufferedImage source) { + protected BufferedImage getBrighterImage(BufferedImage source) { float[] kernel = new float[] { 0, 0, 0, 0, 1.2f, 0, 0, 0, 0 }; BufferedImageOp brighterOp = new ConvolveOp(new Kernel(3, 3, kernel), diff -r 747a32443e86 -r ffd37dfffbdd client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ActionToggleButton.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ActionToggleButton.java Tue Oct 13 11:44:25 2015 -0400 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ActionToggleButton.java Tue Oct 13 12:48:05 2015 -0400 @@ -40,6 +40,7 @@ import javax.swing.Icon; import javax.swing.JToggleButton; +import com.redhat.thermostat.client.core.ToggleActionState; import com.redhat.thermostat.shared.locale.LocalizedString; @SuppressWarnings("serial") @@ -47,17 +48,27 @@ private String lastText; private boolean showText; + private ActionToggleButtonUI buttonUI; + public ActionToggleButton(final Icon icon) { this(icon, LocalizedString.EMPTY_STRING); } public ActionToggleButton(final Icon icon, LocalizedString text) { - super(icon); - + this(icon, icon, text); + } + + public ActionToggleButton(final Icon defaultStateIcon, final Icon selectedStateIcon, LocalizedString text) { + super(); + + setIcon(defaultStateIcon); + setSelectedIcon(selectedStateIcon); + showText = true; setText(text.getContents()); - - setUI(new ActionButtonUI()); + + buttonUI = new ActionToggleButtonUI(); + setUI(buttonUI); setOpaque(false); setContentAreaFilled(false); setBorder(new ToolbarButtonBorder(this)); @@ -88,6 +99,14 @@ } else { setText_noClient(""); } - } + } + + public void setToggleActionState(ToggleActionState toggleActionState) { + setEnabled(!toggleActionState.isTransitionState() && toggleActionState.isButtonEnabled()); + setSelected(toggleActionState.isActionEnabled()); + buttonUI.setState(toggleActionState); + repaint(); + } + } diff -r 747a32443e86 -r ffd37dfffbdd client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ActionToggleButtonUI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ActionToggleButtonUI.java Tue Oct 13 12:48:05 2015 -0400 @@ -0,0 +1,159 @@ +/* + * Copyright 2012-2015 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 + * . + * + * 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.components; + +import com.redhat.thermostat.client.core.ToggleActionState; + +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.GrayFilter; +import javax.swing.JComponent; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +/** + * An ActionButtonUI which is intended specifically for "togglebuttons", which + * are defined to have exactly 4 possible states: conceptually, they are + * STARTING, STARTED, STOPPING, and STOPPED. The initial state is STOPPED. + * Each state is represented by a pairing of "button enabled" and + * "button selected" states: + * + * STOPPED: enabled, not selected + * STARTING: disabled, selected + * STARTED: enabled, selected + * STOPPING: disabled, not selected + * + * @see ActionToggleButton + * @see ToggleActionState + */ +public class ActionToggleButtonUI extends ActionButtonUI { + + protected BufferedImage selectedIcon; + protected Image rolledOverSelectedIcon; + protected Image disabledSelectedIcon; + private ToggleActionState toggleActionState; + + @Override + protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect) { + AbstractButton button = (AbstractButton) c; + ButtonModel model = button.getModel(); + + javax.swing.Icon icon = button.getIcon(); + int w = icon.getIconWidth(); + int h = icon.getIconHeight(); + + if (sourceIcon == null) { + sourceIcon = new BufferedImage(w + 1, h + 1, + BufferedImage.TYPE_INT_ARGB); + Graphics imageGraphics = sourceIcon.getGraphics(); + icon.paintIcon(null, imageGraphics, 0, 0); + } + + if (rollOverIcon == null) { + rollOverIcon = getBrighterImage(sourceIcon); + } + + if (disabledIcon == null) { + disabledIcon = GrayFilter.createDisabledImage(sourceIcon); + } + + if (selectedIcon == null) { + selectedIcon = new BufferedImage(w + 1, h + 1, BufferedImage.TYPE_INT_ARGB); + Graphics imageGraphics = selectedIcon.getGraphics(); + button.getSelectedIcon().paintIcon(null, imageGraphics, 0, 0); + } + + if (rolledOverSelectedIcon == null) { + rolledOverSelectedIcon = getBrighterImage(selectedIcon); + } + + if (disabledSelectedIcon == null) { + disabledSelectedIcon = GrayFilter.createDisabledImage(selectedIcon); + } + + int x = 3; + int y = button.getHeight() / 2 - h / 2; + + String text = button.getText(); + if (text == null || text.equals("")) { + x = button.getWidth() / 2 - w / 2; + } + + boolean transitionState, actionEnabled, buttonEnabled; + if (toggleActionState == null) { + transitionState = false; + actionEnabled = false; + buttonEnabled = false; + } else { + transitionState = toggleActionState.isTransitionState(); + actionEnabled = toggleActionState.isActionEnabled(); + buttonEnabled = toggleActionState.isButtonEnabled(); + } + + boolean stopped = !transitionState && !actionEnabled; + boolean starting = transitionState && actionEnabled; + boolean started = !transitionState && actionEnabled; + boolean stopping = transitionState && !actionEnabled; + + if (!buttonEnabled) { + g.drawImage(disabledIcon, x, y, null); + } else if (stopped) { + if (model.isRollover()) { + g.drawImage(rollOverIcon, x, y, null); + } else { + g.drawImage(sourceIcon, x, y, null); + } + } else if (starting) { + g.drawImage(disabledIcon, x, y, null); + } else if (started) { + if (model.isRollover()) { + g.drawImage(rolledOverSelectedIcon, x, y, null); + } else { + g.drawImage(selectedIcon, x, y, null); + } + } else if (stopping) { + g.drawImage(disabledSelectedIcon, x, y, null); + } + } + + void setState(ToggleActionState toggleActionState) { + this.toggleActionState = toggleActionState; + } + +} diff -r 747a32443e86 -r ffd37dfffbdd thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadView.java --- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadView.java Tue Oct 13 11:44:25 2015 -0400 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadView.java Tue Oct 13 12:48:05 2015 -0400 @@ -36,6 +36,7 @@ package com.redhat.thermostat.thread.client.common.view; +import com.redhat.thermostat.client.core.ToggleActionState; import com.redhat.thermostat.client.core.views.BasicView; import com.redhat.thermostat.client.core.views.UIComponent; import com.redhat.thermostat.common.ActionListener; @@ -50,6 +51,40 @@ START_LIVE_RECORDING, STOP_LIVE_RECORDING }; + + public enum MonitoringState implements ToggleActionState { + STARTED(true, false, true), + STOPPED(true, false, false), + STARTING(true, true, true), + STOPPING(true, true, false), + DISABLED(false, false, false), + ; + + private final boolean isTransitionState; + private final boolean isActionEnabled; + private final boolean isButtonEnabled; + + MonitoringState(boolean isButtonEnabled, boolean isTransitionState, boolean isActionEnabled) { + this.isButtonEnabled = isButtonEnabled; + this.isTransitionState = isTransitionState; + this.isActionEnabled = isActionEnabled; + } + + @Override + public boolean isTransitionState() { + return isTransitionState; + } + + @Override + public boolean isActionEnabled() { + return isActionEnabled; + } + + @Override + public boolean isButtonEnabled() { + return isButtonEnabled; + } + } protected ApplicationService appService; protected String uniqueId; @@ -68,7 +103,7 @@ } public abstract void setEnableRecordingControl(boolean enable); - public abstract void setRecording(boolean recording, boolean notify); + public abstract void setRecording(MonitoringState monitoringState, boolean notify); public abstract ThreadTableView createThreadTableView(); public abstract ThreadTimelineView createThreadTimelineView(); diff -r 747a32443e86 -r ffd37dfffbdd thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java --- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java Tue Oct 13 11:44:25 2015 -0400 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java Tue Oct 13 12:48:05 2015 -0400 @@ -88,12 +88,10 @@ initControllers(); - view.setRecording(isRecording(), false); + view.setRecording(isRecording() ? ThreadView.MonitoringState.STARTED : ThreadView.MonitoringState.STOPPED, false); view.addThreadActionListener(new ThreadActionListener()); - if (!vmInfoDao.getVmInfo(ref).isAlive()) { - view.setEnableRecordingControl(false); - } + view.setEnableRecordingControl(vmInfoDao.getVmInfo(ref).isAlive()); } private boolean isRecording() { @@ -117,11 +115,15 @@ public void actionPerformed(ActionEvent actionEvent) { switch (actionEvent.getActionId()) { case START_LIVE_RECORDING: + view.setRecording(ThreadView.MonitoringState.STARTING, false); startHarvester(); + view.setRecording(ThreadView.MonitoringState.STARTED, false); break; case STOP_LIVE_RECORDING: + view.setRecording(ThreadView.MonitoringState.STOPPING, false); stopHarvester(); + view.setRecording(ThreadView.MonitoringState.STOPPED, false); break; default: @@ -137,7 +139,8 @@ boolean result = collector.startHarvester(); if (!result) { view.displayWarning(translator.localize(LocaleResources.WARNING_CANNOT_ENABLE)); - view.setRecording(false, false); + view.setEnableRecordingControl(false); + view.setRecording(ThreadView.MonitoringState.DISABLED, false); } } }, translator.localize(LocaleResources.STARTING_MONITORING)); @@ -150,7 +153,8 @@ boolean result = collector.stopHarvester(); if (!result) { view.displayWarning(translator.localize(LocaleResources.WARNING_CANNOT_DISABLE)); - view.setRecording(true, false); + view.setEnableRecordingControl(false); + view.setRecording(ThreadView.MonitoringState.DISABLED, false); } } }, translator.localize(LocaleResources.STOPPING_MONITORING)); diff -r 747a32443e86 -r ffd37dfffbdd thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java --- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java Tue Oct 13 11:44:25 2015 -0400 +++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java Tue Oct 13 12:48:05 2015 -0400 @@ -210,7 +210,7 @@ viewFactory, notifier); verify(collector).isHarvesterCollecting(); - verify(view, times(1)).setRecording(false, false); + verify(view, times(1)).setRecording(ThreadView.MonitoringState.STOPPED, false); threadActionListener = viewArgumentCaptor.getValue(); threadActionListener.actionPerformed(new ActionEvent<>(view, ThreadView.ThreadAction.START_LIVE_RECORDING)); @@ -218,7 +218,8 @@ verify(appExecutor, times(1)).execute(isA(Runnable.class)); longRunningTaskCaptor.getValue().run(); - verify(view, times(1)).setRecording(false, false); + verify(view, times(1)).setRecording(ThreadView.MonitoringState.STARTING, false); + verify(view, times(1)).setRecording(ThreadView.MonitoringState.STARTED, false); verify(collector).startHarvester(); threadActionListener.actionPerformed(new ActionEvent<>(view, ThreadView.ThreadAction.STOP_LIVE_RECORDING)); @@ -227,7 +228,8 @@ longRunningTaskCaptor.getValue().run(); verify(collector).stopHarvester(); - verify(view, times(1)).setRecording(false, false); + verify(view, times(1)).setRecording(ThreadView.MonitoringState.STOPPING, false); + verify(view, times(2)).setRecording(ThreadView.MonitoringState.STOPPED, false); threadActionListener.actionPerformed(new ActionEvent<>(view, ThreadView.ThreadAction.STOP_LIVE_RECORDING)); @@ -235,9 +237,8 @@ longRunningTaskCaptor.getValue().run(); verify(collector, times(2)).stopHarvester(); - verify(view, times(1)).setRecording(true, false); - - verify(view, times(0)).setEnableRecordingControl(false); + verify(view, times(2)).setRecording(ThreadView.MonitoringState.STOPPING, false); + verify(view, times(3)).setRecording(ThreadView.MonitoringState.STOPPED, false); } @Test diff -r 747a32443e86 -r ffd37dfffbdd thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java --- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java Tue Oct 13 11:44:25 2015 -0400 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java Tue Oct 13 12:48:05 2015 -0400 @@ -77,7 +77,7 @@ private JTabbedPane topPane; private JTabbedPane bottomPane; - + private static final Translate t = LocaleResources.createLocalizer(); private boolean skipNotification = false; @@ -85,6 +85,7 @@ private int threadDetailsPaneID = 0; private UIDefaults uiDefaults; + private boolean viewControlsEnabled = true; public SwingThreadView(UIDefaults uiDefaults) { @@ -94,13 +95,13 @@ // TODO use ComponentVisiblityNotifier instead // sadly, the BasicView.notifier field can not be accessed here panel.addHierarchyListener(new ComponentVisibleListener() { - + @Override public void componentShown(Component component) { SwingThreadView.this.notify(Action.VISIBLE); restoreDivider(); } - + @Override public void componentHidden(Component component) { SwingThreadView.this.notify(Action.HIDDEN); @@ -108,17 +109,15 @@ }); panel.getSplitPane().addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, - new PropertyChangeListener() - { - @Override - public void propertyChange(PropertyChangeEvent evt) { - JSplitPane sourceSplitPane = (JSplitPane) evt.getSource(); - saveDivider(sourceSplitPane.getDividerLocation()); - } - }); - + new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + JSplitPane sourceSplitPane = (JSplitPane) evt.getSource(); + saveDivider(sourceSplitPane.getDividerLocation()); + } + }); + panel.getToggleButton().setToolTipText(t.localize(LocaleResources.START_RECORDING).getContents()); - panel.getToggleButton().setText(t.localize(LocaleResources.THREAD_MONITOR_SWITCH).getContents()); panel.getToggleButton().addItemListener(new ItemListener() { @Override @@ -198,21 +197,23 @@ @Override public void setEnableRecordingControl(final boolean enable) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - panel.getToggleButton().setEnabled(enable); - } - }); + this.viewControlsEnabled = enable; + if (!enable) { + setRecording(MonitoringState.DISABLED, false); + } } @Override - public void setRecording(final boolean recording, final boolean notify) { + public void setRecording(final MonitoringState monitoringState, final boolean notify) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (!notify) skipNotification = true; - panel.getToggleButton().setSelected(recording); + if (!viewControlsEnabled) { + panel.getToggleButton().setToggleActionState(MonitoringState.DISABLED); + } else { + panel.getToggleButton().setToggleActionState(monitoringState); + } if (!notify) skipNotification = false; } }); diff -r 747a32443e86 -r ffd37dfffbdd thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadMainPanel.java --- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadMainPanel.java Tue Oct 13 11:44:25 2015 -0400 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadMainPanel.java Tue Oct 13 12:48:05 2015 -0400 @@ -44,12 +44,18 @@ import com.redhat.thermostat.client.swing.IconResource; import com.redhat.thermostat.client.swing.components.ActionToggleButton; +import com.redhat.thermostat.client.swing.components.FontAwesomeIcon; import com.redhat.thermostat.client.swing.components.HeaderPanel; +import com.redhat.thermostat.client.swing.components.Icon; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.thread.client.common.locale.LocaleResources; @SuppressWarnings("serial") class ThreadMainPanel extends JPanel { + + private static final Icon START_ICON = IconResource.SAMPLE.getIcon(); + private static final Icon STOP_ICON = new FontAwesomeIcon('\uf04d', START_ICON.getIconHeight()); + private static final Translate t = LocaleResources.createLocalizer(); private JSplitPane splitPane; @@ -64,7 +70,7 @@ JPanel content = new JPanel(); headerPanel.setContent(content); - toggleButton = new ActionToggleButton(IconResource.SAMPLE.getIcon()); + toggleButton = new ActionToggleButton(START_ICON, STOP_ICON, t.localize(LocaleResources.THREAD_MONITOR_SWITCH)); toggleButton.setName("recordButton"); headerPanel.addToolBarButton(toggleButton); diff -r 747a32443e86 -r ffd37dfffbdd thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java --- a/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java Tue Oct 13 11:44:25 2015 -0400 +++ b/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java Tue Oct 13 12:48:05 2015 -0400 @@ -48,6 +48,7 @@ import javax.swing.JFrame; +import com.redhat.thermostat.thread.client.common.view.ThreadView; import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner; import org.fest.swing.annotation.GUITest; @@ -147,11 +148,11 @@ // now try "programmatically" - view.setRecording(true, true); + view.setRecording(ThreadView.MonitoringState.STARTED, true); togglefixture.requireToolTip(t.localize(LocaleResources.STOP_RECORDING).getContents()); - view.setRecording(false, false); + view.setRecording(ThreadView.MonitoringState.STOPPED, false); togglefixture.requireToolTip(t.localize(LocaleResources.START_RECORDING).getContents()); } diff -r 747a32443e86 -r ffd37dfffbdd vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxNotificationsView.java --- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxNotificationsView.java Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxNotificationsView.java Tue Oct 13 12:48:05 2015 -0400 @@ -36,6 +36,7 @@ package com.redhat.thermostat.vm.jmx.client.core; +import com.redhat.thermostat.client.core.ToggleActionState; import com.redhat.thermostat.client.core.views.BasicView; import com.redhat.thermostat.client.core.views.UIComponent; import com.redhat.thermostat.common.ActionListener; @@ -48,11 +49,46 @@ TOGGLE_NOTIFICATIONS, } + public enum MonitoringState implements ToggleActionState { + STARTED(true, false, true), + STOPPED(true, false, false), + STARTING(true, true, true), + STOPPING(true, true, false), + DISABLED(false, false, false), + ; + + private final boolean isTransitionState; + private final boolean isActionEnabled; + private final boolean isButtonEnabled; + + MonitoringState(boolean isButtonEnabled, boolean isTransitionState, boolean isActionEnabled) { + this.isButtonEnabled = isButtonEnabled; + this.isTransitionState = isTransitionState; + this.isActionEnabled = isActionEnabled; + } + + @Override + public boolean isTransitionState() { + return isTransitionState; + } + + @Override + public boolean isActionEnabled() { + return isActionEnabled; + } + + @Override + public boolean isButtonEnabled() { + return isButtonEnabled; + } + } + + public abstract void addNotificationActionListener(ActionListener listener); public abstract void removeNotificationActionListener(ActionListener listener); public abstract void setViewControlsEnabled(boolean enabled); - public abstract void setNotificationsEnabled(boolean enabled); + public abstract void setMonitoringState(MonitoringState monitoringState); public abstract void clearNotifications(); public abstract void addNotification(JmxNotification data); public abstract void displayWarning(LocalizedString warning); diff -r 747a32443e86 -r ffd37dfffbdd vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewController.java --- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewController.java Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewController.java Tue Oct 13 12:48:05 2015 -0400 @@ -101,11 +101,15 @@ // Callbacks for toggle notifications final Runnable successAction = new Runnable() { - @Override public void run() { - notificationsEnabled.set(!notificationsEnabled.get()); - view.setNotificationsEnabled(notificationsEnabled.get()); + boolean val = notificationsEnabled.get(); + notificationsEnabled.set(!val); + if (val) { + view.setMonitoringState(JmxNotificationsView.MonitoringState.STOPPED); + } else { + view.setMonitoringState(JmxNotificationsView.MonitoringState.STARTED); + } } }; @@ -116,12 +120,11 @@ LocalizedString warning; if (notificationsEnabled.get()) { warning = t.localize(LocaleResources.NOTIFICATIONS_CANNOT_DISABLE); - } - else { + } else { warning = t.localize(LocaleResources.NOTIFICATIONS_CANNOT_ENABLE); } view.displayWarning(warning); - view.setNotificationsEnabled(notificationsEnabled.get()); + view.setViewControlsEnabled(false); } }; @@ -138,6 +141,11 @@ stopUpdatingView(); break; case VISIBLE: + if (notificationsEnabled.get() && isVmAlive()) { + view.setMonitoringState(JmxNotificationsView.MonitoringState.STARTED); + } else { + view.setMonitoringState(JmxNotificationsView.MonitoringState.STOPPED); + } view.setViewControlsEnabled(isVmAlive()); startUpdatingView(); break; @@ -149,6 +157,12 @@ @Override public void actionPerformed(ActionEvent actionEvent) { if (actionEvent.getActionId() == NotificationAction.TOGGLE_NOTIFICATIONS) { + final boolean enabled = notificationsEnabled.get(); + if (!enabled) { + view.setMonitoringState(JmxNotificationsView.MonitoringState.STARTING); + } else { + view.setMonitoringState(JmxNotificationsView.MonitoringState.STOPPING); + } // This can block on network, do outside EDT/UI thread appSvc.getApplicationExecutor().execute(new Runnable() { @Override @@ -179,8 +193,13 @@ return; } - notificationsEnabled.set(status.isEnabled()); - view.setNotificationsEnabled(notificationsEnabled.get()); + boolean monitoring = status.isEnabled(); + notificationsEnabled.set(monitoring); + if (monitoring) { + view.setMonitoringState(JmxNotificationsView.MonitoringState.STARTED); + } else { + view.setMonitoringState(JmxNotificationsView.MonitoringState.STOPPED); + } List notifications = dao.getNotifications(vm, lastTimeStamp); for (JmxNotification notification : notifications) { @@ -190,6 +209,8 @@ } }); + view.setViewControlsEnabled(true); + } private boolean isVmAlive() { diff -r 747a32443e86 -r ffd37dfffbdd vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewControllerTest.java --- a/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewControllerTest.java Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewControllerTest.java Tue Oct 13 12:48:05 2015 -0400 @@ -201,7 +201,7 @@ JmxNotificationStatus status = mock(JmxNotificationStatus.class); when(notificationDao.getLatestNotificationStatus(vm)).thenReturn(null); - verify(view, never()).setNotificationsEnabled(anyBoolean()); + verify(view, never()).setMonitoringState(any(JmxNotificationsView.MonitoringState.class)); verify(view, never()).addNotification(isA(JmxNotification.class)); } @@ -245,7 +245,8 @@ listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); verify(toggleReq).sendEnableNotificationsRequestToAgent(eq(vm), eq(true)); - verify(view).setNotificationsEnabled(true); + verify(view).setMonitoringState(JmxNotificationsView.MonitoringState.STARTING); + verify(view).setMonitoringState(JmxNotificationsView.MonitoringState.STARTED); } @Test @@ -258,9 +259,10 @@ listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); verify(toggleReq).sendEnableNotificationsRequestToAgent(vm, true); - verify(view, never()).setNotificationsEnabled(true); - verify(view).setNotificationsEnabled(false); - + verify(view).setMonitoringState(JmxNotificationsView.MonitoringState.STARTING); + verify(view, never()).setMonitoringState(JmxNotificationsView.MonitoringState.STARTED); + verify(view).setViewControlsEnabled(false); + ArgumentCaptor warningCaptor = ArgumentCaptor.forClass(LocalizedString.class); verify(view).displayWarning(warningCaptor.capture()); assertEquals(translator.localize(LocaleResources.NOTIFICATIONS_CANNOT_ENABLE).getContents(), @@ -280,7 +282,8 @@ listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); verify(toggleReq).sendEnableNotificationsRequestToAgent(vm, false); - verify(view).setNotificationsEnabled(false); + verify(view).setMonitoringState(JmxNotificationsView.MonitoringState.STOPPING); + verify(view).setMonitoringState(JmxNotificationsView.MonitoringState.STOPPED); } @Test @@ -339,8 +342,10 @@ listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); verify(toggleReq).sendEnableNotificationsRequestToAgent(vm, false); - verify(view, never()).setNotificationsEnabled(false); - verify(view, times(2)).setNotificationsEnabled(true); + verify(view, times(1)).setMonitoringState(JmxNotificationsView.MonitoringState.STARTED); + verify(view, times(1)).setMonitoringState(JmxNotificationsView.MonitoringState.STOPPING); + verify(view, times(1)).setMonitoringState(JmxNotificationsView.MonitoringState.STARTING); + verify(view).setViewControlsEnabled(false); ArgumentCaptor warningCaptor = ArgumentCaptor.forClass(LocalizedString.class); verify(view).displayWarning(warningCaptor.capture()); diff -r 747a32443e86 -r ffd37dfffbdd vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingView.java --- a/vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingView.java Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingView.java Tue Oct 13 12:48:05 2015 -0400 @@ -67,7 +67,9 @@ import com.redhat.thermostat.client.swing.IconResource; import com.redhat.thermostat.client.swing.SwingComponent; import com.redhat.thermostat.client.swing.components.ActionToggleButton; +import com.redhat.thermostat.client.swing.components.FontAwesomeIcon; import com.redhat.thermostat.client.swing.components.HeaderPanel; +import com.redhat.thermostat.client.swing.components.Icon; import com.redhat.thermostat.client.swing.components.LocalizedLabel; import com.redhat.thermostat.client.swing.components.experimental.EventTimeline; import com.redhat.thermostat.client.swing.components.experimental.EventTimelineRangeChangeListener; @@ -89,6 +91,9 @@ private static final Translate translate = LocaleResources.createLocalizer(); private List> listeners = new CopyOnWriteArrayList<>(); + private static final Icon START_ICON = IconResource.SAMPLE.getIcon(); + private static final Icon STOP_ICON = new FontAwesomeIcon('\uf04d', START_ICON.getIconHeight()); + private final HeaderPanel visiblePanel; private ActionToggleButton toolbarButton; @@ -98,6 +103,7 @@ private DetailPanel timelineDetails; private Timeline ruler; private EventTimeline timeline; + private boolean viewControlsEnabled = true; public JmxNotificationsSwingView() { @@ -155,7 +161,7 @@ new ComponentVisibilityNotifier().initialize(contents, notifier); - toolbarButton = new ActionToggleButton(IconResource.SAMPLE.getIcon(), translate.localize(LocaleResources.NOTIFICATIONS_ENABLE)); + toolbarButton = new ActionToggleButton(START_ICON, STOP_ICON, translate.localize(LocaleResources.NOTIFICATIONS_ENABLE)); toolbarButton.setName("toggleNotifications"); toolbarButton.setToolTipText(translate.localize(LocaleResources.NOTIFICATIONS_ENABLE_DESCRIPTION).getContents()); toolbarButton.addActionListener(new java.awt.event.ActionListener() { @@ -199,30 +205,32 @@ listeners.remove(listener); } - private void fireNotificationAction(NotificationAction action) { - for (ActionListener listener : listeners) { - listener.actionPerformed(new ActionEvent<>(this, action)); - } - } - @Override - public void setViewControlsEnabled(final boolean enabled) { + public void setMonitoringState(final MonitoringState monitoringState) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - toolbarButton.setEnabled(enabled); + if (!viewControlsEnabled) { + toolbarButton.setToggleActionState(MonitoringState.DISABLED); + } else { + toolbarButton.setToggleActionState(monitoringState); + } } }); } @Override - public void setNotificationsEnabled(final boolean enabled) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - toolbarButton.setSelected(enabled); - } - }); + public void setViewControlsEnabled(boolean enabled) { + this.viewControlsEnabled = enabled; + if (!enabled) { + setMonitoringState(MonitoringState.DISABLED); + } + } + + private void fireNotificationAction(NotificationAction action) { + for (ActionListener listener : listeners) { + listener.actionPerformed(new ActionEvent<>(this, action)); + } } @Override diff -r 747a32443e86 -r ffd37dfffbdd vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/LocaleResources.java --- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/LocaleResources.java Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/LocaleResources.java Tue Oct 13 12:48:05 2015 -0400 @@ -47,6 +47,7 @@ PROFILER_CURRENT_STATUS_ACTIVE, PROFILER_CURRENT_STATUS_INACTIVE, + PROFILER_CURRENT_STATUS_DEAD, START_PROFILING, STARTING_PROFILING, STOP_PROFILING, diff -r 747a32443e86 -r ffd37dfffbdd vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java --- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java Tue Oct 13 12:48:05 2015 -0400 @@ -69,9 +69,13 @@ import javax.swing.table.DefaultTableModel; import com.redhat.thermostat.client.swing.EdtHelper; +import com.redhat.thermostat.client.swing.IconResource; import com.redhat.thermostat.client.swing.NonEditableTableModel; import com.redhat.thermostat.client.swing.SwingComponent; +import com.redhat.thermostat.client.swing.components.ActionToggleButton; +import com.redhat.thermostat.client.swing.components.FontAwesomeIcon; import com.redhat.thermostat.client.swing.components.HeaderPanel; +import com.redhat.thermostat.client.swing.components.Icon; import com.redhat.thermostat.client.swing.components.ThermostatScrollPane; import com.redhat.thermostat.client.swing.components.ThermostatTable; import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier; @@ -86,6 +90,9 @@ public class SwingVmProfileView extends VmProfileView implements SwingComponent { + private static final Icon START_ICON = IconResource.SAMPLE.getIcon(); + private static final Icon STOP_ICON = new FontAwesomeIcon('\uf04d', START_ICON.getIconHeight()); + private static final Translate translator = LocaleResources.createLocalizer(); private static final double SPLIT_PANE_RATIO = 0.3; @@ -94,8 +101,7 @@ private HeaderPanel mainContainer; - private JToggleButton startButton; - private JToggleButton stopButton; + private ActionToggleButton toggleButton; private DefaultListModel listModel; private JList profileList; @@ -103,6 +109,7 @@ private DefaultTableModel tableModel; private JLabel currentStatusLabel; + private boolean viewControlsEnabled = true; static class ProfileItemRenderer extends DefaultListCellRenderer { @Override @@ -123,22 +130,37 @@ public SwingVmProfileView() { listModel = new DefaultListModel<>(); + toggleButton = new ActionToggleButton(START_ICON, STOP_ICON, translator.localize(LocaleResources.START_PROFILING)); + toggleButton.toggleText(false); + toggleButton.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + JToggleButton button = (JToggleButton) e.getSource(); + if (button.isSelected()) { + fireProfileAction(ProfileAction.START_PROFILING); + } else { + fireProfileAction(ProfileAction.STOP_PROFILING); + } + } + }); + mainContainer = new HeaderPanel(translator.localize(LocaleResources.PROFILER_HEADING)); new ComponentVisibilityNotifier().initialize(mainContainer, notifier); JPanel contentContainer = new JPanel(new BorderLayout()); mainContainer.setContent(contentContainer); + mainContainer.addToolBarButton(toggleButton); - JComponent actionsPanel = createActionsPanel(); + JComponent actionsPanel = createStatusPanel(); contentContainer.add(actionsPanel, BorderLayout.PAGE_START); JComponent profilingResultsPanel = createInformationPanel(); contentContainer.add(profilingResultsPanel, BorderLayout.CENTER); } - private JPanel createActionsPanel() { + private JPanel createStatusPanel() { GridBagLayout layout = new GridBagLayout(); - JPanel actionsPanel = new JPanel(layout); + JPanel statusPanel = new JPanel(layout); GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; @@ -151,40 +173,14 @@ String wrappedText = "" + translator.localize(LocaleResources.PROFILER_DESCRIPTION).getContents() + ""; JLabel descriptionLabel = new JLabel(wrappedText); - actionsPanel.add(descriptionLabel, constraints); + statusPanel.add(descriptionLabel, constraints); constraints.gridy = 1; constraints.gridx = 0; constraints.gridwidth = 1; currentStatusLabel = new JLabel("Current Status: {0}"); - actionsPanel.add(currentStatusLabel, constraints); - - constraints.fill = GridBagConstraints.NONE; - constraints.gridx = GridBagConstraints.RELATIVE; - constraints.weightx = 0.0; - startButton = new JToggleButton(translator.localize(LocaleResources.START_PROFILING).getContents()); - startButton.addActionListener(new java.awt.event.ActionListener() { - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - JToggleButton button = (JToggleButton) e.getSource(); - if (button.isSelected()) { - fireProfileAction(ProfileAction.START_PROFILING); - } - } - }); - actionsPanel.add(startButton, constraints); - stopButton = new JToggleButton(translator.localize(LocaleResources.STOP_PROFILING).getContents()); - stopButton.addActionListener(new java.awt.event.ActionListener() { - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - JToggleButton button = (JToggleButton) e.getSource(); - if (button.isSelected()) { - fireProfileAction(ProfileAction.STOP_PROFILING); - } - } - }); - actionsPanel.add(stopButton, constraints); - return actionsPanel; + statusPanel.add(currentStatusLabel, constraints); + return statusPanel; } private JComponent createInformationPanel() { @@ -278,40 +274,38 @@ } @Override - public void enableStartProfiling(final boolean start) { + public void setProfilingState(final ProfilingState profilingState) { + final String status, buttonLabel; + if (!viewControlsEnabled) { + status = translator.localize(LocaleResources.PROFILER_CURRENT_STATUS_DEAD).getContents(); + buttonLabel = translator.localize(LocaleResources.START_PROFILING).getContents(); + } else if (profilingState == ProfilingState.STOPPING || profilingState == ProfilingState.STARTED) { + status = translator.localize(LocaleResources.PROFILER_CURRENT_STATUS_ACTIVE).getContents(); + buttonLabel = translator.localize(LocaleResources.STOP_PROFILING).getContents(); + } else { + status = translator.localize(LocaleResources.PROFILER_CURRENT_STATUS_INACTIVE).getContents(); + buttonLabel = translator.localize(LocaleResources.START_PROFILING).getContents(); + } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - startButton.setEnabled(start); + currentStatusLabel.setText(status); + if (!viewControlsEnabled) { + toggleButton.setToggleActionState(ProfilingState.DISABLED); + } else { + toggleButton.setToggleActionState(profilingState); + } + toggleButton.setText(buttonLabel); } }); } @Override - public void enableStopProfiling(final boolean stop) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - stopButton.setEnabled(stop); - } - }); - } - - @Override - public void setProfilingStatus(final String text, final boolean active) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - currentStatusLabel.setText(text); - if (active) { - startButton.setSelected(true); - stopButton.setSelected(false); - } else { - startButton.setSelected(false); - stopButton.setSelected(true); - } - } - }); + public void setViewControlsEnabled(boolean enabled) { + this.viewControlsEnabled = enabled; + if (!enabled) { + setProfilingState(ProfilingState.DISABLED); + } } @Override diff -r 747a32443e86 -r ffd37dfffbdd vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java --- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java Tue Oct 13 12:48:05 2015 -0400 @@ -74,6 +74,7 @@ import com.redhat.thermostat.vm.profiler.client.core.ProfilingResultParser; import com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.Profile; import com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.ProfileAction; +import com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.ProfilingState; import com.redhat.thermostat.vm.profiler.common.ProfileDAO; import com.redhat.thermostat.vm.profiler.common.ProfileInfo; import com.redhat.thermostat.vm.profiler.common.ProfileRequest; @@ -148,6 +149,7 @@ updater.stop(); break; case VISIBLE: + view.setViewControlsEnabled(isAlive()); updater.start(); break; default: @@ -162,10 +164,10 @@ ProfileAction id = actionEvent.getActionId(); switch (id) { case START_PROFILING: - startProfiling(view); + startProfiling(); break; case STOP_PROFILING: - stopProfiling(view); + stopProfiling(); break; case PROFILE_SELECTED: updateViewWithProfileRunData(); @@ -177,20 +179,19 @@ }); + view.setViewControlsEnabled(isAlive()); } - private void startProfiling(final VmProfileView view) { - disableViewControlsAndSendRequest(view, true); + private void startProfiling() { + setProgressNotificationAndSendRequest(true); } - private void stopProfiling(final VmProfileView view) { - disableViewControlsAndSendRequest(view, false); + private void stopProfiling() { + setProgressNotificationAndSendRequest(false); } - private void disableViewControlsAndSendRequest(VmProfileView view, boolean start) { + private void setProgressNotificationAndSendRequest(boolean start) { showProgressNotification(start); - // disable the UI until we get a update in storage - disableViewControls(); sendProfilingRequest(start); } @@ -202,15 +203,15 @@ @Override public void fireComplete(Request request, Response response) { switch (response.getType()) { - case OK: - updateViewWithCurrentProfilingStatus(); - break; - default: - // FIXME show message to user + case OK: + updateViewWithCurrentProfilingStatus(); + break; + default: + // FIXME show message to user - hideProgressNotificationIfVisible(); - profilingStartOrStopRequested = false; - break; + hideProgressNotificationIfVisible(); + profilingStartOrStopRequested = false; + break; } } }); @@ -219,54 +220,46 @@ } private void updateViewWithCurrentProfilingStatus() { - boolean currentlyActive = false; + ProfilingState profilingState = ProfilingState.STOPPED; ProfileStatusChange currentStatus = profileDao.getLatestStatus(vm); if (currentStatus != null) { - currentlyActive = currentStatus.isStarted(); + boolean currentlyActive = currentStatus.isStarted(); + if (currentlyActive && profilingStartOrStopRequested) { + profilingState = ProfilingState.STARTING; + } else if (currentlyActive) { + profilingState = ProfilingState.STARTED; + } else if (profilingStartOrStopRequested) { + profilingState = ProfilingState.STOPPING; + } else { + profilingState = ProfilingState.STOPPED; + } } - String message; - if (currentlyActive) { - message = translator.localize(LocaleResources.PROFILER_CURRENT_STATUS_ACTIVE).getContents(); - } else { - message = translator.localize(LocaleResources.PROFILER_CURRENT_STATUS_INACTIVE).getContents(); - } - + view.setViewControlsEnabled(isAlive()); if (!isAlive()) { - disableViewControls(); - view.setProfilingStatus(message, currentlyActive); + view.setProfilingState(ProfilingState.DISABLED); } else if (profilingStartOrStopRequested) { boolean statusChanged = (previousStatus == null && currentStatus != null) || (currentStatus != null && !(currentStatus.equals(previousStatus))); if (statusChanged) { - enableViewControlsFor(currentlyActive); - view.setProfilingStatus(message, currentlyActive); + view.setProfilingState(profilingState); profilingStartOrStopRequested = false; hideProgressNotificationIfVisible(); } } else { - enableViewControlsFor(currentlyActive); - view.setProfilingStatus(message, currentlyActive); + view.setProfilingState(profilingState); } previousStatus = currentStatus; } - private void disableViewControls() { - view.enableStartProfiling(false); - view.enableStopProfiling(false); - } - - private void enableViewControlsFor(boolean currentlyActive) { - view.enableStartProfiling(!currentlyActive); - view.enableStopProfiling(currentlyActive); - } - private void showProgressNotification(boolean start) { if (start) { + view.setProfilingState(ProfilingState.STARTING); progressDisplay = new ProgressHandle(translator.localize(LocaleResources.STARTING_PROFILING)); } else { + view.setProfilingState(ProfilingState.STOPPING); progressDisplay = new ProgressHandle(translator.localize(LocaleResources.STOPPING_PROFILING)); } progressDisplay.setIndeterminate(true); diff -r 747a32443e86 -r ffd37dfffbdd vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java --- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java Tue Oct 13 12:48:05 2015 -0400 @@ -41,6 +41,7 @@ import com.redhat.thermostat.client.core.views.BasicView; import com.redhat.thermostat.client.core.views.UIComponent; +import com.redhat.thermostat.client.core.ToggleActionState; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult; @@ -81,6 +82,40 @@ PROFILE_SELECTED, } + enum ProfilingState implements ToggleActionState { + STARTED(true, false, true), + STOPPED(true, false, false), + STARTING(true, true, true), + STOPPING(true, true, false), + DISABLED(false, false, false), + ; + + private final boolean isTransitionState; + private final boolean isActionEnabled; + private final boolean isButtonEnabled; + + ProfilingState(boolean isButtonEnabled, boolean isTransitionState, boolean isActionEnabled) { + this.isButtonEnabled = isButtonEnabled; + this.isTransitionState = isTransitionState; + this.isActionEnabled = isActionEnabled; + } + + @Override + public boolean isTransitionState() { + return isTransitionState; + } + + @Override + public boolean isActionEnabled() { + return isActionEnabled; + } + + @Override + public boolean isButtonEnabled() { + return isButtonEnabled; + } + } + public abstract void addProfileActionListener(ActionListener listener); public abstract void removeProfileActionlistener(ActionListener listener); @@ -91,11 +126,8 @@ * indicating profiling in the UI */ - /** Enable (or disable) UI that starts profiling */ - public abstract void enableStartProfiling(boolean start); - /** Enable (or disable) UI that stops profiling */ - public abstract void enableStopProfiling(boolean stop); - public abstract void setProfilingStatus(String text, boolean enabled); + public abstract void setProfilingState(ProfilingState profilingState); + public abstract void setViewControlsEnabled(boolean enabled); public abstract void setAvailableProfilingRuns(List data); diff -r 747a32443e86 -r ffd37dfffbdd vm-profiler/client-swing/src/main/resources/com/redhat/thermostat/vm/profiler/client/swing/internal/strings.properties --- a/vm-profiler/client-swing/src/main/resources/com/redhat/thermostat/vm/profiler/client/swing/internal/strings.properties Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-profiler/client-swing/src/main/resources/com/redhat/thermostat/vm/profiler/client/swing/internal/strings.properties Tue Oct 13 12:48:05 2015 -0400 @@ -7,6 +7,7 @@ PROFILER_CURRENT_STATUS_ACTIVE = Currently profiling: yes PROFILER_CURRENT_STATUS_INACTIVE = Currently profiling: no +PROFILER_CURRENT_STATUS_DEAD = Selected VM is inactive START_PROFILING = Start Profiling STARTING_PROFILING = Starting profiling\u2026 STOP_PROFILING = Stop Profiling diff -r 747a32443e86 -r ffd37dfffbdd vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java --- a/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java Tue Oct 13 11:44:25 2015 -0400 +++ b/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java Tue Oct 13 12:48:05 2015 -0400 @@ -41,6 +41,7 @@ import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -182,7 +183,7 @@ ProfileInfo profile = new ProfileInfo(AGENT_ID, VM_ID, PROFILE_TIMESTAMP, PROFILE_ID); when(profileDao.getAllProfileInfo(vm, - new Range<>(SOME_TIMESTAMP - TimeUnit.DAYS.toMillis(1) , SOME_TIMESTAMP))) + new Range<>(SOME_TIMESTAMP - TimeUnit.DAYS.toMillis(1), SOME_TIMESTAMP))) .thenReturn(Arrays.asList(profile)); ProfileStatusChange status = new ProfileStatusChange(AGENT_ID, VM_ID, PROFILE_TIMESTAMP, false); @@ -197,9 +198,7 @@ assertEquals(1, resultList.size()); assertEquals(PROFILE_TIMESTAMP, resultList.get(0).timeStamp); - verify(view).setProfilingStatus("Currently profiling: no", false); - verify(view).enableStartProfiling(true); - verify(view).enableStopProfiling(false); + verify(view, times(2)).setViewControlsEnabled(true); } @Test @@ -215,8 +214,6 @@ Runnable runnable = runnableCaptor.getValue(); runnable.run(); - verify(view).enableStartProfiling(false); - verify(view).enableStopProfiling(false); } @Test @@ -228,14 +225,13 @@ listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, ProfileAction.START_PROFILING)); - verify(view).enableStartProfiling(false); - verify(view).enableStopProfiling(false); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(Request.class); verify(queue).putRequest(requestCaptor.capture()); Request expectedRequest = ProfileRequest.create(AGENT_ADDRESS, VM_ID, ProfileRequest.START_PROFILING); Request actualRequest = requestCaptor.getValue(); assertRequestEquals(actualRequest, expectedRequest); + + verify(view).setProfilingState(VmProfileView.ProfilingState.STARTING); } @Test @@ -247,9 +243,6 @@ listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, ProfileAction.START_PROFILING)); - verify(view).enableStartProfiling(false); - verify(view).enableStopProfiling(false); - ProfileStatusChange status = new ProfileStatusChange(AGENT_ID, VM_ID, PROFILE_TIMESTAMP, true); when(profileDao.getLatestStatus(vm)).thenReturn(status); @@ -258,7 +251,7 @@ runnableCaptor.getValue().run(); - verify(view).enableStopProfiling(true); + verify(view, times(2)).setProfilingState(VmProfileView.ProfilingState.STARTING); } @Test @@ -277,6 +270,8 @@ Request actualRequest = requestCaptor.getValue(); assertRequestEquals(actualRequest, expectedRequest); + + verify(view).setProfilingState(VmProfileView.ProfilingState.STOPPING); } @Test @@ -288,8 +283,7 @@ listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, ProfileAction.STOP_PROFILING)); - verify(view).enableStartProfiling(false); - verify(view).enableStopProfiling(false); + verify(view).setProfilingState(VmProfileView.ProfilingState.STOPPING); ProfileStatusChange status = new ProfileStatusChange(AGENT_ID, VM_ID, PROFILE_TIMESTAMP, false); when(profileDao.getLatestStatus(vm)).thenReturn(status); @@ -299,7 +293,7 @@ runnableCaptor.getValue().run(); - verify(view).enableStartProfiling(true); + verify(view, times(2)).setProfilingState(VmProfileView.ProfilingState.STOPPING); } @Test