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