Prep for 2019 release
[fanfix.git] / src / jexer / bits / Cell.java
CommitLineData
daa4106c 1/*
624ce48e
KL
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
624ce48e 5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
624ce48e 7 *
e16dda65
KL
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:
624ce48e 14 *
e16dda65
KL
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
624ce48e 17 *
e16dda65
KL
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.
7b5261bc
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
624ce48e
KL
28 */
29package jexer.bits;
30
a69ed767
KL
31import java.awt.Color;
32import java.awt.image.BufferedImage;
33
624ce48e 34/**
a69ed767 35 * This class represents a single text cell or bit of image on the screen.
624ce48e 36 */
2b9c27db 37public final class Cell extends CellAttributes {
624ce48e 38
a69ed767
KL
39 // ------------------------------------------------------------------------
40 // Constants --------------------------------------------------------------
41 // ------------------------------------------------------------------------
42
43 /**
44 * The special "this cell is unset" (null) value. This is the Unicode
45 * "not a character" value.
46 */
47 private static final char UNSET_VALUE = (char) 65535;
48
d36057df
KL
49 // ------------------------------------------------------------------------
50 // Variables --------------------------------------------------------------
51 // ------------------------------------------------------------------------
52
624ce48e 53 /**
7b5261bc
KL
54 * The character at this cell.
55 */
56 private char ch;
57
a69ed767
KL
58 /**
59 * The image at this cell.
60 */
61 private BufferedImage image = null;
62
63 /**
64 * The image at this cell, inverted.
65 */
66 private BufferedImage invertedImage = null;
67
68 /**
69 * The background color used for the area the image portion might not
70 * cover.
71 */
72 private Color background = null;
73
74 /**
75 * hashCode() needs to call image.hashCode(), which can get quite
76 * expensive.
77 */
78 private int imageHashCode = 0;
79
80 /**
81 * hashCode() needs to call background.hashCode(), which can get quite
82 * expensive.
83 */
84 private int backgroundHashCode = 0;
85
d36057df
KL
86 // ------------------------------------------------------------------------
87 // Constructors -----------------------------------------------------------
88 // ------------------------------------------------------------------------
89
90 /**
91 * Public constructor sets default values of the cell to blank.
92 *
93 * @see #isBlank()
94 * @see #reset()
95 */
96 public Cell() {
97 reset();
98 }
99
100 /**
101 * Public constructor sets the character. Attributes are the same as
102 * default.
103 *
104 * @param ch character to set to
105 * @see #reset()
106 */
107 public Cell(final char ch) {
108 reset();
109 this.ch = ch;
110 }
111
112 // ------------------------------------------------------------------------
113 // Cell -------------------------------------------------------------------
114 // ------------------------------------------------------------------------
115
a69ed767
KL
116
117 /**
118 * Set the image data for this cell.
119 *
120 * @param image the image for this cell
121 */
122 public void setImage(final BufferedImage image) {
123 this.image = image;
124 imageHashCode = image.hashCode();
125 }
126
127 /**
128 * Get the image data for this cell.
129 *
130 * @return the image for this cell
131 */
132 public BufferedImage getImage() {
133 if (invertedImage != null) {
134 return invertedImage;
135 }
136 return image;
137 }
138
139 /**
140 * Get the bitmap image background color for this cell.
141 *
142 * @return the bitmap image background color
143 */
144 public Color getBackground() {
145 return background;
146 }
147
148 /**
149 * If true, this cell has image data.
150 *
151 * @return true if this cell is an image rather than a character with
152 * attributes
153 */
154 public boolean isImage() {
155 if (image != null) {
156 return true;
157 }
158 return false;
159 }
160
161 /**
162 * Restore the image in this cell to its normal version, if it has one.
163 */
164 public void restoreImage() {
165 invertedImage = null;
166 }
167
168 /**
169 * If true, this cell has image data, and that data is inverted.
170 *
171 * @return true if this cell is an image rather than a character with
172 * attributes, and the data is inverted
173 */
174 public boolean isInvertedImage() {
175 if ((image != null) && (invertedImage != null)) {
176 return true;
177 }
178 return false;
179 }
180
181 /**
182 * Invert the image in this cell, if it has one.
183 */
184 public void invertImage() {
185 if (image == null) {
186 return;
187 }
188 if (invertedImage == null) {
189 invertedImage = new BufferedImage(image.getWidth(),
190 image.getHeight(), BufferedImage.TYPE_INT_ARGB);
191
192 int [] rgbArray = image.getRGB(0, 0,
193 image.getWidth(), image.getHeight(), null, 0, image.getWidth());
194
195 for (int i = 0; i < rgbArray.length; i++) {
196 // Set the colors to fully inverted.
197 if (rgbArray[i] != 0x00FFFFFF) {
198 rgbArray[i] ^= 0x00FFFFFF;
199 }
200 // Also set alpha to non-transparent.
201 rgbArray[i] |= 0xFF000000;
202 }
203 invertedImage.setRGB(0, 0, image.getWidth(), image.getHeight(),
204 rgbArray, 0, image.getWidth());
205 }
206 }
207
7b5261bc
KL
208 /**
209 * Getter for cell character.
210 *
211 * @return cell character
212 */
2b9c27db 213 public char getChar() {
7b5261bc
KL
214 return ch;
215 }
216
217 /**
218 * Setter for cell character.
219 *
220 * @param ch new cell character
624ce48e 221 */
2b9c27db 222 public void setChar(final char ch) {
7b5261bc
KL
223 this.ch = ch;
224 }
624ce48e
KL
225
226 /**
7b5261bc 227 * Reset this cell to a blank.
624ce48e
KL
228 */
229 @Override
2b9c27db 230 public void reset() {
7b5261bc
KL
231 super.reset();
232 ch = ' ';
a69ed767
KL
233 image = null;
234 imageHashCode = 0;
235 invertedImage = null;
236 background = Color.BLACK;
237 backgroundHashCode = 0;
238 }
239
240 /**
241 * UNset this cell. It will not be equal to any other cell until it has
242 * been assigned attributes and a character.
243 */
244 public void unset() {
245 super.reset();
246 ch = UNSET_VALUE;
247 image = null;
248 imageHashCode = 0;
249 invertedImage = null;
250 background = Color.BLACK;
251 backgroundHashCode = 0;
624ce48e
KL
252 }
253
254 /**
7b5261bc
KL
255 * Check to see if this cell has default attributes: white foreground,
256 * black background, no bold/blink/reverse/underline/protect, and a
257 * character value of ' ' (space).
258 *
259 * @return true if this cell has default attributes.
624ce48e 260 */
2b9c27db 261 public boolean isBlank() {
a69ed767
KL
262 if ((ch == UNSET_VALUE) || (image != null)) {
263 return false;
264 }
7b5261bc
KL
265 if ((getForeColor().equals(Color.WHITE))
266 && (getBackColor().equals(Color.BLACK))
7c870d89
KL
267 && !isBold()
268 && !isBlink()
269 && !isReverse()
270 && !isUnderline()
271 && !isProtect()
051e2913 272 && !isRGB()
a69ed767 273 && !isImage()
7b5261bc
KL
274 && (ch == ' ')
275 ) {
276 return true;
277 }
624ce48e 278
7b5261bc 279 return false;
624ce48e
KL
280 }
281
282 /**
7b5261bc
KL
283 * Comparison check. All fields must match to return true.
284 *
285 * @param rhs another Cell instance
286 * @return true if all fields are equal
624ce48e
KL
287 */
288 @Override
2b9c27db 289 public boolean equals(final Object rhs) {
7b5261bc
KL
290 if (!(rhs instanceof Cell)) {
291 return false;
292 }
624ce48e 293
7b5261bc 294 Cell that = (Cell) rhs;
6358f6e5 295
a69ed767
KL
296 // Unsetted cells can never be equal.
297 if ((ch == UNSET_VALUE) || (that.ch == UNSET_VALUE)) {
298 return false;
299 }
300
301 // If this or rhs has an image and the other doesn't, these are not
302 // equal.
303 if ((image != null) && (that.image == null)) {
304 return false;
305 }
306 if ((image == null) && (that.image != null)) {
307 return false;
308 }
309 // If this and rhs have images, both must match.
310 if ((image != null) && (that.image != null)) {
311 if ((invertedImage == null) && (that.invertedImage != null)) {
312 return false;
313 }
314 if ((invertedImage != null) && (that.invertedImage == null)) {
315 return false;
316 }
317 // Either both objects have their image inverted, or neither do.
318 // Now if the images are identical the cells are the same
319 // visually.
320 if (image.equals(that.image)
321 && (background.equals(that.background))
322 ) {
323 return true;
324 } else {
325 return false;
326 }
327 }
328
329 // Normal case: character and attributes must match.
6358f6e5
KL
330 if (ch == that.ch) {
331 return super.equals(rhs);
332 }
333 return false;
624ce48e
KL
334 }
335
e826b451
KL
336 /**
337 * Hashcode uses all fields in equals().
338 *
339 * @return the hash
340 */
341 @Override
342 public int hashCode() {
343 int A = 13;
344 int B = 23;
345 int hash = A;
346 hash = (B * hash) + super.hashCode();
347 hash = (B * hash) + (int)ch;
a69ed767
KL
348 if (image != null) {
349 /*
350 hash = (B * hash) + image.hashCode();
351 hash = (B * hash) + background.hashCode();
352 */
353 hash = (B * hash) + imageHashCode;
354 hash = (B * hash) + backgroundHashCode;
355 }
356 if (invertedImage != null) {
357 hash = (B * hash) + invertedImage.hashCode();
358 }
e826b451
KL
359 return hash;
360 }
361
624ce48e 362 /**
7b5261bc
KL
363 * Set my field values to that's field.
364 *
365 * @param rhs an instance of either Cell or CellAttributes
624ce48e
KL
366 */
367 @Override
2b9c27db 368 public void setTo(final Object rhs) {
7b5261bc
KL
369 // Let this throw a ClassCastException
370 CellAttributes thatAttr = (CellAttributes) rhs;
a69ed767
KL
371 this.image = null;
372 this.imageHashCode = 0;
373 this.backgroundHashCode = 0;
7b5261bc 374 super.setTo(thatAttr);
624ce48e 375
7b5261bc
KL
376 if (rhs instanceof Cell) {
377 Cell that = (Cell) rhs;
378 this.ch = that.ch;
a69ed767
KL
379 this.image = that.image;
380 this.invertedImage = that.invertedImage;
381 this.background = that.background;
382 this.imageHashCode = that.imageHashCode;
383 this.backgroundHashCode = that.backgroundHashCode;
7b5261bc 384 }
624ce48e
KL
385 }
386
387 /**
7b5261bc
KL
388 * Set my field attr values to that's field.
389 *
390 * @param that a CellAttributes instance
624ce48e 391 */
2b9c27db 392 public void setAttr(final CellAttributes that) {
a69ed767 393 image = null;
7b5261bc 394 super.setTo(that);
624ce48e
KL
395 }
396
624ce48e 397 /**
7b5261bc
KL
398 * Make human-readable description of this Cell.
399 *
400 * @return displayable String
624ce48e
KL
401 */
402 @Override
2b9c27db 403 public String toString() {
8a632d71 404 return String.format("fore: %s back: %s bold: %s blink: %s ch %c",
7c870d89 405 getForeColor(), getBackColor(), isBold(), isBlink(), ch);
624ce48e
KL
406 }
407}