View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans-Common.
5    *
6    * CosyBeans-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * CosyBeans-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with CosyBeans-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui.components.customizer;
21  
22  import java.awt.Color;
23  import java.beans.Customizer;
24  import java.beans.PropertyChangeEvent;
25  import java.beans.PropertyChangeListener;
26  import java.lang.reflect.InvocationTargetException;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  
32  import javax.swing.JComponent;
33  import javax.swing.JSplitPane;
34  import javax.swing.border.LineBorder;
35  import javax.swing.table.TableModel;
36  
37  import com.cosylab.gui.components.introspection.BeanPropertiesTableModel;
38  import com.cosylab.gui.components.introspection.DefaultPropertiesTable;
39  import com.cosylab.util.Debug;
40  import com.cosylab.util.ObjectList;
41  
42  /**
43   * A bridge between java.bean.Customizer and Cosy style Customizers.
44   * 
45   * @author Alen Vrecko
46   * 
47   */
48  public class CustomizerEditor extends JSplitPane implements Customizer, Editor {
49  
50  	private static final long serialVersionUID = 1L;
51  
52  	private DefaultPropertiesTable defaultPropertiesTable = null;
53  
54  	private HashMap<String, ArrayList> customEditors = null;
55  
56  	private HashMap defaultPropertiesTableModels = null;
57  
58  	private Object displayer = null;
59  
60  	private ObjectList activeEditors = null;
61  
62  	/**
63  	 * Cunstructs an empty editor.
64  	 */
65  	public CustomizerEditor() {
66  
67  		defaultPropertiesTableModels = new HashMap();
68  		customEditors = new HashMap<String, ArrayList>();
69  		activeEditors = new ObjectList(Editor.class);
70  		defaultPropertiesTable = new DefaultPropertiesTable();
71  		defaultPropertiesTable.setBorder(new LineBorder(Color.BLACK));
72  
73  		defaultPropertiesTable
74  				.addPropertyChangeListener(new PropertyChangeListener() {
75  
76  					public void propertyChange(PropertyChangeEvent evt) {
77  						firePropertyChange(evt.getPropertyName(), null, evt
78  								.getNewValue());
79  						applyTableProperties();
80  
81  					}
82  				});
83  	}
84  
85  	/**
86  	 * <p>
87  	 * Should return names of properties of a dispalyer to be edited within the
88  	 * sepcified aspect through generic proeprty tables. This empty
89  	 * implementation however returns null.
90  	 * </p>
91  	 * 
92  	 * <p>
93  	 * Subclasses should override this method to return arrays of
94  	 * <code>String</code> names of properties to be edited via property
95  	 * tables in the specified aspect.
96  	 * </p>
97  	 * 
98  	 * <p>
99  	 * The Strings returned should specify names of qualified <i>JavaBean</i>
100 	 * properties with conventional getter and setter methods.
101 	 * </p>
102 	 * 
103 	 * <p>
104 	 * If a custom editor has been specified for the aspect the result of theis
105 	 * method is ignored as the custom editor is used for editing the aspect
106 	 * instead of generic property table.
107 	 * </p>
108 	 * 
109 	 * @param aspect
110 	 *            the aspect of the displayer to be edited with generic proeprty
111 	 *            table
112 	 * 
113 	 * @return array of names of properties to be edited with the proeprty table
114 	 *         in the specified aspect
115 	 */
116 	public String[] getDefaultProperties(String aspect) {
117 		Debug.out(aspect);
118 
119 		return null;
120 	}
121 
122 	/**
123 	 * Returns the editor component for the selected aspect and displayer. If a
124 	 * custom editor exists for the selected aspect, it is returned. Otherwise a
125 	 * property table is returned containing the selected proeprties of the
126 	 * aspect as returned by <code>#getDefaultProperties(String)</code>
127 	 * method.
128 	 * 
129 	 * @param displayer
130 	 *            the component for which the editor should be returned
131 	 * @param aspect
132 	 *            the specific editing aspect for which the editor should be
133 	 *            returned
134 	 * 
135 	 * @return the editor component for the selected aspect and displayer.
136 	 * 
137 	 * @throws IllegalArgumentException
138 	 *             if no custom editor is found for the aspect and
139 	 *             getDefaultProperties(String) returns null for the aspect.
140 	 * 
141 	 * @see com.cosylab.gui.components.customizer.Editor#getEditorComponent(Object,
142 	 *      String)
143 	 */
144 	public JComponent getEditorComponent(Object displayer, String aspect)
145 			throws IllegalArgumentException {
146 		Editor ed;
147 		Iterator it = activeEditors.iterator();
148 
149 		while (it.hasNext()) {
150 			if ((ed = (Editor) it.next()).canEdit(displayer, aspect)) {
151 				return ed.getEditorComponent(displayer, aspect);
152 			}
153 		}
154 
155 		ArrayList l = (ArrayList) customEditors.get(aspect);
156 
157 		if (l != null) {
158 			it = l.iterator();
159 
160 			while (it.hasNext()) {
161 				if ((ed = (Editor) it.next()).canEdit(displayer, aspect)) {
162 					activeEditors.add(ed);
163 
164 					return ed.getEditorComponent(displayer, aspect);
165 				}
166 			}
167 		}
168 
169 		initializeDefaultPropertiesTable(displayer, aspect);
170 
171 		return defaultPropertiesTable;
172 	}
173 
174 	/**
175 	 * Overrides the current editor for the specified aspect with the editor
176 	 * specified. This method can be used to replace generic property tables as
177 	 * editors for the aspect specified.
178 	 * 
179 	 * @param aspect
180 	 *            for which to use the editor specified
181 	 * @param editor
182 	 *            the editor to replace the current editor of the aspect
183 	 *            specified
184 	 */
185 	public void addCustomEditor(String aspect, Editor editor) {
186 		ArrayList<Editor> l = (ArrayList<Editor>) customEditors.get(aspect);
187 
188 		if (l == null) {
189 			l = new ArrayList<Editor>();
190 			customEditors.put(aspect, l);
191 		}
192 
193 		l.add(editor);
194 	}
195 
196 	/**
197 	 * Applies all the changes done to the custom editors as well as the
198 	 * property tables.
199 	 * 
200 	 * @see com.cosylab.gui.components.customizer.Editor#applySettings()
201 	 */
202 	public void applySettings() {
203 		applyTableProperties();
204 
205 		Editor[] eds = getActiveEditors();
206 
207 		for (int i = 0; i < eds.length; i++) {
208 			eds[i].applySettings();
209 		}
210 	}
211 
212 	/**
213 	 * Inspecting the <code>ASPECTS</code> and all the custom editors, this
214 	 * method determines, wether the specified aspect of the dispalyer can be
215 	 * edited by this editor.
216 	 * 
217 	 * @param displayer
218 	 *            Object to be edited (is ignored and can be <code>null</code>)
219 	 * @param aspect
220 	 *            String name of aspect to be edited.
221 	 * 
222 	 * @return <code>true</code> if the specifed aspect of the displayer can
223 	 *         be edited, <code>false</code> otherwise.
224 	 * 
225 	 * @see com.cosylab.gui.components.customizer.Editor#canEdit(Object, String)
226 	 */
227 	public boolean canEdit(Object displayer, String aspect) {
228 		ArrayList l = (ArrayList) customEditors.get(aspect);
229 
230 		if (l != null) {
231 			Iterator it = l.iterator();
232 
233 			while (it.hasNext()) {
234 				if (((Editor) it.next()).canEdit(displayer, aspect)) {
235 					return true;
236 				}
237 			}
238 		}
239 
240 		return (Arrays.binarySearch(ASPECTS, aspect) >= 0 && getDefaultProperties(aspect) != null);
241 	}
242 
243 	/**
244 	 * Reverts all the changes done to the custom editors as well as the
245 	 * property tables.
246 	 * 
247 	 * @see com.cosylab.gui.components.customizer.Editor#revertSettings()
248 	 */
249 	public void revertSettings() {
250 		Editor[] eds = getActiveEditors();
251 
252 		for (int i = 0; i < eds.length; i++) {
253 			eds[i].revertSettings();
254 		}
255 	}
256 
257 	/**
258 	 * Stops editing with all custom editors as well as property tables.
259 	 * 
260 	 * @see com.cosylab.gui.components.customizer.Editor#stopEditing()
261 	 */
262 	public void stopEditing() {
263 		defaultPropertiesTableModels.clear();
264 
265 		Editor[] eds = getActiveEditors();
266 
267 		for (int i = 0; i < eds.length; i++) {
268 			eds[i].stopEditing();
269 		}
270 
271 		activeEditors.clear();
272 	}
273 
274 	/*
275 	 * Applies the changes done to the property tables.
276 	 */
277 	protected void applyTableProperties() {
278 		if (displayer == null) {
279 			return;
280 		}
281 
282 		Object[] modelList = defaultPropertiesTableModels.values().toArray();
283 
284 		for (int i = 0; i < modelList.length; i++) {
285 			BeanPropertiesTableModel model = (BeanPropertiesTableModel) modelList[i];
286 
287 			try {
288 				model.applyProperties();
289 			} catch (Exception e) {
290 				e.printStackTrace();
291 			}
292 		}
293 	}
294 
295 	/*
296 	 * Initializes the proeprty table for the selected displayer and aspect.
297 	 */
298 	protected void initializeDefaultPropertiesTable(Object displayer,
299 			String aspect) throws IllegalArgumentException {
300 		this.displayer = displayer;
301 
302 		if (defaultPropertiesTableModels.containsKey(aspect)) {
303 			defaultPropertiesTable
304 					.setModel((TableModel) defaultPropertiesTableModels
305 							.get(aspect));
306 		} else {
307 			String[] names = getDefaultProperties(aspect);
308 
309 			BeanPropertiesTableModel model = new BeanPropertiesTableModel(
310 					displayer, names);
311 			defaultPropertiesTableModels.put(aspect, model);
312 			defaultPropertiesTable.setModel(model);
313 		}
314 	}
315 
316 	/*
317 	 * Returns all the custom editors that have been supplied.
318 	 */
319 	private Editor[] getActiveEditors() {
320 		return (Editor[]) activeEditors.toArray();
321 	}
322 
323 	/*
324 	 * (non-Javadoc)
325 	 * @see java.beans.Customizer#setObject(java.lang.Object)
326 	 */
327 	public void setObject(Object bean) {
328 		// reflection work since there is no other alternative.
329 
330 		try {
331 			Object o = bean.getClass().getMethod("getCustomizer", null).invoke(
332 					bean, null);
333 			if (o != null) {
334 				customizer = (com.cosylab.gui.components.customizer.Customizer) o;
335 //				JTree aspectTree = customizer.getAspectTree();
336 //
337 //				aspectTree.getSelectionModel().addTreeSelectionListener(
338 //						new TreeSelectionListener() {
339 //							public void valueChanged(TreeSelectionEvent e) {
340 //								Object[] aspectPath = ((DefaultMutableTreeNode) e
341 //										.getNewLeadSelectionPath()
342 //										.getLastPathComponent())
343 //										.getUserObjectPath();
344 //								String aspect = "";
345 //
346 //								for (int i = 1; i < aspectPath.length; i++) {
347 //									aspect += "/" + (String) aspectPath[i];
348 //								}
349 //
350 //								setRightComponent(customizer
351 //										.getAspectInPanel(aspect.substring(1)));
352 //
353 //							}
354 //						});
355 //
356 //				setLeftComponent(aspectTree);
357 //
358 //				String aspect = aspectTree.getModel().getChild(
359 //						aspectTree.getModel().getRoot(), 0).toString();
360 //
361 //				setRightComponent(new JScrollPane(customizer
362 //						.getAspectInPanel(aspect)));
363 //
364 //				aspectTree.setSelectionRow(0);
365 				
366 				setLeftComponent(customizer.getAspectTree());
367 				setRightComponent(customizer.getAspectPanel());
368 
369 			}
370 		} catch (SecurityException e) {
371 
372 		} catch (NoSuchMethodException e) {
373 
374 		} catch (IllegalArgumentException e) {
375 
376 		} catch (IllegalAccessException e) {
377 
378 		} catch (InvocationTargetException e) {
379 
380 		}
381 
382 	}
383 
384 	private com.cosylab.gui.components.customizer.Customizer customizer;
385 
386 }