2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2017 Kevin Lamonte
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:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
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.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.util
.List
;
32 import java
.util
.ResourceBundle
;
34 import jexer
.bits
.Color
;
35 import jexer
.bits
.ColorTheme
;
36 import jexer
.bits
.CellAttributes
;
37 import jexer
.bits
.GraphicsChars
;
38 import jexer
.event
.TKeypressEvent
;
39 import jexer
.event
.TMouseEvent
;
40 import static jexer
.TKeypress
.*;
43 * TEditColorThemeWindow provides an easy UI for users to alter the running
47 public class TEditColorThemeWindow
extends TWindow
{
52 private static final ResourceBundle i18n
= ResourceBundle
.getBundle(TEditColorThemeWindow
.class.getName());
55 * The foreground color picker.
57 class ForegroundPicker
extends TWidget
{
72 * @param parent parent widget
73 * @param x column relative to parent
74 * @param y row relative to parent
75 * @param width width of text area
76 * @param height height of text area
78 public ForegroundPicker(final TWidget parent
, final int x
,
79 final int y
, final int width
, final int height
) {
81 super(parent
, x
, y
, width
, height
);
85 * Get the X grid coordinate for this color.
87 * @param color the Color value
88 * @return the X coordinate
90 private int getXColorPosition(final Color color
) {
91 if (color
.equals(Color
.BLACK
)) {
93 } else if (color
.equals(Color
.BLUE
)) {
95 } else if (color
.equals(Color
.GREEN
)) {
97 } else if (color
.equals(Color
.CYAN
)) {
99 } else if (color
.equals(Color
.RED
)) {
101 } else if (color
.equals(Color
.MAGENTA
)) {
103 } else if (color
.equals(Color
.YELLOW
)) {
105 } else if (color
.equals(Color
.WHITE
)) {
108 throw new IllegalArgumentException("Invalid color: " + color
);
112 * Get the Y grid coordinate for this color.
114 * @param color the Color value
115 * @param bold if true use bold color
116 * @return the Y coordinate
118 private int getYColorPosition(final Color color
, final boolean bold
) {
120 if (color
.equals(Color
.RED
)) {
122 } else if (color
.equals(Color
.MAGENTA
)) {
124 } else if (color
.equals(Color
.YELLOW
)) {
126 } else if (color
.equals(Color
.WHITE
)) {
136 * Get the bold value based on Y grid coordinate.
138 * @param dotY the Y coordinate
139 * @return the bold value
141 private boolean getBoldFromPosition(final int dotY
) {
149 * Get the color based on (X, Y) grid coordinate.
151 * @param dotX the X coordinate
152 * @param dotY the Y coordinate
153 * @return the Color value
155 private Color
getColorFromPosition(final int dotX
, final int dotY
) {
160 if ((1 <= dotX
) && (dotX
<= 3) && (y
== 1)) {
163 if ((4 <= dotX
) && (dotX
<= 6) && (y
== 1)) {
166 if ((7 <= dotX
) && (dotX
<= 9) && (y
== 1)) {
169 if ((10 <= dotX
) && (dotX
<= 12) && (y
== 1)) {
172 if ((1 <= dotX
) && (dotX
<= 3) && (y
== 2)) {
175 if ((4 <= dotX
) && (dotX
<= 6) && (y
== 2)) {
176 return Color
.MAGENTA
;
178 if ((7 <= dotX
) && (dotX
<= 9) && (y
== 2)) {
181 if ((10 <= dotX
) && (dotX
<= 12) && (y
== 2)) {
185 throw new IllegalArgumentException("Invalid coordinates: "
186 + dotX
+ ", " + dotY
);
190 * Draw the foreground colors grid.
194 CellAttributes border
= getWindow().getBorder();
195 CellAttributes background
= getWindow().getBackground();
196 CellAttributes attr
= new CellAttributes();
198 getScreen().drawBox(0, 0, getWidth(), getHeight(), border
,
199 background
, 1, false);
201 attr
.setTo(getTheme().getColor("twindow.background.modal"));
203 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
204 attr
.setBold(getTheme().getColor("tlabel").isBold());
206 getScreen().putStringXY(1, 0, i18n
.getString("foregroundLabel"),
209 // Have to draw the colors manually because the int value matches
212 attr
.setForeColor(Color
.BLACK
);
213 putStringXY(1, 1, "\u2588\u2588\u2588", attr
);
214 attr
.setForeColor(Color
.BLUE
);
215 putStringXY(4, 1, "\u2588\u2588\u2588", attr
);
216 attr
.setForeColor(Color
.GREEN
);
217 putStringXY(7, 1, "\u2588\u2588\u2588", attr
);
218 attr
.setForeColor(Color
.CYAN
);
219 putStringXY(10, 1, "\u2588\u2588\u2588", attr
);
220 attr
.setForeColor(Color
.RED
);
221 putStringXY(1, 2, "\u2588\u2588\u2588", attr
);
222 attr
.setForeColor(Color
.MAGENTA
);
223 putStringXY(4, 2, "\u2588\u2588\u2588", attr
);
224 attr
.setForeColor(Color
.YELLOW
);
225 putStringXY(7, 2, "\u2588\u2588\u2588", attr
);
226 attr
.setForeColor(Color
.WHITE
);
227 putStringXY(10, 2, "\u2588\u2588\u2588", attr
);
230 attr
.setForeColor(Color
.BLACK
);
231 putStringXY(1, 3, "\u2588\u2588\u2588", attr
);
232 attr
.setForeColor(Color
.BLUE
);
233 putStringXY(4, 3, "\u2588\u2588\u2588", attr
);
234 attr
.setForeColor(Color
.GREEN
);
235 putStringXY(7, 3, "\u2588\u2588\u2588", attr
);
236 attr
.setForeColor(Color
.CYAN
);
237 putStringXY(10, 3, "\u2588\u2588\u2588", attr
);
238 attr
.setForeColor(Color
.RED
);
239 putStringXY(1, 4, "\u2588\u2588\u2588", attr
);
240 attr
.setForeColor(Color
.MAGENTA
);
241 putStringXY(4, 4, "\u2588\u2588\u2588", attr
);
242 attr
.setForeColor(Color
.YELLOW
);
243 putStringXY(7, 4, "\u2588\u2588\u2588", attr
);
244 attr
.setForeColor(Color
.WHITE
);
245 putStringXY(10, 4, "\u2588\u2588\u2588", attr
);
248 int dotX
= getXColorPosition(color
);
249 int dotY
= getYColorPosition(color
, bold
);
250 if (color
.equals(Color
.BLACK
) && !bold
) {
251 // Use white-on-black for black. All other colors use
252 // black-on-whatever.
254 getScreen().putCharXY(dotX
, dotY
, GraphicsChars
.CP437
[0x07],
257 attr
.setForeColor(color
);
259 getScreen().putCharXY(dotX
, dotY
, '\u25D8', attr
);
266 * @param keypress keystroke event
269 public void onKeypress(final TKeypressEvent keypress
) {
270 if (keypress
.equals(kbRight
)) {
271 int dotX
= getXColorPosition(color
);
272 int dotY
= getYColorPosition(color
, bold
);
276 color
= getColorFromPosition(dotX
, dotY
);
277 } else if (keypress
.equals(kbLeft
)) {
278 int dotX
= getXColorPosition(color
);
279 int dotY
= getYColorPosition(color
, bold
);
283 color
= getColorFromPosition(dotX
, dotY
);
284 } else if (keypress
.equals(kbUp
)) {
285 int dotX
= getXColorPosition(color
);
286 int dotY
= getYColorPosition(color
, bold
);
290 color
= getColorFromPosition(dotX
, dotY
);
291 bold
= getBoldFromPosition(dotY
);
292 } else if (keypress
.equals(kbDown
)) {
293 int dotX
= getXColorPosition(color
);
294 int dotY
= getYColorPosition(color
, bold
);
298 color
= getColorFromPosition(dotX
, dotY
);
299 bold
= getBoldFromPosition(dotY
);
302 super.onKeypress(keypress
);
306 // Save this update to the local theme.
307 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
311 * Handle mouse press events.
313 * @param mouse mouse button press event
316 public void onMouseDown(final TMouseEvent mouse
) {
317 if (mouse
.isMouseWheelUp()) {
319 int dotX
= getXColorPosition(color
);
320 int dotY
= getYColorPosition(color
, bold
);
324 color
= getColorFromPosition(dotX
, dotY
);
325 bold
= getBoldFromPosition(dotY
);
326 } else if (mouse
.isMouseWheelDown()) {
327 // Do this like kbDown
328 int dotX
= getXColorPosition(color
);
329 int dotY
= getYColorPosition(color
, bold
);
333 color
= getColorFromPosition(dotX
, dotY
);
334 bold
= getBoldFromPosition(dotY
);
335 } else if ((mouse
.getX() > 0)
336 && (mouse
.getX() < getWidth() - 1)
337 && (mouse
.getY() > 0)
338 && (mouse
.getY() < getHeight() - 1)
340 color
= getColorFromPosition(mouse
.getX(), mouse
.getY());
341 bold
= getBoldFromPosition(mouse
.getY());
343 // Let parent class handle it.
344 super.onMouseDown(mouse
);
348 // Save this update to the local theme.
349 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
355 * The background color picker.
357 class BackgroundPicker
extends TWidget
{
360 * The selected color.
365 * Public constructor.
367 * @param parent parent widget
368 * @param x column relative to parent
369 * @param y row relative to parent
370 * @param width width of text area
371 * @param height height of text area
373 public BackgroundPicker(final TWidget parent
, final int x
,
374 final int y
, final int width
, final int height
) {
376 super(parent
, x
, y
, width
, height
);
380 * Get the X grid coordinate for this color.
382 * @param color the Color value
383 * @return the X coordinate
385 private int getXColorPosition(final Color color
) {
386 if (color
.equals(Color
.BLACK
)) {
388 } else if (color
.equals(Color
.BLUE
)) {
390 } else if (color
.equals(Color
.GREEN
)) {
392 } else if (color
.equals(Color
.CYAN
)) {
394 } else if (color
.equals(Color
.RED
)) {
396 } else if (color
.equals(Color
.MAGENTA
)) {
398 } else if (color
.equals(Color
.YELLOW
)) {
400 } else if (color
.equals(Color
.WHITE
)) {
403 throw new IllegalArgumentException("Invalid color: " + color
);
407 * Get the Y grid coordinate for this color.
409 * @param color the Color value
410 * @return the Y coordinate
412 private int getYColorPosition(final Color color
) {
414 if (color
.equals(Color
.RED
)) {
416 } else if (color
.equals(Color
.MAGENTA
)) {
418 } else if (color
.equals(Color
.YELLOW
)) {
420 } else if (color
.equals(Color
.WHITE
)) {
427 * Get the color based on (X, Y) grid coordinate.
429 * @param dotX the X coordinate
430 * @param dotY the Y coordinate
431 * @return the Color value
433 private Color
getColorFromPosition(final int dotX
, final int dotY
) {
434 if ((1 <= dotX
) && (dotX
<= 3) && (dotY
== 1)) {
437 if ((4 <= dotX
) && (dotX
<= 6) && (dotY
== 1)) {
440 if ((7 <= dotX
) && (dotX
<= 9) && (dotY
== 1)) {
443 if ((10 <= dotX
) && (dotX
<= 12) && (dotY
== 1)) {
446 if ((1 <= dotX
) && (dotX
<= 3) && (dotY
== 2)) {
449 if ((4 <= dotX
) && (dotX
<= 6) && (dotY
== 2)) {
450 return Color
.MAGENTA
;
452 if ((7 <= dotX
) && (dotX
<= 9) && (dotY
== 2)) {
455 if ((10 <= dotX
) && (dotX
<= 12) && (dotY
== 2)) {
459 throw new IllegalArgumentException("Invalid coordinates: "
460 + dotX
+ ", " + dotY
);
464 * Draw the background colors grid.
468 CellAttributes border
= getWindow().getBorder();
469 CellAttributes background
= getWindow().getBackground();
470 CellAttributes attr
= new CellAttributes();
472 getScreen().drawBox(0, 0, getWidth(), getHeight(), border
,
473 background
, 1, false);
475 attr
.setTo(getTheme().getColor("twindow.background.modal"));
477 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
478 attr
.setBold(getTheme().getColor("tlabel").isBold());
480 getScreen().putStringXY(1, 0, i18n
.getString("backgroundLabel"),
483 // Have to draw the colors manually because the int value matches
486 attr
.setForeColor(Color
.BLACK
);
487 putStringXY(1, 1, "\u2588\u2588\u2588", attr
);
488 attr
.setForeColor(Color
.BLUE
);
489 putStringXY(4, 1, "\u2588\u2588\u2588", attr
);
490 attr
.setForeColor(Color
.GREEN
);
491 putStringXY(7, 1, "\u2588\u2588\u2588", attr
);
492 attr
.setForeColor(Color
.CYAN
);
493 putStringXY(10, 1, "\u2588\u2588\u2588", attr
);
494 attr
.setForeColor(Color
.RED
);
495 putStringXY(1, 2, "\u2588\u2588\u2588", attr
);
496 attr
.setForeColor(Color
.MAGENTA
);
497 putStringXY(4, 2, "\u2588\u2588\u2588", attr
);
498 attr
.setForeColor(Color
.YELLOW
);
499 putStringXY(7, 2, "\u2588\u2588\u2588", attr
);
500 attr
.setForeColor(Color
.WHITE
);
501 putStringXY(10, 2, "\u2588\u2588\u2588", attr
);
504 int dotX
= getXColorPosition(color
);
505 int dotY
= getYColorPosition(color
);
506 if (color
.equals(Color
.BLACK
)) {
507 // Use white-on-black for black. All other colors use
508 // black-on-whatever.
510 getScreen().putCharXY(dotX
, dotY
, GraphicsChars
.CP437
[0x07],
513 attr
.setForeColor(color
);
514 getScreen().putCharXY(dotX
, dotY
, '\u25D8', attr
);
522 * @param keypress keystroke event
525 public void onKeypress(final TKeypressEvent keypress
) {
526 if (keypress
.equals(kbRight
)) {
527 int dotX
= getXColorPosition(color
);
528 int dotY
= getYColorPosition(color
);
532 color
= getColorFromPosition(dotX
, dotY
);
533 } else if (keypress
.equals(kbLeft
)) {
534 int dotX
= getXColorPosition(color
);
535 int dotY
= getYColorPosition(color
);
539 color
= getColorFromPosition(dotX
, dotY
);
540 } else if (keypress
.equals(kbUp
)) {
541 int dotX
= getXColorPosition(color
);
542 int dotY
= getYColorPosition(color
);
546 color
= getColorFromPosition(dotX
, dotY
);
547 } else if (keypress
.equals(kbDown
)) {
548 int dotX
= getXColorPosition(color
);
549 int dotY
= getYColorPosition(color
);
553 color
= getColorFromPosition(dotX
, dotY
);
556 super.onKeypress(keypress
);
559 // Save this update to the local theme.
560 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
564 * Handle mouse press events.
566 * @param mouse mouse button press event
569 public void onMouseDown(final TMouseEvent mouse
) {
570 if (mouse
.isMouseWheelUp()) {
572 int dotX
= getXColorPosition(color
);
573 int dotY
= getYColorPosition(color
);
577 color
= getColorFromPosition(dotX
, dotY
);
578 } else if (mouse
.isMouseWheelDown()) {
579 // Do this like kbDown
580 int dotX
= getXColorPosition(color
);
581 int dotY
= getYColorPosition(color
);
585 color
= getColorFromPosition(dotX
, dotY
);
587 } else if ((mouse
.getX() > 0)
588 && (mouse
.getX() < getWidth() - 1)
589 && (mouse
.getY() > 0)
590 && (mouse
.getY() < getHeight() - 1)
592 color
= getColorFromPosition(mouse
.getX(), mouse
.getY());
594 // Let parent class handle it.
595 super.onMouseDown(mouse
);
599 // Save this update to the local theme.
600 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
606 * The current editing theme.
608 private ColorTheme editTheme
;
611 * The left-side list of colors pane.
613 private TList colorNames
;
616 * The foreground color.
618 private ForegroundPicker foreground
;
621 * The background color.
623 private BackgroundPicker background
;
626 * Set various widgets/values to the editing theme color.
628 * @param colorName name of color from theme
630 private void refreshFromTheme(final String colorName
) {
631 CellAttributes attr
= editTheme
.getColor(colorName
);
632 foreground
.color
= attr
.getForeColor();
633 foreground
.bold
= attr
.isBold();
634 background
.color
= attr
.getBackColor();
638 * Examines foreground, background, and colorNames and sets the color in
641 private void saveToEditTheme() {
642 String colorName
= colorNames
.getSelected();
643 if (colorName
== null) {
646 CellAttributes attr
= editTheme
.getColor(colorName
);
647 attr
.setForeColor(foreground
.color
);
648 attr
.setBold(foreground
.bold
);
649 attr
.setBackColor(background
.color
);
650 editTheme
.setColor(colorName
, attr
);
654 * Public constructor. The file open box will be centered on screen.
656 * @param application the TApplication that manages this window
658 public TEditColorThemeWindow(final TApplication application
) {
660 // Register with the TApplication
661 super(application
, i18n
.getString("windowTitle"), 0, 0, 60, 18, MODAL
);
663 // Initialize with the first color
664 List
<String
> colors
= getTheme().getColorNames();
665 assert (colors
.size() > 0);
666 editTheme
= new ColorTheme();
667 for (String key
: colors
) {
668 CellAttributes attr
= new CellAttributes();
669 attr
.setTo(getTheme().getColor(key
));
670 editTheme
.setColor(key
, attr
);
673 colorNames
= addList(colors
, 2, 2, 38, getHeight() - 7,
675 // When the user presses Enter
677 refreshFromTheme(colorNames
.getSelected());
681 // When the user navigates with keyboard
683 refreshFromTheme(colorNames
.getSelected());
687 foreground
= new ForegroundPicker(this, 42, 1, 14, 6);
688 background
= new BackgroundPicker(this, 42, 7, 14, 4);
689 refreshFromTheme(colors
.get(0));
690 colorNames
.setSelectedIndex(0);
692 addButton(i18n
.getString("okButton"), getWidth() - 37, getHeight() - 4,
695 ColorTheme global
= getTheme();
696 List
<String
> colors
= editTheme
.getColorNames();
697 for (String key
: colors
) {
698 CellAttributes attr
= new CellAttributes();
699 attr
.setTo(editTheme
.getColor(key
));
700 global
.setColor(key
, attr
);
702 getApplication().closeWindow(TEditColorThemeWindow
.this);
707 addButton(i18n
.getString("cancelButton"), getWidth() - 25,
711 getApplication().closeWindow(TEditColorThemeWindow
.this);
716 // Default to the color list
717 activate(colorNames
);
720 newStatusBar(i18n
.getString("statusBar"));
729 CellAttributes attr
= new CellAttributes();
731 // Draw the label on colorNames
732 attr
.setTo(getTheme().getColor("twindow.background.modal"));
733 if (colorNames
.isActive()) {
734 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
735 attr
.setBold(getTheme().getColor("tlabel").isBold());
737 getScreen().putStringXY(3, 2, i18n
.getString("colorName"), attr
);
739 // Draw the sample text box
741 attr
.setForeColor(foreground
.color
);
742 attr
.setBold(foreground
.bold
);
743 attr
.setBackColor(background
.color
);
744 getScreen().putStringXY(getWidth() - 17, getHeight() - 6,
745 i18n
.getString("textTextText"), attr
);
746 getScreen().putStringXY(getWidth() - 17, getHeight() - 5,
747 i18n
.getString("textTextText"), attr
);
753 * @param keypress keystroke event
756 public void onKeypress(final TKeypressEvent keypress
) {
757 // Escape - behave like cancel
758 if (keypress
.equals(kbEsc
)) {
759 getApplication().closeWindow(this);
764 super.onKeypress(keypress
);