1 package be
.nikiroo
.jvcard
.tui
.panes
;
4 import java
.util
.LinkedList
;
7 import be
.nikiroo
.jvcard
.Contact
;
8 import be
.nikiroo
.jvcard
.Data
;
9 import be
.nikiroo
.jvcard
.TypeInfo
;
10 import be
.nikiroo
.jvcard
.resources
.ColorOption
;
11 import be
.nikiroo
.jvcard
.resources
.DisplayBundle
;
12 import be
.nikiroo
.jvcard
.resources
.DisplayOption
;
13 import be
.nikiroo
.jvcard
.resources
.StringId
;
14 import be
.nikiroo
.jvcard
.tui
.ImageTextControl
;
15 import be
.nikiroo
.jvcard
.tui
.KeyAction
;
16 import be
.nikiroo
.jvcard
.tui
.KeyAction
.DataType
;
17 import be
.nikiroo
.jvcard
.tui
.KeyAction
.Mode
;
18 import be
.nikiroo
.jvcard
.tui
.UiColors
;
19 import be
.nikiroo
.utils
.ImageUtils
;
20 import be
.nikiroo
.utils
.StringUtils
;
22 import com
.googlecode
.lanterna
.TerminalSize
;
23 import com
.googlecode
.lanterna
.gui2
.BorderLayout
;
24 import com
.googlecode
.lanterna
.gui2
.Borders
;
25 import com
.googlecode
.lanterna
.gui2
.Direction
;
26 import com
.googlecode
.lanterna
.gui2
.LinearLayout
;
27 import com
.googlecode
.lanterna
.gui2
.Panel
;
28 import com
.googlecode
.lanterna
.gui2
.TextBox
;
29 import com
.googlecode
.lanterna
.gui2
.TextBox
.Style
;
30 import com
.googlecode
.lanterna
.input
.KeyType
;
32 public class ContactDetails
extends MainContent
{
33 private Contact contact
;
35 private ImageTextControl txtImage
;
37 private boolean fullscreenImage
;
38 private Panel infoPanel
;
41 // from .properties file:
42 private int labelSize
= -1;
43 private String infoFormat
= "";
47 public ContactDetails(Contact contact
) {
48 // Get the .properties info:
49 DisplayBundle map
= new DisplayBundle();
50 labelSize
= map
.getInteger(DisplayOption
.CONTACT_DETAILS_LABEL_WIDTH
,
52 infoFormat
= map
.getString(DisplayOption
.CONTACT_DETAILS_INFO
);
55 BorderLayout blayout
= new BorderLayout();
56 setLayoutManager(blayout
);
59 blayout
= new BorderLayout();
60 top
.setLayoutManager(blayout
);
62 infoPanel
= new Panel();
63 infoPanel
.setLayoutManager(new LinearLayout(Direction
.VERTICAL
));
64 top
.addComponent(infoPanel
, BorderLayout
.Location
.CENTER
);
66 Panel notePanel
= new Panel();
67 notePanel
.setLayoutManager(new LinearLayout(Direction
.HORIZONTAL
));
69 notePanel
.addComponent(UiColors
.createLabel(
70 ColorOption
.VIEW_CONTACT_NOTES_TITLE
, "Notes:"));
71 // 10000x10000 is probably enough or "max"
72 note
= new TextBox(new TerminalSize(10000, 10000), Style
.MULTI_LINE
);
73 note
.setReadOnly(true);
74 notePanel
.addComponent(note
);
75 note
.setVerticalFocusSwitching(false);
76 note
.setHorizontalFocusSwitching(false);
80 addComponent(top
.withBorder(Borders
.doubleLineBevel()),
81 BorderLayout
.Location
.TOP
);
82 addComponent(notePanel
.withBorder(Borders
.singleLineBevel()),
83 BorderLayout
.Location
.CENTER
);
87 * Change the enclosed {@link Contact} from this {@link ContactDetails}.
88 * Also re-set the image.
91 * the new {@link Contact}
93 public void setContact(Contact contact
) {
94 this.contact
= contact
;
97 if (contact
!= null) {
98 infoPanel
.removeAllComponents();
100 String name
= contact
.getPreferredDataValue("FN");
101 infoPanel
.addComponent(UiColors
.createLabel(
102 ColorOption
.VIEW_CONTACT_NAME
, name
));
103 infoPanel
.addComponent(UiColors
.createLabel(
104 ColorOption
.VIEW_CONTACT_NORMAL
, ""));
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
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
119 if (info
.contains("+") || info
.contains("#")) {
122 if (info
.contains("*") || info
.contains("#")) {
126 if (all
|| hl
|| info
.contains("=")) {
127 ColorOption el
= hl ? ColorOption
.VIEW_CONTACT_HIGHLIGHT
128 : ColorOption
.VIEW_CONTACT_NORMAL
;
130 int index
= info
.indexOf('=');
132 index
= info
.indexOf('+');
135 index
= info
.indexOf('#');
138 index
= info
.indexOf('*');
141 String label
= info
.substring(0, index
);
142 String field
= info
.substring(index
+ 1);
145 Data pref
= contact
.getPreferredData(field
);
146 for (Data data
: contact
.getData(field
)) {
148 infoPanel
.addComponent(UiColors
.createLabel(el
,
149 StringUtils
.padString(label
, labelSize
)
152 infoPanel
.addComponent(UiColors
.createLabel(
153 ColorOption
.VIEW_CONTACT_NORMAL
,
154 StringUtils
.padString(label
, labelSize
)
159 String val
= contact
.getPreferredDataValue(field
);
163 infoPanel
.addComponent(UiColors
.createLabel(el
,
164 StringUtils
.padString(label
, labelSize
) + val
));
168 infoPanel
.addComponent(UiColors
.createLabel(
169 ColorOption
.VIEW_CONTACT_NORMAL
,
170 StringUtils
.padString(label
, labelSize
)));
175 infoPanel
.addComponent(UiColors
.createLabel(
176 ColorOption
.VIEW_CONTACT_NORMAL
, ""));
178 String notes
= contact
.getPreferredDataValue("NOTE");
184 Data photo
= contact
.getPreferredData("PHOTO");
186 TypeInfo encoding
= null;
187 for (TypeInfo info
: photo
) {
188 if (info
.getName() != null) {
189 if (info
.getName().equalsIgnoreCase("ENCODING"))
191 // We don't check for the "TYPE" anymore, we just defer
196 if (encoding
!= null && encoding
.getValue() != null
197 && encoding
.getValue().equalsIgnoreCase("b")) {
199 image
= ImageUtils
.fromBase64(photo
.getValue());
200 } catch (Exception e
) {
201 System
.err
.println("Cannot parse image for contact: "
202 + contact
.getPreferredDataValue("UID"));
212 public DataType
getDataType() {
213 return DataType
.DATA
;
217 public List
<KeyAction
> getKeyBindings() {
218 List
<KeyAction
> actions
= new LinkedList
<KeyAction
>();
220 actions
.add(new KeyAction(Mode
.NONE
, KeyType
.Tab
,
221 StringId
.KEY_ACTION_SWITCH_FORMAT
) {
223 public boolean onAction() {
224 if (txtImage
!= null) {
225 txtImage
.switchMode();
231 actions
.add(new KeyAction(Mode
.NONE
, 'i', StringId
.KEY_ACTION_INVERT
) {
233 public boolean onAction() {
234 if (txtImage
!= null) {
235 txtImage
.invertColor();
241 actions
.add(new KeyAction(Mode
.NONE
, 'f',
242 StringId
.KEY_ACTION_FULLSCREEN
) {
244 public boolean onAction() {
245 fullscreenImage
= !fullscreenImage
;
250 // TODO: add "normal" edit
251 actions
.add(new KeyAction(Mode
.CONTACT_DETAILS_RAW
, 'r',
252 StringId
.KEY_ACTION_EDIT_CONTACT_RAW
) {
254 public Object
getObject() {
263 public synchronized Panel
setSize(TerminalSize size
) {
270 * Set the {@link Image} to render and refresh it to the current size
274 * the new {@link Image}
276 private void setImage(Image image
) {
279 if (txtImage
!= null && top
.containsComponent(txtImage
)) {
280 top
.removeComponent(txtImage
);
283 TerminalSize size
= getTxtSize();
285 if (txtImage
!= null) {
286 txtImage
.setSize(size
);
288 txtImage
= new ImageTextControl(image
, size
);
293 top
.addComponent(txtImage
, BorderLayout
.Location
.LEFT
);
300 * Compute the size to use for the {@link Image} text rendering. Return NULL
303 * @return the {@link TerminalSize} to use or NULL if it is not possible
305 private TerminalSize
getTxtSize() {
306 if (image
!= null && getSize() != null && getSize().getColumns() > 0
307 && getSize().getRows() > 0) {
308 if (fullscreenImage
) {
312 // TODO: configure size?
313 int w
= getSize().getColumns() - 40;
314 int h
= getSize().getRows() - 9;
315 if (w
<= 0 || h
<= 0) {
319 return new TerminalSize(w
, h
);