private List<Contact> contacts;
private File file;
private boolean dirty;
+ private String name;
public Card(File file, Format format) throws IOException {
this.file = file;
+ if (file != null) {
+ name = file.getName();
+ }
+
BufferedReader buffer = new BufferedReader(new FileReader(file));
List<String> lines = new LinkedList<String>();
for (String line = buffer.readLine(); line != null; line = buffer
}
load(lines, format);
+ dirty = false; // initial load, so no change yet
}
public List<Contact> getContacts() {
return dirty;
}
+ /**
+ * Return the name of this card.
+ *
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
/**
* Notify that this element has unsaved changes.
*/
return Parser.toString(this, format, startingBKey);
}
+ /**
+ * Return a {@link String} representation of this contact formated
+ * accordingly to the given format.
+ *
+ * The format is basically a list of field names separated by a pipe and
+ * optionally parametrised. 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>
+ * <li>@+: expand the size of this field as much as possible</li>
+ * </ul>
+ *
+ * Example: "N@10|FN@20|NICK@+|PHOTO@x"
+ *
+ * @param format
+ * the format to use
+ *
+ * @return the {@link String} representation
+ */
+ public String toString(String format) {
+ return toString(format, "|", null, -1);
+ }
+
/**
* Return a {@link String} representation of this contact formated
* accordingly to the given format.
* the format to use
* @param separator
* the separator {@link String} to use between fields
+ * @param padding
+ * the {@link String} to use for left and right padding
* @param width
* a fixed width or -1 for "as long as needed"
*
* @return the {@link String} representation
*/
- public String toString(String format, String separator, int width) {
+ public String toString(String format, String separator, String padding,
+ int width) {
StringBuilder builder = new StringBuilder();
- String[] formatFields = format.split("\\|");
- if (width > -1 && separator != null && separator.length() > 0
- && formatFields.length > 1) {
- int swidth = (formatFields.length - 1) * separator.length();
- if (swidth >= width) {
- int num = width / separator.length();
- int remainder = width % separator.length();
-
- if (remainder > 0)
- num++;
-
- while (builder.length() < width) {
- if (builder.length() + separator.length() <= width)
- builder.append(separator);
- else
- builder.append(separator
- .substring(0, (builder.length() + separator
- .length())
- - width));
- }
+ for (String str : toStringArray(format, separator, padding, width)) {
+ builder.append(str);
+ }
- return builder.toString();
- }
+ return builder.toString();
+ }
- width -= swidth;
+ /**
+ * Return a {@link String} representation of this contact formated
+ * accordingly to the given format, part by part.
+ *
+ * The format is basically a list of field names separated by a pipe and
+ * optionally parametrised. 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>
+ * <li>@+: expand the size of this field as much as possible</li>
+ * </ul>
+ *
+ * Example: "N@10|FN@20|NICK@+|PHOTO@x"
+ *
+ * @param format
+ * the format to use
+ * @param separator
+ * the separator {@link String} to use between fields
+ * @param padding
+ * the {@link String} to use for left and right padding
+ * @param width
+ * a fixed width or -1 for "as long as needed"
+ *
+ * @return the {@link String} representation
+ */
+ public String[] toStringArray(String format, String separator,
+ String padding, int width) {
+ if (width > -1) {
+ int numOfFields = format.split("\\|").length;
+ if (separator != null)
+ width -= (numOfFields - 1) * separator.length();
+ if (padding != null)
+ width -= (numOfFields) * (2 * padding.length());
+
+ if (width < 0)
+ width = 0;
}
- for (String str : toStringArray(format, width)) {
- builder.append(str);
+ List<String> str = new LinkedList<String>();
+
+ boolean first = true;
+ for (String s : toStringArray(format, width)) {
+ if (!first) {
+ str.add(separator);
+ }
+
+ if (padding != null)
+ str.add(padding + s + padding);
+ else
+ str.add(s);
+
+ first = false;
}
- return builder.toString();
+ return str.toArray(new String[] {});
}
/**
* accordingly to the given format, part by part.
*
* The format is basically a list of field names separated by a pipe and
- * optionally parametrised. See {@link Contact#toString} for more
- * information about the format.
+ * optionally parametrised. 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>
+ * <li>@+: expand the size of this field as much as possible</li>
+ * </ul>
+ *
+ * Example: "N@10|FN@20|NICK@+|PHOTO@x"
*
* @param format
* the format to use
int totalSize = 0;
if (width == 0) {
+ for (int i = 0; i < formatFields.length; i++) {
+ str.add("");
+ }
+
return str.toArray(new String[] {});
}
for (int i = 0; i < values.length; i++) {
if (expandedFields[i]) {
if (remainder > 0) {
- values[i] = values[i]
- + new String(new char[remainder]).replace(
- '\0', ' ');
+ values[i] = values[i] + fixedString("", remainder);
remainder = 0;
}
if (padPerItem > 0) {
- values[i] = values[i]
- + new String(new char[padPerItem]).replace(
- '\0', ' ');
+ values[i] = values[i] + fixedString("", padPerItem);
}
}
}
static private String fixedString(String string, int size) {
int length = string.length();
- if (length > size)
+ if (length > size) {
string = string.substring(0, size);
- else if (length < size)
+ } else if (length < size) {
string = string
+ new String(new char[size - length]).replace('\0', ' ');
+ }
return string;
}
}
}
- if (add.length() > 0) {
- list.add(add);
- }
-
+ list.add(add);
return add.length();
}
*
*/
public enum StringId {
- KEY_ACTION_BACK, KEY_ACTION_HELP, KEY_ACTION_VIEW_CONTACT, KEY_ACTION_VIEW_CARD, KEY_ACTION_EDIT_CONTACT, KEY_ACTION_SWITCH_FORMAT, TITLE, NULL;
+ DUMMY, // <-- TODO : remove
+ KEY_ACTION_BACK, KEY_ACTION_HELP, KEY_ACTION_VIEW_CONTACT, KEY_ACTION_VIEW_CARD, KEY_ACTION_EDIT_CONTACT, KEY_ACTION_SWITCH_FORMAT, NULL;
public String trans() {
return Trans.getInstance().trans(this);
// TODO: get from a file instead?
map.put(StringId.NULL, "");
+ map.put(StringId.DUMMY, "[dummy]");
map.put(StringId.KEY_ACTION_BACK, "Back");
- map.put(StringId.TITLE, "[ jVcard: version 0.9 ]");
map.put(StringId.KEY_ACTION_VIEW_CONTACT, "view");
map.put(StringId.KEY_ACTION_EDIT_CONTACT, "edit");
- map.put(StringId.KEY_ACTION_SWITCH_FORMAT, "Change view");
+ map.put(StringId.KEY_ACTION_SWITCH_FORMAT, "Change view");
}
}
--- /dev/null
+package be.nikiroo.jvcard.tui;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+import be.nikiroo.jvcard.Card;
+import be.nikiroo.jvcard.parsers.Format;
+import be.nikiroo.jvcard.tui.panes.ContactList;
+import be.nikiroo.jvcard.tui.panes.FileList;
+
+import com.googlecode.lanterna.TerminalSize;
+import com.googlecode.lanterna.TextColor;
+import com.googlecode.lanterna.gui2.BasicWindow;
+import com.googlecode.lanterna.gui2.Button;
+import com.googlecode.lanterna.gui2.DefaultWindowManager;
+import com.googlecode.lanterna.gui2.EmptySpace;
+import com.googlecode.lanterna.gui2.GridLayout;
+import com.googlecode.lanterna.gui2.Label;
+import com.googlecode.lanterna.gui2.MultiWindowTextGUI;
+import com.googlecode.lanterna.gui2.Panel;
+import com.googlecode.lanterna.gui2.TextBox;
+import com.googlecode.lanterna.gui2.Window;
+import com.googlecode.lanterna.gui2.table.Table;
+import com.googlecode.lanterna.screen.Screen;
+import com.googlecode.lanterna.screen.TerminalScreen;
+import com.googlecode.lanterna.terminal.DefaultTerminalFactory;
+import com.googlecode.lanterna.terminal.Terminal;
+
+public class Main {
+ public static final String APPLICATION_TITLE = "jVcard";
+ public static final String APPLICATION_VERSION = "0.9";
+
+ public static void main(String[] args) throws IOException {
+ Boolean textMode = null;
+ if (args.length > 0 && args[0].equals("--tui"))
+ textMode = true;
+ if (args.length > 0 && args[0].equals("--gui"))
+ textMode = false;
+
+ Window win = null;
+
+ // TODO: do not hardcode that:
+ Card card = new Card(new File("/home/niki/.addressbook"), Format.Abook);
+ win = new MainWindow(new ContactList(card));
+ //
+ List<File> files = new LinkedList<File>();
+ files.add(new File("/home/niki/vcf/coworkers.vcf"));
+ files.add(new File("/home/niki/vcf/oce.vcf"));
+ win = new MainWindow(new FileList(files));
+ //
+
+ TuiLauncher.start(textMode, win);
+
+ /*
+ * String file = args.length > 0 ? args[0] : null; String file2 =
+ * args.length > 1 ? args[1] : null;
+ *
+ * if (file == null) file =
+ * "/home/niki/workspace/rcard/utils/CVcard/test.vcf"; if (file2 ==
+ * null) file2 = "/home/niki/workspace/rcard/utils/CVcard/test.abook";
+ *
+ * Card card = new Card(new File(file), Format.VCard21);
+ * System.out.println(card.toString());
+ *
+ * System.out.println("\n -- PINE -- \n");
+ *
+ * card = new Card(new File(file2), Format.Abook);
+ * System.out.println(card.toString(Format.Abook));
+ */
+ }
+
+ static private void fullTestTable() throws IOException {
+ final Table<String> table = new Table<String>("Column 1", "Column 2",
+ "Column 3");
+ table.getTableModel().addRow("1", "2", "3");
+ table.setSelectAction(new Runnable() {
+ @Override
+ public void run() {
+ List<String> data = table.getTableModel().getRow(
+ table.getSelectedRow());
+ for (int i = 0; i < data.size(); i++) {
+ System.out.println(data.get(i));
+ }
+ }
+ });
+
+ Window win = new BasicWindow();
+ win.setComponent(table);
+
+ DefaultTerminalFactory factory = new DefaultTerminalFactory();
+ Terminal terminal = factory.createTerminal();
+
+ Screen screen = new TerminalScreen(terminal);
+ screen.startScreen();
+
+ // Create gui and start gui
+ MultiWindowTextGUI gui = new MultiWindowTextGUI(screen,
+ new DefaultWindowManager(), new EmptySpace(TextColor.ANSI.BLUE));
+ gui.addWindowAndWait(win);
+
+ screen.stopScreen();
+ }
+}
package be.nikiroo.jvcard.tui;
-import java.io.File;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import be.nikiroo.jvcard.Card;
import be.nikiroo.jvcard.Contact;
-import be.nikiroo.jvcard.i18n.Trans;
import be.nikiroo.jvcard.i18n.Trans.StringId;
import be.nikiroo.jvcard.tui.KeyAction.Mode;
import be.nikiroo.jvcard.tui.UiColors.Element;
import be.nikiroo.jvcard.tui.panes.MainContent;
import com.googlecode.lanterna.TerminalSize;
+import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.gui2.BasicWindow;
import com.googlecode.lanterna.gui2.BorderLayout;
+import com.googlecode.lanterna.gui2.ComponentRenderer;
import com.googlecode.lanterna.gui2.Direction;
import com.googlecode.lanterna.gui2.Interactable;
import com.googlecode.lanterna.gui2.Label;
import com.googlecode.lanterna.input.KeyType;
/**
- * This is the main "window" of the program. It will host one
- * {@link MainContent} at any one time.
+ * This is the main "window" of the program. It can show up to one
+ * {@link MainContent} at any one time, but keeps a stack of contents.
*
* @author niki
*
public class MainWindow extends BasicWindow {
private List<KeyAction> defaultActions = new LinkedList<KeyAction>();
private List<KeyAction> actions = new LinkedList<KeyAction>();
- private List<MainContent> content = new LinkedList<MainContent>();
+ private List<MainContent> contentStack = new LinkedList<MainContent>();
private boolean actionsPadded;
- private Boolean waitForOneKeyAnswer; // true, false, (null = do not wait for
- // an answer)
+ private boolean waitForOneKeyAnswer;
+ private KeyStroke questionKey; // key that "asked" a question, and to replay
+ // later with an answer
private String title;
private Panel titlePanel;
private Panel mainPanel;
private Panel messagePanel;
private TextBox text;
+ /**
+ * Create a new, empty window.
+ */
public MainWindow() {
this(null);
}
+ /**
+ * Create a new window hosting the given content.
+ *
+ * @param content
+ * the content to host
+ */
public MainWindow(MainContent content) {
super(content == null ? "" : content.getTitle());
llayout.setSpacing(0);
actionPanel.setLayoutManager(llayout);
- llayout = new LinearLayout(Direction.VERTICAL);
- llayout.setSpacing(0);
- titlePanel.setLayoutManager(llayout);
+ BorderLayout blayout = new BorderLayout();
+ titlePanel.setLayoutManager(blayout);
llayout = new LinearLayout(Direction.VERTICAL);
llayout.setSpacing(0);
messagePanel.setLayoutManager(llayout);
- BorderLayout blayout = new BorderLayout();
+ blayout = new BorderLayout();
mainPanel.setLayoutManager(blayout);
blayout = new BorderLayout();
setComponent(mainPanel);
}
+ /**
+ * "push" some content to the window stack.
+ *
+ * @param content
+ * the new top-of-the-stack content
+ */
public void pushContent(MainContent content) {
List<KeyAction> actions = null;
String title = null;
title = content.getTitle();
actions = content.getKeyBindings();
contentPanel.addComponent(content, BorderLayout.Location.CENTER);
- this.content.add(content);
+ this.contentStack.add(content);
Interactable focus = content.nextFocus(null);
if (focus != null)
}
setTitle(title);
- setActions(actions, true, true);
+ setActions(actions, true);
invalidate();
}
+ /**
+ * "pop" the latest, top-of-the-stack content from the window stack.
+ *
+ * @return the removed content if any
+ */
public MainContent popContent() {
MainContent removed = null;
MainContent prev = null;
- if (content.size() > 0)
- removed = content.remove(content.size() - 1);
- if (content.size() > 0)
- prev = content.remove(content.size() - 1);
- pushContent(prev);
- return removed;
- }
+ MainContent content = getContent();
+ if (content != null)
+ removed = contentStack.remove(contentStack.size() - 1);
- /**
- * Set the application title.
- *
- * @param title
- * the new title or NULL for the default title
- */
- public void setTitle(String title) {
- if (title == null) {
- title = Trans.StringId.TITLE.trans();
- }
+ if (contentStack.size() > 0)
+ prev = contentStack.remove(contentStack.size() - 1);
- if (!title.equals(this.title)) {
- super.setTitle(title);
- this.title = title;
- }
-
- Label lbl = new Label(title);
- titlePanel.removeAllComponents();
+ pushContent(prev);
- titlePanel.addComponent(lbl, LinearLayout
- .createLayoutData(LinearLayout.Alignment.Center));
+ return removed;
}
/**
}
}
- public void setQuestion(String mess, boolean oneKey) {
+ /**
+ * Show a question to the user and switch to "ask for answer" mode see
+ * {@link MainWindow#handleQuestion}.
+ *
+ * @param mess
+ * the message to display
+ * @param oneKey
+ * TRUE for a one-key answer, FALSE for a text answer validated
+ * by ENTER
+ */
+ public void setQuestion(KeyStroke key, String mess, boolean oneKey) {
+ questionKey = key;
+ waitForOneKeyAnswer = oneKey;
+
messagePanel.removeAllComponents();
- if (mess != null) {
- waitForOneKeyAnswer = oneKey;
- Panel hpanel = new Panel();
- LinearLayout llayout = new LinearLayout(Direction.HORIZONTAL);
- llayout.setSpacing(0);
- hpanel.setLayoutManager(llayout);
+ Panel hpanel = new Panel();
+ LinearLayout llayout = new LinearLayout(Direction.HORIZONTAL);
+ llayout.setSpacing(0);
+ hpanel.setLayoutManager(llayout);
- Label lbl = UiColors.Element.LINE_MESSAGE_QUESTION.createLabel(" "
- + mess + " ");
- text = new TextBox(new TerminalSize(getSize().getColumns()
- - lbl.getSize().getColumns(), 1));
+ Label lbl = UiColors.Element.LINE_MESSAGE_QUESTION.createLabel(" "
+ + mess + " ");
+ text = new TextBox(new TerminalSize(getSize().getColumns()
+ - lbl.getSize().getColumns(), 1));
- hpanel.addComponent(lbl, LinearLayout
- .createLayoutData(LinearLayout.Alignment.Beginning));
- hpanel.addComponent(text, LinearLayout
- .createLayoutData(LinearLayout.Alignment.Fill));
+ hpanel.addComponent(lbl, LinearLayout
+ .createLayoutData(LinearLayout.Alignment.Beginning));
+ hpanel.addComponent(text, LinearLayout
+ .createLayoutData(LinearLayout.Alignment.Fill));
- messagePanel.addComponent(hpanel, LinearLayout
- .createLayoutData(LinearLayout.Alignment.Beginning));
+ messagePanel.addComponent(hpanel, LinearLayout
+ .createLayoutData(LinearLayout.Alignment.Beginning));
- text.takeFocus();
- }
+ text.takeFocus();
}
@Override
public void draw(TextGUIGraphics graphics) {
- setTitle(title);
if (!actionsPadded) {
// fill with "desc" colour
actionPanel.addComponent(UiColors.Element.ACTION_DESC
super.draw(graphics);
}
- private void setActions(List<KeyAction> actions, boolean allowKeys,
- boolean enableDefaultactions) {
+ @Override
+ public void setTitle(String title) {
+ String prefix = " " + Main.APPLICATION_TITLE + " (version "
+ + Main.APPLICATION_VERSION + ")";
+
+ int count = -1;
+ MainContent content = getContent();
+ if (content != null)
+ count = content.getCount();
+
+ if (title != null) {
+ prefix = prefix + ": ";
+ }
+
+ if (getSize() != null) {
+ if (title != null)
+ title = StringUtils.padString(title, getSize().getColumns());
+ else
+ // cause busy-loop freeze:
+ prefix = StringUtils.padString(prefix, getSize().getColumns());
+ }
+
+ if (!(title + count).equals(this.title)) {
+ this.title = title + count;
+
+ super.setTitle(prefix + title);
+
+ Label lblPrefix = new Label(prefix);
+ UiColors.Element.TITLE_MAIN.themeLabel(lblPrefix);
+
+ Label lblTitle = null;
+ if (title != null) {
+ lblTitle = new Label(title);
+ UiColors.Element.TITLE_VARIABLE.themeLabel(lblTitle);
+ }
+
+ Label lblCount = null;
+ if (count > -1) {
+ lblCount = new Label("[" + count + "]");
+ UiColors.Element.TITLE_COUNT.themeLabel(lblCount);
+ }
+
+ titlePanel.removeAllComponents();
+ titlePanel.addComponent(lblPrefix, BorderLayout.Location.LEFT);
+ if (lblTitle != null)
+ titlePanel.addComponent(lblTitle, BorderLayout.Location.CENTER);
+ if (lblCount != null)
+ titlePanel.addComponent(lblCount, BorderLayout.Location.RIGHT);
+
+ invalidate();
+ }
+ }
+
+ /**
+ * Return the current {@link MainContent} from the stack if any.
+ *
+ * @return the current {@link MainContent}
+ */
+ private MainContent getContent() {
+ if (contentStack.size() > 0) {
+ return contentStack.get(contentStack.size() - 1);
+ }
+
+ return null;
+ }
+
+ /**
+ * Update the list of actions and refresh the action panel.
+ *
+ * @param actions
+ * the list of actions to support
+ * @param enableDefaultactions
+ * TRUE to enable the default actions
+ */
+ private void setActions(List<KeyAction> actions,
+ boolean enableDefaultactions) {
this.actions.clear();
actionsPadded = false;
}
}
+ /**
+ * Handle user input when in "ask for question" mode (see
+ * {@link MainWindow#questionKey}).
+ *
+ * @param key
+ * the key that has been pressed by the user
+ *
+ * @return the user's answer if done
+ */
private String handleQuestion(KeyStroke key) {
String answer = null;
if (answer != null) {
Interactable focus = null;
- if (this.content.size() > 0)
- // focus = content.get(0).getDefaultFocusElement();
- focus = content.get(0).nextFocus(null);
+ MainContent content = getContent();
+ if (content != null)
+ focus = content.nextFocus(null);
focus.takeFocus();
}
return answer;
}
- @Override
- public boolean handleInput(KeyStroke key) {
+ /**
+ * Handle the input in case of "normal" (not "ask for answer") mode.
+ *
+ * @param key
+ * the key that was pressed
+ * @param answer
+ * the answer given for this key
+ *
+ * @return if the window handled the inout
+ */
+ private boolean handleInput(KeyStroke key, String answer) {
boolean handled = false;
- if (waitForOneKeyAnswer != null) {
- String answer = handleQuestion(key);
- if (answer != null) {
- waitForOneKeyAnswer = null;
- setMessage("ANS: " + answer, false);
+ setMessage(null, false);
- handled = true;
- }
- } else {
- setMessage(null, false);
-
- for (KeyAction action : actions) {
- if (!action.match(key))
- continue;
-
- handled = true;
-
- if (action.onAction()) {
- switch (action.getMode()) {
- case MOVE:
- int x = 0;
- int y = 0;
-
- if (action.getKey().getKeyType() == KeyType.ArrowUp)
- x = -1;
- if (action.getKey().getKeyType() == KeyType.ArrowDown)
- x = 1;
- if (action.getKey().getKeyType() == KeyType.ArrowLeft)
- y = -1;
- if (action.getKey().getKeyType() == KeyType.ArrowRight)
- y = 1;
-
- if (content.size() > 0) {
- String err = content.get(content.size() - 1).move(
- x, y);
- if (err != null)
- setMessage(err, true);
- }
+ for (KeyAction action : actions) {
+ if (!action.match(key))
+ continue;
- break;
- // mode with windows:
- case CONTACT_LIST:
- Card card = action.getCard();
- if (card != null) {
- pushContent(new ContactList(card));
- }
- break;
- case CONTACT_DETAILS:
- Contact contact = action.getContact();
- if (contact != null) {
- pushContent(new ContactDetails(contact));
+ MainContent content = getContent();
+ handled = true;
+
+ if (action.onAction()) {
+ switch (action.getMode()) {
+ case MOVE:
+ int x = 0;
+ int y = 0;
+
+ if (action.getKey().getKeyType() == KeyType.ArrowUp)
+ x = -1;
+ if (action.getKey().getKeyType() == KeyType.ArrowDown)
+ x = 1;
+ if (action.getKey().getKeyType() == KeyType.ArrowLeft)
+ y = -1;
+ if (action.getKey().getKeyType() == KeyType.ArrowRight)
+ y = 1;
+
+ if (content != null) {
+ String err = content.move(x, y);
+ if (err != null)
+ setMessage(err, true);
+ }
+
+ break;
+ // mode with windows:
+ case CONTACT_LIST:
+ Card card = action.getCard();
+ if (card != null) {
+ pushContent(new ContactList(card));
+ }
+ break;
+ case CONTACT_DETAILS:
+ Contact contact = action.getContact();
+ if (contact != null) {
+ pushContent(new ContactDetails(contact));
+ }
+ break;
+ // mode interpreted by MainWindow:
+ case HELP:
+ // TODO
+ // setMessage("Help! I need somebody! Help!", false);
+ if (answer == null) {
+ setQuestion(key, "Test question?", false);
+ } else {
+ setMessage("You answered: " + answer, false);
+ }
+
+ handled = true;
+ break;
+ case BACK:
+ if (content != null) {
+ String warning = content.getExitWarning();
+ if (warning != null) {
+ if (answer == null) {
+ setQuestion(key, warning, true);
+ } else {
+ if (answer.equalsIgnoreCase("y")) {
+ popContent();
+ }
+ }
+ } else {
+ popContent();
}
- break;
- // mode interpreted by MainWindow:
- case HELP:
- // TODO
- // setMessage("Help! I need somebody! Help!", false);
- setQuestion("Test question?", false);
- handled = true;
- break;
- case BACK:
- popContent();
- if (content.size() == 0)
- close();
- break;
- default:
- case NONE:
- break;
}
+
+ if (contentStack.size() == 0)
+ close();
+ break;
+ default:
+ case NONE:
+ break;
}
+ }
- break;
+ break;
+ }
+
+ return handled;
+ }
+
+ @Override
+ public boolean handleInput(KeyStroke key) {
+ boolean handled = false;
+
+ if (questionKey != null) {
+ String answer = handleQuestion(key);
+ if (answer != null) {
+ // TODO
+ key = questionKey;
+ questionKey = null;
+
+ handled = handleInput(key, answer);
}
+ } else {
+ handled = handleInput(key, null);
}
if (!handled)
}
public enum Element {
- DEFAULT, ACTION_KEY, ACTION_DESC, LINE_MESSAGE, LINE_MESSAGE_ERR, LINE_MESSAGE_QUESTION, LINE_MESSAGE_ANS, CONTACT_LINE, CONTACT_LINE_SEPARATOR, CONTACT_LINE_SELECTED, CONTACT_LINE_SEPARATOR_SELECTED;
+ DEFAULT, //
+ TITLE_MAIN, TITLE_VARIABLE, TITLE_COUNT, //
+ ACTION_KEY, ACTION_DESC, //
+ LINE_MESSAGE, LINE_MESSAGE_ERR, LINE_MESSAGE_QUESTION, LINE_MESSAGE_ANS, //
+ CONTACT_LINE, CONTACT_LINE_SEPARATOR, CONTACT_LINE_SELECTED, CONTACT_LINE_SEPARATOR_SELECTED, CONTACT_LINE_DIRTY, CONTACT_LINE_DIRTY_SELECTED;
/**
* Get the foreground colour of this element.
addEl(Element.CONTACT_LINE, TextColor.ANSI.WHITE, TextColor.ANSI.BLACK);
addEl(Element.CONTACT_LINE_SELECTED, TextColor.ANSI.WHITE,
TextColor.ANSI.BLUE);
- addEl(Element.CONTACT_LINE_SEPARATOR, TextColor.ANSI.RED, TextColor.ANSI.BLACK);
+ addEl(Element.CONTACT_LINE_SEPARATOR, TextColor.ANSI.RED,
+ TextColor.ANSI.BLACK);
addEl(Element.CONTACT_LINE_SEPARATOR_SELECTED, TextColor.ANSI.RED,
TextColor.ANSI.BLUE);
addEl(Element.LINE_MESSAGE, TextColor.ANSI.BLUE, TextColor.ANSI.WHITE);
TextColor.ANSI.WHITE);
addEl(Element.LINE_MESSAGE_ANS, TextColor.ANSI.BLUE,
TextColor.ANSI.BLACK);
+ addEl(Element.TITLE_MAIN, TextColor.ANSI.WHITE, TextColor.ANSI.BLUE);
+ addEl(Element.TITLE_VARIABLE, TextColor.ANSI.GREEN,
+ TextColor.ANSI.BLUE);
+ addEl(Element.TITLE_COUNT, TextColor.ANSI.RED, TextColor.ANSI.BLUE);
}
private void addEl(Element el, TextColor fore, TextColor back) {
@Override
public String getTitle() {
- // TODO Auto-generated method stub
- return null;
+ String title = null;
+
+ if (contact != null) {
+ title = contact.getPreferredDataValue("FN");
+ if (title == null || title.length() == 0)
+ title = contact.getPreferredDataValue("N");
+ }
+
+ return title;
}
@Override
import java.util.List;
import be.nikiroo.jvcard.Card;
+import be.nikiroo.jvcard.Contact;
import be.nikiroo.jvcard.i18n.Trans;
import be.nikiroo.jvcard.tui.KeyAction;
import be.nikiroo.jvcard.tui.UiColors;
import be.nikiroo.jvcard.tui.KeyAction.DataType;
import be.nikiroo.jvcard.tui.KeyAction.Mode;
import be.nikiroo.jvcard.tui.UiColors.Element;
-import be.nikiroo.jvcard.tui.panes.MainContentList.TextPart;
import com.googlecode.lanterna.input.KeyType;
@Override
public String getExitWarning() {
if (card != null && card.isDirty()) {
- return "Some of your contact information is not saved";
+ //TODO: save? [y/n] instead
+ return "Some of your contact information is not saved; ignore? [Y/N]";
}
+
return null;
}
List<KeyAction> actions = new LinkedList<KeyAction>();
// TODO del, save...
+ // TODO: remove
+ actions.add(new KeyAction(Mode.NONE, 'd', Trans.StringId.DUMMY) {
+ @Override
+ public boolean onAction() {
+ //TODO dummy action
+ int index = getSelectedIndex();
+ Contact c = card.getContacts().get(index);
+ c.updateFrom(c);
+ return false;
+ }
+ });
actions.add(new KeyAction(Mode.CONTACT_DETAILS, 'e',
Trans.StringId.KEY_ACTION_EDIT_CONTACT) {
@Override
@Override
public String getTitle() {
- // TODO Auto-generated method stub
+ if (card != null) {
+ return card.getName();
+ }
+
return null;
}
@Override
protected List<TextPart> getLabel(int index, int width, boolean selected,
boolean focused) {
- List<TextPart> parts = new LinkedList<TextPart>();
+ Contact c = card.getContacts().get(index);
Element el = (focused && selected) ? Element.CONTACT_LINE_SELECTED
: Element.CONTACT_LINE;
Element elSep = (focused && selected) ? Element.CONTACT_LINE_SEPARATOR_SELECTED
: Element.CONTACT_LINE_SEPARATOR;
+ Element elDirty = (focused && selected) ? Element.CONTACT_LINE_DIRTY_SELECTED
+ : Element.CONTACT_LINE_DIRTY;
- // TODO: width/separator to check
- String separator = " ┃ ";
- width -= (format.split("\\|").length + 1) * separator.length();
- String[] array = card.getContacts().get(index).toStringArray(format,
- width);
+ width -= 2; // dirty mark space
// we could use: " ", "┃", "│"...
- for (String str : array) {
- parts.add(new TextPart(str, el));
- parts.add(new TextPart(separator, elSep));
+ String[] array = c.toStringArray(format, "┃", " ", width);
+
+ List<TextPart> parts = new LinkedList<TextPart>();
+ if (c.isDirty()) {
+ parts.add(new TextPart(" ", el));
+ parts.add(new TextPart("*", elDirty));
+ } else {
+ parts.add(new TextPart(" ", elSep));
}
- if (parts.size() > 0)
- parts.remove(parts.get(parts.size() - 1));
+ boolean separator = false;
+ for (String str : array) {
+ parts.add(new TextPart(str, (separator ? elSep : el)));
+ separator = !separator;
+ }
return parts;
}
setLayoutManager(layout);
}
- /**
- * The title to display instead of the application name, or NULL for the
- * default application name.
- *
- * @return the title or NULL
- */
- abstract public String getTitle();
-
- /**
- * Returns an error message ready to be displayed if we should ask something
- * to the user before exiting.
- *
- * @return an error message or NULL
- */
- abstract public String getExitWarning();
-
/**
* The {@link KeyAction#Mode} that links to this {@link MainContent}.
*
*/
abstract public List<KeyAction> getKeyBindings();
+ /**
+ * The title to display instead of the application name, or NULL for the
+ * default application name.
+ *
+ * @return the title or NULL
+ */
+ abstract public String getTitle();
+
+ /**
+ * Returns an error message ready to be displayed if we should ask something
+ * to the user before exiting.
+ *
+ * @return an error message or NULL
+ */
+ public String getExitWarning() {
+ return null;
+ }
+
/**
* Move the active cursor (not the text cursor, but the currently active
* item).
*
* @return the error message to display if any
*/
- abstract public String move(int x, int y);
+ public String move(int x, int y) {
+ return null;
+ }
+
+ /**
+ * Return the number of items in this {@link MainContent}, or -1 if this
+ * {@link MainContent} is not countable.
+ *
+ * @return -1 or the number of present items
+ */
+ public int getCount() {
+ return -1;
+ }
}
* @author niki
*
*/
- protected class TextPart {
+ public class TextPart {
private String text;
private Element element;
ActionListBox listBox, int index, Runnable item,
boolean selected, boolean focused) {
- // TODO: why +5 ?? padding problem?
+ // width "-1" to reserve space for the optional vertical
+ // scroll bar
List<TextPart> parts = MainContentList.this.getLabel(
- index, lines.getSize().getColumns() + 5,
+ index, lines.getSize().getColumns() - 1,
selected, focused);
int position = 0;
graphics.setBackgroundColor(part
.getBackgroundColor());
String label = part.getText();
+
graphics.putString(position, 0, label);
position += label.length();
}
return null;
}
+ @Override
+ public int getCount() {
+ return lines.getItemCount();
+ }
+
/**
* Return the representation of the selected line, in {@link TextPart}s.
*
if(layout.containsKey(Location.LEFT)) {
Component leftComponent = layout.get(Location.LEFT);
leftComponentWidth = Math.min(leftComponent.getPreferredSize().getColumns(), availableHorizontalSpace);
+
+ /*
+ if(leftComponentWidth == availableHorizontalSpace ){
+ if(layout.containsKey(Location.RIGHT))
+ leftComponentWidth--;
+ if(layout.containsKey(Location.CENTER))
+ leftComponentWidth--;
+ }*/
+
leftComponent.setPosition(new TerminalPosition(0, topComponentHeight));
leftComponent.setSize(new TerminalSize(leftComponentWidth, availableVerticalSpace));
availableHorizontalSpace -= leftComponentWidth;
+
+ if(availableHorizontalSpace<=0)
+ availableHorizontalSpace=1;
}
if(layout.containsKey(Location.RIGHT)) {
Component rightComponent = layout.get(Location.RIGHT);
int rightComponentWidth = Math.min(rightComponent.getPreferredSize().getColumns(), availableHorizontalSpace);
+
+ /*
+ if(rightComponentWidth == availableHorizontalSpace ){
+ if(layout.containsKey(Location.CENTER))
+ rightComponentWidth--;
+ }*/
+
rightComponent.setPosition(new TerminalPosition(area.getColumns() - rightComponentWidth, topComponentHeight));
rightComponent.setSize(new TerminalSize(rightComponentWidth, availableVerticalSpace));
availableHorizontalSpace -= rightComponentWidth;
+
+ if(availableHorizontalSpace<=0)
+ availableHorizontalSpace=1;
}
if(layout.containsKey(Location.CENTER)) {
Component centerComponent = layout.get(Location.CENTER);