New launcher class to start all 3 modes:
[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.io.ByteArrayInputStream;
5 import java.util.LinkedList;
6 import java.util.List;
7
8 import javax.imageio.ImageIO;
9 import javax.xml.bind.DatatypeConverter;
10
11 import be.nikiroo.jvcard.Contact;
12 import be.nikiroo.jvcard.Data;
13 import be.nikiroo.jvcard.TypeInfo;
14 import be.nikiroo.jvcard.resources.Trans;
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.Direction;
24 import com.googlecode.lanterna.gui2.Label;
25 import com.googlecode.lanterna.gui2.LinearLayout;
26 import com.googlecode.lanterna.gui2.Panel;
27 import com.googlecode.lanterna.input.KeyType;
28
29 public class ContactDetails extends MainContent {
30 private Contact contact;
31 private Panel top;
32 private ImageTextControl txtImage;
33 private Image image;
34 private boolean fullscreenImage;
35 private Panel infoPanel;
36 private Label note;
37
38 public ContactDetails(Contact contact) {
39 BorderLayout blayout = new BorderLayout();
40 setLayoutManager(blayout);
41
42 top = new Panel();
43 blayout = new BorderLayout();
44 top.setLayoutManager(blayout);
45
46 infoPanel = new Panel();
47 infoPanel.setLayoutManager(new LinearLayout(Direction.VERTICAL));
48 top.addComponent(infoPanel, BorderLayout.Location.CENTER);
49
50 Panel notePanel = new Panel();
51 notePanel.setLayoutManager(new LinearLayout(Direction.HORIZONTAL));
52
53 notePanel.addComponent(UiColors.Element.VIEW_CONTACT_NOTES_TITLE
54 .createLabel("Notes:"));
55 note = UiColors.Element.VIEW_CONTACT_NORMAL.createLabel("");
56 notePanel.addComponent(note);
57
58 setContact(contact);
59
60 addComponent(top, BorderLayout.Location.TOP);
61 addComponent(notePanel, BorderLayout.Location.CENTER);
62 }
63
64 /**
65 * Change the enclosed {@link Contact} from this {@link ContactDetails}.
66 * Also re-set the image.
67 *
68 * @param contact
69 * the new {@link Contact}
70 */
71 public void setContact(Contact contact) {
72 this.contact = contact;
73 image = null;
74
75 if (contact != null) {
76 infoPanel.removeAllComponents();
77
78 String name = contact.getPreferredDataValue("FN");
79 if (name == null || name.length() == 0) {
80 // TODO format it ourself
81 name = contact.getPreferredDataValue("N");
82 }
83
84 // TODO: i18n + do it properly
85 infoPanel.addComponent(UiColors.Element.VIEW_CONTACT_NAME
86 .createLabel(name));
87
88 infoPanel.addComponent(UiColors.Element.VIEW_CONTACT_NORMAL
89 .createLabel(""));
90 infoPanel.addComponent(UiColors.Element.VIEW_CONTACT_NORMAL
91 .createLabel("Phone: "
92 + contact.getPreferredDataValue("TEL")));
93 infoPanel.addComponent(UiColors.Element.VIEW_CONTACT_NORMAL
94 .createLabel("eMail: "
95 + contact.getPreferredDataValue("EMAIL")));
96 infoPanel.addComponent(UiColors.Element.VIEW_CONTACT_NORMAL
97 .createLabel(""));
98
99 String notes = contact.getPreferredDataValue("NOTE");
100 if (notes == null)
101 notes = "";
102 note.setText(notes.replaceAll("\\\\n", "\n"));
103
104 Data photo = contact.getPreferredData("PHOTO");
105 if (photo != null) {
106 TypeInfo encoding = null;
107 for (int index = 0; index < photo.size(); index++) {
108 TypeInfo info = photo.get(index);
109 if (info.getName() != null) {
110 if (info.getName().equalsIgnoreCase("ENCODING"))
111 encoding = info;
112 // We don't check for the "TYPE" anymore, we just defer
113 // it to ImageIcon
114 }
115 }
116
117 if (encoding != null && encoding.getValue() != null
118 && encoding.getValue().equalsIgnoreCase("b")) {
119
120 try {
121 image = ImageIO.read(new ByteArrayInputStream(
122 DatatypeConverter.parseBase64Binary(photo
123 .getValue())));
124 image.toString();
125 } catch (Exception e) {
126 System.err.println("Cannot parse image for contact: "
127 + contact.getPreferredDataValue("UID"));
128 }
129 }
130 }
131 }
132
133 setImage(image);
134 }
135
136 @Override
137 public DataType getDataType() {
138 return DataType.DATA;
139 }
140
141 @Override
142 public List<KeyAction> getKeyBindings() {
143 List<KeyAction> actions = new LinkedList<KeyAction>();
144
145 // TODO
146 actions.add(new KeyAction(Mode.NONE, KeyType.Tab,
147 Trans.StringId.KEY_ACTION_SWITCH_FORMAT) {
148 @Override
149 public boolean onAction() {
150 if (txtImage != null) {
151 txtImage.switchMode();
152 }
153
154 return false;
155 }
156 });
157 actions.add(new KeyAction(Mode.NONE, 'i',
158 Trans.StringId.KEY_ACTION_INVERT) {
159 @Override
160 public boolean onAction() {
161 if (txtImage != null) {
162 txtImage.invertColor();
163 }
164
165 return false;
166 }
167 });
168 actions.add(new KeyAction(Mode.NONE, 'f',
169 Trans.StringId.KEY_ACTION_FULLSCREEN) {
170 @Override
171 public boolean onAction() {
172 fullscreenImage = !fullscreenImage;
173 setImage(image);
174 return false;
175 }
176 });
177 // TODO: add "normal" edit and remove this one into RAW edit
178 actions.add(new KeyAction(Mode.CONTACT_DETAILS_RAW, 'e',
179 Trans.StringId.KEY_ACTION_EDIT_CONTACT) {
180 @Override
181 public Object getObject() {
182 return contact;
183 }
184 });
185
186 return actions;
187 }
188
189 @Override
190 public synchronized Panel setSize(TerminalSize size) {
191 super.setSize(size);
192 setImage(image);
193 return this;
194 }
195
196 /**
197 * Set the {@link Image} to render and refresh it to the current size
198 * constraints.
199 *
200 * @param image
201 * the new {@link Image}
202 */
203 private void setImage(Image image) {
204 this.image = image;
205
206 if (txtImage != null && top.containsComponent(txtImage))
207 top.removeComponent(txtImage);
208
209 TerminalSize size = getTxtSize();
210 if (size != null) {
211 if (txtImage != null)
212 txtImage.setSize(size);
213 else
214 txtImage = new ImageTextControl(image, size);
215 }
216
217 if (size != null) {
218 top.addComponent(txtImage, BorderLayout.Location.LEFT);
219 }
220
221 invalidate();
222 }
223
224 /**
225 * Compute the size to use for the {@link Image} text rendering. Return NULL
226 * in case of error.
227 *
228 * @return the {@link TerminalSize} to use or NULL if it is not possible
229 */
230 private TerminalSize getTxtSize() {
231 if (image != null && getSize() != null && getSize().getColumns() > 0
232 && getSize().getRows() > 0) {
233 if (fullscreenImage) {
234 return getSize();
235 } else {
236 // TODO: configure size?
237 int w = getSize().getColumns() - 40;
238 int h = getSize().getRows() - 5;
239 if (w <= 0 || h <= 0)
240 return null;
241
242 return new TerminalSize(w, h);
243 }
244 }
245
246 return null;
247 }
248 }