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