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