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