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
;
33 import jexer
.bits
.Color
;
34 import jexer
.bits
.ColorTheme
;
35 import jexer
.bits
.CellAttributes
;
36 import jexer
.bits
.GraphicsChars
;
37 import jexer
.event
.TKeypressEvent
;
38 import jexer
.event
.TMouseEvent
;
39 import static jexer
.TKeypress
.*;
42 * TEditColorThemeWindow provides an easy UI for users to alter the running
46 public final class TEditColorThemeWindow
extends TWindow
{
49 * The foreground color picker.
51 class ForegroundPicker
extends TWidget
{
66 * @param parent parent widget
67 * @param x column relative to parent
68 * @param y row relative to parent
69 * @param width width of text area
70 * @param height height of text area
72 public ForegroundPicker(final TWidget parent
, final int x
,
73 final int y
, final int width
, final int height
) {
75 super(parent
, x
, y
, width
, height
);
79 * Get the X grid coordinate for this color.
81 * @param color the Color value
82 * @return the X coordinate
84 private int getXColorPosition(final Color color
) {
85 if (color
.equals(Color
.BLACK
)) {
87 } else if (color
.equals(Color
.BLUE
)) {
89 } else if (color
.equals(Color
.GREEN
)) {
91 } else if (color
.equals(Color
.CYAN
)) {
93 } else if (color
.equals(Color
.RED
)) {
95 } else if (color
.equals(Color
.MAGENTA
)) {
97 } else if (color
.equals(Color
.YELLOW
)) {
99 } else if (color
.equals(Color
.WHITE
)) {
102 throw new IllegalArgumentException("Invalid color: " + color
);
106 * Get the Y grid coordinate for this color.
108 * @param color the Color value
109 * @param bold if true use bold color
110 * @return the Y coordinate
112 private int getYColorPosition(final Color color
, final boolean bold
) {
114 if (color
.equals(Color
.RED
)) {
116 } else if (color
.equals(Color
.MAGENTA
)) {
118 } else if (color
.equals(Color
.YELLOW
)) {
120 } else if (color
.equals(Color
.WHITE
)) {
130 * Get the bold value based on Y grid coordinate.
132 * @param dotY the Y coordinate
133 * @return the bold value
135 private boolean getBoldFromPosition(final int dotY
) {
143 * Get the color based on (X, Y) grid coordinate.
145 * @param dotX the X coordinate
146 * @param dotY the Y coordinate
147 * @return the Color value
149 private Color
getColorFromPosition(final int dotX
, final int dotY
) {
154 if ((1 <= dotX
) && (dotX
<= 3) && (y
== 1)) {
157 if ((4 <= dotX
) && (dotX
<= 6) && (y
== 1)) {
160 if ((7 <= dotX
) && (dotX
<= 9) && (y
== 1)) {
163 if ((10 <= dotX
) && (dotX
<= 12) && (y
== 1)) {
166 if ((1 <= dotX
) && (dotX
<= 3) && (y
== 2)) {
169 if ((4 <= dotX
) && (dotX
<= 6) && (y
== 2)) {
170 return Color
.MAGENTA
;
172 if ((7 <= dotX
) && (dotX
<= 9) && (y
== 2)) {
175 if ((10 <= dotX
) && (dotX
<= 12) && (y
== 2)) {
179 throw new IllegalArgumentException("Invalid coordinates: "
180 + dotX
+ ", " + dotY
);
184 * Draw the foreground colors grid.
188 CellAttributes border
= getWindow().getBorder();
189 CellAttributes background
= getWindow().getBackground();
190 CellAttributes attr
= new CellAttributes();
192 getScreen().drawBox(0, 0, getWidth(), getHeight(), border
,
193 background
, 1, false);
195 attr
.setTo(getTheme().getColor("twindow.background.modal"));
197 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
198 attr
.setBold(getTheme().getColor("tlabel").isBold());
200 getScreen().putStringXY(1, 0, " Foreground ", attr
);
202 // Have to draw the colors manually because the int value matches
205 attr
.setForeColor(Color
.BLACK
);
206 putStringXY(1, 1, "\u2588\u2588\u2588", attr
);
207 attr
.setForeColor(Color
.BLUE
);
208 putStringXY(4, 1, "\u2588\u2588\u2588", attr
);
209 attr
.setForeColor(Color
.GREEN
);
210 putStringXY(7, 1, "\u2588\u2588\u2588", attr
);
211 attr
.setForeColor(Color
.CYAN
);
212 putStringXY(10, 1, "\u2588\u2588\u2588", attr
);
213 attr
.setForeColor(Color
.RED
);
214 putStringXY(1, 2, "\u2588\u2588\u2588", attr
);
215 attr
.setForeColor(Color
.MAGENTA
);
216 putStringXY(4, 2, "\u2588\u2588\u2588", attr
);
217 attr
.setForeColor(Color
.YELLOW
);
218 putStringXY(7, 2, "\u2588\u2588\u2588", attr
);
219 attr
.setForeColor(Color
.WHITE
);
220 putStringXY(10, 2, "\u2588\u2588\u2588", attr
);
223 attr
.setForeColor(Color
.BLACK
);
224 putStringXY(1, 3, "\u2588\u2588\u2588", attr
);
225 attr
.setForeColor(Color
.BLUE
);
226 putStringXY(4, 3, "\u2588\u2588\u2588", attr
);
227 attr
.setForeColor(Color
.GREEN
);
228 putStringXY(7, 3, "\u2588\u2588\u2588", attr
);
229 attr
.setForeColor(Color
.CYAN
);
230 putStringXY(10, 3, "\u2588\u2588\u2588", attr
);
231 attr
.setForeColor(Color
.RED
);
232 putStringXY(1, 4, "\u2588\u2588\u2588", attr
);
233 attr
.setForeColor(Color
.MAGENTA
);
234 putStringXY(4, 4, "\u2588\u2588\u2588", attr
);
235 attr
.setForeColor(Color
.YELLOW
);
236 putStringXY(7, 4, "\u2588\u2588\u2588", attr
);
237 attr
.setForeColor(Color
.WHITE
);
238 putStringXY(10, 4, "\u2588\u2588\u2588", attr
);
241 int dotX
= getXColorPosition(color
);
242 int dotY
= getYColorPosition(color
, bold
);
243 if (color
.equals(Color
.BLACK
) && !bold
) {
244 // Use white-on-black for black. All other colors use
245 // black-on-whatever.
247 getScreen().putCharXY(dotX
, dotY
, GraphicsChars
.CP437
[0x07],
250 attr
.setForeColor(color
);
252 getScreen().putCharXY(dotX
, dotY
, '\u25D8', attr
);
259 * @param keypress keystroke event
262 public void onKeypress(final TKeypressEvent keypress
) {
263 if (keypress
.equals(kbRight
)) {
264 int dotX
= getXColorPosition(color
);
265 int dotY
= getYColorPosition(color
, bold
);
269 color
= getColorFromPosition(dotX
, dotY
);
270 } else if (keypress
.equals(kbLeft
)) {
271 int dotX
= getXColorPosition(color
);
272 int dotY
= getYColorPosition(color
, bold
);
276 color
= getColorFromPosition(dotX
, dotY
);
277 } else if (keypress
.equals(kbUp
)) {
278 int dotX
= getXColorPosition(color
);
279 int dotY
= getYColorPosition(color
, bold
);
283 color
= getColorFromPosition(dotX
, dotY
);
284 bold
= getBoldFromPosition(dotY
);
285 } else if (keypress
.equals(kbDown
)) {
286 int dotX
= getXColorPosition(color
);
287 int dotY
= getYColorPosition(color
, bold
);
291 color
= getColorFromPosition(dotX
, dotY
);
292 bold
= getBoldFromPosition(dotY
);
295 super.onKeypress(keypress
);
299 // Save this update to the local theme.
300 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
304 * Handle mouse press events.
306 * @param mouse mouse button press event
309 public void onMouseDown(final TMouseEvent mouse
) {
310 if (mouse
.isMouseWheelUp()) {
312 int dotX
= getXColorPosition(color
);
313 int dotY
= getYColorPosition(color
, bold
);
317 color
= getColorFromPosition(dotX
, dotY
);
318 bold
= getBoldFromPosition(dotY
);
319 } else if (mouse
.isMouseWheelDown()) {
320 // Do this like kbDown
321 int dotX
= getXColorPosition(color
);
322 int dotY
= getYColorPosition(color
, bold
);
326 color
= getColorFromPosition(dotX
, dotY
);
327 bold
= getBoldFromPosition(dotY
);
328 } else if ((mouse
.getX() > 0)
329 && (mouse
.getX() < getWidth() - 1)
330 && (mouse
.getY() > 0)
331 && (mouse
.getY() < getHeight() - 1)
333 color
= getColorFromPosition(mouse
.getX(), mouse
.getY());
334 bold
= getBoldFromPosition(mouse
.getY());
336 // Let parent class handle it.
337 super.onMouseDown(mouse
);
341 // Save this update to the local theme.
342 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
348 * The background color picker.
350 class BackgroundPicker
extends TWidget
{
353 * The selected color.
358 * Public constructor.
360 * @param parent parent widget
361 * @param x column relative to parent
362 * @param y row relative to parent
363 * @param width width of text area
364 * @param height height of text area
366 public BackgroundPicker(final TWidget parent
, final int x
,
367 final int y
, final int width
, final int height
) {
369 super(parent
, x
, y
, width
, height
);
373 * Get the X grid coordinate for this color.
375 * @param color the Color value
376 * @return the X coordinate
378 private int getXColorPosition(final Color color
) {
379 if (color
.equals(Color
.BLACK
)) {
381 } else if (color
.equals(Color
.BLUE
)) {
383 } else if (color
.equals(Color
.GREEN
)) {
385 } else if (color
.equals(Color
.CYAN
)) {
387 } else if (color
.equals(Color
.RED
)) {
389 } else if (color
.equals(Color
.MAGENTA
)) {
391 } else if (color
.equals(Color
.YELLOW
)) {
393 } else if (color
.equals(Color
.WHITE
)) {
396 throw new IllegalArgumentException("Invalid color: " + color
);
400 * Get the Y grid coordinate for this color.
402 * @param color the Color value
403 * @return the Y coordinate
405 private int getYColorPosition(final Color color
) {
407 if (color
.equals(Color
.RED
)) {
409 } else if (color
.equals(Color
.MAGENTA
)) {
411 } else if (color
.equals(Color
.YELLOW
)) {
413 } else if (color
.equals(Color
.WHITE
)) {
420 * Get the color based on (X, Y) grid coordinate.
422 * @param dotX the X coordinate
423 * @param dotY the Y coordinate
424 * @return the Color value
426 private Color
getColorFromPosition(final int dotX
, final int dotY
) {
427 if ((1 <= dotX
) && (dotX
<= 3) && (dotY
== 1)) {
430 if ((4 <= dotX
) && (dotX
<= 6) && (dotY
== 1)) {
433 if ((7 <= dotX
) && (dotX
<= 9) && (dotY
== 1)) {
436 if ((10 <= dotX
) && (dotX
<= 12) && (dotY
== 1)) {
439 if ((1 <= dotX
) && (dotX
<= 3) && (dotY
== 2)) {
442 if ((4 <= dotX
) && (dotX
<= 6) && (dotY
== 2)) {
443 return Color
.MAGENTA
;
445 if ((7 <= dotX
) && (dotX
<= 9) && (dotY
== 2)) {
448 if ((10 <= dotX
) && (dotX
<= 12) && (dotY
== 2)) {
452 throw new IllegalArgumentException("Invalid coordinates: "
453 + dotX
+ ", " + dotY
);
457 * Draw the background colors grid.
461 CellAttributes border
= getWindow().getBorder();
462 CellAttributes background
= getWindow().getBackground();
463 CellAttributes attr
= new CellAttributes();
465 getScreen().drawBox(0, 0, getWidth(), getHeight(), border
,
466 background
, 1, false);
468 attr
.setTo(getTheme().getColor("twindow.background.modal"));
470 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
471 attr
.setBold(getTheme().getColor("tlabel").isBold());
473 getScreen().putStringXY(1, 0, " Background ", attr
);
475 // Have to draw the colors manually because the int value matches
478 attr
.setForeColor(Color
.BLACK
);
479 putStringXY(1, 1, "\u2588\u2588\u2588", attr
);
480 attr
.setForeColor(Color
.BLUE
);
481 putStringXY(4, 1, "\u2588\u2588\u2588", attr
);
482 attr
.setForeColor(Color
.GREEN
);
483 putStringXY(7, 1, "\u2588\u2588\u2588", attr
);
484 attr
.setForeColor(Color
.CYAN
);
485 putStringXY(10, 1, "\u2588\u2588\u2588", attr
);
486 attr
.setForeColor(Color
.RED
);
487 putStringXY(1, 2, "\u2588\u2588\u2588", attr
);
488 attr
.setForeColor(Color
.MAGENTA
);
489 putStringXY(4, 2, "\u2588\u2588\u2588", attr
);
490 attr
.setForeColor(Color
.YELLOW
);
491 putStringXY(7, 2, "\u2588\u2588\u2588", attr
);
492 attr
.setForeColor(Color
.WHITE
);
493 putStringXY(10, 2, "\u2588\u2588\u2588", attr
);
496 int dotX
= getXColorPosition(color
);
497 int dotY
= getYColorPosition(color
);
498 if (color
.equals(Color
.BLACK
)) {
499 // Use white-on-black for black. All other colors use
500 // black-on-whatever.
502 getScreen().putCharXY(dotX
, dotY
, GraphicsChars
.CP437
[0x07],
505 attr
.setForeColor(color
);
506 getScreen().putCharXY(dotX
, dotY
, '\u25D8', attr
);
514 * @param keypress keystroke event
517 public void onKeypress(final TKeypressEvent keypress
) {
518 if (keypress
.equals(kbRight
)) {
519 int dotX
= getXColorPosition(color
);
520 int dotY
= getYColorPosition(color
);
524 color
= getColorFromPosition(dotX
, dotY
);
525 } else if (keypress
.equals(kbLeft
)) {
526 int dotX
= getXColorPosition(color
);
527 int dotY
= getYColorPosition(color
);
531 color
= getColorFromPosition(dotX
, dotY
);
532 } else if (keypress
.equals(kbUp
)) {
533 int dotX
= getXColorPosition(color
);
534 int dotY
= getYColorPosition(color
);
538 color
= getColorFromPosition(dotX
, dotY
);
539 } else if (keypress
.equals(kbDown
)) {
540 int dotX
= getXColorPosition(color
);
541 int dotY
= getYColorPosition(color
);
545 color
= getColorFromPosition(dotX
, dotY
);
548 super.onKeypress(keypress
);
551 // Save this update to the local theme.
552 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
556 * Handle mouse press events.
558 * @param mouse mouse button press event
561 public void onMouseDown(final TMouseEvent mouse
) {
562 if (mouse
.isMouseWheelUp()) {
564 int dotX
= getXColorPosition(color
);
565 int dotY
= getYColorPosition(color
);
569 color
= getColorFromPosition(dotX
, dotY
);
570 } else if (mouse
.isMouseWheelDown()) {
571 // Do this like kbDown
572 int dotX
= getXColorPosition(color
);
573 int dotY
= getYColorPosition(color
);
577 color
= getColorFromPosition(dotX
, dotY
);
579 } else if ((mouse
.getX() > 0)
580 && (mouse
.getX() < getWidth() - 1)
581 && (mouse
.getY() > 0)
582 && (mouse
.getY() < getHeight() - 1)
584 color
= getColorFromPosition(mouse
.getX(), mouse
.getY());
586 // Let parent class handle it.
587 super.onMouseDown(mouse
);
591 // Save this update to the local theme.
592 ((TEditColorThemeWindow
) getWindow()).saveToEditTheme();
598 * The current editing theme.
600 private ColorTheme editTheme
;
603 * The left-side list of colors pane.
605 private TList colorNames
;
608 * The foreground color.
610 private ForegroundPicker foreground
;
613 * The background color.
615 private BackgroundPicker background
;
618 * Set various widgets/values to the editing theme color.
620 * @param colorName name of color from theme
622 private void refreshFromTheme(final String colorName
) {
623 CellAttributes attr
= editTheme
.getColor(colorName
);
624 foreground
.color
= attr
.getForeColor();
625 foreground
.bold
= attr
.isBold();
626 background
.color
= attr
.getBackColor();
630 * Examines foreground, background, and colorNames and sets the color in
633 private void saveToEditTheme() {
634 String colorName
= colorNames
.getSelected();
635 if (colorName
== null) {
638 CellAttributes attr
= editTheme
.getColor(colorName
);
639 attr
.setForeColor(foreground
.color
);
640 attr
.setBold(foreground
.bold
);
641 attr
.setBackColor(background
.color
);
642 editTheme
.setColor(colorName
, attr
);
646 * Public constructor. The file open box will be centered on screen.
648 * @param application the TApplication that manages this window
650 public TEditColorThemeWindow(final TApplication application
) {
652 // Register with the TApplication
653 super(application
, "Colors", 0, 0, 60, 18, MODAL
);
655 // Initialize with the first color
656 List
<String
> colors
= getTheme().getColorNames();
657 assert (colors
.size() > 0);
658 editTheme
= new ColorTheme();
659 for (String key
: colors
) {
660 CellAttributes attr
= new CellAttributes();
661 attr
.setTo(getTheme().getColor(key
));
662 editTheme
.setColor(key
, attr
);
665 colorNames
= addList(colors
, 2, 2, 38, getHeight() - 7,
667 // When the user presses Enter
669 refreshFromTheme(colorNames
.getSelected());
673 // When the user navigates with keyboard
675 refreshFromTheme(colorNames
.getSelected());
679 foreground
= new ForegroundPicker(this, 42, 1, 14, 6);
680 background
= new BackgroundPicker(this, 42, 7, 14, 4);
681 refreshFromTheme(colors
.get(0));
682 colorNames
.setSelectedIndex(0);
684 addButton(" &OK ", getWidth() - 37, getHeight() - 4,
687 ColorTheme global
= getTheme();
688 List
<String
> colors
= editTheme
.getColorNames();
689 for (String key
: colors
) {
690 CellAttributes attr
= new CellAttributes();
691 attr
.setTo(editTheme
.getColor(key
));
692 global
.setColor(key
, attr
);
694 getApplication().closeWindow(TEditColorThemeWindow
.this);
699 addButton("&Cancel", getWidth() - 25, getHeight() - 4,
702 getApplication().closeWindow(TEditColorThemeWindow
.this);
707 // Default to the color list
708 activate(colorNames
);
711 newStatusBar("Select Colors");
720 CellAttributes attr
= new CellAttributes();
722 // Draw the label on colorNames
723 attr
.setTo(getTheme().getColor("twindow.background.modal"));
724 if (colorNames
.isActive()) {
725 attr
.setForeColor(getTheme().getColor("tlabel").getForeColor());
726 attr
.setBold(getTheme().getColor("tlabel").isBold());
728 getScreen().putStringXY(3, 2, "Color Name", attr
);
730 // Draw the sample text box
732 attr
.setForeColor(foreground
.color
);
733 attr
.setBold(foreground
.bold
);
734 attr
.setBackColor(background
.color
);
735 getScreen().putStringXY(getWidth() - 17, getHeight() - 6,
736 "Text Text Text", attr
);
737 getScreen().putStringXY(getWidth() - 17, getHeight() - 5,
738 "Text Text Text", attr
);
744 * @param keypress keystroke event
747 public void onKeypress(final TKeypressEvent keypress
) {
748 // Escape - behave like cancel
749 if (keypress
.equals(kbEsc
)) {
750 getApplication().closeWindow(this);
755 super.onKeypress(keypress
);