2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 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());
54 // ------------------------------------------------------------------------
55 // Variables --------------------------------------------------------------
56 // ------------------------------------------------------------------------
59 * The current editing theme.
61 private ColorTheme editTheme
;
64 * The left-side list of colors pane.
66 private TList colorNames
;
69 * The foreground color.
71 private ForegroundPicker foreground
;
74 * The background color.
76 private BackgroundPicker background
;
79 * The foreground color picker.
81 class ForegroundPicker
extends TWidget
{
96 * @param parent parent widget
97 * @param x column relative to parent
98 * @param y row relative to parent
99 * @param width width of text area
100 * @param height height of text area
102 public ForegroundPicker(final TWidget parent
, final int x
,
103 final int y
, final int width
, final int height
) {
105 super(parent
, x
, y
, width
, height
);
109 * Get the X grid coordinate for this color.
111 * @param color the Color value
112 * @return the X coordinate
114 private int getXColorPosition(final Color color
) {
115 if (color
.equals(Color
.BLACK
)) {
117 } else if (color
.equals(Color
.BLUE
)) {
119 } else if (color
.equals(Color
.GREEN
)) {
121 } else if (color
.equals(Color
.CYAN
)) {
123 } else if (color
.equals(Color
.RED
)) {
125 } else if (color
.equals(Color
.MAGENTA
)) {
127 } else if (color
.equals(Color
.YELLOW
)) {
129 } else if (color
.equals(Color
.WHITE
)) {
132 throw new IllegalArgumentException("Invalid color: " + color
);
136 * Get the Y grid coordinate for this color.
138 * @param color the Color value
139 * @param bold if true use bold color
140 * @return the Y coordinate
142 private int getYColorPosition(final Color color
, final boolean bold
) {
144 if (color
.equals(Color
.RED
)) {
146 } else if (color
.equals(Color
.MAGENTA
)) {
148 } else if (color
.equals(Color
.YELLOW
)) {
150 } else if (color
.equals(Color
.WHITE
)) {
160 * Get the bold value based on Y grid coordinate.
162 * @param dotY the Y coordinate
163 * @return the bold value
165 private boolean getBoldFromPosition(final int dotY
) {
173 * Get the color based on (X, Y) grid coordinate.
175 * @param dotX the X coordinate
176 * @param dotY the Y coordinate
177 * @return the Color value
179 private Color
getColorFromPosition(final int dotX
, final int dotY
) {
184 if ((1 <= dotX
) && (dotX
<= 3) && (y
== 1)) {
187 if ((4 <= dotX
) && (dotX
<= 6) && (y
== 1)) {
190 if ((7 <= dotX
) && (dotX
<= 9) && (y
== 1)) {
193 if ((10 <= dotX
) && (dotX
<= 12) && (y
== 1)) {
196 if ((1 <= dotX
) && (dotX
<= 3) && (y
== 2)) {
199 if ((4 <= dotX
) && (dotX
<= 6) && (y
== 2)) {
200 return Color
.MAGENTA
;
202 if ((7 <= dotX
) && (dotX
<= 9) && (y
== 2)) {
205 if ((10 <= dotX
) && (dotX
<= 12) && (y
== 2)) {
209 throw new IllegalArgumentException("Invalid coordinates: "
210 + dotX
+ ", " + dotY
);
214 * Draw the foreground colors grid.
218 CellAttributes border
= getWindow().getBorder();
219 CellAttributes background
= getWindow().getBackground();
220 CellAttributes attr
= new CellAttributes();
222 drawBox(0, 0, getWidth(), getHeight(), border
, background
, 1,
225 attr
.setTo(getTheme().getColor("twindow.background.modal"));
227 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
228 attr
.setBold(getTheme().getColor("tlabel").isBold());
230 putStringXY(1, 0, i18n
.getString("foregroundLabel"), attr
);
232 // Have to draw the colors manually because the int value matches
235 attr
.setForeColor(Color
.BLACK
);
236 putStringXY(1, 1, "\u2588\u2588\u2588", attr
);
237 attr
.setForeColor(Color
.BLUE
);
238 putStringXY(4, 1, "\u2588\u2588\u2588", attr
);
239 attr
.setForeColor(Color
.GREEN
);
240 putStringXY(7, 1, "\u2588\u2588\u2588", attr
);
241 attr
.setForeColor(Color
.CYAN
);
242 putStringXY(10, 1, "\u2588\u2588\u2588", attr
);
243 attr
.setForeColor(Color
.RED
);
244 putStringXY(1, 2, "\u2588\u2588\u2588", attr
);
245 attr
.setForeColor(Color
.MAGENTA
);
246 putStringXY(4, 2, "\u2588\u2588\u2588", attr
);
247 attr
.setForeColor(Color
.YELLOW
);
248 putStringXY(7, 2, "\u2588\u2588\u2588", attr
);
249 attr
.setForeColor(Color
.WHITE
);
250 putStringXY(10, 2, "\u2588\u2588\u2588", attr
);
253 attr
.setForeColor(Color
.BLACK
);
254 putStringXY(1, 3, "\u2588\u2588\u2588", attr
);
255 attr
.setForeColor(Color
.BLUE
);
256 putStringXY(4, 3, "\u2588\u2588\u2588", attr
);
257 attr
.setForeColor(Color
.GREEN
);
258 putStringXY(7, 3, "\u2588\u2588\u2588", attr
);
259 attr
.setForeColor(Color
.CYAN
);
260 putStringXY(10, 3, "\u2588\u2588\u2588", attr
);
261 attr
.setForeColor(Color
.RED
);
262 putStringXY(1, 4, "\u2588\u2588\u2588", attr
);
263 attr
.setForeColor(Color
.MAGENTA
);
264 putStringXY(4, 4, "\u2588\u2588\u2588", attr
);
265 attr
.setForeColor(Color
.YELLOW
);
266 putStringXY(7, 4, "\u2588\u2588\u2588", attr
);
267 attr
.setForeColor(Color
.WHITE
);
268 putStringXY(10, 4, "\u2588\u2588\u2588", attr
);
271 int dotX
= getXColorPosition(color
);
272 int dotY
= getYColorPosition(color
, bold
);
273 if (color
.equals(Color
.BLACK
) && !bold
) {
274 // Use white-on-black for black. All other colors use
275 // black-on-whatever.
277 putCharXY(dotX
, dotY
, GraphicsChars
.CP437
[0x07], attr
);
279 attr
.setForeColor(color
);
281 putCharXY(dotX
, dotY
, '\u25D8', attr
);
288 * @param keypress keystroke event
291 public void onKeypress(final TKeypressEvent keypress
) {
292 if (keypress
.equals(kbRight
)) {
293 int dotX
= getXColorPosition(color
);
294 int dotY
= getYColorPosition(color
, bold
);
298 color
= getColorFromPosition(dotX
, dotY
);
299 } else if (keypress
.equals(kbLeft
)) {
300 int dotX
= getXColorPosition(color
);
301 int dotY
= getYColorPosition(color
, bold
);
305 color
= getColorFromPosition(dotX
, dotY
);
306 } else if (keypress
.equals(kbUp
)) {
307 int dotX
= getXColorPosition(color
);
308 int dotY
= getYColorPosition(color
, bold
);
312 color
= getColorFromPosition(dotX
, dotY
);
313 bold
= getBoldFromPosition(dotY
);
314 } else if (keypress
.equals(kbDown
)) {
315 int dotX
= getXColorPosition(color
);
316 int dotY
= getYColorPosition(color
, bold
);
320 color
= getColorFromPosition(dotX
, dotY
);
321 bold
= getBoldFromPosition(dotY
);
324 super.onKeypress(keypress
);
328 // Save this update to the local theme.
329 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
333 * Handle mouse press events.
335 * @param mouse mouse button press event
338 public void onMouseDown(final TMouseEvent mouse
) {
339 if (mouse
.isMouseWheelUp()) {
341 int dotX
= getXColorPosition(color
);
342 int dotY
= getYColorPosition(color
, bold
);
346 color
= getColorFromPosition(dotX
, dotY
);
347 bold
= getBoldFromPosition(dotY
);
348 } else if (mouse
.isMouseWheelDown()) {
349 // Do this like kbDown
350 int dotX
= getXColorPosition(color
);
351 int dotY
= getYColorPosition(color
, bold
);
355 color
= getColorFromPosition(dotX
, dotY
);
356 bold
= getBoldFromPosition(dotY
);
357 } else if ((mouse
.getX() > 0)
358 && (mouse
.getX() < getWidth() - 1)
359 && (mouse
.getY() > 0)
360 && (mouse
.getY() < getHeight() - 1)
362 color
= getColorFromPosition(mouse
.getX(), mouse
.getY());
363 bold
= getBoldFromPosition(mouse
.getY());
365 // Let parent class handle it.
366 super.onMouseDown(mouse
);
370 // Save this update to the local theme.
371 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
377 * The background color picker.
379 class BackgroundPicker
extends TWidget
{
382 * The selected color.
387 * Public constructor.
389 * @param parent parent widget
390 * @param x column relative to parent
391 * @param y row relative to parent
392 * @param width width of text area
393 * @param height height of text area
395 public BackgroundPicker(final TWidget parent
, final int x
,
396 final int y
, final int width
, final int height
) {
398 super(parent
, x
, y
, width
, height
);
402 * Get the X grid coordinate for this color.
404 * @param color the Color value
405 * @return the X coordinate
407 private int getXColorPosition(final Color color
) {
408 if (color
.equals(Color
.BLACK
)) {
410 } else if (color
.equals(Color
.BLUE
)) {
412 } else if (color
.equals(Color
.GREEN
)) {
414 } else if (color
.equals(Color
.CYAN
)) {
416 } else if (color
.equals(Color
.RED
)) {
418 } else if (color
.equals(Color
.MAGENTA
)) {
420 } else if (color
.equals(Color
.YELLOW
)) {
422 } else if (color
.equals(Color
.WHITE
)) {
425 throw new IllegalArgumentException("Invalid color: " + color
);
429 * Get the Y grid coordinate for this color.
431 * @param color the Color value
432 * @return the Y coordinate
434 private int getYColorPosition(final Color color
) {
436 if (color
.equals(Color
.RED
)) {
438 } else if (color
.equals(Color
.MAGENTA
)) {
440 } else if (color
.equals(Color
.YELLOW
)) {
442 } else if (color
.equals(Color
.WHITE
)) {
449 * Get the color based on (X, Y) grid coordinate.
451 * @param dotX the X coordinate
452 * @param dotY the Y coordinate
453 * @return the Color value
455 private Color
getColorFromPosition(final int dotX
, final int dotY
) {
456 if ((1 <= dotX
) && (dotX
<= 3) && (dotY
== 1)) {
459 if ((4 <= dotX
) && (dotX
<= 6) && (dotY
== 1)) {
462 if ((7 <= dotX
) && (dotX
<= 9) && (dotY
== 1)) {
465 if ((10 <= dotX
) && (dotX
<= 12) && (dotY
== 1)) {
468 if ((1 <= dotX
) && (dotX
<= 3) && (dotY
== 2)) {
471 if ((4 <= dotX
) && (dotX
<= 6) && (dotY
== 2)) {
472 return Color
.MAGENTA
;
474 if ((7 <= dotX
) && (dotX
<= 9) && (dotY
== 2)) {
477 if ((10 <= dotX
) && (dotX
<= 12) && (dotY
== 2)) {
481 throw new IllegalArgumentException("Invalid coordinates: "
482 + dotX
+ ", " + dotY
);
486 * Draw the background colors grid.
490 CellAttributes border
= getWindow().getBorder();
491 CellAttributes background
= getWindow().getBackground();
492 CellAttributes attr
= new CellAttributes();
494 drawBox(0, 0, getWidth(), getHeight(), border
, background
, 1,
497 attr
.setTo(getTheme().getColor("twindow.background.modal"));
499 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
500 attr
.setBold(getTheme().getColor("tlabel").isBold());
502 putStringXY(1, 0, i18n
.getString("backgroundLabel"), attr
);
504 // Have to draw the colors manually because the int value matches
507 attr
.setForeColor(Color
.BLACK
);
508 putStringXY(1, 1, "\u2588\u2588\u2588", attr
);
509 attr
.setForeColor(Color
.BLUE
);
510 putStringXY(4, 1, "\u2588\u2588\u2588", attr
);
511 attr
.setForeColor(Color
.GREEN
);
512 putStringXY(7, 1, "\u2588\u2588\u2588", attr
);
513 attr
.setForeColor(Color
.CYAN
);
514 putStringXY(10, 1, "\u2588\u2588\u2588", attr
);
515 attr
.setForeColor(Color
.RED
);
516 putStringXY(1, 2, "\u2588\u2588\u2588", attr
);
517 attr
.setForeColor(Color
.MAGENTA
);
518 putStringXY(4, 2, "\u2588\u2588\u2588", attr
);
519 attr
.setForeColor(Color
.YELLOW
);
520 putStringXY(7, 2, "\u2588\u2588\u2588", attr
);
521 attr
.setForeColor(Color
.WHITE
);
522 putStringXY(10, 2, "\u2588\u2588\u2588", attr
);
525 int dotX
= getXColorPosition(color
);
526 int dotY
= getYColorPosition(color
);
527 if (color
.equals(Color
.BLACK
)) {
528 // Use white-on-black for black. All other colors use
529 // black-on-whatever.
531 putCharXY(dotX
, dotY
, GraphicsChars
.CP437
[0x07], attr
);
533 attr
.setForeColor(color
);
534 putCharXY(dotX
, dotY
, '\u25D8', attr
);
542 * @param keypress keystroke event
545 public void onKeypress(final TKeypressEvent keypress
) {
546 if (keypress
.equals(kbRight
)) {
547 int dotX
= getXColorPosition(color
);
548 int dotY
= getYColorPosition(color
);
552 color
= getColorFromPosition(dotX
, dotY
);
553 } else if (keypress
.equals(kbLeft
)) {
554 int dotX
= getXColorPosition(color
);
555 int dotY
= getYColorPosition(color
);
559 color
= getColorFromPosition(dotX
, dotY
);
560 } else if (keypress
.equals(kbUp
)) {
561 int dotX
= getXColorPosition(color
);
562 int dotY
= getYColorPosition(color
);
566 color
= getColorFromPosition(dotX
, dotY
);
567 } else if (keypress
.equals(kbDown
)) {
568 int dotX
= getXColorPosition(color
);
569 int dotY
= getYColorPosition(color
);
573 color
= getColorFromPosition(dotX
, dotY
);
576 super.onKeypress(keypress
);
579 // Save this update to the local theme.
580 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
584 * Handle mouse press events.
586 * @param mouse mouse button press event
589 public void onMouseDown(final TMouseEvent mouse
) {
590 if (mouse
.isMouseWheelUp()) {
592 int dotX
= getXColorPosition(color
);
593 int dotY
= getYColorPosition(color
);
597 color
= getColorFromPosition(dotX
, dotY
);
598 } else if (mouse
.isMouseWheelDown()) {
599 // Do this like kbDown
600 int dotX
= getXColorPosition(color
);
601 int dotY
= getYColorPosition(color
);
605 color
= getColorFromPosition(dotX
, dotY
);
607 } else if ((mouse
.getX() > 0)
608 && (mouse
.getX() < getWidth() - 1)
609 && (mouse
.getY() > 0)
610 && (mouse
.getY() < getHeight() - 1)
612 color
= getColorFromPosition(mouse
.getX(), mouse
.getY());
614 // Let parent class handle it.
615 super.onMouseDown(mouse
);
619 // Save this update to the local theme.
620 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
625 // ------------------------------------------------------------------------
626 // Constructors -----------------------------------------------------------
627 // ------------------------------------------------------------------------
630 * Public constructor. The window will be centered on screen.
632 * @param application the TApplication that manages this window
634 public TEditColorThemeWindow(final TApplication application
) {
636 // Register with the TApplication
637 super(application
, i18n
.getString("windowTitle"), 0, 0, 60, 18, MODAL
);
639 // Initialize with the first color
640 List
<String
> colors
= getTheme().getColorNames();
641 assert (colors
.size() > 0);
642 editTheme
= new ColorTheme();
643 for (String key
: colors
) {
644 CellAttributes attr
= new CellAttributes();
645 attr
.setTo(getTheme().getColor(key
));
646 editTheme
.setColor(key
, attr
);
649 colorNames
= addList(colors
, 2, 2, 38, getHeight() - 7,
651 // When the user presses Enter
653 refreshFromTheme(colorNames
.getSelected());
657 // When the user navigates with keyboard
659 refreshFromTheme(colorNames
.getSelected());
663 // When the user navigates with keyboard
665 refreshFromTheme(colorNames
.getSelected());
669 foreground
= new ForegroundPicker(this, 42, 1, 14, 6);
670 background
= new BackgroundPicker(this, 42, 7, 14, 4);
671 refreshFromTheme(colors
.get(0));
672 colorNames
.setSelectedIndex(0);
674 addButton(i18n
.getString("okButton"), getWidth() - 37, getHeight() - 4,
677 ColorTheme global
= getTheme();
678 List
<String
> colors
= editTheme
.getColorNames();
679 for (String key
: colors
) {
680 CellAttributes attr
= new CellAttributes();
681 attr
.setTo(editTheme
.getColor(key
));
682 global
.setColor(key
, attr
);
684 getApplication().closeWindow(TEditColorThemeWindow
.this);
689 addButton(i18n
.getString("cancelButton"), getWidth() - 25,
693 getApplication().closeWindow(TEditColorThemeWindow
.this);
698 // Default to the color list
699 activate(colorNames
);
702 newStatusBar(i18n
.getString("statusBar"));
705 // ------------------------------------------------------------------------
706 // Event handlers ---------------------------------------------------------
707 // ------------------------------------------------------------------------
712 * @param keypress keystroke event
715 public void onKeypress(final TKeypressEvent keypress
) {
716 // Escape - behave like cancel
717 if (keypress
.equals(kbEsc
)) {
718 getApplication().closeWindow(this);
723 super.onKeypress(keypress
);
726 // ------------------------------------------------------------------------
727 // TWindow ----------------------------------------------------------------
728 // ------------------------------------------------------------------------
736 CellAttributes attr
= new CellAttributes();
738 // Draw the label on colorNames
739 attr
.setTo(getTheme().getColor("twindow.background.modal"));
740 if (colorNames
.isActive()) {
741 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
742 attr
.setBold(getTheme().getColor("tlabel").isBold());
744 putStringXY(3, 2, i18n
.getString("colorName"), attr
);
746 // Draw the sample text box
748 attr
.setForeColor(foreground
.color
);
749 attr
.setBold(foreground
.bold
);
750 attr
.setBackColor(background
.color
);
751 putStringXY(getWidth() - 17, getHeight() - 6,
752 i18n
.getString("textTextText"), attr
);
753 putStringXY(getWidth() - 17, getHeight() - 5,
754 i18n
.getString("textTextText"), attr
);
757 // ------------------------------------------------------------------------
758 // TEditColorThemeWindow --------------------------------------------------
759 // ------------------------------------------------------------------------
762 * Set various widgets/values to the editing theme color.
764 * @param colorName name of color from theme
766 private void refreshFromTheme(final String colorName
) {
767 CellAttributes attr
= editTheme
.getColor(colorName
);
768 foreground
.color
= attr
.getForeColor();
769 foreground
.bold
= attr
.isBold();
770 background
.color
= attr
.getBackColor();
774 * Examines foreground, background, and colorNames and sets the color in
777 private void saveToEditTheme() {
778 String colorName
= colorNames
.getSelected();
779 if (colorName
== null) {
782 CellAttributes attr
= editTheme
.getColor(colorName
);
783 attr
.setForeColor(foreground
.color
);
784 attr
.setBold(foreground
.bold
);
785 attr
.setBackColor(background
.color
);
786 editTheme
.setColor(colorName
, attr
);