Fix PREF handling (was not correct relative to the RFC!)
[jvcard.git] / src / be / nikiroo / jvcard / tui / panes / ContactDetails.java
CommitLineData
f04d8b1c
NR
1package be.nikiroo.jvcard.tui.panes;
2
3import java.awt.Image;
25232463 4import java.util.LinkedList;
f04d8b1c
NR
5import java.util.List;
6
f04d8b1c
NR
7import be.nikiroo.jvcard.Contact;
8import be.nikiroo.jvcard.Data;
9import be.nikiroo.jvcard.TypeInfo;
3634193b 10import be.nikiroo.jvcard.resources.StringUtils;
e119a1c1
NR
11import be.nikiroo.jvcard.resources.bundles.DisplayBundle;
12import be.nikiroo.jvcard.resources.enums.DisplayOption;
13import be.nikiroo.jvcard.resources.enums.ColorOption;
14import be.nikiroo.jvcard.resources.enums.StringId;
25232463 15import be.nikiroo.jvcard.tui.ImageTextControl;
f04d8b1c
NR
16import be.nikiroo.jvcard.tui.KeyAction;
17import be.nikiroo.jvcard.tui.KeyAction.DataType;
25232463 18import be.nikiroo.jvcard.tui.KeyAction.Mode;
f82bad11 19import be.nikiroo.jvcard.tui.UiColors;
f04d8b1c
NR
20
21import com.googlecode.lanterna.TerminalSize;
22import com.googlecode.lanterna.gui2.BorderLayout;
3634193b 23import com.googlecode.lanterna.gui2.Borders;
f82bad11
NR
24import com.googlecode.lanterna.gui2.Direction;
25import com.googlecode.lanterna.gui2.Label;
26import com.googlecode.lanterna.gui2.LinearLayout;
f04d8b1c 27import com.googlecode.lanterna.gui2.Panel;
25232463 28import com.googlecode.lanterna.input.KeyType;
f04d8b1c
NR
29
30public class ContactDetails extends MainContent {
31 private Contact contact;
ae22c247 32 private Panel top;
f82bad11 33 private ImageTextControl txtImage;
ae22c247
NR
34 private Image image;
35 private boolean fullscreenImage;
f82bad11
NR
36 private Panel infoPanel;
37 private Label note;
30a4aa17
NR
38
39 // from .properties file:
40 private int labelSize = -1;
41 private String infoFormat = "";
e119a1c1 42
30a4aa17 43 //
f04d8b1c
NR
44
45 public ContactDetails(Contact contact) {
30a4aa17 46 // Get the .properties info:
e119a1c1
NR
47 DisplayBundle map = new DisplayBundle();
48 labelSize = map.getInteger(DisplayOption.CONTACT_DETAILS_LABEL_WIDTH,
49 -1);
50 infoFormat = map.getString(DisplayOption.CONTACT_DETAILS_INFO);
30a4aa17 51 //
3634193b 52
f04d8b1c
NR
53 BorderLayout blayout = new BorderLayout();
54 setLayoutManager(blayout);
55
ae22c247 56 top = new Panel();
f82bad11
NR
57 blayout = new BorderLayout();
58 top.setLayoutManager(blayout);
59
60 infoPanel = new Panel();
61 infoPanel.setLayoutManager(new LinearLayout(Direction.VERTICAL));
62 top.addComponent(infoPanel, BorderLayout.Location.CENTER);
63
64 Panel notePanel = new Panel();
65 notePanel.setLayoutManager(new LinearLayout(Direction.HORIZONTAL));
66
e119a1c1
NR
67 notePanel.addComponent(UiColors.createLabel(
68 ColorOption.VIEW_CONTACT_NOTES_TITLE, "Notes:"));
69 note = UiColors.createLabel(ColorOption.VIEW_CONTACT_NORMAL, "");
f82bad11
NR
70 notePanel.addComponent(note);
71
ae22c247 72 setContact(contact);
f82bad11 73
3634193b
NR
74 addComponent(top.withBorder(Borders.doubleLineBevel()),
75 BorderLayout.Location.TOP);
76 addComponent(notePanel.withBorder(Borders.singleLineBevel()),
77 BorderLayout.Location.CENTER);
ae22c247
NR
78 }
79
f82bad11
NR
80 /**
81 * Change the enclosed {@link Contact} from this {@link ContactDetails}.
2a96e7b2 82 * Also re-set the image.
f82bad11
NR
83 *
84 * @param contact
85 * the new {@link Contact}
86 */
ae22c247 87 public void setContact(Contact contact) {
ae22c247 88 this.contact = contact;
2a96e7b2 89 image = null;
ae22c247 90
2a96e7b2 91 if (contact != null) {
f82bad11
NR
92 infoPanel.removeAllComponents();
93
94 String name = contact.getPreferredDataValue("FN");
e119a1c1
NR
95 infoPanel.addComponent(UiColors.createLabel(
96 ColorOption.VIEW_CONTACT_NAME, name));
97 infoPanel.addComponent(UiColors.createLabel(
98 ColorOption.VIEW_CONTACT_NORMAL, ""));
3634193b
NR
99
100 // List of infos:
3634193b
NR
101 String[] infos = infoFormat.split("\\|");
102 for (String info : infos) {
103 // # - "=FIELD" will take the preferred value for this field
104 // # - "+FIELD" will take the preferred value for this field and
105 // highlight it
106 // # - "#FIELD" will take all the values with this field's name
107 // # - "*FIELD" will take all the values with this field's name,
108 // highlighting the preferred one
109 // #
110
111 boolean hl = false;
112 boolean all = false;
113 if (info.contains("+") || info.contains("#"))
114 hl = true;
115 if (info.contains("*") || info.contains("#"))
116 all = true;
117
118 if (all || hl || info.contains("=")) {
e119a1c1
NR
119 ColorOption el = hl ? ColorOption.VIEW_CONTACT_HIGHLIGHT
120 : ColorOption.VIEW_CONTACT_NORMAL;
3634193b
NR
121
122 int index = info.indexOf('=');
123 if (index < 0)
124 index = info.indexOf('+');
125 if (index < 0)
126 index = info.indexOf('#');
127 if (index < 0)
128 index = info.indexOf('*');
129
130 String label = info.substring(0, index);
131 String field = info.substring(index + 1);
132
133 if (all) {
7671a249 134 Data pref = contact.getPreferredData(field);
3634193b 135 for (Data data : contact.getData(field)) {
7671a249 136 if (data == pref) {
e119a1c1
NR
137 infoPanel.addComponent(UiColors.createLabel(el,
138 StringUtils.padString(label, labelSize)
aecb3399 139 + data.toString()));
3634193b 140 } else {
e119a1c1
NR
141 infoPanel.addComponent(UiColors.createLabel(
142 ColorOption.VIEW_CONTACT_NORMAL,
143 StringUtils.padString(label, labelSize)
144 + data.toString()));
3634193b
NR
145 }
146 }
147 } else {
aecb3399
NR
148 String val = contact.getPreferredDataValue(field);
149 if (val == null)
150 val = "";
e119a1c1
NR
151 infoPanel.addComponent(UiColors.createLabel(el,
152 StringUtils.padString(label, labelSize) + val));
3634193b
NR
153 }
154 } else {
155 String label = info;
e119a1c1
NR
156 infoPanel.addComponent(UiColors.createLabel(
157 ColorOption.VIEW_CONTACT_NORMAL,
158 StringUtils.padString(label, labelSize)));
3634193b
NR
159 }
160 }
161 // end of list
162
e119a1c1
NR
163 infoPanel.addComponent(UiColors.createLabel(
164 ColorOption.VIEW_CONTACT_NORMAL, ""));
f82bad11
NR
165
166 String notes = contact.getPreferredDataValue("NOTE");
167 if (notes == null)
168 notes = "";
aecb3399 169 note.setText(notes);
f82bad11 170
f04d8b1c
NR
171 Data photo = contact.getPreferredData("PHOTO");
172 if (photo != null) {
173 TypeInfo encoding = null;
26d254a3 174 for (TypeInfo info : photo) {
f04d8b1c
NR
175 if (info.getName() != null) {
176 if (info.getName().equalsIgnoreCase("ENCODING"))
177 encoding = info;
2a96e7b2
NR
178 // We don't check for the "TYPE" anymore, we just defer
179 // it to ImageIcon
f04d8b1c
NR
180 }
181 }
182
183 if (encoding != null && encoding.getValue() != null
184 && encoding.getValue().equalsIgnoreCase("b")) {
185
1c03abaf 186 try {
26d254a3 187 image = StringUtils.toImage(photo.getValue());
1c03abaf
NR
188 } catch (Exception e) {
189 System.err.println("Cannot parse image for contact: "
190 + contact.getPreferredDataValue("UID"));
191 }
f04d8b1c
NR
192 }
193 }
194 }
195
f82bad11 196 setImage(image);
f04d8b1c 197 }
25232463
NR
198
199 @Override
200 public DataType getDataType() {
201 return DataType.DATA;
202 }
203
204 @Override
205 public List<KeyAction> getKeyBindings() {
206 List<KeyAction> actions = new LinkedList<KeyAction>();
207
25232463 208 actions.add(new KeyAction(Mode.NONE, KeyType.Tab,
e119a1c1 209 StringId.KEY_ACTION_SWITCH_FORMAT) {
25232463
NR
210 @Override
211 public boolean onAction() {
f82bad11
NR
212 if (txtImage != null) {
213 txtImage.switchMode();
25232463
NR
214 }
215
216 return false;
217 }
218 });
e119a1c1 219 actions.add(new KeyAction(Mode.NONE, 'i', StringId.KEY_ACTION_INVERT) {
25232463
NR
220 @Override
221 public boolean onAction() {
f82bad11
NR
222 if (txtImage != null) {
223 txtImage.invertColor();
25232463
NR
224 }
225
226 return false;
227 }
228 });
ae22c247 229 actions.add(new KeyAction(Mode.NONE, 'f',
e119a1c1 230 StringId.KEY_ACTION_FULLSCREEN) {
ae22c247
NR
231 @Override
232 public boolean onAction() {
233 fullscreenImage = !fullscreenImage;
234 setImage(image);
235 return false;
236 }
237 });
3634193b
NR
238 // TODO: add "normal" edit
239 actions.add(new KeyAction(Mode.CONTACT_DETAILS_RAW, 'r',
e119a1c1 240 StringId.KEY_ACTION_EDIT_CONTACT_RAW) {
176a8327
NR
241 @Override
242 public Object getObject() {
243 return contact;
244 }
245 });
25232463
NR
246
247 return actions;
248 }
ae22c247
NR
249
250 @Override
251 public synchronized Panel setSize(TerminalSize size) {
252 super.setSize(size);
253 setImage(image);
254 return this;
255 }
256
257 /**
f82bad11
NR
258 * Set the {@link Image} to render and refresh it to the current size
259 * constraints.
ae22c247
NR
260 *
261 * @param image
262 * the new {@link Image}
263 */
264 private void setImage(Image image) {
265 this.image = image;
266
f82bad11
NR
267 if (txtImage != null && top.containsComponent(txtImage))
268 top.removeComponent(txtImage);
269
ae22c247
NR
270 TerminalSize size = getTxtSize();
271 if (size != null) {
f82bad11
NR
272 if (txtImage != null)
273 txtImage.setSize(size);
ae22c247 274 else
f82bad11 275 txtImage = new ImageTextControl(image, size);
ae22c247
NR
276 }
277
f82bad11
NR
278 if (size != null) {
279 top.addComponent(txtImage, BorderLayout.Location.LEFT);
280 }
ae22c247 281
f82bad11 282 invalidate();
ae22c247
NR
283 }
284
285 /**
286 * Compute the size to use for the {@link Image} text rendering. Return NULL
287 * in case of error.
288 *
289 * @return the {@link TerminalSize} to use or NULL if it is not possible
290 */
291 private TerminalSize getTxtSize() {
292 if (image != null && getSize() != null && getSize().getColumns() > 0
293 && getSize().getRows() > 0) {
294 if (fullscreenImage) {
295 return getSize();
296 } else {
f82bad11
NR
297 // TODO: configure size?
298 int w = getSize().getColumns() - 40;
aecb3399 299 int h = getSize().getRows() - 9;
f82bad11
NR
300 if (w <= 0 || h <= 0)
301 return null;
302
303 return new TerminalSize(w, h);
ae22c247
NR
304 }
305 }
306
307 return null;
308 }
f04d8b1c 309}