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
action_growtoedge_startup(void)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
setup_func(xmlNodePtr node)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
setup_grow_func(xmlNodePtr node)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
setup_fill_func(xmlNodePtr node)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
setup_shrink_func(xmlNodePtr node)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
do_grow(ObActionsData * data,gint x,gint y,gint w,gint h)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
do_grow_all_edges(ObActionsData * data,ObClientDirectionalResizeType resize_type)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
free_func(gpointer o)178 static void free_func(gpointer o)
179 {
180 g_slice_free(Options, o);
181 }
182
183 /* Always return FALSE because its not interactive */
run_func(ObActionsData * data,gpointer options)184 static gboolean run_func(ObActionsData *data, gpointer options)
185 {
186 Options *o = options;
187
188 if (!data->client)
189 return FALSE;
190
191 gboolean doing_vertical_resize =
192 o->dir == OB_DIRECTION_NORTH ||
193 o->dir == OB_DIRECTION_SOUTH ||
194 o->fill;
195 if (data->client->shaded && doing_vertical_resize)
196 return FALSE;
197
198 if (o->fill) {
199 if (o->shrink) {
200 /* We don't have any implementation of shrinking for the FillToGrow
201 action. */
202 return FALSE;
203 }
204
205 if (do_grow_all_edges(data, CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE))
206 return FALSE;
207
208 /* If all the edges are blocked, then allow them to jump past their
209 current block points. */
210 do_grow_all_edges(data, CLIENT_RESIZE_GROW);
211 return FALSE;
212 }
213
214 if (!o->shrink) {
215 gint x, y, w, h;
216
217 /* Try grow. */
218 client_find_resize_directional(data->client,
219 o->dir,
220 CLIENT_RESIZE_GROW,
221 &x, &y, &w, &h);
222
223 if (do_grow(data, x, y, w, h))
224 return FALSE;
225 }
226
227 /* We couldn't grow, so try shrink! */
228 ObDirection opposite =
229 (o->dir == OB_DIRECTION_NORTH ? OB_DIRECTION_SOUTH :
230 (o->dir == OB_DIRECTION_SOUTH ? OB_DIRECTION_NORTH :
231 (o->dir == OB_DIRECTION_EAST ? OB_DIRECTION_WEST :
232 OB_DIRECTION_EAST)));
233
234 gint x, y, w, h;
235 gint half;
236
237 client_find_resize_directional(data->client,
238 opposite,
239 CLIENT_RESIZE_SHRINK,
240 &x, &y, &w, &h);
241
242 switch (opposite) {
243 case OB_DIRECTION_NORTH:
244 half = data->client->area.y + data->client->area.height / 2;
245 if (y > half) {
246 h += y - half;
247 y = half;
248 }
249 break;
250 case OB_DIRECTION_SOUTH:
251 half = data->client->area.height / 2;
252 if (h < half)
253 h = half;
254 break;
255 case OB_DIRECTION_WEST:
256 half = data->client->area.x + data->client->area.width / 2;
257 if (x > half) {
258 w += x - half;
259 x = half;
260 }
261 break;
262 case OB_DIRECTION_EAST:
263 half = data->client->area.width / 2;
264 if (w < half)
265 w = half;
266 break;
267 default: g_assert_not_reached();
268 }
269 if (do_grow(data, x, y, w, h))
270 return FALSE;
271
272 return FALSE;
273 }
274
275 /* 3.4-compatibility */
setup_north_func(xmlNodePtr node)276 static gpointer setup_north_func(xmlNodePtr node)
277 {
278 Options *o = g_slice_new0(Options);
279 o->shrink = FALSE;
280 o->dir = OB_DIRECTION_NORTH;
281 return o;
282 }
283
setup_south_func(xmlNodePtr node)284 static gpointer setup_south_func(xmlNodePtr node)
285 {
286 Options *o = g_slice_new0(Options);
287 o->shrink = FALSE;
288 o->dir = OB_DIRECTION_SOUTH;
289 return o;
290 }
291
setup_east_func(xmlNodePtr node)292 static gpointer setup_east_func(xmlNodePtr node)
293 {
294 Options *o = g_slice_new0(Options);
295 o->shrink = FALSE;
296 o->dir = OB_DIRECTION_EAST;
297 return o;
298 }
299
setup_west_func(xmlNodePtr node)300 static gpointer setup_west_func(xmlNodePtr node)
301 {
302 Options *o = g_slice_new0(Options);
303 o->shrink = FALSE;
304 o->dir = OB_DIRECTION_WEST;
305 return o;
306 }
307