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