ContactDetailsRaw: fix bad TYPES handling
[jvcard.git] / src / be / nikiroo / jvcard / tui / panes / ContactDetailsRaw.java
CommitLineData
fae07ea7
NR
1package be.nikiroo.jvcard.tui.panes;
2
bcb54330 3import java.util.LinkedList;
fae07ea7
NR
4import java.util.List;
5
6import be.nikiroo.jvcard.Contact;
7import be.nikiroo.jvcard.Data;
bcb54330 8import be.nikiroo.jvcard.TypeInfo;
7da41ecd
NR
9import be.nikiroo.jvcard.launcher.Main;
10import be.nikiroo.jvcard.resources.StringUtils;
e119a1c1
NR
11import be.nikiroo.jvcard.resources.enums.ColorOption;
12import be.nikiroo.jvcard.resources.enums.StringId;
fae07ea7
NR
13import be.nikiroo.jvcard.tui.KeyAction;
14import be.nikiroo.jvcard.tui.KeyAction.DataType;
15import be.nikiroo.jvcard.tui.KeyAction.Mode;
16
78e4af97
NR
17import com.googlecode.lanterna.input.KeyType;
18
296a0b75 19public class ContactDetailsRaw extends MainContentList {
fae07ea7 20 private Contact contact;
176a8327 21 private boolean extMode;
fae07ea7 22
296a0b75 23 public ContactDetailsRaw(Contact contact) {
fae07ea7 24 this.contact = contact;
176a8327 25 this.extMode = false;
fae07ea7 26
78e4af97 27 for (int i = 0; i < contact.size(); i++) {
176a8327 28 addItem("x");
fae07ea7
NR
29 }
30 }
31
32 @Override
33 public DataType getDataType() {
bcb54330 34 return DataType.DATA;
fae07ea7 35 }
ae22c247 36
fae07ea7
NR
37 @Override
38 public List<KeyAction> getKeyBindings() {
bcb54330
NR
39 List<KeyAction> actions = new LinkedList<KeyAction>();
40
78e4af97 41 actions.add(new KeyAction(Mode.ASK_USER, KeyType.Enter,
88eb8122 42 StringId.KEY_ACTION_EDIT_FIELD) {
bcb54330
NR
43 @Override
44 public Object getObject() {
59597d59
NR
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 }
bed93753 50
59597d59 51 return data;
bcb54330 52 }
ae22c247
NR
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) {
30a4aa17 68 return data.getRawValue().replaceAll("\n", "\\\\n");
ae22c247
NR
69 }
70
71 return null;
72 }
73
74 @Override
75 public String callback(String answer) {
76 Data data = getData();
77 if (data != null) {
30a4aa17 78 data.setRawValue(answer.replaceAll("\\\\n", "\n"));
ae22c247
NR
79 return null;
80 }
81
82 // TODO: i18n
83 return "Cannot modify value";
84 }
bcb54330 85 });
88eb8122
NR
86 actions.add(new KeyAction(Mode.ASK_USER_KEY, 'd',
87 StringId.KEY_ACTION_DELETE) {
176a8327
NR
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
88eb8122 116 actions.add(new KeyAction(Mode.ASK_USER, 'a', StringId.KEY_ACTION_ADD) {
176a8327
NR
117 @Override
118 public Object getObject() {
119 return contact;
120 }
121
122 @Override
123 public String getQuestion() {
124 // TODO i18n
b9f192ed 125 return "new data (xx.group = yy): ";
176a8327
NR
126 }
127
128 @Override
129 public String callback(String answer) {
b9f192ed
NR
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);
176a8327
NR
143 getContact().add(data);
144 addItem("x");
145 }
176a8327
NR
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?)
e119a1c1 151 actions.add(new KeyAction(Mode.ASK_USER, 't', StringId.DUMMY) {
176a8327
NR
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)) {
26d2bd05 185 data.replaceListContent(stringToTypes(answer));
176a8327
NR
186 }
187 return null;
188 }
189
190 // TODO: i18n
191 return "Cannot modify value";
192 }
193 });
e119a1c1 194 actions.add(new KeyAction(Mode.ASK_USER, 'g', StringId.DUMMY) {
176a8327
NR
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 });
bcb54330 237 actions.add(new KeyAction(Mode.NONE, KeyType.Tab,
e119a1c1 238 StringId.KEY_ACTION_SWITCH_FORMAT) {
bcb54330
NR
239 @Override
240 public boolean onAction() {
176a8327 241 extMode = !extMode;
bcb54330
NR
242 return false;
243 }
244 });
245
246 return actions;
fae07ea7
NR
247 }
248
fae07ea7
NR
249 @Override
250 public String getTitle() {
0b0b2b0f
NR
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;
fae07ea7
NR
260 }
261
262 @Override
263 public String move(int x, int y) {
264 // TODO Auto-generated method stub
265 return null;
266 }
f82bad11
NR
267
268 @Override
269 protected List<TextPart> getLabel(int index, int width, boolean selected,
270 boolean focused) {
59597d59 271
f82bad11
NR
272 // TODO: from ini file?
273 int SIZE_COL_1 = 15;
176a8327
NR
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;
f82bad11 286
e119a1c1
NR
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;
f82bad11 293
f82bad11
NR
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;
176a8327 302 String group = null;
f82bad11
NR
303
304 StringBuilder valueBuilder = new StringBuilder(" ");
176a8327 305 if (!extMode) {
59597d59
NR
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 }
f82bad11
NR
312 if (data.getGroup() != null && data.getGroup().length() > 0) {
313 valueBuilder.append("(");
314 valueBuilder.append(data.getGroup());
315 valueBuilder.append(")");
316 }
176a8327
NR
317 } else {
318 group = data.getGroup();
319 if (group == null)
320 group = "";
321
322 typesToString(data, valueBuilder);
f82bad11
NR
323 }
324 valueBuilder.append(" ");
325
326 value = valueBuilder.toString();
327
7da41ecd
NR
328 name = StringUtils.sanitize(name, Main.isUnicode());
329 value = StringUtils.sanitize(value, Main.isUnicode());
f82bad11
NR
330
331 name = StringUtils.padString(name, SIZE_COL_1);
176a8327 332 group = StringUtils.padString(group, SIZE_COL_2_OPT);
f82bad11 333 value = StringUtils.padString(value, width - SIZE_COL_1
176a8327
NR
334 - SIZE_COL_2_OPT - (extMode ? 2 : 1) * getSeparator().length()
335 - 2);
f82bad11
NR
336
337 parts.add(new TextPart(name, el));
338 parts.add(new TextPart(getSeparator(), elSep));
339 parts.add(new TextPart(value, el));
176a8327
NR
340 if (extMode) {
341 parts.add(new TextPart(getSeparator(), elSep));
342 parts.add(new TextPart(group, el));
343 }
f82bad11
NR
344
345 return parts;
176a8327
NR
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
bed93753 366 * an optional {@link StringBuilder} to append the serialised
176a8327
NR
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
aecb3399 376 for (TypeInfo type : data) {
176a8327
NR
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 }
f82bad11 386
176a8327
NR
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 == ',') {
bed93753
NR
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
176a8327
NR
435 done = index + 1;
436 }
437
438 previous = car;
439 }
440
441 return infos;
442 }
fae07ea7 443}