Merge branch 'subtree'
[nikiroo-utils.git] / src / be / nikiroo / fanfix / reader / tui / ConfigItem.java
1 package be.nikiroo.fanfix.reader.tui;
2
3 import java.util.List;
4
5 import jexer.TAction;
6 import jexer.TButton;
7 import jexer.TLabel;
8 import jexer.TPanel;
9 import jexer.TWidget;
10 import be.nikiroo.utils.resources.Bundle;
11 import be.nikiroo.utils.resources.MetaInfo;
12 import be.nikiroo.utils.ui.ConfigItemBase;
13
14 /**
15 * A graphical item that reflect a configuration option from the given
16 * {@link Bundle}.
17 * <p>
18 * This graphical item can be edited, and the result will be saved back into the
19 * linked {@link MetaInfo}; you still have to save the {@link MetaInfo} should
20 * you wish to, of course.
21 *
22 * @author niki
23 *
24 * @param <E>
25 * the type of {@link Bundle} to edit
26 */
27 public abstract class ConfigItem<E extends Enum<E>> extends TWidget {
28 /** The code base */
29 private final ConfigItemBase<TWidget, E> base;
30
31 /**
32 * Prepare a new {@link ConfigItem} instance, linked to the given
33 * {@link MetaInfo}.
34 *
35 * @param parent
36 * the parent widget
37 * @param info
38 * the info
39 * @param autoDirtyHandling
40 * TRUE to automatically manage the setDirty/Save operations,
41 * FALSE if you want to do it yourself via
42 * {@link ConfigItem#setDirtyItem(int)}
43 */
44 protected ConfigItem(TWidget parent, MetaInfo<E> info,
45 boolean autoDirtyHandling) {
46 super(parent);
47
48 base = new ConfigItemBase<TWidget, E>(info, autoDirtyHandling) {
49 @Override
50 protected TWidget createEmptyField(int item) {
51 return ConfigItem.this.createEmptyField(item);
52 }
53
54 @Override
55 protected Object getFromInfo(int item) {
56 return ConfigItem.this.getFromInfo(item);
57 }
58
59 @Override
60 protected void setToInfo(Object value, int item) {
61 ConfigItem.this.setToInfo(value, item);
62 }
63
64 @Override
65 protected Object getFromField(int item) {
66 return ConfigItem.this.getFromField(item);
67 }
68
69 @Override
70 protected void setToField(Object value, int item) {
71 ConfigItem.this.setToField(value, item);
72 }
73
74 @Override
75 public TWidget createField(int item) {
76 TWidget field = super.createField(item);
77
78 // TODO: size?
79
80 return field;
81 }
82
83 @Override
84 public List<TWidget> reload() {
85 List<TWidget> removed = base.reload();
86 if (!removed.isEmpty()) {
87 for (TWidget c : removed) {
88 removeChild(c);
89 }
90 }
91
92 return removed;
93 }
94 };
95 }
96
97 /**
98 * Create a new {@link ConfigItem} for the given {@link MetaInfo}.
99 *
100 * @param nhgap
101 * negative horisontal gap in pixel to use for the label, i.e.,
102 * the step lock sized labels will start smaller by that amount
103 * (the use case would be to align controls that start at a
104 * different horisontal position)
105 */
106 public void init(int nhgap) {
107 if (getInfo().isArray()) {
108 // TODO: width
109 int size = getInfo().getListSize(false);
110 final TPanel pane = new TPanel(this, 0, 0, 20, size + 2);
111 final TWidget label = label(0, 0, nhgap);
112 label.setParent(pane, false);
113 setHeight(pane.getHeight());
114
115 for (int i = 0; i < size; i++) {
116 // TODO: minusPanel
117 TWidget field = base.addItem(i, null);
118 field.setParent(pane, false);
119 field.setX(label.getWidth() + 1);
120 field.setY(i);
121 }
122
123 // x, y
124 final TButton add = new TButton(pane, "+", label.getWidth() + 1,
125 size + 1, null);
126 TAction action = new TAction() {
127 @Override
128 public void DO() {
129 TWidget field = base.addItem(base.getFieldsSize(), null);
130 field.setParent(pane, false);
131 field.setX(label.getWidth() + 1);
132 field.setY(add.getY());
133 add.setY(add.getY() + 1);
134 }
135 };
136 add.setAction(action);
137 } else {
138 final TWidget label = label(0, 0, nhgap);
139
140 TWidget field = base.createField(-1);
141 field.setX(label.getWidth() + 1);
142 field.setWidth(10); // TODO
143
144 // TODO
145 setWidth(30);
146 setHeight(1);
147 }
148 }
149
150 /** The {@link MetaInfo} linked to the field. */
151 public MetaInfo<E> getInfo() {
152 return base.getInfo();
153 }
154
155 /**
156 * Retrieve the associated graphical component that was created with
157 * {@link ConfigItemBase#createEmptyField(int)}.
158 *
159 * @param item
160 * the item number to get for an array of values, or -1 to get
161 * the whole value (has no effect if {@link MetaInfo#isArray()}
162 * is FALSE)
163 *
164 * @return the graphical component
165 */
166 protected TWidget getField(int item) {
167 return base.getField(item);
168 }
169
170 /**
171 * Manually specify that the given item is "dirty" and thus should be saved
172 * when asked.
173 * <p>
174 * Has no effect if the class is using automatic dirty handling (see
175 * {@link ConfigItemBase#ConfigItem(MetaInfo, boolean)}).
176 *
177 * @param item
178 * the item number to get for an array of values, or -1 to get
179 * the whole value (has no effect if {@link MetaInfo#isArray()}
180 * is FALSE)
181 */
182 protected void setDirtyItem(int item) {
183 base.setDirtyItem(item);
184 }
185
186 /**
187 * Check if the value changed since the last load/save into the linked
188 * {@link MetaInfo}.
189 * <p>
190 * Note that we consider NULL and an Empty {@link String} to be equals.
191 *
192 * @param value
193 * the value to test
194 * @param item
195 * the item number to get for an array of values, or -1 to get
196 * the whole value (has no effect if {@link MetaInfo#isArray()}
197 * is FALSE)
198 *
199 * @return TRUE if it has
200 */
201 protected boolean hasValueChanged(Object value, int item) {
202 return base.hasValueChanged(value, item);
203 }
204
205 /**
206 * Create an empty graphical component to be used later by
207 * {@link ConfigItem#createField(int)}.
208 * <p>
209 * Note that {@link ConfigItem#reload(int)} will be called after it was
210 * created by {@link ConfigItem#createField(int)}.
211 *
212 * @param item
213 * the item number to get for an array of values, or -1 to get
214 * the whole value (has no effect if {@link MetaInfo#isArray()}
215 * is FALSE)
216 *
217 * @return the graphical component
218 */
219 abstract protected TWidget createEmptyField(int item);
220
221 /**
222 * Get the information from the {@link MetaInfo} in the subclass preferred
223 * format.
224 *
225 * @param item
226 * the item number to get for an array of values, or -1 to get
227 * the whole value (has no effect if {@link MetaInfo#isArray()}
228 * is FALSE)
229 *
230 * @return the information in the subclass preferred format
231 */
232 abstract protected Object getFromInfo(int item);
233
234 /**
235 * Set the value to the {@link MetaInfo}.
236 *
237 * @param value
238 * the value in the subclass preferred format
239 * @param item
240 * the item number to get for an array of values, or -1 to get
241 * the whole value (has no effect if {@link MetaInfo#isArray()}
242 * is FALSE)
243 */
244 abstract protected void setToInfo(Object value, int item);
245
246 /**
247 * The value present in the given item's related field in the subclass
248 * preferred format.
249 *
250 * @param item
251 * the item number to get for an array of values, or -1 to get
252 * the whole value (has no effect if {@link MetaInfo#isArray()}
253 * is FALSE)
254 *
255 * @return the value present in the given item's related field in the
256 * subclass preferred format
257 */
258 abstract protected Object getFromField(int item);
259
260 /**
261 * Set the value (in the subclass preferred format) into the field.
262 *
263 * @param value
264 * the value in the subclass preferred format
265 * @param item
266 * the item number to get for an array of values, or -1 to get
267 * the whole value (has no effect if {@link MetaInfo#isArray()}
268 * is FALSE)
269 */
270 abstract protected void setToField(Object value, int item);
271
272 /**
273 * Create a label which width is constrained in lock steps.
274 *
275 * @param x
276 * the X position of the label
277 * @param y
278 * the Y position of the label
279 * @param nhgap
280 * negative horisontal gap in pixel to use for the label, i.e.,
281 * the step lock sized labels will start smaller by that amount
282 * (the use case would be to align controls that start at a
283 * different horisontal position)
284 *
285 * @return the label
286 */
287 protected TWidget label(int x, int y, int nhgap) {
288 // TODO: see Swing version for lock-step sizes
289 // TODO: see Swing version for help info-buttons
290
291 String lbl = getInfo().getName();
292 return new TLabel(this, lbl, x, y);
293 }
294
295 /**
296 * Create a new {@link ConfigItem} for the given {@link MetaInfo}.
297 *
298 * @param <E>
299 * the type of {@link Bundle} to edit
300 *
301 * @param x
302 * the X position of the item
303 * @param y
304 * the Y position of the item
305 * @param parent
306 * the parent widget to use for this one
307 * @param info
308 * the {@link MetaInfo}
309 * @param nhgap
310 * negative horisontal gap in pixel to use for the label, i.e.,
311 * the step lock sized labels will start smaller by that amount
312 * (the use case would be to align controls that start at a
313 * different horisontal position)
314 *
315 * @return the new {@link ConfigItem}
316 */
317 static public <E extends Enum<E>> ConfigItem<E> createItem(TWidget parent,
318 int x, int y, MetaInfo<E> info, int nhgap) {
319
320 ConfigItem<E> configItem;
321 switch (info.getFormat()) {
322 // TODO
323 // case BOOLEAN:
324 // configItem = new ConfigItemBoolean<E>(info);
325 // break;
326 // case COLOR:
327 // configItem = new ConfigItemColor<E>(info);
328 // break;
329 // case FILE:
330 // configItem = new ConfigItemBrowse<E>(info, false);
331 // break;
332 // case DIRECTORY:
333 // configItem = new ConfigItemBrowse<E>(info, true);
334 // break;
335 // case COMBO_LIST:
336 // configItem = new ConfigItemCombobox<E>(info, true);
337 // break;
338 // case FIXED_LIST:
339 // configItem = new ConfigItemCombobox<E>(info, false);
340 // break;
341 // case INT:
342 // configItem = new ConfigItemInteger<E>(info);
343 // break;
344 // case PASSWORD:
345 // configItem = new ConfigItemPassword<E>(info);
346 // break;
347 // case LOCALE:
348 // configItem = new ConfigItemLocale<E>(info);
349 // break;
350 // case STRING:
351 default:
352 configItem = new ConfigItemString<E>(parent, info);
353 break;
354 }
355
356 configItem.init(nhgap);
357 configItem.setX(x);
358 configItem.setY(y);
359
360 return configItem;
361 }
362 }