2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
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:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
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.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.io
.IOException
;
32 import java
.text
.MessageFormat
;
33 import java
.util
.ResourceBundle
;
35 import jexer
.event
.TCommandEvent
;
36 import jexer
.event
.TKeypressEvent
;
37 import jexer
.event
.TMenuEvent
;
38 import jexer
.event
.TMouseEvent
;
39 import jexer
.event
.TResizeEvent
;
40 import jexer
.menu
.TMenu
;
41 import jexer
.menu
.TMenuItem
;
42 import static jexer
.TCommand
.*;
43 import static jexer
.TKeypress
.*;
46 * TTableWindow is used to display and edit regular two-dimensional tables of
49 public class TTableWindow
extends TScrollableWindow
{
54 private static final ResourceBundle i18n
= ResourceBundle
.getBundle(TTableWindow
.class.getName());
56 // ------------------------------------------------------------------------
57 // Variables --------------------------------------------------------------
58 // ------------------------------------------------------------------------
63 private TTableWidget tableField
;
65 // ------------------------------------------------------------------------
66 // Constructors -----------------------------------------------------------
67 // ------------------------------------------------------------------------
70 * Public constructor sets window title.
72 * @param parent the main application
73 * @param title the window title
75 public TTableWindow(final TApplication parent
, final String title
) {
77 super(parent
, title
, 0, 0, parent
.getScreen().getWidth() / 2,
78 parent
.getScreen().getHeight() / 2 - 2, RESIZABLE
| CENTERED
);
80 tableField
= addTable(0, 0, getWidth() - 2, getHeight() - 2);
84 // ------------------------------------------------------------------------
85 // Event handlers ---------------------------------------------------------
86 // ------------------------------------------------------------------------
89 * Called by application.switchWindow() when this window gets the
90 * focus, and also by application.addWindow().
92 public void onFocus() {
93 // Enable the table menu items.
94 getApplication().enableMenuItem(TMenu
.MID_CUT
);
95 getApplication().enableMenuItem(TMenu
.MID_TABLE_RENAME_COLUMN
);
96 getApplication().enableMenuItem(TMenu
.MID_TABLE_RENAME_ROW
);
97 getApplication().enableMenuItem(TMenu
.MID_TABLE_VIEW_ROW_LABELS
);
98 getApplication().enableMenuItem(TMenu
.MID_TABLE_VIEW_COLUMN_LABELS
);
99 getApplication().enableMenuItem(TMenu
.MID_TABLE_VIEW_HIGHLIGHT_ROW
);
100 getApplication().enableMenuItem(TMenu
.MID_TABLE_VIEW_HIGHLIGHT_COLUMN
);
101 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_NONE
);
102 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_ALL
);
103 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_CELL_NONE
);
104 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_CELL_ALL
);
105 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_RIGHT
);
106 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_LEFT
);
107 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_TOP
);
108 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_BOTTOM
);
109 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_DOUBLE_BOTTOM
);
110 getApplication().enableMenuItem(TMenu
.MID_TABLE_BORDER_THICK_BOTTOM
);
111 getApplication().enableMenuItem(TMenu
.MID_TABLE_DELETE_LEFT
);
112 getApplication().enableMenuItem(TMenu
.MID_TABLE_DELETE_UP
);
113 getApplication().enableMenuItem(TMenu
.MID_TABLE_DELETE_ROW
);
114 getApplication().enableMenuItem(TMenu
.MID_TABLE_DELETE_COLUMN
);
115 getApplication().enableMenuItem(TMenu
.MID_TABLE_INSERT_LEFT
);
116 getApplication().enableMenuItem(TMenu
.MID_TABLE_INSERT_RIGHT
);
117 getApplication().enableMenuItem(TMenu
.MID_TABLE_INSERT_ABOVE
);
118 getApplication().enableMenuItem(TMenu
.MID_TABLE_INSERT_BELOW
);
119 getApplication().enableMenuItem(TMenu
.MID_TABLE_COLUMN_NARROW
);
120 getApplication().enableMenuItem(TMenu
.MID_TABLE_COLUMN_WIDEN
);
121 getApplication().enableMenuItem(TMenu
.MID_TABLE_FILE_OPEN_CSV
);
122 getApplication().enableMenuItem(TMenu
.MID_TABLE_FILE_SAVE_CSV
);
123 getApplication().enableMenuItem(TMenu
.MID_TABLE_FILE_SAVE_TEXT
);
125 if (tableField
!= null) {
127 // Set the menu to match the flags.
128 TMenuItem menuItem
= getApplication().getMenuItem(TMenu
.MID_TABLE_VIEW_ROW_LABELS
);
129 if (menuItem
!= null) {
130 menuItem
.setChecked(tableField
.getShowRowLabels());
132 menuItem
= getApplication().getMenuItem(TMenu
.MID_TABLE_VIEW_COLUMN_LABELS
);
133 if (menuItem
!= null) {
134 menuItem
.setChecked(tableField
.getShowColumnLabels());
136 menuItem
= getApplication().getMenuItem(TMenu
.MID_TABLE_VIEW_HIGHLIGHT_ROW
);
137 if (menuItem
!= null) {
138 menuItem
.setChecked(tableField
.getHighlightRow());
140 menuItem
= getApplication().getMenuItem(TMenu
.MID_TABLE_VIEW_HIGHLIGHT_COLUMN
);
141 if (menuItem
!= null) {
142 menuItem
.setChecked(tableField
.getHighlightColumn());
148 * Called by application.switchWindow() when another window gets the
151 public void onUnfocus() {
152 // Disable the table menu items.
153 getApplication().disableMenuItem(TMenu
.MID_CUT
);
154 getApplication().disableMenuItem(TMenu
.MID_TABLE_RENAME_COLUMN
);
155 getApplication().disableMenuItem(TMenu
.MID_TABLE_RENAME_ROW
);
156 getApplication().disableMenuItem(TMenu
.MID_TABLE_VIEW_ROW_LABELS
);
157 getApplication().disableMenuItem(TMenu
.MID_TABLE_VIEW_COLUMN_LABELS
);
158 getApplication().disableMenuItem(TMenu
.MID_TABLE_VIEW_HIGHLIGHT_ROW
);
159 getApplication().disableMenuItem(TMenu
.MID_TABLE_VIEW_HIGHLIGHT_COLUMN
);
160 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_NONE
);
161 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_ALL
);
162 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_CELL_NONE
);
163 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_CELL_ALL
);
164 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_RIGHT
);
165 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_LEFT
);
166 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_TOP
);
167 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_BOTTOM
);
168 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_DOUBLE_BOTTOM
);
169 getApplication().disableMenuItem(TMenu
.MID_TABLE_BORDER_THICK_BOTTOM
);
170 getApplication().disableMenuItem(TMenu
.MID_TABLE_DELETE_LEFT
);
171 getApplication().disableMenuItem(TMenu
.MID_TABLE_DELETE_UP
);
172 getApplication().disableMenuItem(TMenu
.MID_TABLE_DELETE_ROW
);
173 getApplication().disableMenuItem(TMenu
.MID_TABLE_DELETE_COLUMN
);
174 getApplication().disableMenuItem(TMenu
.MID_TABLE_INSERT_LEFT
);
175 getApplication().disableMenuItem(TMenu
.MID_TABLE_INSERT_RIGHT
);
176 getApplication().disableMenuItem(TMenu
.MID_TABLE_INSERT_ABOVE
);
177 getApplication().disableMenuItem(TMenu
.MID_TABLE_INSERT_BELOW
);
178 getApplication().disableMenuItem(TMenu
.MID_TABLE_COLUMN_NARROW
);
179 getApplication().disableMenuItem(TMenu
.MID_TABLE_COLUMN_WIDEN
);
180 getApplication().disableMenuItem(TMenu
.MID_TABLE_FILE_OPEN_CSV
);
181 getApplication().disableMenuItem(TMenu
.MID_TABLE_FILE_SAVE_CSV
);
182 getApplication().disableMenuItem(TMenu
.MID_TABLE_FILE_SAVE_TEXT
);
185 // ------------------------------------------------------------------------
186 // TWindow ----------------------------------------------------------------
187 // ------------------------------------------------------------------------
190 * Handle mouse press events.
192 * @param mouse mouse button press event
195 public void onMouseDown(final TMouseEvent mouse
) {
196 // Use TWidget's code to pass the event to the children.
197 super.onMouseDown(mouse
);
199 if (mouseOnTable(mouse
)) {
200 // The table might have changed, update the scollbars.
201 setBottomValue(tableField
.getRowCount() - 1);
202 setVerticalValue(tableField
.getSelectedRowNumber());
203 setRightValue(tableField
.getColumnCount() - 1);
204 setHorizontalValue(tableField
.getSelectedColumnNumber());
209 * Handle mouse release events.
211 * @param mouse mouse button release event
214 public void onMouseUp(final TMouseEvent mouse
) {
215 // Use TWidget's code to pass the event to the children.
216 super.onMouseUp(mouse
);
218 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
219 // Clicked/dragged on vertical scrollbar.
220 tableField
.setSelectedRowNumber(getVerticalValue());
222 if (mouse
.isMouse1() && mouseOnHorizontalScroller(mouse
)) {
223 // Clicked/dragged on horizontal scrollbar.
224 tableField
.setSelectedColumnNumber(getHorizontalValue());
229 * Method that subclasses can override to handle mouse movements.
231 * @param mouse mouse motion event
234 public void onMouseMotion(final TMouseEvent mouse
) {
235 // Use TWidget's code to pass the event to the children.
236 super.onMouseMotion(mouse
);
238 if (mouseOnTable(mouse
) && mouse
.isMouse1()) {
239 // The table might have changed, update the scollbars.
240 setBottomValue(tableField
.getRowCount() - 1);
241 setVerticalValue(tableField
.getSelectedRowNumber());
242 setRightValue(tableField
.getColumnCount() - 1);
243 setHorizontalValue(tableField
.getSelectedColumnNumber());
245 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
246 // Clicked/dragged on vertical scrollbar.
247 tableField
.setSelectedRowNumber(getVerticalValue());
249 if (mouse
.isMouse1() && mouseOnHorizontalScroller(mouse
)) {
250 // Clicked/dragged on horizontal scrollbar.
251 tableField
.setSelectedColumnNumber(getHorizontalValue());
260 * @param keypress keystroke event
263 public void onKeypress(final TKeypressEvent keypress
) {
264 // Use TWidget's code to pass the event to the children.
265 super.onKeypress(keypress
);
267 // The table might have changed, update the scollbars.
268 setBottomValue(tableField
.getRowCount() - 1);
269 setVerticalValue(tableField
.getSelectedRowNumber());
270 setRightValue(tableField
.getColumnCount() - 1);
271 setHorizontalValue(tableField
.getSelectedColumnNumber());
275 * Handle window/screen resize events.
277 * @param event resize event
280 public void onResize(final TResizeEvent event
) {
281 if (event
.getType() == TResizeEvent
.Type
.WIDGET
) {
283 TResizeEvent tableSize
= new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
284 event
.getWidth() - 2, event
.getHeight() - 2);
285 tableField
.onResize(tableSize
);
287 // Have TScrollableWindow handle the scrollbars
288 super.onResize(event
);
292 // Pass to children instead
293 for (TWidget widget
: getChildren()) {
294 widget
.onResize(event
);
299 * Method that subclasses can override to handle posted command events.
301 * @param command command event
304 public void onCommand(final TCommandEvent command
) {
305 if (command
.equals(cmOpen
)) {
307 String filename
= fileOpenBox(".");
308 if (filename
!= null) {
312 tableField
.saveToCsvFilename(filename
);
314 } catch (IOException e
) {
315 messageBox(i18n
.getString("errorDialogTitle"),
316 MessageFormat
.format(i18n
.
317 getString("errorReadingFile"), e
.getMessage()));
320 } catch (IOException e
) {
321 messageBox(i18n
.getString("errorDialogTitle"),
322 MessageFormat
.format(i18n
.
323 getString("errorOpeningFileDialog"), e
.getMessage()));
328 // Didn't handle it, let children get it instead
329 super.onCommand(command
);
333 * Handle posted menu events.
335 * @param menu menu event
338 public void onMenu(final TMenuEvent menu
) {
341 switch (menu
.getId()) {
342 case TMenu
.MID_TABLE_RENAME_COLUMN
:
343 inputBox
= inputBox(i18n
.getString("renameColumnInputTitle"),
344 i18n
.getString("renameColumnInputCaption"),
345 tableField
.getColumnLabel(tableField
.getSelectedColumnNumber()),
346 TMessageBox
.Type
.OKCANCEL
);
347 if (inputBox
.isOk()) {
348 tableField
.setColumnLabel(tableField
.getSelectedColumnNumber(),
352 case TMenu
.MID_TABLE_RENAME_ROW
:
353 inputBox
= inputBox(i18n
.getString("renameRowInputTitle"),
354 i18n
.getString("renameRowInputCaption"),
355 tableField
.getRowLabel(tableField
.getSelectedRowNumber()),
356 TMessageBox
.Type
.OKCANCEL
);
357 if (inputBox
.isOk()) {
358 tableField
.setRowLabel(tableField
.getSelectedRowNumber(),
362 case TMenu
.MID_TABLE_VIEW_ROW_LABELS
:
363 tableField
.setShowRowLabels(getApplication().getMenuItem(
364 menu
.getId()).getChecked());
366 case TMenu
.MID_TABLE_VIEW_COLUMN_LABELS
:
367 tableField
.setShowColumnLabels(getApplication().getMenuItem(
368 menu
.getId()).getChecked());
370 case TMenu
.MID_TABLE_VIEW_HIGHLIGHT_ROW
:
371 tableField
.setHighlightRow(getApplication().getMenuItem(
372 menu
.getId()).getChecked());
374 case TMenu
.MID_TABLE_VIEW_HIGHLIGHT_COLUMN
:
375 tableField
.setHighlightColumn(getApplication().getMenuItem(
376 menu
.getId()).getChecked());
378 case TMenu
.MID_TABLE_BORDER_NONE
:
379 tableField
.setBorderAllNone();
381 case TMenu
.MID_TABLE_BORDER_ALL
:
382 tableField
.setBorderAllSingle();
384 case TMenu
.MID_TABLE_BORDER_CELL_NONE
:
385 tableField
.setBorderCellNone();
387 case TMenu
.MID_TABLE_BORDER_CELL_ALL
:
388 tableField
.setBorderCellSingle();
390 case TMenu
.MID_TABLE_BORDER_RIGHT
:
391 tableField
.setBorderColumnRightSingle();
393 case TMenu
.MID_TABLE_BORDER_LEFT
:
394 tableField
.setBorderColumnLeftSingle();
396 case TMenu
.MID_TABLE_BORDER_TOP
:
397 tableField
.setBorderRowAboveSingle();
399 case TMenu
.MID_TABLE_BORDER_BOTTOM
:
400 tableField
.setBorderRowBelowSingle();
402 case TMenu
.MID_TABLE_BORDER_DOUBLE_BOTTOM
:
403 tableField
.setBorderRowBelowDouble();
405 case TMenu
.MID_TABLE_BORDER_THICK_BOTTOM
:
406 tableField
.setBorderRowBelowThick();
408 case TMenu
.MID_TABLE_DELETE_LEFT
:
409 tableField
.deleteCellShiftLeft();
411 case TMenu
.MID_TABLE_DELETE_UP
:
412 tableField
.deleteCellShiftUp();
414 case TMenu
.MID_TABLE_DELETE_ROW
:
415 tableField
.deleteRow(tableField
.getSelectedRowNumber());
417 case TMenu
.MID_TABLE_DELETE_COLUMN
:
418 tableField
.deleteColumn(tableField
.getSelectedColumnNumber());
420 case TMenu
.MID_TABLE_INSERT_LEFT
:
421 tableField
.insertColumnLeft(tableField
.getSelectedColumnNumber());
423 case TMenu
.MID_TABLE_INSERT_RIGHT
:
424 tableField
.insertColumnRight(tableField
.getSelectedColumnNumber());
426 case TMenu
.MID_TABLE_INSERT_ABOVE
:
427 tableField
.insertRowAbove(tableField
.getSelectedColumnNumber());
429 case TMenu
.MID_TABLE_INSERT_BELOW
:
430 tableField
.insertRowBelow(tableField
.getSelectedColumnNumber());
432 case TMenu
.MID_TABLE_COLUMN_NARROW
:
433 tableField
.setColumnWidth(tableField
.getSelectedColumnNumber(),
434 tableField
.getColumnWidth(tableField
.getSelectedColumnNumber()) - 1);
436 case TMenu
.MID_TABLE_COLUMN_WIDEN
:
437 tableField
.setColumnWidth(tableField
.getSelectedColumnNumber(),
438 tableField
.getColumnWidth(tableField
.getSelectedColumnNumber()) + 1);
440 case TMenu
.MID_TABLE_FILE_OPEN_CSV
:
443 case TMenu
.MID_TABLE_FILE_SAVE_CSV
:
446 case TMenu
.MID_TABLE_FILE_SAVE_TEXT
:
456 // ------------------------------------------------------------------------
457 // TTableWindow -----------------------------------------------------------
458 // ------------------------------------------------------------------------
461 * Setup other fields after the table is created.
463 private void setupAfterTable() {
464 hScroller
= new THScroller(this, 17, getHeight() - 2, getWidth() - 20);
465 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
466 setMinimumWindowWidth(25);
467 setMinimumWindowHeight(10);
468 setTopValue(tableField
.getSelectedRowNumber());
469 setBottomValue(tableField
.getRowCount() - 1);
470 setLeftValue(tableField
.getSelectedColumnNumber());
471 setRightValue(tableField
.getColumnCount() - 1);
473 statusBar
= newStatusBar(i18n
.getString("statusBar"));
474 statusBar
.addShortcutKeypress(kbF1
, cmHelp
,
475 i18n
.getString("statusBarHelp"));
477 statusBar
.addShortcutKeypress(kbF2
, cmSave
,
478 i18n
.getString("statusBarSave"));
479 statusBar
.addShortcutKeypress(kbF3
, cmOpen
,
480 i18n
.getString("statusBarOpen"));
481 statusBar
.addShortcutKeypress(kbF10
, cmMenu
,
482 i18n
.getString("statusBarMenu"));
484 // Synchronize the menu with tableField's flags.
489 * Check if a mouse press/release/motion event coordinate is over the
492 * @param mouse a mouse-based event
493 * @return whether or not the mouse is on the table
495 private boolean mouseOnTable(final TMouseEvent mouse
) {
496 if ((mouse
.getAbsoluteX() >= getAbsoluteX() + 1)
497 && (mouse
.getAbsoluteX() < getAbsoluteX() + getWidth() - 1)
498 && (mouse
.getAbsoluteY() >= getAbsoluteY() + 1)
499 && (mouse
.getAbsoluteY() < getAbsoluteY() + getHeight() - 1)