changeset 1854:5f61f05dff9e

Add and use cut-copy-paste-enabled text components PR2927 Reviewed-by: aazores, jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-April/018499.html
author Anirudhan Mukundan <amukunda@redhat.com>
date Tue, 03 May 2016 13:42:39 -0400
parents d2cc25649e4f
children b39cc7254913
files client/swing/src/main/java/com/redhat/thermostat/client/swing/components/CutCopyPastePopup.java client/swing/src/main/java/com/redhat/thermostat/client/swing/components/SearchField.java client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatEditorPane.java client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatPasswordField.java client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatTextArea.java client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatTextComponent.java client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatTextField.java client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ValueField.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/LocaleResources.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/SearchField.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/views/ClientConfigurationPanel.java client/swing/src/main/resources/com/redhat/thermostat/client/swing/internal/strings.properties client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ThermostatEditorPaneTest.java client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ThermostatPasswordFieldTest.java client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ThermostatTextAreaTest.java client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ThermostatTextFieldTest.java notes/client-swing/src/main/java/com/redhat/thermostat/notes/client/swing/internal/NotePanel.java setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/CredentialPanel.java setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/DisplayCredentialPanel.java setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/InputCredentialPanel.java setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/StartView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVmDeadLockView.java
diffstat 23 files changed, 1961 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/CutCopyPastePopup.java	Tue May 03 13:42:39 2016 -0400
@@ -0,0 +1,129 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+import com.redhat.thermostat.client.swing.internal.LocaleResources;
+import com.redhat.thermostat.shared.locale.Translate;
+
+import javax.swing.JMenuItem;
+import javax.swing.text.DefaultEditorKit;
+import javax.swing.text.JTextComponent;
+
+public class CutCopyPastePopup extends ThermostatPopupMenu {
+
+    private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer();
+    private JMenuItem cut;
+    private JMenuItem copy;
+    private JMenuItem paste;
+
+    public CutCopyPastePopup(JTextComponent parent) {
+        this();
+        boolean enableCopy = parent.isEnabled();
+        boolean enableMutators = parent.isEnabled() && parent.isEditable();
+        setCutEnabled(enableMutators);
+        setCopyEnabled(enableCopy);
+        setPasteEnabled(enableMutators);
+    }
+
+    public CutCopyPastePopup() {
+        cut = new JMenuItem(new DefaultEditorKit.CutAction());
+        copy = new JMenuItem(new DefaultEditorKit.CopyAction());
+        paste = new JMenuItem(new DefaultEditorKit.PasteAction());
+
+        cut.setText(translate.localize(LocaleResources.CUT).getContents());
+        copy.setText(translate.localize(LocaleResources.COPY).getContents());
+        paste.setText(translate.localize(LocaleResources.PASTE).getContents());
+
+        this.add(cut);
+        this.add(copy);
+        this.add(paste);
+
+        setCutEnabled(true);
+        setCopyEnabled(true);
+        setPasteEnabled(true);
+    }
+
+    public static CutCopyPastePopup getCopyOnlyMenu() {
+        CutCopyPastePopup menu = new CutCopyPastePopup();
+        menu.setCutEnabled(false);
+        menu.setCopyEnabled(true);
+        menu.setPasteEnabled(false);
+        return menu;
+    }
+
+    public static CutCopyPastePopup getPasteOnlyMenu() {
+        CutCopyPastePopup menu = new CutCopyPastePopup();
+        menu.setCutEnabled(false);
+        menu.setCopyEnabled(false);
+        menu.setPasteEnabled(true);
+        return menu;
+    }
+
+    public static CutCopyPastePopup getDisabledMenu() {
+        CutCopyPastePopup menu = new CutCopyPastePopup();
+        menu.setCutEnabled(false);
+        menu.setCopyEnabled(false);
+        menu.setPasteEnabled(false);
+        menu.setEnabled(false);
+        return menu;
+    }
+
+    public void setCutEnabled(boolean enabled) {
+        cut.setEnabled(enabled);
+    }
+
+    public void setCopyEnabled(boolean enabled) {
+        copy.setEnabled(enabled);
+    }
+
+    public void setPasteEnabled(boolean enabled) {
+        paste.setEnabled(enabled);
+    }
+
+    public boolean isCutEnabled() {
+        return cut.isEnabled();
+    }
+
+    public boolean isCopyEnabled() {
+        return copy.isEnabled();
+    }
+
+    public boolean isPasteEnabled() {
+        return paste.isEnabled();
+    }
+
+}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/SearchField.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/SearchField.java	Tue May 03 13:42:39 2016 -0400
@@ -47,7 +47,6 @@
 
 import javax.swing.BorderFactory;
 import javax.swing.JLabel;
