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());
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 getScreen().drawBox(0, 0, getWidth(), getHeight(), border
,
223 background
, 1, false);
225 attr
.setTo(getTheme().getColor("twindow.background.modal"));
227 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
228 attr
.setBold(getTheme().getColor("tlabel").isBold());
230 getScreen().putStringXY(1, 0, i18n
.getString("foregroundLabel"),
233 // Have to draw the colors manually because the int value matches
236 attr
.setForeColor(Color
.BLACK
);
237 putStringXY(1, 1, "\u2588\u2588\u2588", attr
);
238 attr
.setForeColor(Color
.BLUE
);
239 putStringXY(4, 1, "\u2588\u2588\u2588", attr
);
240 attr
.setForeColor(Color
.GREEN
);
241 putStringXY(7, 1, "\u2588\u2588\u2588", attr
);
242 attr
.setForeColor(Color
.CYAN
);
243 putStringXY(10, 1, "\u2588\u2588\u2588", attr
);
244 attr
.setForeColor(Color
.RED
);
245 putStringXY(1, 2, "\u2588\u2588\u2588", attr
);
246 attr
.setForeColor(Color
.MAGENTA
);
247 putStringXY(4, 2, "\u2588\u2588\u2588", attr
);
248 attr
.setForeColor(Color
.YELLOW
);
249 putStringXY(7, 2, "\u2588\u2588\u2588", attr
);
250 attr
.setForeColor(Color
.WHITE
);
251 putStringXY(10, 2, "\u2588\u2588\u2588", attr
);
254 attr
.setForeColor(Color
.BLACK
);
255 putStringXY(1, 3, "\u2588\u2588\u2588", attr
);
256 attr
.setForeColor(Color
.BLUE
);
257 putStringXY(4, 3, "\u2588\u2588\u2588", attr
);
258 attr
.setForeColor(Color
.GREEN
);
259 putStringXY(7, 3, "\u2588\u2588\u2588", attr
);
260 attr
.setForeColor(Color
.CYAN
);
261 putStringXY(10, 3, "\u2588\u2588\u2588", attr
);
262 attr
.setForeColor(Color
.RED
);
263 putStringXY(1, 4, "\u2588\u2588\u2588", attr
);
264 attr
.setForeColor(Color
.MAGENTA
);
265 putStringXY(4, 4, "\u2588\u2588\u2588", attr
);
266 attr
.setForeColor(Color
.YELLOW
);
267 putStringXY(7, 4, "\u2588\u2588\u2588", attr
);
268 attr
.setForeColor(Color
.WHITE
);
269 putStringXY(10, 4, "\u2588\u2588\u2588", attr
);
272 int dotX
= getXColorPosition(color
);
273 int dotY
= getYColorPosition(color
, bold
);
274 if (color
.equals(Color
.BLACK
) && !bold
) {
275 // Use white-on-black for black. All other colors use
276 // black-on-whatever.
278 getScreen().putCharXY(dotX
, dotY
, GraphicsChars
.CP437
[0x07],
281 attr
.setForeColor(color
);
283 getScreen().putCharXY(dotX
, dotY
, '\u25D8', attr
);
290 * @param keypress keystroke event
293 public void onKeypress(final TKeypressEvent keypress
) {
294 if (keypress
.equals(kbRight
)) {
295 int dotX
= getXColorPosition(color
);
296 int dotY
= getYColorPosition(color
, bold
);
300 color
= getColorFromPosition(dotX
, dotY
);
301 } else if (keypress
.equals(kbLeft
)) {
302 int dotX
= getXColorPosition(color
);
303 int dotY
= getYColorPosition(color
, bold
);
307 color
= getColorFromPosition(dotX
, dotY
);
308 } else if (keypress
.equals(kbUp
)) {
309 int dotX
= getXColorPosition(color
);
310 int dotY
= getYColorPosition(color
, bold
);
314 color
= getColorFromPosition(dotX
, dotY
);
315 bold
= getBoldFromPosition(dotY
);
316 } else if (keypress
.equals(kbDown
)) {
317 int dotX
= getXColorPosition(color
);
318 int dotY
= getYColorPosition(color
, bold
);
322 color
= getColorFromPosition(dotX
, dotY
);
323 bold
= getBoldFromPosition(dotY
);
326 super.onKeypress(keypress
);
330 // Save this update to the local theme.
331 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
335 * Handle mouse press events.
337 * @param mouse mouse button press event
340 public void onMouseDown(final TMouseEvent mouse
) {
341 if (mouse
.isMouseWheelUp()) {
343 int dotX
= getXColorPosition(color
);
344 int dotY
= getYColorPosition(color
, bold
);
348 color
= getColorFromPosition(dotX
, dotY
);
349 bold
= getBoldFromPosition(dotY
);
350 } else if (mouse
.isMouseWheelDown()) {
351 // Do this like kbDown
352 int dotX
= getXColorPosition(color
);
353 int dotY
= getYColorPosition(color
, bold
);
357 color
= getColorFromPosition(dotX
, dotY
);
358 bold
= getBoldFromPosition(dotY
);
359 } else if ((mouse
.getX() > 0)
360 && (mouse
.getX() < getWidth() - 1)
361 && (mouse
.getY() > 0)
362 && (mouse
.getY() < getHeight() - 1)
364 color
= getColorFromPosition(mouse
.getX(), mouse
.getY());
365 bold
= getBoldFromPosition(mouse
.getY());
367 // Let parent class handle it.
368 super.onMouseDown(mouse
);
372 // Save this update to the local theme.
373 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
379 * The background color picker.
381 class BackgroundPicker
extends TWidget
{
384 * The selected color.
389 * Public constructor.
391 * @param parent parent widget
392 * @param x column relative to parent
393 * @param y row relative to parent
394 * @param width width of text area
395 * @param height height of text area
397 public BackgroundPicker(final TWidget parent
, final int x
,
398 final int y
, final int width
, final int height
) {
400 super(parent
, x
, y
, width
, height
);
404 * Get the X grid coordinate for this color.
406 * @param color the Color value
407 * @return the X coordinate
409 private int getXColorPosition(final Color color
) {
410 if (color
.equals(Color
.BLACK
)) {
412 } else if (color
.equals(Color
.BLUE
)) {
414 } else if (color
.equals(Color
.GREEN
)) {
416 } else if (color
.equals(Color
.CYAN
)) {
418 } else if (color
.equals(Color
.RED
)) {
420 } else if (color
.equals(Color
.MAGENTA
)) {
422 } else if (color
.equals(Color
.YELLOW
)) {
424 } else if (color
.equals(Color
.WHITE
)) {
427 throw new IllegalArgumentException("Invalid color: " + color
);
431 * Get the Y grid coordinate for this color.
433 * @param color the Color value
434 * @return the Y coordinate
436 private int getYColorPosition(final Color color
) {
438 if (color
.equals(Color
.RED
)) {
440 } else if (color
.equals(Color
.MAGENTA
)) {
442 } else if (color
.equals(Color
.YELLOW
)) {
444 } else if (color
.equals(Color
.WHITE
)) {
451 * Get the color based on (X, Y) grid coordinate.
453 * @param dotX the X coordinate
454 * @param dotY the Y coordinate
455 * @return the Color value
457 private Color
getColorFromPosition(final int dotX
, final int dotY
) {
458 if ((1 <= dotX
) && (dotX
<= 3) && (dotY
== 1)) {
461 if ((4 <= dotX
) && (dotX
<= 6) && (dotY
== 1)) {
464 if ((7 <= dotX
) && (dotX
<= 9) && (dotY
== 1)) {
467 if ((10 <= dotX
) && (dotX
<= 12) && (dotY
== 1)) {
470 if ((1 <= dotX
) && (dotX
<= 3) && (dotY
== 2)) {
473 if ((4 <= dotX
) && (dotX
<= 6) && (dotY
== 2)) {
474 return Color
.MAGENTA
;
476 if ((7 <= dotX
) && (dotX
<= 9) && (dotY
== 2)) {
479 if ((10 <= dotX
) && (dotX
<= 12) && (dotY
== 2)) {
483 throw new IllegalArgumentException("Invalid coordinates: "
484 + dotX
+ ", " + dotY
);
488 * Draw the background colors grid.
492 CellAttributes border
= getWindow().getBorder();
493 CellAttributes background
= getWindow().getBackground();
494 CellAttributes attr
= new CellAttributes();
496 getScreen().drawBox(0, 0, getWidth(), getHeight(), border
,
497 background
, 1, false);
499 attr
.setTo(getTheme().getColor("twindow.background.modal"));
501 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
502 attr
.setBold(getTheme().getColor("tlabel").isBold());
504 getScreen().putStringXY(1, 0, i18n
.getString("backgroundLabel"),
507 // Have to draw the colors manually because the int value matches
510 attr
.setForeColor(Color
.BLACK
);
511 putStringXY(1, 1, "\u2588\u2588\u2588", attr
);
512 attr
.setForeColor(Color
.BLUE
);
513 putStringXY(4, 1, "\u2588\u2588\u2588", attr
);
514 attr
.setForeColor(Color
.GREEN
);
515 putStringXY(7, 1, "\u2588\u2588\u2588", attr
);
516 attr
.setForeColor(Color
.CYAN
);
517 putStringXY(10, 1, "\u2588\u2588\u2588", attr
);
518 attr
.setForeColor(Color
.RED
);
519 putStringXY(1, 2, "\u2588\u2588\u2588", attr
);
520 attr
.setForeColor(Color
.MAGENTA
);
521 putStringXY(4, 2, "\u2588\u2588\u2588", attr
);
522 attr
.setForeColor(Color
.YELLOW
);
523 putStringXY(7, 2, "\u2588\u2588\u2588", attr
);
524 attr
.setForeColor(Color
.WHITE
);
525 putStringXY(10, 2, "\u2588\u2588\u2588", attr
);
528 int dotX
= getXColorPosition(color
);
529 int dotY
= getYColorPosition(color
);
530 if (color
.equals(Color
.BLACK
)) {
531 // Use white-on-black for black. All other colors use
532 // black-on-whatever.
534 getScreen().putCharXY(dotX
, dotY
, GraphicsChars
.CP437
[0x07],
537 attr
.setForeColor(color
);
538 getScreen().putCharXY(dotX
, dotY
, '\u25D8', attr
);
546 * @param keypress keystroke event
549 public void onKeypress(final TKeypressEvent keypress
) {
550 if (keypress
.equals(kbRight
)) {
551 int dotX
= getXColorPosition(color
);
552 int dotY
= getYColorPosition(color
);
556 color
= getColorFromPosition(dotX
, dotY
);
557 } else if (keypress
.equals(kbLeft
)) {
558 int dotX
= getXColorPosition(color
);
559 int dotY
= getYColorPosition(color
);
563 color
= getColorFromPosition(dotX
, dotY
);
564 } else if (keypress
.equals(kbUp
)) {
565 int dotX
= getXColorPosition(color
);
566 int dotY
= getYColorPosition(color
);
570 color
= getColorFromPosition(dotX
, dotY
);
571 } else if (keypress
.equals(kbDown
)) {
572 int dotX
= getXColorPosition(color
);
573 int dotY
= getYColorPosition(color
);
577 color
= getColorFromPosition(dotX
, dotY
);
580 super.onKeypress(keypress
);
583 // Save this update to the local theme.
584 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
588 * Handle mouse press events.
590 * @param mouse mouse button press event
593 public void onMouseDown(final TMouseEvent mouse
) {
594 if (mouse
.isMouseWheelUp()) {
596 int dotX
= getXColorPosition(color
);
597 int dotY
= getYColorPosition(color
);
601 color
= getColorFromPosition(dotX
, dotY
);
602 } else if (mouse
.isMouseWheelDown()) {
603 // Do this like kbDown
604 int dotX
= getXColorPosition(color
);
605 int dotY
= getYColorPosition(color
);
609 color
= getColorFromPosition(dotX
, dotY
);
611 } else if ((mouse
.getX() > 0)
612 && (mouse
.getX() < getWidth() - 1)
613 && (mouse
.getY() > 0)
614 && (mouse
.getY() < getHeight() - 1)
616 color
= getColorFromPosition(mouse
.getX(), mouse
.getY());
618 // Let parent class handle it.
619 super.onMouseDown(mouse
);
623 // Save this update to the local theme.
624 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
629 // ------------------------------------------------------------------------
630 // Constructors -----------------------------------------------------------
631 // ------------------------------------------------------------------------
634 * Public constructor. The file open box will be centered on screen.
636 * @param application the TApplication that manages this window
638 public TEditColorThemeWindow(final TApplication application
) {
640 // Register with the TApplication
641 super(application
, i18n
.getString("windowTitle"), 0, 0, 60, 18, MODAL
);
643 // Initialize with the first color
644 List
<String
> colors
= getTheme().getColorNames();
645 assert (colors
.size() > 0);
646 editTheme
= new ColorTheme();
647 for (String key
: colors
) {
648 CellAttributes attr
= new CellAttributes();
649 attr
.setTo(getTheme().getColor(key
));
650 editTheme
.setColor(key
, attr
);
653 colorNames
= addList(colors
, 2, 2, 38, getHeight() - 7,
655 // When the user presses Enter
657 refreshFromTheme(colorNames
.getSelected());
661 // When the user navigates with keyboard
663 refreshFromTheme(colorNames
.getSelected());
667 foreground
= new ForegroundPicker(this, 42, 1, 14, 6);
668 background
= new BackgroundPicker(this, 42, 7, 14, 4);
669 refreshFromTheme(colors
.get(0));
670 colorNames
.setSelectedIndex(0);
672 addButton(i18n
.getString("okButton"), getWidth() - 37, getHeight() - 4,
675 ColorTheme global
= getTheme();
676 List
<String
> colors
= editTheme
.getColorNames();
677 for (String key
: colors
) {
678 CellAttributes attr
= new CellAttributes();
679 attr
.setTo(editTheme
.getColor(key
));
680 global
.setColor(key
, attr
);
682 getApplication().closeWindow(TEditColorThemeWindow
.this);
687 addButton(i18n
.getString("cancelButton"), getWidth() - 25,
691 getApplication().closeWindow(TEditColorThemeWindow
.this);
696 // Default to the color list
697 activate(colorNames
);
700 newStatusBar(i18n
.getString("statusBar"));
703 // ------------------------------------------------------------------------
704 // Event handlers ---------------------------------------------------------
705 // ------------------------------------------------------------------------
710 * @param keypress keystroke event
713 public void onKeypress(final TKeypressEvent keypress
) {
714 // Escape - behave like cancel
715 if (keypress
.equals(kbEsc
)) {
716 getApplication().closeWindow(this);
721 super.onKeypress(keypress
);
724 // ------------------------------------------------------------------------
725 // TWindow ----------------------------------------------------------------
726 // ------------------------------------------------------------------------
734 CellAttributes attr
= new CellAttributes();
736 // Draw the label on colorNames
737 attr
.setTo(getTheme().getColor("twindow.background.modal"));
738 if (colorNames
.isActive()) {
739 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
740 attr
.setBold(getTheme().getColor("tlabel").isBold());
742 getScreen().putStringXY(3, 2, i18n
.getString("colorName"), attr
);
744 // Draw the sample text box
746 attr
.setForeColor(foreground
.color
);
747 attr
.setBold(foreground
.bold
);
748 attr
.setBackColor(background
.color
);
749 getScreen().putStringXY(getWidth() - 17, getHeight() - 6,
750 i18n
.getString("textTextText"), attr
);
751 getScreen().putStringXY(getWidth() - 17, getHeight() - 5,
752 i18n
.getString("textTextText"), attr
);
755 // ------------------------------------------------------------------------
756 // TEditColorThemeWindow --------------------------------------------------
757 // ------------------------------------------------------------------------
760 * Set various widgets/values to the editing theme color.
762 * @param colorName name of color from theme
764 private void refreshFromTheme(final String colorName
) {
765 CellAttributes attr
= editTheme
.getColor(colorName
);
766 foreground
.color
= attr
.getForeColor();
767 foreground
.bold
= attr
.isBold();
768 background
.color
= attr
.getBackColor();
772 * Examines foreground, background, and colorNames and sets the color in
775 private void saveToEditTheme() {
776 String colorName
= colorNames
.getSelected();
777 if (colorName
== null) {
780 CellAttributes attr
= editTheme
.getColor(colorName
);
781 attr
.setForeColor(foreground
.color
);
782 attr
.setBold(foreground
.bold
);
783 attr
.setBackColor(background
.color
);
784 editTheme
.setColor(colorName
, attr
);