LICENSE CHANGED TO MIT
[nikiroo-utils.git] / src / jexer / TEditColorThemeWindow.java
CommitLineData
3649b921
KL
1/*
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
3649b921 5 *
e16dda65 6 * Copyright (C) 2016 Kevin Lamonte
3649b921 7 *
e16dda65
KL
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:
3649b921 14 *
e16dda65
KL
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
3649b921 17 *
e16dda65
KL
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.
3649b921
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer;
30
31import java.util.List;
32
33import jexer.bits.Color;
34import jexer.bits.ColorTheme;
35import jexer.bits.CellAttributes;
36import jexer.bits.GraphicsChars;
37import jexer.event.TKeypressEvent;
38import jexer.event.TMouseEvent;
39import static jexer.TKeypress.*;
40
41/**
42 * TEditColorThemeWindow provides an easy UI for users to alter the running
43 * color theme.
44 *
45 */
46public final class TEditColorThemeWindow extends TWindow {
47
48 /**
49 * The foreground color picker.
50 */
51 class ForegroundPicker extends TWidget {
52
53 /**
54 * The selected color.
55 */
56 Color color;
57
58 /**
59 * The bold flag.
60 */
61 boolean bold;
62
63 /**
64 * Public constructor.
65 *
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
71 */
72 public ForegroundPicker(final TWidget parent, final int x,
73 final int y, final int width, final int height) {
74
75 super(parent, x, y, width, height);
76 }
77
78 /**
79 * Get the X grid coordinate for this color.
80 *
81 * @param color the Color value
82 * @return the X coordinate
83 */
84 private int getXColorPosition(final Color color) {
85 if (color.equals(Color.BLACK)) {
86 return 2;
87 } else if (color.equals(Color.BLUE)) {
88 return 5;
89 } else if (color.equals(Color.GREEN)) {
90 return 8;
91 } else if (color.equals(Color.CYAN)) {
92 return 11;
93 } else if (color.equals(Color.RED)) {
94 return 2;
95 } else if (color.equals(Color.MAGENTA)) {
96 return 5;
97 } else if (color.equals(Color.YELLOW)) {
98 return 8;
99 } else if (color.equals(Color.WHITE)) {
100 return 11;
101 }
102 throw new IllegalArgumentException("Invalid color: " + color);
103 }
104
105 /**
106 * Get the Y grid coordinate for this color.
107 *
108 * @param color the Color value
109 * @param bold if true use bold color
110 * @return the Y coordinate
111 */
112 private int getYColorPosition(final Color color, final boolean bold) {
113 int dotY = 1;
114 if (color.equals(Color.RED)) {
115 dotY = 2;
116 } else if (color.equals(Color.MAGENTA)) {
117 dotY = 2;
118 } else if (color.equals(Color.YELLOW)) {
119 dotY = 2;
120 } else if (color.equals(Color.WHITE)) {
121 dotY = 2;
122 }
123 if (bold) {
124 dotY += 2;
125 }
126 return dotY;
127 }
128
129 /**
130 * Get the bold value based on Y grid coordinate.
131 *
132 * @param dotY the Y coordinate
133 * @return the bold value
134 */
135 private boolean getBoldFromPosition(final int dotY) {
136 if (dotY > 2) {
137 return true;
138 }
139 return false;
140 }
141
142 /**
143 * Get the color based on (X, Y) grid coordinate.
144 *
145 * @param dotX the X coordinate
146 * @param dotY the Y coordinate
147 * @return the Color value
148 */
149 private Color getColorFromPosition(final int dotX, final int dotY) {
150 int y = dotY;
151 if (y > 2) {
152 y -= 2;
153 }
154 if ((1 <= dotX) && (dotX <= 3) && (y == 1)) {
155 return Color.BLACK;
156 }
157 if ((4 <= dotX) && (dotX <= 6) && (y == 1)) {
158 return Color.BLUE;
159 }
160 if ((7 <= dotX) && (dotX <= 9) && (y == 1)) {
161 return Color.GREEN;
162 }
163 if ((10 <= dotX) && (dotX <= 12) && (y == 1)) {
164 return Color.CYAN;
165 }
166 if ((1 <= dotX) && (dotX <= 3) && (y == 2)) {
167 return Color.RED;
168 }
169 if ((4 <= dotX) && (dotX <= 6) && (y == 2)) {
170 return Color.MAGENTA;
171 }
172 if ((7 <= dotX) && (dotX <= 9) && (y == 2)) {
173 return Color.YELLOW;
174 }
175 if ((10 <= dotX) && (dotX <= 12) && (y == 2)) {
176 return Color.WHITE;
177 }
178
179 throw new IllegalArgumentException("Invalid coordinates: "
180 + dotX + ", " + dotY);
181 }
182
183 /**
184 * Draw the foreground colors grid.
185 */
186 @Override
187 public void draw() {
188 CellAttributes border = getWindow().getBorder();
189 CellAttributes background = getWindow().getBackground();
190 CellAttributes attr = new CellAttributes();
191
192 getScreen().drawBox(0, 0, getWidth(), getHeight(), border,
193 background, 1, false);
194
195 attr.setTo(getTheme().getColor("twindow.background.modal"));
196 if (isActive()) {
197 attr.setForeColor(getTheme().getColor("tlabel").getForeColor());
198 attr.setBold(getTheme().getColor("tlabel").isBold());
199 }
200 getScreen().putStringXY(1, 0, " Foreground ", attr);
201
202 // Have to draw the colors manually because the int value matches
203 // SGR, not CGA.
204 attr.reset();
205 attr.setReverse(true);
206 attr.setForeColor(Color.BLACK);
207 putStringXY(1, 1, " ", attr);
208 attr.setForeColor(Color.BLUE);
209 putStringXY(4, 1, " ", attr);
210 attr.setForeColor(Color.GREEN);
211 putStringXY(7, 1, " ", attr);
212 attr.setForeColor(Color.CYAN);
213 putStringXY(10, 1, " ", attr);
214 attr.setForeColor(Color.RED);
215 putStringXY(1, 2, " ", attr);
216 attr.setForeColor(Color.MAGENTA);
217 putStringXY(4, 2, " ", attr);
218 attr.setForeColor(Color.YELLOW);
219 putStringXY(7, 2, " ", attr);
220 attr.setForeColor(Color.WHITE);
221 putStringXY(10, 2, " ", attr);
222
223 attr.setBold(true);
224 attr.setForeColor(Color.BLACK);
225 putStringXY(1, 3, " ", attr);
226 attr.setForeColor(Color.BLUE);
227 putStringXY(4, 3, " ", attr);
228 attr.setForeColor(Color.GREEN);
229 putStringXY(7, 3, " ", attr);
230 attr.setForeColor(Color.CYAN);
231 putStringXY(10, 3, " ", attr);
232 attr.setForeColor(Color.RED);
233 putStringXY(1, 4, " ", attr);
234 attr.setForeColor(Color.MAGENTA);
235 putStringXY(4, 4, " ", attr);
236 attr.setForeColor(Color.YELLOW);
237 putStringXY(7, 4, " ", attr);
238 attr.setForeColor(Color.WHITE);
239 putStringXY(10, 4, " ", attr);
240
241 // Draw the dot
242 int dotX = getXColorPosition(color);
243 int dotY = getYColorPosition(color, bold);
244 if (color.equals(Color.BLACK) && !bold) {
245 // Use white-on-black for black. All other colors use
246 // black-on-whatever.
247 attr.reset();
248 getScreen().putCharXY(dotX, dotY, GraphicsChars.CP437[0x07],
249 attr);
250 } else {
251 getScreen().putCharXY(dotX, dotY, GraphicsChars.CP437[0x07]);
252 }
253 }
254
255 /**
256 * Handle keystrokes.
257 *
258 * @param keypress keystroke event
259 */
260 @Override
261 public void onKeypress(final TKeypressEvent keypress) {
262 if (keypress.equals(kbRight)) {
263 int dotX = getXColorPosition(color);
264 int dotY = getYColorPosition(color, bold);
265 if (dotX < 10) {
266 dotX += 3;
267 }
268 color = getColorFromPosition(dotX, dotY);
269 } else if (keypress.equals(kbLeft)) {
270 int dotX = getXColorPosition(color);
271 int dotY = getYColorPosition(color, bold);
272 if (dotX > 3) {
273 dotX -= 3;
274 }
275 color = getColorFromPosition(dotX, dotY);
276 } else if (keypress.equals(kbUp)) {
277 int dotX = getXColorPosition(color);
278 int dotY = getYColorPosition(color, bold);
279 if (dotY > 1) {
280 dotY--;
281 }
282 color = getColorFromPosition(dotX, dotY);
283 bold = getBoldFromPosition(dotY);
284 } else if (keypress.equals(kbDown)) {
285 int dotX = getXColorPosition(color);
286 int dotY = getYColorPosition(color, bold);
287 if (dotY < 4) {
288 dotY++;
289 }
290 color = getColorFromPosition(dotX, dotY);
291 bold = getBoldFromPosition(dotY);
292 } else {
293 // Pass to my parent
294 super.onKeypress(keypress);
295 return;
296 }
297
298 // Save this update to the local theme.
299 ((TEditColorThemeWindow) getWindow()).saveToEditTheme();
300 }
301
302 /**
303 * Handle mouse press events.
304 *
305 * @param mouse mouse button press event
306 */
307 @Override
308 public void onMouseDown(final TMouseEvent mouse) {
309 if (mouse.isMouseWheelUp()) {
310 // Do this like kbUp
311 int dotX = getXColorPosition(color);
312 int dotY = getYColorPosition(color, bold);
313 if (dotY > 1) {
314 dotY--;
315 }
316 color = getColorFromPosition(dotX, dotY);
317 bold = getBoldFromPosition(dotY);
318 } else if (mouse.isMouseWheelDown()) {
319 // Do this like kbDown
320 int dotX = getXColorPosition(color);
321 int dotY = getYColorPosition(color, bold);
322 if (dotY < 4) {
323 dotY++;
324 }
325 color = getColorFromPosition(dotX, dotY);
326 bold = getBoldFromPosition(dotY);
327 } else if ((mouse.getX() > 0)
328 && (mouse.getX() < getWidth() - 1)
329 && (mouse.getY() > 0)
330 && (mouse.getY() < getHeight() - 1)
331 ) {
332 color = getColorFromPosition(mouse.getX(), mouse.getY());
333 bold = getBoldFromPosition(mouse.getY());
334 } else {
335 // Let parent class handle it.
336 super.onMouseDown(mouse);
337 return;
338 }
339
340 // Save this update to the local theme.
341 ((TEditColorThemeWindow) getWindow()).saveToEditTheme();
342 }
343
344 }
345
346 /**
347 * The background color picker.
348 */
349 class BackgroundPicker extends TWidget {
350
351 /**
352 * The selected color.
353 */
354 Color color;
355
356 /**
357 * Public constructor.
358 *
359 * @param parent parent widget
360 * @param x column relative to parent
361 * @param y row relative to parent
362 * @param width width of text area
363 * @param height height of text area
364 */
365 public BackgroundPicker(final TWidget parent, final int x,
366 final int y, final int width, final int height) {
367
368 super(parent, x, y, width, height);
369 }
370
371 /**
372 * Get the X grid coordinate for this color.
373 *
374 * @param color the Color value
375 * @return the X coordinate
376 */
377 private int getXColorPosition(final Color color) {
378 if (color.equals(Color.BLACK)) {
379 return 2;
380 } else if (color.equals(Color.BLUE)) {
381 return 5;
382 } else if (color.equals(Color.GREEN)) {
383 return 8;
384 } else if (color.equals(Color.CYAN)) {
385 return 11;
386 } else if (color.equals(Color.RED)) {
387 return 2;
388 } else if (color.equals(Color.MAGENTA)) {
389 return 5;
390 } else if (color.equals(Color.YELLOW)) {
391 return 8;
392 } else if (color.equals(Color.WHITE)) {
393 return 11;
394 }
395 throw new IllegalArgumentException("Invalid color: " + color);
396 }
397
398 /**
399 * Get the Y grid coordinate for this color.
400 *
401 * @param color the Color value
402 * @return the Y coordinate
403 */
404 private int getYColorPosition(final Color color) {
405 int dotY = 1;
406 if (color.equals(Color.RED)) {
407 dotY = 2;
408 } else if (color.equals(Color.MAGENTA)) {
409 dotY = 2;
410 } else if (color.equals(Color.YELLOW)) {
411 dotY = 2;
412 } else if (color.equals(Color.WHITE)) {
413 dotY = 2;
414 }
415 return dotY;
416 }
417
418 /**
419 * Get the color based on (X, Y) grid coordinate.
420 *
421 * @param dotX the X coordinate
422 * @param dotY the Y coordinate
423 * @return the Color value
424 */
425 private Color getColorFromPosition(final int dotX, final int dotY) {
426 if ((1 <= dotX) && (dotX <= 3) && (dotY == 1)) {
427 return Color.BLACK;
428 }
429 if ((4 <= dotX) && (dotX <= 6) && (dotY == 1)) {
430 return Color.BLUE;
431 }
432 if ((7 <= dotX) && (dotX <= 9) && (dotY == 1)) {
433 return Color.GREEN;
434 }
435 if ((10 <= dotX) && (dotX <= 12) && (dotY == 1)) {
436 return Color.CYAN;
437 }
438 if ((1 <= dotX) && (dotX <= 3) && (dotY == 2)) {
439 return Color.RED;
440 }
441 if ((4 <= dotX) && (dotX <= 6) && (dotY == 2)) {
442 return Color.MAGENTA;
443 }
444 if ((7 <= dotX) && (dotX <= 9) && (dotY == 2)) {
445 return Color.YELLOW;
446 }
447 if ((10 <= dotX) && (dotX <= 12) && (dotY == 2)) {
448 return Color.WHITE;
449 }
450
451 throw new IllegalArgumentException("Invalid coordinates: "
452 + dotX + ", " + dotY);
453 }
454
455 /**
456 * Draw the background colors grid.
457 */
458 @Override
459 public void draw() {
460 CellAttributes border = getWindow().getBorder();
461 CellAttributes background = getWindow().getBackground();
462 CellAttributes attr = new CellAttributes();
463
464 getScreen().drawBox(0, 0, getWidth(), getHeight(), border,
465 background, 1, false);
466
467 attr.setTo(getTheme().getColor("twindow.background.modal"));
468 if (isActive()) {
469 attr.setForeColor(getTheme().getColor("tlabel").getForeColor());
470 attr.setBold(getTheme().getColor("tlabel").isBold());
471 }
472 getScreen().putStringXY(1, 0, " Background ", attr);
473
474 // Have to draw the colors manually because the int value matches
475 // SGR, not CGA.
476 attr.reset();
477 attr.setReverse(true);
478 attr.setForeColor(Color.BLACK);
479 putStringXY(1, 1, " ", attr);
480 attr.setForeColor(Color.BLUE);
481 putStringXY(4, 1, " ", attr);
482 attr.setForeColor(Color.GREEN);
483 putStringXY(7, 1, " ", attr);
484 attr.setForeColor(Color.CYAN);
485 putStringXY(10, 1, " ", attr);
486 attr.setForeColor(Color.RED);
487 putStringXY(1, 2, " ", attr);
488 attr.setForeColor(Color.MAGENTA);
489 putStringXY(4, 2, " ", attr);
490 attr.setForeColor(Color.YELLOW);
491 putStringXY(7, 2, " ", attr);
492 attr.setForeColor(Color.WHITE);
493 putStringXY(10, 2, " ", attr);
494
495 // Draw the dot
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.
501 attr.reset();
502 getScreen().putCharXY(dotX, dotY, GraphicsChars.CP437[0x07],
503 attr);
504 } else {
505 getScreen().putCharXY(dotX, dotY, GraphicsChars.CP437[0x07]);
506 }
507
508 }
509
510 /**
511 * Handle keystrokes.
512 *
513 * @param keypress keystroke event
514 */
515 @Override
516 public void onKeypress(final TKeypressEvent keypress) {
517 if (keypress.equals(kbRight)) {
518 int dotX = getXColorPosition(color);
519 int dotY = getYColorPosition(color);
520 if (dotX < 10) {
521 dotX += 3;
522 }
523 color = getColorFromPosition(dotX, dotY);
524 } else if (keypress.equals(kbLeft)) {
525 int dotX = getXColorPosition(color);
526 int dotY = getYColorPosition(color);
527 if (dotX > 3) {
528 dotX -= 3;
529 }
530 color = getColorFromPosition(dotX, dotY);
531 } else if (keypress.equals(kbUp)) {
532 int dotX = getXColorPosition(color);
533 int dotY = getYColorPosition(color);
534 if (dotY == 2) {
535 dotY--;
536 }
537 color = getColorFromPosition(dotX, dotY);
538 } else if (keypress.equals(kbDown)) {
539 int dotX = getXColorPosition(color);
540 int dotY = getYColorPosition(color);
541 if (dotY == 1) {
542 dotY++;
543 }
544 color = getColorFromPosition(dotX, dotY);
545 } else {
546 // Pass to my parent
547 super.onKeypress(keypress);
548 }
549
550 // Save this update to the local theme.
551 ((TEditColorThemeWindow) getWindow()).saveToEditTheme();
552 }
553
554 /**
555 * Handle mouse press events.
556 *
557 * @param mouse mouse button press event
558 */
559 @Override
560 public void onMouseDown(final TMouseEvent mouse) {
561 if (mouse.isMouseWheelUp()) {
562 // Do this like kbUp
563 int dotX = getXColorPosition(color);
564 int dotY = getYColorPosition(color);
565 if (dotY == 2) {
566 dotY--;
567 }
568 color = getColorFromPosition(dotX, dotY);
569 } else if (mouse.isMouseWheelDown()) {
570 // Do this like kbDown
571 int dotX = getXColorPosition(color);
572 int dotY = getYColorPosition(color);
573 if (dotY == 1) {
574 dotY++;
575 }
576 color = getColorFromPosition(dotX, dotY);
577 return;
578 } else if ((mouse.getX() > 0)
579 && (mouse.getX() < getWidth() - 1)
580 && (mouse.getY() > 0)
581 && (mouse.getY() < getHeight() - 1)
582 ) {
583 color = getColorFromPosition(mouse.getX(), mouse.getY());
584 } else {
585 // Let parent class handle it.
586 super.onMouseDown(mouse);
587 return;
588 }
589
590 // Save this update to the local theme.
591 ((TEditColorThemeWindow) getWindow()).saveToEditTheme();
592 }
593
594 }
595
596 /**
597 * The current editing theme.
598 */
599 private ColorTheme editTheme;
600
601 /**
602 * The left-side list of colors pane.
603 */
604 private TList colorNames;
605
606 /**
607 * The foreground color.
608 */
609 private ForegroundPicker foreground;
610
611 /**
612 * The background color.
613 */
614 private BackgroundPicker background;
615
616 /**
617 * Set various widgets/values to the editing theme color.
618 *
619 * @param colorName name of color from theme
620 */
621 private void refreshFromTheme(final String colorName) {
622 CellAttributes attr = editTheme.getColor(colorName);
623 foreground.color = attr.getForeColor();
624 foreground.bold = attr.isBold();
625 background.color = attr.getBackColor();
626 }
627
628 /**
629 * Examines foreground, background, and colorNames and sets the color in
630 * editTheme.
631 */
632 private void saveToEditTheme() {
633 String colorName = colorNames.getSelected();
634 if (colorName == null) {
635 return;
636 }
637 CellAttributes attr = editTheme.getColor(colorName);
638 attr.setForeColor(foreground.color);
639 attr.setBold(foreground.bold);
640 attr.setBackColor(background.color);
641 editTheme.setColor(colorName, attr);
642 }
643
644 /**
645 * Public constructor. The file open box will be centered on screen.
646 *
647 * @param application the TApplication that manages this window
648 */
649 public TEditColorThemeWindow(final TApplication application) {
650
651 // Register with the TApplication
652 super(application, "Colors", 0, 0, 60, 18, MODAL);
653
654 // Initialize with the first color
655 List<String> colors = getTheme().getColorNames();
656 assert (colors.size() > 0);
657 editTheme = new ColorTheme();
658 for (String key: colors) {
659 CellAttributes attr = new CellAttributes();
660 attr.setTo(getTheme().getColor(key));
661 editTheme.setColor(key, attr);
662 }
663
664 colorNames = addList(colors, 2, 2, 38, getHeight() - 7,
665 new TAction() {
666 // When the user presses Enter
667 public void DO() {
668 refreshFromTheme(colorNames.getSelected());
669 }
670 },
671 new TAction() {
672 // When the user navigates with keyboard
673 public void DO() {
674 refreshFromTheme(colorNames.getSelected());
675 }
676 }
677 );
678 foreground = new ForegroundPicker(this, 42, 1, 14, 6);
679 background = new BackgroundPicker(this, 42, 7, 14, 4);
680 refreshFromTheme(colors.get(0));
681 colorNames.setSelectedIndex(0);
682
683 addButton(" &OK ", getWidth() - 37, getHeight() - 4,
684 new TAction() {
685 public void DO() {
686 ColorTheme global = getTheme();
687 List<String> colors = editTheme.getColorNames();
688 for (String key: colors) {
689 CellAttributes attr = new CellAttributes();
690 attr.setTo(editTheme.getColor(key));
691 global.setColor(key, attr);
692 }
693 getApplication().closeWindow(TEditColorThemeWindow.this);
694 }
695 }
696 );
697
698 addButton("&Cancel", getWidth() - 25, getHeight() - 4,
699 new TAction() {
700 public void DO() {
701 getApplication().closeWindow(TEditColorThemeWindow.this);
702 }
703 }
704 );
705
706 // Default to the color list
707 activate(colorNames);
708
709 }
710
711 /**
712 * Draw me on screen.
713 */
714 @Override
715 public void draw() {
716 super.draw();
717 CellAttributes attr = new CellAttributes();
718
719 // Draw the label on colorNames
720 attr.setTo(getTheme().getColor("twindow.background.modal"));
721 if (colorNames.isActive()) {
722 attr.setForeColor(getTheme().getColor("tlabel").getForeColor());
723 attr.setBold(getTheme().getColor("tlabel").isBold());
724 }
725 getScreen().putStringXY(3, 2, "Color Name", attr);
726
727 // Draw the sample text box
728 attr.reset();
729 attr.setForeColor(foreground.color);
730 attr.setBold(foreground.bold);
731 attr.setBackColor(background.color);
732 getScreen().putStringXY(getWidth() - 17, getHeight() - 6,
733 "Text Text Text", attr);
734 getScreen().putStringXY(getWidth() - 17, getHeight() - 5,
735 "Text Text Text", attr);
736 }
737
738 /**
739 * Handle keystrokes.
740 *
741 * @param keypress keystroke event
742 */
743 @Override
744 public void onKeypress(final TKeypressEvent keypress) {
745 // Escape - behave like cancel
746 if (keypress.equals(kbEsc)) {
747 getApplication().closeWindow(this);
748 return;
749 }
750
751 // Pass to my parent
752 super.onKeypress(keypress);
753 }
754
755}