1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: AdvancedConfigEditor.java 2230 2012-02-08 00:00:10Z dmsmith $
21   */
22  package org.crosswire.common.config.swing;
23  
24  import java.awt.BorderLayout;
25  import java.awt.Color;
26  import java.awt.Component;
27  import java.awt.Dimension;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  
33  import javax.swing.BorderFactory;
34  import javax.swing.JComponent;
35  import javax.swing.JScrollPane;
36  import javax.swing.JTree;
37  import javax.swing.UIManager;
38  import javax.swing.tree.DefaultTreeCellRenderer;
39  
40  import org.crosswire.common.config.Choice;
41  import org.crosswire.common.swing.CWScrollPane;
42  import org.crosswire.common.swing.CWMsg;
43  
44  /**
45   * A further refinement of a Tree Configuration Editor
46   * 
47   * @see gnu.lgpl.License for license details.<br>
48   *      The copyright to this program is held by it's authors.
49   * @author Joe Walker [joe at eireneh dot com]
50   */
51  public class AdvancedConfigEditor extends TreeConfigEditor {
52      /**
53       * WARNING: this code is not called from anywhere and is probably broken
54       */
55      protected AdvancedConfigEditor() {
56          ctm = new AdvancedConfigureTreeModel();
57          tree = new JTree();
58          JScrollPane scroll = new CWScrollPane(tree);
59          scroll.setPreferredSize(new Dimension(150, 150));
60  
61          CustomTreeCellRenderer render = new CustomTreeCellRenderer();
62          comps = new HashMap<String, Component>();
63  
64          // Hack: tree depends on it being a Color not a sub of it.
65          Color orig = UIManager.getColor("control");
66          Color bg = new Color(orig.getRed(), orig.getGreen(), orig.getBlue());
67  
68          // This seems to be broken ...
69          render.setLeafIcon(TASK_ICON_SMALL);
70          render.setBackgroundNonSelectionColor(bg);
71  
72          tree.setBackground(bg);
73          tree.setModel(ctm);
74          tree.setCellRenderer(render);
75          tree.setShowsRootHandles(true);
76          tree.setRootVisible(false);
77          tree.putClientProperty("JTree.lineStyle", "None");
78          tree.setSelectionRow(0);
79          tree.setEditable(true);
80  
81          setLayout(new BorderLayout(5, 10));
82          setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
83  
84          add(BorderLayout.CENTER, scroll);
85          add(BorderLayout.SOUTH, new ButtonPane(this));
86      }
87  
88      /**
89       * Updates to the tree that we need to do on any change
90       */
91      @Override
92      protected void updateTree() {
93          // expand the tree
94          /*
95           * int row = 0; while (row < tree.getRowCount()) {
96           * tree.expandRow(row++); }
97           */
98  
99          ctm.fireTreeStructureChanged(this);
100     }
101 
102     /**
103      * Add a Choice to our set of panels
104      */
105     @Override
106     protected void addChoice(Choice model) {
107         String key = model.getKey();
108         Field field = FieldMap.getField(model);
109         fields.put(key, field);
110 
111         // Add the Field to the FieldPanel
112         JComponent comp = field.getComponent();
113         comp.setToolTipText(model.getHelpText());
114         comps.put(key, comp);
115 
116         // Fill in the current value
117         String value = config.getLocal(key);
118         field.setValue(value);
119     }
120 
121     /**
122      * Add a Choice to our set of panels
123      */
124     @Override
125     protected void removeChoice(Choice choice) {
126         String key = choice.getKey();
127         Field field = fields.get(key);
128         if (field != null) {
129             fields.remove(key);
130         }
131 
132         Component comp = comps.get(key);
133         if (comp != null) {
134             comps.remove(key);
135         }
136     }
137 
138     /**
139      * Used to update the configuration panel whenever someone selects a
140      * different item form the tree on the LHS of the configuration dialog.
141      */
142     @Override
143     public void selectCard() {
144         Object obj = tree.getLastSelectedPathComponent();
145         if (obj == null) {
146             return;
147         }
148 
149         // TRANSLATOR: This is the label for the banner when one opens a type of Options/Preferences.
150         // {0} is the type of preference, e.g. Bible Display
151         title.setText(CWMsg.gettext("{0} Preferences", obj));
152 
153         // Get the name of the current deck
154         Object[] list = tree.getSelectionPath().getPath();
155         StringBuilder path = new StringBuilder();
156 
157         for (int i = 1; i < list.length; i++) {
158             if (i > 1) {
159                 path.append('.');
160             }
161 
162             path.append(list[i].toString());
163         }
164 
165         String key = path.toString();
166         if (decks.containsKey(key)) {
167             layout.show(deck, key);
168         } else {
169             layout.show(deck, BLANK);
170         }
171 
172         deck.repaint();
173     }
174 
175     /**
176      * A hash of components
177      */
178     protected Map<String, Component> comps;
179 
180     /**
181      * Serialization ID
182      */
183     private static final long serialVersionUID = 3616451198199345203L;
184 
185     /**
186      * A custom data model for the TreeConfig Tree
187      * 
188      * @author Joe Walker
189      */
190     class AdvancedConfigureTreeModel extends ConfigureTreeModel {
191         /*
192          * (non-Javadoc)
193          * 
194          * @see
195          * org.crosswire.common.config.swing.TreeConfigEditor.ConfigureTreeModel
196          * #getChildren(java.lang.String)
197          */
198         @Override
199         protected List<String> getChildren(String path) {
200             List<String> retcode = new ArrayList<String>();
201 
202             for (Choice choice : config) {
203                 if (choice.isHidden()) {
204                     continue;
205                 }
206 
207                 String temp = choice.getKey();
208 
209                 if (temp.startsWith(path) && !temp.equals(path)) {
210                     // Chop off the similar start
211                     temp = temp.substring(path.length());
212                     if (temp.charAt(0) == '.') {
213                         temp = temp.substring(1);
214                     }
215 
216                     // Chop off all after the first dot
217                     int dotPos = temp.indexOf('.');
218                     if (dotPos != -1) {
219                         temp = temp.substring(0, dotPos);
220                     }
221 
222                     // Add it to the list if needed
223                     if (temp.length() > 0 && !retcode.contains(temp)) {
224                         retcode.add(temp);
225                     }
226                 }
227             }
228 
229             return retcode;
230         }
231 
232         /*
233          * (non-Javadoc)
234          * 
235          * @see javax.swing.tree.TreeModel#getChild(java.lang.Object, int)
236          */
237         @Override
238         public Object getChild(Object parent, int index) {
239             if (parent instanceof CompNode) {
240                 return null;
241             }
242 
243             String path = ((Node) parent).getFullName();
244             List<String> children = getChildren(path);
245 
246             if (children.isEmpty()) {
247                 return new CompNode(path);
248             }
249 
250             String name = children.get(index);
251             return new Node(path, name);
252         }
253 
254         /*
255          * (non-Javadoc)
256          * 
257          * @see javax.swing.tree.TreeModel#getChildCount(java.lang.Object)
258          */
259         @Override
260         public int getChildCount(Object parent) {
261             if (parent instanceof CompNode) {
262                 return 0;
263             }
264 
265             String path = ((Node) parent).getFullName();
266             int children = getChildren(path).size();
267             if (children == 0) {
268                 children = 1;
269             }
270 
271             return children;
272         }
273 
274         /*
275          * (non-Javadoc)
276          * 
277          * @see javax.swing.tree.TreeModel#isLeaf(java.lang.Object)
278          */
279         @Override
280         public boolean isLeaf(Object node) {
281             return node instanceof CompNode;
282         }
283     }
284 
285     /**
286      * Simple Tree Node
287      */
288     static class CompNode {
289         /**
290          * Create a node with a name and path
291          */
292         public CompNode(String path) {
293             this.path = path;
294         }
295 
296         /*
297          * (non-Javadoc)
298          * 
299          * @see java.lang.Object#toString()
300          */
301         @Override
302         public String toString() {
303             return path;
304         }
305 
306         /**
307          * The path to us
308          */
309         public String getFullName() {
310             return path;
311         }
312 
313         /**
314          * The path to us
315          */
316         private String path;
317     }
318 
319     /**
320      * The renderer for our tree
321      */
322     class CustomTreeCellRenderer extends DefaultTreeCellRenderer {
323         /*
324          * (non-Javadoc)
325          * 
326          * @see
327          * javax.swing.tree.TreeCellRenderer#getTreeCellRendererComponent(javax
328          * .swing.JTree, java.lang.Object, boolean, boolean, boolean, int,
329          * boolean)
330          */
331         @Override
332         public Component getTreeCellRendererComponent(JTree jtree, Object value, boolean isselected, boolean expanded, boolean leaf, int row, boolean focus) {
333             if (!(value instanceof CompNode)) {
334                 return super.getTreeCellRendererComponent(jtree, value, isselected, expanded, leaf, row, focus);
335             }
336 
337             JComponent comp = (JComponent) comps.get(value.toString());
338 
339             if (comp == null) {
340                 return super.getTreeCellRendererComponent(jtree, value, isselected, expanded, leaf, row, focus);
341             }
342 
343             if (isselected) {
344                 comp.setBorder(BorderFactory.createLineBorder(Color.black));
345             } else {
346                 comp.setBorder(BorderFactory.createEmptyBorder());
347             }
348 
349             return comp;
350         }
351 
352         /**
353          * Serialization ID
354          */
355         private static final long serialVersionUID = 3256720688860576049L;
356     }
357 }
358