1 package be
.nikiroo
.jvcard
.tui
.panes
;
4 import java
.io
.ByteArrayInputStream
;
5 import java
.util
.LinkedList
;
7 import java
.util
.MissingResourceException
;
8 import java
.util
.ResourceBundle
;
10 import javax
.imageio
.ImageIO
;
11 import javax
.xml
.bind
.DatatypeConverter
;
13 import be
.nikiroo
.jvcard
.Contact
;
14 import be
.nikiroo
.jvcard
.Data
;
15 import be
.nikiroo
.jvcard
.TypeInfo
;
16 import be
.nikiroo
.jvcard
.resources
.Bundles
;
17 import be
.nikiroo
.jvcard
.resources
.StringUtils
;
18 import be
.nikiroo
.jvcard
.resources
.Trans
;
19 import be
.nikiroo
.jvcard
.tui
.ImageTextControl
;
20 import be
.nikiroo
.jvcard
.tui
.KeyAction
;
21 import be
.nikiroo
.jvcard
.tui
.KeyAction
.DataType
;
22 import be
.nikiroo
.jvcard
.tui
.KeyAction
.Mode
;
23 import be
.nikiroo
.jvcard
.tui
.UiColors
;
25 import com
.googlecode
.lanterna
.TerminalSize
;
26 import com
.googlecode
.lanterna
.gui2
.BorderLayout
;
27 import com
.googlecode
.lanterna
.gui2
.Borders
;
28 import com
.googlecode
.lanterna
.gui2
.Direction
;
29 import com
.googlecode
.lanterna
.gui2
.Label
;
30 import com
.googlecode
.lanterna
.gui2
.LinearLayout
;
31 import com
.googlecode
.lanterna
.gui2
.Panel
;
32 import com
.googlecode
.lanterna
.input
.KeyType
;
34 public class ContactDetails
extends MainContent
{
35 private Contact contact
;
37 private ImageTextControl txtImage
;
39 private boolean fullscreenImage
;
40 private Panel infoPanel
;
44 public ContactDetails(Contact contact
) {
45 map
= Bundles
.getBundle("display");
47 BorderLayout blayout
= new BorderLayout();
48 setLayoutManager(blayout
);
51 blayout
= new BorderLayout();
52 top
.setLayoutManager(blayout
);
54 infoPanel
= new Panel();
55 infoPanel
.setLayoutManager(new LinearLayout(Direction
.VERTICAL
));
56 top
.addComponent(infoPanel
, BorderLayout
.Location
.CENTER
);
58 Panel notePanel
= new Panel();
59 notePanel
.setLayoutManager(new LinearLayout(Direction
.HORIZONTAL
));
61 notePanel
.addComponent(UiColors
.Element
.VIEW_CONTACT_NOTES_TITLE
62 .createLabel("Notes:"));
63 note
= UiColors
.Element
.VIEW_CONTACT_NORMAL
.createLabel("");
64 notePanel
.addComponent(note
);
68 addComponent(top
.withBorder(Borders
.doubleLineBevel()),
69 BorderLayout
.Location
.TOP
);
70 addComponent(notePanel
.withBorder(Borders
.singleLineBevel()),
71 BorderLayout
.Location
.CENTER
);
75 * Change the enclosed {@link Contact} from this {@link ContactDetails}.
76 * Also re-set the image.
79 * the new {@link Contact}
81 public void setContact(Contact contact
) {
82 this.contact
= contact
;
85 if (contact
!= null) {
86 infoPanel
.removeAllComponents();
88 String name
= contact
.getPreferredDataValue("FN");
89 if (name
== null || name
.length() == 0) {
90 // TODO format it ourself
91 name
= contact
.getPreferredDataValue("N");
94 infoPanel
.addComponent(UiColors
.Element
.VIEW_CONTACT_NAME
96 infoPanel
.addComponent(UiColors
.Element
.VIEW_CONTACT_NORMAL
102 labelSize
= Integer
.parseInt(map
103 .getString("CONTACT_DETAILS_LABEL_WIDTH"));
105 } catch (NumberFormatException e
) {
108 } catch (MissingResourceException e
) {
112 String infoFormat
= "";
114 infoFormat
= map
.getString("CONTACT_DETAILS_INFO");
115 } catch (MissingResourceException e
) {
119 String
[] infos
= infoFormat
.split("\\|");
120 for (String info
: infos
) {
121 // # - "=FIELD" will take the preferred value for this field
122 // # - "+FIELD" will take the preferred value for this field and
124 // # - "#FIELD" will take all the values with this field's name
125 // # - "*FIELD" will take all the values with this field's name,
126 // highlighting the preferred one
131 if (info
.contains("+") || info
.contains("#"))
133 if (info
.contains("*") || info
.contains("#"))
136 if (all
|| hl
|| info
.contains("=")) {
137 UiColors
.Element el
= hl ? UiColors
.Element
.VIEW_CONTACT_HIGHLIGHT
138 : UiColors
.Element
.VIEW_CONTACT_NORMAL
;
140 int index
= info
.indexOf('=');
142 index
= info
.indexOf('+');
144 index
= info
.indexOf('#');
146 index
= info
.indexOf('*');
148 String label
= info
.substring(0, index
);
149 String field
= info
.substring(index
+ 1);
152 for (Data data
: contact
.getData(field
)) {
153 if (data
.isPreferred()) {
154 infoPanel
.addComponent(el
155 .createLabel(StringUtils
.padString(
160 .addComponent(UiColors
.Element
.VIEW_CONTACT_NORMAL
161 .createLabel(StringUtils
168 String val
= contact
.getPreferredDataValue(field
);
171 infoPanel
.addComponent(el
.createLabel(StringUtils
172 .padString(label
, labelSize
) + val
));
176 infoPanel
.addComponent(UiColors
.Element
.VIEW_CONTACT_NORMAL
177 .createLabel(StringUtils
178 .padString(label
, labelSize
)));
183 infoPanel
.addComponent(UiColors
.Element
.VIEW_CONTACT_NORMAL
186 String notes
= contact
.getPreferredDataValue("NOTE");
191 Data photo
= contact
.getPreferredData("PHOTO");
193 TypeInfo encoding
= null;
194 for (int index
= 0; index
< photo
.size(); index
++) {
195 TypeInfo info
= photo
.get(index
);
196 if (info
.getName() != null) {
197 if (info
.getName().equalsIgnoreCase("ENCODING"))
199 // We don't check for the "TYPE" anymore, we just defer
204 if (encoding
!= null && encoding
.getValue() != null
205 && encoding
.getValue().equalsIgnoreCase("b")) {
208 image
= ImageIO
.read(new ByteArrayInputStream(
209 DatatypeConverter
.parseBase64Binary(photo
212 } catch (Exception e
) {
213 System
.err
.println("Cannot parse image for contact: "
214 + contact
.getPreferredDataValue("UID"));
224 public DataType
getDataType() {
225 return DataType
.DATA
;
229 public List
<KeyAction
> getKeyBindings() {
230 List
<KeyAction
> actions
= new LinkedList
<KeyAction
>();
232 actions
.add(new KeyAction(Mode
.NONE
, KeyType
.Tab
,
233 Trans
.StringId
.KEY_ACTION_SWITCH_FORMAT
) {
235 public boolean onAction() {
236 if (txtImage
!= null) {
237 txtImage
.switchMode();
243 actions
.add(new KeyAction(Mode
.NONE
, 'i',
244 Trans
.StringId
.KEY_ACTION_INVERT
) {
246 public boolean onAction() {
247 if (txtImage
!= null) {
248 txtImage
.invertColor();
254 actions
.add(new KeyAction(Mode
.NONE
, 'f',
255 Trans
.StringId
.KEY_ACTION_FULLSCREEN
) {
257 public boolean onAction() {
258 fullscreenImage
= !fullscreenImage
;
263 // TODO: add "normal" edit
264 actions
.add(new KeyAction(Mode
.CONTACT_DETAILS_RAW
, 'r',
265 Trans
.StringId
.KEY_ACTION_EDIT_CONTACT_RAW
) {
267 public Object
getObject() {
276 public synchronized Panel
setSize(TerminalSize size
) {
283 * Set the {@link Image} to render and refresh it to the current size
287 * the new {@link Image}
289 private void setImage(Image image
) {
292 if (txtImage
!= null && top
.containsComponent(txtImage
))
293 top
.removeComponent(txtImage
);
295 TerminalSize size
= getTxtSize();
297 if (txtImage
!= null)
298 txtImage
.setSize(size
);
300 txtImage
= new ImageTextControl(image
, size
);
304 top
.addComponent(txtImage
, BorderLayout
.Location
.LEFT
);
311 * Compute the size to use for the {@link Image} text rendering. Return NULL
314 * @return the {@link TerminalSize} to use or NULL if it is not possible
316 private TerminalSize
getTxtSize() {
317 if (image
!= null && getSize() != null && getSize().getColumns() > 0
318 && getSize().getRows() > 0) {
319 if (fullscreenImage
) {
322 // TODO: configure size?
323 int w
= getSize().getColumns() - 40;
324 int h
= getSize().getRows() - 9;
325 if (w
<= 0 || h
<= 0)
328 return new TerminalSize(w
, h
);