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
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
;
38 static void lucca_layout_init(GTypeInstance
* instance
, gpointer g_class
) {
39 LuccaLayout
* layout
= LUCCA_LAYOUT(instance
);
43 layout
->border_size
= 0;
46 layout
->dispose_has_run
= FALSE
;
49 static void _unref_tile(gpointer data
, gpointer user_data
) {
50 LuccaLayoutTile
* tile
= LUCCA_LAYOUTTILE(data
);
54 static void lucca_layout_dispose(GObject
* obj
) {
55 LuccaLayout
* layout
= LUCCA_LAYOUT(obj
);
57 if (layout
->dispose_has_run
) {
60 layout
->dispose_has_run
= TRUE
;
62 /* The layout should currently hold no references because of deinit() */
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
);
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
) {
79 g_value_set_uint(value
, layout
->width
);
82 g_value_set_uint(value
, layout
->height
);
85 g_value_set_uint(value
, layout
->border_size
);
88 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
,property_id
,pspec
);
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
);
98 /* method overrides */
99 gobclass
->dispose
= lucca_layout_dispose
;
100 gobclass
->get_property
= lucca_layout_get_property
;
103 pspec
= g_param_spec_uint(
106 "Get layout width in pixels",
107 0, /* minimum value */
108 G_MAXUINT
, /* maximum value */
109 0, /* default value */
111 g_object_class_install_property(gobclass
, PROP_WIDTH
, pspec
);
113 pspec
= g_param_spec_uint(
116 "Get layout height in pixels",
117 0, /* minimum value */
118 G_MAXUINT
, /* maximum value */
119 0, /* default value */
121 g_object_class_install_property(gobclass
, PROP_HEIGHT
, pspec
);
123 pspec
= g_param_spec_uint(
126 "Get size of gaps between tiles in pixels",
127 0, /* minimum value */
128 G_MAXUINT
, /* maximum value */
129 0, /* default value */
131 g_object_class_install_property(gobclass
, PROP_BORDERSIZE
, pspec
);
134 klass
->new_tile_signal_id
= g_signal_new(
136 G_TYPE_FROM_CLASS(g_class
),
138 0, /* class_offset */
139 NULL
, /* accumulator */
140 NULL
, /* accu_data */
141 g_cclosure_marshal_VOID__OBJECT
,
144 LUCCA_TYPE_LAYOUTTILE
147 parent_class
= g_type_class_peek_parent(klass
);
150 GType
lucca_layout_get_type() {
151 static GType 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
),
164 type
= g_type_register_static (G_TYPE_OBJECT
, "LuccaLayoutType", &info
, 0);
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
;
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
);
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
) {
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 */
235 void lucca_layout_resize(LuccaLayout
* layout
, guint width
, guint height
, guint border_size
) {
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
) {
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
))
309 GList
* lucca_layout_get_tiles_in_rectangle(LuccaLayout
* layout
, gint x
, gint y
, gint width
, gint height
) {
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
);
328 GList
* lucca_layout_get_tiles(LuccaLayout
* layout
) {
332 for (i
=layout
->tiles
; i
!= NULL
; i
=i
->next
) {
333 result
= g_list_prepend(result
, i
->data
);
339 LuccaLayoutTile
* lucca_layout_newtile_at_point(LuccaLayout
* layout
, gint x
, gint y
) {
343 for (i
=layout
->tiles
; i
!= NULL
; i
=i
->next
) {
344 t
= LUCCA_LAYOUTTILE(i
->data
);
349 if (lucca_rect_contains_point(t
->new_rect
, x
, y
))