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