2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2017 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]
32 import java
.io
.IOException
;
33 import java
.util
.Scanner
;
35 import jexer
.TApplication
;
36 import jexer
.TEditorWidget
;
37 import jexer
.THScroller
;
38 import jexer
.TScrollableWindow
;
39 import jexer
.TVScroller
;
41 import jexer
.bits
.CellAttributes
;
42 import jexer
.bits
.GraphicsChars
;
43 import jexer
.event
.TCommandEvent
;
44 import jexer
.event
.TKeypressEvent
;
45 import jexer
.event
.TMouseEvent
;
46 import jexer
.event
.TResizeEvent
;
47 import static jexer
.TCommand
.*;
48 import static jexer
.TKeypress
.*;
51 * TEditorWindow is a basic text file editor.
53 public class TEditorWindow
extends TScrollableWindow
{
56 * Hang onto my TEditor so I can resize it with the window.
58 private TEditorWidget editField
;
61 * The fully-qualified name of the file being edited.
63 private String filename
= "";
66 * Setup other fields after the editor is created.
68 private void setupAfterEditor() {
69 hScroller
= new THScroller(this, 17, getHeight() - 2, getWidth() - 20);
70 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
71 setMinimumWindowWidth(25);
72 setMinimumWindowHeight(10);
74 setBottomValue(editField
.getMaximumRowNumber());
76 setRightValue(editField
.getMaximumColumnNumber());
78 statusBar
= newStatusBar("Editor");
79 statusBar
.addShortcutKeypress(kbF1
, cmHelp
, "Help");
80 statusBar
.addShortcutKeypress(kbF2
, cmSave
, "Save");
81 statusBar
.addShortcutKeypress(kbF3
, cmOpen
, "Open");
82 statusBar
.addShortcutKeypress(kbF10
, cmMenu
, "Menu");
86 * Public constructor sets window title.
88 * @param parent the main application
89 * @param title the window title
91 public TEditorWindow(final TApplication parent
, final String title
) {
93 super(parent
, title
, 0, 0, parent
.getScreen().getWidth(),
94 parent
.getScreen().getHeight() - 2, RESIZABLE
);
96 editField
= addEditor("", 0, 0, getWidth() - 2, getHeight() - 2);
101 * Public constructor sets window title and contents.
103 * @param parent the main application
104 * @param title the window title, usually a filename
105 * @param contents the data for the editing window, usually the file data
107 public TEditorWindow(final TApplication parent
, final String title
,
108 final String contents
) {
110 super(parent
, title
, 0, 0, parent
.getScreen().getWidth(),
111 parent
.getScreen().getHeight() - 2, RESIZABLE
);
114 editField
= addEditor(contents
, 0, 0, getWidth() - 2, getHeight() - 2);
119 * Public constructor opens a file.
121 * @param parent the main application
122 * @param file the file to open
123 * @throws IOException if a java.io operation throws
125 public TEditorWindow(final TApplication parent
,
126 final File file
) throws IOException
{
128 super(parent
, file
.getName(), 0, 0, parent
.getScreen().getWidth(),
129 parent
.getScreen().getHeight() - 2, RESIZABLE
);
131 filename
= file
.getName();
132 String contents
= readFileData(file
);
133 editField
= addEditor(contents
, 0, 0, getWidth() - 2, getHeight() - 2);
138 * Public constructor.
140 * @param parent the main application
142 public TEditorWindow(final TApplication parent
) {
143 this(parent
, "New Text Document");
147 * Read file data into a string.
149 * @param file the file to open
150 * @return the file contents
151 * @throws IOException if a java.io operation throws
153 private String
readFileData(final File file
) throws IOException
{
154 StringBuilder fileContents
= new StringBuilder();
155 Scanner scanner
= new Scanner(file
);
156 String EOL
= System
.getProperty("line.separator");
159 while (scanner
.hasNextLine()) {
160 fileContents
.append(scanner
.nextLine() + EOL
);
162 return fileContents
.toString();
169 * Read file data into a string.
171 * @param filename the file to open
172 * @return the file contents
173 * @throws IOException if a java.io operation throws
175 private String
readFileData(final String filename
) throws IOException
{
176 return readFileData(new File(filename
));
187 // Add the row:col on the bottom row
188 CellAttributes borderColor
= getBorder();
189 String location
= String
.format(" %d:%d ",
190 editField
.getEditingRowNumber(),
191 editField
.getEditingColumnNumber());
192 int colon
= location
.indexOf(':');
193 putStringXY(10 - colon
, getHeight() - 1, location
, borderColor
);
195 if (editField
.isDirty()) {
196 putCharXY(2, getHeight() - 1, GraphicsChars
.OCTOSTAR
, borderColor
);
201 * Check if a mouse press/release/motion event coordinate is over the
204 * @param mouse a mouse-based event
205 * @return whether or not the mouse is on the editor
207 private final boolean mouseOnEditor(final TMouseEvent mouse
) {
208 if ((mouse
.getAbsoluteX() >= getAbsoluteX() + 1)
209 && (mouse
.getAbsoluteX() < getAbsoluteX() + getWidth() - 1)
210 && (mouse
.getAbsoluteY() >= getAbsoluteY() + 1)
211 && (mouse
.getAbsoluteY() < getAbsoluteY() + getHeight() - 1)
219 * Handle mouse press events.
221 * @param mouse mouse button press event
224 public void onMouseDown(final TMouseEvent mouse
) {
225 // Use TWidget's code to pass the event to the children.
226 super.onMouseDown(mouse
);
228 if (mouseOnEditor(mouse
)) {
229 // The editor might have changed, update the scollbars.
230 setBottomValue(editField
.getMaximumRowNumber());
231 setVerticalValue(editField
.getEditingRowNumber());
232 setRightValue(editField
.getMaximumColumnNumber());
233 setHorizontalValue(editField
.getEditingColumnNumber());
235 if (mouse
.isMouseWheelUp() || mouse
.isMouseWheelDown()) {
236 // Vertical scrollbar actions
237 editField
.setEditingRowNumber(getVerticalValue());
243 * Handle mouse release events.
245 * @param mouse mouse button release event
248 public void onMouseUp(final TMouseEvent mouse
) {
249 // Use TWidget's code to pass the event to the children.
250 super.onMouseUp(mouse
);
252 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
253 // Clicked on vertical scrollbar
254 editField
.setEditingRowNumber(getVerticalValue());
257 // TODO: horizontal scrolling
261 * Method that subclasses can override to handle mouse movements.
263 * @param mouse mouse motion event
266 public void onMouseMotion(final TMouseEvent mouse
) {
267 // Use TWidget's code to pass the event to the children.
268 super.onMouseMotion(mouse
);
270 if (mouseOnEditor(mouse
) && mouse
.isMouse1()) {
271 // The editor might have changed, update the scollbars.
272 setBottomValue(editField
.getMaximumRowNumber());
273 setVerticalValue(editField
.getEditingRowNumber());
274 setRightValue(editField
.getMaximumColumnNumber());
275 setHorizontalValue(editField
.getEditingColumnNumber());
277 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
278 // Clicked/dragged on vertical scrollbar
279 editField
.setEditingRowNumber(getVerticalValue());
282 // TODO: horizontal scrolling
290 * @param keypress keystroke event
293 public void onKeypress(final TKeypressEvent keypress
) {
294 // Use TWidget's code to pass the event to the children.
295 super.onKeypress(keypress
);
297 // The editor might have changed, update the scollbars.
298 setBottomValue(editField
.getMaximumRowNumber());
299 setVerticalValue(editField
.getEditingRowNumber());
300 setRightValue(editField
.getMaximumColumnNumber());
301 setHorizontalValue(editField
.getEditingColumnNumber());
305 * Handle window/screen resize events.
307 * @param event resize event
310 public void onResize(final TResizeEvent event
) {
311 if (event
.getType() == TResizeEvent
.Type
.WIDGET
) {
312 // Resize the text field
313 TResizeEvent editSize
= new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
314 event
.getWidth() - 2, event
.getHeight() - 2);
315 editField
.onResize(editSize
);
317 // Have TScrollableWindow handle the scrollbars
318 super.onResize(event
);
322 // Pass to children instead
323 for (TWidget widget
: getChildren()) {
324 widget
.onResize(event
);
329 * Method that subclasses can override to handle posted command events.
331 * @param command command event
334 public void onCommand(final TCommandEvent command
) {
335 if (command
.equals(cmOpen
)) {
337 String filename
= fileOpenBox(".");
338 if (filename
!= null) {
340 String contents
= readFileData(filename
);
341 new TEditorWindow(getApplication(), filename
, contents
);
342 } catch (IOException e
) {
343 messageBox("Error", "Error reading file: " +
347 } catch (IOException e
) {
348 messageBox("Error", "Error opening file dialog: " +
354 if (command
.equals(cmSave
)) {
355 if (filename
.length() > 0) {
357 editField
.saveToFilename(filename
);
358 } catch (IOException e
) {
359 messageBox("Error", "Error saving file: " + e
.getMessage());
365 // Didn't handle it, let children get it instead
366 super.onCommand(command
);