View Javadoc

1   /*
2    *  
3    *  Fosstrak LLRP Commander (www.fosstrak.org)
4    * 
5    *  Copyright (C) 2008 ETH Zurich
6    *
7    *  This program is free software: you can redistribute it and/or modify
8    *  it under the terms of the GNU General Public License as published by
9    *  the Free Software Foundation, either version 3 of the License, or
10   *  (at your option) any later version.
11   *
12   *  This program is distributed in the hope that it will be useful,
13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   *  GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License
18   *  along with this program.  If not, see <http://www.gnu.org/licenses/> 
19   *
20   */
21  
22  package org.fosstrak.llrp.commander.editors.graphical;
23  
24  import java.io.PrintWriter;
25  import java.io.StringWriter;
26  import java.util.LinkedList;
27  import java.util.List;
28  
29  import org.eclipse.jface.action.Action;
30  import org.eclipse.jface.action.IMenuListener;
31  import org.eclipse.jface.action.IMenuManager;
32  import org.eclipse.jface.action.MenuManager;
33  import org.eclipse.jface.action.ToolBarManager;
34  import org.eclipse.jface.dialogs.IMessageProvider;
35  import org.eclipse.jface.resource.ImageDescriptor;
36  import org.eclipse.jface.viewers.*;
37  import org.eclipse.swt.SWT;
38  import org.eclipse.swt.events.DisposeEvent;
39  import org.eclipse.swt.events.DisposeListener;
40  import org.eclipse.swt.events.FocusEvent;
41  import org.eclipse.swt.events.FocusListener;
42  import org.eclipse.swt.graphics.Cursor;
43  import org.eclipse.swt.graphics.Point;
44  import org.eclipse.swt.layout.*;
45  import org.eclipse.swt.widgets.*;
46  import org.eclipse.ui.*;
47  import org.eclipse.ui.forms.*;
48  import org.eclipse.ui.forms.events.HyperlinkAdapter;
49  import org.eclipse.ui.forms.events.HyperlinkEvent;
50  import org.eclipse.ui.forms.widgets.*;
51  import org.fosstrak.llrp.commander.LLRPPlugin;
52  import org.fosstrak.llrp.commander.ResourceCenter;
53  import org.fosstrak.llrp.commander.editors.graphical.actions.AddParameterAction;
54  import org.fosstrak.llrp.commander.editors.graphical.actions.DeleteParameterAction;
55  import org.fosstrak.llrp.commander.util.LLRP;
56  import org.fosstrak.llrp.commander.util.LLRPTreeMaintainer;
57  import org.llrp.ltk.types.LLRPMessage;
58  import org.llrp.ltk.types.LLRPParameter;
59  import org.llrp.ltkGenerator.generated.*;
60  
61  
62  /**
63   * The LLRPMasterDetailsBlock has two parts: a master part that shows the message tree
64   * and a details part that shows details about the currently selected tree element.
65   *
66   * @author Ulrich Etter, ETHZ
67   *
68   */
69  public class LLRPMasterDetailsBlock extends MasterDetailsBlock {
70  	
71  	private static String DUMMY_ACTION_TEXT = "<No Applicable Parameter>";
72  	
73  	private TreeViewer treeViewer;
74  	private GraphicalEditorPage page;
75  	private LLRPTreeMaintainer treeMaintainer;
76  
77  	private List<Object> invalidParameters;
78  	
79  	public LLRPMasterDetailsBlock(GraphicalEditorPage page, LLRPTreeMaintainer treeMaintainer) {
80  		this.page = page;
81  		this.treeMaintainer = treeMaintainer;
82  	}
83  	
84  	/**
85  	 * Updates the error message field, refreshes the TreeViewer and selects the given element in the TreeViewer.
86  	 * 
87  	 * @param elementToSelect the element to select in the TreeViewer
88  	 */
89  	public void refresh(Object elementToSelect){
90  		updateErrorMessage();
91  		treeViewer.refresh();
92  		if(elementToSelect != null){
93  			treeViewer.setSelection(new StructuredSelection(elementToSelect), true);
94  		}
95  		treeViewer.expandAll();
96  	}
97  
98  	protected void createMasterPart(final IManagedForm managedForm, Composite parent) {
99  		final FormToolkit toolkit = managedForm.getToolkit();
100 		Section section = toolkit.createSection(parent, Section.TITLE_BAR | Section.DESCRIPTION);
101 		section.setText("Message Tree");
102 		section.setDescription("Right-click on a tree element to add or delete a parameter.");
103 		section.marginWidth = 10;
104 		section.marginHeight = 5;
105 		Composite client = toolkit.createComposite(section, SWT.WRAP);
106 		GridLayout layout = new GridLayout();
107 		layout.numColumns = 1;
108 		layout.marginWidth = 2;
109 		layout.marginTop = 5;
110 		client.setLayout(layout);
111 		
112 		Tree t = toolkit.createTree(client, SWT.NULL);
113 		GridData gd = new GridData(GridData.FILL_BOTH);
114 		gd.heightHint = 20;
115 		gd.widthHint = 100;
116 		t.setLayoutData(gd);
117 		toolkit.paintBordersFor(client);
118 		section.setClient(client);
119 		final SectionPart spart = new SectionPart(section);
120 		managedForm.addPart(spart);
121 		treeViewer = new TreeViewer(t);
122 		page.getSite().setSelectionProvider(treeViewer);
123 		treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
124 			public void selectionChanged(SelectionChangedEvent event) {
125 				managedForm.getMessageManager().removeAllMessages();
126 				managedForm.fireSelectionChanged(spart, event.getSelection());
127 			}
128 		});
129 
130 		treeViewer.setContentProvider(new LLRPTreeContentProvider(treeMaintainer));
131 		treeViewer.setLabelProvider(new LLRPTreeLabelProvider(treeMaintainer));
132 		treeViewer.setInput(treeMaintainer);
133 		treeViewer.expandAll();
134 		
135 		createSectionToolbar(section, toolkit);
136 		createContextMenu();		
137 		
138 		final ScrolledForm form = managedForm.getForm();	
139 		form.getForm().addMessageHyperlinkListener(new HyperlinkAdapter() {
140 			public void linkActivated(HyperlinkEvent e) {
141 				Point hl = ((Control) e.widget).toDisplay(0, 0);
142 				hl.x += 10;
143 				hl.y += 10;
144 				Shell shell = new Shell(form.getShell(), SWT.ON_TOP | SWT.TOOL);
145 				shell.setLayout(new FillLayout());
146 				FormText text = toolkit.createFormText(shell, true);
147 				text.addFocusListener(new FocusListener(){
148 
149 					public void focusGained(FocusEvent e) {
150 						// do nothing
151 					}
152 
153 					public void focusLost(FocusEvent e) {
154 						((FormText) e.widget).getShell().dispose();
155 					}
156 					
157 				});
158 				configureFormText(text);
159 				text.setText(createFormTextContent(), true, false);
160 				shell.setLocation(hl);
161 				shell.pack();
162 				shell.open();
163 			}
164 		});
165 
166 	}
167 	
168 	private void createContextMenu() {
169 		DeleteParameterAction deleteParameterAction = new DeleteParameterAction(page.getSite().getWorkbenchWindow(), treeViewer, treeMaintainer, this);
170 		ImageDescriptor deleteImageDescriptor = ResourceCenter.getInstance().getImageDescriptor("delete.gif");
171 		deleteParameterAction.setImageDescriptor(deleteImageDescriptor);
172 		
173 		MenuManager contextMenuManager = new MenuManager(null);
174 		final MenuManager addMenuManager = new MenuManager("Add", "add");
175 
176 		final Action a = new Action(DUMMY_ACTION_TEXT) {};
177 		a.setEnabled(false);
178 		addMenuManager.add(a); // add dummy action so that eclipse shows the menu
179 		addMenuManager.setRemoveAllWhenShown(true);
180 		addMenuManager.addMenuListener(new IMenuListener() {
181 
182 			/* (non-Javadoc)
183 			 * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager)
184 			 */
185 			public void menuAboutToShow(IMenuManager manager) {
186 				createAddParameterActions(addMenuManager);
187 			}
188 
189 		});
190 		
191 		contextMenuManager.add(addMenuManager);
192 		contextMenuManager.add(deleteParameterAction);
193 		Menu menu = contextMenuManager.createContextMenu(treeViewer.getControl());
194 		treeViewer.getControl().setMenu(menu);
195 	}
196 	
197 	private void createAddParameterActions(MenuManager addMenuManager) {
198 		IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
199 		if (!selection.isEmpty()) {
200 			Object treeElement = selection.getFirstElement();
201 			if (treeElement instanceof LLRPMessage || treeElement instanceof LLRPParameter){
202 				Object messageOrParameterDefinition = treeMaintainer.getDefinition(treeElement);
203 				java.util.List<Object> parameterOrChoiceList = new LinkedList<Object>();
204 				if (messageOrParameterDefinition instanceof MessageDefinition){
205 					parameterOrChoiceList = ((MessageDefinition) messageOrParameterDefinition).getParameterOrChoice();
206 				}
207 				else if (messageOrParameterDefinition instanceof ParameterDefinition){
208 					parameterOrChoiceList = ((ParameterDefinition) messageOrParameterDefinition).getParameterOrChoice();
209 				}
210 				for (Object o : parameterOrChoiceList){
211 					String childName = "";
212 					if (o instanceof ParameterReference){
213 						childName = ((ParameterReference) o).getType(); 
214 					}
215 					else if (o instanceof ChoiceReference){
216 						childName = ((ChoiceReference) o).getType(); 
217 					}
218 					if (LLRP.canOccurMultipleTimes(messageOrParameterDefinition, childName)){
219 						// lists can't be added or removed, therefore don't create actions for lists
220 					}
221 					else {
222 						if (LLRP.isChoice(messageOrParameterDefinition, childName)){
223 							MenuManager choiceMenuManager = new MenuManager(childName);
224 							addMenuManager.add(choiceMenuManager);
225 							ChoiceDefinition choiceDefinition = LLRP.getChoiceDefinition(childName);
226 							List<ChoiceParameterReference> choiceParameterReferences = choiceDefinition.getParameter();
227 							for (ChoiceParameterReference cpr : choiceParameterReferences){
228 								String parameterName = cpr.getType();
229 								AddParameterAction addParameterAction = createAddParameterAction(treeElement, childName, parameterName);
230 								choiceMenuManager.add(addParameterAction);
231 								if (treeMaintainer.getChild(treeElement, childName) != null){
232 									// parameter does already exist and therefore can't be added anymore
233 									addParameterAction.setEnabled(false);
234 								}
235 							}
236 						}
237 						else{
238 							AddParameterAction addParameterAction = createAddParameterAction(treeElement, childName, childName);
239 							addMenuManager.add(addParameterAction);
240 							if (treeMaintainer.getChild(treeElement, childName) != null){
241 								// parameter does already exist and therefore can't be added anymore
242 								addParameterAction.setEnabled(false);
243 							}
244 						}
245 					}
246 				}
247 			}
248 			else if (treeElement instanceof java.util.List){
249 				
250 				String name = treeMaintainer.getName(treeElement);
251 				LlrpDefinition llrpDefinition = LLRP.getLlrpDefintion();
252 				List<Object> list = llrpDefinition.getMessageDefinitionOrParameterDefinitionOrChoiceDefinition();
253 				for (Object o : list){
254 					if (o instanceof ParameterDefinition){
255 						if (((ParameterDefinition) o).getName().equals(name)){
256 							AddParameterAction addParameterAction = createAddParameterAction(treeElement, null, name);
257 							addMenuManager.add(addParameterAction);
258 							break;
259 						}
260 					}
261 					if (o instanceof ChoiceDefinition){
262 						if (((ChoiceDefinition) o).getName().equals(name)){
263 							ChoiceDefinition choiceDefinition = LLRP.getChoiceDefinition(name);
264 							for (ChoiceParameterReference cpr : choiceDefinition.getParameter()){
265 								AddParameterAction addParameterAction = createAddParameterAction(treeElement, null, cpr.getType());
266 								addMenuManager.add(addParameterAction);
267 							}
268 							break;
269 						}
270 					}
271 				}
272 			}
273 
274 			if (addMenuManager.getSize() == 0){
275 				// add dummy action so that eclipse keeps showing the menu
276 				Action a = new Action(DUMMY_ACTION_TEXT) {};
277 				a.setEnabled(false);
278 				addMenuManager.add(a);
279 			}
280 		}
281 	}
282 
283 	private AddParameterAction createAddParameterAction(Object treeElement, String childName, String parameterName) {
284 		AddParameterAction result = 
285 			new AddParameterAction(treeViewer, treeMaintainer, treeElement, childName, parameterName);
286 		ImageDescriptor imageDescriptor = PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJ_ELEMENT);
287 		result.setImageDescriptor(imageDescriptor);
288 		return result;
289 	}
290 
291 	/**
292 	 * This method is based on the method 'createSectionToolbar' of 
293 	 * org.eclipse.pde.internal.ui.editor.plugin.ExtensionsSection
294 	 * 
295 	 * @param section
296 	 * @param toolkit
297 	 */
298 	private void createSectionToolbar(Section section, FormToolkit toolkit) {
299 		ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
300 		ToolBar toolbar = toolBarManager.createControl(section);
301 		final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND);
302 		toolbar.setCursor(handCursor);
303 		// Cursor needs to be explicitly disposed
304 		toolbar.addDisposeListener(new DisposeListener() {
305 			public void widgetDisposed(DisposeEvent e) {
306 				if ((handCursor != null) &&
307 						(handCursor.isDisposed() == false)) {
308 					handCursor.dispose();
309 				}
310 			}
311 		});	
312 		
313 		// Add "expand all" action to the tool bar	
314 		Action expandAllAction = new Action(){
315 			public void run() {				
316 				if (treeViewer != null){
317 					treeViewer.expandAll();
318 				}			
319 			}
320 		};
321 		expandAllAction.setToolTipText("Expand All");
322 		ImageDescriptor imageDescriptor = ResourceCenter.getInstance().getImageDescriptor("expand_all.gif");
323 		expandAllAction.setImageDescriptor(imageDescriptor);
324 		toolBarManager.add(expandAllAction);
325 		
326 		// Add "collapse all" action to the tool bar	
327 		Action collapseAllAction = new Action(){
328 			public void run() {				
329 				if (treeViewer != null){
330 					treeViewer.collapseAll();
331 				}			
332 			}
333 		};
334 		collapseAllAction.setToolTipText("Collapse All");
335 		imageDescriptor = ResourceCenter.getInstance().getImageDescriptor("collapse_all.gif");
336 		collapseAllAction.setImageDescriptor(imageDescriptor);
337 		toolBarManager.add(collapseAllAction);
338 		
339 		toolBarManager.update(true);
340 
341 		section.setTextClient(toolbar);
342 	}
343 	
344 	private void updateErrorMessage(){
345 		final ScrolledForm form = page.getManagedForm().getForm();
346 		invalidParameters = treeMaintainer.getNonRecursivelyInvalidMessageOrParameterDescendants(treeMaintainer.getRoot());
347 		if (invalidParameters.size() == 0){
348 			form.getForm().setMessage(null, 0);
349 		}
350 		else if (invalidParameters.size() == 1){
351 			form.getForm().setMessage(invalidParameters.size() + " parameter contains errors", IMessageProvider.ERROR);
352 		}
353 		else{
354 			form.getForm().setMessage(invalidParameters.size() + " parameters contain errors", IMessageProvider.ERROR);
355 		}
356 	}
357 	
358 	private void configureFormText(FormText text) {
359 		text.addHyperlinkListener(new HyperlinkAdapter() {
360 			public void linkActivated(HyperlinkEvent e) {
361 				String is = (String) e.getHref();
362 				int index = Integer.parseInt(is);
363 				Object invalidParameter = invalidParameters.get(index);
364 				((FormText) e.widget).getShell().dispose();
365 				treeViewer.setSelection(new StructuredSelection(invalidParameter), true);
366 			}
367 		});
368 		text.setImage("error", PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK));
369 	}
370 	
371 	private String createFormTextContent() {
372 		StringWriter sw = new StringWriter();
373 		PrintWriter pw = new PrintWriter(sw);
374 		pw.println("<form>");
375 		for (int i = 0; i < invalidParameters.size(); i++) {
376 			Object invalidParameter = invalidParameters.get(i);
377 			pw.print("<li vspace=\"false\" style=\"image\" indent=\"16\" value=\"error");
378 			pw.print("\"> <a href=\"");
379 			pw.print(i);
380 			pw.print("\">");
381 			pw.print(treeMaintainer.getName(invalidParameter));
382 			pw.println("</a></li>");
383 		}
384 		pw.println("</form>");
385 		pw.flush();
386 		return sw.toString();
387 	}
388 	
389 	protected void createToolBarActions(IManagedForm managedForm) {
390 		final ScrolledForm form = managedForm.getForm();
391 		Action haction = new Action("hor", Action.AS_RADIO_BUTTON) {
392 			public void run() {
393 				sashForm.setOrientation(SWT.HORIZONTAL);
394 				form.reflow(true);
395 			}
396 		};
397 		haction.setChecked(true);
398 		haction.setToolTipText("Horizontal orientation");
399 		haction.setImageDescriptor(LLRPPlugin.getImageDescriptor("icons/th_horizontal.gif"));
400 		Action vaction = new Action("ver", Action.AS_RADIO_BUTTON) {
401 			public void run() {
402 				sashForm.setOrientation(SWT.VERTICAL);
403 				form.reflow(true);
404 			}
405 		};
406 		vaction.setChecked(false);
407 		vaction.setToolTipText("Vertical orientation");
408 		vaction.setImageDescriptor(LLRPPlugin.getImageDescriptor("icons/th_vertical.gif"));
409 		form.getToolBarManager().add(haction);
410 		form.getToolBarManager().add(vaction);
411 	}
412 	
413 	protected void registerPages(DetailsPart detailsPart) {
414 		detailsPart.setPageProvider(new LLRPDetailsPageProvider());
415 		refresh(treeMaintainer.getRoot());
416 	}
417 	
418 	class LLRPDetailsPageProvider implements IDetailsPageProvider{
419 
420 		public IDetailsPage getPage(Object key) {
421 			if (key instanceof LLRPMessage || key instanceof LLRPParameter){
422 				return new LLRPDetailsPage(key, treeViewer, treeMaintainer, LLRPMasterDetailsBlock.this);
423 			}
424 			return null;
425 		}
426 
427 		public Object getPageKey(Object object) {
428 			return object;
429 		}
430 		
431 	}
432 }