org.eclipse.paho.mqtt.ui.views.OptionsTab.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.paho.mqtt.ui.views.OptionsTab.java

Source

/*******************************************************************************
 * Copyright (c) 2013 IBM Corp.
 *
 * 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
 * and the Eclipse Distribution License is available at 
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *    Bin Zhang - initial API and implementation and/or initial documentation
 *******************************************************************************/
package org.eclipse.paho.mqtt.ui.views;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.paho.mqtt.ui.Constants;
import org.eclipse.paho.mqtt.ui.Constants.ImageKeys;
import org.eclipse.paho.mqtt.ui.core.DataBindings;
import org.eclipse.paho.mqtt.ui.core.DataBindings.Converters;
import org.eclipse.paho.mqtt.ui.core.DataBindings.DataBinding;
import org.eclipse.paho.mqtt.ui.core.DataBindings.DecoratedBinding;
import org.eclipse.paho.mqtt.ui.core.DataBindings.Validators;
import org.eclipse.paho.mqtt.ui.core.model.Connection;
import org.eclipse.paho.mqtt.ui.core.model.LWT;
import org.eclipse.paho.mqtt.ui.core.model.QoS;
import org.eclipse.paho.mqtt.ui.core.model.ServerURI;
import org.eclipse.paho.mqtt.ui.nls.Messages;
import org.eclipse.paho.mqtt.ui.support.table.TableViewerBuilder;
import org.eclipse.paho.mqtt.ui.util.Colors;
import org.eclipse.paho.mqtt.ui.util.Images;
import org.eclipse.paho.mqtt.ui.util.Strings;
import org.eclipse.paho.mqtt.ui.util.Widgets;
import org.eclipse.paho.mqtt.ui.views.editor.ServerUriCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.ToolTip;

/**
 * Options Tab
 * 
 * @author Bin Zhang
 */
public class OptionsTab extends CTabItem {
    private static final String[] KEYSTORE_FILE_EXT = new String[] { "*.jks", "*.p12", "*.pfx", "*.*" };
    private final Connection connection;

    /**
     * @param parent
     * @param style
     * @param connection
     */
    public OptionsTab(CTabFolder parent, int style, Connection connection) {
        super(parent, style);

        this.connection = connection;

        // set text & image
        setText(Messages.OPT_TAB_TITLE);
        setImage(Images.get(ImageKeys.IMG_OPTIONS));

        // create UI
        createControl(parent);
    }

    /**
     * @param tabFolder
     */
    private void createControl(CTabFolder tabFolder) {
        // Create Container
        ScrolledComposite scrolledContainer = new ScrolledComposite(tabFolder, SWT.BORDER | SWT.V_SCROLL);

        Composite container = new Composite(scrolledContainer, SWT.NONE);
        GridLayout glayout = new GridLayout();
        glayout.numColumns = 1;
        glayout.marginTop = 0;
        glayout.marginBottom = 0;
        glayout.marginRight = 4;
        glayout.verticalSpacing = 20;
        container.setLayout(glayout);

        // General
        createGeneralGroup(container);

        // SSL
        createSSLGroup(container);

        // HA
        createHAGroup(container);

        // LWT
        createLWTGroup(container);

        scrolledContainer.setContent(container);
        scrolledContainer.setExpandHorizontal(true);
        scrolledContainer.setExpandVertical(true);
        // scrolledContainer.setMinWidth( 1600 );
        scrolledContainer.setMinHeight(900);

        setControl(scrolledContainer);
    }

    // *********************************************************************************
    // Group: LWT
    // *********************************************************************************
    private ViewUpdater<Boolean> lwtViewUpdater;

