1 /***************************************************************************
2 * Mechanized Assault and Exploration Reloaded Projectfile *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 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 General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
19
20 #include "ui/graphical/menu/widgets/label.h"
21 #include "utility/drawing.h"
22
23 //------------------------------------------------------------------------------
cLabel(const cBox<cPosition> & area,const std::string & text_,eUnicodeFontType fontType_,AlignmentFlags alignment_)24 cLabel::cLabel (const cBox<cPosition>& area, const std::string& text_, eUnicodeFontType fontType_, AlignmentFlags alignment_) :
25 cWidget (area),
26 fontType (fontType_),
27 alignment (alignment_),
28 wordWrap (false)
29 {
30 if (getSize().x() < 0 || getSize().y() < 0)
31 {
32 surface = nullptr;
33 }
34 else
35 {
36 surface = AutoSurface (SDL_CreateRGBSurface (0, getSize().x(), getSize().y(), 32, 0, 0, 0, 0));
37 SDL_FillRect (surface.get(), nullptr, 0xFF00FF);
38 SDL_SetColorKey (surface.get(), SDL_TRUE, 0xFF00FF);
39 }
40
41 setText (text_);
42 }
43
44 //------------------------------------------------------------------------------
setText(const std::string & text_)45 void cLabel::setText (const std::string& text_)
46 {
47 text = text_;
48
49 // NOTE: do we really want to do this here?
50 // we replace the character sequence "\n" by the escape sequence '\n'
51 // because e.g. when reading translation strings, this is used to
52 // indicate line breaks.
53 // May move this directly to @ref cLanguage and make it more robust
54 // (i.e. really parsing escape sequence, so that "\\n" will result in
55 // "\n" and not in "\" followed by '\n')
56 size_t pos = 0;
57 while ((pos = text.find ("\\n", pos)) != std::string::npos)
58 {
59 text.replace (pos, 2, "\n");
60 pos += 1;
61 }
62
63 updateDisplayInformation();
64 }
65
66 //------------------------------------------------------------------------------
getText() const67 const std::string& cLabel::getText() const
68 {
69 return text;
70 }
71
72 //------------------------------------------------------------------------------
setFont(eUnicodeFontType fontType_)73 void cLabel::setFont (eUnicodeFontType fontType_)
74 {
75 std::swap (fontType, fontType_);
76 if (fontType != fontType_) updateDisplayInformation();
77 }
78
79 //------------------------------------------------------------------------------
setAlignment(AlignmentFlags alignment_)80 void cLabel::setAlignment (AlignmentFlags alignment_)
81 {
82 std::swap (alignment, alignment_);
83 if (alignment != alignment_) updateDisplayInformation();
84 }
85
86 //------------------------------------------------------------------------------
setWordWrap(bool wordWrap_)87 void cLabel::setWordWrap (bool wordWrap_)
88 {
89 std::swap (wordWrap, wordWrap_);
90 if (wordWrap != wordWrap_) updateDisplayInformation();
91 }
92
93 //------------------------------------------------------------------------------
resizeToTextHeight()94 void cLabel::resizeToTextHeight()
95 {
96 const auto textHeight = drawLines.size() * font->getFontHeight (fontType);
97 resize (cPosition (getSize().x(), textHeight));
98 }
99
100 //------------------------------------------------------------------------------
breakText(const std::string & text,std::vector<std::string> & lines,int maximalWidth,eUnicodeFontType fontType) const101 void cLabel::breakText (const std::string& text, std::vector<std::string>& lines, int maximalWidth, eUnicodeFontType fontType) const
102 {
103 // NOTE: better would be not to copy each line into the vector
104 // but use something like "string_view". We could simulate this by using
105 // a pair of iterators (like a range) but non of other methods would support such a
106 // rand and therefore the construction of a new string object would be necessary anyway.
107
108 int currentLineLength = 0;
109 int currentWordLength = 0;
110
111 lines.push_back ("");
112
113 auto it = text.begin();
114 auto nextWordBegin = it;
115 while (true)
116 {
117 auto& currentLine = lines.back();
118
119 if (it == text.end() || font->isUtf8Space (& (*it)))
120 {
121 if (currentLineLength + currentWordLength >= maximalWidth || (it != text.end() && *it == '\n'))
122 {
123 if (currentLineLength + currentWordLength >= maximalWidth)
124 {
125 // Remove all leading white spaces
126 while (nextWordBegin != it && font->isUtf8Space (& (*nextWordBegin)))
127 {
128 int increase;
129 auto unicodeCharacter = font->encodeUTF8Char (& (*nextWordBegin), increase);
130 currentWordLength -= font->getUnicodeCharacterWidth (unicodeCharacter, fontType);
131 nextWordBegin += increase;
132 }
133
134 // TODO: may break the word when the single word is to long for the line
135 // put the word into the next line
136 lines.push_back (std::string (nextWordBegin, it));
137 currentLineLength = currentWordLength;
138 }
139 else
140 {
141 currentLine.append (nextWordBegin, it);
142 lines.push_back ("");
143 currentLineLength = 0;
144 }
145 }
146 else
147 {
148 if (currentLine.empty())
149 {
150 // Remove all leading white spaces if we are at the beginning of a new line
151 while (nextWordBegin != it && font->isUtf8Space (& (*nextWordBegin)))
152 {
153 int increase;
154 auto unicodeCharacter = font->encodeUTF8Char (& (*nextWordBegin), increase);
155 currentWordLength -= font->getUnicodeCharacterWidth (unicodeCharacter, fontType);
156 nextWordBegin += increase;
157 }
158 }
159 currentLine.append (nextWordBegin, it);
160 currentLineLength += currentWordLength;
161 }
162
163 if (it == text.end()) break;
164
165 nextWordBegin = it;
166 currentWordLength = 0;
167 }
168
169 int increase;
170 auto unicodeCharacter = font->encodeUTF8Char (& (*it), increase);
171 currentWordLength += font->getUnicodeCharacterWidth (unicodeCharacter, fontType);
172
173 it += increase;
174 }
175 }
176
177 //------------------------------------------------------------------------------
updateDisplayInformation()178 void cLabel::updateDisplayInformation()
179 {
180 if (surface == nullptr) return;
181
182 drawLines.clear();
183
184 if (wordWrap)
185 {
186 breakText (text, drawLines, getSize().x(), fontType);
187 }
188 else
189 {
190 drawLines.push_back (text);
191 }
192
193 SDL_FillRect (surface.get(), nullptr, 0xFF00FF);
194
195 const auto height = font->getFontHeight (fontType) * drawLines.size();
196
197 int drawPositionY;
198 if (alignment & eAlignmentType::Bottom)
199 {
200 drawPositionY = getSize().y() - height;
201 }
202 else if (alignment & eAlignmentType::CenterVerical)
203 {
204 drawPositionY = getSize().y() / 2 - height / 2;
205 }
206 else
207 {
208 drawPositionY = 0;
209 }
210
211 auto originalTargetSurface = font->getTargetSurface();
212 auto fontTargetSurfaceResetter = makeScopedOperation ([originalTargetSurface]() { font->setTargetSurface (originalTargetSurface); });
213 font->setTargetSurface (surface.get());
214 for (size_t i = 0; i < drawLines.size(); ++i)
215 {
216 const auto& line = drawLines[i];
217
218 const auto width = font->getTextWide (line, fontType);
219
220 int drawPositionX;
221 if (alignment & eAlignmentType::Right)
222 {
223 drawPositionX = getSize().x() - width;
224 }
225 else if (alignment & eAlignmentType::CenterHorizontal)
226 {
227 drawPositionX = getSize().x() / 2 - width / 2;
228 }
229 else
230 {
231 drawPositionX = 0;
232 }
233
234 font->showText (drawPositionX, drawPositionY, line, fontType);
235
236 drawPositionY += font->getFontHeight (fontType);
237 }
238 }
239
240 //------------------------------------------------------------------------------
draw(SDL_Surface & destination,const cBox<cPosition> & clipRect)241 void cLabel::draw (SDL_Surface& destination, const cBox<cPosition>& clipRect)
242 {
243 if (surface && getArea().intersects (clipRect))
244 {
245 blitClipped (*surface, getArea(), destination, clipRect);
246 }
247
248 cWidget::draw (destination, clipRect);
249 }
250
251 //------------------------------------------------------------------------------
handleResized(const cPosition & oldSize)252 void cLabel::handleResized (const cPosition& oldSize)
253 {
254 cWidget::handleResized (oldSize);
255
256 if (getSize().x() < 0 || getSize().y() < 0)
257 {
258 surface = nullptr;
259 return;
260 }
261
262 surface = AutoSurface (SDL_CreateRGBSurface (0, getSize().x(), getSize().y(), 32, 0, 0, 0, 0));
263 SDL_FillRect (surface.get(), nullptr, 0xFF00FF);
264 SDL_SetColorKey (surface.get(), SDL_TRUE, 0xFF00FF);
265
266 updateDisplayInformation();
267 }
268