View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans.
5    *
6    * CosyBeans 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 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.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui.adapters;
21  
22  import java.awt.Component;
23  import java.awt.Dimension;
24  import java.awt.GridBagConstraints;
25  import java.awt.GridBagLayout;
26  import java.awt.Insets;
27  import java.awt.event.ActionEvent;
28  import java.awt.event.ActionListener;
29  import java.awt.event.MouseAdapter;
30  import java.awt.event.MouseEvent;
31  import java.beans.Customizer;
32  import java.beans.PropertyChangeEvent;
33  import java.beans.PropertyChangeListener;
34  import java.beans.PropertyVetoException;
35  
36  import javax.swing.DefaultComboBoxModel;
37  import javax.swing.DefaultListCellRenderer;
38  import javax.swing.DefaultListModel;
39  import javax.swing.JButton;
40  import javax.swing.JComboBox;
41  import javax.swing.JFrame;
42  import javax.swing.JLabel;
43  import javax.swing.JList;
44  import javax.swing.JPanel;
45  import javax.swing.JScrollPane;
46  import javax.swing.ScrollPaneConstants;
47  
48  import com.cosylab.gui.components.util.IconHelper;
49  import com.cosylab.gui.displayers.ConvertibleDisplayer;
50  
51  /**
52   * <code>ConverterCustomizer</code> is an implementation of the 
53   * <code>java.beans.Customizer</code> which enables selection and 
54   * setting of a ConverterChain. Customizer can be used together with
55   * <code>{@link ConvertibleDisplayer}</code>.
56   * 
57   * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a>
58   * @version $Id: ConverterCustomizer.java,v 1.21 2008-04-22 12:31:02 jbobnar Exp $
59   *
60   */
61  public class ConverterCustomizer extends JPanel implements Customizer {
62  	private static final long serialVersionUID = 1L;
63  
64  	private class AvailableCellRenderer extends DefaultListCellRenderer {
65  
66  		private static final long serialVersionUID = 1L;
67  
68  		public AvailableCellRenderer() {
69  			setOpaque(true);
70  		}
71  
72  		public Component getListCellRendererComponent(JList list, Object value,
73  				int index, boolean isSelected, boolean cellHasFocus) {
74  
75  			String name = ((Class) value).getSimpleName();
76  			if (name != null && name.endsWith("Converter")) name = name.substring(0, name.lastIndexOf("Converter"));
77  			
78  			return super.getListCellRendererComponent(list,name,index,isSelected,cellHasFocus);
79  		}
80  
81  	}
82  
83  	private JComboBox functionsList = null;
84  	
85  	private JList selectedFunctionsList = null;
86  
87  	private DefaultComboBoxModel functionModel;
88  	
89  	private DefaultListModel selectedModel;
90  
91  	private JPanel buttonPanel = null;
92  
93  	private JLabel functionsLabel = null;
94  
95  	private JButton addButton = null, removeButton = null;
96  
97  	private JButton editButton = null;
98  
99  	private ConvertibleDisplayer convertibleDisplayer = null;
100 
101 	protected Converter converter = null;
102 	
103 	private JScrollPane scrollPane = null;
104 
105 	private Class<Converter>[] converters;
106 
107 	/**
108 	 * Constructs a new ConverterCustomizer which offers Identity, 
109 	 * Multiplier, Linear, Potential, Exponential, and logarithmic converter.
110 	 *
111 	 */
112 	@SuppressWarnings({ "unchecked" })
113 	public ConverterCustomizer() {
114 		this(new Class[] { IdentityConverter.class, MultiplierConverter.class, LinearConverter.class,
115 				PotentialConverter.class, ExponentialConverter.class, LogarithmicConverter.class
116 				});
117 	}
118 
119 	/**
120 	 * Constructs a new ConverterCustomizer which offers only the specified converters.
121 	 * 
122 	 * @param converters
123 	 */
124 	@SuppressWarnings("unchecked")
125 	public ConverterCustomizer(Class<? extends Converter>[] converters) {
126 		this.converters = (Class<Converter>[]) converters;
127 		initialize();
128 	}
129 
130 	private void initialize() {
131 		setLayout(new GridBagLayout());
132 		add(getFunctionsList(), new GridBagConstraints(0, 0, 2, 1, 1, 0, GridBagConstraints.NORTHEAST, GridBagConstraints.HORIZONTAL, new Insets(7, 7, 2, 4), 1, 1));
133 		add(getAddButton(), new GridBagConstraints(2, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(7, 2, 2, 7), 0, 0));
134 		add(getScrollPane(), new GridBagConstraints(0, 1, 3, 1, 1, 1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(4, 7, 4, 7), 1, 1));
135 		add(getButtonPanel(), new GridBagConstraints(0, 2, 3, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(2, 7, 4, 7), 0, 0));
136 		add(getFunctionsLabel(), new GridBagConstraints(0, 3, 3, 1, 1, 0, GridBagConstraints.SOUTHWEST, GridBagConstraints.HORIZONTAL, new Insets(4, 7, 7, 7), 0, 0));
137 	}
138 
139 	/*
140 	 * (non-Javadoc)
141 	 * 
142 	 * @see java.beans.Customizer#setObject(java.lang.Object)
143 	 */
144 	public void setObject(Object bean) {
145 		if (!(bean instanceof ConvertibleDisplayer)) {
146 			throw new IllegalArgumentException("Bean " + bean.getClass()
147 					+ " must implement ConvertibleDisplayer!");
148 		}
149 		convertibleDisplayer = (ConvertibleDisplayer) bean;
150 
151 		if (convertibleDisplayer.getConverter() != null) {
152 			setConverter(convertibleDisplayer.getConverter());
153 		}
154 
155 		convertibleDisplayer.addPropertyChangeListener(
156 				ConvertibleDisplayer.CONVERTER_PROPERTY, new PropertyChangeListener() {
157 					public void propertyChange(PropertyChangeEvent evt) {
158 						if (!ConvertibleDisplayer.CONVERTER_PROPERTY.equals(evt
159 								.getPropertyName()))
160 							return;
161 						setConverter(convertibleDisplayer.getConverter());
162 					}
163 				});
164 	}
165 
166 	private void applyUsedConverters(Converter... converters) {
167 		selectedModel.clear();
168 		for (Converter c : converters) {
169 			if (c != null)
170 				selectedModel.addElement(c);
171 		}
172 
173 		applySelectedFunctions();
174 	}
175 
176 	private JPanel getButtonPanel() {
177 		if (buttonPanel == null) {
178 			buttonPanel = new JPanel();
179 			buttonPanel.setLayout(new GridBagLayout());
180 //			buttonPanel.setSize(new Dimension(81, 72));
181 			buttonPanel.add(getEditButton(), new GridBagConstraints(0, 0, 1, 1,
182 					1, 1, GridBagConstraints.WEST, GridBagConstraints.NONE,
183 					new Insets(0, 0, 0, 5), 0, 0));
184 			buttonPanel.add(getRemoveButton(), new GridBagConstraints(1, 0, 1,
185 					1, 1, 1, GridBagConstraints.EAST,
186 					GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));
187 			buttonPanel.setMaximumSize(new Dimension(100, 100));
188 			
189 		}
190 		return buttonPanel;
191 	}
192 	
193 	private JButton getEditButton() {
194 		if (editButton == null) {
195 			editButton = new JButton("Edit");
196 			editButton.setEnabled(false);
197 			editButton.setToolTipText("Edit function");
198 /*			editButton.setMinimumSize(new Dimension(100, 22));
199 			editButton.setMaximumSize(new Dimension(200, 22));
200 			editButton.setPreferredSize(new Dimension(100, 22));
201 */			editButton.addActionListener(new ActionListener() {
202 				public void actionPerformed(ActionEvent e) {
203 					int i = selectedFunctionsList.getSelectedIndex();
204 					if (i >= 0)
205 						edit((Converter) selectedModel.get(i));
206 					refreshSelectedFunctionsList();
207 					applyFunctionsText();
208 				}
209 			});
210 		}
211 
212 		return editButton;
213 	}
214 
215 	private JButton getAddButton() {
216 		if (addButton == null) {
217 			addButton = new JButton("Add", IconHelper.createIcon("icons/navigation/Plus16.gif"));
218 			addButton.setToolTipText("Add function");
219 			addButton.setMinimumSize(new Dimension(100, 22));
220 			addButton.setMaximumSize(new Dimension(200, 22));
221 			addButton.setPreferredSize(new Dimension(100, 22));
222 			addButton.addActionListener(new ActionListener() {
223 				public void actionPerformed(ActionEvent e) {
224 					addToSelectedList();
225 				}
226 			});
227 		}
228 
229 		return addButton;
230 	}
231 
232 	/**
233 	 * Returns the converter.
234 	 * 
235 	 * @return
236 	 */
237 	public Converter getConverter() {
238 		return converter;
239 	}
240 
241 	/**
242 	 * Sets converter to this customizer.
243 	 * 
244 	 * @param c
245 	 */
246 	public void setConverter(Converter c) {
247 		if (converter != null && converter.equals(c) || (converter == null && c == null))
248 			return;
249 		Converter old = converter;
250 		converter = c;
251 		if (c instanceof ConverterChain) {
252 			applyUsedConverters(((ConverterChain) c).getConverters());
253 		} else {
254 			applyUsedConverters(c);
255 		}
256 
257 		firePropertyChange(ConvertibleDisplayer.CONVERTER_PROPERTY, old, converter);
258 	}
259 
260 	private JLabel getFunctionsLabel() {
261 		if (functionsLabel == null) {
262 			functionsLabel = new JLabel();
263 			Dimension d = new Dimension(200, 21);
264 			functionsLabel.setMaximumSize(d);
265 			functionsLabel.setPreferredSize(d);
266 			functionsLabel.setMinimumSize(new Dimension(100, 10));
267 		}
268 
269 		return functionsLabel;
270 	}
271 
272 	private JComboBox getFunctionsList() {
273 		if (functionsList == null) {
274 			functionsList = new JComboBox();
275 			functionModel = new DefaultComboBoxModel();
276 			for (Class c : converters) {
277 				functionModel.addElement(c);
278 			}
279 			functionsList.setModel(functionModel);
280 			functionsList.setRenderer(new AvailableCellRenderer());
281 			functionsList.setMinimumSize(new Dimension(100, 21));
282 			functionsList.setMaximumSize(new Dimension(200, 21));
283 			functionsList.setPreferredSize(new Dimension(100, 21));
284 			functionsList.setSelectedIndex(0);
285 			functionsList.addMouseListener(new MouseAdapter() {
286 				public void mouseClicked(MouseEvent e) {
287 					if (e.getClickCount() == 2 && functionsList.isEnabled()) {
288 						addToSelectedList();
289 					}
290 				}
291 			});
292 		}
293 
294 		return functionsList;
295 	}
296 
297 	private JButton getRemoveButton() {
298 		if (removeButton == null) {
299 			removeButton = new JButton("Remove", IconHelper.createIcon("icons/navigation/Minus16.gif"));
300 			removeButton.setToolTipText("Remove function");
301 /*			removeButton.setMinimumSize(new Dimension(100, 22));
302 			removeButton.setMaximumSize(new Dimension(200, 22));
303 			removeButton.setPreferredSize(new Dimension(100, 22));
304 */			removeButton.addActionListener(new ActionListener() {
305 				public void actionPerformed(ActionEvent e) {
306 					removeFromSelectedList();
307 				}
308 			});
309 		}
310 
311 		return removeButton;
312 	}
313 
314 	private void removeFromSelectedList() {
315 		Object[] selected = getSelectedFunctionsList().getSelectedValues();
316 		if (selected != null) {
317 			for (Object o : selected) {
318 				selectedModel.removeElement(o);
319 			}
320 		}
321 
322 		applySelectedFunctions();
323 	}
324 
325 	private void addToSelectedList() {
326 		Class conv = (Class) getFunctionsList().getSelectedItem();
327 		if (conv == null)
328 			return;
329 		Converter c = getConverterFromClass(conv);
330 		
331 		if (c != null) {
332 			if (!checkEditable(c) || edit(c)) {
333 				selectedModel.addElement(c);
334 			}
335 		}
336 		applySelectedFunctions();
337 
338 	}
339 
340 	private void applySelectedFunctions() {
341 		int k = selectedModel.size();
342 		if (k == 0) {
343 			converter = null;
344 		} else if (k == 1) {
345 			converter = (Converter) selectedModel.get(0);
346 		} else {
347 			Converter[] c = new Converter[k];
348 			for (int i = 0; i < k; i++) {
349 				c[i] = (Converter) selectedModel.get(i);
350 			}
351 			try {
352 				converter = new ConverterChain(c);
353 			} catch (PropertyVetoException e) {
354 				e.printStackTrace();
355 			}
356 		}
357 
358 		applyFunctionsText();
359 		try {
360 			if (this.convertibleDisplayer != null)
361 				convertibleDisplayer.setConverter(converter);
362 		} catch (PropertyVetoException e) {
363 			e.printStackTrace();
364 		}
365 
366 		firePropertyChange(ConvertibleDisplayer.CONVERTER_PROPERTY, null, converter);
367 	}
368 
369 	private void applyFunctionsText() {
370 		String s = "INPUT";
371 		int k = selectedModel.size();
372 		for (int i = 0; i < k; i++) {
373 			s = composeFunction((Converter) selectedModel.get(i), s);
374 		}
375 		
376 		s = "OUTPUT = " + s;
377 
378 		getFunctionsLabel().setText(s);
379 		getFunctionsLabel().setToolTipText(s);
380 	}
381 
382 	@SuppressWarnings("unchecked")
383 	private Converter getConverterFromClass(Class conv) {
384 		try {
385 			return (Converter) conv.getConstructor().newInstance();
386 		} catch (Exception e) {
387 			e.printStackTrace();
388 		}
389 		return null;
390 	}
391 
392 	private JList getSelectedFunctionsList() {
393 		if (selectedFunctionsList == null) {
394 			selectedFunctionsList = new JList();
395 			selectedModel = new DefaultListModel();
396 			selectedFunctionsList.setModel(selectedModel);
397 			selectedFunctionsList.addMouseListener(new MouseAdapter() {
398 				public void mouseClicked(MouseEvent e) {
399 					if (e.getClickCount() == 2
400 							&& selectedFunctionsList.isEnabled()) {
401 						edit((Converter) selectedModel.get(getSelectedFunctionsList().getSelectedIndex()));
402 					} else if (getSelectedFunctionsList().getSelectedIndex() != -1){
403 						if (checkEditable((Converter) selectedModel.get(getSelectedFunctionsList().getSelectedIndex())))
404 							getEditButton().setEnabled(true);
405 						else
406 							getEditButton().setEnabled(false);
407 					}
408 				}
409 			});
410 		}
411 		
412 		return selectedFunctionsList;
413 	}
414 	
415 	private JScrollPane getScrollPane(){
416 		if (scrollPane == null) {
417 			scrollPane = new JScrollPane(getSelectedFunctionsList(), ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
418 			scrollPane.setPreferredSize(new Dimension(200,200));
419 		}
420 
421 		return scrollPane;
422 	}
423 	
424 	
425 	/**
426 	 * Refreshes list of selected functions.
427 	 */
428 	public void refreshSelectedFunctionsList(){
429 		getSelectedFunctionsList().repaint();
430 		applyFunctionsText();
431 	}
432 
433 	/**
434 	 * Composes the function string displayed in the GUI.
435 	 * 
436 	 * @param c
437 	 * @param input
438 	 * @return
439 	 */
440 	protected String composeFunction(Converter c, String input) {
441 		return ConverterUtilities.composeFunctionString(c, input);
442 	}
443 
444 	/**
445 	 * Check if converter is editable.
446 	 * 
447 	 * @param converter
448 	 */
449 	protected boolean checkEditable(Converter converter) {
450 		return ConverterUtilities.isEditable(converter);
451 	}
452 	
453 	/**
454 	 * Displays editor dialog to edit converter. 
455 	 * 
456 	 * @param converter
457 	 * @return returns true if editing was confirmed, false if it was cancelled 
458 	 */
459 	protected boolean edit(Converter converter) {
460 		return ConverterUtilities.showEditorDialog(this, converter);
461 	}
462 
463 	public static void main(String[] args) {
464 		JFrame f = new JFrame("ConverterCustomizer");
465 		f.add(new ConverterCustomizer());
466 		f.pack();
467 		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
468 		f.setVisible(true);
469 	}
470 }