Fixed typo
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextRenderer.cs
1 //
2 // TextRenderer.cs
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 // Copyright (c) 2006 Novell, Inc.
24 //
25 // Authors:
26 //      Jonathan Pobst (monkey@jpobst.com)
27 //
28
29 using System.Drawing;
30 using System.Runtime.InteropServices;
31 using System.Text;
32 using System.Drawing.Text;
33
34 namespace System.Windows.Forms
35 {
36 #if NET_2_0
37         public sealed 
38 #endif
39         class TextRenderer
40         {
41                 private static Bitmap measure_bitmap = new Bitmap (1, 1);
42
43                 private TextRenderer ()
44                 {
45                 }
46
47                 #region Public Methods
48 #if NET_2_0
49                 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor)
50                 {
51                         DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, false);
52                 }
53
54                 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor)
55                 {
56                         DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false);
57                 }
58
59                 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor)
60                 {
61                         DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, false);
62                 }
63
64                 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags)
65                 {
66                         DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, false);
67                 }
68
69                 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor)
70                 {
71                         DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false);
72                 }
73
74                 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags)
75                 {
76                         DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, false);
77                 }
78
79                 public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags)
80                 {
81                         DrawTextInternal (dc, text, font, pt, foreColor, backColor, flags, false);
82                 }
83
84                 public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags)
85                 {
86                         DrawTextInternal (dc, text, font, bounds, foreColor, backColor, flags, false);
87                 }
88
89                 public static Size MeasureText (string text, Font font)
90                 {
91                         return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, Size.Empty, TextFormatFlags.Default, false);
92                 }
93
94                 public static Size MeasureText (IDeviceContext dc, string text, Font font)
95                 {
96                         return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, false);
97                 }
98
99                 public static Size MeasureText (string text, Font font, Size proposedSize)
100                 {
101                         return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, proposedSize, TextFormatFlags.Default, false);
102                 }
103
104                 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize)
105                 {
106                         return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, false);
107                 }
108
109                 public static Size MeasureText (string text, Font font, Size proposedSize, TextFormatFlags flags)
110                 {
111                         return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, proposedSize, flags, false);
112                 }
113
114                 public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags)
115                 {
116                         return MeasureTextInternal (dc, text, font, proposedSize, flags, false);
117                 }
118 #endif
119                 #endregion
120
121                 #region Internal Methods That Do Stuff
122 #if NET_2_0
123                 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
124 #else
125                 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
126 #endif
127                 {
128 #if NET_2_0
129                         if (dc == null)
130                                 throw new ArgumentNullException ("dc");
131
132                         if (string.IsNullOrEmpty (text))
133                                 return;
134
135                         // We use MS GDI API's unless told not to, or we aren't on Windows
136                         if (!useDrawString && (Environment.OSVersion.Platform == PlatformID.Win32NT || Environment.OSVersion.Platform == PlatformID.Win32Windows)) {
137                                 if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter || (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
138                                         flags |= TextFormatFlags.SingleLine;
139
140                                 // Calculate the text bounds (there is often padding added)
141                                 Rectangle new_bounds = PadRectangle (bounds, flags);
142                                 new_bounds.Offset ((int)(dc as Graphics).Transform.OffsetX, (int)(dc as Graphics).Transform.OffsetY);
143
144                                 IntPtr hdc = IntPtr.Zero;
145                                 bool clear_clip_region = false;
146                                 
147                                 // If we need to use the graphics clipping region, add it to our hdc
148                                 if ((flags & TextFormatFlags.PreserveGraphicsClipping) == TextFormatFlags.PreserveGraphicsClipping) {
149                                         Graphics graphics = (Graphics)dc;
150                                         Region clip_region = graphics.Clip;
151                                         
152                                         if (!clip_region.IsInfinite (graphics)) {
153                                                 IntPtr hrgn = clip_region.GetHrgn (graphics);
154                                                 hdc = dc.GetHdc ();
155                                                 SelectClipRgn (hdc, hrgn);
156                                                 clip_region.ReleaseHrgn (hrgn);
157                                                 clear_clip_region = true;
158                                         }
159                                 }
160                                 
161                                 if (hdc == IntPtr.Zero)
162                                         hdc = dc.GetHdc ();
163                                         
164                                 // Set the fore color
165                                 if (foreColor != Color.Empty)
166                                         SetTextColor (hdc, ColorTranslator.ToWin32 (foreColor));
167
168                                 // Set the back color
169                                 if (backColor != Color.Transparent && backColor != Color.Empty) {
170                                         SetBkMode (hdc, 2);     //1-Transparent, 2-Opaque
171                                         SetBkColor (hdc, ColorTranslator.ToWin32 (backColor));
172                                 }
173                                 else {
174                                         SetBkMode (hdc, 1);     //1-Transparent, 2-Opaque
175                                 }
176
177                                 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new_bounds);
178
179                                 IntPtr prevobj;
180
181                                 if (font != null) {
182                                         prevobj = SelectObject (hdc, font.ToHfont ());
183                                         Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
184                                         prevobj = SelectObject (hdc, prevobj);
185                                         DeleteObject (prevobj);
186                                 }
187                                 else {
188                                         Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
189                                 }
190
191                                 if (clear_clip_region)
192                                         SelectClipRgn (hdc, IntPtr.Zero);
193
194                                 dc.ReleaseHdc ();
195                         }
196                         // Use Graphics.DrawString as a fallback method
197                         else {
198 #endif
199                                 Graphics g;
200
201 #if NET_2_0
202                                 if (dc is Graphics)
203                                         g = (Graphics)dc;
204                                 else
205                                         g = Graphics.FromHdc (dc.GetHdc ());
206 #else
207                                         g = (Graphics)dc;
208
209 #endif
210
211                                 StringFormat sf = FlagsToStringFormat (flags);
212
213                                 Rectangle new_bounds = PadDrawStringRectangle (bounds, flags);
214
215                                 g.DrawString (text, font, ThemeEngine.Current.ResPool.GetSolidBrush (foreColor), new_bounds, sf);
216
217 #if NET_2_0
218                                 if (!(dc is Graphics)) {
219                                         g.Dispose ();
220                                         dc.ReleaseHdc ();
221                                 }
222                         }
223 #endif
224                 }
225
226 #if NET_2_0
227                 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
228 #else
229                 internal static Size MeasureTextInternal (Graphics dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
230 #endif
231                 {
232 #if NET_2_0
233                         if (!useMeasureString && (Environment.OSVersion.Platform == PlatformID.Win32NT || Environment.OSVersion.Platform == PlatformID.Win32Windows)) {
234                                 // Tell DrawText to calculate size instead of draw
235                                 flags |= (TextFormatFlags)1024;         // DT_CALCRECT
236
237                                 IntPtr hdc = dc.GetHdc ();
238
239                                 XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new Rectangle (Point.Empty, proposedSize));
240
241                                 IntPtr prevobj;
242
243                                 if (font != null) {
244                                         prevobj = SelectObject (hdc, font.ToHfont ());
245                                         Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
246                                         prevobj = SelectObject (hdc, prevobj);
247                                         DeleteObject (prevobj);
248                                 }
249                                 else {
250                                         Win32DrawText (hdc, text, text.Length, ref r, (int)flags);
251                                 }
252
253                                 dc.ReleaseHdc ();
254
255                                 // Really, I am just making something up here, which as far as I can tell, MS
256                                 // just makes something up as well.  This will require lots of tweaking to match MS.  :(
257                                 Size retval = r.ToRectangle ().Size;
258
259                                 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0) {
260                                         retval.Width += 6;
261                                         retval.Width += (int)retval.Height / 8;
262                                 }
263
264                                 return retval;
265                         }
266                         else {
267 #endif
268                                 Graphics g = Graphics.FromImage (measure_bitmap);
269
270                                 Size retval = g.MeasureString (text, font).ToSize ();
271
272                                 if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0)
273                                         retval.Width += 9;
274
275                                 return retval;
276                         }
277 #if NET_2_0
278                 }
279 #endif
280                 #endregion
281
282 #region Internal Methods That Are Just Overloads
283 #if NET_2_0
284                 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, bool useDrawString)
285                 {
286                         DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, useDrawString);
287                 }
288
289                 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, bool useDrawString)
290                 {
291                         DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
292                 }
293
294                 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, bool useDrawString)
295                 {
296                         DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, useDrawString);
297                 }
298
299                 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags, bool useDrawString)
300                 {
301                         DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, useDrawString);
302                 }
303
304                 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, bool useDrawString)
305                 {
306                         DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString);
307                 }
308
309                 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
310 #else
311                 internal static void DrawTextInternal (Graphics dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString)
312 #endif
313                 {
314                         DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, useDrawString);
315                 }
316
317 #if NET_2_0
318                 internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString)
319                 {
320                         Size sz = MeasureTextInternal (dc, text, font, useDrawString);
321                         DrawTextInternal (dc, text, font, new Rectangle (pt, sz), foreColor, backColor, flags, useDrawString);
322                 }
323
324                 internal static Size MeasureTextInternal (string text, Font font, bool useMeasureString)
325                 {
326                         return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
327                 }
328
329                 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, bool useMeasureString)
330                 {
331                         return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString);
332                 }
333
334                 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, bool useMeasureString)
335                 {
336                         return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
337                 }
338
339                 internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, bool useMeasureString)
340                 {
341                         return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, useMeasureString);
342                 }
343
344 #endif
345                 internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString)
346                 {
347                         return MeasureTextInternal (Graphics.FromImage (measure_bitmap), text, font, proposedSize, flags, useMeasureString);
348                 }
349 #endregion
350
351 #region Private Methods
352                 private static StringFormat FlagsToStringFormat (TextFormatFlags flags)
353                 {
354                         StringFormat sf = new StringFormat ();
355
356                         // Translation table: http://msdn.microsoft.com/msdnmag/issues/06/03/TextRendering/default.aspx?fig=true#fig4
357
358                         // Horizontal Alignment
359                         if ((flags & TextFormatFlags.HorizontalCenter) == TextFormatFlags.HorizontalCenter)
360                                 sf.Alignment = StringAlignment.Center;
361                         else if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right)
362                                 sf.Alignment = StringAlignment.Far;
363                         else
364                                 sf.Alignment = StringAlignment.Near;
365
366                         // Vertical Alignment
367                         if ((flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
368                                 sf.LineAlignment = StringAlignment.Far;
369                         else if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter)
370                                 sf.LineAlignment = StringAlignment.Center;
371                         else
372                                 sf.LineAlignment = StringAlignment.Near;
373
374                         // Ellipsis
375                         if ((flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis)
376                                 sf.Trimming = StringTrimming.EllipsisCharacter;
377                         else if ((flags & TextFormatFlags.PathEllipsis) == TextFormatFlags.PathEllipsis)
378                                 sf.Trimming = StringTrimming.EllipsisPath;
379                         else if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis)
380                                 sf.Trimming = StringTrimming.EllipsisWord;
381                         else
382                                 sf.Trimming = StringTrimming.Character;
383
384                         // Hotkey Prefix
385                         if ((flags & TextFormatFlags.NoPrefix) == TextFormatFlags.NoPrefix)
386                                 sf.HotkeyPrefix = HotkeyPrefix.None;
387                         else if ((flags & TextFormatFlags.HidePrefix) == TextFormatFlags.HidePrefix)
388                                 sf.HotkeyPrefix = HotkeyPrefix.Hide;
389                         else
390                                 sf.HotkeyPrefix = HotkeyPrefix.Show;
391
392                         // Text Padding
393                         if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding)
394                                 sf.FormatFlags |= StringFormatFlags.FitBlackBox;
395
396                         // Text Wrapping
397                         if ((flags & TextFormatFlags.SingleLine) == TextFormatFlags.SingleLine)
398                                 sf.FormatFlags |= StringFormatFlags.NoWrap;
399                         else if ((flags & TextFormatFlags.TextBoxControl) == TextFormatFlags.TextBoxControl)
400                                 sf.FormatFlags |= StringFormatFlags.LineLimit;
401
402                         // Other Flags
403                         //if ((flags & TextFormatFlags.RightToLeft) == TextFormatFlags.RightToLeft)
404                         //        sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
405                         if ((flags & TextFormatFlags.NoClipping) == TextFormatFlags.NoClipping)
406                                 sf.FormatFlags |= StringFormatFlags.NoClip;
407                         //sf.FormatFlags |= StringFormatFlags.NoClip;
408
409                         if ((flags & TextFormatFlags.WordBreak) == 0)
410                                 sf.FormatFlags |= StringFormatFlags.LineLimit;
411
412                         return sf;
413                 }
414
415                 private static Rectangle PadRectangle (Rectangle r, TextFormatFlags flags)
416                 {
417                         if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) {
418                                 r.X += 3;
419                                 r.Width -= 3;
420                         }
421                         if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) {
422                                 r.Width -= 4;
423                         }
424                         if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
425                                 r.X += 2;
426                                 r.Width -= 2;
427                         }
428                         if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
429                                 r.Width -= 4;
430                         }
431                         if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
432                                 r.Y += 1;
433                         }
434
435                         return r;
436                 }
437
438                 private static Rectangle PadDrawStringRectangle (Rectangle r, TextFormatFlags flags)
439                 {
440                         if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) {
441                                 r.X += 1;
442                                 r.Width -= 1;
443                         }
444                         if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) {
445                                 r.Width -= 4;
446                         }
447                         if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding) {
448                                 r.X -= 2;
449                         }
450                         if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) {
451                                 r.Y += 1;
452                         }
453                         if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) {
454                                 r.X += 2;
455                                 r.Width -= 2;
456                         }
457                         if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) {
458                                 r.Width -= 4;
459                         }
460                         if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) {
461                                 r.Y += 1;
462                         }
463
464                         return r;
465                 }
466 #endregion
467
468 #region DllImports (Windows)
469 #if NET_2_0
470                 [DllImport ("user32", CharSet = CharSet.Unicode, EntryPoint = "DrawText")]
471                 static extern int Win32DrawText (IntPtr hdc, string lpStr, int nCount, ref XplatUIWin32.RECT lpRect, int wFormat);
472
473                 [DllImport ("gdi32")]
474                 static extern int SetTextColor (IntPtr hdc, int crColor);
475
476                 [DllImport ("gdi32")]
477                 static extern IntPtr SelectObject (IntPtr hDC, IntPtr hObject);
478
479                 [DllImport ("gdi32")]
480                 static extern int SetBkColor (IntPtr hdc, int crColor);
481
482                 [DllImport ("gdi32")]
483                 static extern int SetBkMode (IntPtr hdc, int iBkMode);
484
485                 [DllImport ("gdi32")]
486                 static extern bool DeleteObject (IntPtr objectHandle);
487
488                 [DllImport("gdi32")]
489                 static extern bool SelectClipRgn(IntPtr hdc, IntPtr hrgn);
490 #endif
491 #endregion
492         }
493 }