Factor out text measurement from wxDC and wxWindow into wxTextMeasure.
[wxWidgets.git] / src / gtk / textmeasure.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/textmeasure.cpp
3 // Purpose: wxTextMeasure implementation for wxGTK
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/window.h"
23 #endif //WX_PRECOMP
24
25 #include "wx/private/textmeasure.h"
26
27 #include "wx/fontutil.h"
28 #include "wx/gtk/private.h"
29
30 #ifndef __WXGTK3__
31 #include "wx/gtk/dcclient.h"
32 #endif
33
34 // ============================================================================
35 // wxTextMeasure implementation
36 // ============================================================================
37
38 void wxTextMeasure::Init()
39 {
40 wxASSERT_MSG( m_font, wxT("wxTextMeasure needs a valid wxFont") );
41
42 #ifndef __WXGTK3__
43 m_wdc = NULL;
44 #endif // GTK+ < 3
45 m_context = NULL;
46 m_layout = NULL;
47 }
48
49 // Get Gtk needed elements, if we have not them yet.
50 void wxTextMeasure::BeginMeasuring()
51 {
52 if ( m_dc )
53 {
54 #ifndef __WXGTK3__
55 m_wdc = wxDynamicCast(m_dc->GetImpl(), wxWindowDCImpl);
56 if ( m_wdc )
57 {
58 m_context = m_wdc->m_context;
59 m_layout = m_wdc->m_layout;
60 }
61 #endif // GTK+ < 3
62 }
63 else if ( m_win )
64 {
65 m_context = gtk_widget_get_pango_context( m_win->GetHandle() );
66 if ( m_context )
67 m_layout = pango_layout_new(m_context);
68 }
69
70 // set the font to use
71 if ( m_layout )
72 {
73 pango_layout_set_font_description(m_layout,
74 m_font->GetNativeFontInfo()->description);
75 }
76 }
77
78 void wxTextMeasure::EndMeasuring()
79 {
80 if ( !m_layout )
81 return;
82
83 #ifndef __WXGTK3__
84 if ( m_wdc )
85 {
86 // Reset dc own font description
87 pango_layout_set_font_description( m_wdc->m_layout, m_wdc->m_fontdesc );
88 }
89 else
90 #endif // GTK+ < 3
91 {
92 g_object_unref (m_layout);
93 }
94 }
95
96 // Notice we don't check here the font. It is supposed to be OK before the call.
97 void wxTextMeasure::DoGetTextExtent(const wxString& string,
98 wxCoord *width,
99 wxCoord *height,
100 wxCoord *descent,
101 wxCoord *externalLeading)
102 {
103 if ( !m_context )
104 {
105 *width =
106 *height = 0;
107 return;
108 }
109
110 // Set layout's text
111 const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT(string, *m_font);
112 if ( !dataUTF8 )
113 {
114 // hardly ideal, but what else can we do if conversion failed?
115 wxLogLastError(wxT("GetTextExtent"));
116 return;
117 }
118 pango_layout_set_text(m_layout, dataUTF8, -1);
119
120 if ( m_dc )
121 {
122 // in device units
123 pango_layout_get_pixel_size(m_layout, width, height);
124 }
125 else // win
126 {
127 // the logical rect bounds the ink rect
128 PangoRectangle rect;
129 pango_layout_get_extents(m_layout, NULL, &rect);
130 *width = PANGO_PIXELS(rect.width);
131 *height = PANGO_PIXELS(rect.height);
132 }
133
134 if (descent)
135 {
136 PangoLayoutIter *iter = pango_layout_get_iter(m_layout);
137 int baseline = pango_layout_iter_get_baseline(iter);
138 pango_layout_iter_free(iter);
139 *descent = *height - PANGO_PIXELS(baseline);
140 }
141
142 if (externalLeading)
143 {
144 // No support for MSW-like "external leading" in Pango.
145 *externalLeading = 0;
146 }
147 }
148
149 bool wxTextMeasure::DoGetPartialTextExtents(const wxString& text,
150 wxArrayInt& widths,
151 double WXUNUSED(scaleX))
152 {
153 // Set layout's text
154 const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT(text, *m_font);
155 if ( !dataUTF8 )
156 {
157 // hardly ideal, but what else can we do if conversion failed?
158 wxLogLastError(wxT("GetPartialTextExtents"));
159 return false;
160 }
161
162 pango_layout_set_text(m_layout, dataUTF8, -1);
163
164 // Calculate the position of each character based on the widths of
165 // the previous characters
166
167 // Code borrowed from Scintilla's PlatGTK
168 PangoLayoutIter *iter = pango_layout_get_iter(m_layout);
169 PangoRectangle pos;
170 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
171 size_t i = 0;
172 while (pango_layout_iter_next_cluster(iter))
173 {
174 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
175 int position = PANGO_PIXELS(pos.x);
176 widths[i++] = position;
177 }
178
179 const size_t len = text.length();
180 while (i < len)
181 widths[i++] = PANGO_PIXELS(pos.x + pos.width);
182 pango_layout_iter_free(iter);
183
184 return true;
185 }