TEditor working
[fanfix.git] / src / jexer / TEditorWindow.java
CommitLineData
71a389c9
KL
1/*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2017 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
31import java.io.File;
32import java.io.IOException;
33import java.util.Scanner;
34
35import jexer.TApplication;
36import jexer.TEditorWidget;
37import jexer.THScroller;
38import jexer.TScrollableWindow;
39import jexer.TVScroller;
40import jexer.TWidget;
41import jexer.bits.CellAttributes;
42import jexer.bits.GraphicsChars;
43import jexer.event.TCommandEvent;
44import jexer.event.TMouseEvent;
45import jexer.event.TResizeEvent;
46import static jexer.TCommand.*;
47import static jexer.TKeypress.*;
48
49/**
50 * TEditorWindow is a basic text file editor.
51 */
52public class TEditorWindow extends TScrollableWindow {
53
54 /**
55 * Hang onto my TEditor so I can resize it with the window.
56 */
57 private TEditorWidget editField;
58
59 /**
60 * The fully-qualified name of the file being edited.
61 */
62 private String filename = "";
63
64 /**
65 * Setup other fields after the editor is created.
66 */
67 private void setupAfterEditor() {
68 hScroller = new THScroller(this, 17, getHeight() - 2, getWidth() - 20);
69 vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
70 setMinimumWindowWidth(25);
71 setMinimumWindowHeight(10);
72 setTopValue(1);
73 setBottomValue(editField.getMaximumRowNumber());
74 setLeftValue(1);
75 setRightValue(editField.getMaximumColumnNumber());
76
77 statusBar = newStatusBar("Editor");
78 statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
79 statusBar.addShortcutKeypress(kbF2, cmSave, "Save");
80 statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
81 statusBar.addShortcutKeypress(kbF10, cmMenu, "Menu");
82 }
83
84 /**
85 * Public constructor sets window title.
86 *
87 * @param parent the main application
88 * @param title the window title
89 */
90 public TEditorWindow(final TApplication parent, final String title) {
91
92 super(parent, title, 0, 0, parent.getScreen().getWidth(),
93 parent.getScreen().getHeight() - 2, RESIZABLE);
94
95 editField = addEditor("", 0, 0, getWidth() - 2, getHeight() - 2);
96 setupAfterEditor();
97 }
98
99 /**
100 * Public constructor sets window title and contents.
101 *
102 * @param parent the main application
103 * @param title the window title, usually a filename
104 * @param contents the data for the editing window, usually the file data
105 */
106 public TEditorWindow(final TApplication parent, final String title,
107 final String contents) {
108
109 super(parent, title, 0, 0, parent.getScreen().getWidth(),
110 parent.getScreen().getHeight() - 2, RESIZABLE);
111
112 filename = title;
113 editField = addEditor(contents, 0, 0, getWidth() - 2, getHeight() - 2);
114 setupAfterEditor();
115 }
116
df602ccf
KL
117 /**
118 * Public constructor opens a file.
119 *
120 * @param parent the main application
121 * @param file the file to open
122 * @throws IOException if a java.io operation throws
123 */
124 public TEditorWindow(final TApplication parent,
125 final File file) throws IOException {
126
127 super(parent, file.getName(), 0, 0, parent.getScreen().getWidth(),
128 parent.getScreen().getHeight() - 2, RESIZABLE);
129
130 filename = file.getName();
131 String contents = readFileData(file);
132 editField = addEditor(contents, 0, 0, getWidth() - 2, getHeight() - 2);
133 setupAfterEditor();
134 }
135
71a389c9
KL
136 /**
137 * Public constructor.
138 *
139 * @param parent the main application
140 */
141 public TEditorWindow(final TApplication parent) {
142 this(parent, "New Text Document");
143 }
144
df602ccf
KL
145 /**
146 * Read file data into a string.
147 *
148 * @param file the file to open
149 * @return the file contents
150 * @throws IOException if a java.io operation throws
151 */
152 private String readFileData(final File file) throws IOException {
153 StringBuilder fileContents = new StringBuilder();
154 Scanner scanner = new Scanner(file);
155 String EOL = System.getProperty("line.separator");
156
157 try {
158 while (scanner.hasNextLine()) {
159 fileContents.append(scanner.nextLine() + EOL);
160 }
161 return fileContents.toString();
162 } finally {
163 scanner.close();
164 }
165 }
166
167 /**
168 * Read file data into a string.
169 *
170 * @param filename the file to open
171 * @return the file contents
172 * @throws IOException if a java.io operation throws
173 */
174 private String readFileData(final String filename) throws IOException {
175 return readFileData(new File(filename));
176 }
177
71a389c9
KL
178 /**
179 * Draw the window.
180 */
181 @Override
182 public void draw() {
183 // Draw as normal.
184 super.draw();
185
186 // Add the row:col on the bottom row
187 CellAttributes borderColor = getBorder();
188 String location = String.format(" %d:%d ",
189 editField.getEditingRowNumber(),
190 editField.getEditingColumnNumber());
191 int colon = location.indexOf(':');
192 putStringXY(10 - colon, getHeight() - 1, location, borderColor);
193
194 if (editField.isDirty()) {
195 putCharXY(2, getHeight() - 1, GraphicsChars.OCTOSTAR, borderColor);
196 }
197 }
198
199 /**
200 * Check if a mouse press/release/motion event coordinate is over the
201 * editor.
202 *
203 * @param mouse a mouse-based event
204 * @return whether or not the mouse is on the emulator
205 */
206 private final boolean mouseOnEditor(final TMouseEvent mouse) {
207 if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1)
208 && (mouse.getAbsoluteX() < getAbsoluteX() + getWidth() - 1)
209 && (mouse.getAbsoluteY() >= getAbsoluteY() + 1)
210 && (mouse.getAbsoluteY() < getAbsoluteY() + getHeight() - 1)
211 ) {
212 return true;
213 }
214 return false;
215 }
216
217 /**
218 * Handle mouse press events.
219 *
220 * @param mouse mouse button press event
221 */
222 @Override
223 public void onMouseDown(final TMouseEvent mouse) {
df602ccf
KL
224 // Use TWidget's code to pass the event to the children.
225 super.onMouseDown(mouse);
226
71a389c9 227 if (mouseOnEditor(mouse)) {
df602ccf 228 // The editor might have changed, update the scollbars.
71a389c9
KL
229 setBottomValue(editField.getMaximumRowNumber());
230 setVerticalValue(editField.getEditingRowNumber());
231 setRightValue(editField.getMaximumColumnNumber());
232 setHorizontalValue(editField.getEditingColumnNumber());
233 } else {
71a389c9 234 if (mouse.isMouseWheelUp() || mouse.isMouseWheelDown()) {
df602ccf 235 // Vertical scrollbar actions
71a389c9
KL
236 editField.setEditingRowNumber(getVerticalValue());
237 }
238 // TODO: horizontal scrolling
239 }
240 }
241
242 /**
243 * Handle window/screen resize events.
244 *
245 * @param event resize event
246 */
247 @Override
248 public void onResize(final TResizeEvent event) {
249 if (event.getType() == TResizeEvent.Type.WIDGET) {
250 // Resize the text field
251 TResizeEvent editSize = new TResizeEvent(TResizeEvent.Type.WIDGET,
252 event.getWidth() - 2, event.getHeight() - 2);
253 editField.onResize(editSize);
254
255 // Have TScrollableWindow handle the scrollbars
256 super.onResize(event);
257 return;
258 }
259
260 // Pass to children instead
261 for (TWidget widget: getChildren()) {
262 widget.onResize(event);
263 }
264 }
265
266 /**
267 * Method that subclasses can override to handle posted command events.
268 *
269 * @param command command event
270 */
271 @Override
272 public void onCommand(final TCommandEvent command) {
273 if (command.equals(cmOpen)) {
274 try {
275 String filename = fileOpenBox(".");
df602ccf
KL
276 if (filename != null) {
277 try {
278 String contents = readFileData(filename);
279 new TEditorWindow(getApplication(), filename, contents);
280 } catch (IOException e) {
281 messageBox("Error", "Error reading file: " +
282 e.getMessage());
283 }
284 }
71a389c9 285 } catch (IOException e) {
df602ccf
KL
286 messageBox("Error", "Error opening file dialog: " +
287 e.getMessage());
71a389c9
KL
288 }
289 return;
290 }
291
292 if (command.equals(cmSave)) {
293 if (filename.length() > 0) {
294 try {
295 editField.saveToFilename(filename);
296 } catch (IOException e) {
df602ccf 297 messageBox("Error", "Error saving file: " + e.getMessage());
71a389c9
KL
298 }
299 }
300 return;
301 }
302
303 // Didn't handle it, let children get it instead
304 super.onCommand(command);
305 }
306
307}