Some changes to support Files
[jvcard.git] / src / be / nikiroo / jvcard / tui / MainWindow.java
1 package be.nikiroo.jvcard.tui;
2
3 import java.io.File;
4 import java.util.Arrays;
5 import java.util.LinkedList;
6 import java.util.List;
7
8 import be.nikiroo.jvcard.Card;
9 import be.nikiroo.jvcard.Contact;
10 import be.nikiroo.jvcard.i18n.Trans;
11 import be.nikiroo.jvcard.i18n.Trans.StringId;
12 import be.nikiroo.jvcard.tui.KeyAction.Mode;
13 import be.nikiroo.jvcard.tui.UiColors.Element;
14 import be.nikiroo.jvcard.tui.panes.ContactDetails;
15 import be.nikiroo.jvcard.tui.panes.ContactList;
16 import be.nikiroo.jvcard.tui.panes.MainContent;
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;
27 import com.googlecode.lanterna.gui2.TextGUIGraphics;
28 import com.googlecode.lanterna.gui2.Window;
29 import com.googlecode.lanterna.input.KeyStroke;
30 import com.googlecode.lanterna.input.KeyType;
31
32 /**
33 * This is the main "window" of the program. It will host one
34 * {@link MainContent} at any one time.
35 *
36 * @author niki
37 *
38 */
39 public class MainWindow extends BasicWindow {
40 private List<KeyAction> defaultActions = new LinkedList<KeyAction>();
41 private List<KeyAction> actions = new LinkedList<KeyAction>();
42 private List<MainContent> content = new LinkedList<MainContent>();
43 private boolean actionsPadded;
44 private Boolean waitForOneKeyAnswer; // true, false, (null = do not wait for
45 // an answer)
46 private String title;
47 private Panel titlePanel;
48 private Panel mainPanel;
49 private Panel contentPanel;
50 private Panel actionPanel;
51 private Panel messagePanel;
52 private TextBox text;
53
54 public MainWindow() {
55 this(null);
56 }
57
58 public MainWindow(MainContent content) {
59 super(content == null ? "" : content.getTitle());
60
61 setHints(Arrays.asList(Window.Hint.FULL_SCREEN,
62 Window.Hint.NO_DECORATIONS, Window.Hint.FIT_TERMINAL_WINDOW));
63
64 defaultActions.add(new KeyAction(Mode.BACK, 'q',
65 StringId.KEY_ACTION_BACK));
66 defaultActions.add(new KeyAction(Mode.BACK, KeyType.Escape,
67 StringId.NULL));
68 defaultActions.add(new KeyAction(Mode.HELP, 'h',
69 StringId.KEY_ACTION_HELP));
70 defaultActions.add(new KeyAction(Mode.HELP, KeyType.F1, StringId.NULL));
71
72 actionPanel = new Panel();
73 contentPanel = new Panel();
74 mainPanel = new Panel();
75 messagePanel = new Panel();
76 titlePanel = new Panel();
77
78 Panel actionMessagePanel = new Panel();
79
80 LinearLayout llayout = new LinearLayout(Direction.HORIZONTAL);
81 llayout.setSpacing(0);
82 actionPanel.setLayoutManager(llayout);
83
84 llayout = new LinearLayout(Direction.VERTICAL);
85 llayout.setSpacing(0);
86 titlePanel.setLayoutManager(llayout);
87
88 llayout = new LinearLayout(Direction.VERTICAL);
89 llayout.setSpacing(0);
90 messagePanel.setLayoutManager(llayout);
91
92 BorderLayout blayout = new BorderLayout();
93 mainPanel.setLayoutManager(blayout);
94
95 blayout = new BorderLayout();
96 contentPanel.setLayoutManager(blayout);
97
98 blayout = new BorderLayout();
99 actionMessagePanel.setLayoutManager(blayout);
100
101 actionMessagePanel
102 .addComponent(messagePanel, BorderLayout.Location.TOP);
103 actionMessagePanel.addComponent(actionPanel,
104 BorderLayout.Location.CENTER);
105
106 mainPanel.addComponent(titlePanel, BorderLayout.Location.TOP);
107 mainPanel.addComponent(contentPanel, BorderLayout.Location.CENTER);
108 mainPanel
109 .addComponent(actionMessagePanel, BorderLayout.Location.BOTTOM);
110
111 pushContent(content);
112
113 setComponent(mainPanel);
114 }
115
116 public void pushContent(MainContent content) {
117 List<KeyAction> actions = null;
118 String title = null;
119
120 contentPanel.removeAllComponents();
121 if (content != null) {
122 title = content.getTitle();
123 actions = content.getKeyBindings();
124 contentPanel.addComponent(content, BorderLayout.Location.CENTER);
125 this.content.add(content);
126
127 Interactable focus = content.nextFocus(null);
128 if (focus != null)
129 focus.takeFocus();
130 }
131
132 setTitle(title);
133 setActions(actions, true, true);
134
135 invalidate();
136 }
137
138 public MainContent popContent() {
139 MainContent removed = null;
140 MainContent prev = null;
141 if (content.size() > 0)
142 removed = content.remove(content.size() - 1);
143 if (content.size() > 0)
144 prev = content.remove(content.size() - 1);
145 pushContent(prev);
146
147 return removed;
148 }
149
150 /**
151 * Set the application title.
152 *
153 * @param title
154 * the new title or NULL for the default title
155 */
156 public void setTitle(String title) {
157 if (title == null) {
158 title = Trans.StringId.TITLE.trans();
159 }
160
161 if (!title.equals(this.title)) {
162 super.setTitle(title);
163 this.title = title;
164 }
165
166 Label lbl = new Label(title);
167 titlePanel.removeAllComponents();
168
169 titlePanel.addComponent(lbl, LinearLayout
170 .createLayoutData(LinearLayout.Alignment.Center));
171 }
172
173 /**
174 * Show the given message on screen. It will disappear at the next action.
175 *
176 * @param mess
177 * the message to display
178 * @param error
179 * TRUE for an error message, FALSE for an information message
180 */
181 public void setMessage(String mess, boolean error) {
182 messagePanel.removeAllComponents();
183 if (mess != null) {
184 Element element = (error ? UiColors.Element.LINE_MESSAGE_ERR
185 : UiColors.Element.LINE_MESSAGE);
186 Label lbl = element.createLabel(" " + mess + " ");
187 messagePanel.addComponent(lbl, LinearLayout
188 .createLayoutData(LinearLayout.Alignment.Center));
189 }
190 }
191
192 public void setQuestion(String mess, boolean oneKey) {
193 messagePanel.removeAllComponents();
194 if (mess != null) {
195 waitForOneKeyAnswer = oneKey;
196
197 Panel hpanel = new Panel();
198 LinearLayout llayout = new LinearLayout(Direction.HORIZONTAL);
199 llayout.setSpacing(0);
200 hpanel.setLayoutManager(llayout);
201
202 Label lbl = UiColors.Element.LINE_MESSAGE_QUESTION.createLabel(" "
203 + mess + " ");
204 text = new TextBox(new TerminalSize(getSize().getColumns()
205 - lbl.getSize().getColumns(), 1));
206
207 hpanel.addComponent(lbl, LinearLayout
208 .createLayoutData(LinearLayout.Alignment.Beginning));
209 hpanel.addComponent(text, LinearLayout
210 .createLayoutData(LinearLayout.Alignment.Fill));
211
212 messagePanel.addComponent(hpanel, LinearLayout
213 .createLayoutData(LinearLayout.Alignment.Beginning));
214
215 text.takeFocus();
216 }
217 }
218
219 @Override
220 public void draw(TextGUIGraphics graphics) {
221 setTitle(title);
222 if (!actionsPadded) {
223 // fill with "desc" colour
224 actionPanel.addComponent(UiColors.Element.ACTION_DESC
225 .createLabel(StringUtils.padString("", graphics.getSize()
226 .getColumns())));
227 actionsPadded = true;
228 }
229 super.draw(graphics);
230 }
231
232 private void setActions(List<KeyAction> actions, boolean allowKeys,
233 boolean enableDefaultactions) {
234
235 this.actions.clear();
236 actionsPadded = false;
237
238 if (enableDefaultactions)
239 this.actions.addAll(defaultActions);
240
241 if (actions != null)
242 this.actions.addAll(actions);
243
244 actionPanel.removeAllComponents();
245 for (KeyAction action : this.actions) {
246 String trans = " " + action.getStringId().trans() + " ";
247
248 if (" ".equals(trans))
249 continue;
250
251 String keyTrans = "";
252 switch (action.getKey().getKeyType()) {
253 case Enter:
254 keyTrans = " ⤶ ";
255 break;
256 case Tab:
257 keyTrans = " ↹ ";
258 break;
259 case Character:
260 keyTrans = " " + action.getKey().getCharacter() + " ";
261 break;
262 default:
263 keyTrans = "" + action.getKey().getKeyType();
264 int width = 3;
265 if (keyTrans.length() > width) {
266 keyTrans = keyTrans.substring(0, width);
267 } else if (keyTrans.length() < width) {
268 keyTrans = keyTrans
269 + new String(new char[width - keyTrans.length()])
270 .replace('\0', ' ');
271 }
272 break;
273 }
274
275 Panel kPane = new Panel();
276 LinearLayout layout = new LinearLayout(Direction.HORIZONTAL);
277 layout.setSpacing(0);
278 kPane.setLayoutManager(layout);
279
280 kPane.addComponent(UiColors.Element.ACTION_KEY
281 .createLabel(keyTrans));
282 kPane.addComponent(UiColors.Element.ACTION_DESC.createLabel(trans));
283
284 actionPanel.addComponent(kPane);
285 }
286 }
287
288 private String handleQuestion(KeyStroke key) {
289 String answer = null;
290
291 if (waitForOneKeyAnswer) {
292 answer = "" + key.getCharacter();
293 } else {
294 if (key.getKeyType() == KeyType.Enter) {
295 if (text != null)
296 answer = text.getText();
297 else
298 answer = "";
299 }
300 }
301
302 if (answer != null) {
303 Interactable focus = null;
304 if (this.content.size() > 0)
305 // focus = content.get(0).getDefaultFocusElement();
306 focus = content.get(0).nextFocus(null);
307
308 focus.takeFocus();
309 }
310
311 return answer;
312 }
313
314 @Override
315 public boolean handleInput(KeyStroke key) {
316 boolean handled = false;
317
318 if (waitForOneKeyAnswer != null) {
319 String answer = handleQuestion(key);
320 if (answer != null) {
321 waitForOneKeyAnswer = null;
322 setMessage("ANS: " + answer, false);
323
324 handled = true;
325 }
326 } else {
327 setMessage(null, false);
328
329 for (KeyAction action : actions) {
330 if (!action.match(key))
331 continue;
332
333 handled = true;
334
335 if (action.onAction()) {
336 switch (action.getMode()) {
337 case MOVE:
338 int x = 0;
339 int y = 0;
340
341 if (action.getKey().getKeyType() == KeyType.ArrowUp)
342 x = -1;
343 if (action.getKey().getKeyType() == KeyType.ArrowDown)
344 x = 1;
345 if (action.getKey().getKeyType() == KeyType.ArrowLeft)
346 y = -1;
347 if (action.getKey().getKeyType() == KeyType.ArrowRight)
348 y = 1;
349
350 if (content.size() > 0) {
351 String err = content.get(content.size() - 1).move(
352 x, y);
353 if (err != null)
354 setMessage(err, true);
355 }
356
357 break;
358 // mode with windows:
359 case CONTACT_LIST:
360 Card card = action.getCard();
361 if (card != null) {
362 pushContent(new ContactList(card));
363 }
364 break;
365 case CONTACT_DETAILS:
366 Contact contact = action.getContact();
367 if (contact != null) {
368 pushContent(new ContactDetails(contact));
369 }
370 break;
371 // mode interpreted by MainWindow:
372 case HELP:
373 // TODO
374 // setMessage("Help! I need somebody! Help!", false);
375 setQuestion("Test question?", false);
376 handled = true;
377 break;
378 case BACK:
379 popContent();
380 if (content.size() == 0)
381 close();
382 break;
383 default:
384 case NONE:
385 break;
386 }
387 }
388
389 break;
390 }
391 }
392
393 if (!handled)
394 handled = super.handleInput(key);
395
396 return handled;
397 }
398 }