1 /***********************************************************************
2 created: Sun Jun 19 2005
3 author: Paul D Turner <paul@cegui.org.uk>
4 *************************************************************************/
5 /***************************************************************************
6 * Copyright (C) 2004 - 2012 Paul D Turner & The CEGUI Development Team
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 ***************************************************************************/
27 #include "CEGUI/falagard/TextComponent.h"
28 #include "CEGUI/falagard/XMLEnumHelper.h"
29 #include "CEGUI/falagard/XMLHandler.h"
30 #include "CEGUI/FontManager.h"
31 #include "CEGUI/Exceptions.h"
32 #include "CEGUI/PropertyHelper.h"
33 #include "CEGUI/Font.h"
34 #include "CEGUI/LeftAlignedRenderedString.h"
35 #include "CEGUI/RightAlignedRenderedString.h"
36 #include "CEGUI/CentredRenderedString.h"
37 #include "CEGUI/JustifiedRenderedString.h"
38 #include "CEGUI/RenderedStringWordWrapper.h"
39 #include <iostream>
40
41 #if defined (CEGUI_USE_FRIBIDI)
42 #include "CEGUI/FribidiVisualMapping.h"
43 #elif defined (CEGUI_USE_MINIBIDI)
44 #include "CEGUI/MinibidiVisualMapping.h"
45 #else
46 #include "CEGUI/BidiVisualMapping.h"
47 #endif
48
49 // Start of CEGUI namespace section
50 namespace CEGUI
51 {
TextComponent()52 TextComponent::TextComponent() :
53 #ifndef CEGUI_BIDI_SUPPORT
54 d_bidiVisualMapping(0),
55 #elif defined (CEGUI_USE_FRIBIDI)
56 d_bidiVisualMapping(CEGUI_NEW_AO FribidiVisualMapping),
57 #elif defined (CEGUI_USE_MINIBIDI)
58 d_bidiVisualMapping(CEGUI_NEW_AO MinibidiVisualMapping),
59 #else
60 #error "BIDI Configuration is inconsistant, check your config!"
61 #endif
62 d_bidiDataValid(false),
63 d_formattedRenderedString(CEGUI_NEW_AO LeftAlignedRenderedString(d_renderedString)),
64 d_lastHorzFormatting(HTF_LEFT_ALIGNED),
65 d_vertFormatting(VTF_TOP_ALIGNED),
66 d_horzFormatting(HTF_LEFT_ALIGNED)
67 {}
68
~TextComponent()69 TextComponent::~TextComponent()
70 {
71 CEGUI_DELETE_AO d_bidiVisualMapping;
72 }
73
TextComponent(const TextComponent & obj)74 TextComponent::TextComponent(const TextComponent& obj) :
75 FalagardComponentBase(obj),
76 d_textLogical(obj.d_textLogical),
77 #ifndef CEGUI_BIDI_SUPPORT
78 d_bidiVisualMapping(0),
79 #elif defined (CEGUI_USE_FRIBIDI)
80 d_bidiVisualMapping(CEGUI_NEW_AO FribidiVisualMapping),
81 #elif defined (CEGUI_USE_MINIBIDI)
82 d_bidiVisualMapping(CEGUI_NEW_AO MinibidiVisualMapping),
83 #endif
84 d_bidiDataValid(false),
85 d_renderedString(obj.d_renderedString),
86 d_formattedRenderedString(obj.d_formattedRenderedString),
87 d_lastHorzFormatting(obj.d_lastHorzFormatting),
88 d_font(obj.d_font),
89 d_vertFormatting(obj.d_vertFormatting),
90 d_horzFormatting(obj.d_horzFormatting),
91 d_textPropertyName(obj.d_textPropertyName),
92 d_fontPropertyName(obj.d_fontPropertyName)
93 {
94 }
95
operator =(const TextComponent & other)96 TextComponent& TextComponent::operator=(const TextComponent& other)
97 {
98 if (this == &other)
99 return *this;
100
101 FalagardComponentBase::operator=(other);
102
103 d_textLogical = other.d_textLogical;
104 // note we do not assign the BidiVisualMapping object, we just mark our
105 // existing one as invalid so it's data gets regenerated next time it's
106 // needed.
107 d_bidiDataValid = false;
108 d_renderedString = other.d_renderedString;
109 d_formattedRenderedString = other.d_formattedRenderedString;
110 d_lastHorzFormatting = other.d_lastHorzFormatting;
111 d_font = other.d_font;
112 d_vertFormatting = other.d_vertFormatting;
113 d_horzFormatting = other.d_horzFormatting;
114 d_textPropertyName = other.d_textPropertyName;
115 d_fontPropertyName = other.d_fontPropertyName;
116
117 return *this;
118 }
119
getText() const120 const String& TextComponent::getText() const
121 {
122 return d_textLogical;
123 }
124
setText(const String & text)125 void TextComponent::setText(const String& text)
126 {
127 d_textLogical = text;
128 d_bidiDataValid = false;
129 }
130
getFont() const131 const String& TextComponent::getFont() const
132 {
133 return d_font;
134 }
135
setFont(const String & font)136 void TextComponent::setFont(const String& font)
137 {
138 d_font = font;
139 }
140
getVerticalFormatting(const Window & wnd) const141 VerticalTextFormatting TextComponent::getVerticalFormatting(const Window& wnd) const
142 {
143 return d_vertFormatting.get(wnd);
144 }
145
getVerticalFormattingFromComponent() const146 VerticalTextFormatting TextComponent::getVerticalFormattingFromComponent() const
147 {
148 return d_vertFormatting.getValue();
149 }
150
setVerticalFormatting(VerticalTextFormatting fmt)151 void TextComponent::setVerticalFormatting(VerticalTextFormatting fmt)
152 {
153 d_vertFormatting.set(fmt);
154 }
155
getHorizontalFormatting(const Window & wnd) const156 HorizontalTextFormatting TextComponent::getHorizontalFormatting(const Window& wnd) const
157 {
158 return d_horzFormatting.get(wnd);
159 }
160
getHorizontalFormattingFromComponent() const161 HorizontalTextFormatting TextComponent::getHorizontalFormattingFromComponent() const
162 {
163 return d_horzFormatting.getValue();
164 }
165
setHorizontalFormatting(HorizontalTextFormatting fmt)166 void TextComponent::setHorizontalFormatting(HorizontalTextFormatting fmt)
167 {
168 d_horzFormatting.set(fmt);
169 }
170
getHorizontalFormattingPropertySource() const171 const String& TextComponent::getHorizontalFormattingPropertySource() const
172 {
173 return d_horzFormatting.getPropertySource();
174 }
175
setHorizontalFormattingPropertySource(const String & property_name)176 void TextComponent::setHorizontalFormattingPropertySource(
177 const String& property_name)
178 {
179 d_horzFormatting.setPropertySource(property_name);
180 }
181
getVerticalFormattingPropertySource() const182 const String& TextComponent::getVerticalFormattingPropertySource() const
183 {
184 return d_vertFormatting.getPropertySource();
185 }
186
setVerticalFormattingPropertySource(const String & property_name)187 void TextComponent::setVerticalFormattingPropertySource(
188 const String& property_name)
189 {
190 d_vertFormatting.setPropertySource(property_name);
191 }
192
setupStringFormatter(const Window & window,const RenderedString & rendered_string) const193 void TextComponent::setupStringFormatter(const Window& window,
194 const RenderedString& rendered_string) const
195 {
196 const HorizontalTextFormatting horzFormatting = d_horzFormatting.get(window);
197
198 // no formatting change
199 if (horzFormatting == d_lastHorzFormatting)
200 {
201 d_formattedRenderedString->setRenderedString(rendered_string);
202 return;
203 }
204
205 d_lastHorzFormatting = horzFormatting;
206
207 switch(horzFormatting)
208 {
209 case HTF_LEFT_ALIGNED:
210 d_formattedRenderedString =
211 CEGUI_NEW_AO LeftAlignedRenderedString(rendered_string);
212 break;
213
214 case HTF_CENTRE_ALIGNED:
215 d_formattedRenderedString =
216 CEGUI_NEW_AO CentredRenderedString(rendered_string);
217 break;
218
219 case HTF_RIGHT_ALIGNED:
220 d_formattedRenderedString =
221 CEGUI_NEW_AO RightAlignedRenderedString(rendered_string);
222 break;
223
224 case HTF_JUSTIFIED:
225 d_formattedRenderedString =
226 CEGUI_NEW_AO JustifiedRenderedString(rendered_string);
227 break;
228
229 case HTF_WORDWRAP_LEFT_ALIGNED:
230 d_formattedRenderedString =
231 CEGUI_NEW_AO RenderedStringWordWrapper
232 <LeftAlignedRenderedString>(rendered_string);
233 break;
234
235 case HTF_WORDWRAP_CENTRE_ALIGNED:
236 d_formattedRenderedString =
237 CEGUI_NEW_AO RenderedStringWordWrapper
238 <CentredRenderedString>(rendered_string);
239 break;
240
241 case HTF_WORDWRAP_RIGHT_ALIGNED:
242 d_formattedRenderedString =
243 CEGUI_NEW_AO RenderedStringWordWrapper
244 <RightAlignedRenderedString>(rendered_string);
245 break;
246
247 case HTF_WORDWRAP_JUSTIFIED:
248 d_formattedRenderedString =
249 CEGUI_NEW_AO RenderedStringWordWrapper
250 <JustifiedRenderedString>(rendered_string);
251 break;
252 }
253 }
254
render_impl(Window & srcWindow,Rectf & destRect,const CEGUI::ColourRect * modColours,const Rectf * clipper,bool) const255 void TextComponent::render_impl(Window& srcWindow, Rectf& destRect,
256 const CEGUI::ColourRect* modColours, const Rectf* clipper,
257 bool /*clipToDisplay*/) const
258 {
259
260 CEGUI_TRY
261 {
262 updateFormatting(srcWindow, destRect.getSize());
263 }
264 CEGUI_CATCH(...)
265 {
266 return;
267 }
268
269 // Get total formatted height.
270 const float textHeight = d_formattedRenderedString->getVerticalExtent(&srcWindow);
271
272 // handle dest area adjustments for vertical formatting.
273 const VerticalTextFormatting vertFormatting = d_vertFormatting.get(srcWindow);
274
275 switch(vertFormatting)
276 {
277 case VTF_CENTRE_ALIGNED:
278 destRect.d_min.d_y += (destRect.getHeight() - textHeight) * 0.5f;
279 break;
280
281 case VTF_BOTTOM_ALIGNED:
282 destRect.d_min.d_y = destRect.d_max.d_y - textHeight;
283 break;
284
285 default:
286 // default is VTF_TOP_ALIGNED, for which we take no action.
287 break;
288 }
289
290 // calculate final colours to be used
291 ColourRect finalColours;
292 initColoursRect(srcWindow, modColours, finalColours);
293
294 // add geometry for text to the target window.
295 d_formattedRenderedString->draw(&srcWindow, srcWindow.getGeometryBuffer(),
296 destRect.getPosition(),
297 &finalColours, clipper);
298 }
299
getFontObject(const Window & window) const300 const Font* TextComponent::getFontObject(const Window& window) const
301 {
302 CEGUI_TRY
303 {
304 return d_fontPropertyName.empty() ?
305 (d_font.empty() ? window.getFont() : &FontManager::getSingleton().get(d_font))
306 : &FontManager::getSingleton().get(window.getProperty(d_fontPropertyName));
307 }
308 CEGUI_CATCH (UnknownObjectException&)
309 {
310 return 0;
311 }
312 }
313
writeXMLToStream(XMLSerializer & xml_stream) const314 void TextComponent::writeXMLToStream(XMLSerializer& xml_stream) const
315 {
316 // opening tag
317 xml_stream.openTag(Falagard_xmlHandler::TextComponentElement);
318 // write out area
319 d_area.writeXMLToStream(xml_stream);
320
321 // write text element
322 if (!d_font.empty() || !getText().empty())
323 {
324 xml_stream.openTag(Falagard_xmlHandler::TextElement);
325 if (!d_font.empty())
326 xml_stream.attribute(Falagard_xmlHandler::FontAttribute, d_font);
327 if (!getText().empty())
328 xml_stream.attribute(Falagard_xmlHandler::StringAttribute, getText());
329 xml_stream.closeTag();
330 }
331
332 // write text property element
333 if (!d_textPropertyName.empty())
334 {
335 xml_stream.openTag(Falagard_xmlHandler::TextPropertyElement)
336 .attribute(Falagard_xmlHandler::NameAttribute, d_textPropertyName)
337 .closeTag();
338 }
339
340 // write font property element
341 if (!d_fontPropertyName.empty())
342 {
343 xml_stream.openTag(Falagard_xmlHandler::FontPropertyElement)
344 .attribute(Falagard_xmlHandler::NameAttribute, d_fontPropertyName)
345 .closeTag();
346 }
347
348 // get base class to write colours
349 writeColoursXML(xml_stream);
350
351 d_vertFormatting.writeXMLToStream(xml_stream);
352 d_horzFormatting.writeXMLToStream(xml_stream);
353
354 // closing tag
355 xml_stream.closeTag();
356 }
357
isTextFetchedFromProperty() const358 bool TextComponent::isTextFetchedFromProperty() const
359 {
360 return !d_textPropertyName.empty();
361 }
362
getTextPropertySource() const363 const String& TextComponent::getTextPropertySource() const
364 {
365 return d_textPropertyName;
366 }
367
setTextPropertySource(const String & property)368 void TextComponent::setTextPropertySource(const String& property)
369 {
370 d_textPropertyName = property;
371 }
372
isFontFetchedFromProperty() const373 bool TextComponent::isFontFetchedFromProperty() const
374 {
375 return !d_fontPropertyName.empty();
376 }
377
getFontPropertySource() const378 const String& TextComponent::getFontPropertySource() const
379 {
380 return d_fontPropertyName;
381 }
382
setFontPropertySource(const String & property)383 void TextComponent::setFontPropertySource(const String& property)
384 {
385 d_fontPropertyName = property;
386 }
387
getTextVisual() const388 const String& TextComponent::getTextVisual() const
389 {
390 // no bidi support
391 if (!d_bidiVisualMapping)
392 return d_textLogical;
393
394 if (!d_bidiDataValid)
395 {
396 d_bidiVisualMapping->updateVisual(d_textLogical);
397 d_bidiDataValid = true;
398 }
399
400 return d_bidiVisualMapping->getTextVisual();
401 }
402
getHorizontalTextExtent(const Window & window) const403 float TextComponent::getHorizontalTextExtent(const Window& window) const
404 {
405 updateFormatting(window);
406 return d_formattedRenderedString->getHorizontalExtent(&window);
407 }
408
getVerticalTextExtent(const Window & window) const409 float TextComponent::getVerticalTextExtent(const Window& window) const
410 {
411 updateFormatting(window);
412 return d_formattedRenderedString->getVerticalExtent(&window);
413 }
414
handleFontRenderSizeChange(Window & window,const Font * font) const415 bool TextComponent::handleFontRenderSizeChange(Window& window,
416 const Font* font) const
417 {
418 const bool res =
419 FalagardComponentBase::handleFontRenderSizeChange(window, font);
420
421 if (font == getFontObject(window))
422 {
423 window.invalidate();
424 return true;
425 }
426
427 return res;
428 }
429
430 //------------------------------------------------------------------------//
updateFormatting(const Window & srcWindow) const431 void TextComponent::updateFormatting(const Window& srcWindow) const
432 {
433 updateFormatting(srcWindow, d_area.getPixelRect(srcWindow).getSize());
434 }
435
436 //------------------------------------------------------------------------//
updateFormatting(const Window & srcWindow,const Sizef & size) const437 void TextComponent::updateFormatting(
438 const Window& srcWindow, const Sizef& size) const
439 {
440
441 const Font* font = getFontObject(srcWindow);
442
443 // exit if we have no font to use.
444 if (!font)
445 CEGUI_THROW(InvalidRequestException("Window doesn't have a font."));
446
447 const RenderedString* rs = &d_renderedString;
448 // do we fetch text from a property
449 if (!d_textPropertyName.empty())
450 {
451 // fetch text & do bi-directional reordering as needed
452 String vis;
453 #ifdef CEGUI_BIDI_SUPPORT
454 BidiVisualMapping::StrIndexList l2v, v2l;
455 d_bidiVisualMapping->reorderFromLogicalToVisual(
456 srcWindow.getProperty(d_textPropertyName), vis, l2v, v2l);
457 #else
458 vis = srcWindow.getProperty(d_textPropertyName);
459 #endif
460 // parse string using parser from Window.
461 d_renderedString =
462 srcWindow.getRenderedStringParser().parse(vis, font, 0);
463 }
464 // do we use a static text string from the looknfeel
465 else if (!getTextVisual().empty())
466 // parse string using parser from Window.
467 d_renderedString = srcWindow.getRenderedStringParser().
468 parse(getTextVisual(), font, 0);
469 // do we have to override the font?
470 else if (font != srcWindow.getFont())
471 d_renderedString = srcWindow.getRenderedStringParser().
472 parse(srcWindow.getTextVisual(), font, 0);
473 // use ready-made RenderedString from the Window itself
474 else
475 rs = &srcWindow.getRenderedString();
476
477 setupStringFormatter(srcWindow, *rs);
478 d_formattedRenderedString->format(&srcWindow, size);
479
480 }
481
482 //----------------------------------------------------------------------------//
getEffectiveText(const Window & wnd) const483 String TextComponent::getEffectiveText(const Window& wnd) const
484 {
485 if (!d_textPropertyName.empty())
486 return wnd.getProperty(d_textPropertyName);
487 else if (d_textLogical.empty())
488 return wnd.getText();
489 else
490 return d_textLogical;
491 }
492
493 //----------------------------------------------------------------------------//
getEffectiveVisualText(const Window & wnd) const494 String TextComponent::getEffectiveVisualText(const Window& wnd) const
495 {
496 #ifndef CEGUI_BIDI_SUPPORT
497 return getEffectiveText(wnd);
498 #else
499 if (!d_textPropertyName.empty())
500 {
501 String visual;
502 BidiVisualMapping::StrIndexList l2v, v2l;
503 d_bidiVisualMapping->reorderFromLogicalToVisual(
504 wnd.getProperty(d_textPropertyName), visual, l2v, v2l);
505
506 return visual;
507 }
508 // do we use a static text string from the looknfeel
509 else if (d_textLogical.empty())
510 return wnd.getTextVisual();
511 else
512 getTextVisual();
513 #endif
514 }
515
516 //----------------------------------------------------------------------------//
getEffectiveFont(const Window & wnd) const517 String TextComponent::getEffectiveFont(const Window& wnd) const
518 {
519 if (!d_fontPropertyName.empty())
520 return wnd.getProperty(d_fontPropertyName);
521 else if (d_font.empty())
522 {
523 if (const Font* font = wnd.getFont())
524 return font->getName();
525 else
526 return String();
527 }
528 else
529 return d_font;
530 }
531
532 //----------------------------------------------------------------------------//
533
534 } // End of CEGUI namespace section
535