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()) {
155 .addComponent(el
.createLabel(StringUtils
156 .padString(label
, labelSize
)
158 .getPreferredDataValue(field
)));
161 .addComponent(UiColors
.Element
.VIEW_CONTACT_NORMAL
.createLabel(StringUtils
162 .padString(label
, labelSize
)
164 .getPreferredDataValue(field
)));
168 infoPanel
.addComponent(el
.createLabel(StringUtils
169 .padString(label
, labelSize
)
170 + contact
.getPreferredDataValue(field
)));
174 infoPanel
.addComponent(UiColors
.Element
.VIEW_CONTACT_NORMAL
175 .createLabel(StringUtils
176 .padString(label
, labelSize
)));
181 infoPanel
.addComponent(UiColors
.Element
.VIEW_CONTACT_NORMAL
184 String notes
= contact
.getPreferredDataValue("NOTE");
187 note
.setText(notes
.replaceAll("\\\\n", "\n"));
189 Data photo
= contact
.getPreferredData("PHOTO");
191 TypeInfo encoding
= null;
192 for (int index
= 0; index
< photo
.size(); index
++) {
193 TypeInfo info
= photo
.get(index
);
194 if (info
.getName() != null) {
195 if (info
.getName().equalsIgnoreCase("ENCODING"))
197 // We don't check for the "TYPE" anymore, we just defer
202 if (encoding
!= null && encoding
.getValue() != null
203 && encoding
.getValue().equalsIgnoreCase("b")) {
206 image
= ImageIO
.read(new ByteArrayInputStream(
207 DatatypeConverter
.parseBase64Binary(photo
210 } catch (Exception e
) {
211 System
.err
.println("Cannot parse image for contact: "
212 + contact
.getPreferredDataValue("UID"));
222 public DataType
getDataType() {
223 return DataType
.DATA
;
227 public List
<KeyAction
> getKeyBindings() {
228 List
<KeyAction
> actions
= new LinkedList
<KeyAction
>();
230 actions
.add(new KeyAction(Mode
.NONE
, KeyType
.Tab
,
231 Trans
.StringId
.KEY_ACTION_SWITCH_FORMAT
) {
233 public boolean onAction() {
234 if (txtImage
!= null) {
235 txtImage
.switchMode();
241 actions
.add(new KeyAction(Mode
.NONE
, 'i',
242 Trans
.StringId
.KEY_ACTION_INVERT
) {
244 public boolean onAction() {
245 if (txtImage
!= null) {
246 txtImage
.invertColor();
252 actions
.add(new KeyAction(Mode
.NONE
, 'f',
253 Trans
.StringId
.KEY_ACTION_FULLSCREEN
) {
255 public boolean onAction() {
256 fullscreenImage
= !fullscreenImage
;
261 // TODO: add "normal" edit
262 actions
.add(new KeyAction(Mode
.CONTACT_DETAILS_RAW
, 'r',
263 Trans
.StringId
.KEY_ACTION_EDIT_CONTACT_RAW
) {
265 public Object
getObject() {
274 public synchronized Panel
setSize(TerminalSize size
) {
281 * Set the {@link Image} to render and refresh it to the current size
285 * the new {@link Image}
287 private void setImage(Image image
) {
290 if (txtImage
!= null && top
.containsComponent(txtImage
))
291 top
.removeComponent(txtImage
);
293 TerminalSize size
= getTxtSize();
295 if (txtImage
!= null)
296 txtImage
.setSize(size
);
298 txtImage
= new ImageTextControl(image
, size
);
302 top
.addComponent(txtImage
, BorderLayout
.Location
.LEFT
);
309 * Compute the size to use for the {@link Image} text rendering. Return NULL
312 * @return the {@link TerminalSize} to use or NULL if it is not possible
314 private TerminalSize
getTxtSize() {
315 if (image
!= null && getSize() != null && getSize().getColumns() > 0
316 && getSize().getRows() > 0) {
317 if (fullscreenImage
) {
320 // TODO: configure size?
321 int w
= getSize().getColumns() - 40;
322 int h
= getSize().getRows() - 5;
323 if (w
<= 0 || h
<= 0)
326 return new TerminalSize(w
, h
);