TButton animate for keyboard
[nikiroo-utils.git] / src / jexer / TMessageBox.java
... / ...
CommitLineData
1/*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2017 Kevin Lamonte
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer;
30
31import java.util.ArrayList;
32import java.util.List;
33
34import jexer.event.TKeypressEvent;
35import static jexer.TKeypress.*;
36
37/**
38 * TMessageBox is a system-modal dialog with buttons for OK, Cancel, Yes, or
39 * No. Call it like:
40 *
41 * <p>
42 * <pre>
43 * {@code
44 * box = application.messageBox(title, caption,
45 * TMessageBox.Type.OK | TMessageBox.Type.CANCEL);
46 *
47 * if (box.getResult() == TMessageBox.OK) {
48 * ... the user pressed OK, do stuff ...
49 * }
50 * }
51 * </pre>
52 *
53 */
54public class TMessageBox extends TWindow {
55
56 /**
57 * Message boxes have these supported types.
58 */
59 public enum Type {
60 /**
61 * Show an OK button.
62 */
63 OK,
64
65 /**
66 * Show both OK and Cancel buttons.
67 */
68 OKCANCEL,
69
70 /**
71 * Show both Yes and No buttons.
72 */
73 YESNO,
74
75 /**
76 * Show Yes, No, and Cancel buttons.
77 */
78 YESNOCANCEL
79 };
80
81 /**
82 * The type of this message box.
83 */
84 private Type type;
85
86 /**
87 * My buttons.
88 */
89 private List<TButton> buttons;
90
91 /**
92 * Message boxes have these possible results.
93 */
94 public enum Result {
95 /**
96 * User clicked "OK".
97 */
98 OK,
99
100 /**
101 * User clicked "Cancel".
102 */
103 CANCEL,
104
105 /**
106 * User clicked "Yes".
107 */
108 YES,
109
110 /**
111 * User clicked "No".
112 */
113 NO
114 };
115
116 /**
117 * Which button was clicked: OK, CANCEL, YES, or NO.
118 */
119 private Result result = Result.OK;
120
121 /**
122 * Get the result.
123 *
124 * @return the result: OK, CANCEL, YES, or NO.
125 */
126 public final Result getResult() {
127 return result;
128 }
129
130 /**
131 * Public constructor. The message box will be centered on screen.
132 *
133 * @param application TApplication that manages this window
134 * @param title window title, will be centered along the top border
135 * @param caption message to display. Use embedded newlines to get a
136 * multi-line box.
137 */
138 public TMessageBox(final TApplication application, final String title,
139 final String caption) {
140
141 this(application, title, caption, Type.OK, true);
142 }
143
144 /**
145 * Public constructor. The message box will be centered on screen.
146 *
147 * @param application TApplication that manages this window
148 * @param title window title, will be centered along the top border
149 * @param caption message to display. Use embedded newlines to get a
150 * multi-line box.
151 * @param type one of the Type constants. Default is Type.OK.
152 */
153 public TMessageBox(final TApplication application, final String title,
154 final String caption, final Type type) {
155
156 this(application, title, caption, type, true);
157 }
158
159 /**
160 * Public constructor. The message box will be centered on screen.
161 *
162 * @param application TApplication that manages this window
163 * @param title window title, will be centered along the top border
164 * @param caption message to display. Use embedded newlines to get a
165 * multi-line box.
166 * @param type one of the Type constants. Default is Type.OK.
167 * @param yield if true, yield this Thread. Subclasses need to set this
168 * to false and yield at their end of their constructor intead.
169 */
170 protected TMessageBox(final TApplication application, final String title,
171 final String caption, final Type type, final boolean yield) {
172
173 // Start as 50x50 at (1, 1). These will be changed later.
174 super(application, title, 1, 1, 100, 100, CENTERED | MODAL);
175
176 // Hang onto type so that we can provide more convenience in
177 // onKeypress().
178 this.type = type;
179
180 // Determine width and height
181 String [] lines = caption.split("\n");
182 int width = title.length() + 12;
183 setHeight(6 + lines.length);
184 for (String line: lines) {
185 if (line.length() + 4 > width) {
186 width = line.length() + 4;
187 }
188 }
189 setWidth(width);
190 if (getWidth() > getScreen().getWidth()) {
191 setWidth(getScreen().getWidth());
192 }
193 // Re-center window to get an appropriate (x, y)
194 center();
195
196 // Now add my elements
197 int lineI = 1;
198 for (String line: lines) {
199 addLabel(line, 1, lineI, "twindow.background.modal");
200 lineI++;
201 }
202
203 // The button line
204 lineI++;
205 buttons = new ArrayList<TButton>();
206
207 int buttonX = 0;
208
209 // Setup button actions
210 switch (type) {
211
212 case OK:
213 result = Result.OK;
214 if (getWidth() < 15) {
215 setWidth(15);
216 }
217 buttonX = (getWidth() - 11) / 2;
218 buttons.add(addButton(" &OK ", buttonX, lineI,
219 new TAction() {
220 public void DO() {
221 result = Result.OK;
222 getApplication().closeWindow(TMessageBox.this);
223 }
224 }
225 )
226 );
227 break;
228
229 case OKCANCEL:
230 result = Result.CANCEL;
231 if (getWidth() < 26) {
232 setWidth(26);
233 }
234 buttonX = (getWidth() - 22) / 2;
235 buttons.add(addButton(" &OK ", buttonX, lineI,
236 new TAction() {
237 public void DO() {
238 result = Result.OK;
239 getApplication().closeWindow(TMessageBox.this);
240 }
241 }
242 )
243 );
244 buttonX += 8 + 4;
245 buttons.add(addButton("&Cancel", buttonX, lineI,
246 new TAction() {
247 public void DO() {
248 result = Result.CANCEL;
249 getApplication().closeWindow(TMessageBox.this);
250 }
251 }
252 )
253 );
254 break;
255
256 case YESNO:
257 result = Result.NO;
258 if (getWidth() < 20) {
259 setWidth(20);
260 }
261 buttonX = (getWidth() - 16) / 2;
262 buttons.add(addButton("&Yes", buttonX, lineI,
263 new TAction() {
264 public void DO() {
265 result = Result.YES;
266 getApplication().closeWindow(TMessageBox.this);
267 }
268 }
269 )
270 );
271 buttonX += 5 + 4;
272 buttons.add(addButton("&No", buttonX, lineI,
273 new TAction() {
274 public void DO() {
275 result = Result.NO;
276 getApplication().closeWindow(TMessageBox.this);
277 }
278 }
279 )
280 );
281 break;
282
283 case YESNOCANCEL:
284 result = Result.CANCEL;
285 if (getWidth() < 31) {
286 setWidth(31);
287 }
288 buttonX = (getWidth() - 27) / 2;
289 buttons.add(addButton("&Yes", buttonX, lineI,
290 new TAction() {
291 public void DO() {
292 result = Result.YES;
293 getApplication().closeWindow(TMessageBox.this);
294 }
295 }
296 )
297 );
298 buttonX += 5 + 4;
299 buttons.add(addButton("&No", buttonX, lineI,
300 new TAction() {
301 public void DO() {
302 result = Result.NO;
303 getApplication().closeWindow(TMessageBox.this);
304 }
305 }
306 )
307 );
308 buttonX += 4 + 4;
309 buttons.add(addButton("&Cancel", buttonX, lineI,
310 new TAction() {
311 public void DO() {
312 result = Result.CANCEL;
313 getApplication().closeWindow(TMessageBox.this);
314 }
315 }
316 )
317 );
318 break;
319
320 default:
321 throw new IllegalArgumentException("Invalid message box type: " + type);
322 }
323
324 // Set the secondaryThread to run me
325 getApplication().enableSecondaryEventReceiver(this);
326
327 if (yield) {
328 // Yield to the secondary thread. When I come back from the
329 // constructor response will already be set.
330 getApplication().yield();
331 }
332 }
333
334 /**
335 * Handle keystrokes.
336 *
337 * @param keypress keystroke event
338 */
339 @Override
340 public void onKeypress(final TKeypressEvent keypress) {
341
342 if (this instanceof TInputBox) {
343 super.onKeypress(keypress);
344 return;
345 }
346
347 // Some convenience for message boxes: Alt won't be needed for the
348 // buttons.
349 switch (type) {
350
351 case OK:
352 if (keypress.equals(kbO)) {
353 buttons.get(0).dispatch();
354 return;
355 }
356 break;
357
358 case OKCANCEL:
359 if (keypress.equals(kbO)) {
360 buttons.get(0).dispatch();
361 return;
362 } else if (keypress.equals(kbC)) {
363 buttons.get(1).dispatch();
364 return;
365 }
366 break;
367
368 case YESNO:
369 if (keypress.equals(kbY)) {
370 buttons.get(0).dispatch();
371 return;
372 } else if (keypress.equals(kbN)) {
373 buttons.get(1).dispatch();
374 return;
375 }
376 break;
377
378 case YESNOCANCEL:
379 if (keypress.equals(kbY)) {
380 buttons.get(0).dispatch();
381 return;
382 } else if (keypress.equals(kbN)) {
383 buttons.get(1).dispatch();
384 return;
385 } else if (keypress.equals(kbC)) {
386 buttons.get(2).dispatch();
387 return;
388 }
389 break;
390
391 default:
392 throw new IllegalArgumentException("Invalid message box type: " + type);
393 }
394
395 super.onKeypress(keypress);
396 }
397
398}