1 //
2 // PangoUtils.cs
3 //
4 // Author:
5 //       Michael Hutchinson <mhutchinson@novell.com>
6 //
7 // Copyright (c) 2010 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26 
27 using System;
28 using Gtk;
29 using System.Runtime.InteropServices;
30 
31 namespace Pinta.Docking
32 {
33 	static class PangoUtil
34 	{
35 		internal const string LIBGTK          = "libgtk-win32-2.0-0.dll";
36 		internal const string LIBATK          = "libatk-1.0-0.dll";
37 		internal const string LIBGLIB         = "libglib-2.0-0.dll";
38 		internal const string LIBGDK          = "libgdk-win32-2.0-0.dll";
39 		internal const string LIBGOBJECT      = "libgobject-2.0-0.dll";
40 		internal const string LIBPANGO        = "libpango-1.0-0.dll";
41 		internal const string LIBPANGOCAIRO   = "libpangocairo-1.0-0.dll";
42 		internal const string LIBQUARTZ       = "libgtk-quartz-2.0.dylib";
43 		internal const string LIBGTKGLUE      = "gtksharpglue-2";
44 
45 		/// <summary>
46 		/// This doesn't leak Pango layouts, unlike some other ways to create them in GTK# &lt;= 2.12.11
47 		/// </summary>
CreateLayout(Widget widget)48 		public static Pango.Layout CreateLayout (Widget widget)
49 		{
50 			var ptr = gtk_widget_create_pango_layout (widget.Handle, IntPtr.Zero);
51 			return ptr == IntPtr.Zero? null : new Pango.Layout (ptr);
52 		}
53 
CreateLayout(Widget widget, string text)54 		public static Pango.Layout CreateLayout (Widget widget, string text)
55 		{
56 			IntPtr textPtr = text == null? IntPtr.Zero : GLib.Marshaller.StringToPtrGStrdup (text);
57 
58 			var ptr = gtk_widget_create_pango_layout (widget.Handle, textPtr);
59 
60 			if (textPtr != IntPtr.Zero)
61 				GLib.Marshaller.Free (textPtr);
62 
63 			return ptr == IntPtr.Zero? null : new Pango.Layout (ptr);
64 		}
65 
CreateLayout(PrintContext context)66 		public static Pango.Layout CreateLayout (PrintContext context)
67 		{
68 			var ptr = gtk_print_context_create_pango_layout (context.Handle);
69 			return ptr == IntPtr.Zero? null : new Pango.Layout (ptr);
70 		}
71 
72 		[DllImport (LIBGTK, CallingConvention=CallingConvention.Cdecl)]
gtk_widget_create_pango_layout(IntPtr widget, IntPtr text)73 		static extern IntPtr gtk_widget_create_pango_layout (IntPtr widget, IntPtr text);
74 
75 		[DllImport (LIBGTK, CallingConvention=CallingConvention.Cdecl)]
gtk_print_context_create_pango_layout(IntPtr context)76 		static extern IntPtr gtk_print_context_create_pango_layout (IntPtr context);
77 	}
78 
79 	/// <summary>
80 	/// This creates a Pango list and applies attributes to it with *much* less overhead than the GTK# version.
81 	/// </summary>
82 	class FastPangoAttrList : IDisposable
83 	{
84 		IntPtr list;
85 
FastPangoAttrList()86 		public FastPangoAttrList ()
87 		{
88 			list = pango_attr_list_new ();
89 		}
90 
AddStyleAttribute(Pango.Style style, uint start, uint end)91 		public void AddStyleAttribute (Pango.Style style, uint start, uint end)
92 		{
93 			Add (pango_attr_style_new (style), start, end);
94 		}
95 
AddWeightAttribute(Pango.Weight weight, uint start, uint end)96 		public void AddWeightAttribute (Pango.Weight weight, uint start, uint end)
97 		{
98 			Add (pango_attr_weight_new (weight), start, end);
99 		}
100 
AddForegroundAttribute(Gdk.Color color, uint start, uint end)101 		public void AddForegroundAttribute (Gdk.Color color, uint start, uint end)
102 		{
103 			Add (pango_attr_foreground_new (color.Red, color.Green, color.Blue), start, end);
104 		}
105 
AddBackgroundAttribute(Gdk.Color color, uint start, uint end)106 		public void AddBackgroundAttribute (Gdk.Color color, uint start, uint end)
107 		{
108 			Add (pango_attr_background_new (color.Red, color.Green, color.Blue), start, end);
109 		}
110 
AddUnderlineAttribute(Pango.Underline underline, uint start, uint end)111 		public void AddUnderlineAttribute (Pango.Underline underline, uint start, uint end)
112 		{
113 			Add (pango_attr_underline_new (underline), start, end);
114 		}
115 
Add(IntPtr attribute, uint start, uint end)116 		void Add (IntPtr attribute, uint start, uint end)
117 		{
118 			unsafe {
119 				PangoAttribute *attPtr = (PangoAttribute *) attribute;
120 				attPtr->start_index = start;
121 				attPtr->end_index = end;
122 			}
123 			pango_attr_list_insert (list, attribute);
124 		}
125 
126 		/// <summary>
127 		/// Like Splice, except it only offsets/clamps the inserted items, doesn't affect items already in the list.
128 		/// </summary>
InsertOffsetList(Pango.AttrList atts, uint startOffset, uint endOffset)129 		public void InsertOffsetList (Pango.AttrList atts, uint startOffset, uint endOffset)
130 		{
131 			//HACK: atts.Iterator.Attrs broken (throws NRE), so manually P/Invoke
132 			var iter = pango_attr_list_get_iterator (atts.Handle);
133 			try {
134 				do {
135 					IntPtr list = pango_attr_iterator_get_attrs (iter);
136 					try {
137 						int len = g_slist_length (list);
138 						for (uint i = 0; i < len; i++) {
139 							IntPtr val = g_slist_nth_data (list, i);
140 							AddOffsetCopy (val, startOffset, endOffset);
141 						}
142 					} finally {
143 						g_slist_free (list);
144 					}
145 				} while (pango_attr_iterator_next (iter));
146 			} finally {
147 				pango_attr_iterator_destroy (iter);
148 			}
149 		}
150 
AddOffsetCopy(IntPtr attr, uint startOffset, uint endOffset)151 		void AddOffsetCopy (IntPtr attr, uint startOffset, uint endOffset)
152 		{
153 			var copy = pango_attribute_copy (attr);
154 			unsafe {
155 				PangoAttribute *attPtr = (PangoAttribute *) copy;
156 				attPtr->start_index = startOffset + attPtr->start_index;
157 				attPtr->end_index = System.Math.Min (endOffset, startOffset + attPtr->end_index);
158 			}
159 			pango_attr_list_insert (list, copy);
160 		}
161 
162 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_style_new(Pango.Style style)163 		static extern IntPtr pango_attr_style_new (Pango.Style style);
164 
165 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_stretch_new(Pango.Stretch stretch)166 		static extern IntPtr pango_attr_stretch_new (Pango.Stretch stretch);
167 
168 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_weight_new(Pango.Weight weight)169 		static extern IntPtr pango_attr_weight_new (Pango.Weight weight);
170 
171 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_foreground_new(ushort red, ushort green, ushort blue)172 		static extern IntPtr pango_attr_foreground_new (ushort red, ushort green, ushort blue);
173 
174 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_background_new(ushort red, ushort green, ushort blue)175 		static extern IntPtr pango_attr_background_new (ushort red, ushort green, ushort blue);
176 
177 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_underline_new(Pango.Underline underline)178 		static extern IntPtr pango_attr_underline_new (Pango.Underline underline);
179 
180 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_list_new()181 		static extern IntPtr pango_attr_list_new ();
182 
183 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_list_unref(IntPtr list)184 		static extern void pango_attr_list_unref (IntPtr list);
185 
186 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_list_insert(IntPtr list, IntPtr attr)187 		static extern void pango_attr_list_insert (IntPtr list, IntPtr attr);
188 
189 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_layout_set_attributes(IntPtr layout, IntPtr attrList)190 		static extern void pango_layout_set_attributes (IntPtr layout, IntPtr attrList);
191 
192 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_list_splice(IntPtr attr_list, IntPtr other, Int32 pos, Int32 len)193 		static extern void pango_attr_list_splice (IntPtr attr_list, IntPtr other, Int32 pos, Int32 len);
194 
195 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attribute_copy(IntPtr attr)196 		static extern IntPtr pango_attribute_copy (IntPtr attr);
197 
198 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_list_get_iterator(IntPtr list)199 		static extern IntPtr pango_attr_list_get_iterator (IntPtr list);
200 
201 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_iterator_next(IntPtr iterator)202 		static extern bool pango_attr_iterator_next (IntPtr iterator);
203 
204 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_iterator_destroy(IntPtr iterator)205 		static extern void pango_attr_iterator_destroy (IntPtr iterator);
206 
207 		[DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)]
pango_attr_iterator_get_attrs(IntPtr iterator)208 		static extern IntPtr pango_attr_iterator_get_attrs (IntPtr iterator);
209 
210 		[DllImport (PangoUtil.LIBGLIB, CallingConvention = CallingConvention.Cdecl)]
g_slist_length(IntPtr l)211 		private static extern int g_slist_length (IntPtr l);
212 
213 		[DllImport (PangoUtil.LIBGLIB, CallingConvention = CallingConvention.Cdecl)]
g_slist_nth_data(IntPtr l, uint n)214 		private static extern IntPtr g_slist_nth_data (IntPtr l, uint n);
215 
216 		[DllImport (PangoUtil.LIBGLIB, CallingConvention = CallingConvention.Cdecl)]
g_slist_free(IntPtr l)217 		private static extern void g_slist_free (IntPtr l);
218 
Splice(Pango.AttrList attrs, int pos, int len)219 		public void Splice (Pango.AttrList attrs, int pos, int len)
220 		{
221 			pango_attr_list_splice (list, attrs.Handle, pos, len);
222 		}
223 
AssignTo(Pango.Layout layout)224 		public void AssignTo (Pango.Layout layout)
225 		{
226 			pango_layout_set_attributes (layout.Handle, list);
227 		}
228 
229 		[StructLayout (LayoutKind.Sequential)]
230 		struct PangoAttribute
231 		{
232 			public IntPtr klass;
233 			public uint start_index;
234 			public uint end_index;
235 		}
236 
Dispose()237 		public void Dispose ()
238 		{
239 			if (list != IntPtr.Zero) {
240 				GC.SuppressFinalize (this);
241 				Destroy ();
242 			}
243 		}
244 
245 		//NOTE: the list destroys all its attributes when the ref count reaches zero
Destroy()246 		void Destroy ()
247 		{
248 			pango_attr_list_unref (list);
249 			list = IntPtr.Zero;
250 		}
251 
~FastPangoAttrList()252 		~FastPangoAttrList ()
253 		{
254 			GLib.Idle.Add (delegate {
255 				Destroy ();
256 				return false;
257 			});
258 		}
259 	}
260 }