1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.form.field;
35
36 import info.magnolia.cms.i18n.I18nContentSupport;
37 import info.magnolia.objectfactory.ComponentProvider;
38 import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
39 import info.magnolia.ui.form.field.definition.ConfiguredFieldDefinition;
40 import info.magnolia.ui.form.field.definition.MultiValueFieldDefinition;
41 import info.magnolia.ui.form.field.factory.FieldFactoryFactory;
42 import info.magnolia.ui.form.field.transformer.TransformedProperty;
43 import info.magnolia.ui.form.field.transformer.Transformer;
44 import info.magnolia.ui.form.field.transformer.multi.MultiTransformer;
45
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import com.google.common.base.Function;
50 import com.google.common.base.Optional;
51 import com.google.common.base.Predicates;
52 import com.google.common.collect.Iterators;
53 import com.vaadin.data.Item;
54 import com.vaadin.data.Property;
55 import com.vaadin.data.util.PropertysetItem;
56 import com.vaadin.ui.Alignment;
57 import com.vaadin.ui.Button;
58 import com.vaadin.ui.Button.ClickEvent;
59 import com.vaadin.ui.Button.ClickListener;
60 import com.vaadin.ui.Component;
61 import com.vaadin.ui.Field;
62 import com.vaadin.ui.HasComponents;
63 import com.vaadin.ui.HorizontalLayout;
64 import com.vaadin.ui.NativeButton;
65 import com.vaadin.ui.VerticalLayout;
66
67
68
69
70
71
72
73
74
75 public class MultiField extends AbstractCustomMultiField<MultiValueFieldDefinition, PropertysetItem> {
76
77 private static final Logger log = LoggerFactory.getLogger(MultiField.class);
78
79 private final ConfiguredFieldDefinition fieldDefinition;
80
81 private final Button addButton = new NativeButton();
82 private String buttonCaptionAdd;
83 private String buttonCaptionRemove;
84 private String buttonCaptionMoveUp = "Move Up";
85 private String buttonCaptionMoveDown = "Move Down";
86
87 public MultiField(MultiValueFieldDefinition definition, FieldFactoryFactory fieldFactoryFactory, ComponentProvider componentProvider, Item relatedFieldItem, I18NAuthoringSupport i18nAuthoringSupport) {
88 super(definition, fieldFactoryFactory, componentProvider, relatedFieldItem, i18nAuthoringSupport);
89 this.fieldDefinition = definition.getField();
90
91 if (definition.isReadOnly()) {
92 fieldDefinition.setReadOnly(true);
93 }
94 }
95
96
97
98
99 @Deprecated
100 public MultiField(MultiValueFieldDefinition definition, FieldFactoryFactory fieldFactoryFactory, I18nContentSupport i18nContentSupport, ComponentProvider componentProvider, Item relatedFieldItem) {
101 this(definition, fieldFactoryFactory, componentProvider, relatedFieldItem, null);
102 }
103
104 @Override
105 protected Component initContent() {
106
107 addStyleName("linkfield");
108 root = new VerticalLayout();
109 root.setSpacing(true);
110 root.setWidth(100, Unit.PERCENTAGE);
111 root.setHeight(-1, Unit.PIXELS);
112
113
114 addButton.setCaption(buttonCaptionAdd);
115 addButton.addStyleName("magnoliabutton");
116 addButton.addClickListener(new Button.ClickListener() {
117 @Override
118 public void buttonClick(ClickEvent event) {
119 int newPropertyId;
120 Property<?> property = null;
121
122 Transformer<?> transformer = ((TransformedProperty<?>) getPropertyDataSource()).getTransformer();
123 PropertysetItem item = (PropertysetItem) getPropertyDataSource().getValue();
124
125 if (transformer instanceof MultiTransformer) {
126
127 property = ((MultiTransformer) transformer).createProperty();
128 newPropertyId = findPropertyId(item, property);
129 } else {
130
131 newPropertyId = item.getItemPropertyIds().size();
132 }
133
134 if (newPropertyId == -1) {
135 log.warn("Could not resolve new propertyId; cannot add new multifield entry to item '{}'.", item);
136 return;
137 }
138
139 root.addComponent(createEntryComponent(newPropertyId, property), root.getComponentCount() - 1);
140 }
141 });
142
143
144 initFields();
145
146 return root;
147 }
148
149
150
151
152
153 @Override
154 protected void initFields(PropertysetItem newValue) {
155 root.removeAllComponents();
156 for (Object propertyId : newValue.getItemPropertyIds()) {
157 Property<?> property = newValue.getItemProperty(propertyId);
158 root.addComponent(createEntryComponent(propertyId, property));
159 }
160 if (!this.definition.isReadOnly()) {
161 root.addComponent(addButton);
162 }
163 }
164
165
166
167
168
169
170
171 private Component createEntryComponent(Object propertyId, Property<?> property) {
172 final HorizontalLayout layout = new HorizontalLayout();
173 layout.setWidth(100, Unit.PERCENTAGE);
174 layout.setHeight(-1, Unit.PIXELS);
175
176 final Field<?> field = createLocalField(fieldDefinition, property, true);
177 layout.addComponent(field);
178
179
180 if (property == null) {
181 property = field.getPropertyDataSource();
182 ((PropertysetItem) getPropertyDataSource().getValue()).addItemProperty(propertyId, property);
183 }
184 final Property<?> propertyReference = property;
185
186 layout.setWidth(100, Unit.PERCENTAGE);
187
188
189 layout.setExpandRatio(field, 1);
190 if (definition.isReadOnly()) {
191 return layout;
192 }
193
194
195 Button moveUpButton = new Button();
196 moveUpButton.setHtmlContentAllowed(true);
197 moveUpButton.setCaption("<span class=\"" + "icon-arrow2_n" + "\"></span>");
198 moveUpButton.addStyleName("inline");
199 moveUpButton.setDescription(buttonCaptionMoveUp);
200 moveUpButton.addClickListener(new Button.ClickListener() {
201
202 @Override
203 public void buttonClick(Button.ClickEvent event) {
204 onMove(layout, propertyReference, true);
205 }
206 });
207
208
209 Button moveDownButton = new Button();
210 moveDownButton.setHtmlContentAllowed(true);
211 moveDownButton.setCaption("<span class=\"" + "icon-arrow2_s" + "\"></span>");
212 moveDownButton.addStyleName("inline");
213 moveDownButton.setDescription(buttonCaptionMoveDown);
214 moveDownButton.addClickListener(new Button.ClickListener() {
215
216 @Override
217 public void buttonClick(Button.ClickEvent event) {
218 onMove(layout, propertyReference, false);
219 }
220 });
221
222
223
224 Button deleteButton = new Button();
225 deleteButton.setHtmlContentAllowed(true);
226 deleteButton.setCaption("<span class=\"" + "icon-trash" + "\"></span>");
227 deleteButton.addStyleName("inline");
228 deleteButton.setDescription(buttonCaptionRemove);
229 deleteButton.addClickListener(new ClickListener() {
230
231 @Override
232 public void buttonClick(ClickEvent event) {
233 onDelete(layout, propertyReference);
234 }
235 });
236
237 layout.addComponents(moveUpButton, moveDownButton, deleteButton);
238
239
240 layout.setComponentAlignment(deleteButton, Alignment.BOTTOM_RIGHT);
241 layout.setComponentAlignment(moveUpButton, Alignment.BOTTOM_RIGHT);
242 layout.setComponentAlignment(moveDownButton, Alignment.BOTTOM_RIGHT);
243
244 return layout;
245 }
246
247 @Override
248 public Class<? extends PropertysetItem> getType() {
249 return PropertysetItem.class;
250 }
251
252
253
254
255 public void setButtonCaptionAdd(String buttonCaptionAdd) {
256 this.buttonCaptionAdd = buttonCaptionAdd;
257 }
258
259 public void setButtonCaptionRemove(String buttonCaptionRemove) {
260 this.buttonCaptionRemove = buttonCaptionRemove;
261 }
262
263
264
265
266
267
268
269 private void removeValueProperty(int fromIndex) {
270 getValue().removeItemProperty(fromIndex);
271 int valuesSize = getValue().getItemPropertyIds().size();
272 if (fromIndex == valuesSize) {
273 return;
274 }
275 while (fromIndex < valuesSize) {
276 int toIndex = fromIndex;
277 fromIndex +=1;
278 getValue().addItemProperty(toIndex, getValue().getItemProperty(fromIndex));
279 getValue().removeItemProperty(fromIndex);
280 }
281 }
282
283
284
285
286 private void switchItemProperties(Object firstPropertyId, Object secondPropertyId) {
287 Property propertyFirst = getValue().getItemProperty(firstPropertyId);
288 Property propertySecond = getValue().getItemProperty(secondPropertyId);
289
290 try {
291 PropertysetItem storedValues = (PropertysetItem) getValue().clone();
292 if (storedValues != null) {
293 for (Object propertyId : storedValues.getItemPropertyIds()) {
294 getValue().removeItemProperty(propertyId);
295 if (propertyId == firstPropertyId) {
296 getValue().addItemProperty(firstPropertyId, propertySecond);
297 } else if (propertyId == secondPropertyId) {
298 getValue().addItemProperty(secondPropertyId, propertyFirst);
299 } else {
300 getValue().addItemProperty(propertyId, storedValues.getItemProperty(propertyId));
301 }
302 }
303 getPropertyDataSource().setValue(getValue());
304 }
305 } catch (CloneNotSupportedException e) {
306 log.error("Unable to switch properties on MultiField. Unable to clone PropertysetItem.", e);
307 }
308
309 }
310
311 private void onDelete(Component layout, Property<?> propertyReference) {
312 root.removeComponent(layout);
313 Transformer<?> transformer = ((TransformedProperty<?>) getPropertyDataSource()).getTransformer();
314
315
316 Object propertyId = findPropertyId(getValue(), propertyReference);
317
318 if (transformer instanceof MultiTransformer) {
319 ((MultiTransformer) transformer).removeProperty(propertyId);
320 } else {
321 if (propertyId.getClass().isAssignableFrom(Integer.class)) {
322 removeValueProperty((Integer) propertyId);
323 } else {
324 log.error("Property id {} is not an integer and as such property can't be removed", propertyId);
325 }
326 getPropertyDataSource().setValue(getValue());
327 }
328 }
329
330
331
332
333
334 private void onMove(Component layout, Property<?> propertyReference, boolean moveUp) {
335 int currentPosition = root.getComponentIndex(layout);
336 int switchPosition = currentPosition + (moveUp ? -1 : 1);
337
338 Field[] fields = Iterators.toArray(Iterators.filter(Iterators.transform(root.iterator(), new Function<Component, Field>() {
339 @Override
340 public Field apply(Component input) {
341 if (input instanceof HasComponents) {
342 Optional<Component> field = Iterators.tryFind(((HasComponents) input).iterator(), Predicates.instanceOf(Field.class));
343 if (field.isPresent()) {
344 return (Field) field.get();
345 }
346 }
347 return null;
348 }
349 }), Predicates.notNull()), Field.class);
350
351 if (moveUp && currentPosition != 0 || (!moveUp && currentPosition != fields.length - 1)) {
352 Field switchField = fields[switchPosition];
353 Object currentPropertyId = MultiField.this.findPropertyId(getValue(), propertyReference);
354 Object switchPropertyId = MultiField.this.findPropertyId(getValue(), switchField.getPropertyDataSource());
355
356 root.replaceComponent(root.getComponent(currentPosition), root.getComponent(switchPosition));
357 switchItemProperties(currentPropertyId, switchPropertyId);
358 }
359 }
360 }