/*********************************************************************************** * * 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; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.control.ChoiceBox; import javafx.scene.control.ColorPicker; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.MenuButton; import javafx.scene.control.TitledPane; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.paint.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.baczkowicz.mqttspy.configuration.generated.TabbedSubscriptionDetails; import pl.baczkowicz.mqttspy.connectivity.MqttAsyncConnection; import pl.baczkowicz.mqttspy.ui.connections.ConnectionManager; import pl.baczkowicz.mqttspy.ui.events.ShowNewSubscriptionWindowEvent; import pl.baczkowicz.mqttspy.utils.MqttUtils; import pl.baczkowicz.spy.eventbus.IKBus; import pl.baczkowicz.spy.exceptions.SpyException; import pl.baczkowicz.spy.ui.keyboard.TimeBasedKeyEventFilter; import pl.baczkowicz.spy.ui.panes.PaneVisibilityStatus; import pl.baczkowicz.spy.ui.panes.TitledPaneController; import pl.baczkowicz.spy.ui.utils.DialogFactory; /** * Controller for creating new subscriptions. */ public class NewSubscriptionController implements Initializable, TitledPaneController { final static Logger logger = LoggerFactory.getLogger(NewSubscriptionController.class); @FXML private Button subscribeButton; @FXML private ComboBox<String> subscriptionTopicText; @FXML private ChoiceBox<String> subscriptionQosChoice; @FXML private Label subscriptionQosLabel; @FXML private ColorPicker subscriptionColorPicker; private ObservableList<String> subscriptionTopics = FXCollections.observableArrayList(); private MqttAsyncConnection connection; private List<Color> colors = new ArrayList<Color>(); private ConnectionController connectionController; private boolean connected; private ConnectionManager connectionManager; private boolean detailedView; private TimeBasedKeyEventFilter timeBasedFilter; private TitledPane pane; private AnchorPane paneTitle; private MenuButton settingsButton; private PaneVisibilityStatus status; private PaneVisibilityStatus previousStatus; private IKBus eventBus; private Label titleLabel; public NewSubscriptionController() { // TODO: subscription colors - move that to a property file // 8 colors.add(Color.valueOf("f9d900")); colors.add(Color.valueOf("a9e200")); colors.add(Color.valueOf("22bad9")); colors.add(Color.valueOf("0181e2")); colors.add(Color.valueOf("2f357f")); colors.add(Color.valueOf("860061")); colors.add(Color.valueOf("c62b00")); colors.add(Color.valueOf("ff5700")); // 8 colors.add(Color.valueOf("f9d950")); colors.add(Color.valueOf("a9e250")); colors.add(Color.valueOf("22baa9")); colors.add(Color.valueOf("018122")); colors.add(Color.valueOf("2f351f")); colors.add(Color.valueOf("8600F1")); colors.add(Color.valueOf("c62b60")); colors.add(Color.valueOf("ff5760")); } public void initialize(URL location, ResourceBundle resources) { timeBasedFilter = new TimeBasedKeyEventFilter(100); subscriptionColorPicker.setValue(colors.get(0)); subscriptionTopicText.setItems(subscriptionTopics); subscriptionTopicText.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() { @Override public void handle(KeyEvent event) { switch (event.getCode()) { case ENTER: { if (connected && timeBasedFilter.processEvent(event)) { onSubscribe(event.isControlDown()); event.consume(); } break; } default: break; } } }); // subscribeButton.setOnMouseClicked(new EventHandler<MouseEvent>() // { // @Override // public void handle(final MouseEvent event) // { // if (connected && timeBasedFilter.processEvent(event)) // { // onSubscribe(event.isControlDown()); // event.consume(); // } // } // }); } @FXML private void onSubscribe() { onSubscribe(false); } private void onSubscribe(final boolean controlDown) { logger.debug("onSubscribe() {} {}", controlDown, status); if (PaneVisibilityStatus.DETACHED.equals(status)) { subscribe(); if (!controlDown) { eventBus.publish(new ShowNewSubscriptionWindowEvent(connectionController, previousStatus, PaneVisibilityStatus.DETACHED)); } } else { subscribe(); } } public void init() { titleLabel = new Label(pane.getText()); paneTitle = new AnchorPane(); settingsButton = ViewManager.createTitleButtons(this, paneTitle, connectionController); } private void updateVisibility() { if (detailedView) { AnchorPane.setRightAnchor(subscriptionTopicText, 262.0); subscriptionQosChoice.setVisible(true); subscriptionQosLabel.setVisible(true); } else { AnchorPane.setRightAnchor(subscriptionTopicText, 179.0); subscriptionQosChoice.setVisible(false); subscriptionQosLabel.setVisible(false); } // TODO: basic perspective } public void setViewVisibility(final boolean detailedView) { this.detailedView = detailedView; updateVisibility(); } public void toggleDetailedViewVisibility() { detailedView = !detailedView; updateVisibility(); } public void setConnected(final boolean connected) { this.connected = connected; this.subscribeButton.setDisable(!connected); this.subscriptionTopicText.setDisable(!connected); } public boolean recordSubscriptionTopic(final String subscriptionTopic) { return MqttUtils.recordTopic(subscriptionTopic, subscriptionTopics); } @FXML public void subscribe() { // Note: here using the editor, as the value stored directly in the ComboBox might // not be committed yet, whereas the editor (TextField) has got the current text in it // Note: this is also a workaround for bug in JRE 8 Update 60-66 (https://bugs.openjdk.java.net/browse/JDK-8136838) final String subscriptionTopic = subscriptionTopicText.getEditor().getText(); logger.debug("subscribe() to {}", subscriptionTopic); if (subscriptionTopic != null) { try { MqttUtils.validateTopic(subscriptionTopic); final TabbedSubscriptionDetails subscriptionDetails = new TabbedSubscriptionDetails(); subscriptionDetails.setTopic(subscriptionTopic); subscriptionDetails.setQos(subscriptionQosChoice.getSelectionModel().getSelectedIndex()); subscribe(subscriptionDetails, true); } catch (SpyException e) { DialogFactory.createErrorDialog("Invalid topic", "Provided topic is not valid. " + e.getMessage()); } } else { DialogFactory.createErrorDialog("Invalid topic", "Cannot subscribe to an empty topic."); } } public void subscribe(final TabbedSubscriptionDetails subscriptionDetails, final boolean subscribe) { logger.info("Subscribing to " + subscriptionDetails.getTopic()); if (!connection.getSubscriptions().keySet().contains(subscriptionDetails.getTopic())) { recordSubscriptionTopic(subscriptionDetails.getTopic()); connectionManager.getSubscriptionManager(connectionController). createSubscription(subscriptionColorPicker.getValue(), subscribe, subscriptionDetails, connection, connectionController, this); subscriptionColorPicker.setValue(colors.get(connection.getLastUsedSubscriptionId() % 16)); } else { DialogFactory.createErrorDialog("Duplicate topic", "You already have a subscription tab with " + subscriptionDetails.getTopic() + " topic."); } } @Override public void updatePane(final PaneVisibilityStatus status) { this.status = status; if (PaneVisibilityStatus.ATTACHED.equals(status)) { settingsButton.setVisible(true); titleLabel.setText("Define new subscription"); } else { settingsButton.setVisible(false); titleLabel.setText("After typing the value, hit Enter or click Subscribe; hold Control to keep the window"); } } public void requestFocus() { // Bring to front if (status.equals(PaneVisibilityStatus.DETACHED)) { connectionController.getPaneToStatusMapping().get(pane).getParentWhenDetached().toFront(); } pane.requestFocus(); subscriptionTopicText.requestFocus(); // Select all text, so it's easier to edit subscriptionTopicText.fireEvent(new KeyEvent(this, subscriptionTopicText, KeyEvent.KEY_PRESSED, "", "", KeyCode.A, false, true, false, false)); } public void setConnectionManager(final ConnectionManager connectionManager) { this.connectionManager = connectionManager; } public void setConnectionController(ConnectionController connectionController) { this.connectionController = connectionController; } public void setConnection(MqttAsyncConnection connection) { this.connection = connection; } @Override public TitledPane getTitledPane() { return pane; } @Override public void setTitledPane(TitledPane pane) { this.pane = pane; } /** * Sets the event bus. * * @param eventBus the eventBus to set */ public void setEventBus(final IKBus eventBus) { this.eventBus = eventBus; } @Override public Label getTitleLabel() { return titleLabel; } public void setPreviousStatus(PaneVisibilityStatus previousStatus) { this.previousStatus = previousStatus; } }