1 | /* gtksizerequest.c |
2 | * Copyright (C) 2007-2010 Openismus GmbH |
3 | * |
4 | * Authors: |
5 | * Mathias Hasselmann <mathias@openismus.com> |
6 | * Tristan Van Berkom <tristan.van.berkom@gmail.com> |
7 | * |
8 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Library General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2 of the License, or (at your option) any later version. |
12 | * |
13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Library General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Library General Public |
19 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | #include <config.h> |
23 | |
24 | #include "gtksizerequestcacheprivate.h" |
25 | |
26 | #include <string.h> |
27 | |
28 | void |
29 | _gtk_size_request_cache_init (SizeRequestCache *cache) |
30 | { |
31 | memset (s: cache, c: 0, n: sizeof (SizeRequestCache)); |
32 | } |
33 | |
34 | static void |
35 | free_sizes_x (SizeRequestX **sizes) |
36 | { |
37 | int i; |
38 | |
39 | for (i = 0; i < GTK_SIZE_REQUEST_CACHED_SIZES && sizes[i] != NULL; i++) |
40 | g_slice_free (SizeRequestX, sizes[i]); |
41 | |
42 | g_slice_free1 (block_size: sizeof (SizeRequestY *) * GTK_SIZE_REQUEST_CACHED_SIZES, mem_block: sizes); |
43 | } |
44 | |
45 | static void |
46 | free_sizes_y (SizeRequestY **sizes) |
47 | { |
48 | int i; |
49 | |
50 | for (i = 0; i < GTK_SIZE_REQUEST_CACHED_SIZES && sizes[i] != NULL; i++) |
51 | g_slice_free (SizeRequestY, sizes[i]); |
52 | |
53 | g_slice_free1 (block_size: sizeof (SizeRequestY *) * GTK_SIZE_REQUEST_CACHED_SIZES, mem_block: sizes); |
54 | } |
55 | |
56 | void |
57 | _gtk_size_request_cache_free (SizeRequestCache *cache) |
58 | { |
59 | if (cache->requests_x) |
60 | free_sizes_x (sizes: cache->requests_x); |
61 | if (cache->requests_y) |
62 | free_sizes_y (sizes: cache->requests_y); |
63 | } |
64 | |
65 | void |
66 | _gtk_size_request_cache_clear (SizeRequestCache *cache) |
67 | { |
68 | _gtk_size_request_cache_free (cache); |
69 | _gtk_size_request_cache_init (cache); |
70 | } |
71 | |
72 | void |
73 | _gtk_size_request_cache_commit (SizeRequestCache *cache, |
74 | GtkOrientation orientation, |
75 | int for_size, |
76 | int minimum_size, |
77 | int natural_size, |
78 | int minimum_baseline, |
79 | int natural_baseline) |
80 | { |
81 | guint i, n_sizes; |
82 | |
83 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
84 | { |
85 | g_assert (minimum_baseline == -1); |
86 | g_assert (natural_baseline == -1); |
87 | } |
88 | |
89 | /* First handle caching of the base requests */ |
90 | if (for_size < 0) |
91 | { |
92 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
93 | { |
94 | cache->cached_size_x.minimum_size = minimum_size; |
95 | cache->cached_size_x.natural_size = natural_size; |
96 | } |
97 | else |
98 | { |
99 | cache->cached_size_y.minimum_size = minimum_size; |
100 | cache->cached_size_y.natural_size = natural_size; |
101 | cache->cached_size_y.minimum_baseline = minimum_baseline; |
102 | cache->cached_size_y.natural_baseline = natural_baseline; |
103 | } |
104 | |
105 | cache->flags[orientation].cached_size_valid = TRUE; |
106 | return; |
107 | } |
108 | |
109 | /* Check if the minimum_size and natural_size is already |
110 | * in the cache and if this result can be used to extend |
111 | * that cache entry |
112 | */ |
113 | n_sizes = cache->flags[orientation].n_cached_requests; |
114 | |
115 | |
116 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
117 | { |
118 | SizeRequestX **cached_sizes = cache->requests_x; |
119 | SizeRequestX *cached_size; |
120 | |
121 | for (i = 0; i < n_sizes; i++) |
122 | { |
123 | if (cached_sizes[i]->cached_size.minimum_size == minimum_size && |
124 | cached_sizes[i]->cached_size.natural_size == natural_size) |
125 | { |
126 | cached_sizes[i]->lower_for_size = MIN (cached_sizes[i]->lower_for_size, for_size); |
127 | cached_sizes[i]->upper_for_size = MAX (cached_sizes[i]->upper_for_size, for_size); |
128 | return; |
129 | } |
130 | } |
131 | |
132 | /* If not found, pull a new size from the cache, the returned size cache |
133 | * will immediately be used to cache the new computed size so we go ahead |
134 | * and increment the last_cached_request right away */ |
135 | if (n_sizes < GTK_SIZE_REQUEST_CACHED_SIZES) |
136 | { |
137 | cache->flags[orientation].n_cached_requests++; |
138 | cache->flags[orientation].last_cached_request = cache->flags[orientation].n_cached_requests - 1; |
139 | } |
140 | else |
141 | { |
142 | if (++cache->flags[orientation].last_cached_request == GTK_SIZE_REQUEST_CACHED_SIZES) |
143 | cache->flags[orientation].last_cached_request = 0; |
144 | } |
145 | |
146 | if (cache->requests_x == NULL) |
147 | cache->requests_x = g_slice_alloc0 (block_size: sizeof (SizeRequestX *) * GTK_SIZE_REQUEST_CACHED_SIZES); |
148 | |
149 | if (cache->requests_x[cache->flags[orientation].last_cached_request] == NULL) |
150 | cache->requests_x[cache->flags[orientation].last_cached_request] = g_slice_new (SizeRequestX); |
151 | |
152 | cached_size = cache->requests_x[cache->flags[orientation].last_cached_request]; |
153 | cached_size->lower_for_size = for_size; |
154 | cached_size->upper_for_size = for_size; |
155 | cached_size->cached_size.minimum_size = minimum_size; |
156 | cached_size->cached_size.natural_size = natural_size; |
157 | } |
158 | else |
159 | { |
160 | SizeRequestY **cached_sizes = cache->requests_y; |
161 | SizeRequestY *cached_size; |
162 | |
163 | for (i = 0; i < n_sizes; i++) |
164 | { |
165 | if (cached_sizes[i]->cached_size.minimum_size == minimum_size && |
166 | cached_sizes[i]->cached_size.natural_size == natural_size && |
167 | cached_sizes[i]->cached_size.minimum_baseline == minimum_baseline && |
168 | cached_sizes[i]->cached_size.natural_baseline == natural_baseline) |
169 | { |
170 | cached_sizes[i]->lower_for_size = MIN (cached_sizes[i]->lower_for_size, for_size); |
171 | cached_sizes[i]->upper_for_size = MAX (cached_sizes[i]->upper_for_size, for_size); |
172 | return; |
173 | } |
174 | } |
175 | |
176 | /* If not found, pull a new size from the cache, the returned size cache |
177 | * will immediately be used to cache the new computed size so we go ahead |
178 | * and increment the last_cached_request right away */ |
179 | if (n_sizes < GTK_SIZE_REQUEST_CACHED_SIZES) |
180 | { |
181 | cache->flags[orientation].n_cached_requests++; |
182 | cache->flags[orientation].last_cached_request = cache->flags[orientation].n_cached_requests - 1; |
183 | } |
184 | else |
185 | { |
186 | if (++cache->flags[orientation].last_cached_request == GTK_SIZE_REQUEST_CACHED_SIZES) |
187 | cache->flags[orientation].last_cached_request = 0; |
188 | } |
189 | |
190 | if (cache->requests_y == NULL) |
191 | cache->requests_y = g_slice_alloc0 (block_size: sizeof (SizeRequestY *) * GTK_SIZE_REQUEST_CACHED_SIZES); |
192 | |
193 | if (cache->requests_y[cache->flags[orientation].last_cached_request] == NULL) |
194 | cache->requests_y[cache->flags[orientation].last_cached_request] = g_slice_new (SizeRequestY); |
195 | |
196 | cached_size = cache->requests_y[cache->flags[orientation].last_cached_request]; |
197 | cached_size->lower_for_size = for_size; |
198 | cached_size->upper_for_size = for_size; |
199 | cached_size->cached_size.minimum_size = minimum_size; |
200 | cached_size->cached_size.natural_size = natural_size; |
201 | cached_size->cached_size.minimum_baseline = minimum_baseline; |
202 | cached_size->cached_size.natural_baseline = natural_baseline; |
203 | } |
204 | } |
205 | |
206 | /* looks for a cached size request for this for_size. |
207 | * |
208 | * Note that this caching code was originally derived from |
209 | * the Clutter toolkit but has evolved for other GTK requirements. |
210 | */ |
211 | gboolean |
212 | _gtk_size_request_cache_lookup (const SizeRequestCache *cache, |
213 | GtkOrientation orientation, |
214 | int for_size, |
215 | int *minimum, |
216 | int *natural, |
217 | int *minimum_baseline, |
218 | int *natural_baseline) |
219 | { |
220 | guint i, p; |
221 | |
222 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
223 | { |
224 | if (for_size < 0) |
225 | { |
226 | if (cache->flags[GTK_ORIENTATION_HORIZONTAL].cached_size_valid) |
227 | { |
228 | const CachedSizeX *result = &cache->cached_size_x; |
229 | |
230 | *minimum = result->minimum_size; |
231 | *natural = result->natural_size; |
232 | return TRUE; |
233 | } |
234 | |
235 | return FALSE; |
236 | } |
237 | else |
238 | { |
239 | /* Search for an already cached size */ |
240 | for (i = 0, p = cache->flags[GTK_ORIENTATION_HORIZONTAL].n_cached_requests; i < p; i++) |
241 | { |
242 | const SizeRequestX *cur = cache->requests_x[i]; |
243 | |
244 | if (cur->lower_for_size <= for_size && |
245 | cur->upper_for_size >= for_size) |
246 | { |
247 | const CachedSizeX *result = &cur->cached_size; |
248 | |
249 | *minimum = result->minimum_size; |
250 | *natural = result->natural_size; |
251 | |
252 | return TRUE; |
253 | } |
254 | } |
255 | |
256 | return FALSE; |
257 | } |
258 | } |
259 | else |
260 | { |
261 | if (for_size < 0) |
262 | { |
263 | if (cache->flags[GTK_ORIENTATION_VERTICAL].cached_size_valid) |
264 | { |
265 | const CachedSizeY *result = &cache->cached_size_y; |
266 | |
267 | *minimum = result->minimum_size; |
268 | *natural = result->natural_size; |
269 | *minimum_baseline = result->minimum_baseline; |
270 | *natural_baseline = result->natural_baseline; |
271 | return TRUE; |
272 | } |
273 | |
274 | return FALSE; |
275 | } |
276 | else |
277 | { |
278 | /* Search for an already cached size */ |
279 | for (i = 0, p = cache->flags[GTK_ORIENTATION_VERTICAL].n_cached_requests; i < p; i++) |
280 | { |
281 | const SizeRequestY *cur = cache->requests_y[i]; |
282 | |
283 | if (cur->lower_for_size <= for_size && |
284 | cur->upper_for_size >= for_size) |
285 | { |
286 | const CachedSizeY *result = &cur->cached_size; |
287 | |
288 | *minimum = result->minimum_size; |
289 | *natural = result->natural_size; |
290 | *minimum_baseline = result->minimum_baseline; |
291 | *natural_baseline = result->natural_baseline; |
292 | return TRUE; |
293 | } |
294 | } |
295 | |
296 | return FALSE; |
297 | } |
298 | } |
299 | } |
300 | |
301 | |