make interactive actions a type and not special cases.
[mikachu/openbox.git] / openbox / place.c
1 #include "client.h"
2 #include "group.h"
3 #include "screen.h"
4 #include "frame.h"
5
6 static Rect* pick_head(ObClient *c)
7 {
8     /* try direct parent first */
9     if (c->transient_for && c->transient_for != OB_TRAN_GROUP) {
10         return screen_area_monitor(c->desktop,
11                                    client_monitor(c->transient_for));
12     }
13
14     /* more than one guy in his group (more than just him) */
15     if (c->group && c->group->members->next) {
16         GSList *it;
17
18         /* try on the client's desktop */
19         for (it = c->group->members; it; it = g_slist_next(it)) {
20             ObClient *itc = it->data;            
21             if (itc != c &&
22                 (itc->desktop == c->desktop ||
23                  itc->desktop == DESKTOP_ALL || c->desktop == DESKTOP_ALL))
24                 return screen_area_monitor(c->desktop,
25                                            client_monitor(it->data));
26         }
27
28         /* try on all desktops */
29         for (it = c->group->members; it; it = g_slist_next(it)) {
30             ObClient *itc = it->data;            
31             if (itc != c)
32                 return screen_area_monitor(c->desktop,
33                                            client_monitor(it->data));
34         }
35     }
36
37     return NULL;
38 }
39
40 static gboolean place_random(ObClient *client, gint *x, gint *y)
41 {
42     int l, r, t, b;
43     Rect *area;
44
45     area = pick_head(client);
46     if (!area)
47         area = screen_area_monitor(client->desktop,
48                                    g_random_int_range(0, screen_num_monitors));
49
50     l = area->x;
51     t = area->y;
52     r = area->x + area->width - client->frame->area.width;
53     b = area->y + area->height - client->frame->area.height;
54
55     if (r > l) *x = g_random_int_range(l, r + 1);
56     else       *x = 0;
57     if (b > t) *y = g_random_int_range(t, b + 1);
58     else       *y = 0;
59
60     /* get where the client should be */
61     frame_frame_gravity(client->frame, x, y);
62
63     return TRUE;
64 }
65
66 static gboolean place_transient(ObClient *client, gint *x, gint *y)
67 {
68     if (client->transient_for) {
69         if (client->transient_for != OB_TRAN_GROUP) {
70             ObClient *c = client;
71             ObClient *p = client->transient_for;
72             *x = (p->frame->area.width - c->frame->area.width) / 2 +
73                 p->frame->area.x;
74             *y = (p->frame->area.height - c->frame->area.height) / 2 +
75                 p->frame->area.y;
76             return TRUE;
77         } else {
78             GSList *it;
79             gboolean first = TRUE;
80             int l, r, t, b;
81             for (it = client->group->members; it; it = it->next) {
82                 ObClient *m = it->data;
83                 if (!(m == client || m->transient_for)) {
84                     if (first) {
85                         l = m->frame->area.x;
86                         t = m->frame->area.y;
87                         r = m->frame->area.x + m->frame->area.width - 1;
88                         b = m->frame->area.y + m->frame->area.height - 1;
89                         first = FALSE;
90                     } else {
91                         l = MIN(l, m->frame->area.x);
92                         t = MIN(t, m->frame->area.y);
93                         r = MAX(r, m->frame->area.x +m->frame->area.width - 1);
94                         b = MAX(b, m->frame->area.y +m->frame->area.height -1);
95                     }
96                 }
97             }
98             if (!first) {
99                 *x = ((r + 1 - l) - client->frame->area.width) / 2 + l; 
100                 *y = ((b + 1 - t) - client->frame->area.height) / 2 + t;
101                 return TRUE;
102             }
103         }
104     }
105     return FALSE;
106 }
107
108 static gboolean place_dialog(ObClient *client, gint *x, gint *y)
109 {
110     /* center parentless dialogs on the screen */
111     if (client->type == OB_CLIENT_TYPE_DIALOG) {
112         Rect *area;
113
114         area = pick_head(client);
115         if (!area)
116             area = screen_area_monitor(client->desktop, 0);
117
118         *x = (area->width - client->frame->area.width) / 2 + area->x;
119         *y = (area->height - client->frame->area.height) / 2 + area->y;
120         return TRUE;
121     }
122     return FALSE;
123 }
124
125 void place_client(ObClient *client, gint *x, gint *y)
126 {
127     if (client->positioned)
128         return;
129     if (place_transient(client, x, y))
130         return;
131     if (place_dialog(client, x, y))
132         return;
133     if (place_random(client, x, y))
134         return;
135     g_assert_not_reached(); /* the last one better succeed */
136 }