Fix UTF8 bug, create first executable JAR file
[jvcard.git] / src / com / googlecode / lanterna / gui2 / Label.java
... / ...
CommitLineData
1/*
2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
3 *
4 * lanterna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Copyright (C) 2010-2015 Martin
18 */
19package com.googlecode.lanterna.gui2;
20
21import com.googlecode.lanterna.TerminalTextUtils;
22import com.googlecode.lanterna.SGR;
23import com.googlecode.lanterna.TerminalSize;
24import com.googlecode.lanterna.TextColor;
25import com.googlecode.lanterna.graphics.ThemeDefinition;
26
27import java.util.EnumSet;
28import java.util.List;
29
30/**
31 * Label is a simple read-only text display component. It supports customized colors and multi-line text.
32 * @author Martin
33 */
34public class Label extends AbstractComponent<Label> {
35 private String[] lines;
36 private Integer labelWidth;
37 private TerminalSize labelSize;
38 private TextColor foregroundColor;
39 private TextColor backgroundColor;
40 private final EnumSet<SGR> additionalStyles;
41
42 /**
43 * Main constructor, creates a new Label displaying a specific text.
44 * @param text Text the label will display
45 */
46 public Label(String text) {
47 this.lines = null;
48 this.labelSize = TerminalSize.ZERO;
49 this.labelWidth = 0;
50 this.foregroundColor = null;
51 this.backgroundColor = null;
52 this.additionalStyles = EnumSet.noneOf(SGR.class);
53 setText(text);
54 }
55
56 /**
57 * Protected access to set the internal representation of the text in this label, to be used by sub-classes of label
58 * in certain cases where {@code setText(..)} doesn't work. In general, you probably want to stick to
59 * {@code setText(..)} instead of this method unless you have a good reason not to.
60 * @param lines New lines this label will display
61 */
62 protected void setLines(String[] lines) {
63 this.lines = lines;
64 }
65
66 /**
67 * Updates the text this label is displaying
68 * @param text New text to display
69 */
70 public synchronized void setText(String text) {
71 setLines(splitIntoMultipleLines(text));
72 this.labelSize = getBounds(lines, labelSize);
73 invalidate();
74 }
75
76 /**
77 * Returns the text this label is displaying. Multi-line labels will have their text concatenated with \n, even if
78 * they were originally set using multi-line text having \r\n as line terminators.
79 * @return String of the text this label is displaying
80 */
81 public synchronized String getText() {
82 if(lines.length == 0) {
83 return "";
84 }
85 StringBuilder bob = new StringBuilder(lines[0]);
86 for(int i = 1; i < lines.length; i++) {
87 bob.append("\n").append(lines[i]);
88 }
89 return bob.toString();
90 }
91
92 /**
93 * Utility method for taking a string and turning it into an array of lines. This method is used in order to deal
94 * with line endings consistently.
95 * @param text Text to split
96 * @return Array of strings that forms the lines of the original string
97 */
98 protected String[] splitIntoMultipleLines(String text) {
99 return text.replace("\r", "").split("\n");
100 }
101
102 /**
103 * Returns the area, in terminal columns and rows, required to fully draw the lines passed in.
104 * @param lines Lines to measure the size of
105 * @param currentBounds Optional (can pass {@code null}) terminal size to use for storing the output values. If the
106 * method is called many times and always returning the same value, passing in an external
107 * reference of this size will avoid creating new {@code TerminalSize} objects every time
108 * @return Size that is required to draw the lines
109 */
110 protected TerminalSize getBounds(String[] lines, TerminalSize currentBounds) {
111 if(currentBounds == null) {
112 currentBounds = TerminalSize.ZERO;
113 }
114 currentBounds = currentBounds.withRows(lines.length);
115 if(labelWidth == null || labelWidth == 0) {
116 int preferredWidth = 0;
117 for(String line : lines) {
118 int lineWidth = TerminalTextUtils.getColumnWidth(line);
119 if(preferredWidth < lineWidth) {
120 preferredWidth = lineWidth;
121 }
122 }
123 currentBounds = currentBounds.withColumns(preferredWidth);
124 }
125 else {
126 List<String> wordWrapped = TerminalTextUtils.getWordWrappedText(labelWidth, lines);
127 currentBounds = currentBounds.withColumns(labelWidth).withRows(wordWrapped.size());
128 }
129 return currentBounds;
130 }
131
132 /**
133 * Overrides the current theme's foreground color and use the one specified. If called with {@code null}, the
134 * override is cleared and the theme is used again.
135 * @param foregroundColor Foreground color to use when drawing the label, if {@code null} then use the theme's
136 * default
137 * @return Itself
138 */
139 public synchronized Label setForegroundColor(TextColor foregroundColor) {
140 this.foregroundColor = foregroundColor;
141 return this;
142 }
143
144 /**
145 * Returns the foreground color used when drawing the label, or {@code null} if the color is read from the current
146 * theme.
147 * @return Foreground color used when drawing the label, or {@code null} if the color is read from the current
148 * theme.
149 */
150 public TextColor getForegroundColor() {
151 return foregroundColor;
152 }
153
154 /**
155 * Overrides the current theme's background color and use the one specified. If called with {@code null}, the
156 * override is cleared and the theme is used again.
157 * @param backgroundColor Background color to use when drawing the label, if {@code null} then use the theme's
158 * default
159 * @return Itself
160 */
161 public synchronized Label setBackgroundColor(TextColor backgroundColor) {
162 this.backgroundColor = backgroundColor;
163 return this;
164 }
165
166 /**
167 * Returns the background color used when drawing the label, or {@code null} if the color is read from the current
168 * theme.
169 * @return Background color used when drawing the label, or {@code null} if the color is read from the current
170 * theme.
171 */
172 public TextColor getBackgroundColor() {
173 return backgroundColor;
174 }
175
176 /**
177 * Adds an additional SGR style to use when drawing the label, in case it wasn't enabled by the theme
178 * @param sgr SGR style to enable for this label
179 * @return Itself
180 */
181 public synchronized Label addStyle(SGR sgr) {
182 additionalStyles.add(sgr);
183 return this;
184 }
185
186 /**
187 * Removes an additional SGR style used when drawing the label, previously added by {@code addStyle(..)}. If the
188 * style you are trying to remove is specified by the theme, calling this method will have no effect.
189 * @param sgr SGR style to remove
190 * @return Itself
191 */
192 public synchronized Label removeStyle(SGR sgr) {
193 additionalStyles.remove(sgr);
194 return this;
195 }
196
197 /**
198 * Use this method to limit how wide the label can grow. If set to {@code null} there is no limit but if set to a
199 * positive integer then the preferred size will be calculated using word wrapping for lines that are longer than
200 * this label width. This may make the label increase in height as new rows may be requested. Please note that some
201 * layout managers might assign more space to the label and because of this the wrapping might not be as you expect
202 * it. If set to 0, the label will request the same space as if set to {@code null}, but when drawing it will apply
203 * word wrapping instead of truncation in order to fit the label inside the designated area if it's smaller than
204 * what was requested. By default this is set to 0.
205 *
206 * @param labelWidth Either {@code null} or 0 for no limit on how wide the label can be, where 0 indicates word
207 * wrapping should be used if the assigned area is smaller than the requested size, or a positive
208 * integer setting the requested maximum width at what point word wrapping will begin
209 * @return Itself
210 */
211 public synchronized Label setLabelWidth(Integer labelWidth) {
212 this.labelWidth = labelWidth;
213 return this;
214 }
215
216 /**
217 * Returns the limit how wide the label can grow. If set to {@code null} or 0 there is no limit but if set to a
218 * positive integer then the preferred size will be calculated using word wrapping for lines that are longer than
219 * the label width. This may make the label increase in height as new rows may be requested. Please note that some
220 * layout managers might assign more space to the label and because of this the wrapping might not be as you expect
221 * it. If set to 0, the label will request the same space as if set to {@code null}, but when drawing it will apply
222 * word wrapping instead of truncation in order to fit the label inside the designated area if it's smaller than
223 * what was requested.
224 * @return Either {@code null} or 0 for no limit on how wide the label can be, where 0 indicates word
225 * wrapping should be used if the assigned area is smaller than the requested size, or a positive
226 * integer setting the requested maximum width at what point word wrapping will begin
227 */
228 public Integer getLabelWidth() {
229 return labelWidth;
230 }
231
232 @Override
233 protected ComponentRenderer<Label> createDefaultRenderer() {
234 return new ComponentRenderer<Label>() {
235 @Override
236 public TerminalSize getPreferredSize(Label Label) {
237 return labelSize;
238 }
239
240 @Override
241 public void drawComponent(TextGUIGraphics graphics, Label component) {
242 ThemeDefinition themeDefinition = graphics.getThemeDefinition(Label.class);
243 graphics.applyThemeStyle(themeDefinition.getNormal());
244 if(foregroundColor != null) {
245 graphics.setForegroundColor(foregroundColor);
246 }
247 if(backgroundColor != null) {
248 graphics.setBackgroundColor(backgroundColor);
249 }
250 for(SGR sgr: additionalStyles) {
251 graphics.enableModifiers(sgr);
252 }
253
254 String[] linesToDraw;
255 if(component.getLabelWidth() == null) {
256 linesToDraw = component.lines;
257 }
258 else {
259 linesToDraw = TerminalTextUtils.getWordWrappedText(graphics.getSize().getColumns(), component.lines).toArray(new String[0]);
260 }
261
262 for(int row = 0; row < Math.min(graphics.getSize().getRows(), linesToDraw.length); row++) {
263 String line = linesToDraw[row];
264 if(graphics.getSize().getColumns() >= labelSize.getColumns()) {
265 graphics.putString(0, row, line);
266 }
267 else {
268 int availableColumns = graphics.getSize().getColumns();
269 String fitString = TerminalTextUtils.fitString(line, availableColumns);
270 graphics.putString(0, row, fitString);
271 }
272 }
273 }
274 };
275 }
276}