]> icculus.org git repositories - dana/openbox.git/blob - plugins/placement/history.c
ECKS EM ELL
[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 "kernel/parse.h"
7 #include "history.h"
8 #include <glib.h>
9 #include <string.h>
10 #ifdef HAVE_STDLIB_H
11 #  include <stdlib.h>
12 #endif
13
14 #define PLACED        (1 << 0)
15
16 #define HAVE_POSITION (1 << 1)
17 #define HAVE_SIZE     (1 << 2)
18 #define HAVE_DESKTOP  (1 << 3)
19
20 struct HistoryItem {
21     char *name;
22     char *class;
23     char *role;
24
25     int flags;
26
27     int x, y;
28     int w, h;
29     guint desk;
30 };
31
32 static GSList *history_list = NULL;
33 static char *history_path = NULL;
34
35 static struct HistoryItem *history_find(const char *name, const char *class,
36                                  const char *role)
37 {
38     GSList *it;
39     struct HistoryItem *hi = NULL;
40
41     /* find the client */
42     for (it = history_list; it != NULL; it = it->next) {
43         hi = it->data;
44         if (!g_utf8_collate(hi->name, name) &&
45             !g_utf8_collate(hi->class, class) &&
46             !g_utf8_collate(hi->role, role))
47             return hi;
48     }
49     return NULL;
50 }
51
52 gboolean place_history(Client *c)
53 {
54     struct HistoryItem *hi;
55     int x, y, w, h;
56
57     hi = history_find(c->name, c->class, c->role);
58
59     if (hi && !(hi->flags & PLACED)) {
60         hi->flags |= PLACED;
61         if (ob_state != State_Starting) {
62             if (hi->flags & HAVE_POSITION ||
63                 hi->flags & HAVE_SIZE) {
64                 if (hi->flags & HAVE_POSITION) {
65                     x = hi->x;
66                     y = hi->y;
67                     /* get where the client should be */
68                     frame_frame_gravity(c->frame, &x, &y);
69                 } else {
70                     x = c->area.x;
71                     y = c->area.y;
72                 }
73                 if (hi->flags & HAVE_SIZE) {
74                     w = hi->w * c->size_inc.width;
75                     h = hi->h * c->size_inc.height;
76                 } else {
77                     w = c->area.width;
78                     h = c->area.height;
79                 }
80                 client_configure(c, Corner_TopLeft, x, y, w, h,
81                                  TRUE, TRUE);
82             }
83             if (hi->flags & HAVE_DESKTOP) {
84                 client_set_desktop(c, hi->desk, FALSE);
85             }
86         }
87         return hi->flags & HAVE_POSITION;
88     }
89
90     return FALSE;
91 }
92
93 static void set_history(Client *c)
94 {
95     struct HistoryItem *hi;
96
97     hi = history_find(c->name, c->class, c->role);
98
99     if (hi == NULL) {
100         hi = g_new(struct HistoryItem, 1);
101         history_list = g_slist_append(history_list, hi);
102         hi->name = g_strdup(c->name);
103         hi->class = g_strdup(c->class);
104         hi->role = g_strdup(c->role);
105         hi->flags = HAVE_POSITION;
106     }
107
108     if (hi->flags & HAVE_POSITION) {
109         hi->x = c->frame->area.x;
110         hi->y = c->frame->area.y;
111     }
112
113     hi->flags &= ~PLACED;
114 }
115
116 static void event(ObEvent *e, void *foo)
117 {
118     g_assert(e->type == Event_Client_Destroy);
119
120     set_history(e->data.c.client);
121 }
122
123 /*
124
125 <entry name="name" class="class" role="role">
126   <x>0</x>
127   <y>0</y>
128   <width>300</width>
129   <height>200</height>
130   <desktop>1</desktop>
131 </entry>
132
133 */
134
135 static void save_history()
136 {
137     xmlDocPtr doc;
138     xmlNodePtr root, node;
139     char *s;
140     GSList *it;
141
142     doc = xmlNewDoc(NULL);
143     root = xmlNewNode(NULL, (const xmlChar*) "openbox_history");
144     xmlDocSetRootElement(doc, root);
145
146     for (it = history_list; it; it = g_slist_next(it)) {
147         struct HistoryItem *hi = it->data;
148         g_message("adding %s", hi->name);
149         node = xmlNewChild(root, NULL, (const xmlChar*) "entry", NULL);
150         xmlNewProp(node, (const xmlChar*) "name", (const xmlChar*) hi->name);
151         xmlNewProp(node, (const xmlChar*) "class", (const xmlChar*) hi->class);
152         xmlNewProp(node, (const xmlChar*) "role", (const xmlChar*) hi->role);
153         if (hi->flags & HAVE_POSITION) {
154             s = g_strdup_printf("%d", hi->x);
155             xmlNewTextChild(node, NULL,
156                             (const xmlChar*) "x", (const xmlChar*) s);
157             g_free(s);
158             s = g_strdup_printf("%d", hi->y);
159             xmlNewTextChild(node, NULL,
160                             (const xmlChar*) "y", (const xmlChar*) s);
161             g_free(s);
162         }
163         if (hi->flags & HAVE_SIZE) {
164             s = g_strdup_printf("%d", hi->w);
165             xmlNewTextChild(node, NULL,
166                             (const xmlChar*) "width", (const xmlChar*) s);
167             g_free(s);
168             s = g_strdup_printf("%d", hi->h);
169             xmlNewTextChild(node, NULL,
170                             (const xmlChar*) "height", (const xmlChar*) s);
171             g_free(s);
172         }
173         if (hi->flags & HAVE_DESKTOP) {
174             s = g_strdup_printf("%d", hi->desk < 0 ? hi->desk : hi->desk + 1);
175             xmlNewTextChild(node, NULL,
176                             (const xmlChar*) "desktop", (const xmlChar*) s);
177             g_free(s);
178         }
179     }
180
181     xmlIndentTreeOutput = 1;
182     xmlSaveFormatFile(history_path, doc, 1);
183
184     xmlFree(doc);
185 }
186
187 static void load_history()
188 {
189     xmlDocPtr doc;
190     xmlNodePtr node, n;
191     char *name;
192     char *class;
193     char *role;
194     struct HistoryItem *hi;
195
196     if (!(doc = xmlParseFile(history_path)))
197         return;
198     if (!(node = xmlDocGetRootElement(doc))) {
199         xmlFreeDoc(doc);
200         doc = NULL;
201         return;
202     }
203     if (xmlStrcasecmp(node->name, (const xmlChar*)"openbox_history")) {
204         xmlFreeDoc(doc);
205         doc = NULL;
206         return;
207     }
208
209     node = parse_find_node("entry", node->xmlChildrenNode);
210     while (node) {
211         name = class = role = NULL;
212         if (parse_attr_string("name", node, &name) &&
213             parse_attr_string("class", node, &class) &&
214             parse_attr_string("role", node, &role)) {
215
216             hi = history_find(name, class, role);
217             if (!hi) {
218                 hi = g_new(struct HistoryItem, 1);
219                 hi->name = g_strdup(name);
220                 hi->class = g_strdup(class);
221                 hi->role = g_strdup(role);
222                 hi->flags = 0;
223             }
224             if ((n = parse_find_node("x", node->xmlChildrenNode))) {
225                 hi->x = parse_int(doc, n);
226                 if ((n = parse_find_node("y", node->xmlChildrenNode))) {
227                     hi->y = parse_int(doc, n);
228                     hi->flags |= HAVE_POSITION;
229                 }
230             }
231             if ((n = parse_find_node("width", node->xmlChildrenNode))) {
232                 hi->w = parse_int(doc, n);
233                 if ((n = parse_find_node("height", node->xmlChildrenNode))) {
234                     hi->h = parse_int(doc, n);
235                     hi->flags |= HAVE_SIZE;
236                 }
237             }
238             if ((n = parse_find_node("desktop", node->xmlChildrenNode))) {
239                 hi->desk = parse_int(doc, n);
240                 if (hi->desk > 0) --hi->desk;
241                 hi->flags |= HAVE_DESKTOP;
242             }
243
244             history_list = g_slist_append(history_list, hi);
245         }
246         g_free(name); g_free(class); g_free(role);
247         node = parse_find_node("entry", node->next);
248     }
249     xmlFree(doc);
250 }
251
252 void history_startup()
253 {
254     char *path;
255
256     history_list = NULL;
257
258     path = g_build_filename(g_get_home_dir(), ".openbox", "history", NULL);
259     history_path = g_strdup_printf("%s.%d", path, ob_screen);
260     g_free(path);
261
262     load_history(); /* load from the historydb file */
263
264     dispatch_register(Event_Client_Destroy, (EventHandler)event, NULL);
265 }
266
267 void history_shutdown()
268 {
269     GSList *it;
270
271     save_history(); /* save to the historydb file */
272     for (it = history_list; it != NULL; it = it->next) {
273         struct HistoryItem *hi = it->data;
274         g_free(hi->name);
275         g_free(hi->class);
276         g_free(hi->role);
277         g_free(hi);
278     }
279     g_slist_free(history_list);
280
281     dispatch_register(0, (EventHandler)event, NULL);
282
283     g_free(history_path);
284 }