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