ContactDetailsRaw: fix bad TYPES handling
[jvcard.git] / src / be / nikiroo / jvcard / tui / panes / ContactDetailsRaw.java
1 package be.nikiroo.jvcard.tui.panes;
2
3 import java.util.LinkedList;
4 import java.util.List;
5
6 import be.nikiroo.jvcard.Contact;
7 import be.nikiroo.jvcard.Data;
8 import be.nikiroo.jvcard.TypeInfo;
9 import be.nikiroo.jvcard.launcher.Main;
10 import be.nikiroo.jvcard.resources.StringUtils;
11 import be.nikiroo.jvcard.resources.enums.ColorOption;
12 import be.nikiroo.jvcard.resources.enums.StringId;
13 import be.nikiroo.jvcard.tui.KeyAction;
14 import be.nikiroo.jvcard.tui.KeyAction.DataType;
15 import be.nikiroo.jvcard.tui.KeyAction.Mode;
16
17 import com.googlecode.lanterna.input.KeyType;
18
19 public class ContactDetailsRaw extends MainContentList {
20 private Contact contact;
21 private boolean extMode;
22
23 public ContactDetailsRaw(Contact contact) {
24 this.contact = contact;
25 this.extMode = false;
26
27 for (int i = 0; i < contact.size(); i++) {
28 addItem("x");
29 }
30 }
31
32 @Override
33 public DataType getDataType() {
34 return DataType.DATA;
35 }
36
37 @Override
38 public List<KeyAction> getKeyBindings() {
39 List<KeyAction> actions = new LinkedList<KeyAction>();
40
41 actions.add(new KeyAction(Mode.ASK_USER, KeyType.Enter,
42 StringId.KEY_ACTION_EDIT_FIELD) {
43 @Override
44 public Object getObject() {
45 Data data = getSelectedData();
46 if (data != null && data.getB64Key() != -1) {
47 setMessage("Cannot modify binary values in RAW mode", true);
48 data = null;
49 }
50
51 return data;
52 }
53
54 @Override
55 public String getQuestion() {
56 Data data = getData();
57 if (data != null) {
58 return data.getName();
59 }
60
61 return null;
62 }
63
64 @Override
65 public String getDefaultAnswer() {
66 Data data = getData();
67 if (data != null) {
68 return data.getRawValue().replaceAll("\n", "\\\\n");
69 }
70
71 return null;
72 }
73
74 @Override
75 public String callback(String answer) {
76 Data data = getData();
77 if (data != null) {
78 data.setRawValue(answer.replaceAll("\\\\n", "\n"));
79 return null;
80 }
81
82 // TODO: i18n
83 return "Cannot modify value";
84 }
85 });
86 actions.add(new KeyAction(Mode.ASK_USER_KEY, 'd',
87 StringId.KEY_ACTION_DELETE) {
88 @Override
89 public Object getObject() {
90 return getSelectedData();
91 }
92
93 @Override
94 public String getQuestion() {
95 // TODO i18n
96 return "Delete data? [Y/N]";
97 }
98
99 @Override
100 public String callback(String answer) {
101 if (answer.equalsIgnoreCase("y")) {
102 Data data = getData();
103 if (data != null && data.delete()) {
104 removeItem("x");
105 return null;
106 }
107
108 // TODO i18n
109 return "Cannot delete data";
110 }
111
112 return null;
113 }
114 });
115 // TODO: ui
116 actions.add(new KeyAction(Mode.ASK_USER, 'a', StringId.KEY_ACTION_ADD) {
117 @Override
118 public Object getObject() {
119 return contact;
120 }
121
122 @Override
123 public String getQuestion() {
124 // TODO i18n
125 return "new data (xx.group = yy): ";
126 }
127
128 @Override
129 public String callback(String answer) {
130 int indexEq = answer.indexOf('=');
131 if (indexEq >= 0) {
132 String name = answer.substring(0, indexEq).trim();
133 String value = answer.substring(indexEq + 1).trim();
134 String group = null;
135
136 int indexDt = name.indexOf('.');
137 if (indexDt >= 0) {
138 group = name.substring(indexDt + 1).trim();
139 name = name.substring(0, indexDt).trim();
140 }
141
142 Data data = new Data(null, name, value, group);
143 getContact().add(data);
144 addItem("x");
145 }
146 return null;
147 }
148 });
149 // TODO: use a real UI for this, not a simple text box (a list or
150 // something, maybe a whole new pane?)
151 actions.add(new KeyAction(Mode.ASK_USER, 't', StringId.DUMMY) {
152 private String previous;
153
154 @Override
155 public Object getObject() {
156 return getSelectedData();
157 }
158
159 @Override
160 public String getQuestion() {
161 Data data = getData();
162 if (data != null) {
163 return data.getName();
164 }
165
166 return null;
167 }
168
169 @Override
170 public String getDefaultAnswer() {
171 Data data = getData();
172 if (data != null) {
173 previous = typesToString(data, null).toString();
174 return previous;
175 }
176
177 return null;
178 }
179
180 @Override
181 public String callback(String answer) {
182 Data data = getData();
183 if (data != null) {
184 if (!answer.equals(previous)) {
185 data.replaceListContent(stringToTypes(answer));
186 }
187 return null;
188 }
189
190 // TODO: i18n
191 return "Cannot modify value";
192 }
193 });
194 actions.add(new KeyAction(Mode.ASK_USER, 'g', StringId.DUMMY) {
195 private String previous;
196
197 @Override
198 public Object getObject() {
199 return getSelectedData();
200 }
201
202 @Override
203 public String getQuestion() {
204 Data data = getData();
205 if (data != null) {
206 return data.getName();
207 }
208
209 return null;
210 }
211
212 @Override
213 public String getDefaultAnswer() {
214 Data data = getData();
215 if (data != null) {
216 previous = data.getGroup();
217 return previous;
218 }
219
220 return null;
221 }
222
223 @Override
224 public String callback(String answer) {
225 Data data = getData();
226 if (data != null) {
227 if (!answer.equals(previous)) {
228 data.setGroup(answer);
229 }
230 return null;
231 }
232
233 // TODO: i18n
234 return "Cannot modify group";
235 }
236 });
237 actions.add(new KeyAction(Mode.NONE, KeyType.Tab,
238 StringId.KEY_ACTION_SWITCH_FORMAT) {
239 @Override
240 public boolean onAction() {
241 extMode = !extMode;
242 return false;
243 }
244 });
245
246 return actions;
247 }
248
249 @Override
250 public String getTitle() {
251 String title = null;
252
253 if (contact != null) {
254 title = contact.getPreferredDataValue("FN");
255 if (title == null || title.length() == 0)
256 title = contact.getPreferredDataValue("N");
257 }
258
259 return title;
260 }
261
262 @Override
263 public String move(int x, int y) {
264 // TODO Auto-generated method stub
265 return null;
266 }
267
268 @Override
269 protected List<TextPart> getLabel(int index, int width, boolean selected,
270 boolean focused) {
271
272 // TODO: from ini file?
273 int SIZE_COL_1 = 15;
274 int SIZE_COL_2_OPT = 10;
275
276 if (!extMode)
277 SIZE_COL_2_OPT = 0;
278
279 List<TextPart> parts = new LinkedList<TextPart>();
280 Data data = null;
281 if (index > -1 && index < contact.size())
282 data = contact.get(index);
283
284 if (data == null)
285 return parts;
286
287 ColorOption el = (focused && selected) ? ColorOption.CONTACT_LINE_SELECTED
288 : ColorOption.CONTACT_LINE;
289 ColorOption elSep = (focused && selected) ? ColorOption.CONTACT_LINE_SEPARATOR_SELECTED
290 : ColorOption.CONTACT_LINE_SEPARATOR;
291 ColorOption elDirty = (focused && selected) ? ColorOption.CONTACT_LINE_DIRTY_SELECTED
292 : ColorOption.CONTACT_LINE_DIRTY;
293
294 if (data.isDirty()) {
295 parts.add(new TextPart(" ", el));
296 parts.add(new TextPart("*", elDirty));
297 } else {
298 parts.add(new TextPart(" ", elSep));
299 }
300 String name = " " + data.getName() + " ";
301 String value = null;
302 String group = null;
303
304 StringBuilder valueBuilder = new StringBuilder(" ");
305 if (!extMode) {
306 if (data.getB64Key() != -1) {
307 // TODO: i18n
308 valueBuilder.append("<BKey " + data.getB64Key() + ">");
309 } else {
310 valueBuilder.append(data.getValue().replaceAll("\n", "\\\\n"));
311 }
312 if (data.getGroup() != null && data.getGroup().length() > 0) {
313 valueBuilder.append("(");
314 valueBuilder.append(data.getGroup());
315 valueBuilder.append(")");
316 }
317 } else {
318 group = data.getGroup();
319 if (group == null)
320 group = "";
321
322 typesToString(data, valueBuilder);
323 }
324 valueBuilder.append(" ");
325
326 value = valueBuilder.toString();
327
328 name = StringUtils.sanitize(name, Main.isUnicode());
329 value = StringUtils.sanitize(value, Main.isUnicode());
330
331 name = StringUtils.padString(name, SIZE_COL_1);
332 group = StringUtils.padString(group, SIZE_COL_2_OPT);
333 value = StringUtils.padString(value, width - SIZE_COL_1
334 - SIZE_COL_2_OPT - (extMode ? 2 : 1) * getSeparator().length()
335 - 2);
336
337 parts.add(new TextPart(name, el));
338 parts.add(new TextPart(getSeparator(), elSep));
339 parts.add(new TextPart(value, el));
340 if (extMode) {
341 parts.add(new TextPart(getSeparator(), elSep));
342 parts.add(new TextPart(group, el));
343 }
344
345 return parts;
346 }
347
348 /**
349 * Return the currently selected {@link Data}.
350 *
351 * @return the currently selected {@link Data}
352 */
353 private Data getSelectedData() {
354 int index = getSelectedIndex();
355 if (index > -1 && index < this.contact.size())
356 return contact.get(index);
357 return null;
358 }
359
360 /**
361 * Serialise the {@link TypeInfo}s in the given {@link Data}.
362 *
363 * @param data
364 * the {@link Data} from which to take the {@link TypeInfo}s
365 * @param builder
366 * an optional {@link StringBuilder} to append the serialised
367 * version to
368 *
369 * @return the given {@link StringBuilder} or a new one if the given one is
370 * NULL
371 */
372 static private StringBuilder typesToString(Data data, StringBuilder builder) {
373 if (builder == null)
374 builder = new StringBuilder();
375
376 for (TypeInfo type : data) {
377 if (builder.length() > 1)
378 builder.append(", ");
379 builder.append(type.getName().replaceAll(",", "\\,"));
380 builder.append(": ");
381 builder.append(type.getValue().replaceAll(":", "\\:"));
382 }
383
384 return builder;
385 }
386
387 /**
388 * Unserialise a list of {@link TypeInfo}s.
389 *
390 * @param value
391 * the serialised value
392 *
393 * @return the {@link TypeInfo} in their object form
394 */
395 static private List<TypeInfo> stringToTypes(String value) {
396 List<TypeInfo> infos = new LinkedList<TypeInfo>();
397 if (value == null || value.length() == 0)
398 return infos;
399
400 char previous = '\0';
401 char car = '\0';
402 int done = 0;
403 for (int index = 0; index < value.length(); index++) {
404 car = value.charAt(index);
405 if (index == value.length() - 1) {
406 index++;
407 previous = '\0';
408 car = ',';
409 }
410
411 if (previous != '\\' && car == ',') {
412 String subValue = value.substring(done, index);
413 int indexColumn = subValue.indexOf(':');
414 while (indexColumn > 0
415 && subValue.charAt(indexColumn - 1) == '\\') {
416 if (indexColumn == subValue.length() - 1) {
417 indexColumn = -1;
418 } else {
419 indexColumn = subValue.indexOf(':', indexColumn + 1);
420 }
421 }
422
423 String n = "";
424 String v = "";
425 if (indexColumn >= 0) {
426 n = subValue.substring(0, indexColumn).trim();
427 v = subValue.substring(indexColumn + 1)
428 .replaceAll("\\\\:", ":").trim();
429 } else {
430 n = subValue.trim();
431 }
432
433 infos.add(new TypeInfo(n, v));
434
435 done = index + 1;
436 }
437
438 previous = car;
439 }
440
441 return infos;
442 }
443 }