]> icculus.org git repositories - dana/openbox.git/blob - openbox/actions/growtoedge.c
Created a 'GrowToFill' action. (Bug 3356)
[dana/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_func(xmlNodePtr node);
15 static gpointer setup_shrink_func(xmlNodePtr node);
16 static void free_func(gpointer o);
17 static gboolean run_func(ObActionsData *data, gpointer options);
18 /* 3.4-compatibility */
19 static gpointer setup_north_func(xmlNodePtr node);
20 static gpointer setup_south_func(xmlNodePtr node);
21 static gpointer setup_east_func(xmlNodePtr node);
22 static gpointer setup_west_func(xmlNodePtr node);
23 static gpointer setup_fill_func(xmlNodePtr node);
24
25 void action_growtoedge_startup(void)
26 {
27     actions_register("GrowToEdge", setup_func,
28                      free_func, run_func);
29     actions_register("ShrinkToEdge", setup_shrink_func,
30                      free_func, run_func);
31     /* 3.4-compatibility */
32     actions_register("GrowToEdgeNorth", setup_north_func, free_func, run_func);
33     actions_register("GrowToEdgeSouth", setup_south_func, free_func, run_func);
34     actions_register("GrowToEdgeEast", setup_east_func, free_func, run_func);
35     actions_register("GrowToEdgeWest", setup_west_func, free_func, run_func);
36     actions_register("GrowToFill", setup_fill_func, free_func, run_func);
37 }
38
39 static gpointer setup_func(xmlNodePtr node)
40 {
41     xmlNodePtr n;
42     Options *o;
43
44     o = g_slice_new0(Options);
45     o->dir = OB_DIRECTION_NORTH;
46     o->shrink = FALSE;
47     o->fill = FALSE;
48
49     if ((n = obt_xml_find_node(node, "direction"))) {
50         gchar *s = obt_xml_node_string(n);
51         if (!g_ascii_strcasecmp(s, "north") ||
52             !g_ascii_strcasecmp(s, "up"))
53             o->dir = OB_DIRECTION_NORTH;
54         else if (!g_ascii_strcasecmp(s, "south") ||
55                  !g_ascii_strcasecmp(s, "down"))
56             o->dir = OB_DIRECTION_SOUTH;
57         else if (!g_ascii_strcasecmp(s, "west") ||
58                  !g_ascii_strcasecmp(s, "left"))
59             o->dir = OB_DIRECTION_WEST;
60         else if (!g_ascii_strcasecmp(s, "east") ||
61                  !g_ascii_strcasecmp(s, "right"))
62             o->dir = OB_DIRECTION_EAST;
63         g_free(s);
64     }
65
66     return o;
67 }
68
69 static gpointer setup_fill_func(xmlNodePtr node)
70 {
71     Options *o;
72
73     o = setup_func(node);
74     o->fill = TRUE;
75
76     return o;
77 }
78
79
80 static gpointer setup_shrink_func(xmlNodePtr node)
81 {
82     Options *o;
83
84     o = setup_func(node);
85     o->shrink = TRUE;
86
87     return o;
88 }
89
90 static gboolean do_grow(ObActionsData *data, gint x, gint y, gint w, gint h)
91 {
92     gint realw, realh, lw, lh;
93
94     realw = w;
95     realh = h;
96     client_try_configure(data->client, &x, &y, &realw, &realh,
97                          &lw, &lh, TRUE);
98     /* if it's going to be resized smaller than it intended, don't
99        move the window over */
100     if (x != data->client->area.x) x += w - realw;
101     if (y != data->client->area.y) y += h - realh;
102
103     if (x != data->client->area.x || y != data->client->area.y ||
104         realw != data->client->area.width ||
105         realh != data->client->area.height)
106     {
107         actions_client_move(data, TRUE);
108         client_move_resize(data->client, x, y, realw, realh);
109         actions_client_move(data, FALSE);
110         return TRUE;
111     }
112     return FALSE;
113 }
114
115 static void free_func(gpointer o)
116 {
117     g_slice_free(Options, o);
118 }
119
120 /* Always return FALSE because its not interactive */
121 static gboolean run_func(ObActionsData *data, gpointer options)
122 {
123     Options *o = options;
124     gint x, y, w, h;
125
126     ObDirection opp;
127     gint half;
128
129     if (!data->client)
130         return FALSE;
131     if (data->client->shaded) {
132         gboolean doing_verical_resize =
133             o->dir == OB_DIRECTION_NORTH ||
134             o->dir == OB_DIRECTION_SOUTH ||
135             o->fill;
136         if (doing_verical_resize)
137             return FALSE;
138     }
139
140     if (o->fill) {
141         if (o->shrink) {
142             /* We don't have any implementation of shrinking for the FillToGrow
143                action. */
144             return FALSE;
145         }
146
147         gint head, size;
148         gint e_start, e_size;
149         gboolean near;
150
151         gint north_edge;
152         head = RECT_TOP(data->client->frame->area)+1;
153         size = data->client->frame->area.height;
154         e_start = RECT_LEFT(data->client->frame->area);
155         e_size = data->client->frame->area.width;
156
157         client_find_edge_directional(data->client, OB_DIRECTION_NORTH,
158                                      head, size, e_start, e_size,
159                                      &north_edge, &near);
160
161         gint south_edge;
162         head = RECT_BOTTOM(data->client->frame->area)-1;
163         size = data->client->frame->area.height;
164         e_start = RECT_LEFT(data->client->frame->area);
165         e_size = data->client->frame->area.width;
166
167         client_find_edge_directional(data->client, OB_DIRECTION_SOUTH,
168                                      head, size, e_start, e_size,
169                                      &south_edge, &near);
170
171         gint east_edge;
172         head = RECT_RIGHT(data->client->frame->area)-1;
173         size = data->client->frame->area.width;
174         e_start = RECT_TOP(data->client->frame->area);
175         e_size = data->client->frame->area.height;
176
177         client_find_edge_directional(data->client, OB_DIRECTION_EAST,
178                                      head, size, e_start, e_size,
179                                      &east_edge, &near);
180
181         gint west_edge;
182         head = RECT_LEFT(data->client->frame->area)+1;
183         size = data->client->frame->area.width;
184         e_start = RECT_TOP(data->client->frame->area);
185         e_size = data->client->frame->area.height;
186
187         client_find_edge_directional(data->client, OB_DIRECTION_WEST,
188                                      head, size, e_start, e_size,
189                                      &west_edge, &near);
190
191         /* Calculate the client pos and size, based on frame pos and size.
192          */
193
194         gint w_client_delta =
195             data->client->frame->area.width - data->client->area.width;
196         gint h_client_delta =
197             data->client->frame->area.height - data->client->area.height;
198
199         gint x_client_delta =
200             data->client->area.x - data->client->frame->area.x;
201         gint y_client_delta =
202             data->client->area.y - data->client->frame->area.y;
203
204         x = west_edge + x_client_delta + 1;
205         y = north_edge + y_client_delta + 1;
206
207         w = east_edge - west_edge - w_client_delta - 1;
208         h = south_edge - north_edge - h_client_delta - 1;
209
210         /* grow passing client pos and size */
211
212         do_grow(data, x, y, w, h);
213         return FALSE;
214     }
215
216     if (!o->shrink) {
217         /* Try grow. */
218         client_find_resize_directional(data->client, o->dir, TRUE,
219                                        &x, &y, &w, &h);
220
221         if (do_grow(data, x, y, w, h))
222             return FALSE;
223     }
224
225     /* We couldn't grow, so try shrink! */
226     opp = (o->dir == OB_DIRECTION_NORTH ? OB_DIRECTION_SOUTH :
227            (o->dir == OB_DIRECTION_SOUTH ? OB_DIRECTION_NORTH :
228             (o->dir == OB_DIRECTION_EAST ? OB_DIRECTION_WEST :
229              OB_DIRECTION_EAST)));
230     client_find_resize_directional(data->client, opp, FALSE,
231                                    &x, &y, &w, &h);
232     switch (opp) {
233     case OB_DIRECTION_NORTH:
234         half = data->client->area.y + data->client->area.height / 2;
235         if (y > half) {
236             h += y - half;
237             y = half;
238         }
239         break;
240     case OB_DIRECTION_SOUTH:
241         half = data->client->area.height / 2;
242         if (h < half)
243             h = half;
244         break;
245     case OB_DIRECTION_WEST:
246         half = data->client->area.x + data->client->area.width / 2;
247         if (x > half) {
248             w += x - half;
249             x = half;
250         }
251         break;
252     case OB_DIRECTION_EAST:
253         half = data->client->area.width / 2;
254         if (w < half)
255             w = half;
256         break;
257     default: g_assert_not_reached();
258     }
259     if (do_grow(data, x, y, w, h))
260         return FALSE;
261
262     return FALSE;
263 }
264
265 /* 3.4-compatibility */
266 static gpointer setup_north_func(xmlNodePtr node)
267 {
268     Options *o = g_slice_new0(Options);
269     o->shrink = FALSE;
270     o->dir = OB_DIRECTION_NORTH;
271     return o;
272 }
273
274 static gpointer setup_south_func(xmlNodePtr node)
275 {
276     Options *o = g_slice_new0(Options);
277     o->shrink = FALSE;
278     o->dir = OB_DIRECTION_SOUTH;
279     return o;
280 }
281
282 static gpointer setup_east_func(xmlNodePtr node)
283 {
284     Options *o = g_slice_new0(Options);
285     o->shrink = FALSE;
286     o->dir = OB_DIRECTION_EAST;
287     return o;
288 }
289
290 static gpointer setup_west_func(xmlNodePtr node)
291 {
292     Options *o = g_slice_new0(Options);
293     o->shrink = FALSE;
294     o->dir = OB_DIRECTION_WEST;
295     return o;
296 }