You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by se...@apache.org on 2012/12/06 02:42:22 UTC

svn commit: r1417726 - in /jmeter/trunk: src/core/org/apache/jmeter/gui/util/TristateCheckBox.java src/core/org/apache/jmeter/gui/util/TristateState.java test/src/org/apache/jmeter/gui/util/TristateCheckBoxTest.java xdocs/changes.xml

Author: sebb
Date: Thu Dec  6 01:42:20 2012
New Revision: 1417726

URL: http://svn.apache.org/viewvc?rev=1417726&view=rev
Log:
Add tristate checkbox implementation
Bugzilla Id: 54251

Added:
    jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateCheckBox.java   (with props)
    jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateState.java   (with props)
    jmeter/trunk/test/src/org/apache/jmeter/gui/util/TristateCheckBoxTest.java   (with props)
Modified:
    jmeter/trunk/xdocs/changes.xml

Added: jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateCheckBox.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateCheckBox.java?rev=1417726&view=auto
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateCheckBox.java (added)
+++ jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateCheckBox.java Thu Dec  6 01:42:20 2012
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.gui.util;
+
+import java.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.EventQueue;
+import java.awt.Graphics;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.io.Serializable;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.JCheckBox;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+
+import sun.swing.plaf.synth.SynthUI;
+
+// derived from: http://www.javaspecialists.eu/archive/Issue145.html
+
+public final class TristateCheckBox extends JCheckBox {
+    private static final long serialVersionUID = 1L;
+    // Listener on model changes to maintain correct focusability
+    private final ChangeListener enableListener = new ChangeListener() {
+        @Override
+        public void stateChanged(ChangeEvent e) {
+            TristateCheckBox.this.setFocusable(
+                    getModel().isEnabled());
+        }
+    };
+
+    public TristateCheckBox(String text) {
+        this(text, null, TristateState.DESELECTED);
+    }
+
+    public TristateCheckBox(String text, Icon icon,
+            TristateState initial) {
+        super(text, icon);
+
+        //Set default single model
+        setModel(new TristateButtonModel(initial, this));
+
+        // override action behaviour
+        super.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mousePressed(MouseEvent e) {
+                TristateCheckBox.this.iterateState();
+            }
+        });
+        ActionMap actions = new ActionMapUIResource();
+        actions.put("pressed", new AbstractAction() {
+            private static final long serialVersionUID = 1L;
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                TristateCheckBox.this.iterateState();
+            }
+        });
+        actions.put("released", null);
+        SwingUtilities.replaceUIActionMap(this, actions);
+    }
+
+    // Next two methods implement new API by delegation to model
+    public void setIndeterminate() {
+        getTristateModel().setIndeterminate();
+    }
+
+    public boolean isIndeterminate() {
+        return getTristateModel().isIndeterminate();
+    }
+
+    public TristateState getState() {
+        return getTristateModel().getState();
+    }
+
+    //Overrides superclass method
+    @Override
+    public void setModel(ButtonModel newModel) {
+        super.setModel(newModel);
+        //Listen for enable changes
+        if (model instanceof TristateButtonModel)
+            model.addChangeListener(enableListener);
+    }
+
+    //Empty override of superclass method
+    @Override
+    public synchronized void addMouseListener(MouseListener l) {
+    }
+
+    // Mostly delegates to model
+    private void iterateState() {
+        //Maybe do nothing at all?
+        if (!getModel().isEnabled()) return;
+
+        grabFocus();
+
+        // Iterate state
+        getTristateModel().iterateState();
+
+        // Fire ActionEvent
+        int modifiers = 0;
+        AWTEvent currentEvent = EventQueue.getCurrentEvent();
+        if (currentEvent instanceof InputEvent) {
+            modifiers = ((InputEvent) currentEvent).getModifiers();
+        } else if (currentEvent instanceof ActionEvent) {
+            modifiers = ((ActionEvent) currentEvent).getModifiers();
+        }
+        fireActionPerformed(new ActionEvent(this,
+                ActionEvent.ACTION_PERFORMED, getText(),
+                System.currentTimeMillis(), modifiers));
+    }
+
+    //Convenience cast
+    public TristateButtonModel getTristateModel() {
+        return (TristateButtonModel) super.getModel();
+    }
+
+    private static class TristateButtonModel extends ToggleButtonModel {
+
+        private static final long serialVersionUID = 1L;
+        private TristateState state = TristateState.DESELECTED;
+        private final TristateCheckBox tristateCheckBox;
+        private final Icon icon;
+
+        public TristateButtonModel(TristateState initial,
+                TristateCheckBox tristateCheckBox) {
+            setState(TristateState.DESELECTED);
+            this.tristateCheckBox = tristateCheckBox;
+            icon = new TristateCheckBoxIcon(tristateCheckBox);
+        }
+
+        public void setIndeterminate() {
+            setState(TristateState.INDETERMINATE);
+        }
+
+        public boolean isIndeterminate() {
+            return state == TristateState.INDETERMINATE;
+        }
+
+        // Overrides of superclass methods
+        @Override
+        public void setEnabled(boolean enabled) {
+            super.setEnabled(enabled);
+            // Restore state display
+            displayState();
+        }
+
+        @Override
+        public void setSelected(boolean selected) {
+            setState(selected ?
+                    TristateState.SELECTED : TristateState.DESELECTED);
+        }
+
+        // Empty overrides of superclass methods
+        @Override
+        public void setArmed(boolean b) {
+        }
+
+        @Override
+        public void setPressed(boolean b) {
+        }
+
+        void iterateState() {
+            setState(state.next());
+        }
+
+        private void setState(TristateState state) {
+            //Set internal state
+            this.state = state;
+            displayState();
+            if (state == TristateState.INDETERMINATE && isEnabled()) {
+                // force the events to fire
+
+                // Send ChangeEvent
+                fireStateChanged();
+
+                // Send ItemEvent
+                int indeterminate = 3;
+                fireItemStateChanged(new ItemEvent(
+                        this, ItemEvent.ITEM_STATE_CHANGED, this,
+                        indeterminate));
+            }
+        }
+
+        private void displayState() {
+            super.setSelected(state != TristateState.DESELECTED);
+            //    original used:
+//            super.setArmed(state == TristateState.INDETERMINATE);
+            if (state == TristateState.INDETERMINATE) {
+                tristateCheckBox.setIcon(icon); // Needed for all but Nimbus
+                tristateCheckBox.setSelectedIcon(icon); // Nimbus works - after a fashion - with this
+            } else { // reset
+                if (tristateCheckBox!= null){
+                    tristateCheckBox.setIcon(null);
+                    tristateCheckBox.setSelectedIcon(null);
+                }
+            }
+            super.setPressed(state == TristateState.INDETERMINATE);
+
+        }
+
+        public TristateState getState() {
+            return state;
+        }
+    }
+
+  // derived from: http://www.coderanch.com/t/342563/GUI/java/TriState-CheckBox
+
+    private static class TristateCheckBoxIcon implements Icon, UIResource, Serializable {
+
+        private static final long serialVersionUID = 290L;
+
+        private final TristateCheckBox tristateCheckBox;
+
+        public TristateCheckBoxIcon(TristateCheckBox tristateCheckBox) {
+            this.tristateCheckBox = tristateCheckBox;
+            System.out.println(tristateCheckBox.getUI());
+        }
+
+        @Override
+        public void paintIcon(Component c, Graphics g, int x, int y) {
+            JCheckBox cb = (JCheckBox) c;
+            ButtonModel model = cb.getModel();
+            int controlSize = getControlSize();
+
+            // TODO fix up for Nimbus LAF
+            if (model.isEnabled()) {
+                if (model.isPressed() && model.isArmed()) {
+                    g.setColor(MetalLookAndFeel.getControlShadow());
+                    g.fillRect(x, y, controlSize - 1, controlSize - 1);
+                    drawPressed3DBorder(g, x, y, controlSize, controlSize);
+                } else {
+                    drawFlush3DBorder(g, x, y, controlSize, controlSize);
+                }
+                g.setColor(MetalLookAndFeel.getControlInfo());
+            } else {
+                g.setColor(MetalLookAndFeel.getControlShadow());
+                g.drawRect(x, y, controlSize - 1, controlSize - 1);
+            }
+
+            drawCross(c, g, x, y);
+
+        }// paintIcon
+
+        private void drawCross(Component c, Graphics g, int x, int y) {
+            int controlSize = getControlSize();
+            g.drawLine(x + (controlSize - 4), y + 2, x + 3, y
+                    + (controlSize - 5));
+            g.drawLine(x + (controlSize - 4), y + 3, x + 3, y
+                    + (controlSize - 4));
+            g.drawLine(x + 3, y + 2, x + (controlSize - 4), y
+                    + (controlSize - 5));
+            g.drawLine(x + 3, y + 3, x + (controlSize - 4), y
+                    + (controlSize - 4));
+        }
+
+        private void drawFlush3DBorder(Graphics g, int x, int y, int w, int h) {
+            g.translate(x, y);
+            g.setColor(MetalLookAndFeel.getControlDarkShadow());
+            g.drawRect(0, 0, w - 2, h - 2);
+            g.setColor(MetalLookAndFeel.getControlHighlight());
+            g.drawRect(1, 1, w - 2, h - 2);
+            g.setColor(MetalLookAndFeel.getControl());
+            g.drawLine(0, h - 1, 1, h - 2);
+            g.drawLine(w - 1, 0, w - 2, 1);
+            g.translate(-x, -y);
+        }
+
+        private void drawPressed3DBorder(Graphics g, int x, int y, int w, int h) {
+            g.translate(x, y);
+            drawFlush3DBorder(g, 0, 0, w, h);
+            g.setColor(MetalLookAndFeel.getControlShadow());
+            g.drawLine(1, 1, 1, h - 2);
+            g.drawLine(1, 1, w - 2, 1);
+            g.translate(-x, -y);
+        }
+
+        @Override
+        public int getIconWidth() {
+            return getControlSize();
+        }
+
+        @Override
+        public int getIconHeight() {
+            return getControlSize();
+        }
+
+        private int getControlSize() {
+            if (tristateCheckBox.getUI() instanceof SynthUI) {
+                return 18; // see http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/_nimbusDefaults.html
+            }
+            return 13;
+        }
+    }
+}
\ No newline at end of file

