Ticket #2394: templatedialog.patch

File templatedialog.patch, 24.8 KB (added by boyanl, 15 years ago)
  • modules/org.sophie2.base.commons/src/main/java/org/sophie2/base/commons/gui/TriStateCheckBox.java

    ### Eclipse Workspace Patch 1.0
    #P sophie
     
     1package org.sophie2.base.commons.gui; 
     2 
     3import java.awt.Color; 
     4import java.awt.Graphics; 
     5import java.awt.Graphics2D; 
     6 
     7import javax.swing.JCheckBox; 
     8import javax.swing.JToggleButton; 
     9 
     10/** 
     11 * A tri-check box control, extending JCheckBox 
     12 *  
     13 * @author boyanl 
     14 */ 
     15public class TriStateCheckBox extends JCheckBox { 
     16            
     17        /** 
     18         *  Serial version ID for the class. 
     19         */ 
     20        private static final long serialVersionUID = 953941340362829945L; 
     21 
     22        /** 
     23         * Enumerates the possible states of the checkbox 
     24         */ 
     25        public static enum State { 
     26            /** 
     27             * Represents checked state 
     28             */ 
     29            CHECKED, 
     30            /** 
     31             * Represents unchecked state 
     32             */ 
     33            UNCHECKED,  
     34            /** 
     35             * Represents partial state 
     36             */ 
     37            PARTIAL  
     38          } 
     39           
     40          /** 
     41           * Creates an initially unselected check box button with no text, no icon. 
     42           */ 
     43          public TriStateCheckBox() { 
     44              this(null, State.UNCHECKED); 
     45          } 
     46           
     47          /** 
     48           * Creates a check box with text and icon, 
     49           * and specifies whether or not it is initially selected. 
     50           * 
     51           * @param text 
     52           *                    The text of the check box. 
     53           * @param initial 
     54           *                    A value indicating the initial selection state. 
     55           */ 
     56          public TriStateCheckBox(String text, State initial) {       
     57            super.setText(text); 
     58            setModel(new TriStateModel(initial)); 
     59            
     60            //some UI settings 
     61            setRolloverEnabled( false ); 
     62          } 
     63           
     64          
     65          /** 
     66           * Set the new state to either CHECKED, PARTIAL or UNCHECKED. 
     67           * @param state 
     68           *                            The new state to set. 
     69           */ 
     70          public void setState(State state) { 
     71            ((TriStateModel) this.model).setState(state); 
     72          } 
     73           
     74          /** 
     75           * Return the current state, which is determined by the selection status of 
     76           * the model. 
     77           *  
     78           * @return 
     79           *            The state of the checkbox (gets it from the model). 
     80           */ 
     81          public State getState() { 
     82            return ((TriStateModel) this.model).getState(); 
     83          }  
     84           
     85          @Override 
     86          public void setSelected(boolean selected) { 
     87            ((TriStateModel) this.model).setSelected(selected);     
     88          }  
     89           
     90            
     91          @Override 
     92          public void paintComponent( Graphics g ) { 
     93                 super.paintComponent( g ); 
     94                  
     95            if(((TriStateModel) this.model).getState() == State.PARTIAL) { 
     96                //this.getModel().setArmed(false); 
     97              Graphics2D g2 = (Graphics2D) g; 
     98               
     99              int cx = getWidth()/4, width = getWidth()/2 - 1; 
     100              int cy = getHeight()/4, height = getHeight()/2 - 1 ; 
     101               
     102              g2.setColor(Color.blue);       
     103              g2.fillRect( cx,  cy, width , height );   
     104            } 
     105          } 
     106           
     107          /** 
     108           *  The model for the button  
     109           */ 
     110          public static class TriStateModel extends JToggleButton.ToggleButtonModel {       
     111            /** 
     112                 * The serial version ID for this class. 
     113                 */ 
     114                private static final long serialVersionUID = 1980283253004401043L; 
     115                /** 
     116                 * Contains the state of the model 
     117                 */ 
     118                protected State state;   
     119          
     120            /** 
     121             * Creates a new model by a given state. 
     122             *  
     123             * @param state 
     124             *                          Constructs the model from a given state. 
     125             */ 
     126            public TriStateModel(State state) { 
     127              this.state = state; 
     128            } 
     129            
     130            /** 
     131             * Gets the state of the model. 
     132             *  
     133             * @return  
     134             *                  The state of the model. 
     135             */ 
     136            public State getState() { 
     137              return this.state; 
     138            } 
     139             
     140            /** 
     141             * Sets the state of the model 
     142             *  
     143             * @param state 
     144             *                          The new state of the model. 
     145             */ 
     146            public void setState(State state) { 
     147              this.state = state; 
     148             // fireStateChanged(); 
     149            } 
     150             
     151            @Override 
     152                public void setPressed(boolean pressed)  {       
     153              if (pressed) { 
     154                switch(this.state) { 
     155                case UNCHECKED:  
     156                  this.state = State.CHECKED; 
     157                  break; 
     158                case PARTIAL:  
     159                  this.state = State.UNCHECKED; 
     160                  break; 
     161                case CHECKED:  
     162                  this.state = State.UNCHECKED; 
     163                  break; 
     164                }         
     165              } 
     166            } 
     167             
     168            @Override 
     169                public boolean isSelected() {       
     170              return this.state == State.CHECKED; 
     171            }  
     172             
     173            @Override 
     174                public void setSelected(boolean selected) {        
     175              if (selected) { 
     176                this.state = State.CHECKED; 
     177              } else { 
     178                this.state = State.UNCHECKED; 
     179              }       
     180            } 
     181          }   
     182} 
     183 No newline at end of file 
  • modules/org.sophie2.main.app.commons/src/main/java/org/sophie2/main/app/commons/dialogs/TemplateDialog.java

     
    99import java.awt.event.ActionListener; 
    1010import java.awt.event.FocusEvent; 
    1111import java.awt.event.FocusListener; 
     12import java.awt.event.MouseAdapter; 
     13import java.awt.event.MouseEvent; 
     14import java.util.Enumeration; 
    1215import java.util.HashMap; 
    1316import java.util.Map; 
    1417import java.util.Set; 
     
    2124import javax.swing.JPanel; 
    2225import javax.swing.JScrollPane; 
    2326import javax.swing.JTextField; 
     27import javax.swing.JTree; 
     28import javax.swing.tree.DefaultMutableTreeNode; 
     29import javax.swing.tree.DefaultTreeModel; 
     30import javax.swing.tree.TreeCellRenderer; 
     31import javax.swing.tree.TreeModel; 
     32import javax.swing.tree.TreePath; 
    2433 
    2534import org.sophie2.base.bound.BoundCheckBox; 
    2635import org.sophie2.base.bound.BoundControl; 
    2736import org.sophie2.base.bound.BoundGroupCheckBox; 
    2837import org.sophie2.base.bound.BoundControl.EventIds; 
     38import org.sophie2.base.commons.gui.TriStateCheckBox; 
    2939import org.sophie2.base.commons.util.ImmList; 
    3040import org.sophie2.base.commons.util.NaiveImmList; 
    3141import org.sophie2.base.dialogs.Dialog; 
     
    3747import org.sophie2.base.visual.skins.ElementSkinPart; 
    3848import org.sophie2.base.visual.skins.SkinPartDef; 
    3949import org.sophie2.core.mvc.EventFilterBuilder; 
    40 import org.sophie2.core.mvc.LogicR3; 
    4150import org.sophie2.core.mvc.OperationDef; 
    4251import org.sophie2.core.mvc.events.EventR3; 
    4352import org.sophie2.core.prolib.interfaces.RwProp; 
     53import org.sophie2.main.app.commons.dialogs.CheckBoxTree.CheckNode; 
    4454 
    4555/** 
     56 * A checkbox tree class which extends (and internally uses) JTree. 
     57 *  
     58 * @author boyanl 
     59 */ 
     60class CheckBoxTree extends JTree 
     61{ 
     62        /** 
     63         * Serial version ID for the class. 
     64         */ 
     65        private static final long serialVersionUID = -6024233811457619061L; 
     66 
     67        /** 
     68         * A node class for the tree. It contains just the logical data of a node (in this case, a tricheck model),  
     69         * and a backlink to the container tree (needed for the update logic). 
     70         *  
     71         * @author boyanl 
     72         */ 
     73        public static class CheckNode extends DefaultMutableTreeNode { 
     74                /** 
     75                 * Serial version ID for the class. 
     76                 */ 
     77                private static final long serialVersionUID = 1701971996798985354L; 
     78                 
     79                /** 
     80                 * A backlink to the containing tree. Needed for the visual update of items 
     81                 * in setState 
     82                 */ 
     83                private JTree backlink; 
     84                private TriStateCheckBox.TriStateModel state; 
     85                 /** 
     86                 * Default constructor 
     87                 */ 
     88                public CheckNode() { 
     89                    this(null); 
     90                  } 
     91                   
     92                 /** 
     93                 * @param link  
     94                 *                              The backlink value to set to. 
     95                 */ 
     96                void setBacklink(JTree link) { 
     97                          this.backlink = link; 
     98                  } 
     99 
     100                 /** 
     101                 * Constructs the node from a user-defined object (just String is used here, for the label text). 
     102                 *   
     103                 * @param userObject 
     104                 *                                 The user-defined object to construct from. 
     105                 */ 
     106                public CheckNode(Object userObject) { 
     107                    this(userObject, true, false); 
     108                  } 
     109 
     110                 /** 
     111                 * Constructs the node from a user-defined object and sets whether the node allows children and is selected.  
     112                 * @param userObject 
     113                 *                                      The user-passed object. 
     114                 * @param allowsChildren 
     115                 *                                      A parameter indicating whether the node allows children. 
     116                 * @param isSelected 
     117                 *                                      A parameter indicating whether the node is selected. 
     118                 */ 
     119                public CheckNode(Object userObject, boolean allowsChildren, 
     120                      boolean isSelected) { 
     121                        super(userObject, allowsChildren); 
     122                        this.state = new TriStateCheckBox.TriStateModel(TriStateCheckBox.State.UNCHECKED); 
     123                    this.state.setSelected(isSelected); 
     124                  } 
     125                   
     126                /** 
     127                 * Gets the state of the check node. 
     128                 *  
     129                 * @return The state of the node 
     130                 */ 
     131                public TriStateCheckBox.State getState () { 
     132                        return this.state.getState(); 
     133                } 
     134                 
     135                /** 
     136                 * Changes the state of the node to the "next" state. 
     137                 */ 
     138                public void nextState () { 
     139                        this.state.setPressed(true); 
     140                        this.setState (this.state.getState()); 
     141                } 
     142                 
     143                /** 
     144                 * Sets the state of the node. Propagates the changes to children/parent if necessary. 
     145                 *  
     146                 * @param newState 
     147                 *                                      The new state. 
     148                 */ 
     149                @SuppressWarnings("unchecked") 
     150                public void setState (TriStateCheckBox.State newState) { 
     151                        this.state.setState (newState); 
     152                        ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this); 
     153 
     154                    // propagate changes to children (if any) 
     155                    if (this.children != null && newState != TriStateCheckBox.State.PARTIAL) { 
     156                      Enumeration<CheckNode> e = this.children.elements(); 
     157                               
     158                      while (e.hasMoreElements()) { 
     159                        CheckNode node = e.nextElement(); 
     160                         
     161                        if (node.getState() != newState) { 
     162                                node.setState(newState); 
     163                                ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (node); 
     164                        } 
     165                      } 
     166                    } 
     167                     
     168                    //propagate to parent (if any) 
     169                    if (this.getParent() != null) { 
     170                        /* If either all the nodes are selected or all are de-selected, change the state 
     171                         * to selected/de-selected. Otherwise, partial 
     172                         */ 
     173                        boolean allSelected = true, noneSelected = true;  
     174                        Enumeration<CheckNode> e = this.parent.children(); 
     175                         
     176                        while (e.hasMoreElements()) { 
     177                                CheckNode node = e.nextElement(); 
     178                                if (node.getState () != TriStateCheckBox.State.CHECKED) { 
     179                                        allSelected = false; 
     180                                } 
     181                                if (node.getState () != TriStateCheckBox.State.UNCHECKED) { 
     182                                        noneSelected = false; 
     183                                } 
     184                                if (!allSelected && !noneSelected) { 
     185                                        break; 
     186                                } 
     187                        } 
     188                         
     189                        if (allSelected) { 
     190                                ((CheckNode) this.parent).setState (TriStateCheckBox.State.CHECKED); 
     191                                ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this.parent); 
     192                        } else if (noneSelected) { 
     193                                ((CheckNode) this.parent).setState (TriStateCheckBox.State.UNCHECKED); 
     194                                ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this.parent); 
     195                        } else { 
     196                                ((CheckNode) this.parent).setState (TriStateCheckBox.State.PARTIAL); 
     197                                ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this.parent);                                
     198                        } 
     199                    }    
     200                } 
     201                  /** 
     202                 *  Changes the node and updates children/parent accordingly  
     203                 * @param isSelected 
     204                 *                                 The new state of the node 
     205                 */ 
     206                public void setSelected(boolean isSelected) { 
     207                   this.state.setSelected(isSelected); 
     208                   setState (this.state.getState()); 
     209                  } 
     210 
     211                  /** 
     212                 * Getter for the isSelected field 
     213                 * @return The field 
     214                 *                       
     215                 */ 
     216                public boolean isSelected() { 
     217                    return this.state.isSelected(); 
     218                  } 
     219        } 
     220         
     221        /** 
     222         * A renderer class for the tree. Contains the checkbox and the label used to  
     223         * visualize a node in the tree  
     224         * @author boyanl 
     225         * 
     226         */ 
     227        class CheckRenderer extends JPanel implements TreeCellRenderer { 
     228                 
     229                /** 
     230                 * Serial Version ID for the class. 
     231                 */ 
     232                private static final long serialVersionUID = 6223558893132192535L; 
     233                private TriStateCheckBox check; 
     234                private JLabel label; 
     235                 
     236                /** 
     237                 * Default constructor, initializes the checkbox and the label 
     238                 */ 
     239                CheckRenderer() { 
     240                        setLayout(null); 
     241                        add(this.check = new TriStateCheckBox()); 
     242                        add(this.label = new JLabel ()); 
     243                } 
     244 
     245                public Component getTreeCellRendererComponent( 
     246                                JTree tree, Object value, 
     247                                boolean isSelected, boolean expanded, boolean leaf, int row, 
     248                                boolean hasFocus) { 
     249                         
     250                                String stringValue = tree.convertValueToText(value, isSelected, 
     251                                                expanded, leaf, row, hasFocus); 
     252                                setEnabled(tree.isEnabled()); 
     253                                this.label.setText (stringValue); 
     254                                this.check.setState(((CheckNode) value).getState()); 
     255                                return this; 
     256                        } 
     257 
     258                @Override 
     259                public Dimension getPreferredSize() { 
     260                          Dimension d_check = this.check.getPreferredSize(); 
     261                          Dimension d_label = this.label.getPreferredSize(); 
     262                          return new Dimension(d_check.width + d_label.width, 
     263                               (d_check.height < d_label.height ? d_label.height 
     264                                   : d_check.height)); 
     265                  } 
     266 
     267                /** 
     268                 * Layouts the component, centering the label and the checkbox height-wise 
     269                 */ 
     270            @Override 
     271                public void doLayout() { 
     272                          Dimension d_check = this.check.getPreferredSize(); 
     273                          Dimension d_label = this.label.getPreferredSize(); 
     274                           
     275                      int y_check = 0; 
     276                      int y_label = 0; 
     277                      if (d_check.height < d_label.height) { 
     278                          y_check = (d_label.height - d_check.height) / 2; 
     279                      } else { 
     280                          y_label = (d_check.height - d_label.height) / 2; 
     281                      } 
     282                      this.check.setLocation(0, y_check); 
     283                      this.check.setBounds(0, y_check, d_check.width, d_check.height); 
     284                      this.label.setLocation(d_check.width, y_label); 
     285                      this.label.setBounds(d_check.width, y_label, d_label.width, d_label.height); 
     286                  }      
     287        } 
     288         
     289        private class NodeSelectionListener extends MouseAdapter { 
     290                private JTree tree; 
     291                 
     292                NodeSelectionListener(final JTree tree) { 
     293                        this.tree = tree;                        
     294                } 
     295                 
     296                @Override 
     297                public void mouseClicked (MouseEvent e) { 
     298                        int x = e.getX (); 
     299                        int y = e.getY (); 
     300                        int row = this.tree.getRowForLocation(x, y); 
     301                        TreePath path = this.tree.getPathForRow (row); 
     302                         
     303                        if (path != null) { 
     304                                CheckNode node = (CheckNode) path.getLastPathComponent(); 
     305                                node.nextState();                        
     306                        }                        
     307                }        
     308        } 
     309         
     310        /** 
     311         * Adds a new node with a given text and parent node to the tree 
     312         * @param nodeText 
     313         *                              The text 
     314         * @param parent 
     315         *                              The parent 
     316         * @return  
     317         *                      The added node 
     318         */ 
     319        public CheckNode addNode (String nodeText, CheckNode parent) { 
     320                CheckNode node = new CheckNode (nodeText); 
     321                if (parent != null){ 
     322                        parent.add(node); 
     323                } 
     324                node.setBacklink(this); 
     325                return node; 
     326        } 
     327        /** 
     328         * Adds a new node with a given text and parent node to the tree 
     329         * @param nodeText 
     330         *                              The text 
     331         * @return 
     332         *                      The added node 
     333         */ 
     334        public CheckNode addNode (String nodeText) { 
     335                CheckNode node = new CheckNode (nodeText); 
     336                CheckNode root = ((CheckNode) this.getModel ().getRoot ()); 
     337                if (root!= null) { 
     338                        root.add(node); 
     339                } 
     340                node.setBacklink(this); 
     341                return node; 
     342        } 
     343         
     344        /** 
     345         * @return The root node of the tree 
     346         */ 
     347        public CheckNode getRootNode () { 
     348                return (CheckNode) this.getModel().getRoot(); 
     349        } 
     350         
     351        /** 
     352         * Constructs a tree with a given root name 
     353         * @param rootName 
     354         *                                the root name 
     355         */ 
     356        public CheckBoxTree (final String rootName) 
     357        { 
     358                super(); 
     359                CheckNode root = new CheckNode (rootName); 
     360                TreeModel model = new DefaultTreeModel (root); 
     361                setModel (model); 
     362                 
     363                root.setBacklink (this); 
     364                setCellRenderer (new CheckRenderer()); 
     365                addMouseListener(new NodeSelectionListener(this)); 
     366        } 
     367} 
     368 
     369 
     370 
     371 
     372/** 
    46373 * The dialog which is used to add a page or frame as a template. It generates a 
    47374 * tree-like structure of checkboxes which represent the {@link Key}s of the 
    48375 * object to create a template from. 
     
    131458                private JDialog dialog = null; 
    132459                private JPanel mainPanel; 
    133460                private JLabel titleLabel = null; 
    134                 private JPanel checkboxPanel = null; 
     461                private JScrollPane treeScrollPane = null; 
    135462                private JTextField titleField = null; 
    136463                private JButton okButton = null; 
    137464                private JButton cancelButton = null; 
    138465                private JButton selectAllButton = null; 
    139466                private JButton selectNoneButton = null; 
    140                 private BoundGroupCheckBox topLevelCheckBox = null; 
    141                 private Map<Key<?>, SubCheckBox> keyMap = null; 
     467                private CheckBoxTree checkBoxTree = null; 
     468                private Map<Key<?>, CheckBoxTree.CheckNode> keyMap = null; 
    142469                private HashMap<Key<?>, Boolean> keyStates = null; 
    143                 private SubCheckBox isDefaultTemplateCheckBox = null; 
     470                private CheckNode isDefaultTemplateNode = null; 
    144471 
    145472                /** 
    146473                 * Holds the initial info for the dialog. 
     
    194521                                JPanel bottom = new JPanel(); 
    195522                                bottom.add(getOkButton()); 
    196523                                bottom.add(getCancelButton()); 
    197  
    198                                 JScrollPane scrollPane = new JScrollPane(getCheckboxPanel()); 
    199  
     524                 
     525                                this.treeScrollPane = new JScrollPane(); 
     526                                                                 
    200527                                this.mainPanel.add(top, BorderLayout.NORTH); 
    201                                 this.mainPanel.add(scrollPane, BorderLayout.CENTER); 
     528                                this.mainPanel.add(this.treeScrollPane, BorderLayout.CENTER); 
    202529                                this.mainPanel.add(bottom, BorderLayout.SOUTH); 
    203530 
    204531                                this.mainPanel.setSize(PANEL_WIDTH, PANEL_HEIGHT); 
     
    207534                } 
    208535 
    209536                /** 
    210                  * Getter for the check box with the default template value.  
    211                  * 
    212                  * @return 
    213                  *        The check box for the default template. 
    214                  */ 
    215                 public SubCheckBox getIsDefaultTemplateCheckBox() { 
    216                         if(this.isDefaultTemplateCheckBox == null) { 
    217                                 this.isDefaultTemplateCheckBox = new SubCheckBox() { 
    218                                         @Override 
    219                                         public String computeTitle() { 
    220                                                 return DEFAULT_TEMPLATE_CHECK_BOX_LABEL; 
    221                                         } 
    222  
    223                                         @Override 
    224                                         public Boolean getDefault() { 
    225                                                 return false; 
    226                                         } 
    227                                 }; 
    228                         } 
    229                         return this.isDefaultTemplateCheckBox; 
    230                 } 
    231                 /** 
    232537                 * Getter for the title field. 
    233538                 *  
    234539                 * @return The title {@link JTextField}. 
     
    285590                                        @SuppressWarnings("synthetic-access") 
    286591                                        public void actionPerformed(final ActionEvent e) { 
    287592                                                String title = getTitleField().getText(); 
    288                                                 boolean isDefault = getIsDefaultTemplateCheckBox().value().get(); 
     593                                                boolean isDefault = SwingDialog.this.isDefaultTemplateNode.isSelected(); 
    289594 
    290595                                                SwingDialog.this.keyStates = new HashMap<Key<?>, Boolean>(); 
    291                                                 for (Entry<Key<?>, SubCheckBox> entry : SwingDialog.this.keyMap.entrySet()) { 
    292                                                         SwingDialog.this.keyStates.put(entry.getKey(), entry.getValue().value().get()); 
     596                                                for (Entry<Key<?>, CheckNode> entry : SwingDialog.this.keyMap.entrySet()) { 
     597                                                        SwingDialog.this.keyStates.put(entry.getKey(), entry.getValue().isSelected()); 
    293598                                                } 
    294599 
    295600                                                TemplateInfo res = new TemplateInfo(title, SwingDialog.this.keyStates, isDefault);                                               
    296601 
    297602                                                setResultInfo(res); 
    298603                                                SwingDialog.this.dialog.setVisible(false); 
    299                                                 SwingDialog.this.isDefaultTemplateCheckBox.value().set(false); 
    300604                                        } 
    301605 
    302606                                }); 
     
    338642                                this.selectAllButton.addActionListener(new ActionListener() { 
    339643                                        @SuppressWarnings("synthetic-access") 
    340644                                        public void actionPerformed(final ActionEvent e) { 
    341                                                 LogicR3.fire(SwingDialog.this.topLevelCheckBox, null, 
    342                                                                 null, null, BoundControl.EventIds.SUBMIT, true); 
     645                                                SwingDialog.this.checkBoxTree.getRootNode().setSelected(true); 
    343646                                        } 
    344647                                }); 
    345648                        } 
     
    357660                                this.selectNoneButton.addActionListener(new ActionListener() { 
    358661                                        @SuppressWarnings("synthetic-access") 
    359662                                        public void actionPerformed(final ActionEvent e) { 
    360                                                 LogicR3.fire(SwingDialog.this.topLevelCheckBox, null, 
    361                                                                 null, null, 
    362                                                                 BoundControl.EventIds.SUBMIT, false); 
     663                                                SwingDialog.this.checkBoxTree.getRootNode().setSelected(false); 
    363664                                        } 
    364665                                }); 
    365666                        } 
     
    367668                } 
    368669 
    369670                /** 
    370                  * Getter for the panel containing the checkboxes. 
    371                  *  
    372                  * @return The {@link JPanel} which contains the checkboxes. 
    373                  */ 
    374                 public JPanel getCheckboxPanel() { 
    375                         if (this.checkboxPanel == null) { 
    376                                 this.checkboxPanel = new JPanel(); 
    377                         } 
    378                         return this.checkboxPanel; 
    379                 } 
    380  
    381                 /** 
    382671                 * Performs setup of the dialog - sets up the title and the panel with 
    383672                 * the checkboxes. 
    384673                 */ 
    385                 @SuppressWarnings("synthetic-access") 
    386674                private void setup() { 
    387675                        getTitleField().setText(this.initialInfo.getTitle()); 
    388676 
    389                         this.checkboxPanel.removeAll(); 
    390                         this.keyMap = new HashMap<Key<?>, SubCheckBox>(); 
     677                        this.keyMap = new HashMap<Key<?>, CheckNode>(); 
    391678                        this.keyStates = new HashMap<Key<?>, Boolean>(); 
    392679 
    393                         this.topLevelCheckBox = new TopCheckBox() { 
    394                                 @Override 
    395                                 public String computeTitle() { 
    396                                         return SwingDialog.this.initialInfo.getTitle(); 
    397                                 } 
    398                         }; 
    399                         getCheckboxPanel().add(this.topLevelCheckBox.swingComponent().get()); 
    400  
    401                         this.topLevelCheckBox.subControls().add(getIsDefaultTemplateCheckBox()); 
     680                        this.checkBoxTree = new CheckBoxTree (this.initialInfo.getTitle ()); 
     681                        this.isDefaultTemplateNode = this.checkBoxTree.addNode (DEFAULT_TEMPLATE_CHECK_BOX_LABEL); 
    402682                         
     683                        this.treeScrollPane.setViewportView(this.checkBoxTree); 
    403684                        Set<Key<?>> keys = this.initialInfo.getKeyStates().keySet(); 
    404                         for (final Key<?> key : keys) {                          
    405                                 if (key instanceof TemplatedKey && key.getParts().size() == 1) { 
    406                                         Boolean state = this.initialInfo.getKeyStates().get(key); 
    407                                         addTemplatedKey(this.topLevelCheckBox, (TemplatedKey)key, state, this.keyMap, this.keyStates); 
    408                                 } else if (key instanceof CompositeKey) { 
    409                                         TopCheckBox composite = new TopCheckBox() { 
    410                                                 @Override 
    411                                                 public String computeTitle() { 
    412                                                         ImmList<String> parts = key.getParts(); 
    413                                                         return parts.get(parts.size() - 1); 
    414                                                 } 
    415                                         }; 
    416                                         this.topLevelCheckBox.subControls().add(composite); 
    417  
    418                                         String prefix = key.getId().concat(Key.SEPARATOR); 
    419                                         for (final Key<?> subKey : keys) { 
    420                                                 String keyId = subKey.getId(); 
    421                                                 if (subKey instanceof TemplatedKey && keyId.startsWith(prefix)) { 
    422                                                         Boolean state = this.initialInfo.getKeyStates().get(subKey); 
    423                                                         addTemplatedKey(composite, (TemplatedKey)subKey, state, this.keyMap, this.keyStates);                                                    
    424                                                 } 
    425                                         } 
    426                                 } 
     685                        for (final Key<?> key : keys) {  
     686                                addKey(this.checkBoxTree.getRootNode(), key, this.initialInfo.getKeyStates().get(key), this.keyMap, keys, 1); 
    427687                        } 
     688                         
     689                        this.checkBoxTree.expandPath(new TreePath (this.checkBoxTree.getRootNode())); 
    428690                } 
    429691 
    430692                /* 
    431                  * Helper method to reduce code duplication. 
     693                 * Helper method to build the tree, 
     694                 * adds the key if he's at the appropriate level 
    432695                 */ 
    433696                @SuppressWarnings("synthetic-access") 
    434                 private static void addTemplatedKey(BoundGroupCheckBox container, 
    435                                 final TemplatedKey<?> key, final Boolean state, Map<Key<?>, SubCheckBox> keyMap, HashMap<Key<?>, Boolean> keyStates) { 
    436                         if (!HIDDEN_KEYS.contains(key)) { 
    437                                 SubCheckBox subCheckbox = new SubCheckBox() { 
    438                                         @Override 
    439                                         public String computeTitle() { 
    440                                                 ImmList<String> parts = key.getParts(); 
    441                                                 return parts.get(parts.size() - 1); 
     697                private void addKey (CheckNode parent, final Key<?> key, final Boolean state,  Map<Key<?>, CheckNode> keyControlMap, Set<Key<?>> keys, int level) { 
     698                        if (key instanceof TemplatedKey) { 
     699                                if (!HIDDEN_KEYS.contains((TemplatedKey) key) && key.getParts().size() == level) { 
     700                                        ImmList<String> parts = key.getParts(); 
     701                                        String text = parts.get(parts.size() - 1); 
     702                                         
     703                                        CheckNode node = this.checkBoxTree.addNode(text, parent); 
     704                                        node.setSelected(state); 
     705                                        keyControlMap.put(key, node); 
     706                                } 
     707                        } 
     708                         
     709                        else if (key instanceof CompositeKey && key.getParts().size() == level) { 
     710                                ImmList<String> parts = key.getParts(); 
     711                                String text = parts.get(parts.size() - 1); 
     712                                 
     713                                CheckNode node = this.checkBoxTree.addNode(text, parent); 
     714                                 
     715                                String prefix = key.getId().concat(Key.SEPARATOR); 
     716                                for (final Key<?> subKey : keys) { 
     717                                        String keyId = subKey.getId(); 
     718                                        if (keyId.startsWith(prefix)) { 
     719                                                Boolean subState = this.initialInfo.getKeyStates().get(subKey); 
     720                                                addKey(node, subKey, subState, this.keyMap, keys, level + 1);                                                    
    442721                                        } 
    443  
    444                                         @Override 
    445                                         public Boolean getDefault() { 
    446                                                 return state; 
    447                                         } 
    448                                 }; 
    449  
    450                                 container.subControls().add(subCheckbox); 
    451                                 keyMap.put(key, subCheckbox); 
    452                                 keyStates.put(key, state); 
     722                                } 
     723                                 
    453724                        } 
    454725                } 
    455726