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