Fix UTF8 bug, create first executable JAR file
[jvcard.git] / src / com / googlecode / lanterna / gui2 / AbstractBasePane.java
... / ...
CommitLineData
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 */
19package com.googlecode.lanterna.gui2;
20
21import com.googlecode.lanterna.TerminalPosition;
22import com.googlecode.lanterna.TerminalSize;
23import com.googlecode.lanterna.input.KeyStroke;
24import com.googlecode.lanterna.input.KeyType;
25import com.googlecode.lanterna.input.MouseAction;
26
27/**
28 * This abstract implementation of {@code BasePane} has the common code shared by all different concrete
29 * implementations.
30 */
31public abstract class AbstractBasePane implements BasePane {
32 protected final ContentHolder contentHolder;
33 protected InteractableLookupMap interactableLookupMap;
34 private Interactable focusedInteractable;
35 private boolean invalid;
36 private boolean strictFocusChange;
37 private boolean enableDirectionBasedMovements;
38
39 protected AbstractBasePane() {
40 this.contentHolder = new ContentHolder();
41 this.interactableLookupMap = new InteractableLookupMap(new TerminalSize(80, 25));
42 this.invalid = false;
43 this.strictFocusChange = false;
44 this.enableDirectionBasedMovements = true;
45 }
46
47 @Override
48 public boolean isInvalid() {
49 return invalid || contentHolder.isInvalid();
50 }
51
52 @Override
53 public void invalidate() {
54 invalid = true;
55
56 //Propagate
57 contentHolder.invalidate();
58 }
59
60 @Override
61 public void draw(TextGUIGraphics graphics) {
62 graphics.applyThemeStyle(graphics.getThemeDefinition(Window.class).getNormal());
63 graphics.fill(' ');
64 contentHolder.draw(graphics);
65
66 if(!interactableLookupMap.getSize().equals(graphics.getSize())) {
67 interactableLookupMap = new InteractableLookupMap(graphics.getSize());
68 } else {
69 interactableLookupMap.reset();
70 }
71 contentHolder.updateLookupMap(interactableLookupMap);
72 //interactableLookupMap.debug();
73 invalid = false;
74 }
75
76 @Override
77 public boolean handleInput(KeyStroke key) {
78 if(key.getKeyType() == KeyType.MouseEvent) {
79 MouseAction mouseAction = (MouseAction)key;
80 TerminalPosition localCoordinates = fromGlobal(mouseAction.getPosition());
81 Interactable interactable = interactableLookupMap.getInteractableAt(localCoordinates);
82 interactable.handleInput(key);
83 }
84 else if(focusedInteractable != null) {
85 Interactable next = null;
86 Interactable.FocusChangeDirection direction = Interactable.FocusChangeDirection.TELEPORT; //Default
87 Interactable.Result result = focusedInteractable.handleInput(key);
88 if(!enableDirectionBasedMovements) {
89 if(result == Interactable.Result.MOVE_FOCUS_DOWN || result == Interactable.Result.MOVE_FOCUS_RIGHT) {
90 result = Interactable.Result.MOVE_FOCUS_NEXT;
91 }
92 else if(result == Interactable.Result.MOVE_FOCUS_UP || result == Interactable.Result.MOVE_FOCUS_LEFT) {
93 result = Interactable.Result.MOVE_FOCUS_PREVIOUS;
94 }
95 }
96 switch (result) {
97 case HANDLED:
98 return true;
99 case UNHANDLED:
100 //Filter the event recursively through all parent containers until we hit null; give the containers
101 //a chance to absorb the event
102 Container parent = focusedInteractable.getParent();
103 while(parent != null) {
104 if(parent.handleInput(key)) {
105 return true;
106 }
107 parent = parent.getParent();
108 }
109 return false;
110 case MOVE_FOCUS_NEXT:
111 next = contentHolder.nextFocus(focusedInteractable);
112 if(next == null) {
113 next = contentHolder.nextFocus(null);
114 }
115 direction = Interactable.FocusChangeDirection.NEXT;
116 break;
117 case MOVE_FOCUS_PREVIOUS:
118 next = contentHolder.previousFocus(focusedInteractable);
119 if(next == null) {
120 next = contentHolder.previousFocus(null);
121 }
122 direction = Interactable.FocusChangeDirection.PREVIOUS;
123 break;
124 case MOVE_FOCUS_DOWN:
125 next = interactableLookupMap.findNextDown(focusedInteractable);
126 direction = Interactable.FocusChangeDirection.DOWN;
127 if(next == null && !strictFocusChange) {
128 next = contentHolder.nextFocus(focusedInteractable);
129 direction = Interactable.FocusChangeDirection.NEXT;
130 }
131 break;
132 case MOVE_FOCUS_LEFT:
133 next = interactableLookupMap.findNextLeft(focusedInteractable);
134 direction = Interactable.FocusChangeDirection.LEFT;
135 break;
136 case MOVE_FOCUS_RIGHT:
137 next = interactableLookupMap.findNextRight(focusedInteractable);
138 direction = Interactable.FocusChangeDirection.RIGHT;
139 break;
140 case MOVE_FOCUS_UP:
141 next = interactableLookupMap.findNextUp(focusedInteractable);
142 direction = Interactable.FocusChangeDirection.UP;
143 if(next == null && !strictFocusChange) {
144 next = contentHolder.previousFocus(focusedInteractable);
145 direction = Interactable.FocusChangeDirection.PREVIOUS;
146 }
147 break;
148 }
149 if(next != null) {
150 setFocusedInteractable(next, direction);
151 }
152 return true;
153 }
154 return false;
155 }
156
157 @Override
158 public Component getComponent() {
159 return contentHolder.getComponent();
160 }
161
162 @Override
163 public void setComponent(Component component) {
164 contentHolder.setComponent(component);
165 }
166
167 @Override
168 public Interactable getFocusedInteractable() {
169 return focusedInteractable;
170 }
171
172 @Override
173 public TerminalPosition getCursorPosition() {
174 if(focusedInteractable == null) {
175 return null;
176 }
177 TerminalPosition position = focusedInteractable.getCursorLocation();
178 if(position == null) {
179 return null;
180 }
181 //Don't allow the component to set the cursor outside of its own boundaries
182 if(position.getColumn() < 0 ||
183 position.getRow() < 0 ||
184 position.getColumn() >= focusedInteractable.getSize().getColumns() ||
185 position.getRow() >= focusedInteractable.getSize().getRows()) {
186 return null;
187 }
188 return focusedInteractable.toBasePane(position);
189 }
190
191 @Override
192 public void setFocusedInteractable(Interactable toFocus) {
193 setFocusedInteractable(toFocus,
194 toFocus != null ?
195 Interactable.FocusChangeDirection.TELEPORT : Interactable.FocusChangeDirection.RESET);
196 }
197
198 protected void setFocusedInteractable(Interactable toFocus, Interactable.FocusChangeDirection direction) {
199 if(focusedInteractable == toFocus) {
200 return;
201 }
202 if(focusedInteractable != null) {
203 focusedInteractable.onLeaveFocus(direction, focusedInteractable);
204 }
205 Interactable previous = focusedInteractable;
206 focusedInteractable = toFocus;
207 if(toFocus != null) {
208 toFocus.onEnterFocus(direction, previous);
209 }
210 invalidate();
211 }
212
213 @Override
214 public void setStrictFocusChange(boolean strictFocusChange) {
215 this.strictFocusChange = strictFocusChange;
216 }
217
218 @Override
219 public void setEnableDirectionBasedMovements(boolean enableDirectionBasedMovements) {
220 this.enableDirectionBasedMovements = enableDirectionBasedMovements;
221 }
222
223 protected class ContentHolder extends AbstractComposite<Container> {
224 @Override
225 public void setComponent(Component component) {
226 if(getComponent() == component) {
227 return;
228 }
229 setFocusedInteractable(null);
230 super.setComponent(component);
231 if(focusedInteractable == null && component instanceof Interactable) {
232 setFocusedInteractable((Interactable)component);
233 }
234 else if(focusedInteractable == null && component instanceof Container) {
235 setFocusedInteractable(((Container)component).nextFocus(null));
236 }
237 }
238
239 public boolean removeComponent(Component component) {
240 boolean removed = super.removeComponent(component);
241 if (removed) {
242 focusedInteractable = null;
243 }
244 return removed;
245 }
246
247 @Override
248 public TextGUI getTextGUI() {
249 return AbstractBasePane.this.getTextGUI();
250 }
251
252 @Override
253 protected ComponentRenderer<Container> createDefaultRenderer() {
254 return new ComponentRenderer<Container>() {
255 @Override
256 public TerminalSize getPreferredSize(Container component) {
257 Component subComponent = getComponent();
258 if(subComponent == null) {
259 return TerminalSize.ZERO;
260 }
261 return subComponent.getPreferredSize();
262 }
263
264 @Override
265 public void drawComponent(TextGUIGraphics graphics, Container component) {
266 Component subComponent = getComponent();
267 if(subComponent == null) {
268 return;
269 }
270 subComponent.draw(graphics);
271 }
272 };
273 }
274
275 @Override
276 public TerminalPosition toGlobal(TerminalPosition position) {
277 return AbstractBasePane.this.toGlobal(position);
278 }
279
280 @Override
281 public TerminalPosition toBasePane(TerminalPosition position) {
282 return position;
283 }
284
285 @Override
286 public BasePane getBasePane() {
287 return AbstractBasePane.this;
288 }
289 }
290}