limit images in scrollback
[fanfix.git] / src / jexer / TStatusBar.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;
30
31 import java.util.ArrayList;
32 import java.util.List;
33
34 import jexer.bits.CellAttributes;
35 import jexer.bits.GraphicsChars;
36 import jexer.bits.StringUtils;
37 import jexer.event.TCommandEvent;
38 import jexer.event.TKeypressEvent;
39 import jexer.event.TMouseEvent;
40
41 /**
42 * TStatusBar implements a status line with clickable buttons.
43 */
44 public class TStatusBar extends TWidget {
45
46 // ------------------------------------------------------------------------
47 // Variables --------------------------------------------------------------
48 // ------------------------------------------------------------------------
49
50 /**
51 * Remember mouse state.
52 */
53 private TMouseEvent mouse;
54
55 /**
56 * The text to display on the right side of the shortcut keys.
57 */
58 private String text = null;
59
60 /**
61 * The shortcut keys.
62 */
63 private List<TStatusBarKey> keys = new ArrayList<TStatusBarKey>();
64
65 /**
66 * A single shortcut key.
67 */
68 private class TStatusBarKey {
69
70 /**
71 * The keypress for this action.
72 */
73 public TKeypress key;
74
75 /**
76 * The command to issue.
77 */
78 public TCommand cmd;
79
80 /**
81 * The label text.
82 */
83 public String label;
84
85 /**
86 * If true, the mouse is on this key.
87 */
88 public boolean selected;
89
90 /**
91 * The left edge coordinate to draw this key with.
92 */
93 public int x = 0;
94
95 /**
96 * The width of this key on the screen.
97 *
98 * @return the number of columns this takes when drawn
99 */
100 public int width() {
101 return StringUtils.width(this.label) +
102 StringUtils.width(this.key.toString()) + 3;
103 }
104
105 /**
106 * Add a key to this status bar.
107 *
108 * @param key the key to trigger on
109 * @param cmd the command event to issue when key is pressed or this
110 * item is clicked
111 * @param label the label for this action
112 */
113 public TStatusBarKey(final TKeypress key, final TCommand cmd,
114 final String label) {
115
116 this.key = key;
117 this.cmd = cmd;
118 this.label = label;
119 }
120
121 }
122
123 // ------------------------------------------------------------------------
124 // Constructors -----------------------------------------------------------
125 // ------------------------------------------------------------------------
126
127 /**
128 * Public constructor.
129 *
130 * @param window the window associated with this status bar
131 * @param text text for the bar on the bottom row
132 */
133 public TStatusBar(final TWindow window, final String text) {
134
135 // TStatusBar is a parentless widget, because TApplication handles
136 // its drawing and event routing directly.
137 super(null, false, 0, 0, StringUtils.width(text), 1);
138
139 this.text = text;
140 setWindow(window);
141 }
142
143 /**
144 * Public constructor.
145 *
146 * @param window the window associated with this status bar
147 */
148 public TStatusBar(final TWindow window) {
149 this(window, "");
150 }
151
152 // ------------------------------------------------------------------------
153 // Event handlers ---------------------------------------------------------
154 // ------------------------------------------------------------------------
155
156 /**
157 * Handle keypresses.
158 *
159 * @param keypress keystroke event
160 * @return true if this keypress was consumed
161 */
162 public boolean statusBarKeypress(final TKeypressEvent keypress) {
163 for (TStatusBarKey key: keys) {
164 if (keypress.equals(key.key)) {
165 getApplication().postMenuEvent(new TCommandEvent(key.cmd));
166 return true;
167 }
168 }
169 return false;
170 }
171
172 /**
173 * Returns true if the mouse is currently on the button.
174 *
175 * @param statusBarKey the status bar item
176 * @return if true the mouse is currently on the button
177 */
178 private boolean mouseOnShortcut(final TStatusBarKey statusBarKey) {
179 if ((mouse != null)
180 && (mouse.getAbsoluteY() == getApplication().getDesktopBottom())
181 && (mouse.getAbsoluteX() >= statusBarKey.x)
182 && (mouse.getAbsoluteX() < statusBarKey.x + statusBarKey.width())
183 ) {
184 return true;
185 }
186 return false;
187 }
188
189 /**
190 * Handle mouse button presses.
191 *
192 * @param mouse mouse button event
193 * @return true if this mouse event was consumed
194 */
195 public boolean statusBarMouseDown(final TMouseEvent mouse) {
196 this.mouse = mouse;
197
198 for (TStatusBarKey key: keys) {
199 if ((mouseOnShortcut(key)) && (mouse.isMouse1())) {
200 key.selected = true;
201 return true;
202 }
203 }
204 return false;
205 }
206
207 /**
208 * Handle mouse button releases.
209 *
210 * @param mouse mouse button release event
211 * @return true if this mouse event was consumed
212 */
213 public boolean statusBarMouseUp(final TMouseEvent mouse) {
214 this.mouse = mouse;
215
216 for (TStatusBarKey key: keys) {
217 if (key.selected && mouse.isMouse1()) {
218 key.selected = false;
219
220 // Dispatch the event
221 getApplication().postMenuEvent(new TCommandEvent(key.cmd));
222 return true;
223 }
224 }
225 return false;
226 }
227
228 /**
229 * Handle mouse movements.
230 *
231 * @param mouse mouse motion event
232 */
233 public void statusBarMouseMotion(final TMouseEvent mouse) {
234 this.mouse = mouse;
235
236 for (TStatusBarKey key: keys) {
237 if (!mouseOnShortcut(key)) {
238 key.selected = false;
239 }
240 }
241 }
242
243 // ------------------------------------------------------------------------
244 // TWidget ----------------------------------------------------------------
245 // ------------------------------------------------------------------------
246
247 /**
248 * Draw the bar.
249 */
250 @Override
251 public void draw() {
252 CellAttributes barColor = new CellAttributes();
253 barColor.setTo(getTheme().getColor("tstatusbar.text"));
254 CellAttributes keyColor = new CellAttributes();
255 keyColor.setTo(getTheme().getColor("tstatusbar.button"));
256 CellAttributes selectedColor = new CellAttributes();
257 selectedColor.setTo(getTheme().getColor("tstatusbar.selected"));
258
259 // Status bar is weird. Its draw() method is called directly by
260 // TApplication after everything is drawn, and after
261 // Screen.resetClipping(). So at this point we are drawing in
262 // absolute coordinates, not relative to our TWindow.
263 int row = getScreen().getHeight() - 1;
264 int width = getScreen().getWidth();
265
266 hLineXY(0, row, width, ' ', barColor);
267
268 int col = 0;
269 for (TStatusBarKey key: keys) {
270 String keyStr = key.key.toString();
271 if (key.selected) {
272 putCharXY(col++, row, ' ', selectedColor);
273 putStringXY(col, row, keyStr, selectedColor);
274 col += StringUtils.width(keyStr);
275 putCharXY(col++, row, ' ', selectedColor);
276 putStringXY(col, row, key.label, selectedColor);
277 col += StringUtils.width(key.label);
278 putCharXY(col++, row, ' ', selectedColor);
279 } else {
280 putCharXY(col++, row, ' ', barColor);
281 putStringXY(col, row, keyStr, keyColor);
282 col += StringUtils.width(keyStr) + 1;
283 putStringXY(col, row, key.label, barColor);
284 col += StringUtils.width(key.label);
285 putCharXY(col++, row, ' ', barColor);
286 }
287 }
288 if (text.length() > 0) {
289 if (keys.size() > 0) {
290 putCharXY(col++, row, GraphicsChars.VERTICAL_BAR, barColor);
291 }
292 putCharXY(col++, row, ' ', barColor);
293 putStringXY(col, row, text, barColor);
294 }
295 }
296
297 // ------------------------------------------------------------------------
298 // TStatusBar -------------------------------------------------------------
299 // ------------------------------------------------------------------------
300
301 /**
302 * Add a key to this status bar.
303 *
304 * @param key the key to trigger on
305 * @param cmd the command event to issue when key is pressed or this item
306 * is clicked
307 * @param label the label for this action
308 */
309 public void addShortcutKeypress(final TKeypress key, final TCommand cmd,
310 final String label) {
311
312 TStatusBarKey newKey = new TStatusBarKey(key, cmd, label);
313 if (keys.size() > 0) {
314 TStatusBarKey oldKey = keys.get(keys.size() - 1);
315 newKey.x = oldKey.x + oldKey.width();
316 }
317 keys.add(newKey);
318 }
319
320 /**
321 * Set the text to display on the right side of the shortcut keys.
322 *
323 * @param text the new text
324 */
325 public void setText(final String text) {
326 this.text = text;
327 }
328
329 }