]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/actions/growtoedge.c
Forgot to init restack sibling to NULL
[mikachu/openbox.git] / openbox / actions / growtoedge.c
1 #include "openbox/actions.h"
2 #include "openbox/misc.h"
3 #include "openbox/client.h"
4 #include "openbox/frame.h"
5 #include "openbox/screen.h"
6 #include <glib.h>
7
8 typedef struct {
9     ObDirection dir;
10     gboolean shrink;
11     gboolean fill;
12 } Options;
13
14 static gpointer setup_grow_func(xmlNodePtr node);
15 static gpointer setup_fill_func(xmlNodePtr node);
16 static gpointer setup_shrink_func(xmlNodePtr node);
17 static void free_func(gpointer o);
18 static gboolean run_func(ObActionsData *data, gpointer options);
19 /* 3.4-compatibility */
20 static gpointer setup_north_func(xmlNodePtr node);
21 static gpointer setup_south_func(xmlNodePtr node);
22 static gpointer setup_east_func(xmlNodePtr node);
23 static gpointer setup_west_func(xmlNodePtr node);
24
25 void action_growtoedge_startup(void)
26 {
27     actions_register("GrowToEdge", setup_grow_func,
28                      free_func, run_func);
29     actions_register("GrowToFill", setup_fill_func,
30                      free_func, run_func);
31     actions_register("ShrinkToEdge", setup_shrink_func,
32                      free_func, run_func);
33     /* 3.4-compatibility */
34     actions_register("GrowToEdgeNorth", setup_north_func, free_func, run_func);
35     actions_register("GrowToEdgeSouth", setup_south_func, free_func, run_func);
36     actions_register("GrowToEdgeEast", setup_east_func, free_func, run_func);
37     actions_register("GrowToEdgeWest", setup_west_func, free_func, run_func);
38 }
39
40 static gpointer setup_func(xmlNodePtr node)
41 {
42     xmlNodePtr n;
43     Options *o;
44
45     o = g_slice_new0(Options);
46     o->dir = OB_DIRECTION_NORTH;
47
48     if ((n = obt_xml_find_node(node, "direction"))) {
49         gchar *s = obt_xml_node_string(n);
50         if (!g_ascii_strcasecmp(s, "north") ||
51             !g_ascii_strcasecmp(s, "up"))
52             o->dir = OB_DIRECTION_NORTH;
53         else if (!g_ascii_strcasecmp(s, "south") ||
54                  !g_ascii_strcasecmp(s, "down"))
55             o->dir = OB_DIRECTION_SOUTH;
56         else if (!g_ascii_strcasecmp(s, "west") ||
57                  !g_ascii_strcasecmp(s, "left"))
58             o->dir = OB_DIRECTION_WEST;
59         else if (!g_ascii_strcasecmp(s, "east") ||
60                  !g_ascii_strcasecmp(s, "right"))
61             o->dir = OB_DIRECTION_EAST;
62         g_free(s);
63     }
64
65     return o;
66 }
67
68 static gpointer setup_grow_func(xmlNodePtr node)
69 {
70     Options *o;
71
72     o = setup_func(node);
73     o->shrink = FALSE;
74     o->fill = FALSE;
75
76     return o;
77 }
78
79 static gpointer setup_fill_func(xmlNodePtr node)
80 {
81     Options *o;
82
83     o = setup_func(node);
84     o->shrink = FALSE;
85     o->fill = TRUE;
86
87     return o;
88 }
89
90 static gpointer setup_shrink_func(xmlNodePtr node)
91 {
92     Options *o;
93
94     o = setup_func(node);
95     o->shrink = TRUE;
96     o->fill = FALSE;
97
98     return o;
99 }
100
101 static gboolean do_grow(ObActionsData *data, gint x, gint y, gint w, gint h)
102 {
103     gint realw, realh, lw, lh;
104
105     realw = w;
106     realh = h;
107     client_try_configure(data->client, &x, &y, &realw, &realh,
108                          &lw, &lh, TRUE);
109     /* if it's going to be resized smaller than it intended, don't
110        move the window over */
111     if (x != data->client->area.x) x += w - realw;
112     if (y != data->client->area.y) y += h - realh;
113
114     if (x != data->client->area.x || y != data->client->area.y ||
115         realw != data->client->area.width ||
116         realh != data->client->area.height)
117     {
118         actions_client_move(data, TRUE);
119         client_move_resize(data->client, x, y, realw, realh);
120         actions_client_move(data, FALSE);
121         return TRUE;
122     }
123     return FALSE;
124 }
125
126 static gboolean do_grow_all_edges(ObActionsData* data,
127                                   ObClientDirectionalResizeType resize_type)
128 {
129     gint x, y, w, h;
130     gint temp_x, temp_y, temp_w, temp_h;
131
132     client_find_resize_directional(data->client,
133                                    OB_DIRECTION_NORTH,
134                                    resize_type,
135                                    &temp_x, &temp_y, &temp_w, &temp_h);
136     y = temp_y;
137     h = temp_h;
138
139     client_find_resize_directional(data->client,
140                                    OB_DIRECTION_SOUTH,
141                                    resize_type,
142                                    &temp_x, &temp_y, &temp_w, &temp_h);
143     h += temp_h - data->client->area.height;
144
145
146     client_find_resize_directional(data->client,
147                                    OB_DIRECTION_WEST,
148                                    resize_type,
149                                    &temp_x, &temp_y, &temp_w, &temp_h);
150     x = temp_x;
151     w = temp_w;
152
153     client_find_resize_directional(data->client,
154                                    OB_DIRECTION_EAST,
155                                    resize_type,
156                                    &temp_x, &temp_y, &temp_w, &temp_h);
157     w += temp_w - data->client->area.width;
158
159     /* When filling, we allow the window to move to an arbitrary x/y
160        position, since we'll be growing the other edge as well. */
161     int lw, lh;
162     client_try_configure(data->client, &x, &y, &w, &h, &lw, &lh, TRUE);
163
164     if (x == data->client->area.x &&
165         y == data->client->area.y &&
166         w == data->client->area.width &&
167         h == data->client->area.height)
168     {
169         return FALSE;
170     }
171
172     actions_client_move(data, TRUE);
173     client_move_resize(data->client, x, y, w, h);
174     actions_client_move(data, FALSE);
175     return TRUE;
176 }
177
178 static void free_func(gpointer o)
179 {
180     g_slice_free(Options, o);
181 }
182
183 /* Always return FALSE because its not interactive */
184 static gboolean run_func(ObActionsData *data, gpointer options)
185 {
186     Options *o = options;
187
188     if (!data->client || actions_client_locked(data))
189     {
190         return FALSE;
191     }
192
193     gboolean doing_vertical_resize =
194         o->dir == OB_DIRECTION_NORTH ||
195         o->dir == OB_DIRECTION_SOUTH ||
196         o->fill;
197     if (data->client->shaded && doing_vertical_resize)
198             return FALSE;
199
200     if (o->fill) {
201         if (o->shrink) {
202             /* We don't have any implementation of shrinking for the FillToGrow
203                action. */
204             return FALSE;
205         }
206
207         if (do_grow_all_edges(data, CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE))
208             return FALSE;
209
210         /* If all the edges are blocked, then allow them to jump past their
211            current block points. */
212         do_grow_all_edges(data, CLIENT_RESIZE_GROW);
213         return FALSE;
214     }
215
216     if (!o->shrink) {
217         gint x, y, w, h;
218
219         /* Try grow. */
220         client_find_resize_directional(data->client,
221                                        o->dir,
222                                        CLIENT_RESIZE_GROW,
223                                        &x, &y, &w, &h);
224
225         if (do_grow(data, x, y, w, h))
226             return FALSE;
227     }
228
229     /* We couldn't grow, so try shrink! */
230     ObDirection opposite =
231         (o->dir == OB_DIRECTION_NORTH ? OB_DIRECTION_SOUTH :
232          (o->dir == OB_DIRECTION_SOUTH ? OB_DIRECTION_NORTH :
233           (o->dir == OB_DIRECTION_EAST ? OB_DIRECTION_WEST :
234            OB_DIRECTION_EAST)));
235
236     gint x, y, w, h;
237     gint half;
238
239     client_find_resize_directional(data->client,
240                                    opposite,
241                                    CLIENT_RESIZE_SHRINK,
242                                    &x, &y, &w, &h);
243
244     switch (opposite) {
245     case OB_DIRECTION_NORTH:
246         half = data->client->area.y + data->client->area.height / 2;
247         if (y > half) {
248             h += y - half;
249             y = half;
250         }
251         break;
252     case OB_DIRECTION_SOUTH:
253         half = data->client->area.height / 2;
254         if (h < half)
255             h = half;
256         break;
257     case OB_DIRECTION_WEST:
258         half = data->client->area.x + data->client->area.width / 2;
259         if (x > half) {
260             w += x - half;
261             x = half;
262         }
263         break;
264     case OB_DIRECTION_EAST:
265         half = data->client->area.width / 2;
266         if (w < half)
267             w = half;
268         break;
269     default: g_assert_not_reached();
270     }
271     if (do_grow(data, x, y, w, h))
272         return FALSE;
273
274     return FALSE;
275 }
276
277 /* 3.4-compatibility */
278 static gpointer setup_north_func(xmlNodePtr node)
279 {
280     Options *o = g_slice_new0(Options);
281     o->shrink = FALSE;
282     o->dir = OB_DIRECTION_NORTH;
283     return o;
284 }
285
286 static gpointer setup_south_func(xmlNodePtr node)
287 {
288     Options *o = g_slice_new0(Options);
289     o->shrink = FALSE;
290     o->dir = OB_DIRECTION_SOUTH;
291     return o;
292 }
293
294 static gpointer setup_east_func(xmlNodePtr node)
295 {
296     Options *o = g_slice_new0(Options);
297     o->shrink = FALSE;
298     o->dir = OB_DIRECTION_EAST;
299     return o;
300 }
301
302 static gpointer setup_west_func(xmlNodePtr node)
303 {
304     Options *o = g_slice_new0(Options);
305     o->shrink = FALSE;
306     o->dir = OB_DIRECTION_WEST;
307     return o;
308 }