]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/elements/text.cxx
Fix a Clang warning in Shiva.
[flightgear.git] / src / Canvas / elements / text.cxx
1 // A text on the canvas
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19 #include "text.hxx"
20 #include <Canvas/property_helper.hxx>
21
22 #include <Main/globals.hxx>
23 #include <Main/fg_props.hxx>
24
25 #include <osgText/Text>
26
27 namespace canvas
28 {
29   class Text::TextOSG:
30     public osgText::Text
31   {
32     public:
33
34       void setCharacterAspect(float aspect);
35       void setFill(const std::string& fill);
36       void setBackgroundColor(const std::string& fill);
37
38       osg::Vec2 handleHit(float x, float y);
39
40       virtual osg::BoundingBox computeBound() const;
41   };
42
43   //----------------------------------------------------------------------------
44   void Text::TextOSG::setCharacterAspect(float aspect)
45   {
46     setCharacterSize(getCharacterHeight(), aspect);
47   }
48
49   //----------------------------------------------------------------------------
50   void Text::TextOSG::setFill(const std::string& fill)
51   {
52 //    if( fill == "none" )
53 //      TODO No text
54 //    else
55       setColor( parseColor(fill) );
56   }
57
58   //----------------------------------------------------------------------------
59   void Text::TextOSG::setBackgroundColor(const std::string& fill)
60   {
61     setBoundingBoxColor( parseColor(fill) );
62   }
63
64   //----------------------------------------------------------------------------
65   osg::Vec2 Text::TextOSG::handleHit(float x, float y)
66   {
67     float line_height = _characterHeight + _lineSpacing;
68
69     // TODO check with align other than TOP
70     float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
71     size_t line = std::max<int>(0, (y - first_line_y) / line_height);
72
73     if( _textureGlyphQuadMap.empty() )
74       return osg::Vec2(-1, -1);
75
76     // TODO check when it can be larger
77     assert( _textureGlyphQuadMap.size() == 1 );
78
79     const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
80     const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
81     const GlyphQuads::Coords2& coords = glyphquad._coords;
82     const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
83
84     const float HIT_FRACTION = 0.6;
85     const float character_width = getCharacterHeight()
86                                 * getCharacterAspectRatio();
87
88     y = (line + 0.5) * line_height;
89
90     bool line_found = false;
91     for(size_t i = 0; i < line_numbers.size(); ++i)
92     {
93       if( line_numbers[i] != line )
94       {
95         if( !line_found )
96         {
97           if( line_numbers[i] < line )
98             // Wait for the correct line...
99             continue;
100
101           // We have already passed the correct line -> It's empty...
102           return osg::Vec2(0, y);
103         }
104
105         // Next line and not returned -> not before any character
106         // -> return position after last character of line
107         return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
108       }
109
110       line_found = true;
111
112       // Get threshold for mouse x position for setting cursor before or after
113       // current character
114       float threshold = coords[i * 4].x()
115                       + HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
116                                      * character_width;
117
118       if( x <= threshold )
119       {
120         if( i == 0 || line_numbers[i - 1] != line )
121           // first character of line
122           x = coords[i * 4].x();
123         else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
124           // If previous character width is zero set to begin of next character
125           // (Happens eg. with spaces)
126           x = coords[i * 4].x();
127         else
128           // position at center between characters
129           x = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
130
131         return osg::Vec2(x, y);
132       }
133     }
134
135     // Nothing found -> return position after last character
136     return osg::Vec2
137     (
138       coords.back().x(),
139       (_lineCount - 0.5) * line_height
140     );
141   }
142
143   //----------------------------------------------------------------------------
144   osg::BoundingBox Text::TextOSG::computeBound() const
145   {
146     osg::BoundingBox bb = osgText::Text::computeBound();
147     if( !bb.valid() )
148       return bb;
149
150     // TODO bounding box still doesn't seem always right (eg. with center
151     //      horizontal alignment not completely accurate)
152     bb._min.y() += _offset.y();
153     bb._max.y() += _offset.y();
154
155     return bb;
156   }
157
158   //----------------------------------------------------------------------------
159   Text::Text(SGPropertyNode_ptr node, const Style& parent_style):
160     Element(node, parent_style, BOUNDING_BOX),
161     _text( new Text::TextOSG() )
162   {
163     setDrawable(_text);
164     _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
165     _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
166     _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
167
168     addStyle("fill", &TextOSG::setFill, _text);
169     addStyle("background", &TextOSG::setBackgroundColor, _text);
170     addStyle("character-size",
171              static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
172              _text);
173     addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text);
174     addStyle("padding", &TextOSG::setBoundingBoxMargin, _text);
175     //  TEXT              = 1 default
176     //  BOUNDINGBOX       = 2
177     //  FILLEDBOUNDINGBOX = 4
178     //  ALIGNMENT         = 8
179     addStyle<int>("draw-mode", &TextOSG::setDrawMode, _text);
180     addStyle("max-width", &TextOSG::setMaximumWidth, _text);
181     addStyle("font", &Text::setFont, this);
182     addStyle("alignment", &Text::setAlignment, this);
183     addStyle("text", &Text::setText, this);
184
185     setupStyle();
186   }
187
188   //----------------------------------------------------------------------------
189   Text::~Text()
190   {
191
192   }
193
194   //----------------------------------------------------------------------------
195   void Text::setText(const char* text)
196   {
197     _text->setText(text, osgText::String::ENCODING_UTF8);
198   }
199
200   //----------------------------------------------------------------------------
201   void Text::setFont(const char* name)
202   {
203     _text->setFont( getFont(name) );
204   }
205
206   //----------------------------------------------------------------------------
207   void Text::setAlignment(const char* align)
208   {
209     const std::string align_string(align);
210     if( 0 ) return;
211 #define ENUM_MAPPING(enum_val, string_val) \
212     else if( align_string == string_val )\
213       _text->setAlignment( osgText::Text::enum_val );
214 #include "text-alignment.hxx"
215 #undef ENUM_MAPPING
216     else
217     {
218       if( !align_string.empty() )
219         SG_LOG
220         (
221           SG_GENERAL,
222           SG_WARN,
223           "canvas::Text: unknown alignment '" << align_string << "'"
224         );
225       _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
226     }
227   }
228
229   //----------------------------------------------------------------------------
230 #if 0
231   const char* Text::getAlignment() const
232   {
233     switch( _text->getAlignment() )
234     {
235 #define ENUM_MAPPING(enum_val, string_val) \
236       case osgText::Text::enum_val:\
237         return string_val;
238 #include "text-alignment.hxx"
239 #undef ENUM_MAPPING
240       default:
241         return "unknown";
242     }
243   }
244 #endif
245
246   //----------------------------------------------------------------------------
247   void Text::childChanged(SGPropertyNode* child)
248   {
249     if( child->getParent() != _node )
250       return;
251
252     const std::string& name = child->getNameString();
253     if( name == "hit-y" )
254       handleHit
255       (
256         _node->getFloatValue("hit-x"),
257         _node->getFloatValue("hit-y")
258       );
259   }
260
261   //----------------------------------------------------------------------------
262   void Text::handleHit(float x, float y)
263   {
264     const osg::Vec2& pos = _text->handleHit(x, y);
265     _node->setFloatValue("cursor-x", pos.x());
266     _node->setFloatValue("cursor-y", pos.y());
267   }
268
269   //----------------------------------------------------------------------------
270   Text::font_ptr Text::getFont(const std::string& name)
271   {
272     SGPath path = globals->resolve_ressource_path("Fonts/" + name);
273     if( path.isNull() )
274     {
275       SG_LOG
276       (
277         SG_GL,
278         SG_ALERT,
279         "canvas::Text: No such font: " << name
280       );
281       return font_ptr();
282     }
283
284     SG_LOG
285     (
286       SG_GL,
287       SG_INFO,
288       "canvas::Text: using font file " << path.str()
289     );
290
291     font_ptr font = osgText::readFontFile(path.c_str());
292     if( !font )
293       SG_LOG
294       (
295         SG_GL,
296         SG_ALERT,
297         "canvas::Text: Failed to open font file " << path.c_str()
298       );
299
300     return font;
301   }
302
303 } // namespace canvas