Commit | Line | Data |
---|---|---|
a3b510ab NR |
1 | package be.nikiroo.jvcard.tui; |
2 | ||
bcb54330 | 3 | import java.util.ArrayList; |
a3b510ab NR |
4 | import java.util.Arrays; |
5 | import java.util.LinkedList; | |
6 | import java.util.List; | |
7 | ||
7da41ecd NR |
8 | import be.nikiroo.jvcard.launcher.Main; |
9 | import be.nikiroo.jvcard.resources.StringUtils; | |
10 | import be.nikiroo.jvcard.resources.Trans.StringId; | |
a3b510ab NR |
11 | import be.nikiroo.jvcard.tui.KeyAction.Mode; |
12 | import be.nikiroo.jvcard.tui.UiColors.Element; | |
f04d8b1c | 13 | import be.nikiroo.jvcard.tui.panes.ContactDetails; |
296a0b75 | 14 | import be.nikiroo.jvcard.tui.panes.ContactDetailsRaw; |
fae07ea7 NR |
15 | import be.nikiroo.jvcard.tui.panes.ContactList; |
16 | import be.nikiroo.jvcard.tui.panes.MainContent; | |
a3b510ab NR |
17 | |
18 | import com.googlecode.lanterna.TerminalSize; | |
19 | import com.googlecode.lanterna.gui2.BasicWindow; | |
20 | import com.googlecode.lanterna.gui2.BorderLayout; | |
21 | import com.googlecode.lanterna.gui2.Direction; | |
22 | import com.googlecode.lanterna.gui2.Interactable; | |
23 | import com.googlecode.lanterna.gui2.Label; | |
24 | import com.googlecode.lanterna.gui2.LinearLayout; | |
25 | import com.googlecode.lanterna.gui2.Panel; | |
26 | import com.googlecode.lanterna.gui2.TextBox; | |
a3b510ab NR |
27 | import com.googlecode.lanterna.gui2.Window; |
28 | import com.googlecode.lanterna.input.KeyStroke; | |
29 | import com.googlecode.lanterna.input.KeyType; | |
30 | ||
31 | /** | |
0b0b2b0f NR |
32 | * This is the main "window" of the program. It can show up to one |
33 | * {@link MainContent} at any one time, but keeps a stack of contents. | |
a3b510ab NR |
34 | * |
35 | * @author niki | |
36 | * | |
37 | */ | |
f720df72 | 38 | |
a3b510ab NR |
39 | public class MainWindow extends BasicWindow { |
40 | private List<KeyAction> defaultActions = new LinkedList<KeyAction>(); | |
41 | private List<KeyAction> actions = new LinkedList<KeyAction>(); | |
0b0b2b0f | 42 | private List<MainContent> contentStack = new LinkedList<MainContent>(); |
f720df72 | 43 | private UserQuestion userQuestion; |
bcb54330 | 44 | private String titleCache; |
a3b510ab NR |
45 | private Panel titlePanel; |
46 | private Panel mainPanel; | |
47 | private Panel contentPanel; | |
48 | private Panel actionPanel; | |
49 | private Panel messagePanel; | |
50 | private TextBox text; | |
51 | ||
f720df72 NR |
52 | /** |
53 | * Information about a question to ask the user and its answer. | |
54 | * | |
55 | * @author niki | |
56 | * | |
57 | */ | |
58 | private class UserQuestion { | |
59 | private boolean oneKeyAnswer; | |
60 | private KeyAction action; | |
61 | private String answer; | |
62 | ||
63 | /** | |
64 | * Create a new {@link UserQuestion}. | |
65 | * | |
66 | * @param action | |
67 | * the action that triggered the question | |
68 | * @param oneKeyAnswer | |
69 | * TRUE if we expect a one-key answer | |
70 | */ | |
71 | public UserQuestion(KeyAction action, boolean oneKeyAnswer) { | |
72 | this.action = action; | |
73 | this.oneKeyAnswer = oneKeyAnswer; | |
74 | } | |
75 | ||
76 | /** | |
77 | * Return the {@link KeyAction} that triggered the question. | |
78 | * | |
79 | * @return the {@link KeyAction} | |
80 | */ | |
81 | public KeyAction getAction() { | |
82 | return action; | |
83 | } | |
84 | ||
85 | /** | |
86 | * Check if a one-key answer is expected. | |
87 | * | |
88 | * @return TRUE if a one-key answer is expected | |
89 | */ | |
90 | public boolean isOneKeyAnswer() { | |
91 | return oneKeyAnswer; | |
92 | } | |
93 | ||
94 | /** | |
95 | * Return the user answer. | |
96 | * | |
97 | * @return the user answer | |
98 | */ | |
99 | public String getAnswer() { | |
100 | return answer; | |
101 | } | |
102 | ||
103 | /** | |
104 | * Set the user answer. | |
105 | * | |
106 | * @param answer | |
107 | * the new answer | |
108 | */ | |
109 | public void setAnswer(String answer) { | |
110 | this.answer = answer; | |
111 | } | |
112 | } | |
113 | ||
0b0b2b0f NR |
114 | /** |
115 | * Create a new, empty window. | |
116 | */ | |
a3b510ab NR |
117 | public MainWindow() { |
118 | this(null); | |
119 | } | |
120 | ||
0b0b2b0f NR |
121 | /** |
122 | * Create a new window hosting the given content. | |
123 | * | |
124 | * @param content | |
125 | * the content to host | |
126 | */ | |
a3b510ab NR |
127 | public MainWindow(MainContent content) { |
128 | super(content == null ? "" : content.getTitle()); | |
129 | ||
130 | setHints(Arrays.asList(Window.Hint.FULL_SCREEN, | |
131 | Window.Hint.NO_DECORATIONS, Window.Hint.FIT_TERMINAL_WINDOW)); | |
132 | ||
133 | defaultActions.add(new KeyAction(Mode.BACK, 'q', | |
134 | StringId.KEY_ACTION_BACK)); | |
135 | defaultActions.add(new KeyAction(Mode.BACK, KeyType.Escape, | |
136 | StringId.NULL)); | |
137 | defaultActions.add(new KeyAction(Mode.HELP, 'h', | |
138 | StringId.KEY_ACTION_HELP)); | |
139 | defaultActions.add(new KeyAction(Mode.HELP, KeyType.F1, StringId.NULL)); | |
140 | ||
141 | actionPanel = new Panel(); | |
142 | contentPanel = new Panel(); | |
143 | mainPanel = new Panel(); | |
144 | messagePanel = new Panel(); | |
145 | titlePanel = new Panel(); | |
146 | ||
147 | Panel actionMessagePanel = new Panel(); | |
148 | ||
149 | LinearLayout llayout = new LinearLayout(Direction.HORIZONTAL); | |
150 | llayout.setSpacing(0); | |
151 | actionPanel.setLayoutManager(llayout); | |
152 | ||
0b0b2b0f NR |
153 | BorderLayout blayout = new BorderLayout(); |
154 | titlePanel.setLayoutManager(blayout); | |
a3b510ab NR |
155 | |
156 | llayout = new LinearLayout(Direction.VERTICAL); | |
157 | llayout.setSpacing(0); | |
158 | messagePanel.setLayoutManager(llayout); | |
159 | ||
0b0b2b0f | 160 | blayout = new BorderLayout(); |
a3b510ab NR |
161 | mainPanel.setLayoutManager(blayout); |
162 | ||
163 | blayout = new BorderLayout(); | |
164 | contentPanel.setLayoutManager(blayout); | |
165 | ||
166 | blayout = new BorderLayout(); | |
167 | actionMessagePanel.setLayoutManager(blayout); | |
168 | ||
169 | actionMessagePanel | |
170 | .addComponent(messagePanel, BorderLayout.Location.TOP); | |
171 | actionMessagePanel.addComponent(actionPanel, | |
172 | BorderLayout.Location.CENTER); | |
173 | ||
174 | mainPanel.addComponent(titlePanel, BorderLayout.Location.TOP); | |
175 | mainPanel.addComponent(contentPanel, BorderLayout.Location.CENTER); | |
176 | mainPanel | |
177 | .addComponent(actionMessagePanel, BorderLayout.Location.BOTTOM); | |
178 | ||
179 | pushContent(content); | |
180 | ||
181 | setComponent(mainPanel); | |
182 | } | |
183 | ||
0b0b2b0f NR |
184 | /** |
185 | * "push" some content to the window stack. | |
186 | * | |
187 | * @param content | |
188 | * the new top-of-the-stack content | |
189 | */ | |
a3b510ab NR |
190 | public void pushContent(MainContent content) { |
191 | List<KeyAction> actions = null; | |
a3b510ab NR |
192 | |
193 | contentPanel.removeAllComponents(); | |
194 | if (content != null) { | |
a3b510ab NR |
195 | actions = content.getKeyBindings(); |
196 | contentPanel.addComponent(content, BorderLayout.Location.CENTER); | |
0b0b2b0f | 197 | this.contentStack.add(content); |
9c8baf0c NR |
198 | |
199 | Interactable focus = content.nextFocus(null); | |
200 | if (focus != null) | |
201 | focus.takeFocus(); | |
a3b510ab NR |
202 | } |
203 | ||
bcb54330 | 204 | setTitle(); |
0b0b2b0f | 205 | setActions(actions, true); |
a3b510ab NR |
206 | } |
207 | ||
0b0b2b0f NR |
208 | /** |
209 | * "pop" the latest, top-of-the-stack content from the window stack. | |
210 | * | |
211 | * @return the removed content if any | |
212 | */ | |
9c8baf0c NR |
213 | public MainContent popContent() { |
214 | MainContent removed = null; | |
215 | MainContent prev = null; | |
9c8baf0c | 216 | |
0b0b2b0f NR |
217 | MainContent content = getContent(); |
218 | if (content != null) | |
219 | removed = contentStack.remove(contentStack.size() - 1); | |
9c8baf0c | 220 | |
0b0b2b0f NR |
221 | if (contentStack.size() > 0) |
222 | prev = contentStack.remove(contentStack.size() - 1); | |
a3b510ab | 223 | |
0b0b2b0f | 224 | pushContent(prev); |
a3b510ab | 225 | |
0b0b2b0f | 226 | return removed; |
a3b510ab NR |
227 | } |
228 | ||
9c8baf0c NR |
229 | /** |
230 | * Show the given message on screen. It will disappear at the next action. | |
231 | * | |
232 | * @param mess | |
233 | * the message to display | |
234 | * @param error | |
235 | * TRUE for an error message, FALSE for an information message | |
bcb54330 NR |
236 | * |
237 | * @return TRUE if changes were performed | |
9c8baf0c | 238 | */ |
bcb54330 NR |
239 | public boolean setMessage(String mess, boolean error) { |
240 | if (mess != null || messagePanel.getChildCount() > 0) { | |
241 | messagePanel.removeAllComponents(); | |
242 | if (mess != null) { | |
243 | Element element = (error ? UiColors.Element.LINE_MESSAGE_ERR | |
244 | : UiColors.Element.LINE_MESSAGE); | |
245 | Label lbl = element.createLabel(" " + mess + " "); | |
246 | messagePanel.addComponent(lbl, LinearLayout | |
247 | .createLayoutData(LinearLayout.Alignment.Center)); | |
248 | } | |
249 | return true; | |
250 | } | |
251 | ||
252 | return false; | |
253 | } | |
254 | ||
255 | /** | |
256 | * Show a question to the user and switch to "ask for answer" mode see | |
257 | * {@link MainWindow#handleQuestion}. The user will be asked to enter some | |
258 | * answer and confirm with ENTER. | |
259 | * | |
296a0b75 NR |
260 | * @param action |
261 | * the related action | |
bcb54330 NR |
262 | * @param question |
263 | * the question to ask | |
264 | * @param initial | |
265 | * the initial answer if any (to be edited by the user) | |
266 | */ | |
296a0b75 NR |
267 | public void setQuestion(KeyAction action, String question, String initial) { |
268 | setQuestion(action, question, initial, false); | |
bcb54330 NR |
269 | } |
270 | ||
271 | /** | |
272 | * Show a question to the user and switch to "ask for answer" mode see | |
273 | * {@link MainWindow#handleQuestion}. The user will be asked to hit one key | |
274 | * as an answer. | |
275 | * | |
296a0b75 NR |
276 | * @param action |
277 | * the related action | |
bcb54330 NR |
278 | * @param question |
279 | * the question to ask | |
280 | */ | |
296a0b75 NR |
281 | public void setQuestion(KeyAction action, String question) { |
282 | setQuestion(action, question, null, true); | |
9c8baf0c NR |
283 | } |
284 | ||
0b0b2b0f NR |
285 | /** |
286 | * Show a question to the user and switch to "ask for answer" mode see | |
287 | * {@link MainWindow#handleQuestion}. | |
288 | * | |
296a0b75 NR |
289 | * @param action |
290 | * the related action | |
bcb54330 NR |
291 | * @param question |
292 | * the question to ask | |
293 | * @param initial | |
294 | * the initial answer if any (to be edited by the user) | |
0b0b2b0f NR |
295 | * @param oneKey |
296 | * TRUE for a one-key answer, FALSE for a text answer validated | |
297 | * by ENTER | |
298 | */ | |
296a0b75 | 299 | private void setQuestion(KeyAction action, String question, String initial, |
bcb54330 | 300 | boolean oneKey) { |
f720df72 | 301 | userQuestion = new UserQuestion(action, oneKey); |
0b0b2b0f | 302 | |
9c8baf0c | 303 | messagePanel.removeAllComponents(); |
9c8baf0c | 304 | |
0b0b2b0f NR |
305 | Panel hpanel = new Panel(); |
306 | LinearLayout llayout = new LinearLayout(Direction.HORIZONTAL); | |
307 | llayout.setSpacing(0); | |
308 | hpanel.setLayoutManager(llayout); | |
9c8baf0c | 309 | |
0b0b2b0f | 310 | Label lbl = UiColors.Element.LINE_MESSAGE_QUESTION.createLabel(" " |
bcb54330 | 311 | + question + " "); |
296a0b75 NR |
312 | text = new TextBox(new TerminalSize(getSize().getColumns() |
313 | - lbl.getSize().getColumns(), 1)); | |
f720df72 NR |
314 | |
315 | if (initial != null) { | |
316 | // add all chars one by one so the caret is at the end | |
317 | for (int index = 0; index < initial.length(); index++) { | |
318 | text.handleInput(new KeyStroke(initial.charAt(index), false, | |
319 | false)); | |
320 | } | |
321 | } | |
9c8baf0c | 322 | |
bcb54330 NR |
323 | hpanel.addComponent(lbl, |
324 | LinearLayout.createLayoutData(LinearLayout.Alignment.Beginning)); | |
325 | hpanel.addComponent(text, | |
326 | LinearLayout.createLayoutData(LinearLayout.Alignment.Fill)); | |
9c8baf0c | 327 | |
bcb54330 NR |
328 | messagePanel |
329 | .addComponent(hpanel, LinearLayout | |
330 | .createLayoutData(LinearLayout.Alignment.Beginning)); | |
9c8baf0c | 331 | |
0b0b2b0f | 332 | text.takeFocus(); |
9c8baf0c NR |
333 | } |
334 | ||
296a0b75 NR |
335 | /** |
336 | * Refresh the window and the empty-space handling. You should call this | |
337 | * method when the window size changed. | |
338 | * | |
339 | * @param size | |
340 | * the new size of the window | |
341 | */ | |
342 | public void refresh(TerminalSize size) { | |
343 | if (size == null) | |
344 | return; | |
345 | ||
346 | if (getSize() == null || !getSize().equals(size)) | |
347 | setSize(size); | |
348 | ||
349 | setTitle(); | |
350 | ||
351 | if (actions != null) | |
352 | setActions(new ArrayList<KeyAction>(actions), false); | |
353 | ||
354 | invalidate(); | |
355 | } | |
356 | ||
357 | @Override | |
358 | public void invalidate() { | |
359 | super.invalidate(); | |
360 | for (MainContent content : contentStack) { | |
361 | content.invalidate(); | |
362 | } | |
363 | } | |
364 | ||
365 | @Override | |
366 | public boolean handleInput(KeyStroke key) { | |
367 | boolean handled = false; | |
368 | ||
f720df72 NR |
369 | if (userQuestion != null) { |
370 | handled = handleQuestion(userQuestion, key); | |
371 | if (handled) { | |
372 | if (userQuestion.getAnswer() != null) { | |
373 | handleAction(userQuestion.getAction(), | |
374 | userQuestion.getAnswer()); | |
296a0b75 | 375 | |
f720df72 NR |
376 | userQuestion = null; |
377 | } | |
296a0b75 NR |
378 | } |
379 | } else { | |
380 | handled = handleKey(key); | |
381 | } | |
382 | ||
f720df72 | 383 | if (!handled) { |
296a0b75 | 384 | handled = super.handleInput(key); |
f720df72 | 385 | } |
296a0b75 NR |
386 | |
387 | return handled; | |
388 | } | |
389 | ||
bcb54330 NR |
390 | /** |
391 | * Actually set the title <b>inside</b> the window. Will also call | |
296a0b75 | 392 | * {@link BasicWindow#setTitle} with the computed parameters. |
bcb54330 NR |
393 | */ |
394 | private void setTitle() { | |
0b0b2b0f NR |
395 | String prefix = " " + Main.APPLICATION_TITLE + " (version " |
396 | + Main.APPLICATION_VERSION + ")"; | |
397 | ||
bcb54330 | 398 | String title = null; |
0b0b2b0f | 399 | int count = -1; |
bcb54330 | 400 | |
0b0b2b0f | 401 | MainContent content = getContent(); |
bcb54330 NR |
402 | if (content != null) { |
403 | title = content.getTitle(); | |
0b0b2b0f | 404 | count = content.getCount(); |
bcb54330 NR |
405 | } |
406 | ||
407 | if (title == null) | |
408 | title = ""; | |
0b0b2b0f | 409 | |
bcb54330 | 410 | if (title.length() > 0) { |
0b0b2b0f | 411 | prefix = prefix + ": "; |
7da41ecd | 412 | title = StringUtils.sanitize(title, Main.isUnicode()); |
0b0b2b0f NR |
413 | } |
414 | ||
bcb54330 NR |
415 | String countStr = ""; |
416 | if (count > -1) { | |
417 | countStr = "[" + count + "]"; | |
418 | } | |
419 | ||
296a0b75 NR |
420 | int width = -1; |
421 | if (getSize() != null) { | |
422 | width = getSize().getColumns(); | |
423 | } | |
424 | ||
bcb54330 NR |
425 | if (width > 0) { |
426 | int padding = width - prefix.length() - title.length() | |
427 | - countStr.length(); | |
428 | if (padding > 0) { | |
429 | if (title.length() > 0) | |
430 | title = StringUtils.padString(title, title.length() | |
431 | + padding); | |
432 | else | |
433 | prefix = StringUtils.padString(prefix, prefix.length() | |
434 | + padding); | |
435 | } | |
0b0b2b0f | 436 | } |
0b0b2b0f | 437 | |
bcb54330 NR |
438 | String titleCache = prefix + title + count; |
439 | if (!titleCache.equals(this.titleCache)) { | |
440 | super.setTitle(prefix); | |
0b0b2b0f NR |
441 | |
442 | Label lblPrefix = new Label(prefix); | |
443 | UiColors.Element.TITLE_MAIN.themeLabel(lblPrefix); | |
444 | ||
445 | Label lblTitle = null; | |
bcb54330 | 446 | if (title.length() > 0) { |
0b0b2b0f NR |
447 | lblTitle = new Label(title); |
448 | UiColors.Element.TITLE_VARIABLE.themeLabel(lblTitle); | |
449 | } | |
450 | ||
451 | Label lblCount = null; | |
bcb54330 NR |
452 | if (countStr != null) { |
453 | lblCount = new Label(countStr); | |
0b0b2b0f NR |
454 | UiColors.Element.TITLE_COUNT.themeLabel(lblCount); |
455 | } | |
456 | ||
457 | titlePanel.removeAllComponents(); | |
a3b510ab | 458 | |
0b0b2b0f NR |
459 | titlePanel.addComponent(lblPrefix, BorderLayout.Location.LEFT); |
460 | if (lblTitle != null) | |
461 | titlePanel.addComponent(lblTitle, BorderLayout.Location.CENTER); | |
462 | if (lblCount != null) | |
463 | titlePanel.addComponent(lblCount, BorderLayout.Location.RIGHT); | |
0b0b2b0f NR |
464 | } |
465 | } | |
466 | ||
467 | /** | |
468 | * Return the current {@link MainContent} from the stack if any. | |
469 | * | |
470 | * @return the current {@link MainContent} | |
471 | */ | |
472 | private MainContent getContent() { | |
473 | if (contentStack.size() > 0) { | |
474 | return contentStack.get(contentStack.size() - 1); | |
475 | } | |
476 | ||
477 | return null; | |
478 | } | |
479 | ||
480 | /** | |
481 | * Update the list of actions and refresh the action panel. | |
482 | * | |
483 | * @param actions | |
484 | * the list of actions to support | |
485 | * @param enableDefaultactions | |
486 | * TRUE to enable the default actions | |
487 | */ | |
488 | private void setActions(List<KeyAction> actions, | |
489 | boolean enableDefaultactions) { | |
a3b510ab | 490 | this.actions.clear(); |
9c8baf0c | 491 | |
a3b510ab NR |
492 | if (enableDefaultactions) |
493 | this.actions.addAll(defaultActions); | |
494 | ||
495 | if (actions != null) | |
496 | this.actions.addAll(actions); | |
497 | ||
498 | actionPanel.removeAllComponents(); | |
499 | for (KeyAction action : this.actions) { | |
668268fc | 500 | String trans = " " + Main.trans(action.getStringId()) + " "; |
a3b510ab NR |
501 | |
502 | if (" ".equals(trans)) | |
503 | continue; | |
504 | ||
7da41ecd | 505 | String keyTrans = KeyAction.trans(action.getKey()); |
a3b510ab NR |
506 | |
507 | Panel kPane = new Panel(); | |
508 | LinearLayout layout = new LinearLayout(Direction.HORIZONTAL); | |
509 | layout.setSpacing(0); | |
510 | kPane.setLayoutManager(layout); | |
511 | ||
512 | kPane.addComponent(UiColors.Element.ACTION_KEY | |
513 | .createLabel(keyTrans)); | |
514 | kPane.addComponent(UiColors.Element.ACTION_DESC.createLabel(trans)); | |
515 | ||
516 | actionPanel.addComponent(kPane); | |
517 | } | |
bcb54330 NR |
518 | |
519 | // fill with "desc" colour | |
296a0b75 NR |
520 | int width = -1; |
521 | if (getSize() != null) { | |
522 | width = getSize().getColumns(); | |
523 | } | |
524 | ||
bcb54330 NR |
525 | if (width > 0) { |
526 | actionPanel.addComponent(UiColors.Element.ACTION_DESC | |
527 | .createLabel(StringUtils.padString("", width))); | |
bcb54330 | 528 | } |
a3b510ab NR |
529 | } |
530 | ||
0b0b2b0f NR |
531 | /** |
532 | * Handle user input when in "ask for question" mode (see | |
f720df72 | 533 | * {@link MainWindow#userQuestion}). |
0b0b2b0f | 534 | * |
f720df72 NR |
535 | * @param userQuestion |
536 | * the question data | |
0b0b2b0f NR |
537 | * @param key |
538 | * the key that has been pressed by the user | |
539 | * | |
f720df72 | 540 | * @return TRUE if the {@link KeyStroke} was handled |
0b0b2b0f | 541 | */ |
f720df72 NR |
542 | private boolean handleQuestion(UserQuestion userQuestion, KeyStroke key) { |
543 | userQuestion.setAnswer(null); | |
ae22c247 | 544 | |
f720df72 NR |
545 | if (userQuestion.isOneKeyAnswer()) { |
546 | userQuestion.setAnswer("" + key.getCharacter()); | |
a3b510ab | 547 | } else { |
f720df72 NR |
548 | // ^h == Backspace |
549 | if (key.isCtrlDown() && key.getCharacter() == 'h') { | |
550 | key = new KeyStroke(KeyType.Backspace); | |
551 | } | |
552 | ||
553 | switch (key.getKeyType()) { | |
554 | case Enter: | |
a3b510ab | 555 | if (text != null) |
f720df72 | 556 | userQuestion.setAnswer(text.getText()); |
a3b510ab | 557 | else |
f720df72 NR |
558 | userQuestion.setAnswer(""); |
559 | break; | |
560 | case Backspace: | |
561 | int pos = text.getCaretPosition().getColumn(); | |
562 | if (pos > 0) { | |
563 | String current = text.getText(); | |
564 | // force caret one space before: | |
565 | text.setText(current.substring(0, pos - 1)); | |
566 | // re-add full text: | |
567 | text.setText(current.substring(0, pos - 1) | |
568 | + current.substring(pos)); | |
569 | } | |
570 | return true; | |
571 | default: | |
572 | // Do nothing (continue entering text) | |
573 | break; | |
a3b510ab NR |
574 | } |
575 | } | |
576 | ||
f720df72 | 577 | if (userQuestion.getAnswer() != null) { |
a3b510ab | 578 | Interactable focus = null; |
0b0b2b0f NR |
579 | MainContent content = getContent(); |
580 | if (content != null) | |
581 | focus = content.nextFocus(null); | |
a3b510ab | 582 | |
fae07ea7 | 583 | focus.takeFocus(); |
f720df72 NR |
584 | |
585 | return true; | |
a3b510ab NR |
586 | } |
587 | ||
f720df72 | 588 | return false; |
a3b510ab NR |
589 | } |
590 | ||
0b0b2b0f NR |
591 | /** |
592 | * Handle the input in case of "normal" (not "ask for answer") mode. | |
593 | * | |
594 | * @param key | |
595 | * the key that was pressed | |
596 | * @param answer | |
597 | * the answer given for this key | |
598 | * | |
bcb54330 | 599 | * @return if the window handled the input |
0b0b2b0f | 600 | */ |
296a0b75 | 601 | private boolean handleKey(KeyStroke key) { |
a3b510ab NR |
602 | boolean handled = false; |
603 | ||
296a0b75 NR |
604 | if (setMessage(null, false)) |
605 | return true; | |
a3b510ab | 606 | |
0b0b2b0f NR |
607 | for (KeyAction action : actions) { |
608 | if (!action.match(key)) | |
609 | continue; | |
a3b510ab | 610 | |
0b0b2b0f NR |
611 | handled = true; |
612 | ||
613 | if (action.onAction()) { | |
296a0b75 NR |
614 | handleAction(action, null); |
615 | } | |
0b0b2b0f | 616 | |
296a0b75 NR |
617 | break; |
618 | } | |
0b0b2b0f | 619 | |
296a0b75 NR |
620 | return handled; |
621 | } | |
622 | ||
623 | /** | |
624 | * Handle the input in case of "normal" (not "ask for answer") mode. | |
625 | * | |
626 | * @param key | |
627 | * the key that was pressed | |
628 | * @param answer | |
629 | * the answer given for this key | |
630 | * | |
631 | * @return if the window handled the input | |
632 | */ | |
633 | private void handleAction(KeyAction action, String answer) { | |
634 | MainContent content = getContent(); | |
635 | ||
296a0b75 NR |
636 | switch (action.getMode()) { |
637 | case MOVE: | |
638 | int x = 0; | |
639 | int y = 0; | |
640 | ||
641 | if (action.getKey().getKeyType() == KeyType.ArrowUp) | |
642 | x = -1; | |
643 | if (action.getKey().getKeyType() == KeyType.ArrowDown) | |
644 | x = 1; | |
645 | if (action.getKey().getKeyType() == KeyType.ArrowLeft) | |
646 | y = -1; | |
647 | if (action.getKey().getKeyType() == KeyType.ArrowRight) | |
648 | y = 1; | |
649 | ||
650 | if (content != null) { | |
651 | String err = content.move(x, y); | |
652 | if (err != null) | |
653 | setMessage(err, true); | |
654 | } | |
655 | ||
656 | break; | |
657 | // mode with windows: | |
658 | case CONTACT_LIST: | |
ae22c247 NR |
659 | if (action.getCard() != null) { |
660 | pushContent(new ContactList(action.getCard())); | |
296a0b75 NR |
661 | } |
662 | break; | |
663 | case CONTACT_DETAILS: | |
ae22c247 NR |
664 | if (action.getContact() != null) { |
665 | pushContent(new ContactDetails(action.getContact())); | |
f04d8b1c NR |
666 | } |
667 | break; | |
668 | case CONTACT_DETAILS_RAW: | |
ae22c247 NR |
669 | if (action.getContact() != null) { |
670 | pushContent(new ContactDetailsRaw(action.getContact())); | |
296a0b75 NR |
671 | } |
672 | break; | |
673 | // mode interpreted by MainWindow: | |
674 | case HELP: | |
675 | // TODO | |
f720df72 | 676 | setMessage("Help! I need somebody! Help!", false); |
296a0b75 NR |
677 | |
678 | break; | |
679 | case BACK: | |
680 | String warning = content.getExitWarning(); | |
681 | if (warning != null) { | |
682 | if (answer == null) { | |
683 | setQuestion(action, warning); | |
684 | } else { | |
685 | setMessage(null, false); | |
686 | if (answer.equalsIgnoreCase("y")) { | |
bcb54330 | 687 | popContent(); |
a3b510ab | 688 | } |
296a0b75 NR |
689 | } |
690 | } else { | |
691 | popContent(); | |
692 | } | |
0b0b2b0f | 693 | |
296a0b75 NR |
694 | if (contentStack.size() == 0) { |
695 | close(); | |
696 | } | |
bcb54330 | 697 | |
296a0b75 NR |
698 | break; |
699 | // action modes: | |
ae22c247 | 700 | case ASK_USER: |
296a0b75 | 701 | if (answer == null) { |
ae22c247 NR |
702 | setQuestion(action, action.getQuestion(), |
703 | action.getDefaultAnswer()); | |
296a0b75 | 704 | } else { |
ae22c247 NR |
705 | setMessage(action.callback(answer), true); |
706 | content.refreshData(); | |
707 | invalidate(); | |
708 | setTitle(); | |
296a0b75 NR |
709 | } |
710 | break; | |
ae22c247 | 711 | case ASK_USER_KEY: |
296a0b75 | 712 | if (answer == null) { |
ae22c247 | 713 | setQuestion(action, action.getQuestion()); |
296a0b75 | 714 | } else { |
ae22c247 NR |
715 | setMessage(action.callback(answer), true); |
716 | content.refreshData(); | |
717 | invalidate(); | |
718 | setTitle(); | |
0b0b2b0f | 719 | } |
296a0b75 NR |
720 | break; |
721 | default: | |
722 | case NONE: | |
0b0b2b0f NR |
723 | break; |
724 | } | |
a3b510ab NR |
725 | } |
726 | } |