1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/textmeasurecmn.cpp
3 // Purpose:     wxTextMeasureBase implementation
4 // Author:      Manuel Martin
5 // Created:     2012-10-05
6 // Copyright:   (c) 1997-2012 wxWidgets team
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #ifndef WX_PRECOMP
22     #include "wx/dc.h"
23     #include "wx/window.h"
24 #endif //WX_PRECOMP
25 
26 #include "wx/private/textmeasure.h"
27 
28 // ============================================================================
29 // wxTextMeasureBase implementation
30 // ============================================================================
31 
wxTextMeasureBase(const wxDC * dc,const wxFont * theFont)32 wxTextMeasureBase::wxTextMeasureBase(const wxDC *dc, const wxFont *theFont)
33     : m_dc(dc),
34       m_win(NULL),
35       m_font(theFont)
36 {
37     wxASSERT_MSG( dc, wxS("wxTextMeasure needs a valid wxDC") );
38 
39     // By default, use wxDC version, we'll explicitly reset this to false in
40     // the derived classes if the DC is of native variety.
41     m_useDCImpl = true;
42 }
43 
wxTextMeasureBase(const wxWindow * win,const wxFont * theFont)44 wxTextMeasureBase::wxTextMeasureBase(const wxWindow *win, const wxFont *theFont)
45     : m_dc(NULL),
46       m_win(win),
47       m_font(theFont)
48 {
49     wxASSERT_MSG( win, wxS("wxTextMeasure needs a valid wxWindow") );
50 
51     // We don't have any wxDC so we can't forward to it.
52     m_useDCImpl = false;
53 }
54 
GetFont() const55 wxFont wxTextMeasureBase::GetFont() const
56 {
57     return m_font ? *m_font
58                   : m_win ? m_win->GetFont()
59                           : m_dc->GetFont();
60 }
61 
CallGetTextExtent(const wxString & string,wxCoord * width,wxCoord * height,wxCoord * descent,wxCoord * externalLeading)62 void wxTextMeasureBase::CallGetTextExtent(const wxString& string,
63                                           wxCoord *width,
64                                           wxCoord *height,
65                                           wxCoord *descent,
66                                           wxCoord *externalLeading)
67 {
68     if ( m_useDCImpl )
69         m_dc->GetTextExtent(string, width, height, descent, externalLeading);
70     else
71         DoGetTextExtent(string, width, height, descent, externalLeading);
72 }
73 
GetTextExtent(const wxString & string,wxCoord * width,wxCoord * height,wxCoord * descent,wxCoord * externalLeading)74 void wxTextMeasureBase::GetTextExtent(const wxString& string,
75                                       wxCoord *width,
76                                       wxCoord *height,
77                                       wxCoord *descent,
78                                       wxCoord *externalLeading)
79 {
80     // To make the code simpler, make sure that the width and height pointers
81     // are always valid, even if they point to dummy variables.
82     int unusedWidth, unusedHeight;
83     if ( !width )
84         width = &unusedWidth;
85     if ( !height )
86         height = &unusedHeight;
87 
88     // Avoid even setting up the DC for measuring if we don't actually need to
89     // measure anything.
90     if ( string.empty() && !descent && !externalLeading )
91     {
92         *width =
93         *height = 0;
94 
95         return;
96     }
97 
98     MeasuringGuard guard(*this);
99 
100     CallGetTextExtent(string, width, height, descent, externalLeading);
101 }
102 
GetEmptyLineHeight()103 int wxTextMeasureBase::GetEmptyLineHeight()
104 {
105     int dummy, height;
106     CallGetTextExtent(wxS("W"), &dummy, &height);
107     return height;
108 }
109 
GetMultiLineTextExtent(const wxString & text,wxCoord * width,wxCoord * height,wxCoord * heightOneLine)110 void wxTextMeasureBase::GetMultiLineTextExtent(const wxString& text,
111                                                wxCoord *width,
112                                                wxCoord *height,
113                                                wxCoord *heightOneLine)
114 {
115     // To make the code simpler, make sure that the width and height pointers
116     // are always valid, by making them point to dummy variables if necessary.
117     int unusedWidth, unusedHeight;
118     if ( !width )
119         width = &unusedWidth;
120     if ( !height )
121         height = &unusedHeight;
122 
123     *width = 0;
124     *height = 0;
125 
126     MeasuringGuard guard(*this);
127 
128     // It's noticeably faster to handle the case of a string which isn't
129     // actually multiline specially here, to skip iteration above in this case.
130     if ( text.find('\n') == wxString::npos )
131     {
132         // This case needs to be handled specially as we're supposed to return
133         // a non-zero height even for empty string.
134         if ( text.empty() )
135             *height = GetEmptyLineHeight();
136         else
137             CallGetTextExtent(text, width, height);
138 
139         if ( heightOneLine )
140             *heightOneLine = *height;
141         return;
142     }
143 
144     wxCoord widthLine, heightLine = 0, heightLineDefault = 0;
145 
146     wxString::const_iterator lineStart = text.begin();
147     for ( wxString::const_iterator pc = text.begin(); ; ++pc )
148     {
149         if ( pc == text.end() || *pc == wxS('\n') )
150         {
151             if ( pc == lineStart )
152             {
153                 // we can't use GetTextExtent - it will return 0 for both width
154                 // and height and an empty line should count in height
155                 // calculation
156 
157                 // assume that this line has the same height as the previous
158                 // one
159                 if ( !heightLineDefault )
160                     heightLineDefault = heightLine;
161 
162                 // and if we hadn't had any previous one neither, compute it now
163                 if ( !heightLineDefault )
164                     heightLineDefault = GetEmptyLineHeight();
165 
166                 *height += heightLineDefault;
167             }
168             else
169             {
170                 CallGetTextExtent(wxString(lineStart, pc), &widthLine, &heightLine);
171                 if ( widthLine > *width )
172                     *width = widthLine;
173                 *height += heightLine;
174             }
175 
176             if ( pc == text.end() )
177             {
178                break;
179             }
180             else // '\n'
181             {
182                lineStart = pc;
183                ++lineStart;
184             }
185         }
186     }
187 
188     if ( heightOneLine )
189         *heightOneLine = heightLine;
190 }
191 
GetLargestStringExtent(size_t n,const wxString * strings)192 wxSize wxTextMeasureBase::GetLargestStringExtent(size_t n,
193                                                  const wxString* strings)
194 {
195     MeasuringGuard guard(*this);
196 
197     wxCoord w, h, widthMax = 0, heightMax = 0;
198     for ( size_t i = 0; i < n; ++i )
199     {
200         CallGetTextExtent(strings[i], &w, &h);
201 
202         if ( w > widthMax )
203             widthMax = w;
204         if ( h > heightMax )
205             heightMax = h;
206     }
207 
208     return wxSize(widthMax, heightMax);
209 }
210 
GetPartialTextExtents(const wxString & text,wxArrayInt & widths,double scaleX)211 bool wxTextMeasureBase::GetPartialTextExtents(const wxString& text,
212                                               wxArrayInt& widths,
213                                               double scaleX)
214 {
215     widths.Empty();
216     if ( text.empty() )
217         return true;
218 
219     MeasuringGuard guard(*this);
220 
221     widths.Add(0, text.length());
222 
223     return DoGetPartialTextExtents(text, widths, scaleX);
224 }
225 
226 // ----------------------------------------------------------------------------
227 // Generic and inefficient DoGetPartialTextExtents() implementation.
228 // ----------------------------------------------------------------------------
229 
230 // Each element of the widths array will be the width of the string up to and
231 // including the corresponding character in text.  This is the generic
232 // implementation, the port-specific classes should do this with native APIs
233 // if available and if faster.  Note: pango_layout_index_to_pos is much slower
234 // than calling GetTextExtent!!
235 
236 #define FWC_SIZE 256
237 
238 class FontWidthCache
239 {
240 public:
FontWidthCache()241     FontWidthCache() : m_scaleX(1), m_widths(NULL) { }
~FontWidthCache()242     ~FontWidthCache() { delete []m_widths; }
243 
Reset()244     void Reset()
245     {
246         if ( !m_widths )
247             m_widths = new int[FWC_SIZE];
248 
249         memset(m_widths, 0, sizeof(int)*FWC_SIZE);
250     }
251 
252     wxFont m_font;
253     double m_scaleX;
254     int *m_widths;
255 };
256 
257 static FontWidthCache s_fontWidthCache;
258 
DoGetPartialTextExtents(const wxString & text,wxArrayInt & widths,double scaleX)259 bool wxTextMeasureBase::DoGetPartialTextExtents(const wxString& text,
260                                                 wxArrayInt& widths,
261                                                 double scaleX)
262 {
263     int totalWidth = 0;
264 
265     // reset the cache if font or horizontal scale have changed
266     if ( !s_fontWidthCache.m_widths ||
267          !wxIsSameDouble(s_fontWidthCache.m_scaleX, scaleX) ||
268          (s_fontWidthCache.m_font != *m_font) )
269     {
270         s_fontWidthCache.Reset();
271         s_fontWidthCache.m_font = *m_font;
272         s_fontWidthCache.m_scaleX = scaleX;
273     }
274 
275     // Calculate the position of each character based on the widths of
276     // the previous characters. This is inexact for not fixed fonts.
277     int n = 0;
278     for ( wxString::const_iterator it = text.begin();
279           it != text.end();
280           ++it )
281     {
282         const wxChar c = *it;
283         unsigned int c_int = (unsigned int)c;
284 
285         int w;
286         if ((c_int < FWC_SIZE) && (s_fontWidthCache.m_widths[c_int] != 0))
287         {
288             w = s_fontWidthCache.m_widths[c_int];
289         }
290         else
291         {
292             DoGetTextExtent(c, &w, NULL);
293             if (c_int < FWC_SIZE)
294                 s_fontWidthCache.m_widths[c_int] = w;
295         }
296 
297         totalWidth += w;
298         widths[n++] = totalWidth;
299     }
300 
301     return true;
302 }
303 
304