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