1 package be
.nikiroo
.utils
.ui
;
3 import java
.util
.ArrayList
;
4 import java
.util
.HashMap
;
8 import be
.nikiroo
.utils
.resources
.Bundle
;
9 import be
.nikiroo
.utils
.resources
.MetaInfo
;
12 * A graphical item that reflect a configuration option from the given
15 * This graphical item can be edited, and the result will be saved back into the
16 * linked {@link MetaInfo}; you still have to save the {@link MetaInfo} should
17 * you wish to, of course.
22 * the graphical base type to use (i.e., T or TWidget)
24 * the type of {@link Bundle} to edit
26 public abstract class ConfigItemBase
<T
, E
extends Enum
<E
>> {
27 /** The original value before current changes. */
29 private List
<Object
> origs
= new ArrayList
<Object
>();
30 private List
<Integer
> dirtyBits
;
32 /** The fields (one for non-array, a list for arrays). */
34 private List
<T
> fields
= new ArrayList
<T
>();
36 /** The fields to panel map to get the actual item added to 'main'. */
37 private Map
<Integer
, T
> itemFields
= new HashMap
<Integer
, T
>();
39 /** The {@link MetaInfo} linked to the field. */
40 private MetaInfo
<E
> info
;
42 /** The {@link MetaInfo} linked to the field. */
43 public MetaInfo
<E
> getInfo() {
48 * The number of fields, for arrays.
52 public int getFieldsSize() {
57 * The number of fields to panel map to get the actual item added to 'main'.
59 public int getItemFieldsSize() {
60 return itemFields
.size();
64 * Add a new item in an array-value {@link MetaInfo}.
67 * the index of the new item
69 * a linked T, if we want to link it into the itemFields (can be
70 * NULL) -- that way, we can get it back later on
71 * {@link ConfigItemBase#removeItem(int)}
73 * @return the newly created graphical field
75 public T
addItem(final int item
, T panel
) {
77 itemFields
.put(item
, panel
);
79 return createField(item
);
83 * The counter-part to {@link ConfigItemBase#addItem(int, Object)}, to
84 * remove a specific item of an array-values {@link MetaInfo}; all the
85 * remaining items will be shifted as required (so, always the last
86 * graphical object will be removed).
89 * the index of the item to remove
91 * @return the linked graphical T to remove if any (always the latest
92 * graphical object if any)
94 protected T
removeItem(int item
) {
95 int last
= itemFields
.size() - 1;
97 for (int i
= item
; i
<= last
; i
++) {
100 value
= getFromField(i
+ 1);
102 setToField(value
, i
);
107 return itemFields
.remove(last
);
111 * Prepare a new {@link ConfigItemBase} instance, linked to the given
116 * @param autoDirtyHandling
117 * TRUE to automatically manage the setDirty/Save operations,
118 * FALSE if you want to do it yourself via
119 * {@link ConfigItemBase#setDirtyItem(int)}
121 protected ConfigItemBase(MetaInfo
<E
> info
, boolean autoDirtyHandling
) {
123 if (!autoDirtyHandling
) {
124 dirtyBits
= new ArrayList
<Integer
>();
129 * Create an empty graphical component to be used later by
130 * {@link ConfigItemBase#createField(int)}.
132 * Note that {@link ConfigItemBase#reload(int)} will be called after it was
133 * created by {@link ConfigItemBase#createField(int)}.
136 * the item number to get for an array of values, or -1 to get
137 * the whole value (has no effect if {@link MetaInfo#isArray()}
140 * @return the graphical component
142 abstract protected T
createEmptyField(int item
);
145 * Get the information from the {@link MetaInfo} in the subclass preferred
149 * the item number to get for an array of values, or -1 to get
150 * the whole value (has no effect if {@link MetaInfo#isArray()}
153 * @return the information in the subclass preferred format
155 abstract protected Object
getFromInfo(int item
);
158 * Set the value to the {@link MetaInfo}.
161 * the value in the subclass preferred format
163 * the item number to get for an array of values, or -1 to get
164 * the whole value (has no effect if {@link MetaInfo#isArray()}
167 abstract protected void setToInfo(Object value
, int item
);
170 * The value present in the given item's related field in the subclass
174 * the item number to get for an array of values, or -1 to get
175 * the whole value (has no effect if {@link MetaInfo#isArray()}
178 * @return the value present in the given item's related field in the
179 * subclass preferred format
181 abstract protected Object
getFromField(int item
);
184 * Set the value (in the subclass preferred format) into the field.
187 * the value in the subclass preferred format
189 * the item number to get for an array of values, or -1 to get
190 * the whole value (has no effect if {@link MetaInfo#isArray()}
193 abstract protected void setToField(Object value
, int item
);
196 * Create a new field for the given graphical component at the given index
197 * (note that the component is usually created by
198 * {@link ConfigItemBase#createEmptyField(int)}).
201 * the item number to get for an array of values, or -1 to get
202 * the whole value (has no effect if {@link MetaInfo#isArray()}
205 * the graphical component
207 private void setField(int item
, T field
) {
213 for (int i
= fields
.size(); i
<= item
; i
++) {
217 fields
.set(item
, field
);
221 * Retrieve the associated graphical component that was created with
222 * {@link ConfigItemBase#createEmptyField(int)}.
225 * the item number to get for an array of values, or -1 to get
226 * the whole value (has no effect if {@link MetaInfo#isArray()}
229 * @return the graphical component
231 public T
getField(int item
) {
236 if (item
< fields
.size()) {
237 return fields
.get(item
);
244 * The original value (before any changes to the {@link MetaInfo}) for this
248 * the item number to get for an array of values, or -1 to get
249 * the whole value (has no effect if {@link MetaInfo#isArray()}
252 * @return the original value
254 private Object
getOrig(int item
) {
259 if (item
< origs
.size()) {
260 return origs
.get(item
);
267 * The original value (before any changes to the {@link MetaInfo}) for this
271 * the item number to get for an array of values, or -1 to get
272 * the whole value (has no effect if {@link MetaInfo#isArray()}
275 * the new original value
277 private void setOrig(Object value
, int item
) {
281 while (item
>= origs
.size()) {
285 origs
.set(item
, value
);
290 * Manually specify that the given item is "dirty" and thus should be saved
293 * Has no effect if the class is using automatic dirty handling (see
294 * {@link ConfigItemBase#ConfigItem(MetaInfo, boolean)}).
297 * the item number to get for an array of values, or -1 to get
298 * the whole value (has no effect if {@link MetaInfo#isArray()}
301 public void setDirtyItem(int item
) {
302 if (dirtyBits
!= null) {
308 * Check if the value changed since the last load/save into the linked
311 * Note that we consider NULL and an Empty {@link String} to be equals.
316 * the item number to get for an array of values, or -1 to get
317 * the whole value (has no effect if {@link MetaInfo#isArray()}
320 * @return TRUE if it has
322 public boolean hasValueChanged(Object value
, int item
) {
323 // We consider "" and NULL to be equals
324 Object orig
= getOrig(item
);
328 return !orig
.equals(value
== null ?
"" : value
);
332 * Reload the values to what they currently are in the {@link MetaInfo}.
334 * @return for arrays, the list of graphical T objects we don't need any
335 * more (never NULL, but can be empty)
337 public List
<T
> reload() {
338 List
<T
> removed
= new ArrayList
<T
>();
339 if (info
.isArray()) {
340 while (!itemFields
.isEmpty()) {
341 removed
.add(itemFields
.remove(itemFields
.size() - 1));
343 for (int item
= 0; item
< info
.getListSize(false); item
++) {
354 * Reload the values to what they currently are in the {@link MetaInfo}.
357 * the item number to get for an array of values, or -1 to get
358 * the whole value (has no effect if {@link MetaInfo#isArray()}
361 private void reload(int item
) {
362 if (item
>= 0 && !itemFields
.containsKey(item
)) {
366 Object value
= getFromInfo(item
);
367 setToField(value
, item
);
368 setOrig(value
== null ?
"" : value
, item
);
372 * If the item has been modified, set the {@link MetaInfo} to dirty then
373 * modify it to, reflect the changes so it can be saved later.
375 * This method does <b>not</b> call {@link MetaInfo#save(boolean)}.
377 private void save() {
378 if (info
.isArray()) {
379 boolean dirty
= itemFields
.size() != info
.getListSize(false);
380 for (int item
= 0; item
< itemFields
.size(); item
++) {
381 if (getDirtyBit(item
)) {
388 info
.setString(null, -1);
390 for (int item
= 0; item
< itemFields
.size(); item
++) {
392 if (getField(item
) != null) {
393 value
= getFromField(item
);
394 if ("".equals(value
)) {
399 setToInfo(value
, item
);
400 setOrig(value
, item
);
404 if (getDirtyBit(-1)) {
405 Object value
= getFromField(-1);
408 setToInfo(value
, -1);
415 * Check if the item is dirty, and clear the dirty bit if set.
418 * the item number to get for an array of values, or -1 to get
419 * the whole value (has no effect if {@link MetaInfo#isArray()}
422 * @return TRUE if it was dirty, FALSE if not
424 private boolean getDirtyBit(int item
) {
425 if (dirtyBits
!= null) {
426 return dirtyBits
.remove((Integer
) item
);
430 if (getField(item
) != null) {
431 value
= getFromField(item
);
434 return hasValueChanged(value
, item
);
438 * Create a new field for the given item.
441 * the item number to get for an array of values, or -1 to get
442 * the whole value (has no effect if {@link MetaInfo#isArray()}
445 * @return the newly created field
447 public T
createField(final int item
) {
448 T field
= createEmptyField(item
);
449 setField(item
, field
);
452 info
.addReloadedListener(new Runnable() {
458 info
.addSaveListener(new Runnable() {