dont let windows be placed offscreen
[dana/openbox.git] / plugins / placement / history.c
1 #include "kernel/openbox.h"
2 #include "kernel/dispatch.h"
3 #include "kernel/frame.h"
4 #include "kernel/client.h"
5 #include "kernel/screen.h"
6 #include <glib.h>
7 #include <string.h>
8 #ifdef HAVE_STDLIB_H
9 #  include <stdlib.h>
10 #endif
11
12 struct HistoryItem {
13     char *name;
14     char *class;
15     char *role;
16     int x;
17     int y;
18     gboolean placed;
19 };
20
21 static GSList *history = NULL;
22 static char *history_path = NULL;
23
24 static struct HistoryItem *find_history(Client *c)
25 {
26     GSList *it;
27     struct HistoryItem *hi = NULL;
28
29     /* find the client */
30     for (it = history; it != NULL; it = it->next) {
31         hi = it->data;
32         g_assert(hi->name != NULL);
33         g_assert(hi->class != NULL);
34         g_assert(hi->role != NULL);
35         g_assert(c->name != NULL);
36         g_assert(c->class != NULL);
37         g_assert(c->role != NULL);
38         if (!strcmp(hi->name, c->name) &&
39             !strcmp(hi->class, c->class) &&
40             !strcmp(hi->role, c->role))
41             return hi;
42     }
43     return NULL;
44 }
45
46 gboolean place_history(Client *c)
47 {
48     struct HistoryItem *hi;
49     int x, y;
50
51     hi = find_history(c);
52
53     if (hi != NULL && !hi->placed) {
54         hi->placed = TRUE;
55         if (ob_state != State_Starting) {
56             x = hi->x;
57             y = hi->y;
58
59             /* make sure the window is on the display */
60             if (x >= screen_physical_size.width ||
61                 y >= screen_physical_size.height ||
62                 x + c->frame->area.width < 1 ||
63                 y + c->frame->area.height < 1)
64                 return FALSE;
65
66             frame_frame_gravity(c->frame, &x, &y); /* get where the client
67                                                       should be */
68             client_configure(c, Corner_TopLeft, x, y,
69                              c->area.width, c->area.height,
70                              TRUE, TRUE);
71         }
72         return TRUE;
73     }
74
75     return FALSE;
76 }
77
78 static void strip_tabs(char *s)
79 {
80     while (*s != '\0') {
81         if (*s == '\t')
82             *s = ' ';
83         ++s;
84     }
85 }
86
87 static void set_history(Client *c)
88 {
89     struct HistoryItem *hi;
90
91     hi = find_history(c);
92
93     if (hi == NULL) {
94         hi = g_new(struct HistoryItem, 1);
95         history = g_slist_append(history, hi);
96         hi->name = g_strdup(c->name);
97         strip_tabs(hi->name);
98         hi->class = g_strdup(c->class);
99         strip_tabs(hi->class);
100         hi->role = g_strdup(c->role);
101         strip_tabs(hi->role);
102     }
103
104     hi->x = c->frame->area.x;
105     hi->y = c->frame->area.y;
106     hi->placed = FALSE;
107 }
108
109 static void event(ObEvent *e, void *foo)
110 {
111     g_assert(e->type == Event_Client_Destroy);
112
113     set_history(e->data.c.client);
114 }
115
116 static void save_history()
117 {
118     GError *err = NULL;
119     GIOChannel *io;
120     GString *buf;
121     GSList *it;
122     struct HistoryItem *hi;
123     gsize ret;
124
125     io = g_io_channel_new_file(history_path, "w", &err);
126     if (io != NULL) {
127         for (it = history; it != NULL; it = it->next) {
128             hi = it->data;
129             buf = g_string_sized_new(0);
130             buf=g_string_append(buf, hi->name);
131             g_string_append_c(buf, '\t');
132             buf=g_string_append(buf, hi->class);
133             g_string_append_c(buf, '\t');
134             buf=g_string_append(buf, hi->role);
135             g_string_append_c(buf, '\t');
136             g_string_append_printf(buf, "%d", hi->x);
137             buf=g_string_append_c(buf, '\t');
138             g_string_append_printf(buf, "%d", hi->y);
139             buf=g_string_append_c(buf, '\n');
140             if (g_io_channel_write_chars(io, buf->str, buf->len, &ret, &err) !=
141                 G_IO_STATUS_NORMAL)
142                 break;
143             g_string_free(buf, TRUE);
144         }
145         g_io_channel_unref(io);
146     }
147 }
148
149 static void load_history()
150 {
151     GError *err = NULL;
152     GIOChannel *io;
153     char *buf = NULL;
154     char *b, *c;
155     struct HistoryItem *hi = NULL;
156
157     io = g_io_channel_new_file(history_path, "r", &err);
158     if (io != NULL) {
159         while (g_io_channel_read_line(io, &buf, NULL, NULL, &err) ==
160                G_IO_STATUS_NORMAL) {
161             hi = g_new0(struct HistoryItem, 1);
162
163             b = buf;
164             if ((c = strchr(b, '\t')) == NULL) break;
165             *c = '\0';
166             hi->name = g_strdup(b);
167
168             b = c + 1;
169             if ((c = strchr(b, '\t')) == NULL) break;
170             *c = '\0';
171             hi->class = g_strdup(b);
172
173             b = c + 1;
174             if ((c = strchr(b, '\t')) == NULL) break;
175             *c = '\0';
176             hi->role = g_strdup(b);
177
178             b = c + 1;
179             if ((c = strchr(b, '\t')) == NULL) break;
180             *c = '\0';
181             hi->x = atoi(b);
182
183             b = c + 1;
184             if ((c = strchr(b, '\n')) == NULL) break;
185             *c = '\0';
186             hi->y = atoi(b);
187
188             hi->placed = FALSE;
189
190             g_free(buf);
191             buf = NULL;
192
193             history = g_slist_append(history, hi);
194             hi = NULL;
195         }
196         g_io_channel_unref(io);
197     }
198         
199     g_free(buf);
200
201     if (hi != NULL) {
202         g_free(hi->name);
203         g_free(hi->class);
204         g_free(hi->role);
205     }
206     g_free(hi);
207 }
208
209 void history_startup()
210 {
211     char *path;
212
213     history = NULL;
214
215     path = g_build_filename(g_get_home_dir(), ".openbox", "history", NULL);
216     history_path = g_strdup_printf("%s.%d", path, ob_screen);
217     g_free(path);
218
219     load_history(); /* load from the historydb file */
220
221     dispatch_register(Event_Client_Destroy, (EventHandler)event, NULL);
222 }
223
224 void history_shutdown()
225 {
226     GSList *it;
227
228     save_history(); /* save to the historydb file */
229     for (it = history; it != NULL; it = it->next)
230         g_free(it->data);
231     g_slist_free(history);
232
233     dispatch_register(0, (EventHandler)event, NULL);
234
235     g_free(history_path);
236 }