-import javax.swing.JTextField;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 import javax.swing.text.BadLocationException;
@@ -73,7 +72,7 @@
 
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
 
-    private final JTextField searchField = new JTextField();
+    private final ThermostatTextField searchField = new ThermostatTextField();
 
     private final AtomicReference<String> searchText = new AtomicReference<String>("");
     private final AtomicReference<LocalizedString> label = new AtomicReference<>(translator.localize(LocaleResources.SEARCH_HINT));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatEditorPane.java	Tue May 03 13:42:39 2016 -0400
@@ -0,0 +1,91 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+import javax.swing.JEditorPane;
+import java.io.IOException;
+import java.net.URL;
+
+public class ThermostatEditorPane extends JEditorPane implements ThermostatTextComponent {
+
+    private CutCopyPastePopup contextMenu = new CutCopyPastePopup(this);
+    {
+        this.setComponentPopupMenu(contextMenu);
+    }
+
+    public ThermostatEditorPane() {
+        super();
+    }
+
+    public ThermostatEditorPane(URL initialPage) throws IOException {
+        super(initialPage);
+    }
+
+    public ThermostatEditorPane(String url) throws IOException {
+        super(url);
+    }
+
+    public ThermostatEditorPane(String type, String text) {
+        super(type, text);
+    }
+
+    @Override
+    public CutCopyPastePopup getContextMenu() {
+        return contextMenu;
+    }
+
+    @Override
+    public void setEditable(boolean b) {
+        super.setEditable(b);
+        if (contextMenu != null) {
+            contextMenu.setCutEnabled(b && isEnabled());
+            contextMenu.setPasteEnabled(b && isEnabled());
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean b) {
+        super.setEnabled(b);
+        if (contextMenu != null) {
+            contextMenu.setCutEnabled(b && isEditable());
+            contextMenu.setCopyEnabled(b);
+            contextMenu.setPasteEnabled(b && isEditable());
+            contextMenu.setEnabled(b);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatPasswordField.java	Tue May 03 13:42:39 2016 -0400
@@ -0,0 +1,113 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+import javax.swing.JPasswordField;
+import javax.swing.text.Document;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+public class ThermostatPasswordField extends JPasswordField implements ThermostatTextComponent {
+
+    private CutCopyPastePopup contextMenu = CutCopyPastePopup.getPasteOnlyMenu();
+    {
+        this.setComponentPopupMenu(contextMenu);
+        addPropertyChangeListener("JPasswordField.cutCopyAllowed", new PropertyChangeListener() {
+            @Override
+            public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
+                boolean cutCopyEnabled = (Boolean) propertyChangeEvent.getNewValue();
+                contextMenu.setCutEnabled(cutCopyEnabled && isEnabled() && isEditable());
+                contextMenu.setCopyEnabled(cutCopyEnabled && isEnabled());
+            }
+        });
+    }
+
+    public ThermostatPasswordField() {
+        super();
+    }
+
+    public ThermostatPasswordField(String text) {
+        super(text);
+    }
+
+    public ThermostatPasswordField(int columns) {
+        super(columns);
+    }
+
+    public ThermostatPasswordField(String text, int columns) {
+        super(text, columns);
+    }
+
+    public ThermostatPasswordField(Document doc, String txt, int columns) {
+        super(doc, txt, columns);
+    }
+
+    @Override
+    public CutCopyPastePopup getContextMenu() {
+        return contextMenu;
+    }
+
+    @Override
+    public void setEditable(boolean b) {
+        super.setEditable(b);
+        if (contextMenu != null) {
+            contextMenu.setCutEnabled(b && isEnabled() && isCutCopyEnabled());
+            contextMenu.setPasteEnabled(b && isEnabled());
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean b) {
+        super.setEnabled(b);
+        if (contextMenu != null) {
+            contextMenu.setCutEnabled(b && isEditable() &&  isCutCopyEnabled());
+            contextMenu.setCopyEnabled(b && isCutCopyEnabled());
+            contextMenu.setPasteEnabled(b && isEditable());
+            contextMenu.setEnabled(b);
+        }
+    }
+
+    public void setCutCopyEnabled(boolean b) {
+        this.putClientProperty("JPasswordField.cutCopyAllowed", b);
+    }
+
+    public boolean isCutCopyEnabled() {
+        Boolean enabled = (Boolean) getClientProperty("JPasswordField.cutCopyAllowed");
+        return enabled != null && enabled;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatTextArea.java	Tue May 03 13:42:39 2016 -0400
@@ -0,0 +1,98 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+import javax.swing.JTextArea;
+import javax.swing.text.Document;
+
+public class ThermostatTextArea extends JTextArea implements ThermostatTextComponent {
+
+    private CutCopyPastePopup contextMenu = new CutCopyPastePopup(this);
+    {
+        this.setComponentPopupMenu(contextMenu);
+    }
+
+    public ThermostatTextArea() {
+        super();
+    }
+
+    public ThermostatTextArea(String text) {
+        super(text);
+    }
+
+    public ThermostatTextArea(int rows, int columns) {
+        super(rows, columns);
+    }
+
+    public ThermostatTextArea(String text, int rows, int columns) {
+        super(text, rows, columns);
+    }
+
+    public ThermostatTextArea(Document doc) {
+        super(doc);
+    }
+
+    public ThermostatTextArea(Document doc, String text, int rows, int columns) {
+        super(doc, text, rows, columns);
+    }
+
+    @Override
+    public CutCopyPastePopup getContextMenu() {
+        return contextMenu;
+    }
+
+    @Override
+    public void setEditable(boolean b) {
+        super.setEditable(b);
+        if (contextMenu != null) {
+            contextMenu.setCutEnabled(b && isEnabled());
+            contextMenu.setPasteEnabled(b && isEnabled());
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean b) {
+        super.setEnabled(b);
+        if (contextMenu != null) {
+            contextMenu.setCutEnabled(b && isEditable());
+            contextMenu.setCopyEnabled(b);
+            contextMenu.setPasteEnabled(b && isEditable());
+            contextMenu.setEnabled(b);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatTextComponent.java	Tue May 03 13:42:39 2016 -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+public interface ThermostatTextComponent {
+
+    ThermostatPopupMenu getContextMenu();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatTextField.java	Tue May 03 13:42:39 2016 -0400
@@ -0,0 +1,94 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+import javax.swing.JTextField;
+import javax.swing.text.Document;
+
+public class ThermostatTextField extends JTextField implements ThermostatTextComponent {
+
+    private CutCopyPastePopup contextMenu = new CutCopyPastePopup(this);
+    {
+        this.setComponentPopupMenu(contextMenu);
+    }
+
+    public ThermostatTextField() {
+        super();
+    }
+
+    public ThermostatTextField(String text) {
+        super(text);
+    }
+
+    public ThermostatTextField(int columns) {
+        super(columns);
+    }
+
+    public ThermostatTextField(String text, int columns) {
+        super(text, columns);
+    }
+
+    public ThermostatTextField(Document doc, String text, int columns) {
+        super(doc, text, columns);
+    }
+
+    @Override
+    public CutCopyPastePopup getContextMenu() {
+        return contextMenu;
+    }
+
+    @Override
+    public void setEditable(boolean b) {
+        super.setEditable(b);
+        if (contextMenu != null) {
+            contextMenu.setCutEnabled(b && isEnabled());
+            contextMenu.setPasteEnabled(b && isEnabled());
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean b) {
+        super.setEnabled(b);
+        if (contextMenu != null) {
+            contextMenu.setCutEnabled(b && isEditable());
+            contextMenu.setCopyEnabled(b);
+            contextMenu.setPasteEnabled(b && isEditable());
+            contextMenu.setEnabled(b);
+        }
+    }
+
+}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ValueField.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ValueField.java	Tue May 03 13:42:39 2016 -0400
@@ -38,7 +38,6 @@
 
 import java.awt.Color;
 
-import javax.swing.JEditorPane;
 import javax.swing.UIManager;
 import javax.swing.text.DefaultCaret;
 
@@ -47,7 +46,7 @@
  * any other JTextComponent.
  */
 @SuppressWarnings("serial")
-public class ValueField extends JEditorPane {
+public class ValueField extends ThermostatEditorPane {
 
     public ValueField(String text) {
         setUI(new ValueFieldUI());
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/LocaleResources.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/LocaleResources.java	Tue May 03 13:42:39 2016 -0400
@@ -46,6 +46,10 @@
     ZOOM_IN,
     ZOOM_OUT,
     RESET_ZOOM,
+
+    CUT,
+    COPY,
+    PASTE,
     ;
 
     static final String RESOURCE_BUNDLE =
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/SearchField.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/search/SearchField.java	Tue May 03 13:42:39 2016 -0400
@@ -45,7 +45,6 @@
 import javax.swing.Box;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
-import javax.swing.JTextField;
 import javax.swing.border.EmptyBorder;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
@@ -55,12 +54,13 @@
 import com.redhat.thermostat.client.swing.GraphicsUtils;
 import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
 import com.redhat.thermostat.client.swing.components.Icon;
+import com.redhat.thermostat.client.swing.components.ThermostatTextField;
 import com.redhat.thermostat.client.ui.Palette;
 
 @SuppressWarnings("serial")
 public class SearchField extends BaseSearchProvider {
 
-    private JTextField searchField;
+    private ThermostatTextField searchField;
     
     public SearchField() {
         setFocusable(true);
@@ -70,7 +70,7 @@
         final Icon searchIcon =
                 new FontAwesomeIcon('\uf002', 12, Palette.DARK_GRAY.getColor());
                 
-        searchField = new JTextField(20);
+        searchField = new ThermostatTextField(20);
         
         final JLabel searchLabel = new JLabel(searchIcon);
 
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/views/ClientConfigurationPanel.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/views/ClientConfigurationPanel.java	Tue May 03 13:42:39 2016 -0400
@@ -41,12 +41,12 @@
 import javax.swing.JCheckBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
-import javax.swing.JPasswordField;
-import javax.swing.JTextField;
 import javax.swing.LayoutStyle.ComponentPlacement;
 import javax.swing.border.TitledBorder;
 
 import com.redhat.thermostat.client.locale.LocaleResources;
+import com.redhat.thermostat.client.swing.components.ThermostatPasswordField;
+import com.redhat.thermostat.client.swing.components.ThermostatTextField;
 import com.redhat.thermostat.shared.locale.Translate;
 
 @SuppressWarnings("serial")
@@ -54,9 +54,9 @@
 
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
 
-    final JTextField storageUrl = new JTextField();
-    final JTextField userName = new JTextField();
-    final JPasswordField password = new JPasswordField();
+    final ThermostatTextField storageUrl = new ThermostatTextField();
+    final ThermostatTextField userName = new ThermostatTextField();
+    final ThermostatPasswordField password = new ThermostatPasswordField();
     
     final JCheckBox saveEntitlements;
     
--- a/client/swing/src/main/resources/com/redhat/thermostat/client/swing/internal/strings.properties	Tue Apr 05 14:58:50 2016 -0400
+++ b/client/swing/src/main/resources/com/redhat/thermostat/client/swing/internal/strings.properties	Tue May 03 13:42:39 2016 -0400
@@ -3,4 +3,8 @@
 
 ZOOM_IN = Zoom In
 ZOOM_OUT = Zoom Out
-RESET_ZOOM = Reset Zoom
\ No newline at end of file
+RESET_ZOOM = Reset Zoom
+
+CUT = Cut
+COPY = Copy
+PASTE = Paste
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ThermostatEditorPaneTest.java	Tue May 03 13:42:39 2016 -0400
@@ -0,0 +1,311 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiTask;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+
+public class ThermostatEditorPaneTest {
+
+    private ThermostatEditorPane textField;
+
+    @BeforeClass
+    public static void setupOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
+    @Before
+    public void setUp() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField = new ThermostatEditorPane();
+            }
+        });
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testComponentPopupMenuIsSet() {
+        assertThat(textField.getContextMenu(), is(not(equalTo(null))));
+        assertThat(textField.getContextMenu(), is(textField.getComponentPopupMenu()));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testMenuEnabledWhenParentEnabled() {
+        assertThat(textField.getContextMenu().isEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCutDisabledWhenParentDisabled() {
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCopyDisabledWhenParentDisabled() {
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testPasteDisabledWhenParentDisabled() {
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCutDisabledWhenParentNotEditable() {
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testPasteDisabledWhenParentNotEditable() {
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCopyNotDisabledWhenParentNotEditable() {
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testEnableDoesNotOverrideEditable() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testEditableDoesNotOverrideEnabled() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ThermostatPasswordFieldTest.java	Tue May 03 13:42:39 2016 -0400
@@ -0,0 +1,394 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiTask;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+
+public class ThermostatPasswordFieldTest {
+
+    private ThermostatPasswordField textField;
+
+    @BeforeClass
+    public static void setupOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
+    @Before
+    public void setUp() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField = new ThermostatPasswordField();
+            }
+        });
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testComponentPopupMenuIsSet() {
+        assertThat(textField.getContextMenu(), is(not(equalTo(null))));
+        assertThat(textField.getContextMenu(), is(textField.getComponentPopupMenu()));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testMenuEnabledWhenParentEnabled() {
+        assertThat(textField.getContextMenu().isEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCutCopyDisabledByDefault() {
+        assertThat(textField.isCutCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testSetCutCopyEnabled() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(true);
+            }
+        });
+
+        assertThat(textField.isCutCopyEnabled(), is(true));
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(false);
+            }
+        });
+
+        assertThat(textField.isCutCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCutDisabledWhenParentDisabled() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCopyDisabledWhenParentDisabled() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testPasteDisabledWhenParentDisabled() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCutDisabledWhenParentNotEditable() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testPasteDisabledWhenParentNotEditable() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCopyNotDisabledWhenParentNotEditable() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testEnableDoesNotOverrideEditable() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(true);
+                textField.setEnabled(false);
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testEditableDoesNotOverrideEnabled() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setCutCopyEnabled(true);
+                textField.setEnabled(false);
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ThermostatTextAreaTest.java	Tue May 03 13:42:39 2016 -0400
@@ -0,0 +1,311 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiTask;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+
+public class ThermostatTextAreaTest {
+
+    private ThermostatTextArea textField;
+
+    @BeforeClass
+    public static void setupOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
+    @Before
+    public void setUp() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField = new ThermostatTextArea();
+            }
+        });
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testComponentPopupMenuIsSet() {
+        assertThat(textField.getContextMenu(), is(not(equalTo(null))));
+        assertThat(textField.getContextMenu(), is(textField.getComponentPopupMenu()));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testMenuEnabledWhenParentEnabled() {
+        assertThat(textField.getContextMenu().isEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCutDisabledWhenParentDisabled() {
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCopyDisabledWhenParentDisabled() {
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testPasteDisabledWhenParentDisabled() {
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCutDisabledWhenParentNotEditable() {
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testPasteDisabledWhenParentNotEditable() {
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCopyNotDisabledWhenParentNotEditable() {
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testEnableDoesNotOverrideEditable() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testEditableDoesNotOverrideEnabled() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ThermostatTextFieldTest.java	Tue May 03 13:42:39 2016 -0400
@@ -0,0 +1,311 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing.components;
+
+import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiTask;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+
+public class ThermostatTextFieldTest {
+
+    private ThermostatTextField textField;
+
+    @BeforeClass
+    public static void setupOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
+    @Before
+    public void setUp() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField = new ThermostatTextField();
+            }
+        });
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testComponentPopupMenuIsSet() {
+        assertThat(textField.getContextMenu(), is(not(equalTo(null))));
+        assertThat(textField.getContextMenu(), is(textField.getComponentPopupMenu()));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testMenuEnabledWhenParentEnabled() {
+        assertThat(textField.getContextMenu().isEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCutDisabledWhenParentDisabled() {
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCopyDisabledWhenParentDisabled() {
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testPasteDisabledWhenParentDisabled() {
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCutDisabledWhenParentNotEditable() {
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testPasteDisabledWhenParentNotEditable() {
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testCopyNotDisabledWhenParentNotEditable() {
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testEnableDoesNotOverrideEditable() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(true));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void testEditableDoesNotOverrideEnabled() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEnabled(false);
+                textField.setEditable(false);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                textField.setEditable(true);
+            }
+        });
+
+        assertThat(textField.getContextMenu().isCutEnabled(), is(false));
+        assertThat(textField.getContextMenu().isCopyEnabled(), is(false));
+        assertThat(textField.getContextMenu().isPasteEnabled(), is(false));
+    }
+
+}
--- a/notes/client-swing/src/main/java/com/redhat/thermostat/notes/client/swing/internal/NotePanel.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/notes/client-swing/src/main/java/com/redhat/thermostat/notes/client/swing/internal/NotePanel.java	Tue May 03 13:42:39 2016 -0400
@@ -48,12 +48,12 @@
 import javax.swing.JButton;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
-import javax.swing.JTextArea;
 import javax.swing.SwingWorker;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 
 import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
+import com.redhat.thermostat.client.swing.components.ThermostatTextArea;
 import com.redhat.thermostat.common.ActionNotifier;
 import com.redhat.thermostat.notes.client.swing.internal.NotesView.Action;
 import com.redhat.thermostat.notes.common.Note;
@@ -63,7 +63,7 @@
 
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
 
-    private JTextArea text;
+    private ThermostatTextArea text;
     private JLabel timeStampLabel;
     private ActionNotifier<Action> actionNotifier;
 
@@ -74,7 +74,7 @@
         // wrap in html tags to enable line wrapping
         String date = getPrettyTimeStamp(note.getTimeStamp());
         timeStampLabel = new JLabel(date);
-        text = new JTextArea(note.getContent());
+        text = new ThermostatTextArea(note.getContent());
         text.setWrapStyleWord(true);
         text.setLineWrap(true);
         text.getDocument().addDocumentListener(new DocumentListener() {
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/CredentialPanel.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/CredentialPanel.java	Tue May 03 13:42:39 2016 -0400
@@ -38,13 +38,14 @@
 
 import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
 import com.redhat.thermostat.client.swing.components.Icon;
+import com.redhat.thermostat.client.swing.components.ThermostatPasswordField;
+import com.redhat.thermostat.client.swing.components.ThermostatTextField;
 import com.redhat.thermostat.setup.command.locale.LocaleResources;
 import com.redhat.thermostat.shared.locale.Translate;
 
 import javax.swing.BorderFactory;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
-import javax.swing.JPasswordField;
 import javax.swing.JTextField;
 import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
@@ -69,8 +70,8 @@
 
     protected JLabel usernameText;
     protected JLabel passwordText;
-    protected JTextField username;
-    protected JPasswordField password;
+    protected ThermostatTextField username;
+    protected ThermostatPasswordField password;
 
     public CredentialPanel(String titleText, String helpMessage) {
         this.titleText = titleText;
@@ -86,8 +87,9 @@
     private void initComponents() {
         usernameText = new JLabel(translator.localize(LocaleResources.USERNAME).getContents());
         passwordText = new JLabel(translator.localize(LocaleResources.PASSWORD).getContents());
-        username = new JTextField();
-        password = new JPasswordField();
+        username = new ThermostatTextField();
+        password = new ThermostatPasswordField();
+        password.setCutCopyEnabled(true);
 
         title = new JLabel(titleText, infoIcon, JLabel.CENTER);
         title.setHorizontalTextPosition(JLabel.LEFT);
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/DisplayCredentialPanel.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/DisplayCredentialPanel.java	Tue May 03 13:42:39 2016 -0400
@@ -47,30 +47,30 @@
         javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
         this.setLayout(layout);
         layout.setHorizontalGroup(
-            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                .addGroup(layout.createSequentialGroup()
-                    .addGap(33, 33, 33)
-                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addComponent(passwordText)
-                        .addComponent(usernameText))
-                    .addGap(108, 108, 108)
-                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addComponent(password, javax.swing.GroupLayout.DEFAULT_SIZE, 277, Short.MAX_VALUE)
-                        .addComponent(username))
-                    .addContainerGap())
+                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                        .addGroup(layout.createSequentialGroup()
+                                .addGap(33, 33, 33)
+                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                                        .addComponent(passwordText)
+                                        .addComponent(usernameText))
+                                .addGap(108, 108, 108)
+                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                                        .addComponent(password, javax.swing.GroupLayout.DEFAULT_SIZE, 277, Short.MAX_VALUE)
+                                        .addComponent(username))
+                                .addContainerGap())
         );
         layout.setVerticalGroup(
-            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                .addGroup(layout.createSequentialGroup()
-                    .addContainerGap()
-                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
-                        .addComponent(usernameText)
-                        .addComponent(username, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
-                        .addComponent(passwordText)
-                        .addComponent(password, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                    .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                        .addGroup(layout.createSequentialGroup()
+                                .addContainerGap()
+                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                                        .addComponent(usernameText)
+                                        .addComponent(username, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                                        .addComponent(passwordText)
+                                        .addComponent(password, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
         );
 
         username.setEditable(false);
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/InputCredentialPanel.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/InputCredentialPanel.java	Tue May 03 13:42:39 2016 -0400
@@ -36,6 +36,7 @@
 
 package com.redhat.thermostat.setup.command.internal;
 
+import com.redhat.thermostat.client.swing.components.ThermostatPasswordField;
 import com.redhat.thermostat.setup.command.internal.model.CredentialGenerator;
 import com.redhat.thermostat.setup.command.internal.model.UserCredsValidator;
 import com.redhat.thermostat.setup.command.locale.LocaleResources;
@@ -45,7 +46,6 @@
 import javax.swing.JCheckBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
-import javax.swing.JPasswordField;
 import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 import java.awt.BorderLayout;
@@ -58,7 +58,7 @@
 public class InputCredentialPanel extends CredentialPanel {
 
     private JLabel passwordConfirmText;
-    private JPasswordField passwordConfirm;
+    private ThermostatPasswordField passwordConfirm;
     private JPanel messagePanel;
     private JLabel errorMessage;
     private JCheckBox showPasswordCheckbox;
@@ -102,7 +102,8 @@
         });
 
         passwordConfirmText = new JLabel(translator.localize(LocaleResources.VERIFY_PASSWORD).getContents());
-        passwordConfirm = new JPasswordField();
+        passwordConfirm = new ThermostatPasswordField();
+        passwordConfirm.setCutCopyEnabled(true);
 
         errorMessage = new JLabel();
         errorMessage.setForeground(Color.RED);
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java	Tue May 03 13:42:39 2016 -0400
@@ -64,13 +64,11 @@
 import javax.swing.BoxLayout;
 import javax.swing.JButton;
 import javax.swing.JDialog;
-import javax.swing.JEditorPane;
 import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
 import javax.swing.SwingUtilities;
 import javax.swing.SwingWorker;
 import javax.swing.WindowConstants;
@@ -78,7 +76,9 @@
 import javax.swing.event.HyperlinkListener;
 import javax.swing.text.JTextComponent;
 
+import com.redhat.thermostat.client.swing.components.ThermostatEditorPane;
 import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
+import com.redhat.thermostat.client.swing.components.ThermostatTextArea;
 import com.redhat.thermostat.common.ApplicationInfo;
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.utils.LoggingUtils;
@@ -495,7 +495,7 @@
         }
 
         private static JScrollPane createStackTracePane(Throwable throwable) {
-            JTextArea textArea = new JTextArea();
+            ThermostatTextArea textArea = new ThermostatTextArea();
             textArea.setEditable(false);
             textArea.setText(stackTracetoString(throwable));
             return new ThermostatScrollPane(textArea);
@@ -508,10 +508,10 @@
         }
 
         private static JTextComponent createStepsToResolveText() {
-            JEditorPane component = new JEditorPane();
+            ThermostatEditorPane component = new ThermostatEditorPane();
 
             final String userGuideURL = new ApplicationInfo().getUserGuide();
-            component.setEditorKit(JEditorPane.createEditorKitForContentType("text/html"));
+            component.setEditorKit(ThermostatEditorPane.createEditorKitForContentType("text/html"));
             component.setEditable(false);
             component.setBackground(new Color(0, 0, 0, 0));
             component.setHighlighter(null);
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/StartView.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/StartView.java	Tue May 03 13:42:39 2016 -0400
@@ -36,6 +36,7 @@
 
 package com.redhat.thermostat.setup.command.internal;
 
+import com.redhat.thermostat.client.swing.components.ThermostatEditorPane;
 import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
 import com.redhat.thermostat.setup.command.internal.model.ThermostatSetup;
 import com.redhat.thermostat.common.ApplicationInfo;
@@ -48,7 +49,6 @@
 import javax.swing.ButtonGroup;
 import javax.swing.ImageIcon;
 import javax.swing.JButton;
-import javax.swing.JEditorPane;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
@@ -76,7 +76,7 @@
     private JRadioButton customSetupBtn;
 
     private JPanel toolbar;
-    private JEditorPane thermostatBlurb;
+    private ThermostatEditorPane thermostatBlurb;
     private JPanel midPanel;
 
     private static final String THERMOSTAT_LOGO = "thermostat.png";
@@ -112,8 +112,8 @@
     }
 
     private void createMidPanel() {
-        thermostatBlurb = new JEditorPane();
-        thermostatBlurb.setEditorKit(JEditorPane.createEditorKitForContentType("text/html"));
+        thermostatBlurb = new ThermostatEditorPane();
+        thermostatBlurb.setEditorKit(ThermostatEditorPane.createEditorKitForContentType("text/html"));
         thermostatBlurb.setEditable(false);
         thermostatBlurb.addHyperlinkListener(new HyperlinkListener() {
             public void hyperlinkUpdate(HyperlinkEvent e) {
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVmDeadLockView.java	Tue Apr 05 14:58:50 2016 -0400
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVmDeadLockView.java	Tue May 03 13:42:39 2016 -0400
@@ -53,7 +53,6 @@
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JSplitPane;
-import javax.swing.JTextArea;
 import javax.swing.SwingUtilities;
 
 import com.mxgraph.layout.mxCircleLayout;
@@ -66,6 +65,7 @@
 import com.redhat.thermostat.client.swing.SwingComponent;
 import com.redhat.thermostat.client.swing.components.ThermostatScrollBar;
 import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
+import com.redhat.thermostat.client.swing.components.ThermostatTextArea;
 import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
 import com.redhat.thermostat.common.utils.StringUtils;
 import com.redhat.thermostat.shared.locale.Translate;
@@ -83,7 +83,7 @@
     private final JSplitPane deadlockTextAndVisualization = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
 
     private final JPanel graphical = new JPanel();
-    private final JTextArea description = new JTextArea();
+    private final ThermostatTextArea description = new ThermostatTextArea();
 
     /**
      * Whether to set the divider's location. Do this only once to set a sane