/***********************************************************************************
*
* Copyright (c) 2014 Kamil Baczkowicz
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
*
* Kamil Baczkowicz - initial API and implementation and/or initial documentation
*
*/
package pl.baczkowicz.mqttspy.ui.utils;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.Tab;
import javafx.scene.paint.Color;
import pl.baczkowicz.mqttspy.configuration.ConfigurationManager;
import pl.baczkowicz.mqttspy.configuration.generated.TabbedSubscriptionDetails;
import pl.baczkowicz.mqttspy.connectivity.MqttAsyncConnection;
import pl.baczkowicz.mqttspy.connectivity.MqttSubscription;
import pl.baczkowicz.mqttspy.messages.FormattedMqttMessage;
import pl.baczkowicz.mqttspy.stats.StatisticsManager;
import pl.baczkowicz.mqttspy.ui.ConnectionController;
import pl.baczkowicz.mqttspy.ui.SubscriptionController;
import pl.baczkowicz.mqttspy.ui.charts.ChartFactory;
import pl.baczkowicz.mqttspy.ui.charts.ChartMode;
import pl.baczkowicz.mqttspy.ui.connections.ConnectionManager;
import pl.baczkowicz.mqttspy.ui.connections.SubscriptionManager;
import pl.baczkowicz.mqttspy.ui.events.ShowEditConnectionsWindowEvent;
import pl.baczkowicz.spy.eventbus.IKBus;
import pl.baczkowicz.spy.ui.events.ClearTabEvent;
import pl.baczkowicz.spy.ui.panes.PaneVisibilityStatus;
import pl.baczkowicz.spy.ui.panes.TitledPaneStatus;
import pl.baczkowicz.spy.ui.utils.DialogFactory;
import pl.baczkowicz.spy.ui.utils.TabUtils;
import pl.baczkowicz.spy.ui.utils.UiUtils;
/**
* Context menu utils - mainly for creating various context menus.
*/
public class ContextMenuUtils
{
private final static Logger logger = LoggerFactory.getLogger(ContextMenuUtils.class);
/**
* Create context menu for the subscription tab.
*
* @param connection The connection associated with the tab
* @param subscription The subscription associated with the tab
* @param eventManager The global event manager
* @param subscriptionManager The connection's subscription manager
* @param configurationManager The global configuration manager
* @param subscriptionController The subscription controller for this subscription
*
* @return The created context menu
*/
public static ContextMenu createSubscriptionTabContextMenu(
final MqttAsyncConnection connection,
final MqttSubscription subscription,
final IKBus eventBus,
final SubscriptionManager subscriptionManager,
final ConfigurationManager configurationManager,
final SubscriptionController subscriptionController)
{
final ContextMenu contextMenu = new ContextMenu();
// Cancel
MenuItem cancelItem = new MenuItem("Unsubscribe (and keep the tab)");
cancelItem.setDisable(false);
cancelItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
connection.unsubscribe(subscription, true);
}
});
contextMenu.getItems().add(cancelItem);
// Re-subscribe
MenuItem resubscribeItem = new MenuItem("Re-subscribe");
resubscribeItem.setDisable(true);
resubscribeItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
connection.resubscribe(subscription);
}
});
contextMenu.getItems().add(resubscribeItem);
// Close
MenuItem closeItem = new MenuItem("Unsubscribe (and close tab)");
closeItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
subscriptionManager.removeSubscription(subscription.getTopic());
}
});
contextMenu.getItems().add(closeItem);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
// Copy subscription topic string
final MenuItem copyTopicItem = new MenuItem("Copy subscription topic to clipboard");
copyTopicItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
UiUtils.copyToClipboard(subscription.getTopic());
}
});
contextMenu.getItems().add(copyTopicItem);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
// Clear data
MenuItem clearItem = new MenuItem("Clear subscription history");
clearItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
eventBus.publish(new ClearTabEvent(subscription.getStore()));
// eventManager.notifyClearHistory(subscription.getStore());
StatisticsManager.resetMessagesReceived(connection.getId(), subscription.getTopic());
subscription.getStore().clear();
}
});
contextMenu.getItems().add(clearItem);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
final Menu chartsMenu = new Menu("Charts");
contextMenu.getItems().add(chartsMenu);
// Message load charts
MenuItem messageLoadChartItem = new MenuItem("Show message load chart");
messageLoadChartItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
new ChartFactory<FormattedMqttMessage>().createMessageBasedLineChart(
Arrays.asList(SubscriptionController.AVG5_TOPIC, SubscriptionController.AVG30_TOPIC, SubscriptionController.AVG300_TOPIC),
subscriptionController.getStatsHistory(),
ChartMode.USER_DRIVEN_MSG_PAYLOAD,
"Series", "Load", "msgs/s",
"Message load statistics for " + subscription.getTopic() + " - " + connection.getName(),
subscriptionController.getScene(), eventBus);
}
});
chartsMenu.getItems().add(messageLoadChartItem);
// Message count charts
MenuItem messageCountChartItem = new MenuItem("Show message count chart");
messageCountChartItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
new ChartFactory<FormattedMqttMessage>().createMessageBasedPieChart("Message count statistics for " + subscription.getTopic(),
subscriptionController.getScene(),
subscriptionController.getSubscription().getStore().getNonFilteredMessageList().getTopicSummary().getObservableMessagesPerTopic());
}
});
chartsMenu.getItems().add(messageCountChartItem);
final Menu configuration = new Menu("Configuration");
contextMenu.getItems().add(configuration);
// Separator
// configuration.getItems().add(new SeparatorMenuItem());
// Adds/updates this subscription in the configuration file
final MenuItem addItem = new MenuItem("Add/update this subscription");
addItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
configurationManager.updateSubscriptionConfiguration(connection, subscription);
}
});
configuration.getItems().add(addItem);
// Removes this subscription from the configuration file
final MenuItem removeItem = new MenuItem("Remove this subscription");
removeItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
configurationManager.deleteSubscriptionConfiguration(connection, subscription);
}
});
configuration.getItems().add(removeItem);
final Menu view = new Menu("View");
contextMenu.getItems().add(view);
// Change color
final MenuItem changeColorMenu = new MenuItem("Change tab color");
changeColorMenu.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
final Color newColor =
DialogFactory.createColorChoiceDialog(subscription.getColor(),
"Select new subscription color", "Subscription color: ");
if (!newColor.equals(subscription.getColor()))
{
subscription.setColor(newColor);
subscription.getSubscriptionController().getTab().setStyle(StylingUtils.createBaseRGBString(newColor));
// Update subscription tab
subscription.getSubscriptionController().getSummaryTablePaneController().refreshRowStyling();
// Update 'all' tab
subscriptionManager.getSubscriptionControllersMap().
get(SubscriptionManager.ALL_SUBSCRIPTIONS_TAB_TITLE).getSummaryTablePaneController().refreshRowStyling();
}
}
});
view.getItems().add(changeColorMenu);
// View
final MenuItem detachMenu = new MenuItem("Detach to a separate window");
detachMenu.setOnAction(TabUtils.createTabDetachEvent(
detachMenu, subscriptionController,
connection.getName() + " - " + subscription.getTopic(), 5));
view.getItems().add(detachMenu);
return contextMenu;
}
/**
* Creates a context menu for the 'all' tab of the connection.
*
* @param connection The connection associated with this tab
* @param eventManager The global event manager
* @param subscriptionManager The connection's subscription manager
* @param configurationManager The global configuration manager
*
* @return Created context menu
*/
public static ContextMenu createAllSubscriptionsTabContextMenu(
final MqttAsyncConnection connection,
final IKBus eventBus,
final SubscriptionManager subscriptionManager,
final ConfigurationManager configurationManager,
final SubscriptionController subscriptionController)
{
final ContextMenu contextMenu = new ContextMenu();
MenuItem cancelItem = new MenuItem("Unsubscribe from all active subscriptions (if any)");
cancelItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
connection.unsubscribeAll(true);
}
});
contextMenu.getItems().add(cancelItem);
MenuItem resubscribeItem = new MenuItem("Re-subscribe to all non-active subscriptions (if any)");
resubscribeItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
connection.resubscribeAll(false);
}
});
contextMenu.getItems().add(resubscribeItem);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
// Clear data
MenuItem clearItem = new MenuItem("Clear tab history");
clearItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
eventBus.publish(new ClearTabEvent(connection.getStore()));
// eventManager.notifyClearHistory(connection.getStore());
StatisticsManager.resetMessagesReceived(connection.getId());
connection.getStore().clear();
}
});
contextMenu.getItems().add(clearItem);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
final Menu charts = new Menu("Charts");
MenuItem showAllChartItem = new MenuItem("Show overall message load chart");
showAllChartItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
new ChartFactory<FormattedMqttMessage>().createMessageBasedLineChart(
Arrays.asList(SubscriptionController.AVG5_TOPIC, SubscriptionController.AVG30_TOPIC, SubscriptionController.AVG300_TOPIC),
subscriptionController.getStatsHistory(),
ChartMode.USER_DRIVEN_MSG_PAYLOAD,
"Series", "Load", "msgs/s",
"Message load statistics for all subscriptions - " + connection.getName(),
subscriptionController.getScene(), eventBus);
}
});
contextMenu.getItems().add(charts);
charts.getItems().add(showAllChartItem);
final Menu configuration = new Menu("Configuration");
// Adds/updates this subscription in the configuration file
final MenuItem addItem = new MenuItem("Add/update all shown subscriptions");
addItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
for (final SubscriptionController controller : subscriptionManager.getSubscriptionControllers())
{
if (controller.getSubscription() != null)
{
configurationManager.updateSubscriptionConfiguration(connection, controller.getSubscription());
}
}
}
});
contextMenu.getItems().add(configuration);
configuration.getItems().add(addItem);
// Removes this subscription from the configuration file
final MenuItem removeItem = new MenuItem("Remove all shown subscriptions");
removeItem.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
for (final SubscriptionController controller : subscriptionManager.getSubscriptionControllers())
{
if (controller.getSubscription() != null)
{
configurationManager.deleteSubscriptionConfiguration(connection, controller.getSubscription());
}
}
}
});
configuration.getItems().add(removeItem);
return contextMenu;
}
private static Menu createConnectionPaneMenu(final String name,
final ConnectionController connectionController,
final TitledPaneStatus status)
{
final Menu menu = new Menu(name);
status.setContentMenu(menu);
final CheckMenuItem hidden = new CheckMenuItem("Hidden");
hidden.setSelected(true);
final CheckMenuItem visible = new CheckMenuItem("Visible (attached to connection tab)");
final CheckMenuItem detached = new CheckMenuItem("Visible (detached from connection tab)");
menu.getItems().add(hidden);
menu.getItems().add(visible);
menu.getItems().add(detached);
hidden.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
connectionController.setPaneVisiblity(status, PaneVisibilityStatus.NOT_VISIBLE);
}
});
visible.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
connectionController.setPaneVisiblity(status, PaneVisibilityStatus.ATTACHED);
}
});
detached.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
connectionController.setPaneVisiblity(status, PaneVisibilityStatus.DETACHED);
}
});
return menu;
}
/**
* Creates a context menu for the connection tab.
*
* @param connection The connection associated with this tab
* @param connectionController The ConnectionController for this tab's pane
* @param connectionManager The global connection manager
*
* @return Created context menu
*/
public static ContextMenu createConnectionMenu(final MqttAsyncConnection connection, final IKBus eventBus,
final ConnectionController connectionController, final ConnectionManager connectionManager)
{
// Context menu
ContextMenu contextMenu = new ContextMenu();
MenuItem reconnectItem = new MenuItem("Connect / reconnect");
reconnectItem.setOnAction(ActionUtils.createConnectAction(connectionManager, connection));
MenuItem disconnectItem = new MenuItem("Disconnect (and keep tab)");
disconnectItem.setOnAction(ActionUtils.createDisconnectAction(connectionManager, connection));
MenuItem disconnectAndCloseItem = new MenuItem("Disconnect (and close tab)");
disconnectAndCloseItem.setOnAction(ActionUtils.createDisconnectAndCloseAction(connectionManager, connection));
contextMenu.getItems().add(reconnectItem);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
contextMenu.getItems().add(disconnectItem);
contextMenu.getItems().add(disconnectAndCloseItem);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
// Show statistics
final MenuItem stats = new MenuItem("Show broker's statistics");
stats.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
final TabbedSubscriptionDetails subscriptionDetails = new TabbedSubscriptionDetails();
subscriptionDetails.setTopic("$SYS/#");
subscriptionDetails.setQos(0);
connectionController.getNewSubscriptionPaneController().subscribe(subscriptionDetails, true);
}
});
contextMenu.getItems().add(stats);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
final Menu view = new Menu("View");
contextMenu.getItems().add(view);
// View
final MenuItem detachMenu = new MenuItem("Detach to a separate window");
detachMenu.setOnAction(TabUtils.createTabDetachEvent(
detachMenu, connectionController,
"Connection " + connection.getName(), 0));
view.getItems().add(detachMenu);
// Separator
view.getItems().add(new SeparatorMenuItem());
final MenuItem detailedView = new MenuItem("Toggle between simplified and detailed views (QoS, Retained)");
view.getItems().add(detailedView);
final CheckMenuItem resizeMessageContent = connectionController.getResizeMessageContentMenu();
resizeMessageContent.setText("Resizable message pane");
resizeMessageContent.selectedProperty().addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
{
connectionController.toggleMessagePayloadSize(resizeMessageContent.isSelected());
}
});
view.getItems().add(resizeMessageContent);
// Separator
view.getItems().add(new SeparatorMenuItem());
final Menu panes = new Menu("Panes");
final Menu manualPublications = createConnectionPaneMenu("'Publish message' pane", connectionController, connectionController.getNewPublicationPaneStatus());
final Menu scriptedPublications = createConnectionPaneMenu("'Scripted publications' pane", connectionController, connectionController.getPublicationScriptsPaneStatus());
final Menu newSubscription = createConnectionPaneMenu("'Define new subscription' pane", connectionController, connectionController.getNewSubscriptionPaneStatus());
final Menu messageSummary = createConnectionPaneMenu("'Subscriptions and received messages' pane", connectionController, connectionController.getSubscriptionsStatus());
final Menu testCases = createConnectionPaneMenu("'Test cases' pane", connectionController, connectionController.getTestCasesPaneStatus());
panes.getItems().add(manualPublications);
panes.getItems().add(scriptedPublications);
panes.getItems().add(newSubscription);
panes.getItems().add(messageSummary);
panes.getItems().add(testCases);
detailedView.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent e)
{
connectionController.toggleDetailedViewVisibility();
}
});
view.getItems().add(panes);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
MenuItem editItem = new MenuItem("Edit...");
editItem.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
eventBus.publish(new ShowEditConnectionsWindowEvent(
connectionController.getTab().getTabPane().getScene().getWindow(), false, connection));
}
});
contextMenu.getItems().add(editItem);
return contextMenu;
}
/**
* Creates a context menu for the message log tab.
*
* @param tab The tab for which to create the menu
* @param connectionController
*
* @return Created context menu
*/
public static ContextMenu createMessageLogMenu(
final Tab tab,
final ConnectionController connectionController,
final ConnectionManager connectionManager)
{
// Context menu
ContextMenu contextMenu = new ContextMenu();
MenuItem closedItem = new MenuItem("Close");
closedItem.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
connectionManager.closeOfflineTab(connectionController);
}
});
contextMenu.getItems().add(closedItem);
// Separator
contextMenu.getItems().add(new SeparatorMenuItem());
final Menu view = new Menu("View");
contextMenu.getItems().add(view);
// View
final MenuItem detachMenu = new MenuItem("Detach to a separate window");
detachMenu.setOnAction(TabUtils.createTabDetachEvent(
detachMenu, connectionController,
"Message audit " + tab.getText(), 0));
view.getItems().add(detachMenu);
final CheckMenuItem resizeMessageContent = connectionController.getResizeMessageContentMenu();
resizeMessageContent.setText("Resizable message pane");
resizeMessageContent.selectedProperty().addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
{
connectionController.toggleMessagePayloadSize(resizeMessageContent.isSelected());
}
});
view.getItems().add(resizeMessageContent);
return contextMenu;
}
}