TEditor 80% complete
[nikiroo-utils.git] / src / jexer / teditor / Line.java
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 */
29 package jexer.teditor;
30
31 import java.util.ArrayList;
32 import java.util.List;
33
34 import jexer.bits.CellAttributes;
35
36 /**
37 * A Line represents a single line of text on the screen, as a collection of
38 * words.
39 */
40 public class Line {
41
42 /**
43 * The list of words.
44 */
45 private ArrayList<Word> words = new ArrayList<Word>();
46
47 /**
48 * The default color for the TEditor class.
49 */
50 private CellAttributes defaultColor = null;
51
52 /**
53 * The text highlighter to use.
54 */
55 private Highlighter highlighter = null;
56
57 /**
58 * The current cursor position on this line.
59 */
60 private int cursor = 0;
61
62 /**
63 * The raw text of this line, what is passed to Word to determine
64 * highlighting behavior.
65 */
66 private StringBuilder rawText;
67
68 /**
69 * Get a (shallow) copy of the words in this line.
70 *
71 * @return a copy of the word list
72 */
73 public List<Word> getWords() {
74 return new ArrayList<Word>(words);
75 }
76
77 /**
78 * Get the current cursor position.
79 *
80 * @return the cursor position
81 */
82 public int getCursor() {
83 return cursor;
84 }
85
86 /**
87 * Set the current cursor position.
88 *
89 * @param cursor the new cursor position
90 */
91 public void setCursor(final int cursor) {
92 if ((cursor < 0)
93 || ((cursor >= getDisplayLength())
94 && (getDisplayLength() > 0))
95 ) {
96 throw new IndexOutOfBoundsException("Max length is " +
97 getDisplayLength() + ", requested position " + cursor);
98 }
99 this.cursor = cursor;
100 }
101
102 /**
103 * Get the on-screen display length.
104 *
105 * @return the number of cells needed to display this line
106 */
107 public int getDisplayLength() {
108 int n = rawText.length();
109
110 // For now just return the raw text length.
111 if (n > 0) {
112 // If we have any visible characters, add one to the display so
113 // that the cursor is immediately after the data.
114 return n + 1;
115 }
116 return n;
117 }
118
119 /**
120 * Get the raw string that matches this line.
121 *
122 * @return the string
123 */
124 public String getRawString() {
125 return rawText.toString();
126 }
127
128 /**
129 * Scan rawText and make words out of it.
130 */
131 private void scanLine() {
132 words.clear();
133 Word word = new Word(this.defaultColor, this.highlighter);
134 words.add(word);
135 for (int i = 0; i < rawText.length(); i++) {
136 char ch = rawText.charAt(i);
137 Word newWord = word.addChar(ch);
138 if (newWord != word) {
139 words.add(newWord);
140 word = newWord;
141 }
142 }
143 for (Word w: words) {
144 w.applyHighlight();
145 }
146 }
147
148 /**
149 * Construct a new Line from an existing text string, and highlight
150 * certain strings.
151 *
152 * @param str the text string
153 * @param defaultColor the color for unhighlighted text
154 * @param highlighter the highlighter to use
155 */
156 public Line(final String str, final CellAttributes defaultColor,
157 final Highlighter highlighter) {
158
159 this.defaultColor = defaultColor;
160 this.highlighter = highlighter;
161 this.rawText = new StringBuilder(str);
162
163 scanLine();
164 }
165
166 /**
167 * Construct a new Line from an existing text string.
168 *
169 * @param str the text string
170 * @param defaultColor the color for unhighlighted text
171 */
172 public Line(final String str, final CellAttributes defaultColor) {
173 this(str, defaultColor, null);
174 }
175
176 /**
177 * Decrement the cursor by one. If at the first column, do nothing.
178 *
179 * @return true if the cursor position changed
180 */
181 public boolean left() {
182 if (cursor == 0) {
183 return false;
184 }
185 cursor--;
186 return true;
187 }
188
189 /**
190 * Increment the cursor by one. If at the last column, do nothing.
191 *
192 * @return true if the cursor position changed
193 */
194 public boolean right() {
195 if (getDisplayLength() == 0) {
196 return false;
197 }
198 if (cursor == getDisplayLength() - 1) {
199 return false;
200 }
201 cursor++;
202 return true;
203 }
204
205 /**
206 * Go to the first column of this line.
207 *
208 * @return true if the cursor position changed
209 */
210 public boolean home() {
211 if (cursor > 0) {
212 cursor = 0;
213 return true;
214 }
215 return false;
216 }
217
218 /**
219 * Go to the last column of this line.
220 *
221 * @return true if the cursor position changed
222 */
223 public boolean end() {
224 if (cursor != getDisplayLength() - 1) {
225 cursor = getDisplayLength() - 1;
226 if (cursor < 0) {
227 cursor = 0;
228 }
229 return true;
230 }
231 return false;
232 }
233
234 /**
235 * Delete the character under the cursor.
236 */
237 public void del() {
238 assert (words.size() > 0);
239
240 if (cursor < getDisplayLength()) {
241 rawText.deleteCharAt(cursor);
242 }
243
244 // Re-scan the line to determine the new word boundaries.
245 scanLine();
246 }
247
248 /**
249 * Delete the character immediately preceeding the cursor.
250 */
251 public void backspace() {
252 if (left()) {
253 del();
254 }
255 }
256
257 /**
258 * Insert a character at the cursor.
259 *
260 * @param ch the character to insert
261 */
262 public void addChar(final char ch) {
263 if (cursor < getDisplayLength() - 1) {
264 rawText.insert(cursor, ch);
265 } else {
266 rawText.append(ch);
267 }
268 scanLine();
269 cursor++;
270 }
271
272 /**
273 * Replace a character at the cursor.
274 *
275 * @param ch the character to replace
276 */
277 public void replaceChar(final char ch) {
278 if (cursor < getDisplayLength() - 1) {
279 rawText.setCharAt(cursor, ch);
280 } else {
281 rawText.append(ch);
282 }
283 scanLine();
284 cursor++;
285 }
286
287 }