xwm: fix keyboard focus for SDL windows with icons, including dosbox
[luccawm.git] / layout / lucca-layout.c
blob719a1e0505dcb56d29ea905d93f585499c0c83b5
1 /* Copyright (c) 2008 Vincent Povirk
3 Permission is hereby granted, free of charge, to any person
4 obtaining a copy of this software and associated documentation
5 files (the "Software"), to deal in the Software without
6 restriction, including without limitation the rights to use,
7 copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following
10 conditions:
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 OTHER DEALINGS IN THE SOFTWARE.
25 #include "lucca-layout-private.h"
27 /* Implementation of LuccaLayout */
29 static GObjectClass* parent_class = NULL;
31 /* property id's */
32 enum {
33 PROP_WIDTH=1,
34 PROP_HEIGHT,
35 PROP_BORDERSIZE,
38 static void lucca_layout_init(GTypeInstance* instance, gpointer g_class) {
39 LuccaLayout* layout = LUCCA_LAYOUT(instance);
41 layout->width = 0;
42 layout->height = 0;
43 layout->border_size = 0;
44 layout->tiles = NULL;
46 layout->dispose_has_run = FALSE;
49 static void _unref_tile(gpointer data, gpointer user_data) {
50 LuccaLayoutTile* tile = LUCCA_LAYOUTTILE(data);
52 g_object_unref(tile);
54 static void lucca_layout_dispose(GObject* obj) {
55 LuccaLayout* layout = LUCCA_LAYOUT(obj);
57 if (layout->dispose_has_run) {
58 return;
60 layout->dispose_has_run = TRUE;
62 /* The layout should currently hold no references because of deinit() */
63 if (layout->tiles) {
64 g_warning("LuccaLayout at %p was disposed before calling lucca_layout_deinit()\n", layout);
66 g_slist_foreach(layout->tiles, _unref_tile, NULL);
67 g_slist_free(layout->tiles);
68 layout->tiles = NULL;
71 G_OBJECT_CLASS(parent_class)->dispose(obj);
74 static void lucca_layout_get_property(GObject* object, guint property_id, GValue* value, GParamSpec* pspec) {
75 LuccaLayout* layout = LUCCA_LAYOUT(object);
77 switch (property_id) {
78 case PROP_WIDTH:
79 g_value_set_uint(value, layout->width);
80 break;
81 case PROP_HEIGHT:
82 g_value_set_uint(value, layout->height);
83 break;
84 case PROP_BORDERSIZE:
85 g_value_set_uint(value, layout->border_size);
86 break;
87 default:
88 G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
89 break;
93 static void lucca_layout_class_init(gpointer g_class, gpointer class_data) {
94 LuccaLayoutClass* klass = (LuccaLayoutClass*)g_class;
95 GObjectClass* gobclass = G_OBJECT_CLASS(g_class);
96 GParamSpec *pspec;
98 /* method overrides */
99 gobclass->dispose = lucca_layout_dispose;
100 gobclass->get_property = lucca_layout_get_property;
102 /* properties */
103 pspec = g_param_spec_uint(
104 "width",
105 "Layout width",
106 "Get layout width in pixels",
107 0, /* minimum value */
108 G_MAXUINT, /* maximum value */
109 0, /* default value */
110 G_PARAM_READABLE);
111 g_object_class_install_property(gobclass, PROP_WIDTH, pspec);
113 pspec = g_param_spec_uint(
114 "height",
115 "Layout height",
116 "Get layout height in pixels",
117 0, /* minimum value */
118 G_MAXUINT, /* maximum value */
119 0, /* default value */
120 G_PARAM_READABLE);
121 g_object_class_install_property(gobclass, PROP_HEIGHT, pspec);
123 pspec = g_param_spec_uint(
124 "border-size",
125 "Tile border size",
126 "Get size of gaps between tiles in pixels",
127 0, /* minimum value */
128 G_MAXUINT, /* maximum value */
129 0, /* default value */
130 G_PARAM_READABLE);
131 g_object_class_install_property(gobclass, PROP_BORDERSIZE, pspec);
133 /* signals */
134 klass->new_tile_signal_id = g_signal_new(
135 "new-tile",
136 G_TYPE_FROM_CLASS(g_class),
137 G_SIGNAL_RUN_LAST,
138 0, /* class_offset */
139 NULL, /* accumulator */
140 NULL, /* accu_data */
141 g_cclosure_marshal_VOID__OBJECT,
142 G_TYPE_NONE,
143 1, /* n_params */
144 LUCCA_TYPE_LAYOUTTILE
147 parent_class = g_type_class_peek_parent(klass);
150 GType lucca_layout_get_type() {
151 static GType type = 0;
152 if (type == 0) {
153 static const GTypeInfo info = {
154 sizeof (LuccaLayoutClass),
155 NULL, /* base_init */
156 NULL, /* base_finalize */
157 lucca_layout_class_init,
158 NULL, /* class_finalize */
159 NULL, /* class_data */
160 sizeof (LuccaLayout),
161 0, /* n_preallocs */
162 lucca_layout_init
164 type = g_type_register_static (G_TYPE_OBJECT, "LuccaLayoutType", &info, 0);
166 return type;
170 Method definitions.
173 void lucca_layout_initialize(LuccaLayout* layout, guint width, guint height, guint border_size, GdkGeometry geometry, GdkWindowHints geom_mask) {
174 LuccaLayoutTile* tile;
175 g_assert(layout->tiles == NULL);
176 g_assert(width >= 1 && height >= 1);
178 layout->width = width;
179 layout->height = height;
180 layout->border_size = border_size;
182 layout->rect = lucca_rect_from_xywh(0, 0, width+border_size, height+border_size);
184 /* The given geometry cannot have a minimum size that is larger than the layout */
185 if (geom_mask&GDK_HINT_MIN_SIZE) {
186 g_assert(geometry.min_width <= width);
187 g_assert(geometry.min_height <= height);
190 tile = g_object_new(LUCCA_TYPE_LAYOUTTILE, NULL);
191 tile->layout = layout;
192 g_object_ref(layout);
193 tile->rect = layout->rect;
194 tile->valid = TRUE;
196 lucca_layouttile_set_geometry_hints(tile, geometry, geom_mask);
198 layout->tiles = g_slist_prepend(layout->tiles, tile);
200 /* emit the new-tile signal */
201 g_signal_emit(layout, LUCCA_LAYOUT_GET_CLASS(layout)->new_tile_signal_id, 0, tile);
204 static void _invalidate_tile(gpointer data, gpointer user_data) {
205 LuccaLayoutTile* tile = LUCCA_LAYOUTTILE(data);
207 tile->valid = FALSE;
209 static void _emitdelete_tile(gpointer data, gpointer user_data) {
210 LuccaLayoutTile* tile = LUCCA_LAYOUTTILE(data);
212 g_signal_emit(tile, LUCCA_LAYOUTTILE_GET_CLASS(tile)->delete_signal_id, 0);
214 void lucca_layout_deinit(LuccaLayout* layout) {
215 GSList* tiles;
217 tiles = layout->tiles;
219 /* Mark all tiles as invalid */
220 g_slist_foreach(tiles, _invalidate_tile, NULL);
222 /* "Remove" all tiles from the layout. */
223 layout->tiles = NULL;
225 /* Emit a delete signal on each tile*/
226 g_slist_foreach(tiles, _emitdelete_tile, NULL);
228 /* Actually unref the tiles */
229 g_slist_foreach(tiles, _unref_tile, NULL);
231 /* Delete the linked list of tiles */
232 g_slist_free(tiles);
235 void lucca_layout_resize(LuccaLayout* layout, guint width, guint height, guint border_size) {
236 GSList* item;
237 guint old_width;
238 guint new_width;
239 guint old_height;
240 guint new_height;
241 GSList* configured=NULL;
242 GSList* deleted=NULL;
244 g_assert(width >= 1 && height >= 1);
246 old_width = layout->width+layout->border_size;
247 new_width = width+border_size;
248 old_height = layout->height+layout->border_size;
249 new_height = height+border_size;
251 for (item=layout->tiles; item != NULL; item=item->next) {
252 LuccaLayoutTile* tile = LUCCA_LAYOUTTILE(item->data);
254 tile->rect.c[LUCCA_SIDE_LEFT] = tile->rect.c[LUCCA_SIDE_LEFT] * new_width / old_width;
255 tile->rect.c[LUCCA_SIDE_RIGHT] = (tile->rect.c[LUCCA_SIDE_RIGHT]+1) * new_width / old_width -1;
256 tile->rect.c[LUCCA_SIDE_TOP] = tile->rect.c[LUCCA_SIDE_TOP] * new_height / old_height;
257 tile->rect.c[LUCCA_SIDE_BOTTOM] = (tile->rect.c[LUCCA_SIDE_BOTTOM]+1) * new_height / old_height -1;
260 layout->width = width;
261 layout->height = height;
262 layout->border_size = border_size;
264 /* check if this would make any tiles too small */
265 for (item=layout->tiles; item != NULL; item=item->next) {
266 LuccaLayoutTile* tile = LUCCA_LAYOUTTILE(item->data);
268 /* NOTE: lucca_layout_ensure_size may delete other items from layout->tiles, but this is safe because it's a linked list */
269 lucca_layouttile_ensure_size(tile, &configured, &deleted);
272 /* emit delete events */
273 for (item=deleted; item != NULL; item=item->next) {
274 LuccaLayoutTile* tile = LUCCA_LAYOUTTILE(item->data);
276 g_signal_emit(tile, LUCCA_LAYOUTTILE_GET_CLASS(tile)->delete_signal_id, 0);
279 /* it's unlikely that any tile's size is unchanged so emit a configure event for all of them */
280 for (item=layout->tiles; item != NULL; item=item->next) {
281 LuccaLayoutTile* tile = LUCCA_LAYOUTTILE(item->data);
283 g_signal_emit(tile, LUCCA_LAYOUTTILE_GET_CLASS(tile)->configure_signal_id, 0);
286 /* unref deleted tiles */
287 for (item=deleted; item != NULL; item=item->next) {
288 LuccaLayoutTile* tile = LUCCA_LAYOUTTILE(item->data);
291 g_object_unref(tile);
295 LuccaLayoutTile* lucca_layout_get_tile_at_position(LuccaLayout* layout, gint x, gint y) {
296 GSList* i;
297 LuccaLayoutTile* t;
299 for (i=layout->tiles; i != NULL; i=i->next) {
300 t = LUCCA_LAYOUTTILE(i->data);
302 if (lucca_rect_contains_point(tile_rect(t), x, y))
303 return t;
306 return NULL;
309 GList* lucca_layout_get_tiles_in_rectangle(LuccaLayout* layout, gint x, gint y, gint width, gint height) {
310 GList* result=NULL;
311 GSList* i;
312 LuccaLayoutTile* t;
313 LuccaRect r;
315 r = lucca_rect_from_xywh(x, y, width, height);
317 for (i=layout->tiles; i != NULL; i=i->next) {
318 t = LUCCA_LAYOUTTILE(i->data);
320 if (lucca_rects_intersect(tile_rect(t), r)) {
321 result = g_list_prepend(result, (gpointer)t);
325 return result;
328 GList* lucca_layout_get_tiles(LuccaLayout* layout) {
329 GList* result=NULL;
330 GSList* i;
332 for (i=layout->tiles; i != NULL; i=i->next) {
333 result = g_list_prepend(result, i->data);
336 return result;
339 LuccaLayoutTile* lucca_layout_newtile_at_point(LuccaLayout* layout, gint x, gint y) {
340 GSList* i;
341 LuccaLayoutTile* t;
343 for (i=layout->tiles; i != NULL; i=i->next) {
344 t = LUCCA_LAYOUTTILE(i->data);
346 if (!t->new_valid)
347 continue;
349 if (lucca_rect_contains_point(t->new_rect, x, y))
350 return t;
353 return NULL;