1 | /* |
2 | * Copyright © 2011 Red Hat Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Authors: Benjamin Otte <otte@gnome.org> |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include <string.h> |
23 | |
24 | #include "gtkcssimageurlprivate.h" |
25 | |
26 | #include "gtkcssimageinvalidprivate.h" |
27 | #include "gtkcssimagepaintableprivate.h" |
28 | #include "gtkstyleproviderprivate.h" |
29 | #include "gtk/css/gtkcssdataurlprivate.h" |
30 | |
31 | |
32 | G_DEFINE_TYPE (GtkCssImageUrl, _gtk_css_image_url, GTK_TYPE_CSS_IMAGE) |
33 | |
34 | static GtkCssImage * |
35 | gtk_css_image_url_load_image (GtkCssImageUrl *url, |
36 | GError **error) |
37 | { |
38 | GdkTexture *texture; |
39 | GError *local_error = NULL; |
40 | |
41 | if (url->loaded_image) |
42 | return url->loaded_image; |
43 | |
44 | if (url->file == NULL) |
45 | { |
46 | url->loaded_image = gtk_css_image_invalid_new (); |
47 | return url->loaded_image; |
48 | } |
49 | |
50 | /* We special case resources here so we can use gdk_texture_new_from_resource. */ |
51 | if (g_file_has_uri_scheme (file: url->file, uri_scheme: "resource" )) |
52 | { |
53 | char *uri = g_file_get_uri (file: url->file); |
54 | char *resource_path = g_uri_unescape_string (escaped_string: uri + strlen (s: "resource://" ), NULL); |
55 | |
56 | texture = gdk_texture_new_from_resource (resource_path); |
57 | |
58 | g_free (mem: resource_path); |
59 | g_free (mem: uri); |
60 | } |
61 | else |
62 | { |
63 | texture = gdk_texture_new_from_file (file: url->file, error: &local_error); |
64 | } |
65 | |
66 | if (texture == NULL) |
67 | { |
68 | if (error && local_error) |
69 | { |
70 | char *uri; |
71 | |
72 | uri = g_file_get_uri (file: url->file); |
73 | g_set_error (err: error, |
74 | GTK_CSS_PARSER_ERROR, |
75 | code: GTK_CSS_PARSER_ERROR_FAILED, |
76 | format: "Error loading image '%s': %s" , uri, local_error->message); |
77 | g_free (mem: uri); |
78 | } |
79 | |
80 | url->loaded_image = gtk_css_image_invalid_new (); |
81 | } |
82 | else |
83 | { |
84 | url->loaded_image = gtk_css_image_paintable_new (paintable: GDK_PAINTABLE (ptr: texture), static_paintable: GDK_PAINTABLE (ptr: texture)); |
85 | g_object_unref (object: texture); |
86 | } |
87 | |
88 | g_clear_error (err: &local_error); |
89 | |
90 | return url->loaded_image; |
91 | } |
92 | |
93 | static int |
94 | gtk_css_image_url_get_width (GtkCssImage *image) |
95 | { |
96 | GtkCssImageUrl *url = GTK_CSS_IMAGE_URL (image); |
97 | |
98 | return _gtk_css_image_get_width (image: gtk_css_image_url_load_image (url, NULL)); |
99 | } |
100 | |
101 | static int |
102 | gtk_css_image_url_get_height (GtkCssImage *image) |
103 | { |
104 | GtkCssImageUrl *url = GTK_CSS_IMAGE_URL (image); |
105 | |
106 | return _gtk_css_image_get_height (image: gtk_css_image_url_load_image (url, NULL)); |
107 | } |
108 | |
109 | static double |
110 | gtk_css_image_url_get_aspect_ratio (GtkCssImage *image) |
111 | { |
112 | GtkCssImageUrl *url = GTK_CSS_IMAGE_URL (image); |
113 | |
114 | return _gtk_css_image_get_aspect_ratio (image: gtk_css_image_url_load_image (url, NULL)); |
115 | } |
116 | |
117 | static void |
118 | gtk_css_image_url_snapshot (GtkCssImage *image, |
119 | GtkSnapshot *snapshot, |
120 | double width, |
121 | double height) |
122 | { |
123 | GtkCssImageUrl *url = GTK_CSS_IMAGE_URL (image); |
124 | |
125 | gtk_css_image_snapshot (image: gtk_css_image_url_load_image (url, NULL), snapshot, width, height); |
126 | } |
127 | |
128 | static GtkCssImage * |
129 | gtk_css_image_url_compute (GtkCssImage *image, |
130 | guint property_id, |
131 | GtkStyleProvider *provider, |
132 | GtkCssStyle *style, |
133 | GtkCssStyle *parent_style) |
134 | { |
135 | GtkCssImageUrl *url = GTK_CSS_IMAGE_URL (image); |
136 | GtkCssImage *copy; |
137 | GError *error = NULL; |
138 | |
139 | copy = gtk_css_image_url_load_image (url, error: &error); |
140 | if (error) |
141 | { |
142 | GtkCssSection *section = gtk_css_style_get_section (style, id: property_id); |
143 | gtk_style_provider_emit_error (provider, section, error); |
144 | g_error_free (error); |
145 | } |
146 | |
147 | return g_object_ref (copy); |
148 | } |
149 | |
150 | static gboolean |
151 | gtk_css_image_url_equal (GtkCssImage *image1, |
152 | GtkCssImage *image2) |
153 | { |
154 | GtkCssImageUrl *url1 = GTK_CSS_IMAGE_URL (image1); |
155 | GtkCssImageUrl *url2 = GTK_CSS_IMAGE_URL (image2); |
156 | |
157 | /* FIXME: We don't save data: urls, so we can't compare them here */ |
158 | if (url1->file == NULL || url2->file == NULL) |
159 | return FALSE; |
160 | |
161 | return g_file_equal (file1: url1->file, file2: url2->file); |
162 | } |
163 | |
164 | static gboolean |
165 | gtk_css_image_url_is_invalid (GtkCssImage *image) |
166 | { |
167 | GtkCssImageUrl *url = GTK_CSS_IMAGE_URL (image); |
168 | |
169 | return gtk_css_image_is_invalid (image: gtk_css_image_url_load_image (url, NULL)); |
170 | } |
171 | |
172 | static gboolean |
173 | gtk_css_image_url_is_computed (GtkCssImage *image) |
174 | { |
175 | return TRUE; |
176 | } |
177 | |
178 | static gboolean |
179 | gtk_css_image_url_parse (GtkCssImage *image, |
180 | GtkCssParser *parser) |
181 | { |
182 | GtkCssImageUrl *self = GTK_CSS_IMAGE_URL (image); |
183 | char *url, *scheme; |
184 | |
185 | url = gtk_css_parser_consume_url (self: parser); |
186 | if (url == NULL) |
187 | return FALSE; |
188 | |
189 | scheme = g_uri_parse_scheme (uri: url); |
190 | if (scheme && g_ascii_strcasecmp (s1: scheme, s2: "data" ) == 0) |
191 | { |
192 | GBytes *bytes; |
193 | GError *error = NULL; |
194 | |
195 | bytes = gtk_css_data_url_parse (url, NULL, error: &error); |
196 | if (bytes) |
197 | { |
198 | GdkTexture *texture; |
199 | |
200 | texture = gdk_texture_new_from_bytes (bytes, error: &error); |
201 | g_bytes_unref (bytes); |
202 | if (texture) |
203 | { |
204 | GdkPaintable *paintable = GDK_PAINTABLE (ptr: texture); |
205 | self->loaded_image = gtk_css_image_paintable_new (paintable, static_paintable: paintable); |
206 | } |
207 | else |
208 | { |
209 | gtk_css_parser_emit_error (self: parser, |
210 | start: gtk_css_parser_get_start_location (self: parser), |
211 | end: gtk_css_parser_get_end_location (self: parser), |
212 | error); |
213 | g_clear_error (err: &error); |
214 | } |
215 | } |
216 | else |
217 | { |
218 | gtk_css_parser_emit_error (self: parser, |
219 | start: gtk_css_parser_get_start_location (self: parser), |
220 | end: gtk_css_parser_get_end_location (self: parser), |
221 | error); |
222 | g_clear_error (err: &error); |
223 | } |
224 | } |
225 | else |
226 | { |
227 | self->file = gtk_css_parser_resolve_url (self: parser, url); |
228 | } |
229 | |
230 | g_free (mem: url); |
231 | g_free (mem: scheme); |
232 | |
233 | return TRUE; |
234 | } |
235 | |
236 | static void |
237 | gtk_css_image_url_print (GtkCssImage *image, |
238 | GString *string) |
239 | { |
240 | GtkCssImageUrl *url = GTK_CSS_IMAGE_URL (image); |
241 | |
242 | _gtk_css_image_print (image: gtk_css_image_url_load_image (url, NULL), string); |
243 | } |
244 | |
245 | static void |
246 | gtk_css_image_url_dispose (GObject *object) |
247 | { |
248 | GtkCssImageUrl *url = GTK_CSS_IMAGE_URL (object); |
249 | |
250 | g_clear_object (&url->file); |
251 | g_clear_object (&url->loaded_image); |
252 | |
253 | G_OBJECT_CLASS (_gtk_css_image_url_parent_class)->dispose (object); |
254 | } |
255 | |
256 | static void |
257 | _gtk_css_image_url_class_init (GtkCssImageUrlClass *klass) |
258 | { |
259 | GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass); |
260 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
261 | |
262 | image_class->get_width = gtk_css_image_url_get_width; |
263 | image_class->get_height = gtk_css_image_url_get_height; |
264 | image_class->get_aspect_ratio = gtk_css_image_url_get_aspect_ratio; |
265 | image_class->compute = gtk_css_image_url_compute; |
266 | image_class->snapshot = gtk_css_image_url_snapshot; |
267 | image_class->parse = gtk_css_image_url_parse; |
268 | image_class->print = gtk_css_image_url_print; |
269 | image_class->equal = gtk_css_image_url_equal; |
270 | image_class->is_invalid = gtk_css_image_url_is_invalid; |
271 | image_class->is_computed = gtk_css_image_url_is_computed; |
272 | |
273 | object_class->dispose = gtk_css_image_url_dispose; |
274 | } |
275 | |
276 | static void |
277 | _gtk_css_image_url_init (GtkCssImageUrl *image_url) |
278 | { |
279 | } |
280 | |
281 | |