| 1 | package be.nikiroo.jvcard.tui; |
| 2 | |
| 3 | import java.io.File; |
| 4 | |
| 5 | import be.nikiroo.jvcard.Card; |
| 6 | import be.nikiroo.jvcard.Contact; |
| 7 | import be.nikiroo.jvcard.Data; |
| 8 | import be.nikiroo.jvcard.launcher.Main; |
| 9 | import be.nikiroo.jvcard.resources.Trans.StringId; |
| 10 | |
| 11 | import com.googlecode.lanterna.input.KeyStroke; |
| 12 | import com.googlecode.lanterna.input.KeyType; |
| 13 | |
| 14 | /** |
| 15 | * This class represents a keybinding; it encapsulates data about the actual key |
| 16 | * to press and the associated action to take. |
| 17 | * |
| 18 | * You are expected to subclass it if you want to create a custom action. |
| 19 | * |
| 20 | * @author niki |
| 21 | * |
| 22 | */ |
| 23 | public class KeyAction { |
| 24 | /** |
| 25 | * The keybinding mode that will be triggered by this action. |
| 26 | * |
| 27 | * @author niki |
| 28 | * |
| 29 | */ |
| 30 | public enum Mode { |
| 31 | NONE, MOVE, BACK, HELP, FILE_LIST, CONTACT_LIST, CONTACT_DETAILS_RAW, CONTACT_DETAILS, ASK_USER, ASK_USER_KEY, |
| 32 | } |
| 33 | |
| 34 | public enum DataType { |
| 35 | /** |
| 36 | * A list of Card {@link File}s. |
| 37 | */ |
| 38 | CARD_FILES, |
| 39 | /** |
| 40 | * Contains a list of contacts. |
| 41 | */ |
| 42 | CARD, |
| 43 | /** |
| 44 | * All the known informations about a specific contact person or |
| 45 | * company. |
| 46 | */ |
| 47 | CONTACT, |
| 48 | /** |
| 49 | * An information about a contact. |
| 50 | */ |
| 51 | DATA, |
| 52 | /** |
| 53 | * Empty. |
| 54 | */ |
| 55 | NONE |
| 56 | } |
| 57 | |
| 58 | private StringId id; |
| 59 | private KeyStroke key; |
| 60 | private Mode mode; |
| 61 | private String message; |
| 62 | private boolean error; |
| 63 | |
| 64 | public KeyAction(Mode mode, KeyStroke key, StringId id) { |
| 65 | this.id = id; |
| 66 | this.key = key; |
| 67 | this.mode = mode; |
| 68 | } |
| 69 | |
| 70 | public KeyAction(Mode mode, KeyType keyType, StringId id) { |
| 71 | this.id = id; |
| 72 | this.key = new KeyStroke(keyType); |
| 73 | this.mode = mode; |
| 74 | } |
| 75 | |
| 76 | public KeyAction(Mode mode, char car, StringId id) { |
| 77 | this.id = id; |
| 78 | this.key = new KeyStroke(car, false, false); |
| 79 | this.mode = mode; |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Return the key used to trigger this {@link KeyAction}. |
| 84 | * |
| 85 | * @return the shortcut {@link KeyStroke} to use to invoke this |
| 86 | * {@link KeyAction} |
| 87 | */ |
| 88 | public KeyStroke getKey() { |
| 89 | return key; |
| 90 | } |
| 91 | |
| 92 | /** |
| 93 | * Return the associated message if any. |
| 94 | * |
| 95 | * @return the associated message or NULL |
| 96 | */ |
| 97 | public String getMessage() { |
| 98 | return message; |
| 99 | } |
| 100 | |
| 101 | /** |
| 102 | * Set a message to display to the user. This message will be get after |
| 103 | * {@link KeyAction#getObject()} has been called. |
| 104 | * |
| 105 | * @param message |
| 106 | * the message |
| 107 | * @param error |
| 108 | * TRUE for an error message, FALSE for information |
| 109 | */ |
| 110 | public void setMessage(String message, boolean error) { |
| 111 | this.message = message; |
| 112 | this.error = error; |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * Check if the included message ({@link KeyAction#getMessage()}) is an |
| 117 | * error message or an information message. |
| 118 | * |
| 119 | * @return TRUE for error, FALSE for information |
| 120 | */ |
| 121 | public boolean isError() { |
| 122 | return error; |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * Check if the given {@link KeyStroke} should trigger this action. |
| 127 | * |
| 128 | * @param mkey |
| 129 | * the {@link KeyStroke} to check against |
| 130 | * |
| 131 | * @return TRUE if it should |
| 132 | */ |
| 133 | public boolean match(KeyStroke mkey) { |
| 134 | if (mkey == null || key == null) |
| 135 | return false; |
| 136 | |
| 137 | if (mkey.getKeyType() == key.getKeyType()) { |
| 138 | if (mkey.getKeyType() != KeyType.Character) |
| 139 | return true; |
| 140 | |
| 141 | return mkey.getCharacter() == key.getCharacter(); |
| 142 | } |
| 143 | |
| 144 | return false; |
| 145 | } |
| 146 | |
| 147 | /** |
| 148 | * The mode to change to when this action is completed. |
| 149 | * |
| 150 | * @return the new mode |
| 151 | */ |
| 152 | public Mode getMode() { |
| 153 | return mode; |
| 154 | } |
| 155 | |
| 156 | /** |
| 157 | * Get the associated {@link StringId} or NULL if the action must not be |
| 158 | * displayed in the action bar. |
| 159 | * |
| 160 | * @return the {@link StringId} or NULL |
| 161 | */ |
| 162 | public StringId getStringId() { |
| 163 | return id; |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Get the associated object as a {@link Card} if it is a {@link Card}. |
| 168 | * |
| 169 | * @return the associated {@link Card} or NULL |
| 170 | */ |
| 171 | public Card getCard() { |
| 172 | Object o = getObject(); |
| 173 | if (o instanceof Card) |
| 174 | return (Card) o; |
| 175 | return null; |
| 176 | } |
| 177 | |
| 178 | /** |
| 179 | * Get the associated object as a {@link Contact} if it is a {@link Contact} |
| 180 | * . |
| 181 | * |
| 182 | * @return the associated {@link Contact} or NULL |
| 183 | */ |
| 184 | public Contact getContact() { |
| 185 | Object o = getObject(); |
| 186 | if (o instanceof Contact) |
| 187 | return (Contact) o; |
| 188 | return null; |
| 189 | } |
| 190 | |
| 191 | /** |
| 192 | * Get the associated object as a {@link Data} if it is a {@link Data}. |
| 193 | * |
| 194 | * @return the associated {@link Data} or NULL |
| 195 | */ |
| 196 | public Data getData() { |
| 197 | Object o = getObject(); |
| 198 | if (o instanceof Data) |
| 199 | return (Data) o; |
| 200 | return null; |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * Return the associated target object. You should use |
| 205 | * {@link KeyAction#getCard()}, {@link KeyAction#getContact()} or |
| 206 | * {@link KeyAction#getData()} instead if you know the kind of object it is. |
| 207 | * |
| 208 | * <p> |
| 209 | * |
| 210 | * You are expected to override this method to return your object, the 3 |
| 211 | * afore-mentioned methods will use this one as the source. |
| 212 | * |
| 213 | * <p> |
| 214 | * |
| 215 | * <b>DO NOT</b> process data here, this method will be called often; this |
| 216 | * should only be a <b>getter</b> method. |
| 217 | * |
| 218 | * @return the associated object |
| 219 | */ |
| 220 | public Object getObject() { |
| 221 | return null; |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * The method which is called when the action is performed. You can subclass |
| 226 | * it if you want to customise the action (by default, it just accepts the |
| 227 | * mode change (see {@link KeyAction#getMode}). |
| 228 | * |
| 229 | * @return false to cancel mode change |
| 230 | */ |
| 231 | public boolean onAction() { |
| 232 | return true; |
| 233 | } |
| 234 | |
| 235 | /** |
| 236 | * Used to callback a function from the menu when the user has to introduce |
| 237 | * some text. |
| 238 | * |
| 239 | * @param answer |
| 240 | * the user answer |
| 241 | * |
| 242 | * @return an error message if any |
| 243 | */ |
| 244 | public String callback(String answer) { |
| 245 | return null; |
| 246 | } |
| 247 | |
| 248 | /** |
| 249 | * When asking a question to the user, return the question. |
| 250 | * |
| 251 | * @return the question |
| 252 | */ |
| 253 | public String getQuestion() { |
| 254 | return null; |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * When asking a question to the user (not for one-key mode), return the |
| 259 | * default answer. |
| 260 | * |
| 261 | * @return the default answer |
| 262 | */ |
| 263 | public String getDefaultAnswer() { |
| 264 | return null; |
| 265 | } |
| 266 | |
| 267 | /** |
| 268 | * Translate the given {@link KeyStroke} into a user text {@link String} of |
| 269 | * size 3. |
| 270 | * |
| 271 | * @param key |
| 272 | * the key to translate |
| 273 | * |
| 274 | * @return the translated text |
| 275 | */ |
| 276 | static public String trans(KeyStroke key) { |
| 277 | String keyTrans = ""; |
| 278 | |
| 279 | switch (key.getKeyType()) { |
| 280 | case Enter: |
| 281 | if (Main.isUnicode()) |
| 282 | keyTrans = " ⤶ "; |
| 283 | else |
| 284 | keyTrans = Main.trans(StringId.KEY_ENTER); |
| 285 | break; |
| 286 | case Tab: |
| 287 | if (Main.isUnicode()) |
| 288 | keyTrans = " ↹ "; |
| 289 | else |
| 290 | keyTrans = Main.trans(StringId.KEY_TAB); |
| 291 | |
| 292 | break; |
| 293 | case Character: |
| 294 | keyTrans = " " + key.getCharacter() + " "; |
| 295 | break; |
| 296 | default: |
| 297 | keyTrans = "" + key.getKeyType(); |
| 298 | int width = 3; |
| 299 | if (keyTrans.length() > width) { |
| 300 | keyTrans = keyTrans.substring(0, width); |
| 301 | } else if (keyTrans.length() < width) { |
| 302 | keyTrans = keyTrans |
| 303 | + new String(new char[width - keyTrans.length()]) |
| 304 | .replace('\0', ' '); |
| 305 | } |
| 306 | break; |
| 307 | } |
| 308 | |
| 309 | return keyTrans; |
| 310 | } |
| 311 | } |