]> icculus.org git repositories - dana/openbox.git/blob - openbox/client_set.c
Make warnings about parse problems in .desktop files "debug" messages. Most people...
[dana/openbox.git] / openbox / client_set.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    client_set.c for the Openbox window manager
4    Copyright (c) 2011        Dana Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "client_set.h"
20 #include "client.h"
21 #include "event.h"
22
23 #include <glib.h>
24
25 struct _ObClientSet {
26     guint ref;
27     gboolean all;
28     GHashTable *h;
29 };
30
31 static void client_destroyed(ObClient *client, ObClientSet *set)
32 {
33     g_hash_table_remove(set->h, &client->window);
34 }
35
36 static void client_set_create_hash(ObClientSet *set)
37 {
38     set->h = g_hash_table_new(g_int_hash, g_int_equal);
39     client_add_destroy_notify((ObClientCallback)client_destroyed, set);
40 }
41
42 static void client_set_destroy_hash(ObClientSet *set)
43 {
44     g_hash_table_unref(set->h);
45     client_remove_destroy_notify_data(
46         (ObClientCallback)client_destroyed, set);
47     set->h = NULL;
48 }
49
50 ObClientSet* client_set_empty(void)
51 {
52     ObClientSet *set;
53
54     set = g_slice_new(ObClientSet);
55     set->all = FALSE;
56     set->h = NULL;
57     return set;
58 }
59
60 ObClientSet* client_set_single(ObClient *c)
61 {
62     ObClientSet *set;
63
64     set = client_set_empty();
65     if (c) {
66         client_set_create_hash(set);
67         g_hash_table_insert(set->h, &c->window, c);
68     }
69     return set;
70 }
71
72 /*! Returns a new set of clients with all possible client in it.*/
73 ObClientSet* client_set_all(void)
74 {
75     ObClientSet *set;
76
77     if (!client_list) return NULL;
78     set = g_slice_new(ObClientSet);
79     set->all = TRUE;
80     set->h = NULL;
81     return set;
82 }
83
84 static void foreach_clone(gpointer k, gpointer v, gpointer u)
85 {
86     ObClient *c = v;
87     GHashTable *seth = u;
88     g_hash_table_insert(seth, &c->window, c);
89 }
90
91 ObClientSet* client_set_clone(const ObClientSet *a)
92 {
93     ObClientSet *set;
94
95     if (!a) return NULL;
96     set = g_slice_new(ObClientSet);
97     set->all = a->all;
98     if (!a->h) set->h = NULL;
99     else {
100         client_set_create_hash(set);
101         g_hash_table_foreach(a->h, foreach_clone, set->h);
102     }
103     return set;
104 }
105
106 void client_set_destroy(ObClientSet *set)
107 {
108     if (set) {
109         if (!set->all) {
110             if (set->h) {
111                 client_remove_destroy_notify_data(
112                     (ObClientCallback)client_destroyed, set);
113                 g_hash_table_destroy(set->h);
114             }
115         }
116         g_slice_free(ObClientSet, set);
117     }
118 }
119
120 static void foreach_union(gpointer k, gpointer v, gpointer u)
121 {
122     GHashTable *set = u;
123     g_hash_table_insert(set, k, v); /* add everything in the other set */
124 }
125
126 /* Returns a new set which contains all clients in either @a or @b.  The sets
127    @a and @b are considered freed once passed to this function.
128 */
129 ObClientSet* client_set_union(ObClientSet *a, ObClientSet *b)
130 {
131     g_return_val_if_fail(a != NULL, NULL);
132     g_return_val_if_fail(b != NULL, NULL);
133
134     if (a == b)
135         return a;
136     if (a->all) {
137         client_set_destroy(b);
138         return a;
139     }
140     if (b->all) {
141         client_set_destroy(a);
142         return b;
143     }
144     if (!a->h) {
145         client_set_destroy(a);
146         return b;
147     }
148     if (!b->h) {
149         client_set_destroy(b);
150         return a;
151     }
152
153     g_hash_table_foreach(b->h, foreach_union, a->h);
154     client_set_destroy(b);
155     return a;
156 }
157
158 static gboolean foreach_intersection(gpointer k, gpointer v, gpointer u)
159 {
160     GHashTable *set = u;
161     return !g_hash_table_lookup(set, k); /* remove if not in the other set */
162 }
163
164 /* Returns a new set which contains all clients in both @a and @b.  The sets
165    @a and @b are considered freed once passed to this function.
166 */
167 ObClientSet* client_set_intersection(ObClientSet *a, ObClientSet *b)
168 {
169     g_return_val_if_fail(a != NULL, NULL);
170     g_return_val_if_fail(b != NULL, NULL);
171
172     if (a == b)
173         return a;
174     if (a->all) {
175         client_set_destroy(a);
176         return b;
177     }
178     if (b->all) {
179         client_set_destroy(b);
180         return a;
181     }
182     if (!a->h) {
183         client_set_destroy(b);
184         return a;
185     }
186     if (!b->h) {
187         client_set_destroy(a);
188         return b;
189     }
190
191     g_hash_table_foreach_remove(a->h, foreach_intersection, b->h);
192     client_set_destroy(b);
193     return a;
194 }
195
196 static gboolean reduce_minus(struct _ObClient *c, gpointer data)
197 {
198     ObClientSet *b = data;
199     return client_set_contains(b, c);
200 }
201
202 ObClientSet* client_set_minus(ObClientSet *a, ObClientSet *b)
203 {
204     g_return_val_if_fail(a != NULL, NULL);
205     g_return_val_if_fail(b != NULL, NULL);
206
207     if (a == b) {
208         if (a->h)
209             client_set_destroy_hash(a);
210         a->all = FALSE;
211         return a;
212     }
213     if (b->all) {
214         client_set_destroy(a);
215         b->all = FALSE; /* make empty */
216         return b;
217     }
218     if (!b->h) {
219         client_set_destroy(b);
220         return a;
221     }
222     if (!a->h) {
223         client_set_destroy(b);
224         return a;
225     }
226
227     return client_set_reduce(a, reduce_minus, b);
228 }
229
230 struct ObClientSetForeachReduce {
231     ObClientSetReduceFunc f;
232     gpointer data;
233 };
234
235 static gboolean foreach_reduce(gpointer k, gpointer v, gpointer u)
236 {
237     ObClient *c = v;
238     struct ObClientSetForeachReduce *d = u;
239     return d->f(c, d->data);
240 }
241
242 static gboolean func_invert(struct _ObClient *c, gpointer data)
243 {
244     struct ObClientSetForeachReduce *d = data;
245     return !d->f(c, d->data);
246 }
247
248 ObClientSet* client_set_reduce(ObClientSet *set, ObClientSetReduceFunc f,
249                                gpointer data)
250 {
251     struct ObClientSetForeachReduce d;
252
253     g_return_val_if_fail(set != NULL, NULL);
254     g_return_val_if_fail(f != NULL, NULL);
255
256     if (set->all) {
257         struct ObClientSetForeachReduce d;
258
259         /* use set expansion on an empty set rather than building a full set
260            and then removing stuff.  but we're given a reduce function.
261            so when reduce says TRUE, we want to add (expand) it.
262            we use func_invert() to do this.
263         */
264         set->all = FALSE; /* make it empty */
265         d.f = f;
266         d.data = data;
267         return client_set_expand(set, func_invert, &d);
268     }
269
270     if (!set->h) return set; /* already empty */
271
272     d.f = f;
273     d.data = data;
274     g_hash_table_foreach_remove(set->h, foreach_reduce, &d);
275     if (g_hash_table_size(set->h) == 0)
276         client_set_destroy_hash(set);
277     return set;
278 }
279
280 ObClientSet* client_set_expand(ObClientSet *set, ObClientSetExpandFunc f,
281                                gpointer data)
282 {
283     GList *it;
284     guint avail;
285
286     g_return_val_if_fail(set != NULL, NULL);
287     g_return_val_if_fail(f != NULL, NULL);
288
289     if (set->all) return set; /* already full */
290
291     avail = 0;
292     for (it = client_list; it; it = g_list_next(it)) {
293         ObClient *c = it->data;
294         if (!set->h || !g_hash_table_lookup(set->h, &c->window))
295             if (f(c, data)) {
296                 if (!set->h)
297                     client_set_create_hash(set);
298                 g_hash_table_insert(set->h, &c->window, c);
299             }
300         ++avail;
301     }
302     if (g_hash_table_size(set->h) == avail) {
303         client_set_destroy_hash(set);
304         set->all = TRUE;
305     }
306     return set;
307 }
308
309 gboolean client_set_is_empty(const ObClientSet *set)
310 {
311     if (set->all) return client_list == NULL;
312     else return set->h == NULL;
313 }
314
315 gboolean client_set_test_boolean(const ObClientSet *set)
316 {
317     if (set->all) return TRUE;
318     else return set->h != NULL;
319 }
320
321 gboolean client_set_contains(const ObClientSet *set, struct _ObClient *c)
322 {
323     if (!c) return FALSE;
324     if (set->all) return TRUE;
325     if (!set->h) return FALSE;
326     return g_hash_table_lookup(set->h, &c->window) != NULL;
327 }
328
329 typedef struct {
330     union {
331         ObClientSetForeachFunc foreach;
332         ObClientSetRunFunc run;
333     } func;
334     const struct _ObActionListRun *run;
335     gpointer data;
336     gboolean running;
337 } ObClientSetForeachData;
338
339 void foreach_func(gpointer k, gpointer v, gpointer u)
340 {
341     ObClientSetForeachData *const d = u;
342     if (!d->running) return;
343     d->running = d->func.foreach((ObClient*)v, d->data);
344 }
345
346 void client_set_foreach(const ObClientSet *set, ObClientSetForeachFunc func,
347                         gpointer data)
348 {
349     g_return_if_fail(set != NULL);
350
351     if (set->all) {
352         GList *it;
353         for (it = client_list; it; it = g_list_next(it))
354             func(it->data, data);
355     }
356     else if (set->h) {
357         ObClientSetForeachData d;
358         d.func.foreach = func;
359         d.data = data;
360         d.running = TRUE;
361         g_hash_table_foreach(set->h, foreach_func, &d);
362     }
363 }
364
365 void run_func(gpointer k, gpointer v, gpointer u)
366 {
367     ObClientSetForeachData *const d = u;
368     if (!d->running) return;
369     d->running = d->func.run((ObClient*)v, d->run, d->data);
370 }
371
372 void client_set_run(const ObClientSet *set, const struct _ObActionListRun *run,
373                     ObClientSetRunFunc func, gpointer data)
374 {
375     g_return_if_fail(set != NULL);
376
377     if (set->all) {
378         GList *it;
379         for (it = client_list; it; it = g_list_next(it))
380             func(it->data, run, data);
381     }
382     else if (set->h) {
383         ObClientSetForeachData d;
384         d.func.run = func;
385         d.data = data;
386         d.run = run;
387         d.running = TRUE;
388         g_hash_table_foreach(set->h, run_func, &d);
389     }
390 }
391
392 guint client_set_size(const ObClientSet *set)
393 {
394     if (set->all)
395         return (unsigned)-1;
396     else if (!set->h)
397         return 0;
398     else
399         return g_hash_table_size(set->h);
400 }
401
402 GList *client_set_get_all(const ObClientSet *set)
403 {
404     if (set->all)
405         return g_list_copy(client_list);
406     else if (!set->h)
407         return NULL;
408     else
409         return g_hash_table_get_values(set->h);
410 }