    private void createLWTGroup(Composite container) {
        // data model
        final LWT lwt = connection.getOptions().getLwt();

        final Group group = new Group(container, SWT.NONE);
        group.setText(Messages.OPT_TAB_GROUP_LWT);

        // layout
        FormLayout layout = new FormLayout();
        layout.marginBottom = 10;
        group.setLayout(layout);
        GridData gd = new GridData(GridData.FILL_HORIZONTAL);
        group.setLayoutData(gd);

        // **********************************************
        // Row: Topic + QoS + Retain
        // **********************************************
        // Topic - label
        Label topicLabel = new Label(group, SWT.NONE);
        FormData fd = new FormData();
        fd.top = new FormAttachment(0, 10);
        fd.left = new FormAttachment(0, 10);
        topicLabel.setLayoutData(fd);
        topicLabel.setText(Messages.OPT_TAB_GROUP_LWT_TOPIC);

        // Topic - input
        final Text topicText = new Text(group, SWT.BORDER);
        fd = new FormData();
        fd.top = new FormAttachment(topicLabel, 10, SWT.CENTER);
        fd.left = new FormAttachment(topicLabel, 40);
        fd.right = new FormAttachment(100, -4);
        topicText.setLayoutData(fd);

        // QoS - label
        Label qosLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(topicLabel, 10);
        fd.left = new FormAttachment(0, 10);
        qosLabel.setLayoutData(fd);
        qosLabel.setText(Messages.OPT_TAB_GROUP_LWT_QOS);

        // QoS - selection
        CCombo combo = new CCombo(group, SWT.BORDER);
        List<String> qosList = new ArrayList<String>();
        for (QoS qos : QoS.values()) {
            qosList.add(qos.getLabel());
        }
        combo.setItems(qosList.toArray(new String[0]));
        combo.setEditable(false);
        combo.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(final SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        // Note: need manually set (because NOT using databinding for combo)
                        CCombo combo = (CCombo) e.widget;
                        lwt.setQos(QoS.valueOf(combo.getSelectionIndex()));
                    }
                });
            }
        });

        // Note: need manually set (because NOT using databinding for combo)
        combo.select(lwt.getQos().getValue());
        fd = new FormData();
        fd.top = new FormAttachment(qosLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(topicText, 0, SWT.LEFT);
        combo.setLayoutData(fd);

        // Retained
        Button retained = new Button(group, SWT.CHECK);
        retained.setText(Messages.OPT_TAB_GROUP_LWT_RETAINED);
        fd = new FormData();
        fd.top = new FormAttachment(qosLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(combo, 10);
        retained.setLayoutData(fd);

        // Hex
        final Button hex = new Button(group, SWT.CHECK);
        hex.setText(Messages.OPT_TAB_GROUP_LWT_HEX);
        fd = new FormData();
        fd.top = new FormAttachment(qosLabel, 0, SWT.CENTER);
        fd.right = new FormAttachment(100, -4);
        hex.setLayoutData(fd);

        // **********************************************
        // Row: Message - label
        // **********************************************
        // Topic - label
        Label messageLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(topicLabel, 60);
        fd.left = new FormAttachment(0, 10);
        messageLabel.setLayoutData(fd);
        messageLabel.setText(Messages.OPT_TAB_GROUP_LWT_MSG);

        // Message - input
        final Text messageText = new Text(group, SWT.MULTI | SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);
        fd = new FormData();
        fd.top = new FormAttachment(messageLabel, 10, SWT.CENTER);
        fd.left = new FormAttachment(topicText, 0, SWT.LEFT);
        fd.right = new FormAttachment(topicText, 0, SWT.RIGHT);
        // fd.width = 300;
        fd.height = 60;
        messageText.setLayoutData(fd);

        // XXX handle input when in HEX mode
        hex.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(final SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        Button button = (Button) e.widget;
                        if (button.getSelection()) {
                            messageText.setText(Strings.toHex(messageText.getText()));
                            messageText.setEditable(false);
                        } else {
                            messageText.setText(Strings.hexToString(messageText.getText()));
                            messageText.setEditable(true);
                        }
                    }
                });
            }
        });

        // **********************************************
        // DataBinding
        // **********************************************
        final DataBinding dataBinding = DataBindings.createDataBinding();
        dataBinding.bindTextAsBytes(messageText, connection, Connection.PROP_OPT_LWT_PAYLOAD);
        dataBinding.bindSelection(retained, connection, Connection.PROP_OPT_LWT_RETAIN);

        // LWT view updater
        lwtViewUpdater = new ViewUpdater<Boolean>() {
            final DecoratedBinding binding;
            {
                binding = dataBinding
                        .bindText(topicText, connection, Connection.PROP_OPT_LWT_TOPIC, Validators.publishTopic)
                        .hideDecorations();
            }

            @Override
            public void update(Boolean selected) {
                Widgets.enable(group, selected);
                if (selected) {
                    binding.showDecorations();
                } else {
                    binding.hideDecorations();
                }
            }
        };
        // dispose dataBinding when UI is disposed
        addDisposeListener(dataBinding);

        // initial state
        Widgets.enable(group, connection.getOptions().isLwtEnabled());
    }

    // *********************************************************************************
    // Group: HA
    // *********************************************************************************
    private ViewUpdater<Boolean> haViewUpdater;
    // HA table viewer
    private TableViewer viewer;

    private void createHAGroup(Composite container) {
        final Group group = new Group(container, SWT.NONE);
        group.setText(Messages.OPT_TAB_GROUP_HA);

        // layout
        GridLayout layout = new GridLayout();
        layout.numColumns = 1;
        layout.marginTop = 0;
        layout.marginBottom = 10;
        layout.verticalSpacing = 0;
        group.setLayout(layout);
        group.setLayoutData(new GridData(GridData.FILL_BOTH));

        // Toolbar
        ToolBar toolBar = new ToolBar(group, SWT.NONE);
        // add
        ToolItem itemAdd = new ToolItem(toolBar, SWT.FLAT);
        itemAdd.setImage(Images.get(ImageKeys.IMG_ADD));
        itemAdd.setDisabledImage(Images.get(ImageKeys.IMG_ADD_GRAY));
        itemAdd.setToolTipText(Messages.OPT_TAB_GROUP_HA_ADD_BTN_TOOLTIP);
        itemAdd.addSelectionListener(new SelectionAdapter() {
            @SuppressWarnings("unchecked")
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        ServerURI uri = new ServerURI(Constants.TCP_SERVER_URI);
                        ((List<ServerURI>) viewer.getInput()).add(uri);
                        viewer.refresh();

                        // select the added uri
                        viewer.setSelection(new StructuredSelection(uri));

                        viewer.editElement(uri, 1);
                    }
                });
            }
        });

        // remove
        final ToolItem itemRemove = new ToolItem(toolBar, SWT.FLAT);
        itemRemove.setImage(Images.get(ImageKeys.IMG_REMOVE));
        itemRemove.setDisabledImage(Images.get(ImageKeys.IMG_REMOVE_GRAY));
        itemRemove.setToolTipText(Messages.OPT_TAB_GROUP_HA_RM_BTN_TOOLTIP);
        itemRemove.setEnabled(false);
        itemRemove.addSelectionListener(new SelectionAdapter() {
            @SuppressWarnings("unchecked")
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        StructuredSelection selection = (StructuredSelection) viewer.getSelection();
                        List<ServerURI> input = ((List<ServerURI>) viewer.getInput());
                        int index = -1;
                        for (ServerURI uri : (List<ServerURI>) selection.toList()) {
                            if (index == -1) {
                                index = input.indexOf(uri);
                            }
                            input.remove(uri);
                        }

                        viewer.refresh();

                        // select previous index
                        if (!input.isEmpty()) {
                            index = index - 1 >= 0 ? index - 1 : 0;
                            viewer.setSelection(new StructuredSelection(input.get(index)));
                        }
                    }
                });
            }
        });

        // clear
        ToolItem itemClear = new ToolItem(toolBar, SWT.FLAT);
        itemClear.setImage(Images.get(ImageKeys.IMG_CLEAR));
        // itemClear.setDisabledImage(Images.get("clear_gray"));
        itemClear.setToolTipText(Messages.OPT_TAB_GROUP_HA_CLEAR_BTN_TOOLTIP);
        itemClear.setEnabled(true);
        itemClear.addSelectionListener(new SelectionAdapter() {
            @SuppressWarnings("unchecked")
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        ((List<ServerURI>) viewer.getInput()).clear();
                        viewer.refresh();
                    }
                });
            }
        });

        // Table viewer
        Composite composite = new Composite(group, SWT.NONE);
        composite.setLayoutData(new GridData(GridData.FILL_BOTH));

        // Note: using V_SCROLL | NO_SCROLL to force disable H_SCROLL
        TableViewerBuilder builder = new TableViewerBuilder(composite,
                SWT.MULTI | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.NO_SCROLL | SWT.BORDER)
                        .selectionChangedListener(new ISelectionChangedListener() {
                            public void selectionChanged(SelectionChangedEvent event) {
                                Display.getDefault().asyncExec(new Runnable() {
                                    @Override
                                    public void run() {
                                        itemRemove.setEnabled(!viewer.getSelection().isEmpty());
                                    }
                                });
                            }
                        }).makeEditable().input(connection.getOptions().getServerURIs());

        // build the table viewer
        viewer = builder.build();

        // table columns
        // First extra column to display error message
        builder.columnBuilder("", SWT.LEFT).pixelWidth(20).emptyCellLabelProvider().build();
        builder.columnBuilder(Messages.OPT_TAB_GROUP_HA_SERVER_URIS, SWT.LEFT).percentWidth(90)
                .cellLabelProvider(new ColumnLabelProvider() {
                    @Override
                    public String getText(Object element) {
                        return ((ServerURI) element).getValue();
                    }

                    @Override
                    public String getToolTipText(Object element) {
                        return Messages.TOOLTIP_DBCLICK_TO_EDIT;
                    }
                }).editingSupport(new ServerUriCellEditor(viewer)).build();

        viewer.refresh();

        // initial state
        Widgets.enable(group, connection.getOptions().isHaEnabled());
        haViewUpdater = new ViewUpdater<Boolean>() {
            @Override
            public void update(Boolean selected) {
                Widgets.enable(group, selected);
                viewer.refresh();
            }
        };

    }

    // *********************************************************************************
    // Group: SSL
    // *********************************************************************************
    private ViewUpdater<Boolean> sslViewUpdater;

    private void createSSLGroup(Composite container) {
        final Group group = new Group(container, SWT.NONE);
        group.setText(Messages.OPT_TAB_GROUP_SSL);
        FormLayout layout = new FormLayout();
        layout.marginBottom = 10;
        group.setLayout(layout);
        group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        // 1)
        // **********************************************
        // Row: Keystore - Location
        // **********************************************
        // Keystore Location - Label
        Label keystoreLabel = new Label(group, SWT.NONE);
        FormData fd = new FormData();
        fd.top = new FormAttachment(0, 10);
        fd.left = new FormAttachment(0, 10);
        keystoreLabel.setLayoutData(fd);
        keystoreLabel.setText(Messages.OPT_TAB_GROUP_SSL_KEY_STORE);

        // Keystore Location - Directory - Text
        final Text keystoreDirText = new Text(group, SWT.BORDER);
        keystoreDirText.setEditable(false);
        fd = new FormData();
        fd.top = new FormAttachment(keystoreLabel, 0, SWT.CENTER);
        // fd.left = new FormAttachment(keystoreLabel, 30);
        fd.left = new FormAttachment(0, 160);
        fd.width = 454;
        keystoreDirText.setLayoutData(fd);

        // Keystore Location- Browse - Button
        final Button keystoreBrowseButton = new Button(group, SWT.PUSH);
        keystoreBrowseButton.setText(Messages.LABEL_BROWSE);
        fd = new FormData();
        fd.top = new FormAttachment(keystoreDirText, 0, SWT.CENTER);
        fd.left = new FormAttachment(keystoreDirText, 10);
        keystoreBrowseButton.setLayoutData(fd);
        keystoreBrowseButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        FileDialog fileDialog = new FileDialog(getControl().getShell());
                        fileDialog.setText(Messages.OPT_TAB_GROUP_SSL_KEY_STORE_DLG);
                        fileDialog.setFilterExtensions(KEYSTORE_FILE_EXT);
                        String selectedFile = fileDialog.open();
                        if (selectedFile != null) {
                            keystoreDirText.setText(selectedFile);
                        }
                    }
                });
            }
        });

        // **********************************************
        // Row: Keystore - Password
        // **********************************************
        // Keystore - Password - Label
        Label keystorePwdLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(keystoreLabel, 10);
        fd.left = new FormAttachment(0, 10);
        keystorePwdLabel.setLayoutData(fd);
        keystorePwdLabel.setText(Messages.OPT_TAB_GROUP_SSL_KEY_STORE_PWD);

        // Keystore - Password - Text
        final Text keystorePwdText = new Text(group, SWT.BORDER | SWT.PASSWORD);
        fd = new FormData();
        fd.top = new FormAttachment(keystorePwdLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(keystoreDirText, 0, SWT.LEFT);
        fd.right = new FormAttachment(keystoreDirText, 0, SWT.RIGHT);
        keystorePwdText.setLayoutData(fd);

        // 2)
        // **********************************************
        // Row: Truststore - Location
        // **********************************************
        // Truststore Location - Label
        Label truststoreLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(keystorePwdLabel, 10);
        fd.left = new FormAttachment(0, 10);
        truststoreLabel.setLayoutData(fd);
        truststoreLabel.setText(Messages.OPT_TAB_GROUP_SSL_TRUST_STORE);

        // Truststore Location - Directory - Text
        final Text truststoreDirText = new Text(group, SWT.BORDER);
        truststoreDirText.setEditable(false);
        fd = new FormData();
        fd.top = new FormAttachment(truststoreLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(keystoreDirText, 0, SWT.LEFT);
        fd.right = new FormAttachment(keystoreDirText, 0, SWT.RIGHT);
        truststoreDirText.setLayoutData(fd);

        // Truststore Location- Browse - Button
        final Button truststoreBrowseButton = new Button(group, SWT.PUSH);
        truststoreBrowseButton.setText(Messages.LABEL_BROWSE);
        fd = new FormData();
        fd.top = new FormAttachment(truststoreDirText, 0, SWT.CENTER);
        fd.left = new FormAttachment(truststoreDirText, 10);
        truststoreBrowseButton.setLayoutData(fd);
        truststoreBrowseButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        FileDialog fileDialog = new FileDialog(getControl().getShell());
                        fileDialog.setText(Messages.OPT_TAB_GROUP_SSL_TRUST_STORE_DLG);
                        fileDialog.setFilterExtensions(KEYSTORE_FILE_EXT);
                        String selectedFile = fileDialog.open();
                        if (selectedFile != null) {
                            truststoreDirText.setText(selectedFile);
                        }
                    }
                });
            }
        });

        // **********************************************
        // Row: Truststore - Password
        // **********************************************
        // Truststore - Password - Label
        Label truststorePwdLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(truststoreLabel, 10);
        fd.left = new FormAttachment(0, 10);
        truststorePwdLabel.setLayoutData(fd);
        truststorePwdLabel.setText(Messages.OPT_TAB_GROUP_SSL_TRUST_STORE_PWD);

        // Truststore - Password - Text
        final Text truststorePwdText = new Text(group, SWT.BORDER | SWT.PASSWORD);
        fd = new FormData();
        fd.top = new FormAttachment(truststorePwdLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(keystoreDirText, 0, SWT.LEFT);
        fd.right = new FormAttachment(keystoreDirText, 0, SWT.RIGHT);
        truststorePwdText.setLayoutData(fd);

        // **********************************************
        // DataBinding
        // **********************************************
        final DataBinding dataBinding = DataBindings.createDataBinding();
        // SSL view updater
        sslViewUpdater = new ViewUpdater<Boolean>() {
            final DecoratedBinding keystoreBinding;
            final DecoratedBinding truststoreBinding;
            {
                // key store
                keystoreBinding = dataBinding
                        .bindText(keystoreDirText, connection, Connection.PROP_OPT_SSL_KS, Validators.required)
                        .hideDecorations();

                // PWD is not required, no decoration is required
                dataBinding.bindText(keystorePwdText, connection, Connection.PROP_OPT_SSL_KSPWD,
                        Converters.stringToChars);

                // trust store
                truststoreBinding = dataBinding
                        .bindText(truststoreDirText, connection, Connection.PROP_OPT_SSL_TS, Validators.required)
                        .hideDecorations();

                // PWD is not required, no decoration is required
                dataBinding.bindText(truststorePwdText, connection, Connection.PROP_OPT_SSL_TSPWD,
                        Converters.stringToChars);
            }

            @Override
            public void update(Boolean selected) {
                Widgets.enable(group, selected);
                if (selected) {
                    keystoreBinding.showDecorations();
                    truststoreBinding.showDecorations();
                } else {
                    keystoreBinding.hideDecorations();
                    truststoreBinding.hideDecorations();
                }
            }
        };

        // dispose dataBinding when UI is disposed
        addDisposeListener(dataBinding);

        // disable all when creation
        Widgets.enable(group, connection.getOptions().isSslEnabled());
    }

    // *********************************************************************************
    // Group: General
    // *********************************************************************************
    private ViewUpdater<Boolean> loginViewUpdater;
    private ViewUpdater<Boolean> persistenceViewUpdater;

    private void createGeneralGroup(Composite container) {
        final Group group = new Group(container, SWT.NONE);
        group.setText(Messages.OPT_TAB_GROUP_GENERAL);
        FormLayout layout = new FormLayout();
        layout.marginBottom = 10;
        group.setLayout(layout);
        group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        // **********************************************
        // Row: CleanSession
        // **********************************************
        // CleanSession - Label
        Label cleanSessionLabel = new Label(group, SWT.NONE);
        FormData fd = new FormData();
        fd.top = new FormAttachment(0, 10);
        fd.left = new FormAttachment(0, 10);
        cleanSessionLabel.setLayoutData(fd);
        cleanSessionLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_CLEAN_SESSION);

        // CleanSession - Check
        final Button cleanSessionCheck = new Button(group, SWT.CHECK);
        fd = new FormData();
        fd.top = new FormAttachment(cleanSessionLabel, 0, SWT.CENTER);
        // fd.left = new FormAttachment(cleanSessionLabel, 60);
        fd.left = new FormAttachment(0, 160);
        cleanSessionCheck.setLayoutData(fd);

        // **********************************************
        // Row: SSL
        // **********************************************
        // SSL - Label
        Label sslLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(cleanSessionLabel, 10);
        fd.left = new FormAttachment(0, 10);
        sslLabel.setLayoutData(fd);
        sslLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_ENABLE_SSL);

        // SSL - Check
        final Button sslCheck = new Button(group, SWT.CHECK);
        fd = new FormData();
        fd.top = new FormAttachment(sslLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(cleanSessionCheck, 0, SWT.LEFT);
        sslCheck.setLayoutData(fd);
        sslCheck.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        sslViewUpdater.update(sslCheck.getSelection());
                    }
                });
            }
        });

        // **********************************************
        // Row: HA
        // **********************************************
        // HA - Label
        Label haLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(sslLabel, 10);
        fd.left = new FormAttachment(0, 10);
        haLabel.setLayoutData(fd);
        haLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_ENABLE_HA);

        // HA - Check
        final Button haCheck = new Button(group, SWT.CHECK);
        fd = new FormData();
        fd.top = new FormAttachment(haLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(cleanSessionCheck, 0, SWT.LEFT);
        haCheck.setLayoutData(fd);
        haCheck.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        haViewUpdater.update(haCheck.getSelection());
                    }
                });
            }
        });

        // **********************************************
        // Row: LWT
        // **********************************************
        // LWT - Label
        Label lwtLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(haLabel, 10);
        fd.left = new FormAttachment(0, 10);
        lwtLabel.setLayoutData(fd);
        lwtLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_ENABLE_LWT);

        // LWT - Check
        final Button lwtCheck = new Button(group, SWT.CHECK);
        fd = new FormData();
        fd.top = new FormAttachment(lwtLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(cleanSessionCheck, 0, SWT.LEFT);
        lwtCheck.setLayoutData(fd);
        lwtCheck.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        lwtViewUpdater.update(lwtCheck.getSelection());
                    }
                });
            }
        });

        // **********************************************
        // Row: Keep Alive Interval
        // **********************************************
        // Keep Alive - Label
        Label keepAliveLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(lwtLabel, 10);
        fd.left = new FormAttachment(0, 10);
        keepAliveLabel.setLayoutData(fd);
        keepAliveLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_KEEP_ALIVE);

        // Keep Alive - Spinner
        final Spinner keepAliveSpinner = new Spinner(group, SWT.BORDER);
        keepAliveSpinner.setMinimum(0);
        keepAliveSpinner.setMaximum(Integer.MAX_VALUE);
        keepAliveSpinner.setIncrement(5);
        keepAliveSpinner.setPageIncrement(30);
        trackSpinnerUpdates(keepAliveSpinner);

        fd = new FormData();
        fd.top = new FormAttachment(keepAliveLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(cleanSessionCheck, 0, SWT.LEFT);
        keepAliveSpinner.setLayoutData(fd);

        // Keep Alive Unit - Label
        Label keepAliveUnitLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(keepAliveLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(keepAliveSpinner, 10);
        keepAliveUnitLabel.setLayoutData(fd);
        keepAliveUnitLabel.setText(Messages.LABEL_SECONDS);

        // **********************************************
        // Row: Connection Timeout
        // **********************************************
        // Connection Timeout - Label
        Label connectionTimeoutLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(keepAliveLabel, 10);
        fd.left = new FormAttachment(0, 10);
        connectionTimeoutLabel.setLayoutData(fd);
        connectionTimeoutLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_CONNECTION_TIMEOUT);

        // Connection Timeout - Spinner
        final Spinner connectionTimeoutSpinner = new Spinner(group, SWT.BORDER);
        connectionTimeoutSpinner.setMinimum(0);
        connectionTimeoutSpinner.setMaximum(Integer.MAX_VALUE);
        connectionTimeoutSpinner.setIncrement(5);
        connectionTimeoutSpinner.setPageIncrement(30);
        trackSpinnerUpdates(connectionTimeoutSpinner);

        fd = new FormData();
        fd.top = new FormAttachment(connectionTimeoutLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(cleanSessionCheck, 0, SWT.LEFT);
        connectionTimeoutSpinner.setLayoutData(fd);

        // Connection Timeout Unit - Label
        Label connectionTimeoutUnitLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(connectionTimeoutLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(connectionTimeoutSpinner, 10);
        connectionTimeoutUnitLabel.setLayoutData(fd);
        connectionTimeoutUnitLabel.setText(Messages.LABEL_SECONDS);

        // **********************************************
        // Row: Use Login
        // **********************************************
        // Use Login - Label
        Label loginLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(connectionTimeoutLabel, 10);
        fd.left = new FormAttachment(0, 10);
        loginLabel.setLayoutData(fd);
        loginLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_ENABLE_LOGIN);

        // Use Login - Check
        final Button loginCheck = new Button(group, SWT.CHECK);
        fd = new FormData();
        fd.top = new FormAttachment(loginLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(cleanSessionCheck, 0, SWT.LEFT);
        loginCheck.setLayoutData(fd);
        loginCheck.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        loginViewUpdater.update(loginCheck.getSelection());
                    }
                });
            }
        });

        // Use Login - Username - Label
        Label usernameLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(loginLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(connectionTimeoutUnitLabel, 0, SWT.LEFT);
        usernameLabel.setLayoutData(fd);
        usernameLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_USERNAME);

        // Use Login - Username - Text
        final Text usernameText = new Text(group, SWT.BORDER);
        fd = new FormData();
        fd.top = new FormAttachment(usernameLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(usernameLabel, 10);
        fd.width = 120;
        usernameText.setLayoutData(fd);

        // Use Login - Password - Label
        Label passwordLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(loginLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(usernameText, 20);
        passwordLabel.setLayoutData(fd);
        passwordLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_PASSWORD);

        // Use Login - Password - Text
        final Text passwordText = new Text(group, SWT.BORDER | SWT.PASSWORD);
        fd = new FormData();
        fd.top = new FormAttachment(passwordLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(passwordLabel, 10);
        fd.width = 120;
        passwordText.setLayoutData(fd);

        // **********************************************
        // Row: Use Persistence
        // **********************************************
        // Use Persistence - Label
        Label persistLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(loginLabel, 10);
        fd.left = new FormAttachment(0, 10);
        persistLabel.setLayoutData(fd);
        persistLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_ENABLE_PERSISTENCE);

        // Use Persistence - Check
        final Button persistCheck = new Button(group, SWT.CHECK);
        fd = new FormData();
        fd.top = new FormAttachment(persistLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(cleanSessionCheck, 0, SWT.LEFT);
        persistCheck.setLayoutData(fd);
        persistCheck.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        persistenceViewUpdater.update(persistCheck.getSelection());
                    }
                });
            }
        });

        // Use Persistence - Directory - Label
        Label directoryLabel = new Label(group, SWT.NONE);
        fd = new FormData();
        fd.top = new FormAttachment(persistLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(connectionTimeoutUnitLabel, 0, SWT.LEFT);
        directoryLabel.setLayoutData(fd);
        directoryLabel.setText(Messages.OPT_TAB_GROUP_GENERAL_PERSISTENCE_DIRECTORY);

        // Use Persistence - Directory - Text
        final Text directoryText = new Text(group, SWT.BORDER);
        directoryText.setEditable(false);
        fd = new FormData();
        fd.top = new FormAttachment(directoryLabel, 0, SWT.CENTER);
        fd.left = new FormAttachment(usernameText, 0, SWT.LEFT);
        fd.right = new FormAttachment(passwordText, 0, SWT.RIGHT);
        directoryText.setLayoutData(fd);

        // Use Persistence - Browse - Button
        final Button browseDirButton = new Button(group, SWT.PUSH);
        browseDirButton.setText(Messages.LABEL_BROWSE);
        fd = new FormData();
        fd.top = new FormAttachment(directoryText, 0, SWT.CENTER);
        fd.left = new FormAttachment(directoryText, 10);
        browseDirButton.setLayoutData(fd);
        browseDirButton.setEnabled(connection.getOptions().isPersistenceEnabled());
        browseDirButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        DirectoryDialog directoryDialog = new DirectoryDialog(getControl().getShell());
                        directoryDialog.setMessage(Messages.OPT_TAB_GROUP_GENERAL_PERSISTENCE_DLG);
                        String dir = directoryDialog.open();
                        if (dir != null) {
                            directoryText.setText(dir);
                        }
                    }
                });
            }
        });

        // **********************************************
        // DataBinding
        // **********************************************
        final DataBinding dataBinding = DataBindings.createDataBinding();

        // updater
        loginViewUpdater = new ViewUpdater<Boolean>() {
            final DecoratedBinding usrBinding;
            final DecoratedBinding pwdBinding;
            {
                // username is required
                usrBinding = dataBinding
                        .bindText(usernameText, connection, Connection.PROP_OPT_LOGIN_USERNAME, Validators.required)
                        .hideDecorations();

                // password is not required
                pwdBinding = dataBinding.bindText(passwordText, connection, Connection.PROP_OPT_LOGIN_PASSWORD,
                        Converters.stringToChars).hideDecorations();
            }

            @Override
            public void update(Boolean selected) {
                if (selected) {
                    usrBinding.showDecorations();
                    pwdBinding.showDecorations();
                    Widgets.enable(true, usernameText, passwordText);
                } else {
                    usrBinding.hideDecorations();
                    pwdBinding.hideDecorations();
                    Widgets.enable(false, usernameText, passwordText);
                }
            }
        };

        // Persistence view updater
        persistenceViewUpdater = new ViewUpdater<Boolean>() {
            final DecoratedBinding binding;
            {
                binding = dataBinding.bindText(directoryText, connection, Connection.PROP_OPT_PERSISTENCE_DIRECTORY,
                        Validators.required).hideDecorations();
                directoryText.setText(System.getProperty("user.dir")); //$NON-NLS-1$
            }

            @Override
            public void update(Boolean selected) {
                browseDirButton.setEnabled(selected);
                if (selected) {
                    binding.showDecorations();
                } else {
                    binding.hideDecorations();
                }
            }
        };

        // checkbox
        dataBinding.bindSelection(cleanSessionCheck, connection, Connection.PROP_OPT_CLEAN_SESSION);
        dataBinding.bindSelection(sslCheck, connection, Connection.PROP_OPT_SSL_ENABLED);
        dataBinding.bindSelection(haCheck, connection, Connection.PROP_OPT_HA_ENABLED);
        dataBinding.bindSelection(lwtCheck, connection, Connection.PROP_OPT_LWT_ENABLED);
        dataBinding.bindSelection(loginCheck, connection, Connection.PROP_OPT_LOGIN_ENABLED);
        dataBinding.bindSelection(persistCheck, connection, Connection.PROP_OPT_PERSISTENCE_ENABLED);

        // spinner
        dataBinding.bindSelection(keepAliveSpinner, connection, Connection.PROP_OPT_KEEP_ALIVE_INTERVAL);
        dataBinding.bindSelection(connectionTimeoutSpinner, connection, Connection.PROP_OPT_CONNECTION_TIMEOUT);

        // dispose dataBinding when UI is disposed
        addDisposeListener(dataBinding);

        // initial state
        Widgets.enable(connection.getOptions().isLoginEnabled(), usernameText, passwordText);
    }

    /**
     * @param spinner
     */
    private void trackSpinnerUpdates(final Spinner spinner) {

        final ToolTip toolTip = new ToolTip(spinner.getParent().getShell(), SWT.BALLOON | SWT.ICON_ERROR);
        spinner.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        String message = null;
                        try {
                            int value = Integer.parseInt(spinner.getText());
                            int maximum = spinner.getMaximum();
                            int minimum = spinner.getMinimum();
                            if (value > maximum) {
                                message = Messages.bind(Messages.VALIDATION_NUM_GT_MAX, value, maximum);
                            } else if (value < minimum) {
                                message = Messages.bind(Messages.VALIDATION_NUM_LT_MIN, value, minimum);
                            }
                        } catch (Exception ex) {
                            message = Messages.bind(Messages.VALIDATION_INVALID_NUM, spinner.getText());
                        }

                        // display tooltip if error
                        if (message != null) {
                            spinner.setForeground(Colors.getColor(SWT.COLOR_RED));
                            Rectangle rect = spinner.getBounds();
                            GC gc = new GC(spinner);
                            Point pt = gc.textExtent(spinner.getText());
                            gc.dispose();
                            toolTip.setLocation(getControl().getDisplay().map(spinner.getParent(), null,
                                    rect.x + pt.x, rect.y + rect.height));
                            toolTip.setMessage(message);
                            toolTip.setVisible(true);
                        } else {
                            toolTip.setVisible(false);
                            spinner.setForeground(null);
                        }
                    }
                });
            }
        });

        // reset to defaults if invalid inputs
        spinner.addFocusListener(new FocusAdapter() {
            final int DEFAULT = spinner.getSelection();

            @Override
            public void focusLost(FocusEvent e) {
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        // in case of this tab is already disposed
                        // and the UI is updated async
                        if (spinner.isDisposed()) {
                            return;
                        }

                        int value = spinner.getSelection();
                        if (value > spinner.getMaximum() || value < spinner.getMinimum()) {
                            spinner.setSelection(DEFAULT);
                        }
                    }
                });
            }
        });
    }
}