Add 'src/jexer/' from commit 'cf01c92f5809a0732409e280fb0f32f27393618d'
[fanfix.git] / src / jexer / menu / TMenuItem.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2019 Kevin Lamonte
7 *
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:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
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.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29 package jexer.menu;
30
31 import jexer.TKeypress;
32 import jexer.TWidget;
33 import jexer.bits.CellAttributes;
34 import jexer.bits.GraphicsChars;
35 import jexer.bits.MnemonicString;
36 import jexer.bits.StringUtils;
37 import jexer.event.TKeypressEvent;
38 import jexer.event.TMouseEvent;
39 import jexer.event.TMenuEvent;
40 import static jexer.TKeypress.*;
41
42 /**
43 * TMenuItem implements a menu item.
44 */
45 public class TMenuItem extends TWidget {
46
47 // ------------------------------------------------------------------------
48 // Variables --------------------------------------------------------------
49 // ------------------------------------------------------------------------
50
51 /**
52 * Label for this menu item.
53 */
54 private String label;
55
56 /**
57 * Menu ID. IDs less than 1024 are reserved for common system
58 * functions. Existing ones are defined in TMenu, i.e. TMenu.MID_EXIT.
59 */
60 private int id = TMenu.MID_UNUSED;
61
62 /**
63 * When true, this item can be checked or unchecked.
64 */
65 private boolean checkable = false;
66
67 /**
68 * When true, this item is checked.
69 */
70 private boolean checked = false;
71
72 /**
73 * Global shortcut key.
74 */
75 private TKeypress key;
76
77 /**
78 * The title string. Use '&' to specify a mnemonic, i.e. "&File" will
79 * highlight the 'F' and allow 'f' or 'F' to select it.
80 */
81 private MnemonicString mnemonic;
82
83 // ------------------------------------------------------------------------
84 // Constructors -----------------------------------------------------------
85 // ------------------------------------------------------------------------
86
87 /**
88 * Package private constructor.
89 *
90 * @param parent parent widget
91 * @param id menu id
92 * @param x column relative to parent
93 * @param y row relative to parent
94 * @param label menu item title
95 */
96 TMenuItem(final TMenu parent, final int id, final int x, final int y,
97 final String label) {
98
99 // Set parent and window
100 super(parent);
101
102 mnemonic = new MnemonicString(label);
103
104 setX(x);
105 setY(y);
106 setHeight(1);
107 this.label = mnemonic.getRawLabel();
108 setWidth(StringUtils.width(label) + 4);
109 this.id = id;
110
111 // Default state for some known menu items
112 switch (id) {
113
114 case TMenu.MID_CUT:
115 setEnabled(false);
116 break;
117 case TMenu.MID_COPY:
118 setEnabled(false);
119 break;
120 case TMenu.MID_PASTE:
121 setEnabled(false);
122 break;
123 case TMenu.MID_CLEAR:
124 setEnabled(false);
125 break;
126
127 case TMenu.MID_TILE:
128 break;
129 case TMenu.MID_CASCADE:
130 break;
131 case TMenu.MID_CLOSE_ALL:
132 break;
133 case TMenu.MID_WINDOW_MOVE:
134 break;
135 case TMenu.MID_WINDOW_ZOOM:
136 break;
137 case TMenu.MID_WINDOW_NEXT:
138 break;
139 case TMenu.MID_WINDOW_PREVIOUS:
140 break;
141 case TMenu.MID_WINDOW_CLOSE:
142 break;
143 default:
144 break;
145 }
146
147 }
148
149 // ------------------------------------------------------------------------
150 // Event handlers ---------------------------------------------------------
151 // ------------------------------------------------------------------------
152
153 /**
154 * Returns true if the mouse is currently on the menu item.
155 *
156 * @param mouse mouse event
157 * @return if true then the mouse is currently on this item
158 */
159 private boolean mouseOnMenuItem(final TMouseEvent mouse) {
160 if ((mouse.getY() == 0)
161 && (mouse.getX() >= 0)
162 && (mouse.getX() < getWidth())
163 ) {
164 return true;
165 }
166 return false;
167 }
168
169 /**
170 * Handle mouse button releases.
171 *
172 * @param mouse mouse button release event
173 */
174 @Override
175 public void onMouseUp(final TMouseEvent mouse) {
176 if ((mouseOnMenuItem(mouse)) && (mouse.isMouse1())) {
177 dispatch();
178 return;
179 }
180 }
181
182 /**
183 * Handle keystrokes.
184 *
185 * @param keypress keystroke event
186 */
187 @Override
188 public void onKeypress(final TKeypressEvent keypress) {
189 if (keypress.equals(kbEnter)) {
190 dispatch();
191 return;
192 }
193
194 // Pass to parent for the things we don't care about.
195 super.onKeypress(keypress);
196 }
197
198 // ------------------------------------------------------------------------
199 // TWidget ----------------------------------------------------------------
200 // ------------------------------------------------------------------------
201
202 /**
203 * Draw a menu item with label.
204 */
205 @Override
206 public void draw() {
207 CellAttributes background = getTheme().getColor("tmenu");
208 CellAttributes menuColor;
209 CellAttributes menuMnemonicColor;
210 if (isAbsoluteActive()) {
211 menuColor = getTheme().getColor("tmenu.highlighted");
212 menuMnemonicColor = getTheme().getColor("tmenu.mnemonic.highlighted");
213 } else {
214 if (isEnabled()) {
215 menuColor = getTheme().getColor("tmenu");
216 menuMnemonicColor = getTheme().getColor("tmenu.mnemonic");
217 } else {
218 menuColor = getTheme().getColor("tmenu.disabled");
219 menuMnemonicColor = getTheme().getColor("tmenu.disabled");
220 }
221 }
222
223 char cVSide = GraphicsChars.WINDOW_SIDE;
224 vLineXY(0, 0, 1, cVSide, background);
225 vLineXY(getWidth() - 1, 0, 1, cVSide, background);
226
227 hLineXY(1, 0, getWidth() - 2, ' ', menuColor);
228 putStringXY(2, 0, mnemonic.getRawLabel(), menuColor);
229 if (key != null) {
230 String keyLabel = key.toString();
231 putStringXY((getWidth() - StringUtils.width(keyLabel) - 2), 0,
232 keyLabel, menuColor);
233 }
234 if (mnemonic.getScreenShortcutIdx() >= 0) {
235 putCharXY(2 + mnemonic.getScreenShortcutIdx(), 0,
236 mnemonic.getShortcut(), menuMnemonicColor);
237 }
238 if (checked) {
239 assert (checkable);
240 putCharXY(1, 0, GraphicsChars.CHECK, menuColor);
241 }
242
243 }
244
245 // ------------------------------------------------------------------------
246 // TMenuItem --------------------------------------------------------------
247 // ------------------------------------------------------------------------
248
249 /**
250 * Get the menu item ID.
251 *
252 * @return the id
253 */
254 public final int getId() {
255 return id;
256 }
257
258 /**
259 * Set checkable flag.
260 *
261 * @param checkable if true, this menu item can be checked/unchecked
262 */
263 public final void setCheckable(final boolean checkable) {
264 this.checkable = checkable;
265 }
266
267 /**
268 * Get checkable flag.
269 *
270 * @return true if this menu item is both checkable and checked
271 */
272 public final boolean getChecked() {
273 return ((checkable == true) && (checked == true));
274 }
275
276 /**
277 * Set checked flag. Note that setting checked on an item checkable will
278 * do nothing.
279 *
280 * @param checked if true, and if this menu item is checkable, then
281 * getChecked() will return true
282 */
283 public final void setChecked(final boolean checked) {
284 if (checkable) {
285 this.checked = checked;
286 } else {
287 this.checked = false;
288 }
289 }
290
291 /**
292 * Get the mnemonic string for this menu item.
293 *
294 * @return mnemonic string
295 */
296 public final MnemonicString getMnemonic() {
297 return mnemonic;
298 }
299
300 /**
301 * Get a global accelerator key for this menu item.
302 *
303 * @return global keyboard accelerator, or null if no key is associated
304 * with this item
305 */
306 public final TKeypress getKey() {
307 return key;
308 }
309
310 /**
311 * Set a global accelerator key for this menu item.
312 *
313 * @param key global keyboard accelerator
314 */
315 public final void setKey(final TKeypress key) {
316 this.key = key;
317
318 if (key != null) {
319 int newWidth = (StringUtils.width(label) + 4 +
320 StringUtils.width(key.toString()) + 2);
321 if (newWidth > getWidth()) {
322 setWidth(newWidth);
323 }
324 }
325 }
326
327 /**
328 * Dispatch event(s) due to selection or click.
329 */
330 public void dispatch() {
331 assert (isEnabled());
332
333 getApplication().postMenuEvent(new TMenuEvent(id));
334 if (checkable) {
335 checked = !checked;
336 }
337 }
338
339 }