View Javadoc
1   package org.argeo.cms.ui.forms;
2   
3   import java.util.List;
4   
5   import javax.jcr.Node;
6   import javax.jcr.RepositoryException;
7   
8   import org.argeo.cms.ui.util.CmsUiUtils;
9   import org.argeo.cms.ui.viewers.EditablePart;
10  import org.argeo.cms.ui.widgets.StyledControl;
11  import org.argeo.eclipse.ui.EclipseUiUtils;
12  import org.eclipse.jface.dialogs.MessageDialog;
13  import org.eclipse.swt.SWT;
14  import org.eclipse.swt.events.SelectionListener;
15  import org.eclipse.swt.events.TraverseEvent;
16  import org.eclipse.swt.events.TraverseListener;
17  import org.eclipse.swt.layout.GridData;
18  import org.eclipse.swt.layout.GridLayout;
19  import org.eclipse.swt.layout.RowLayout;
20  import org.eclipse.swt.widgets.Button;
21  import org.eclipse.swt.widgets.Composite;
22  import org.eclipse.swt.widgets.Control;
23  import org.eclipse.swt.widgets.Label;
24  import org.eclipse.swt.widgets.Text;
25  
26  /** Display, add or remove values from a list in a CMS context */
27  public class EditableMultiStringProperty extends StyledControl implements EditablePart {
28  	private static final long serialVersionUID = -7044614381252178595L;
29  
30  	private String propertyName;
31  	private String message;
32  	// TODO implement the ability to provide a list of possible values
33  	private String[] possibleValues;
34  	private boolean canEdit;
35  	private SelectionListener removeValueSL;
36  	private List<String> values;
37  
38  	// TODO manage within the CSS
39  	private int rowSpacing = 5;
40  	private int rowMarging = 0;
41  	private int oneValueMargingRight = 5;
42  	private int btnWidth = 16;
43  	private int btnHeight = 16;
44  	private int btnHorizontalIndent = 3;
45  
46  	public EditableMultiStringProperty(Composite parent, int style, Node node, String propertyName, List<String> values,
47  			String[] possibleValues, String addValueMsg, SelectionListener removeValueSelectionListener)
48  			throws RepositoryException {
49  		super(parent, style, node, true);
50  
51  		this.propertyName = propertyName;
52  		this.values = values;
53  		this.possibleValues = new String[]{"Un", "Deux", "Trois"};
54  		this.message = addValueMsg;
55  		this.canEdit = removeValueSelectionListener != null;
56  		this.removeValueSL = removeValueSelectionListener;
57  	}
58  
59  	public List<String> getValues() {
60  		return values;
61  	}
62  
63  	public void setValues(List<String> values) {
64  		this.values = values;
65  	}
66  
67  	// Row layout items do not need explicit layout data
68  	protected void setControlLayoutData(Control control) {
69  	}
70  
71  	/** To be overridden */
72  	protected void setContainerLayoutData(Composite composite) {
73  		composite.setLayoutData(CmsUiUtils.fillWidth());
74  	}
75  
76  	@Override
77  	public Control getControl() {
78  		return super.getControl();
79  	}
80  
81  	@Override
82  	protected Control createControl(Composite box, String style) {
83  		Composite row = new Composite(box, SWT.NO_FOCUS);
84  		row.setLayoutData(EclipseUiUtils.fillAll());
85  
86  		RowLayout rl = new RowLayout(SWT.HORIZONTAL);
87  		rl.wrap = true;
88  		rl.spacing = rowSpacing;
89  		rl.marginRight = rl.marginLeft = rl.marginBottom = rl.marginTop = rowMarging;
90  		row.setLayout(rl);
91  
92  		if (values != null) {
93  			for (final String value : values) {
94  				if (canEdit)
95  					createRemovableValue(row, SWT.SINGLE, value);
96  				else
97  					createValueLabel(row, SWT.SINGLE, value);
98  			}
99  		}
100 
101 		if (!canEdit)
102 			return row;
103 		else if (isEditing())
104 			return createText(row, style);
105 		else
106 			return createLabel(row, style);
107 	}
108 
109 	/**
110 	 * Override to provide specific layout for the existing values, typically
111 	 * adding a pound (#) char for tags or anchor info for browsable links. We
112 	 * assume the parent composite already has a layout and it is the caller
113 	 * responsibility to apply corresponding layout data
114 	 */
115 	protected Label createValueLabel(Composite parent, int style, String value) {
116 		Label label = new Label(parent, style);
117 		label.setText("#" + value);
118 		CmsUiUtils.markup(label);
119 		CmsUiUtils.style(label, FormStyle.propertyText.style());
120 		return label;
121 	}
122 
123 	private Composite createRemovableValue(Composite parent, int style, String value) {
124 		Composite valCmp = new Composite(parent, SWT.NO_FOCUS);
125 		GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false));
126 		gl.marginRight = oneValueMargingRight;
127 		valCmp.setLayout(gl);
128 
129 		createValueLabel(valCmp, SWT.WRAP, value);
130 
131 		Button deleteBtn = new Button(valCmp, SWT.FLAT);
132 		deleteBtn.setData(FormConstants.LINKED_VALUE, value);
133 		deleteBtn.addSelectionListener(removeValueSL);
134 		CmsUiUtils.style(deleteBtn, FormStyle.delete.style() + FormStyle.BUTTON_SUFFIX);
135 		GridData gd = new GridData();
136 		gd.heightHint = btnHeight;
137 		gd.widthHint = btnWidth;
138 		gd.horizontalIndent = btnHorizontalIndent;
139 		deleteBtn.setLayoutData(gd);
140 
141 		return valCmp;
142 	}
143 
144 	protected Text createText(Composite box, String style) {
145 		final Text text = new Text(box, getStyle());
146 		// The "add new value" text is not meant to change, so we can set it on
147 		// creation
148 		text.setMessage(message);
149 		CmsUiUtils.style(text, style);
150 		text.setFocus();
151 
152 		text.addTraverseListener(new TraverseListener() {
153 			private static final long serialVersionUID = 1L;
154 
155 			public void keyTraversed(TraverseEvent e) {
156 				if (e.keyCode == SWT.CR) {
157 					addValue(text);
158 					e.doit = false;
159 				}
160 			}
161 		});
162 
163 		// The OK button does not work with the focusOut listener
164 		// because focus out is called before the OK button is pressed
165 
166 		// // we must call layout() now so that the row data can compute the
167 		// height
168 		// // of the other controls.
169 		// text.getParent().layout();
170 		// int height = text.getSize().y;
171 		//
172 		// Button okBtn = new Button(box, SWT.BORDER | SWT.PUSH | SWT.BOTTOM);
173 		// okBtn.setText("OK");
174 		// RowData rd = new RowData(SWT.DEFAULT, height - 2);
175 		// okBtn.setLayoutData(rd);
176 		//
177 		// okBtn.addSelectionListener(new SelectionAdapter() {
178 		// private static final long serialVersionUID = 2780819012423622369L;
179 		//
180 		// @Override
181 		// public void widgetSelected(SelectionEvent e) {
182 		// addValue(text);
183 		// }
184 		// });
185 
186 		return text;
187 	}
188 
189 	/** Performs the real addition, overwrite to make further sanity checks */
190 	protected void addValue(Text text) {
191 		String value = text.getText();
192 		String errMsg = null;
193 
194 		if (EclipseUiUtils.isEmpty(value))
195 			return;
196 
197 		if (values.contains(value))
198 			errMsg = "Dupplicated value: " + value + ", please correct and try again";
199 		if (errMsg != null)
200 			MessageDialog.openError(this.getShell(), "Addition not allowed", errMsg);
201 		else {
202 			values.add(value);
203 			Composite newCmp = createRemovableValue(text.getParent(), SWT.SINGLE, value);
204 			newCmp.moveAbove(text);
205 			text.setText("");
206 			newCmp.getParent().layout();
207 		}
208 	}
209 
210 	protected Label createLabel(Composite box, String style) {
211 		if (canEdit) {
212 			Label lbl = new Label(box, getStyle());
213 			lbl.setText(message);
214 			CmsUiUtils.style(lbl, style);
215 			CmsUiUtils.markup(lbl);
216 			if (mouseListener != null)
217 				lbl.addMouseListener(mouseListener);
218 			return lbl;
219 		}
220 		return null;
221 	}
222 
223 	protected void clear(boolean deep) {
224 		Control child = getControl();
225 		if (deep)
226 			super.clear(deep);
227 		else {
228 			child.getParent().dispose();
229 		}
230 	}
231 
232 	public void setText(String text) {
233 		Control child = getControl();
234 		if (child instanceof Label) {
235 			Label lbl = (Label) child;
236 			if (canEdit)
237 				lbl.setText(text);
238 			else
239 				lbl.setText("");
240 		} else if (child instanceof Text) {
241 			Text txt = (Text) child;
242 			txt.setText(text);
243 		}
244 	}
245 
246 	public synchronized void startEditing() {
247 		getControl().setData(STYLE, FormStyle.propertyText.style());
248 		super.startEditing();
249 	}
250 
251 	public synchronized void stopEditing() {
252 		getControl().setData(STYLE, FormStyle.propertyMessage.style());
253 		super.stopEditing();
254 	}
255 
256 	public String getPropertyName() {
257 		return propertyName;
258 	}
259 }