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