b085d2b9cb26325f5dcac29dcff9aedbc834a3bc
[scilab.git] / scilab / modules / preferences / src / java / org / scilab / modules / preferences / XUpdateVisitor.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2011 - Pierre GRADIT
4  * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
11  *
12  */
13
14 package org.scilab.modules.preferences;
15
16 import java.awt.Color;
17 import java.awt.Component;
18 import java.awt.Container;
19 import java.awt.Dimension;
20 import java.awt.FlowLayout;
21 import java.lang.reflect.Constructor;
22 import java.lang.reflect.InvocationTargetException;
23 import java.util.Map;
24
25 import javax.swing.AbstractButton;
26 import javax.swing.BorderFactory;
27 import javax.swing.Box;
28 import javax.swing.JLabel;
29 import javax.swing.JPanel;
30 import javax.swing.border.Border;
31 import javax.swing.border.TitledBorder;
32
33 import org.scilab.modules.preferences.Component.Entry;
34 import org.scilab.modules.preferences.Component.FileSelector;
35 import org.scilab.modules.preferences.Component.Scroll;
36 import org.scilab.modules.preferences.Component.Table;
37 import org.scilab.modules.preferences.Component.Select;
38
39 import org.w3c.dom.Node;
40 import org.w3c.dom.NodeList;
41
42 /** Updates swing component to reach correspondence with dom nodes.
43  */
44 public class XUpdateVisitor {
45
46     /**
47      * stores preceding correspondence to compute diff.
48      */
49     private Map<Component, XSentinel> matching;
50
51     /** Construction of  visitor.
52      *
53      * @param table : previous correspondence.
54      */
55     public XUpdateVisitor(final Map<Component, XSentinel> table) {
56         matching = table;
57     }
58
59     /** build a component from scratch with a given node.
60      *
61      * @param view : container of the built component.
62      * @param peer : peer of the container.
63      * @param item : peer of the built component.
64      * @param index : component index in container layout.
65      * @return a built component inserted in its container.
66      */
67     public final Component build(final Container view, final Node peer, final Node item, final int index) {
68         Component component = buildPeerFor(item);
69         Object constraints = getLayoutConstraints(peer, item);
70         if (index >= 0) {
71             view.add(component, constraints, index);
72         } else {
73             view.add(component, constraints);
74         }
75         return component;
76     }
77
78     /** Suppress a component.
79      *
80      * @param view : container of the suppressed component.
81      * @param component : suppressed component.
82      */
83     public final void forget(final Container view, final Component component) {
84         view.remove(component);
85         matching.remove(component);
86     }
87
88     /** Computes a recursive diff on both tree structure
89      *  to achieve correspondence.
90      *
91      * @param view : the visited container.
92      * @param peer : the visited node.
93      */
94     public final void visit(final Container view, final Node peer) {
95         int visibleIndex = 0;
96         int count;
97         NodeList nodes = peer.getChildNodes();
98         int size = nodes.getLength();
99         Component component;
100         XSentinel sentinel;
101         String indent;
102
103         if (view instanceof Scroll) {
104             count = ((Scroll) view).getXComponentCount();
105         } else {
106             count = view.getComponentCount();
107         }
108
109         for (int allIndex = 0; allIndex < size; allIndex++) {
110             Node item = nodes.item(allIndex);
111             if (isVisible(item)) {
112                 if (visibleIndex < count) {
113                     if (view instanceof Scroll) {
114                         component = ((Scroll) view).getXComponent(visibleIndex);
115                     } else {
116                         component = view.getComponent(visibleIndex);
117                     }
118                     sentinel = matching.get(component);
119                     if (sentinel == null || !sentinel.checks(item)) {
120                         forget(view, component);
121                         component = build(view, peer, item, visibleIndex);
122                     }
123                 } else {
124                     component = build(view, peer, item, -1);
125                 }
126                 if (component instanceof XComponent) {
127                     // Rebuild container children.
128                     Container container = (Container) component;
129                     visit(container, item);
130                 }
131                 visibleIndex++;
132             }
133         }
134
135         // Clean children tail.
136         while (visibleIndex < view.getComponentCount()) {
137             component = view.getComponent(visibleIndex);
138             if (component instanceof XComponent) {
139                 forget(view, component);
140                 continue;
141             }
142             visibleIndex++;
143         }
144
145         // Sentinel sets watch.
146         sentinel = matching.get(view);
147         if (sentinel == null) {
148             sentinel = new XSentinel(view, peer);
149             matching.put(view, sentinel);
150             if (view instanceof XComponent) {
151                 addListeners(view, peer, sentinel);
152             }
153         } else {
154             sentinel.setPeer(peer);
155         }
156         // Attribute correspondence once children rebuilt.
157         if (view instanceof XComponent) {
158             XComponent xView = (XComponent) view;
159             xView.refresh(peer);
160         }
161     }
162
163     /** Builds the layout constraint object.
164      *
165      * @param parent : parent node
166      * @param current : current node
167      * @return layout constraint (e.g. border side for border layout)
168      */
169     final Object getLayoutConstraints(final Node parent, final Node current) {
170         if (XConfigManager.getAttribute(parent, "layout").equals("border")) {
171             return XConfigManager.getAttribute(current, "border-side");
172         }
173
174         if (parent.getNodeName().equals("Grid")) {
175             return current;
176         }
177         return null;
178     }
179
180     /** Checks whether a node is visible or not.
181      *
182      * @param node : the checked node
183      * @return its visibility
184      */
185     public final boolean isVisible(final Node node) {
186         // a. Event nodes are invisibles.
187         if (node.getNodeName().equals("mouseClicked")) {
188             return false;
189         }
190         if (node.getNodeName().equals("actionPerformed")) {
191             return false;
192         }
193         if (node.getNodeName().equals("entryChanged")) {
194             return false;
195         }
196         // b. Text nodes with only invisible characters are invisible.
197         if (node.getNodeName().equals("#text")) {
198             if (node.getNodeValue().replaceAll("^[ \t\n]+$", "").equals("")) {
199                 return false;
200             }
201         }
202         // c. Chooser options are invisible.
203         if (node.getNodeName().equals("option")) {
204             return false;
205         }
206         // d. Table descriptors are invisible.
207         if (node.getNodeName().startsWith("table")) {
208             return false;
209         }
210
211         // d. List element are invisible.
212         if (node.getNodeName().equals("listElement")) {
213             return false;
214         }
215
216         // d. List element are invisible.
217         if (node.getNodeName().equals("html")) {
218             return false;
219         }
220         return true;
221     }
222
223     /** Link XSentinal as listener for the given component.
224      *
225      * @param component : listened component
226      * @param node : peer node of the component
227      * @param sentinel : listener of component, node interpreter
228      */
229     public final void addListeners(final Component component, final Node node, final XSentinel sentinel) {
230         String listener = XCommonManager.getAttribute(node, "listener");
231         if (listener.equals("ActionListener")) {
232             if (component instanceof AbstractButton) {
233                 AbstractButton button = (AbstractButton) component;
234                 button.addActionListener(sentinel);
235                 return;
236             }
237             if (component instanceof XChooser) {
238                 XChooser chooser = (XChooser) component;
239                 chooser.addActionListener(sentinel);
240                 return;
241             }
242         }
243
244         if (listener.equals("MouseListener")) {
245             //component.addKeyListener(sentinel); Provide focus with proper focus policy.
246             component.addMouseListener(sentinel);
247             return;
248         }
249
250         if (listener.equals("KeyListener")) {
251             component.addKeyListener(sentinel);
252             return;
253         }
254
255         if (listener.equals("EntryListener")) {
256             if (component instanceof Entry) {
257                 ((Entry) component).getDocument().addDocumentListener(sentinel);
258                 return;
259             }
260
261             if (component instanceof FileSelector) {
262                 ((FileSelector) component).addDocumentListener(sentinel);
263                 return;
264             }
265         }
266
267         if (listener.equals("TableListener")) {
268             if (component instanceof Table) {
269                 Table table = (Table) component;
270                 table.addTableModelListener(sentinel);
271                 return;
272             }
273         }
274     }
275
276     /** Build component from scratch with its node description.
277      *
278      * @param node : description of component
279      * @return the built component
280      */
281     @SuppressWarnings("unchecked")
282     public final Component buildPeerFor(final Node node) {
283         return ComponentFactory.getComponent(node);
284     }
285 }
286
287