Change build scripts
[jvcard.git] / src / com / googlecode / lanterna / gui2 / CheckBox.java
1 /*
2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
3 *
4 * lanterna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Copyright (C) 2010-2015 Martin
18 */
19 package com.googlecode.lanterna.gui2;
20
21 import com.googlecode.lanterna.TerminalTextUtils;
22 import com.googlecode.lanterna.TerminalPosition;
23 import com.googlecode.lanterna.TerminalSize;
24 import com.googlecode.lanterna.graphics.ThemeDefinition;
25 import com.googlecode.lanterna.input.KeyStroke;
26 import com.googlecode.lanterna.input.KeyType;
27
28 import java.util.List;
29 import java.util.concurrent.CopyOnWriteArrayList;
30
31 /**
32 * The checkbox component looks like a regular checkbox that you can find in modern graphics user interfaces, a label
33 * and a space that the user can toggle on and off by using enter or space keys.
34 *
35 * @author Martin
36 */
37 public class CheckBox extends AbstractInteractableComponent<CheckBox> {
38
39 /**
40 * Listener interface that can be used to catch user events on the check box
41 */
42 public interface Listener {
43 /**
44 * This is fired when the user has altered the checked state of this {@code CheckBox}
45 * @param checked If the {@code CheckBox} is now toggled on, this is set to {@code true}, otherwise
46 * {@code false}
47 */
48 void onStatusChanged(boolean checked);
49 }
50
51 private final List<Listener> listeners;
52 private String label;
53 private boolean checked;
54
55 /**
56 * Creates a new checkbox with no label, initially set to un-checked
57 */
58 public CheckBox() {
59 this("");
60 }
61
62 /**
63 * Creates a new checkbox with a specific label, initially set to un-checked
64 * @param label Label to assign to the check box
65 */
66 public CheckBox(String label) {
67 if(label == null) {
68 throw new IllegalArgumentException("Cannot create a CheckBox with null label");
69 }
70 else if(label.contains("\n") || label.contains("\r")) {
71 throw new IllegalArgumentException("Multiline checkbox labels are not supported");
72 }
73 this.listeners = new CopyOnWriteArrayList<Listener>();
74 this.label = label;
75 this.checked = false;
76 }
77
78 /**
79 * Programmatically updated the check box to a particular checked state
80 * @param checked If {@code true}, the check box will be set to toggled on, otherwise {@code false}
81 * @return Itself
82 */
83 public synchronized CheckBox setChecked(final boolean checked) {
84 this.checked = checked;
85 runOnGUIThreadIfExistsOtherwiseRunDirect(new Runnable() {
86 @Override
87 public void run() {
88 for(Listener listener : listeners) {
89 listener.onStatusChanged(checked);
90 }
91 }
92 });
93 invalidate();
94 return this;
95 }
96
97 /**
98 * Returns the checked state of this check box
99 * @return {@code true} if the check box is toggled on, otherwise {@code false}
100 */
101 public boolean isChecked() {
102 return checked;
103 }
104
105 @Override
106 public Result handleKeyStroke(KeyStroke keyStroke) {
107 if((keyStroke.getKeyType() == KeyType.Character && keyStroke.getCharacter() == ' ') ||
108 keyStroke.getKeyType() == KeyType.Enter) {
109 setChecked(!isChecked());
110 return Result.HANDLED;
111 }
112 return super.handleKeyStroke(keyStroke);
113 }
114
115 /**
116 * Updates the label of the checkbox
117 * @param label New label to assign to the check box
118 * @return Itself
119 */
120 public synchronized CheckBox setLabel(String label) {
121 if(label == null) {
122 throw new IllegalArgumentException("Cannot set CheckBox label to null");
123 }
124 this.label = label;
125 invalidate();
126 return this;
127 }
128
129 /**
130 * Returns the label of check box
131 * @return Label currently assigned to the check box
132 */
133 public String getLabel() {
134 return label;
135 }
136
137 /**
138 * Adds a listener to this check box so that it will be notificed on certain user actions
139 * @param listener Listener to fire events on
140 * @return Itself
141 */
142 public CheckBox addListener(Listener listener) {
143 if(listener != null && !listeners.contains(listener)) {
144 listeners.add(listener);
145 }
146 return this;
147 }
148
149 /**
150 * Removes a listener from this check box so that, if it was previously added, it will no long receive any events
151 * @param listener Listener to remove from the check box
152 * @return Itself
153 */
154 public CheckBox removeListener(Listener listener) {
155 listeners.remove(listener);
156 return this;
157 }
158
159 @Override
160 protected CheckBoxRenderer createDefaultRenderer() {
161 return new DefaultCheckBoxRenderer();
162 }
163
164 /**
165 * Helper interface that doesn't add any new methods but makes coding new check box renderers a little bit more clear
166 */
167 public static abstract class CheckBoxRenderer implements InteractableRenderer<CheckBox> {
168 }
169
170 /**
171 * The default renderer that is used unless overridden. This renderer will draw the checkbox label on the right side
172 * of a "[ ]" block which will contain a "X" inside it if the check box has toggle status on
173 */
174 public static class DefaultCheckBoxRenderer extends CheckBoxRenderer {
175 private static final TerminalPosition CURSOR_LOCATION = new TerminalPosition(1, 0);
176 @Override
177 public TerminalPosition getCursorLocation(CheckBox component) {
178 return CURSOR_LOCATION;
179 }
180
181 @Override
182 public TerminalSize getPreferredSize(CheckBox component) {
183 int width = 3;
184 if(!component.label.isEmpty()) {
185 width += 1 + TerminalTextUtils.getColumnWidth(component.label);
186 }
187 return new TerminalSize(width, 1);
188 }
189
190 @Override
191 public void drawComponent(TextGUIGraphics graphics, CheckBox component) {
192 ThemeDefinition themeDefinition = graphics.getThemeDefinition(CheckBox.class);
193 if(component.isFocused()) {
194 graphics.applyThemeStyle(themeDefinition.getActive());
195 }
196 else {
197 graphics.applyThemeStyle(themeDefinition.getNormal());
198 }
199
200 graphics.fill(' ');
201 graphics.putString(4, 0, component.label);
202
203 String head = "[" + (component.isChecked() ? themeDefinition.getCharacter("MARKER", 'x') : " ") + "]";
204 if(component.isFocused()) {
205 graphics.applyThemeStyle(themeDefinition.getPreLight());
206 }
207 else {
208 graphics.applyThemeStyle(themeDefinition.getNormal());
209 }
210 graphics.putString(0, 0, head);
211 }
212 }
213 }