Propchange: jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateCheckBox.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateCheckBox.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateState.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateState.java?rev=1417726&view=auto
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateState.java (added)
+++ jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateState.java Thu Dec  6 01:42:20 2012
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.gui.util;
+
+//derived from: http://www.javaspecialists.eu/archive/Issue145.html
+
+public enum TristateState {
+  SELECTED {
+    @Override
+    public TristateState next() {
+      return INDETERMINATE;
+    }
+  },
+  INDETERMINATE {
+    @Override
+    public TristateState next() {
+      return DESELECTED;
+    }
+  },
+  DESELECTED {
+    @Override
+    public TristateState next() {
+      return SELECTED;
+    }
+  };
+
+  public abstract TristateState next();
+}
\ No newline at end of file

Propchange: jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateState.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jmeter/trunk/src/core/org/apache/jmeter/gui/util/TristateState.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: jmeter/trunk/test/src/org/apache/jmeter/gui/util/TristateCheckBoxTest.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/test/src/org/apache/jmeter/gui/util/TristateCheckBoxTest.java?rev=1417726&view=auto
==============================================================================
--- jmeter/trunk/test/src/org/apache/jmeter/gui/util/TristateCheckBoxTest.java (added)
+++ jmeter/trunk/test/src/org/apache/jmeter/gui/util/TristateCheckBoxTest.java Thu Dec  6 01:42:20 2012
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.gui.util;
+
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.JCheckBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.UIManager;
+
+//derived from: http://www.javaspecialists.eu/archive/Issue145.html
+
+public class TristateCheckBoxTest {
+    public static void main(String args[]) throws Exception {
+//        UIManager.get
+        JFrame frame = new JFrame("TristateCheckBoxTest");
+        frame.setLayout(new GridLayout(0, 1, 15, 15));
+        UIManager.LookAndFeelInfo[] lfs =
+                UIManager.getInstalledLookAndFeels();
+        for (UIManager.LookAndFeelInfo lf : lfs) {
+            System.out.println("Look&Feel " + lf.getName());
+            UIManager.setLookAndFeel(lf.getClassName());
+            frame.add(makePanel(lf.getName()));
+        }
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.pack();
+        frame.setVisible(true);
+    }
+
+    private static JPanel makePanel(String name) {
+        final TristateCheckBox tristateBox = new TristateCheckBox("Tristate checkbox");
+        tristateBox.addItemListener(new ItemListener() {
+            @Override
+            public void itemStateChanged(ItemEvent e) {
+                System.out.println(e);
+                switch(tristateBox.getState()) {
+                case SELECTED:
+                    System.out.println("Selected"); break;
+                case DESELECTED:
+                    System.out.println("Not Selected"); break;
+                case INDETERMINATE:
+                    System.out.println("Tristate Selected"); break;
+                }
+            }
+        });
+        tristateBox.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                System.out.println(e);
+                switch(tristateBox.getState()) {
+                case SELECTED:
+                    System.out.println("Selected"); break;
+                case DESELECTED:
+                    System.out.println("Not Selected"); break;
+                case INDETERMINATE:
+                    System.out.println("Tristate Selected"); break;
+                }
+            }
+        });
+        final JCheckBox normalBox = new JCheckBox("Normal checkbox");
+        normalBox.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                System.out.println(e);
+            }
+        });
+
+        final JCheckBox enabledBox = new JCheckBox("Enable", true);
+        enabledBox.addItemListener(new ItemListener() {
+            @Override
+            public void itemStateChanged(ItemEvent e) {
+                tristateBox.setEnabled(enabledBox.isSelected());
+                normalBox.setEnabled(enabledBox.isSelected());
+            }
+        });
+
+        JPanel panel = new JPanel(new GridLayout(0, 1, 5, 5));
+        panel.add(new JLabel(name));
+        panel.add(tristateBox);
+        panel.add(normalBox);
+        panel.add(enabledBox);
+        return panel;
+    }
+}
\ No newline at end of file

Propchange: jmeter/trunk/test/src/org/apache/jmeter/gui/util/TristateCheckBoxTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jmeter/trunk/test/src/org/apache/jmeter/gui/util/TristateCheckBoxTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1417726&r1=1417725&r2=1417726&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml (original)
+++ jmeter/trunk/xdocs/changes.xml Thu Dec  6 01:42:20 2012
@@ -213,6 +213,7 @@ to the elements View Results Tree, Asser
 <li><bugzilla>54204</bugzilla> - Result Status Action Handler : Add start next thread loop option</li>
 <li><bugzilla>54232</bugzilla> - Search Feature : Add a button to search and expand results</li>
 <li><bugzilla>54155</bugzilla> - ModuleController : Add a shortcut button to unfold the tree up to referenced controller and highlight it</li>
+<li><bugzilla>54251</bugzilla> - Add tristate checkbox implementation</li>
 </ul>
 
 <h2>Non-functional changes</h2>