Commit | Line | Data |
---|---|---|
cd92b0ba 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 | */ | |
29 | package jexer.teditor; | |
30 | ||
31 | import java.util.ArrayList; | |
32 | import java.util.List; | |
33 | ||
e8a11f98 KL |
34 | import jexer.bits.CellAttributes; |
35 | ||
cd92b0ba | 36 | /** |
12b55d76 KL |
37 | * A Line represents a single line of text on the screen, as a collection of |
38 | * words. | |
cd92b0ba | 39 | */ |
12b55d76 | 40 | public class Line { |
cd92b0ba KL |
41 | |
42 | /** | |
12b55d76 | 43 | * The list of words. |
cd92b0ba | 44 | */ |
12b55d76 | 45 | private ArrayList<Word> words = new ArrayList<Word>(); |
cd92b0ba | 46 | |
e8a11f98 KL |
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 | ||
cd92b0ba | 57 | /** |
12b55d76 | 58 | * The current cursor position on this line. |
cd92b0ba | 59 | */ |
e8a11f98 | 60 | private int cursor = 0; |
cd92b0ba KL |
61 | |
62 | /** | |
71a389c9 KL |
63 | * The raw text of this line, what is passed to Word to determine |
64 | * highlighting behavior. | |
cd92b0ba | 65 | */ |
71a389c9 | 66 | private StringBuilder rawText; |
cd92b0ba KL |
67 | |
68 | /** | |
71a389c9 KL |
69 | * Get a (shallow) copy of the words in this line. |
70 | * | |
71 | * @return a copy of the word list | |
cd92b0ba | 72 | */ |
71a389c9 KL |
73 | public List<Word> getWords() { |
74 | return new ArrayList<Word>(words); | |
75 | } | |
cd92b0ba | 76 | |
e8a11f98 KL |
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; | |
e8a11f98 KL |
100 | } |
101 | ||
cd92b0ba | 102 | /** |
71a389c9 | 103 | * Get the on-screen display length. |
cd92b0ba | 104 | * |
71a389c9 | 105 | * @return the number of cells needed to display this line |
cd92b0ba | 106 | */ |
71a389c9 KL |
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; | |
cd92b0ba KL |
117 | } |
118 | ||
119 | /** | |
71a389c9 | 120 | * Get the raw string that matches this line. |
cd92b0ba | 121 | * |
71a389c9 | 122 | * @return the string |
cd92b0ba | 123 | */ |
71a389c9 KL |
124 | public String getRawString() { |
125 | return rawText.toString(); | |
126 | } | |
e8a11f98 | 127 | |
71a389c9 KL |
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(); | |
e8a11f98 | 145 | } |
cd92b0ba KL |
146 | } |
147 | ||
148 | /** | |
e8a11f98 KL |
149 | * Construct a new Line from an existing text string, and highlight |
150 | * certain strings. | |
cd92b0ba | 151 | * |
12b55d76 | 152 | * @param str the text string |
e8a11f98 KL |
153 | * @param defaultColor the color for unhighlighted text |
154 | * @param highlighter the highlighter to use | |
12b55d76 | 155 | */ |
e8a11f98 KL |
156 | public Line(final String str, final CellAttributes defaultColor, |
157 | final Highlighter highlighter) { | |
158 | ||
159 | this.defaultColor = defaultColor; | |
160 | this.highlighter = highlighter; | |
71a389c9 | 161 | this.rawText = new StringBuilder(str); |
e8a11f98 | 162 | |
71a389c9 | 163 | scanLine(); |
e8a11f98 KL |
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); | |
cd92b0ba KL |
174 | } |
175 | ||
176 | /** | |
12b55d76 | 177 | * Decrement the cursor by one. If at the first column, do nothing. |
e8a11f98 KL |
178 | * |
179 | * @return true if the cursor position changed | |
cd92b0ba | 180 | */ |
e8a11f98 KL |
181 | public boolean left() { |
182 | if (cursor == 0) { | |
183 | return false; | |
cd92b0ba | 184 | } |
e8a11f98 KL |
185 | cursor--; |
186 | return true; | |
cd92b0ba KL |
187 | } |
188 | ||
189 | /** | |
12b55d76 | 190 | * Increment the cursor by one. If at the last column, do nothing. |
e8a11f98 KL |
191 | * |
192 | * @return true if the cursor position changed | |
cd92b0ba | 193 | */ |
e8a11f98 KL |
194 | public boolean right() { |
195 | if (getDisplayLength() == 0) { | |
196 | return false; | |
cd92b0ba | 197 | } |
e8a11f98 KL |
198 | if (cursor == getDisplayLength() - 1) { |
199 | return false; | |
200 | } | |
e8a11f98 KL |
201 | cursor++; |
202 | return true; | |
cd92b0ba KL |
203 | } |
204 | ||
205 | /** | |
12b55d76 | 206 | * Go to the first column of this line. |
e8a11f98 KL |
207 | * |
208 | * @return true if the cursor position changed | |
cd92b0ba | 209 | */ |
e8a11f98 KL |
210 | public boolean home() { |
211 | if (cursor > 0) { | |
212 | cursor = 0; | |
e8a11f98 KL |
213 | return true; |
214 | } | |
215 | return false; | |
cd92b0ba KL |
216 | } |
217 | ||
218 | /** | |
12b55d76 | 219 | * Go to the last column of this line. |
e8a11f98 KL |
220 | * |
221 | * @return true if the cursor position changed | |
cd92b0ba | 222 | */ |
e8a11f98 KL |
223 | public boolean end() { |
224 | if (cursor != getDisplayLength() - 1) { | |
225 | cursor = getDisplayLength() - 1; | |
226 | if (cursor < 0) { | |
227 | cursor = 0; | |
228 | } | |
e8a11f98 KL |
229 | return true; |
230 | } | |
231 | return false; | |
cd92b0ba KL |
232 | } |
233 | ||
234 | /** | |
12b55d76 | 235 | * Delete the character under the cursor. |
cd92b0ba | 236 | */ |
12b55d76 | 237 | public void del() { |
71a389c9 KL |
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(); | |
cd92b0ba KL |
246 | } |
247 | ||
248 | /** | |
12b55d76 | 249 | * Delete the character immediately preceeding the cursor. |
cd92b0ba | 250 | */ |
12b55d76 | 251 | public void backspace() { |
71a389c9 KL |
252 | if (left()) { |
253 | del(); | |
254 | } | |
cd92b0ba KL |
255 | } |
256 | ||
257 | /** | |
e8a11f98 | 258 | * Insert a character at the cursor. |
cd92b0ba | 259 | * |
e8a11f98 | 260 | * @param ch the character to insert |
cd92b0ba | 261 | */ |
12b55d76 | 262 | public void addChar(final char ch) { |
71a389c9 KL |
263 | if (cursor < getDisplayLength() - 1) { |
264 | rawText.insert(cursor, ch); | |
265 | } else { | |
266 | rawText.append(ch); | |
267 | } | |
268 | scanLine(); | |
269 | cursor++; | |
cd92b0ba KL |
270 | } |
271 | ||
e8a11f98 KL |
272 | /** |
273 | * Replace a character at the cursor. | |
274 | * | |
275 | * @param ch the character to replace | |
276 | */ | |
277 | public void replaceChar(final char ch) { | |
71a389c9 KL |
278 | if (cursor < getDisplayLength() - 1) { |
279 | rawText.setCharAt(cursor, ch); | |
280 | } else { | |
281 | rawText.append(ch); | |
282 | } | |
283 | scanLine(); | |
284 | cursor++; | |
e8a11f98 KL |
285 | } |
286 | ||
cd92b0ba | 287 | } |