View Javadoc

1   /**
2    * 
3    */
4   package org.ximtec.igesture.tool.view.admin.panel;
5   
6   import java.awt.BorderLayout;
7   import java.awt.Color;
8   import java.awt.Dimension;
9   import java.awt.GridBagConstraints;
10  import java.awt.GridBagLayout;
11  import java.awt.event.ActionEvent;
12  import java.awt.event.ActionListener;
13  import java.awt.event.ItemEvent;
14  import java.awt.event.ItemListener;
15  import java.awt.event.KeyEvent;
16  import java.net.URL;
17  import java.util.ArrayList;
18  import java.util.HashMap;
19  import java.util.HashSet;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  import java.util.concurrent.ConcurrentSkipListMap;
25  import java.util.logging.Level;
26  import java.util.logging.Logger;
27  
28  import javax.swing.Action;
29  import javax.swing.BorderFactory;
30  import javax.swing.DefaultListModel;
31  import javax.swing.JButton;
32  import javax.swing.JCheckBox;
33  import javax.swing.JComboBox;
34  import javax.swing.JLabel;
35  import javax.swing.JList;
36  import javax.swing.JOptionPane;
37  import javax.swing.JPanel;
38  import javax.swing.JScrollPane;
39  import javax.swing.JSpinner;
40  import javax.swing.JTextField;
41  import javax.swing.ListSelectionModel;
42  import javax.swing.SpinnerNumberModel;
43  
44  import org.sigtec.graphix.GridBagLayouter;
45  import org.w3c.dom.Node;
46  import org.w3c.dom.NodeList;
47  import org.ximtec.igesture.core.GestureClass;
48  import org.ximtec.igesture.core.GestureSet;
49  import org.ximtec.igesture.core.composite.CompositeDescriptor;
50  import org.ximtec.igesture.core.composite.Constraint;
51  import org.ximtec.igesture.core.composite.DefaultConstraint;
52  import org.ximtec.igesture.core.composite.DefaultConstraintEntry;
53  import org.ximtec.igesture.io.AbstractGestureDevice;
54  import org.ximtec.igesture.io.DeviceManagerListener;
55  import org.ximtec.igesture.io.IDeviceManager;
56  import org.ximtec.igesture.tool.GestureConstants;
57  import org.ximtec.igesture.tool.binding.ConstraintTextFieldBinding;
58  import org.ximtec.igesture.tool.core.Controller;
59  import org.ximtec.igesture.tool.service.DeviceManagerService;
60  import org.ximtec.igesture.tool.util.Formatter;
61  import org.ximtec.igesture.tool.view.MainController;
62  import org.ximtec.igesture.tool.view.MainModel;
63  import org.ximtec.igesture.tool.view.admin.action.AddGestureClassToConstraintAction;
64  import org.ximtec.igesture.tool.view.admin.action.RemoveAllGestureClassesFromConstraintAction;
65  import org.ximtec.igesture.tool.view.admin.action.RemoveGestureClassFromConstraintAction;
66  import org.ximtec.igesture.util.XMLParser;
67  
68  import com.jgoodies.forms.builder.DefaultFormBuilder;
69  import com.jgoodies.forms.layout.FormLayout;
70  
71  /**
72   * @author Bjorn Puype, bpuype@gmail.com
73   *
74   */
75  public class CompositeDescriptorPanel extends DefaultDescriptorPanel<CompositeDescriptor> implements DeviceManagerListener{
76  
77  	private static final Logger LOGGER = Logger.getLogger(CompositeDescriptorPanel.class.getName());
78  	
79  	private static final int INPUTAREA_SIZE = 200;
80  	
81  	private static final String SELECT_SET = "Select a set";
82  	private static final String SELECT_GESTURE = "Select a gesture";
83  	private static final String SELECT_DEVICE_TYPE = "Select a device type";
84  	
85  	private JComboBox cmbSets, cmbGestures, cmbDevices;
86  	private JCheckBox chkUser, chkDevices;
87  	private JSpinner spinUser;
88  	private JList devicesList, gestureList;
89  	private JButton btnRemove, btnClear, btnAdd;
90  	
91  	private IDeviceManager deviceManager;
92  	
93  	private Map<String, String> deviceMapping;
94  	
95  	private Constraint constraint;
96  	
97  	public CompositeDescriptorPanel(Controller controller, CompositeDescriptor descriptor) {
98  		super(controller, descriptor);
99  		
100 		deviceManager = controller.getLocator().getService(DeviceManagerService.IDENTIFIER, IDeviceManager.class);
101 		deviceManager.addDeviceManagerListener(this);
102 		constraint = descriptor.getConstraint();
103 		((DefaultConstraint)constraint).addPropertyChangeListener(controller.getLocator().getService(MainController.IDENTIFIER,MainController.class));
104 		init();
105 	}
106 
107 	private void init() 
108 	{	
109 		initTitle();		
110 		initParameterSection();
111 	    initInputSection();
112 	}
113 	
114 	/**
115 	 * Create the parameter section to configure the constraint parameters
116 	 */
117 	private void initParameterSection()
118 	{		
119 		JPanel panel = new JPanel();
120 										
121 		FormLayout layout = new FormLayout(
122 		        "100dlu, 4dlu, 100dlu",
123 		        "pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref");
124 
125 	    DefaultFormBuilder builder = new DefaultFormBuilder(layout);
126 	    builder.setDefaultDialogBorder();
127 
128 	    builder.append(getComponentFactory().createLabel(GestureConstants.CONFIGURATION_PANEL_PARAMETERS));
129 	    builder.nextLine(2);
130 	    
131 	    // get the parameters from the constraint and add them to the panel
132 	    Map<String, String> parameter = getDescriptor().getConstraint().getParameters();
133 	    ConcurrentSkipListMap<String, String> map = new ConcurrentSkipListMap<String,String>();
134 	    map.putAll(parameter);
135 	    for (String parameterName : parameter.keySet()) {
136 	      builder.append(new JLabel(parameterName));
137 	      JTextField paramTextField = new JTextField();
138 	      new ConstraintTextFieldBinding(paramTextField, (DefaultConstraint)constraint, parameterName);
139 	      builder.append(paramTextField);
140 	      builder.nextLine(2);
141 	    }
142 	    
143 	    JPanel paramPanel = builder.getPanel();
144 	    paramPanel.setOpaque(true);
145 	    paramPanel.setBackground(Color.WHITE);
146 
147 		panel.add(paramPanel,BorderLayout.CENTER);
148 
149 	    panel.setOpaque(true);
150 	    panel.setAutoscrolls(true);
151 	    setContent(panel);
152 	}
153 	
154 	/**
155 	 * Creates the input area to add gestures to the constraint.
156 	 * @param manager 
157 	 */
158 	private void initInputSection()
159 	{		
160 		/* create device mapping */
161 		deviceMapping = new HashMap<String, String>();
162 		XMLParser parser = new XMLParser(){
163 
164 			@Override
165 			public void execute(ArrayList<NodeList> nodeLists) {
166 				String name = ((Node)nodeLists.get(0).item(0)).getNodeValue();
167 				String clazz = ((Node)nodeLists.get(1).item(0)).getNodeValue();
168 				deviceMapping.put(name, clazz);
169 			}
170 			
171 		};
172 		ArrayList<String> nodes = new ArrayList<String>();
173 		nodes.add("name");
174 		nodes.add("class");
175 		try {
176 			URL path = CompositeDescriptorPanel.class.getClassLoader().getResource("config.xml");
177 			parser.parse(path.getFile(),"device", nodes);
178 		} catch (Exception e) {
179 			LOGGER.log(Level.SEVERE,"Could not parse configuration file (config.xml - devices).",e);
180 		};
181 		
182 		//*** UI ***//
183 		/* choose gesture set */
184 		JLabel lblSets = getComponentFactory().createLabel(GestureConstants.COMPOSITE_DESCRIPTOR_GESTURE_SETS);
185 		cmbSets = new JComboBox(getController().getLocator().getService(MainModel.IDENTIFIER, MainModel.class)
186 		        .getGestureSets().toArray());
187 		cmbSets.insertItemAt(SELECT_SET, 0);
188 		cmbSets.setSelectedIndex(0);
189 		/* choose gesture from the set */
190 		JLabel lblGestures = getComponentFactory().createLabel(GestureConstants.COMPOSITE_DESCRIPTOR_GESTURE_CLASSES);
191 		cmbGestures = new JComboBox(new Object[]{SELECT_GESTURE});
192 		cmbGestures.setEnabled(false);
193 		
194 		cmbSets.addActionListener(new ActionListener(){
195 
196 			@Override
197 			public void actionPerformed(ActionEvent e) {
198 				// if a gesture set is selected, show the gestures of that set
199 				
200 				JComboBox source = (JComboBox) e.getSource();
201 				
202 				cmbGestures.removeAllItems();
203 				cmbGestures.addItem(SELECT_GESTURE);
204 				
205 				if(source.getSelectedIndex() == 0)
206 				{
207 					cmbGestures.setEnabled(false);
208 				}
209 				else
210 				{
211 					cmbGestures.setEnabled(true);
212 					GestureSet set = (GestureSet) cmbSets.getSelectedItem();
213 
214 					Object[] items = set.getGestureClasses().toArray();
215 					for (int i = 0; i < items.length; i++) {
216 						cmbGestures.addItem(items[i]);
217 					}
218 				}				
219 			}
220 			
221 		});	
222 		
223 		/* enable and specify user */
224 		chkUser = createCheckBox("User:",KeyEvent.VK_U);
225 		spinUser = new JSpinner(new SpinnerNumberModel(0,0,100,1));
226 		spinUser.setEnabled(false);
227 		chkUser.addItemListener(new ItemListener(){
228 
229 			@Override
230 			public void itemStateChanged(ItemEvent e) {
231 				//enable or disable user selection
232 				JCheckBox source = (JCheckBox) e.getSource();
233 				spinUser.setEnabled(source.isSelected());
234 			}
235 			
236 		});		
237 		
238 		/* enable devices and specify kind and specific devices */
239 		// device type list
240 		chkDevices = createCheckBox("Device(s):", KeyEvent.VK_D);
241 		cmbDevices = new JComboBox(deviceMapping.keySet().toArray());
242 		cmbDevices.setEnabled(false);
243 		cmbDevices.insertItemAt(SELECT_DEVICE_TYPE, 0);
244 		cmbDevices.setSelectedIndex(0);
245 		// specific device list
246 		devicesList = new JList();
247 		DefaultListModel devicesModel = new DefaultListModel();
248 		devicesList.setModel(devicesModel);
249 		devicesList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
250 		devicesList.setEnabled(false);
251 		// make specific device list scrollable
252 		final JScrollPane scrollDevicesList = new JScrollPane(devicesList);
253 		scrollDevicesList.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.GRAY), 
254 				"Devices"));		
255 		scrollDevicesList.setPreferredSize(new Dimension(INPUTAREA_SIZE,INPUTAREA_SIZE/2));
256 		scrollDevicesList.setEnabled(false);
257 		
258 		chkDevices.addItemListener(new ItemListener(){
259 
260 			@Override
261 			public void itemStateChanged(ItemEvent e) {
262 				// enable or disable device type and device selection
263 				JCheckBox source = (JCheckBox)e.getSource();
264 				boolean selected = source.isSelected();
265 				cmbDevices.setEnabled(selected);
266 				scrollDevicesList.setEnabled(selected);
267 				devicesList.setEnabled(selected);
268 			}
269 			
270 		});
271 		cmbDevices.addActionListener(new ActionListener(){
272 
273 			@Override
274 			public void actionPerformed(ActionEvent e) {
275 				JComboBox source = (JComboBox)e.getSource();
276 				//if the selected device type changed, update the device list ...
277 				if(source.getSelectedIndex() != 0)//index 0: "select device type"
278 				{
279 					//... by getting devices of that type from the device manager
280 					DefaultListModel model = new DefaultListModel();
281 					for(Iterator<AbstractGestureDevice<?,?>> iterator = deviceManager.getDevices().iterator(); iterator.hasNext();)
282 					{
283 						AbstractGestureDevice< ?, ?> device = iterator.next();
284 						if(device.getClass().getName().equals(deviceMapping.get(source.getSelectedItem())))//TODO reflection
285 						{
286 							model.addElement(device.getDeviceID());
287 						}
288 					}
289 					devicesList.setModel(model);
290 				}
291 				else
292 					devicesList.setModel(new DefaultListModel());
293 			}
294 			
295 		});
296 		// add components to the content panel
297 		JPanel contentPanel = new JPanel();
298 		contentPanel.setLayout(new GridBagLayout());
299 		GridBagLayouter.addComponent(contentPanel, lblSets, 
300 				0, 0, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
301 		GridBagLayouter.addComponent(contentPanel, cmbSets, 
302 				1, 0, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
303 		GridBagLayouter.addComponent(contentPanel, lblGestures, 
304 				0, 1, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
305 		GridBagLayouter.addComponent(contentPanel, cmbGestures, 
306 				1, 1, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
307 		GridBagLayouter.addComponent(contentPanel, chkUser, 
308 				2, 0, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
309 		GridBagLayouter.addComponent(contentPanel, spinUser, 
310 				3, 0, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
311 		GridBagLayouter.addComponent(contentPanel, chkDevices, 
312 				2, 1, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
313 		GridBagLayouter.addComponent(contentPanel, cmbDevices, 
314 				3, 1, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
315 		GridBagLayouter.addComponent(contentPanel, scrollDevicesList, 
316 				3, 2, 1, 3, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
317 		
318 		btnAdd = createButton(
319 				GestureConstants.ADD_GESTURE_CLASS_TO_CONSTRAINT,
320 				new AddGestureClassToConstraintAction(getController(),this),false);
321 		btnRemove = createButton(
322 				GestureConstants.REMOVE_GESTURE_CLASS_FROM_CONSTRAINT,
323 				new RemoveGestureClassFromConstraintAction(getController(),this),false); 
324 		btnClear = createButton(
325 				GestureConstants.REMOVE_ALL_GESTURE_CLASSES_FROM_CONSTRAINT,
326 				new RemoveAllGestureClassesFromConstraintAction(getController(),this),false);
327 		
328 		cmbGestures.addActionListener(new ActionListener(){
329 
330 			@Override
331 			public void actionPerformed(ActionEvent e) {
332 				JComboBox source = (JComboBox)e.getSource();
333 				// if a valid gesture was selected, allow adding the gesture to the constraint
334 				if(source.getSelectedIndex() == 0)
335 					btnAdd.setEnabled(false);
336 				else
337 					btnAdd.setEnabled(true);
338 				
339 			}
340 			
341 		});
342 		
343 		GridBagLayouter.addComponent(contentPanel, btnAdd, 
344 				1, 2, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH);
345 		GridBagLayouter.addComponent(contentPanel, btnRemove, 
346 				0, 4, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.PAGE_END, GridBagConstraints.HORIZONTAL);
347 		GridBagLayouter.addComponent(contentPanel, btnClear, 
348 				1, 4, 1, 1, 0, 0, 0, 0, 0.5, 0, GridBagConstraints.PAGE_END, GridBagConstraints.HORIZONTAL);
349 			
350 		JPanel basePanel = new JPanel();
351 		basePanel.setLayout(new BorderLayout());
352 		basePanel.add(contentPanel,BorderLayout.CENTER);
353 
354 		// list that displays the composing gestures
355 		gestureList = new JList();
356 		DefaultListModel gesturesModel = new DefaultListModel();
357 		gestureList.setModel(gesturesModel);
358 		gestureList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
359 		
360 		//if the constraint already contains composing gestures, ...
361 		List<DefaultConstraintEntry> entries = getDescriptor().getConstraint().getGestureEntries(); 
362 		if(entries != null && !entries.isEmpty())
363 		{
364 			//...add them to the list ...
365 			for (Iterator iterator = entries.iterator(); iterator.hasNext();) {
366 				DefaultConstraintEntry entry = (DefaultConstraintEntry) iterator.next();
367 				gesturesModel.addElement(entry);
368 			}
369 			
370 			//... and enable removal of the composing gestures
371 			btnRemove.setEnabled(true);
372 			btnClear.setEnabled(true);
373 		}
374 		
375 		// make the gesture list scrollable
376 		JScrollPane scrollResultList = new JScrollPane(gestureList);
377 		scrollResultList.setPreferredSize(new Dimension(basePanel.getWidth(),INPUTAREA_SIZE/2));
378 		basePanel.add(scrollResultList,BorderLayout.SOUTH);
379 		
380 		setBottom(basePanel);
381 	}
382 	
383 	/**
384 	 * Create a button
385 	 * @param key		key to look up in the guiBundle
386 	 * @param action	action to bind to the button
387 	 * @param enabled	true to enable the button
388 	 * @return
389 	 */
390 	private JButton createButton(String key, Action action, boolean enabled)
391 	{
392 		JButton btn = getComponentFactory().createButton(key,action);
393 		btn.setEnabled(enabled);
394 		Formatter.formatButton(btn);
395 		return btn;
396 	}
397 	
398 	/**
399 	 * Create a checkbox
400 	 */
401 	private JCheckBox createCheckBox(String text, int mnemonic)
402 	{
403 		JCheckBox chk = new JCheckBox(text);
404 		chk.setMnemonic(mnemonic);
405 		return chk;
406 	}
407 	
408 	/**
409 	 * Remove the selected gesture class from the list and constraint
410 	 */
411 	public void removeGestureClass()
412 	{
413 		// remove selected gesture class from model/list
414 		DefaultListModel model = (DefaultListModel)gestureList.getModel();
415 		DefaultConstraintEntry entry = (DefaultConstraintEntry) gestureList.getSelectedValue();
416 	    model.removeElement(entry);
417 	    // remove gesture class from constraint
418 	    getDescriptor().getConstraint().removeGestureClass(entry);
419 	    
420 	    // if there are no elements left, disable remove actions
421 	    if(model.getSize() == 0)
422 	    {
423 	    	btnRemove.setEnabled(false);
424 	    	btnClear.setEnabled(false);
425 	    }
426 	}
427 	
428 	/**
429 	 * Remove all gesture classes from the list and constraint
430 	 */
431 	public void removeAllGestureClasses()
432 	{
433 		// remove all gesture classes from model/list
434 		DefaultListModel model = (DefaultListModel)gestureList.getModel();
435 		model.removeAllElements();
436 		// remove all gesture classes from constraint
437 		getDescriptor().getConstraint().removeAllGestureClasses();
438 		
439 		// disable remove actions
440     	btnRemove.setEnabled(false);
441     	btnClear.setEnabled(false);
442 	}
443 	
444 	/**
445 	 * Add a gesture class to the list and constraint
446 	 */
447 	public void addGestureClass()
448 	{		
449 		String gestureClass = null, deviceType = null;
450 		int user = -1;
451 		Set<String> devices = null;
452 		
453 		// get the name of the gesture class (name + uuid)
454 		GestureClass gs = (GestureClass)cmbGestures.getSelectedItem();
455 		gestureClass = gs.getName()+":"+gs.getId();
456 		
457 		// if user specified, get the user
458 		if(chkUser.isSelected())
459 			user = ((Integer)spinUser.getValue()).intValue();
460 		
461 		// if device type specified, get the device type
462 		if(chkDevices.isSelected())
463 		{
464 			deviceType = cmbDevices.getSelectedItem().toString();
465 			// if specific devices were selected, get the IDs
466 			if(!devicesList.isSelectionEmpty())
467 			{
468 				devices = new HashSet<String>();
469 				Object[] values = devicesList.getSelectedValues();
470 				for (int i = 0; i < values.length; i++) {
471 					devices.add(values[i].toString());
472 				}
473 			}
474 		}
475 		try
476 		{
477 			// add the gesture class to the constraint
478 			if(chkUser.isSelected() && chkDevices.isSelected())
479 				getDescriptor().getConstraint().addGestureClass(gestureClass, user, deviceType, devices);
480 			else if(chkUser.isSelected())
481 				getDescriptor().getConstraint().addGestureClass(gestureClass, user);
482 			else if(chkDevices.isSelected())
483 				getDescriptor().getConstraint().addGestureClass(gestureClass, deviceType, devices);
484 			else
485 				getDescriptor().getConstraint().addGestureClass(gestureClass);
486 			
487 			// get the last added gesture and display it in the list
488 			List<DefaultConstraintEntry> entries = getDescriptor().getConstraint().getGestureEntries();
489 			DefaultConstraintEntry entry = entries.get(entries.size()-1);			
490 			DefaultListModel model = (DefaultListModel)gestureList.getModel();
491 		    model.addElement(entry);
492 		}
493 		catch(IllegalArgumentException e)
494 		{
495 			// if an illegal argument was entered by the user, notify the user and allow correction
496 			LOGGER.log(Level.SEVERE,"Constraint Configuration Error.",e);
497 			JOptionPane.showMessageDialog(null, e.getMessage(), "Constraint Configuration Error", JOptionPane.ERROR_MESSAGE);
498 		}
499 	    
500 		// now that data was added, enable removal of gesture classes
501 	    btnClear.setEnabled(true);
502 	    btnRemove.setEnabled(true);
503 	}
504 
505 	/* (non-Javadoc)
506 	 * @see org.ximtec.igesture.io.DeviceManagerListener#updateDeviceManagerListener(int, org.ximtec.igesture.io.AbstractGestureDevice)
507 	 */
508 	@Override
509 	public void updateDeviceManagerListener(int operation, AbstractGestureDevice<?, ?> device) {
510 		//if a valid device type is selected, update the device list if devices of that type are added or removed 
511 		if(cmbDevices.getSelectedIndex() != 0) // index 0: "select a device type"
512 		{
513 			if(operation == DeviceManagerListener.ADD && device.getClass().getSimpleName().equals(cmbDevices.getSelectedItem().toString()))
514 			{
515 				((DefaultListModel)devicesList.getModel()).addElement(device.getDeviceID());
516 			} 
517 			else if(operation == DeviceManagerListener.REMOVE && device.getClass().getSimpleName().equals(cmbDevices.getSelectedItem().toString()))
518 			{
519 				((DefaultListModel)devicesList.getModel()).removeElement(device.getDeviceID());
520 			}
521 		}
522 	}
523 
524 }