View Javadoc
1   package org.argeo.activities.ui;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import javax.jcr.Node;
7   import javax.jcr.Property;
8   import javax.jcr.RepositoryException;
9   import javax.jcr.Value;
10  
11  import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
12  import org.argeo.cms.util.CmsUtils;
13  import org.argeo.connect.ConnectException;
14  import org.argeo.connect.ui.ConnectEditor;
15  import org.argeo.connect.ui.ConnectUiStyles;
16  import org.argeo.connect.ui.SystemWorkbenchService;
17  import org.argeo.connect.ui.parts.PickUpRelatedDialog;
18  import org.argeo.connect.util.ConnectJcrUtils;
19  import org.argeo.eclipse.ui.EclipseUiUtils;
20  import org.eclipse.jface.dialogs.MessageDialog;
21  import org.eclipse.jface.window.Window;
22  import org.eclipse.swt.SWT;
23  import org.eclipse.swt.events.SelectionAdapter;
24  import org.eclipse.swt.events.SelectionEvent;
25  import org.eclipse.swt.layout.GridData;
26  import org.eclipse.swt.layout.GridLayout;
27  import org.eclipse.swt.layout.RowLayout;
28  import org.eclipse.swt.widgets.Button;
29  import org.eclipse.swt.widgets.Composite;
30  import org.eclipse.swt.widgets.Control;
31  import org.eclipse.swt.widgets.Label;
32  import org.eclipse.swt.widgets.Link;
33  //import org.eclipse.ui.forms.AbstractFormPart;
34  
35  /** Provides basic information about a task in a form context */
36  public class LinkListPart extends Composite {
37  	private static final long serialVersionUID = -5813631462166187272L;
38  
39  	// Context
40  	private final SystemWorkbenchService systemWorkbenchService;
41  	private final Node entity;
42  	private final String propName;
43  	private final List<String> hiddenItemIds = new ArrayList<String>();
44  
45  	// UI Context
46  	private final ConnectEditor editor;
47  	private final AbstractFormPart formPart;
48  
49  	// COMPOSITES
50  	private static final String VALUE_KEY = "valueKey";
51  
52  	protected Node getTargetWithValue(String string) {
53  		try {
54  			return entity.getSession().getNodeByIdentifier(string);
55  		} catch (RepositoryException e) {
56  			throw new ConnectException("Unable to retrieve target node of related entity for " + entity, e);
57  		}
58  	}
59  
60  	public LinkListPart(ConnectEditor editor, AbstractFormPart formPart, Composite parent, int style,
61  			SystemWorkbenchService systemWorkbenchService, Node entity, String propName) {
62  		this(editor, formPart, parent, style, systemWorkbenchService, entity, propName, null);
63  	}
64  
65  	/**
66  	 * Will prevent display of linked entities whose id is passed in the list
67  	 * 
68  	 * @param toolkit
69  	 * @param formPart
70  	 * @param parent
71  	 * @param style
72  	 * @param systemWorkbenchService
73  	 * @param entity
74  	 * @param propName
75  	 * @param hiddenItemIds
76  	 */
77  	public LinkListPart(ConnectEditor editor, AbstractFormPart formPart, Composite parent, int style,
78  			SystemWorkbenchService systemWorkbenchService, Node entity, String propName, List<String> hiddenItemIds) {
79  		super(parent, style);
80  		this.formPart = formPart;
81  		this.editor = editor;
82  		this.systemWorkbenchService = systemWorkbenchService;
83  		this.entity = entity;
84  		this.propName = propName;
85  
86  		// Initialise the form
87  		RowLayout rl = new RowLayout(SWT.HORIZONTAL);
88  		rl.wrap = true;
89  		rl.marginBottom = rl.marginTop = rl.marginLeft = rl.marginRight = 0;
90  		rl.spacing = 8;
91  		this.setLayout(rl);
92  		if (hiddenItemIds != null)
93  			this.hiddenItemIds.addAll(hiddenItemIds);
94  		recreateRelated(this);
95  	}
96  
97  	public void refresh() {
98  		if (relatedHasChanged()) {
99  			recreateRelated(this);
100 		}
101 		this.layout();
102 	}
103 
104 	// HELPERS
105 	private void recreateRelated(Composite relatedCmp) {
106 		try {
107 			// Dispose existing
108 			EclipseUiUtils.clear(relatedCmp);
109 
110 			if (entity.hasProperty(propName)) {
111 				Value[] values = entity.getProperty(propName).getValues();
112 				for (Value value : values) {
113 					String valueStr = value.getString();
114 					if (!hiddenItemIds.contains(valueStr)) {
115 						Node targetNode = getTargetWithValue(valueStr);
116 						String labelStr = ConnectJcrUtils.get(targetNode, Property.JCR_TITLE);
117 						createDeletableClickable(relatedCmp, valueStr, labelStr, editor.isEditing());
118 					}
119 				}
120 			} else if (!editor.isEditing())
121 				// Add an empty label to force a correct layout
122 				new Label(relatedCmp, SWT.NONE);
123 
124 			if (editor.isEditing()) {
125 				// The add button
126 				Link newRelatedLk = new Link(relatedCmp, SWT.CENTER);
127 				newRelatedLk.setText("<a>Add</a>");
128 				addNewRelatedSelList(newRelatedLk);
129 			}
130 		} catch (RepositoryException e) {
131 			throw new ConnectException("Unable to create related to composite for " + entity, e);
132 		}
133 	}
134 
135 	private Composite createDeletableClickable(Composite parent, final String value, String label, boolean isEditing) {
136 		Composite part = new Composite(parent, SWT.NO_FOCUS);
137 		part.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)));
138 		part.setData(VALUE_KEY, value);
139 
140 		Link relatedLk = new Link(part, SWT.LEFT);
141 		relatedLk.setText("<a>" + label + "</a>");
142 		CmsUtils.style(relatedLk, ConnectUiStyles.ENTITY_HEADER);
143 		relatedLk.addSelectionListener(new OpenEditorAdapter(value));
144 
145 		if (isEditing) {
146 			// Display delete button only in edit mode.
147 			Button deleteBtn = new Button(part, SWT.FLAT);
148 			CmsUtils.style(deleteBtn, ConnectUiStyles.SMALL_DELETE_BTN);
149 			GridData gd = new GridData(8, 8);
150 			gd.horizontalIndent = 2;
151 			deleteBtn.setLayoutData(gd);
152 			deleteBtn.addSelectionListener(new SelectionAdapter() {
153 				private static final long serialVersionUID = 1L;
154 
155 				@Override
156 				public void widgetSelected(final SelectionEvent event) {
157 					ConnectJcrUtils.removeRefFromMultiValuedProp(entity, propName, value);
158 					formPart.refresh();
159 					formPart.markDirty();
160 				}
161 			});
162 		}
163 		part.layout();
164 		return part;
165 	}
166 
167 	private boolean relatedHasChanged() {
168 		try {
169 			Value[] values = null;
170 			Control[] oldChildren;
171 
172 			if (entity.hasProperty(propName))
173 				values = entity.getProperty(propName).getValues();
174 			oldChildren = this.getChildren();
175 
176 			if (values == null)
177 				return oldChildren.length > 0;
178 
179 			int valueIndex = 0;
180 			loop: for (int i = 0; i < oldChildren.length; i++) {
181 				String currUiValue = (String) oldChildren[i].getData(VALUE_KEY);
182 				if (currUiValue == null)
183 					// skip non business controls
184 					continue loop;
185 				if (valueIndex >= values.length || !currUiValue.equals(values[valueIndex].getString()))
186 					return true;
187 				valueIndex++;
188 			}
189 			// initialisation or added value
190 			if (valueIndex < values.length)
191 				return true;
192 			return false;
193 		} catch (RepositoryException e) {
194 			throw new ConnectException("Unable to determine stale state for prop " + propName + " of " + entity, e);
195 		}
196 	}
197 
198 	// Local classes
199 	private class OpenEditorAdapter extends SelectionAdapter {
200 		private static final long serialVersionUID = 1L;
201 
202 		private final String jcrId;
203 
204 		public OpenEditorAdapter(String jcrId) {
205 			this.jcrId = jcrId;
206 		}
207 
208 		@Override
209 		public void widgetSelected(final SelectionEvent event) {
210 			try {
211 				Node node = entity.getSession().getNodeByIdentifier(jcrId);
212 				systemWorkbenchService.openEntityEditor(node);
213 			} catch (RepositoryException e) {
214 				throw new ConnectException("Cannot open editor for JCR id " + jcrId, e);
215 			}
216 			// CommandUtils.callCommand(systemWorkbenchService.getOpenEntityEditorCmdId(),
217 			// ConnectEditor.PARAM_JCR_ID,
218 			// jcrId);
219 		}
220 	}
221 
222 	/**
223 	 * Configure the action launched when the user click the add link in the
224 	 * "relatedTo" composite
225 	 */
226 	private void addNewRelatedSelList(final Link link) {
227 		link.addSelectionListener(new SelectionAdapter() {
228 			private static final long serialVersionUID = -7118320199160680131L;
229 
230 			@Override
231 			public void widgetSelected(final SelectionEvent event) {
232 				try {
233 					PickUpRelatedDialog diag = new PickUpRelatedDialog(link.getShell(), "Choose a related item",
234 							entity.getSession(), systemWorkbenchService, entity);
235 
236 					int result = diag.open();
237 					if (Window.OK == result) {
238 						Node node = diag.getSelected();
239 						String errMsg = ConnectJcrUtils.addRefToMultiValuedProp(entity, propName, node);
240 						if (errMsg != null)
241 							MessageDialog.openError(link.getShell(), "Duplicates", errMsg);
242 						else {
243 							formPart.refresh();
244 							formPart.markDirty();
245 						}
246 					}
247 				} catch (RepositoryException e) {
248 					throw new ConnectException("Unable to link chosen node " + "to current activity " + entity, e);
249 				}
250 			}
251 		});
252 	}
253 }