box drawing intersections
[fanfix.git] / src / jexer / TTableWindow.java
CommitLineData
1dac6b8d
KL
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 */
29package jexer;
30
759cb83e
KL
31import java.io.IOException;
32import java.text.MessageFormat;
1dac6b8d
KL
33import java.util.ResourceBundle;
34
35import jexer.event.TCommandEvent;
36import jexer.event.TKeypressEvent;
37import jexer.event.TMouseEvent;
38import jexer.event.TResizeEvent;
39import jexer.menu.TMenu;
40import static jexer.TCommand.*;
41import static jexer.TKeypress.*;
42
43/**
44 * TTableWindow is used to display and edit regular two-dimensional tables of
45 * cells.
46 */
47public class TTableWindow extends TScrollableWindow {
48
49 /**
50 * Translated strings.
51 */
52 private static final ResourceBundle i18n = ResourceBundle.getBundle(TTableWindow.class.getName());
53
54 // ------------------------------------------------------------------------
55 // Variables --------------------------------------------------------------
56 // ------------------------------------------------------------------------
57
58 /**
59 * The table widget.
60 */
61 private TTableWidget tableField;
62
63 // ------------------------------------------------------------------------
64 // Constructors -----------------------------------------------------------
65 // ------------------------------------------------------------------------
66
67 /**
68 * Public constructor sets window title.
69 *
70 * @param parent the main application
71 * @param title the window title
72 */
73 public TTableWindow(final TApplication parent, final String title) {
74
75 super(parent, title, 0, 0, parent.getScreen().getWidth() / 2,
76 parent.getScreen().getHeight() / 2 - 2, RESIZABLE | CENTERED);
77
78 tableField = new TTableWidget(this, 0, 0, getWidth() - 2, getHeight() - 2);
79 setupAfterTable();
80 }
81
82 // ------------------------------------------------------------------------
83 // Event handlers ---------------------------------------------------------
84 // ------------------------------------------------------------------------
85
86 /**
87 * Called by application.switchWindow() when this window gets the
88 * focus, and also by application.addWindow().
89 */
90 public void onFocus() {
91 // Enable the table menu items.
92 getApplication().enableMenuItem(TMenu.MID_CUT);
77961919
KL
93 getApplication().enableMenuItem(TMenu.MID_TABLE_VIEW_ROW_LABELS);
94 getApplication().enableMenuItem(TMenu.MID_TABLE_VIEW_COLUMN_LABELS);
95 getApplication().enableMenuItem(TMenu.MID_TABLE_VIEW_HIGHLIGHT_ROW);
96 getApplication().enableMenuItem(TMenu.MID_TABLE_VIEW_HIGHLIGHT_COLUMN);
1dac6b8d
KL
97 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_NONE);
98 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_ALL);
e9bb3c1e
KL
99 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_CELL_NONE);
100 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_CELL_ALL);
1dac6b8d
KL
101 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_RIGHT);
102 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_LEFT);
103 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_TOP);
104 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_BOTTOM);
105 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_DOUBLE_BOTTOM);
106 getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_THICK_BOTTOM);
107 getApplication().enableMenuItem(TMenu.MID_TABLE_DELETE_LEFT);
108 getApplication().enableMenuItem(TMenu.MID_TABLE_DELETE_UP);
109 getApplication().enableMenuItem(TMenu.MID_TABLE_DELETE_ROW);
110 getApplication().enableMenuItem(TMenu.MID_TABLE_DELETE_COLUMN);
111 getApplication().enableMenuItem(TMenu.MID_TABLE_INSERT_LEFT);
112 getApplication().enableMenuItem(TMenu.MID_TABLE_INSERT_RIGHT);
113 getApplication().enableMenuItem(TMenu.MID_TABLE_INSERT_ABOVE);
114 getApplication().enableMenuItem(TMenu.MID_TABLE_INSERT_BELOW);
115 getApplication().enableMenuItem(TMenu.MID_TABLE_COLUMN_NARROW);
116 getApplication().enableMenuItem(TMenu.MID_TABLE_COLUMN_WIDEN);
117 getApplication().enableMenuItem(TMenu.MID_TABLE_FILE_SAVE_CSV);
118 getApplication().enableMenuItem(TMenu.MID_TABLE_FILE_SAVE_TEXT);
119 }
120
121 /**
122 * Called by application.switchWindow() when another window gets the
123 * focus.
124 */
125 public void onUnfocus() {
126 // Disable the table menu items.
127 getApplication().disableMenuItem(TMenu.MID_CUT);
77961919
KL
128 getApplication().disableMenuItem(TMenu.MID_TABLE_VIEW_ROW_LABELS);
129 getApplication().disableMenuItem(TMenu.MID_TABLE_VIEW_COLUMN_LABELS);
130 getApplication().disableMenuItem(TMenu.MID_TABLE_VIEW_HIGHLIGHT_ROW);
131 getApplication().disableMenuItem(TMenu.MID_TABLE_VIEW_HIGHLIGHT_COLUMN);
1dac6b8d
KL
132 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_NONE);
133 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_ALL);
e9bb3c1e
KL
134 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_CELL_NONE);
135 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_CELL_ALL);
1dac6b8d
KL
136 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_RIGHT);
137 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_LEFT);
138 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_TOP);
139 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_BOTTOM);
140 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_DOUBLE_BOTTOM);
141 getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_THICK_BOTTOM);
142 getApplication().disableMenuItem(TMenu.MID_TABLE_DELETE_LEFT);
143 getApplication().disableMenuItem(TMenu.MID_TABLE_DELETE_UP);
144 getApplication().disableMenuItem(TMenu.MID_TABLE_DELETE_ROW);
145 getApplication().disableMenuItem(TMenu.MID_TABLE_DELETE_COLUMN);
146 getApplication().disableMenuItem(TMenu.MID_TABLE_INSERT_LEFT);
147 getApplication().disableMenuItem(TMenu.MID_TABLE_INSERT_RIGHT);
148 getApplication().disableMenuItem(TMenu.MID_TABLE_INSERT_ABOVE);
149 getApplication().disableMenuItem(TMenu.MID_TABLE_INSERT_BELOW);
150 getApplication().disableMenuItem(TMenu.MID_TABLE_COLUMN_NARROW);
151 getApplication().disableMenuItem(TMenu.MID_TABLE_COLUMN_WIDEN);
152 getApplication().disableMenuItem(TMenu.MID_TABLE_FILE_SAVE_CSV);
153 getApplication().disableMenuItem(TMenu.MID_TABLE_FILE_SAVE_TEXT);
154 }
155
156 // ------------------------------------------------------------------------
157 // TWindow ----------------------------------------------------------------
158 // ------------------------------------------------------------------------
159
1dac6b8d
KL
160 /**
161 * Handle mouse press events.
162 *
163 * @param mouse mouse button press event
164 */
165 @Override
166 public void onMouseDown(final TMouseEvent mouse) {
167 // Use TWidget's code to pass the event to the children.
168 super.onMouseDown(mouse);
169
170 if (mouseOnTable(mouse)) {
171 // The table might have changed, update the scollbars.
759cb83e
KL
172 setBottomValue(tableField.getRowCount() - 1);
173 setVerticalValue(tableField.getSelectedRowNumber());
174 setRightValue(tableField.getColumnCount() - 1);
175 setHorizontalValue(tableField.getSelectedColumnNumber());
1dac6b8d
KL
176 }
177 }
178
179 /**
180 * Handle mouse release events.
181 *
182 * @param mouse mouse button release event
183 */
184 @Override
185 public void onMouseUp(final TMouseEvent mouse) {
186 // Use TWidget's code to pass the event to the children.
187 super.onMouseUp(mouse);
188
189 if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
759cb83e
KL
190 // Clicked/dragged on vertical scrollbar.
191 tableField.setSelectedRowNumber(getVerticalValue());
192 }
193 if (mouse.isMouse1() && mouseOnHorizontalScroller(mouse)) {
194 // Clicked/dragged on horizontal scrollbar.
195 tableField.setSelectedColumnNumber(getHorizontalValue());
1dac6b8d 196 }
1dac6b8d
KL
197 }
198
199 /**
200 * Method that subclasses can override to handle mouse movements.
201 *
202 * @param mouse mouse motion event
203 */
204 @Override
205 public void onMouseMotion(final TMouseEvent mouse) {
206 // Use TWidget's code to pass the event to the children.
207 super.onMouseMotion(mouse);
208
209 if (mouseOnTable(mouse) && mouse.isMouse1()) {
759cb83e
KL
210 // The table might have changed, update the scollbars.
211 setBottomValue(tableField.getRowCount() - 1);
212 setVerticalValue(tableField.getSelectedRowNumber());
213 setRightValue(tableField.getColumnCount() - 1);
214 setHorizontalValue(tableField.getSelectedColumnNumber());
1dac6b8d
KL
215 } else {
216 if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
759cb83e
KL
217 // Clicked/dragged on vertical scrollbar.
218 tableField.setSelectedRowNumber(getVerticalValue());
219 }
220 if (mouse.isMouse1() && mouseOnHorizontalScroller(mouse)) {
221 // Clicked/dragged on horizontal scrollbar.
222 tableField.setSelectedColumnNumber(getHorizontalValue());
1dac6b8d 223 }
1dac6b8d
KL
224 }
225
226 }
227
228 /**
229 * Handle keystrokes.
230 *
231 * @param keypress keystroke event
232 */
233 @Override
234 public void onKeypress(final TKeypressEvent keypress) {
235 // Use TWidget's code to pass the event to the children.
236 super.onKeypress(keypress);
237
759cb83e
KL
238 // The table might have changed, update the scollbars.
239 setBottomValue(tableField.getRowCount() - 1);
240 setVerticalValue(tableField.getSelectedRowNumber());
241 setRightValue(tableField.getColumnCount() - 1);
242 setHorizontalValue(tableField.getSelectedColumnNumber());
1dac6b8d
KL
243 }
244
245 /**
246 * Handle window/screen resize events.
247 *
248 * @param event resize event
249 */
250 @Override
251 public void onResize(final TResizeEvent event) {
252 if (event.getType() == TResizeEvent.Type.WIDGET) {
253 // Resize the table
254 TResizeEvent tableSize = new TResizeEvent(TResizeEvent.Type.WIDGET,
255 event.getWidth() - 2, event.getHeight() - 2);
256 tableField.onResize(tableSize);
257
258 // Have TScrollableWindow handle the scrollbars
259 super.onResize(event);
260 return;
261 }
262
263 // Pass to children instead
264 for (TWidget widget: getChildren()) {
265 widget.onResize(event);
266 }
267 }
268
759cb83e
KL
269 /**
270 * Method that subclasses can override to handle posted command events.
271 *
272 * @param command command event
273 */
274 @Override
275 public void onCommand(final TCommandEvent command) {
276 if (command.equals(cmOpen)) {
277 try {
278 String filename = fileOpenBox(".");
279 if (filename != null) {
280 try {
281 // TODO
282 if (false) {
283 tableField.saveToFilename(filename);
284 }
285 } catch (IOException e) {
286 messageBox(i18n.getString("errorDialogTitle"),
287 MessageFormat.format(i18n.
288 getString("errorReadingFile"), e.getMessage()));
289 }
290 }
291 } catch (IOException e) {
292 messageBox(i18n.getString("errorDialogTitle"),
293 MessageFormat.format(i18n.
294 getString("errorOpeningFileDialog"), e.getMessage()));
295 }
296 return;
297 }
298
299 if (command.equals(cmSave)) {
300 try {
301 String filename = fileSaveBox(".");
302 if (filename != null) {
303 tableField.saveToFilename(filename);
304 }
305 } catch (IOException e) {
306 messageBox(i18n.getString("errorDialogTitle"),
307 MessageFormat.format(i18n.
308 getString("errorSavingFile"), e.getMessage()));
309 }
310 return;
311 }
312
313 // Didn't handle it, let children get it instead
314 super.onCommand(command);
315 }
316
1dac6b8d
KL
317 // ------------------------------------------------------------------------
318 // TTableWindow -----------------------------------------------------------
319 // ------------------------------------------------------------------------
320
321 /**
322 * Setup other fields after the table is created.
323 */
324 private void setupAfterTable() {
325 hScroller = new THScroller(this, 17, getHeight() - 2, getWidth() - 20);
326 vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
327 setMinimumWindowWidth(25);
328 setMinimumWindowHeight(10);
759cb83e
KL
329 setTopValue(tableField.getSelectedRowNumber());
330 setBottomValue(tableField.getRowCount() - 1);
331 setLeftValue(tableField.getSelectedColumnNumber());
332 setRightValue(tableField.getColumnCount() - 1);
1dac6b8d
KL
333
334 statusBar = newStatusBar(i18n.getString("statusBar"));
335 statusBar.addShortcutKeypress(kbF1, cmHelp,
336 i18n.getString("statusBarHelp"));
759cb83e 337
1dac6b8d
KL
338 statusBar.addShortcutKeypress(kbF2, cmSave,
339 i18n.getString("statusBarSave"));
340 statusBar.addShortcutKeypress(kbF3, cmOpen,
341 i18n.getString("statusBarOpen"));
1dac6b8d
KL
342 statusBar.addShortcutKeypress(kbF10, cmMenu,
343 i18n.getString("statusBarMenu"));
344 }
345
346 /**
347 * Check if a mouse press/release/motion event coordinate is over the
348 * table.
349 *
350 * @param mouse a mouse-based event
351 * @return whether or not the mouse is on the table
352 */
353 private boolean mouseOnTable(final TMouseEvent mouse) {
354 if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1)
355 && (mouse.getAbsoluteX() < getAbsoluteX() + getWidth() - 1)
356 && (mouse.getAbsoluteY() >= getAbsoluteY() + 1)
357 && (mouse.getAbsoluteY() < getAbsoluteY() + getHeight() - 1)
358 ) {
359 return true;
360 }
361 return false;
362 }
363
364}