]> icculus.org git repositories - dana/openbox.git/blob - openbox/action.c
stacked and linear cycling
[dana/openbox.git] / openbox / action.c
1 #include "client.h"
2 #include "focus.h"
3 #include "stacking.h"
4 #include "frame.h"
5 #include "screen.h"
6 #include "action.h"
7 #include "dispatch.h"
8 #include "openbox.h"
9
10 #include <glib.h>
11
12 Action *action_new(void (*func)(union ActionData *data))
13 {
14     Action *a = g_new0(Action, 1);
15     a->func = func;
16
17     /* deal with pointers */
18     if (func == action_execute)
19         a->data.execute.path = NULL;
20
21     return a;
22 }
23
24 void action_free(Action *a)
25 {
26     if (a == NULL) return;
27
28     /* deal with pointers */
29     if (a->func == action_execute || a->func == action_restart)
30         g_free(a->data.execute.path);
31
32     g_free(a);
33 }
34
35 Action *action_from_string(char *name)
36 {
37     Action *a = NULL;
38     if (!g_ascii_strcasecmp(name, "execute")) {
39         a = action_new(action_execute);
40     } else if (!g_ascii_strcasecmp(name, "focus")) {
41         a = action_new(action_focus);
42     } else if (!g_ascii_strcasecmp(name, "unfocus")) {
43         a = action_new(action_unfocus);
44     } else if (!g_ascii_strcasecmp(name, "iconify")) {
45         a = action_new(action_iconify);
46     } else if (!g_ascii_strcasecmp(name, "raise")) {
47         a = action_new(action_raise);
48     } else if (!g_ascii_strcasecmp(name, "lower")) {
49         a = action_new(action_lower);
50     } else if (!g_ascii_strcasecmp(name, "focusraise")) {
51         a = action_new(action_focusraise);
52     } else if (!g_ascii_strcasecmp(name, "close")) {
53         a = action_new(action_close);
54     } else if (!g_ascii_strcasecmp(name, "kill")) {
55         a = action_new(action_kill);
56     } else if (!g_ascii_strcasecmp(name, "shadelower")) {
57         a = action_new(action_shadelower);
58     } else if (!g_ascii_strcasecmp(name, "unshaderaise")) {
59         a = action_new(action_unshaderaise);
60     } else if (!g_ascii_strcasecmp(name, "shade")) {
61         a = action_new(action_shade);
62     } else if (!g_ascii_strcasecmp(name, "unshade")) {
63         a = action_new(action_unshade);
64     } else if (!g_ascii_strcasecmp(name, "toggleshade")) {
65         a = action_new(action_toggle_shade);
66     } else if (!g_ascii_strcasecmp(name, "toggleomnipresent")) {
67         a = action_new(action_toggle_omnipresent);
68     } else if (!g_ascii_strcasecmp(name, "moverelativehorz")) {
69         a = action_new(action_move_relative_horz);
70     } else if (!g_ascii_strcasecmp(name, "moverelativevert")) {
71         a = action_new(action_move_relative_vert);
72     } else if (!g_ascii_strcasecmp(name, "resizerelativehorz")) {
73         a = action_new(action_resize_relative_horz);
74     } else if (!g_ascii_strcasecmp(name, "resizerelativevert")) {
75         a = action_new(action_resize_relative_vert);
76     } else if (!g_ascii_strcasecmp(name, "maximizefull")) {
77         a = action_new(action_maximize_full);
78     } else if (!g_ascii_strcasecmp(name, "unmaximizefull")) {
79         a = action_new(action_unmaximize_full);
80     } else if (!g_ascii_strcasecmp(name, "togglemaximizefull")) {
81         a = action_new(action_toggle_maximize_full);
82     } else if (!g_ascii_strcasecmp(name, "maximizehorz")) {
83         a = action_new(action_maximize_horz);
84     } else if (!g_ascii_strcasecmp(name, "unmaximizehorz")) {
85         a = action_new(action_unmaximize_horz);
86     } else if (!g_ascii_strcasecmp(name, "togglemaximizehorz")) {
87         a = action_new(action_toggle_maximize_horz);
88     } else if (!g_ascii_strcasecmp(name, "maximizevert")) {
89         a = action_new(action_maximize_vert);
90     } else if (!g_ascii_strcasecmp(name, "unmaximizevert")) {
91         a = action_new(action_unmaximize_vert);
92     } else if (!g_ascii_strcasecmp(name, "togglemaximizevert")) {
93         a = action_new(action_toggle_maximize_vert);
94     } else if (!g_ascii_strcasecmp(name, "sendtodesktop")) {
95         a = action_new(action_send_to_desktop);
96         a->data.sendto.follow = TRUE;
97     } else if (!g_ascii_strcasecmp(name, "sendtonextdesktop")) {
98         a = action_new(action_send_to_next_desktop);
99         a->data.sendtonextprev.wrap = FALSE;
100         a->data.sendtonextprev.follow = TRUE;
101     } else if (!g_ascii_strcasecmp(name, "sendtonextdesktopwrap")) {
102         a = action_new(action_send_to_next_desktop);
103         a->data.sendtonextprev.wrap = TRUE;
104         a->data.sendtonextprev.follow = TRUE;
105     } else if (!g_ascii_strcasecmp(name, "sendtopreviousdesktop")) {
106         a = action_new(action_send_to_previous_desktop);
107         a->data.sendtonextprev.wrap = FALSE;
108         a->data.sendtonextprev.follow = TRUE;
109     } else if (!g_ascii_strcasecmp(name, "sendtopreviousdesktopwrap")) {
110         a = action_new(action_send_to_previous_desktop);
111         a->data.sendtonextprev.wrap = TRUE;
112         a->data.sendtonextprev.follow = TRUE;
113     } else if (!g_ascii_strcasecmp(name, "desktop")) {
114         a = action_new(action_desktop);
115     } else if (!g_ascii_strcasecmp(name, "nextdesktop")) {
116         a = action_new(action_next_desktop);
117         a->data.nextprevdesktop.wrap = FALSE;
118     } else if (!g_ascii_strcasecmp(name, "nextdesktopwrap")) {
119         a = action_new(action_next_desktop);
120         a->data.nextprevdesktop.wrap = TRUE;
121     } else if (!g_ascii_strcasecmp(name, "previousdesktop")) {
122         a = action_new(action_previous_desktop);
123         a->data.nextprevdesktop.wrap = FALSE;
124     } else if (!g_ascii_strcasecmp(name, "previousdesktopwrap")) {
125         a = action_new(action_previous_desktop);
126         a->data.nextprevdesktop.wrap = TRUE;
127     } else if (!g_ascii_strcasecmp(name, "nextdesktopcolumn")) {
128         a = action_new(action_next_desktop_column);
129         a->data.nextprevdesktop.wrap = FALSE;
130     } else if (!g_ascii_strcasecmp(name, "nextdesktopcolumnwrap")) {
131         a = action_new(action_next_desktop_column);
132         a->data.nextprevdesktop.wrap = TRUE;
133     } else if (!g_ascii_strcasecmp(name, "previousdesktopcolumn")) {
134         a = action_new(action_previous_desktop_column);
135         a->data.nextprevdesktop.wrap = FALSE;
136     } else if (!g_ascii_strcasecmp(name, "previousdesktopcolumnwrap")) {
137         a = action_new(action_previous_desktop_column);
138         a->data.nextprevdesktop.wrap = TRUE;
139     } else if (!g_ascii_strcasecmp(name, "nextdesktoprow")) {
140         a = action_new(action_next_desktop_row);
141         a->data.nextprevdesktop.wrap = FALSE;
142     } else if (!g_ascii_strcasecmp(name, "nextdesktoprowwrap")) {
143         a = action_new(action_next_desktop_row);
144         a->data.nextprevdesktop.wrap = TRUE;
145     } else if (!g_ascii_strcasecmp(name, "previousdesktoprow")) {
146         a = action_new(action_previous_desktop_row);
147         a->data.nextprevdesktop.wrap = FALSE;
148     } else if (!g_ascii_strcasecmp(name, "previousdesktoprowwrap")) {
149         a = action_new(action_previous_desktop_row);
150         a->data.nextprevdesktop.wrap = TRUE;
151     } else if (!g_ascii_strcasecmp(name, "toggledecorations")) {
152         a = action_new(action_toggle_decorations);
153     } else if (!g_ascii_strcasecmp(name, "move")) {
154         a = action_new(action_move);
155     } else if (!g_ascii_strcasecmp(name, "resize")) {
156         a = action_new(action_resize);
157     } else if (!g_ascii_strcasecmp(name, "restart")) {
158         a = action_new(action_restart);
159     } else if (!g_ascii_strcasecmp(name, "exit")) {
160         a = action_new(action_exit);
161     } else if (!g_ascii_strcasecmp(name, "showmenu")) {
162         a = action_new(action_showmenu);
163     } else if (!g_ascii_strcasecmp(name, "nextwindowlinear")) {
164         a = action_new(action_cycle_windows);
165         a->data.cycle.linear = TRUE;
166         a->data.cycle.forward = TRUE;
167     } else if (!g_ascii_strcasecmp(name, "previouswindowlinear")) {
168         a = action_new(action_cycle_windows);
169         a->data.cycle.linear = TRUE;
170         a->data.cycle.forward = FALSE;
171     } else if (!g_ascii_strcasecmp(name, "nextwindow")) {
172         a = action_new(action_cycle_windows);
173         a->data.cycle.linear = FALSE;
174         a->data.cycle.forward = TRUE;
175     } else if (!g_ascii_strcasecmp(name, "previouswindow")) {
176         a = action_new(action_cycle_windows);
177         a->data.cycle.linear = FALSE;
178         a->data.cycle.forward = FALSE;
179     }
180     
181     return a;
182 }
183
184 void action_execute(union ActionData *data)
185 {
186     GError *e = NULL;
187     if (data->execute.path)
188         if (!g_spawn_command_line_async(data->execute.path, &e)) {
189             g_warning("failed to execute '%s': %s",
190                       data->execute.path, e->message);
191         }
192 }
193
194 void action_focus(union ActionData *data)
195 {
196     if (data->client.c)
197         client_focus(data->client.c);
198 }
199
200 void action_unfocus (union ActionData *data)
201 {
202     if (data->client.c)
203         client_unfocus(data->client.c);
204 }
205
206 void action_iconify(union ActionData *data)
207 {
208     if (data->client.c)
209         client_iconify(data->client.c, TRUE, TRUE);
210 }
211
212 void action_focusraise(union ActionData *data)
213 {
214     if (data->client.c) {
215         client_focus(data->client.c);
216         stacking_raise(data->client.c);
217     }
218 }
219
220 void action_raise(union ActionData *data)
221 {
222     if (data->client.c)
223         stacking_raise(data->client.c);
224 }
225
226 void action_unshaderaise(union ActionData *data)
227 {
228     if (data->client.c) {
229         if (data->client.c->shaded)
230             client_shade(data->client.c, FALSE);
231         else
232             stacking_raise(data->client.c);
233     }
234 }
235
236 void action_shadelower(union ActionData *data)
237 {
238     if (data->client.c) {
239         if (data->client.c->shaded)
240             stacking_lower(data->client.c);
241         else
242             client_shade(data->client.c, TRUE);
243     }
244 }
245
246 void action_lower(union ActionData *data)
247 {
248     if (data->client.c)
249         stacking_lower(data->client.c);
250 }
251
252 void action_close(union ActionData *data)
253 {
254     if (data->client.c)
255         client_close(data->client.c);
256 }
257
258 void action_kill(union ActionData *data)
259 {
260     if (data->client.c)
261         client_kill(data->client.c);
262 }
263
264 void action_shade(union ActionData *data)
265 {
266     if (data->client.c)
267         client_shade(data->client.c, TRUE);
268 }
269
270 void action_unshade(union ActionData *data)
271 {
272     if (data->client.c)
273         client_shade(data->client.c, FALSE);
274 }
275
276 void action_toggle_shade(union ActionData *data)
277 {
278     if (data->client.c)
279         client_shade(data->client.c, !data->client.c->shaded);
280 }
281
282 void action_toggle_omnipresent(union ActionData *data)
283
284     if (data->client.c)
285         client_set_desktop(data->client.c,
286                            data->client.c->desktop == DESKTOP_ALL ?
287                            screen_desktop : DESKTOP_ALL, FALSE);
288 }
289
290 void action_move_relative_horz(union ActionData *data)
291 {
292     Client *c = data->relative.c;
293     if (c)
294         client_configure(c, Corner_TopLeft,
295                          c->area.x + data->relative.delta, c->area.y,
296                          c->area.width, c->area.height, TRUE, TRUE);
297 }
298
299 void action_move_relative_vert(union ActionData *data)
300 {
301     Client *c = data->relative.c;
302     if (c)
303         client_configure(c, Corner_TopLeft,
304                          c->area.x, c->area.y + data->relative.delta,
305                          c->area.width, c->area.height, TRUE, TRUE);
306 }
307
308 void action_resize_relative_horz(union ActionData *data)
309 {
310     Client *c = data->relative.c;
311     if (c)
312         client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
313                          c->area.width + data->relative.delta,
314                          c->area.height, TRUE, TRUE);
315 }
316
317 void action_resize_relative_vert(union ActionData *data)
318 {
319     Client *c = data->relative.c;
320     if (c && !c->shaded)
321         client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
322                          c->area.width, c->area.height + data->relative.delta,
323                          TRUE, TRUE);
324 }
325
326 void action_maximize_full(union ActionData *data)
327 {
328     if (data->client.c)
329         client_maximize(data->client.c, TRUE, 0, TRUE);
330 }
331
332 void action_unmaximize_full(union ActionData *data)
333 {
334     if (data->client.c)
335         client_maximize(data->client.c, FALSE, 0, TRUE);
336 }
337
338 void action_toggle_maximize_full(union ActionData *data)
339 {
340     if (data->client.c)
341         client_maximize(data->client.c,
342                         !(data->client.c->max_horz ||
343                           data->client.c->max_vert),
344                         0, TRUE);
345 }
346
347 void action_maximize_horz(union ActionData *data)
348 {
349     if (data->client.c)
350         client_maximize(data->client.c, TRUE, 1, TRUE);
351 }
352
353 void action_unmaximize_horz(union ActionData *data)
354 {
355     if (data->client.c)
356         client_maximize(data->client.c, FALSE, 1, TRUE);
357 }
358
359 void action_toggle_maximize_horz(union ActionData *data)
360 {
361     if (data->client.c)
362         client_maximize(data->client.c, !data->client.c->max_horz, 1, TRUE);
363 }
364
365 void action_maximize_vert(union ActionData *data)
366 {
367     if (data->client.c)
368         client_maximize(data->client.c, TRUE, 2, TRUE);
369 }
370
371 void action_unmaximize_vert(union ActionData *data)
372 {
373     if (data->client.c)
374         client_maximize(data->client.c, FALSE, 2, TRUE);
375 }
376
377 void action_toggle_maximize_vert(union ActionData *data)
378 {
379     if (data->client.c)
380         client_maximize(data->client.c, !data->client.c->max_vert, 2, TRUE);
381 }
382
383 void action_send_to_desktop(union ActionData *data)
384 {
385     if (data->sendto.c) {
386         if (data->sendto.desk < screen_num_desktops ||
387             data->sendto.desk == DESKTOP_ALL) {
388             client_set_desktop(data->desktop.c,
389                                data->sendto.desk, data->sendto.follow);
390             if (data->sendto.follow) screen_set_desktop(data->sendto.desk);
391         }
392     }
393 }
394
395 void action_send_to_next_desktop(union ActionData *data)
396 {
397     guint d;
398
399     if (!data->sendtonextprev.c) return;
400
401     d = screen_desktop + 1;
402     if (d >= screen_num_desktops) {
403         if (!data->sendtonextprev.wrap) return;
404         d = 0;
405     }
406     client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
407     if (data->sendtonextprev.follow) screen_set_desktop(d);
408 }
409
410 void action_send_to_previous_desktop(union ActionData *data)
411 {
412     guint d;
413
414     if (!data->sendtonextprev.c) return;
415
416     d = screen_desktop - 1;
417     if (d >= screen_num_desktops) {
418         if (!data->sendtonextprev.wrap) return;
419         d = screen_num_desktops - 1;
420     }
421     client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
422     if (data->sendtonextprev.follow) screen_set_desktop(d);
423 }
424
425 void action_desktop(union ActionData *data)
426 {
427     if (data->desktop.desk < screen_num_desktops ||
428         data->desktop.desk == DESKTOP_ALL)
429         screen_set_desktop(data->desktop.desk);
430 }
431
432 void action_next_desktop(union ActionData *data)
433 {
434     guint d;
435
436     d = screen_desktop + 1;
437     if (d >= screen_num_desktops) {
438         if (!data->nextprevdesktop.wrap) return;
439         d = 0;
440     }
441     screen_set_desktop(d);
442 }
443
444 void action_previous_desktop(union ActionData *data)
445 {
446     guint d;
447
448     d = screen_desktop - 1;
449     if (d >= screen_num_desktops) {
450         if (!data->nextprevdesktop.wrap) return;
451         d = screen_num_desktops - 1;
452     }
453     screen_set_desktop(d);
454 }
455
456 static void cur_row_col(guint *r, guint *c)
457 {
458     switch (screen_desktop_layout.orientation) {
459     case Orientation_Horz:
460         switch (screen_desktop_layout.start_corner) {
461         case Corner_TopLeft:
462             *r = screen_desktop / screen_desktop_layout.columns;
463             *c = screen_desktop % screen_desktop_layout.columns;
464             break;
465         case Corner_BottomLeft:
466             *r = screen_desktop_layout.rows - 1 -
467                 screen_desktop / screen_desktop_layout.columns;
468             *c = screen_desktop % screen_desktop_layout.columns;
469             break;
470         case Corner_TopRight:
471             *r = screen_desktop / screen_desktop_layout.columns;
472             *c = screen_desktop_layout.columns - 1 -
473                 screen_desktop % screen_desktop_layout.columns;
474             break;
475         case Corner_BottomRight:
476             *r = screen_desktop_layout.rows - 1 -
477                 screen_desktop / screen_desktop_layout.columns;
478             *c = screen_desktop_layout.columns - 1 -
479                 screen_desktop % screen_desktop_layout.columns;
480             break;
481         }
482         break;
483     case Orientation_Vert:
484         switch (screen_desktop_layout.start_corner) {
485         case Corner_TopLeft:
486             *r = screen_desktop % screen_desktop_layout.rows;
487             *c = screen_desktop / screen_desktop_layout.rows;
488             break;
489         case Corner_BottomLeft:
490             *r = screen_desktop_layout.rows - 1 -
491                 screen_desktop % screen_desktop_layout.rows;
492             *c = screen_desktop / screen_desktop_layout.rows;
493             break;
494         case Corner_TopRight:
495             *r = screen_desktop % screen_desktop_layout.rows;
496             *c = screen_desktop_layout.columns - 1 -
497                 screen_desktop / screen_desktop_layout.rows;
498             break;
499         case Corner_BottomRight:
500             *r = screen_desktop_layout.rows - 1 -
501                 screen_desktop % screen_desktop_layout.rows;
502             *c = screen_desktop_layout.columns - 1 -
503                 screen_desktop / screen_desktop_layout.rows;
504             break;
505         }
506         break;
507     }
508 }
509
510 static guint translate_row_col(guint r, guint c)
511 {
512     switch (screen_desktop_layout.orientation) {
513     case Orientation_Horz:
514         switch (screen_desktop_layout.start_corner) {
515         case Corner_TopLeft:
516             return r * screen_desktop_layout.columns + c;
517         case Corner_BottomLeft:
518             return (screen_desktop_layout.rows - 1 - r) *
519                 screen_desktop_layout.columns + c;
520         case Corner_TopRight:
521             return r * screen_desktop_layout.columns +
522                 (screen_desktop_layout.columns - 1 - c);
523         case Corner_BottomRight:
524             return (screen_desktop_layout.rows - 1 - r) *
525                 screen_desktop_layout.columns +
526                 (screen_desktop_layout.columns - 1 - c);
527         }
528     case Orientation_Vert:
529         switch (screen_desktop_layout.start_corner) {
530         case Corner_TopLeft:
531             return c * screen_desktop_layout.rows + r;
532         case Corner_BottomLeft:
533             return c * screen_desktop_layout.rows +
534                 (screen_desktop_layout.rows - 1 - r);
535         case Corner_TopRight:
536             return (screen_desktop_layout.columns - 1 - c) *
537                 screen_desktop_layout.rows + r;
538         case Corner_BottomRight:
539             return (screen_desktop_layout.columns - 1 - c) *
540                 screen_desktop_layout.rows +
541                 (screen_desktop_layout.rows - 1 - r);
542         }
543     }
544     g_assert_not_reached();
545     return 0;
546 }
547
548 void action_next_desktop_column(union ActionData *data)
549 {
550     guint r, c, d;
551
552     cur_row_col(&r, &c);
553     ++c;
554     d = translate_row_col(r, c);
555     if (d >= screen_num_desktops) {
556         if (!data->nextprevdesktop.wrap) return;
557         c = 0;
558     }
559     if (d >= screen_num_desktops)
560         ++c;
561     d = translate_row_col(r, c);
562     if (d < screen_num_desktops)
563         screen_set_desktop(d);
564 }
565
566 void action_previous_desktop_column(union ActionData *data)
567 {
568     guint r, c, d;
569
570     cur_row_col(&r, &c);
571     --c;
572     d = translate_row_col(r, c);
573     if (d >= screen_num_desktops) {
574         if (!data->nextprevdesktop.wrap) return;
575         c = screen_desktop_layout.columns - 1;
576     }
577     if (d >= screen_num_desktops)
578         --c;
579     d = translate_row_col(r, c);
580     if (d < screen_num_desktops)
581         screen_set_desktop(d);
582 }
583
584 void action_next_desktop_row(union ActionData *data)
585 {
586     guint r, c, d;
587
588     cur_row_col(&r, &c);
589     ++r;
590     d = translate_row_col(r, c);
591     if (d >= screen_num_desktops) {
592         if (!data->nextprevdesktop.wrap) return;
593         r = 0;
594     }
595     if (d >= screen_num_desktops)
596         ++r;
597     d = translate_row_col(r, c);
598     if (d < screen_num_desktops)
599         screen_set_desktop(d);
600 }
601
602 void action_previous_desktop_row(union ActionData *data)
603 {
604     guint r, c, d;
605
606     cur_row_col(&r, &c);
607     --r;
608     d = translate_row_col(r, c);
609     if (d >= screen_num_desktops) {
610         if (!data->nextprevdesktop.wrap) return;
611         c = screen_desktop_layout.rows - 1;
612     }
613     if (d >= screen_num_desktops)
614         --r;
615     d = translate_row_col(r, c);
616     if (d < screen_num_desktops)
617         screen_set_desktop(d);
618 }
619
620 void action_toggle_decorations(union ActionData *data)
621 {
622     Client *c = data->client.c;
623     c->disabled_decorations = c->disabled_decorations ? 0 : ~0;
624     client_setup_decor_and_functions(c);
625 }
626
627 void action_move(union ActionData *data)
628 {
629     Client *c = data->move.c;
630     int x = data->move.x;
631     int y = data->move.y;
632
633     if (!c || !client_normal(c)) return;
634
635     dispatch_move(c, &x, &y);
636
637     frame_frame_gravity(c->frame, &x, &y); /* get where the client should be */
638     client_configure(c, Corner_TopLeft, x, y, c->area.width, c->area.height,
639                      TRUE, data->move.final);
640 }
641
642 void action_resize(union ActionData *data)
643 {
644     Client *c = data->resize.c;
645     int w = data->resize.x;
646     int h = data->resize.y;
647  
648     if (!c || c->shaded || !client_normal(c)) return;
649
650     dispatch_resize(c, &w, &h, data->resize.corner);
651     
652     w -= c->frame->size.left + c->frame->size.right;
653     h -= c->frame->size.top + c->frame->size.bottom;
654     
655     client_configure(c, data->resize.corner, c->area.x, c->area.y, w, h,
656                      TRUE, data->resize.final);
657 }
658
659 void action_restart(union ActionData *data)
660 {
661     ob_restart_path = data->execute.path;
662     ob_shutdown = ob_restart = TRUE;
663 }
664
665 void action_exit(union ActionData *data)
666 {
667     ob_shutdown = TRUE;
668 }
669
670 void action_showmenu(union ActionData *data)
671 {
672     g_message(__FUNCTION__);
673 }
674
675 void action_cycle_windows(union ActionData *data)
676 {
677     static Client *first = NULL;
678     static Client *t = NULL;
679     static GList *order = NULL;
680     GList *it, *start, *list;
681
682     if (data->cycle.cancel) {
683         if (first) client_focus(first);
684         goto done_cycle;
685     }
686     if (!first) first = focus_client;
687
688     if (data->cycle.linear)
689         list = client_list;
690     else {
691         if (!order) order = g_list_copy(focus_order[screen_desktop]);
692         list = order;
693     }
694     start = it = g_list_find(list, data->cycle.c);
695     if (!start) goto done_cycle;
696
697     if (!data->cycle.final) {
698         t = NULL;
699         if (!start) /* switched desktops or something? */
700             goto done_cycle;
701
702         do {
703             if (data->cycle.forward) {
704                 it = it->next;
705                 if (it == NULL) it = list;
706             } else {
707                 it = it->prev;
708                 if (it == NULL) it = g_list_last(list);
709             }
710             if (client_focus(it->data)) {
711                 t = it->data;
712                 break;
713             }
714         } while (it != start);
715     } else {
716         if (t) stacking_raise(t);
717         goto done_cycle;
718     }
719     return;
720
721     done_cycle:
722         first = NULL;
723         g_list_free(order);
724         order = NULL;
725 }
726