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