*
* <p>
* The format is basically a list of field names separated by a pipe and
- * optionally parametrised. The parameters allows you to:
+ * optionally parametrised with the 'at' (@) symbol. The parameters allows
+ * you to:
* <ul>
* <li>@x: show only a present/not present info</li>
* <li>@n: limit the size to a fixed value 'n'</li>
* </p>
*
* <p>
+ * In case of lists or multiple-fields values, you can select a specific
+ * list or field with:
+ * <ul>
+ * <li>FIELD@(0): select the first value in a list</li>
+ * <li>FIELD@[1]: select the second field in a multiple-fields value</li>
+ * </ul>
+ * </p>
+ *
+ * <p>
* You can also add a fixed text if it starts with a simple-quote (').
* </p>
*
* </p>
*
* <p>
+ * In case of lists or multiple-fields values, you can select a specific
+ * list or field with:
+ * <ul>
+ * <li>FIELD@(0): select the first value in a list</li>
+ * <li>FIELD@[1]: select the second field in a multiple-fields value</li>
+ * </ul>
+ * </p>
+ *
+ * <p>
* You can also add a fixed text if it starts with a simple-quote (').
* </p>
*
* </p>
*
* <p>
+ * In case of lists or multiple-fields values, you can select a specific
+ * list or field with:
+ * <ul>
+ * <li>FIELD@(0): select the first value in a list</li>
+ * <li>FIELD@[1]: select the second field in a multiple-fields value</li>
+ * </ul>
+ * </p>
+ *
+ * <p>
* You can also add a fixed text if it starts with a simple-quote (').
* </p>
*
* </p>
*
* <p>
+ * In case of lists or multiple-fields values, you can select a specific
+ * list or field with:
+ * <ul>
+ * <li>FIELD@(0): select the first value in a list</li>
+ * <li>FIELD@[1]: select the second field in a multiple-fields value</li>
+ * </ul>
+ * </p>
+ *
+ * <p>
* You can also add a fixed text if it starts with a simple-quote (').
* </p>
*
int size = -1;
boolean binary = false;
boolean expand = false;
+ int fieldNum = -1;
+ int valueNum = -1;
if (field.length() > 0 && field.charAt(0) != '\''
&& field.contains("@")) {
} else if (opt.equals("+")) {
expand = true;
numOfFieldsToExpand++;
+ } else if (opt.length() > 0 && opt.charAt(0) == '(') {
+ try {
+ opt = opt.substring(1, opt.length() - 1);
+ valueNum = Integer.parseInt(opt);
+ } catch (Exception e) {
+ }
+ } else if (opt.length() > 0 && opt.charAt(0) == '[') {
+ try {
+ opt = opt.substring(1, opt.length() - 1);
+ fieldNum = Integer.parseInt(opt);
+ } catch (Exception e) {
+ }
} else {
try {
size = Integer.parseInt(opt);
- } catch (Exception e) {
+ } catch (NumberFormatException e) {
}
}
}
String value = null;
if (field.length() > 0 && field.charAt(0) == '\'') {
value = field.substring(1);
+ } else if (valueNum >= 0) {
+ List<String> vv = getPreferredData(field).getValues();
+ if (valueNum < vv.size()) {
+ value = vv.get(valueNum);
+ }
+ } else if (fieldNum >= 0) {
+ List<String> ff = getPreferredData(field).getFields();
+ if (fieldNum < ff.size()) {
+ value = ff.get(fieldNum);
+ }
} else {
value = getPreferredDataValue(field);
}
public String getValue() {
return unescape(value);
}
-
+
+ /**
+ * Change the value of this {@link Data}
+ *
+ * @param value
+ * the new value
+ */
+ public void setValue(String value) {
+ setRawValue(escape(value));
+ }
+
/**
- * Return the RAW value of this {@link Data}
+ * Return the raw value of this {@link Data}
*
- * @return the RAW value
+ * @return the raw value
*/
public String getRawValue() {
return value;
}
/**
- * Change the value of this {@link Data}
+ * Change the raw value of this {@link Data}
*
* @param value
- * the new value
+ * the new raw value
*/
- public void setValue(String value) {
- value = escape(value);
-
+ public void setRawValue(String value) {
if ((value == null && this.value != null)
|| (value != null && !value.equals(this.value))) {
this.value = value;
for (int i = 0; i < value.length(); i++) {
if (value.charAt(i) == sep
&& (i == 0 || value.charAt(i - 1) != '\\')) {
- rep.add(value.substring(last, i - last));
+ rep.add(value.substring(last, i));
+ last = i + 1;
}
}
- rep.add(value.substring(last));
+ if (last < value.length())
+ rep.add(value.substring(last));
}
return rep;
import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
import javax.imageio.ImageIO;
static private final int ERR_INTERNAL = 3;
static private Trans transService;
+ static private String defaultFn;
+ static private boolean forceComputedFn;
+
enum Mode {
CONTACT_MANAGER, I18N, SERVER, LOAD_PHOTO, SAVE_PHOTO, ONLY_PHOTO,
}
utf8();
}
+ // N/FN fix information:
+ readNFN();
+
// Error management:
if (mode == Mode.SERVER && files.size() > 0) {
System.err
/**
* Return the {@link Card} corresponding to the given resource name -- a
- * file or a remote jvcard URL
+ * file or a remote jvcard URL.
+ *
+ * <p>
+ * Will also fix the FN if required (see display.properties).
+ * </p>
*
* @param input
* a filename or a remote jvcard url with named resource (e.g.:
throw new IOException("Remoting support not available", e);
}
+ // Fix the FN value
+ if (defaultFn != null) {
+ try {
+ for (Contact contact : card.getCard()) {
+ Data name = contact.getPreferredData("FN");
+ if (name == null || name.getValue().length() == 0
+ || forceComputedFn) {
+ name.setValue(contact.toString(defaultFn, ""));
+ }
+ }
+ } catch (Exception e) {
+ // sync failed -> getCard() throws.
+ // do not update.
+ }
+ }
+
return card;
}
} catch (IllegalAccessException e) {
}
}
+
+ /**
+ * Read display.properties to know if we should fix the FN field when empty,
+ * or always, or never.
+ */
+ static private void readNFN() {
+ ResourceBundle map = Bundles.getBundle("display");
+ try {
+ defaultFn = map.getString("CONTACT_DETAILS_DEFAULT_FN");
+ if (defaultFn.trim().length() == 0)
+ defaultFn = null;
+ } catch (MissingResourceException e) {
+ e.printStackTrace();
+ }
+
+ try {
+ String forceComputedFnStr = map
+ .getString("CONTACT_DETAILS_SHOW_COMPUTED_FN");
+ if (forceComputedFnStr.length() > 0
+ && forceComputedFnStr.equalsIgnoreCase("true"))
+ forceComputedFn = true;
+ } catch (MissingResourceException e) {
+ e.printStackTrace();
+ }
+ }
}
KEY_TAB, // keys
@Meta(what = "a key to press", where = "action keys", format = "MUST BE 3 chars long", info = "Enter key")
KEY_ENTER, //
- @Meta(what = "", where = "", format = "", info = "")
- KEY_ACTION_BACK, // MainWindow
- @Meta(what = "", where = "", format = "", info = "")
+ @Meta(what = "Action key", where = "All screens except the first (KEY_ACTION_QUIT)", format = "", info = "Go back to previous screen")
+ KEY_ACTION_BACK, //
+ @Meta(what = "Action key", where = "MainWindow", format = "", info = "Get help text")
KEY_ACTION_HELP, //
- @Meta(what = "", where = "", format = "", info = "")
- KEY_ACTION_VIEW_CARD, // FileList
- @Meta(what = "", where = "", format = "", info = "")
- KEY_ACTION_VIEW_CONTACT, // ContactList
- @Meta(what = "", where = "", format = "", info = "")
+ @Meta(what = "Action key", where = "FileList", format = "", info = "View the selected card")
+ KEY_ACTION_VIEW_CARD, //
+ @Meta(what = "Action key", where = "ContactList", format = "", info = "View the selected contact")
+ KEY_ACTION_VIEW_CONTACT, //
+ @Meta(what = "Action key", where = "ContactDetails", format = "", info = "Edit the contact")
KEY_ACTION_EDIT_CONTACT, //
- @Meta(what = "", where = "", format = "", info = "")
+ @Meta(what = "Action key", where = "ContactDetails", format = "", info = "Edit the contact in RAW mode")
KEY_ACTION_EDIT_CONTACT_RAW, //
- @Meta(what = "", where = "", format = "", info = "")
+ @Meta(what = "Action key", where = "ContactList", format = "", info = "Save the whole card")
KEY_ACTION_SAVE_CARD, //
- @Meta(what = "", where = "", format = "", info = "")
+ @Meta(what = "", where = "ContactList", format = "", info = "Delete the selected contact")
KEY_ACTION_DELETE_CONTACT, //
- @Meta(what = "", where = "", format = "", info = "")
+ @Meta(what = "Action key", where = "ContactList", format = "", info = "Filter the displayed contacts")
KEY_ACTION_SEARCH, //
- @Meta(what = "", where = "", format = "", info = "we could use: ' ', ┃, │...")
+ @Meta(what = "", where = "", format = "we could use: ' ', ┃, │...", info = "Field separator")
DEAULT_FIELD_SEPARATOR, // MainContentList
- @Meta(what = "", where = "", format = "", info = "")
- DEAULT_FIELD_SEPARATOR_NOUTF, //
- @Meta(what = "", where = "", format = "", info = "")
- KEY_ACTION_INVERT, // ContactDetails
- @Meta(what = "", where = "", format = "", info = "")
+ @Meta(what = "Action key", where = "ContactDetails", format = "", info = "Invert the photo's colours")
+ KEY_ACTION_INVERT, //
+ @Meta(what = "Action key", where = "ContactDetails", format = "", info = "Show the photo in 'fullscreen'")
KEY_ACTION_FULLSCREEN, //
- @Meta(what = "", where = "", format = "", info = "")
+ @Meta(what = "Action key", where = "ContactList, ContactDetails, ContactDetailsRaw", format = "", info = "Switch between the available display formats")
KEY_ACTION_SWITCH_FORMAT, // multi-usage
@Meta(what = "Action key", where = "Contact list, Edit Contact", format = "", info = "Add a new contact/field")
KEY_ACTION_ADD, //
# - @n: limit the size to a fixed value 'n'
# - @+: expand the size of this field as much as possible
#
+# In case of lists or multiple-fields values, you can select a specific
+# list or field with:
+# - FIELD@(0): select the first value in a list</li>
+# - FIELD@[1]: select the second field in a multiple-fields value</li>
+#
# You can also add a fixed text if it starts with a simple-quote (').
#
# Example: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x"
#
# You can cycle through them if you have more than one
# (in this case, separate them with a comma (',')
-CONTACT_LIST_FORMAT = NICKNAME@10|FN@+|EMAIL@30|PHOTO@x,FN@+|EMAIL@40
+CONTACT_LIST_FORMAT = NICKNAME@10|FN@+|EMAIL@30|PHOTO@x,N@[0]@20|N@[1]@+|EMAIL@40
# The list of details to show in View Contact mode:
# - Each detail (separated by a pipe "|" character) is visible on its own line
# The size of the details' labels
CONTACT_DETAILS_LABEL_WIDTH = 12
+
+CONTACT_DETAILS_DEFAULT_FN = N@[1]|' |N@[0]
+
+CONTACT_DETAILS_SHOW_COMPUTED_FN = true
private boolean fullscreenImage;
private Panel infoPanel;
private Label note;
- ResourceBundle map;
+
+ // from .properties file:
+ private int labelSize = -1;
+ private String infoFormat = "";
+ //
public ContactDetails(Contact contact) {
- map = Bundles.getBundle("display");
+ // Get the .properties info:
+ ResourceBundle map = Bundles.getBundle("display");
+
+ try {
+ labelSize = Integer.parseInt(map
+ .getString("CONTACT_DETAILS_LABEL_WIDTH"));
+
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ labelSize = -1;
+ } catch (MissingResourceException e) {
+ labelSize = -1;
+ }
+
+ try {
+ infoFormat = map.getString("CONTACT_DETAILS_INFO");
+ } catch (MissingResourceException e) {
+ e.printStackTrace();
+ }
+ //
BorderLayout blayout = new BorderLayout();
setLayoutManager(blayout);
infoPanel.removeAllComponents();
String name = contact.getPreferredDataValue("FN");
- if (name == null || name.length() == 0) {
- // TODO format it ourself
- name = contact.getPreferredDataValue("N");
- }
-
infoPanel.addComponent(UiColors.Element.VIEW_CONTACT_NAME
.createLabel(name));
infoPanel.addComponent(UiColors.Element.VIEW_CONTACT_NORMAL
.createLabel(""));
// List of infos:
- int labelSize = -1;
- try {
- labelSize = Integer.parseInt(map
- .getString("CONTACT_DETAILS_LABEL_WIDTH"));
-
- } catch (NumberFormatException e) {
- e.printStackTrace();
- labelSize = -1;
- } catch (MissingResourceException e) {
- labelSize = -1;
- }
-
- String infoFormat = "";
- try {
- infoFormat = map.getString("CONTACT_DETAILS_INFO");
- } catch (MissingResourceException e) {
- e.printStackTrace();
- }
-
String[] infos = infoFormat.split("\\|");
for (String info : infos) {
// # - "=FIELD" will take the preferred value for this field
public String getDefaultAnswer() {
Data data = getData();
if (data != null) {
- return data.getValue().replaceAll("\n", "\\\\n");
+ return data.getRawValue().replaceAll("\n", "\\\\n");
}
return null;
public String callback(String answer) {
Data data = getData();
if (data != null) {
- data.setValue(answer.replaceAll("\\\\n", "\n"));
+ data.setRawValue(answer.replaceAll("\\\\n", "\n"));
return null;
}