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
34
35
36 public class LinkListPart extends Composite {
37 private static final long serialVersionUID = -5813631462166187272L;
38
39
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
46 private final ConnectEditor editor;
47 private final AbstractFormPart formPart;
48
49
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
67
68
69
70
71
72
73
74
75
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
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
105 private void recreateRelated(Composite relatedCmp) {
106 try {
107
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
122 new Label(relatedCmp, SWT.NONE);
123
124 if (editor.isEditing()) {
125
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
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
184 continue loop;
185 if (valueIndex >= values.length || !currUiValue.equals(values[valueIndex].getString()))
186 return true;
187 valueIndex++;
188 }
189
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
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
217
218
219 }
220 }
221
222
223
224
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 }