View Javadoc
1   package org.argeo.tracker.ui.controls;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import javax.jcr.Node;
7   import javax.jcr.Property;
8   
9   import org.argeo.cms.util.CmsUtils;
10  import org.argeo.connect.ui.ConnectUiStyles;
11  import org.argeo.connect.ui.ConnectUiUtils;
12  import org.argeo.connect.ui.widgets.ConnectAbstractDropDown;
13  import org.argeo.connect.util.ConnectJcrUtils;
14  import org.argeo.eclipse.ui.EclipseUiUtils;
15  import org.eclipse.swt.SWT;
16  import org.eclipse.swt.events.SelectionAdapter;
17  import org.eclipse.swt.events.SelectionEvent;
18  import org.eclipse.swt.events.TraverseEvent;
19  import org.eclipse.swt.events.TraverseListener;
20  import org.eclipse.swt.layout.GridData;
21  import org.eclipse.swt.layout.GridLayout;
22  import org.eclipse.swt.layout.RowData;
23  import org.eclipse.swt.layout.RowLayout;
24  import org.eclipse.swt.widgets.Button;
25  import org.eclipse.swt.widgets.Composite;
26  import org.eclipse.swt.widgets.Control;
27  import org.eclipse.swt.widgets.Label;
28  import org.eclipse.swt.widgets.Text;
29  
30  /**
31   * Wraps an Abstract form part that enable management of a tag like list in a
32   * form editor.
33   */
34  public abstract class TagListWithDropDownComposite extends Composite {
35  
36  	private static final long serialVersionUID = 5439358000985800234L;
37  
38  	// Context
39  	private List<String> chosenValues;
40  	private final String newTagMsg = "Add...";
41  
42  	public List<String> getChosenValues() {
43  		return chosenValues;
44  	}
45  
46  	/**
47  	 * 
48  	 * @param parent
49  	 * @param style
50  	 * @param toolkit
51  	 * @param form
52  	 * @param peopleService
53  	 * @param peopleWorkbenchService
54  	 * @param taggable
55  	 * @param tagId
56  	 * @param newTagMsg
57  	 */
58  	public TagListWithDropDownComposite(Composite parent, int style, List<String> initialValues) {
59  		super(parent, style);
60  		if (initialValues == null)
61  			chosenValues = new ArrayList<>();
62  		else
63  			chosenValues = initialValues;
64  
65  		populate(this);
66  	}
67  
68  	public void populate(Composite parent) {
69  
70  		RowLayout rl = new RowLayout(SWT.HORIZONTAL);
71  		rl.wrap = true;
72  		rl.marginLeft = rl.marginBottom = 0;
73  		rl.marginTop = 4;
74  		rl.marginRight = 8;
75  		parent.setLayout(rl);
76  
77  		for (String value : chosenValues) {
78  			addValueCmp(parent, value);
79  		}
80  
81  		final Text addValueTxt = new Text(parent, SWT.BORDER);
82  		addValueTxt.setMessage(newTagMsg);
83  		RowData rd = new RowData(80, SWT.DEFAULT);
84  		addValueTxt.setLayoutData(rd);
85  		final TagLikeDropDown tagDD = new TagLikeDropDown(addValueTxt);
86  
87  		// we must call this so that the row data can compute the OK
88  		// button size.
89  		addValueTxt.getParent().layout();
90  		Button okBtn = new Button(parent, SWT.BORDER | SWT.PUSH | SWT.BOTTOM);
91  		okBtn.setText("OK");
92  		rd = new RowData(SWT.DEFAULT, addValueTxt.getSize().y - 2);
93  		okBtn.setLayoutData(rd);
94  
95  		addValueTxt.addTraverseListener(new TraverseListener() {
96  			private static final long serialVersionUID = 1L;
97  
98  			public void keyTraversed(TraverseEvent e) {
99  				if (e.keyCode == SWT.CR) {
100 					String newTag = tagDD.getText();
101 					addValue(addValueTxt, newTag);
102 					tagDD.reset("");
103 					e.doit = false;
104 				}
105 			}
106 		});
107 
108 		okBtn.addSelectionListener(new SelectionAdapter() {
109 			private static final long serialVersionUID = 2780819012423622369L;
110 
111 			@Override
112 			public void widgetSelected(SelectionEvent e) {
113 				String newTag = tagDD.getText();
114 				if (EclipseUiUtils.isEmpty(newTag))
115 					return;
116 				else
117 					addValue(addValueTxt, newTag);
118 				tagDD.reset("");
119 			}
120 		});
121 	}
122 
123 	private void addValue(Control dropdownTxt, String newTag) {
124 		if (!tagExists(newTag))
125 			// TODO manage creation
126 			return;
127 
128 		if (chosenValues.contains(newTag))
129 			return;
130 
131 		chosenValues.add(newTag);
132 		Composite parent = dropdownTxt.getParent();
133 		Composite valueCmp = addValueCmp(parent, newTag);
134 		valueCmp.moveAbove(dropdownTxt);
135 		parent.layout(true, true);
136 	}
137 
138 	private Composite addValueCmp(Composite parent, String value) {
139 		Composite valueCmp = new Composite(parent, SWT.NO_FOCUS);
140 		GridLayout layout = ConnectUiUtils.noSpaceGridLayout(2);
141 		layout.horizontalSpacing = 3;
142 		valueCmp.setLayout(layout);
143 		Label valueLbl = new Label(valueCmp, SWT.NONE);
144 		valueLbl.setText(" " + value);
145 		addDeleteButton(valueCmp, value);
146 		return valueCmp;
147 	}
148 
149 	private void addDeleteButton(Composite parent, String value) {
150 		final Button deleteBtn = new Button(parent, SWT.FLAT);
151 		CmsUtils.style(deleteBtn, ConnectUiStyles.SMALL_DELETE_BTN);
152 		deleteBtn.setLayoutData(new GridData(8, 8));
153 		deleteBtn.addSelectionListener(new SelectionAdapter() {
154 			private static final long serialVersionUID = 1L;
155 
156 			@Override
157 			public void widgetSelected(final SelectionEvent event) {
158 				chosenValues.remove(value);
159 				Composite lineCmp = parent.getParent();
160 				parent.dispose();
161 				lineCmp.layout(true, true);
162 			}
163 		});
164 	}
165 
166 	private class TagLikeDropDown extends ConnectAbstractDropDown {
167 
168 		public TagLikeDropDown(Text text) {
169 			super(text);
170 			init();
171 		}
172 
173 		@Override
174 		protected List<String> getFilteredValues(String filter) {
175 			return TagListWithDropDownComposite.this.getFilteredValues(filter);
176 		}
177 	}
178 
179 	/**
180 	 * Overwrite to provide a filtered list of relevant possible new values
181 	 */
182 	abstract protected List<String> getFilteredValues(String filter);
183 
184 	/**
185 	 * Overwrite to perform a check prior to effectively creating the new value
186 	 */
187 	protected boolean tagExists(String tagKey) {
188 		List<String> existings = getFilteredValues(null);
189 		return existings.contains(tagKey);
190 	}
191 
192 	/** Overwrite to store the tag key in another property */
193 	protected String getTagKey(Node tagDefinition) {
194 		return ConnectJcrUtils.get(tagDefinition, Property.JCR_TITLE);
195 	}
196 
197 	/** Overwrite to display a label rather than the stored value */
198 	protected String getEncodedTagValue(String tagKey) {
199 		return tagKey;
200 	}
201 
202 	/**
203 	 * Overwrite to get the stored value from the displayed label, we expect a
204 	 * bijection between the 2 of them
205 	 */
206 	protected String getDecodedTagValue(String tagValue) {
207 		return tagValue;
208 	}
209 
210 	/**
211 	 * Overwrite to call the relevant open editor command, does nothing by
212 	 * default
213 	 */
214 	protected void callOpenEditor(String tagKey) {
215 	}
216 }