Let the If action match window titles with GRegex
[dana/openbox.git] / openbox / actions / if.c
1 #include "openbox/actions.h"
2 #include "openbox/misc.h"
3 #include "openbox/client.h"
4 #include "openbox/frame.h"
5 #include "openbox/screen.h"
6 #include "openbox/focus.h"
7 #include <glib.h>
8
9 typedef struct {
10     gboolean shaded_on;
11     gboolean shaded_off;
12     gboolean maxvert_on;
13     gboolean maxvert_off;
14     gboolean maxhorz_on;
15     gboolean maxhorz_off;
16     gboolean maxfull_on;
17     gboolean maxfull_off;
18     gboolean iconic_on;
19     gboolean iconic_off;
20     gboolean focused;
21     gboolean unfocused;
22     gboolean urgent_on;
23     gboolean urgent_off;
24     gboolean decor_off;
25     gboolean decor_on;
26     gboolean omnipresent_on;
27     gboolean omnipresent_off;
28     gboolean desktop_current;
29     gboolean desktop_other;
30     guint    desktop_number;
31     guint    screendesktop_number;
32     GPatternSpec *matchtitle;
33     GRegex *regextitle;
34     gchar  *plaintitle;
35     GSList *thenacts;
36     GSList *elseacts;
37 } Options;
38
39 static gpointer setup_func(xmlNodePtr node);
40 static void     free_func(gpointer options);
41 static gboolean run_func(ObActionsData *data, gpointer options);
42
43 void action_if_startup(void)
44 {
45     actions_register("If", setup_func, free_func, run_func);
46 }
47
48 static inline set_bool(xmlNodePtr node,
49                        const char *name,
50                        gboolean *on,
51                        gboolean *off)
52 {
53     xmlNodePtr n;
54
55     if ((n = obt_xml_find_node(node, name))) {
56         if (obt_xml_node_bool(n))
57             *on = TRUE;
58         else
59             *off = TRUE;
60     }
61 }
62
63 static gpointer setup_func(xmlNodePtr node)
64 {
65     xmlNodePtr n;
66     Options *o;
67
68     o = g_slice_new0(Options);
69
70     set_bool(node, "shaded", &o->shaded_on, &o->shaded_off);
71     set_bool(node, "maximized", &o->maxfull_on, &o->maxfull_off);
72     set_bool(node, "maximizedhorizontal", &o->maxhorz_on, &o->maxhorz_off);
73     set_bool(node, "maximizedvertical", &o->maxvert_on, &o->maxvert_off);
74     set_bool(node, "iconified", &o->iconic_on, &o->iconic_off);
75     set_bool(node, "focused", &o->focused, &o->unfocused);
76     set_bool(node, "urgent", &o->urgent_on, &o->urgent_off);
77     set_bool(node, "undecorated", &o->decor_off, &o->decor_on);
78     set_bool(node, "omnipresent", &o->omnipresent_on, &o->omnipresent_off);
79
80     if ((n = obt_xml_find_node(node, "desktop"))) {
81         gchar *s;
82         if ((s = obt_xml_node_string(n))) {
83             if (!g_ascii_strcasecmp(s, "current"))
84                 o->desktop_current = TRUE;
85             if (!g_ascii_strcasecmp(s, "other"))
86                 o->desktop_other = TRUE;
87             else
88                 o->desktop_number = atoi(s);
89             g_free(s);
90         }
91     }
92     if ((n = obt_xml_find_node(node, "activedesktop"))) {
93         o->screendesktop_number = obt_xml_node_int(n);
94     }
95     if ((n = obt_xml_find_node(node, "title"))) {
96         gchar *s, *type = NULL;
97         if ((s = obt_xml_node_string(n))) {
98             if (!obt_xml_attr_string(n, "type", &type) ||
99                 !g_ascii_strcasecmp(type, "pattern"))
100             {
101                 o->matchtitle = g_pattern_spec_new(s);
102             } else if (type && !g_ascii_strcasecmp(type, "regex")) {
103                 o->regextitle = g_regex_new(s, 0, 0, NULL);
104             } else if (type && !g_ascii_strcasecmp(type, "plain")) {
105                 o->plaintitle = g_strdup(s);
106             }
107             g_free(s);
108         }
109     }
110
111     if ((n = obt_xml_find_node(node, "then"))) {
112         xmlNodePtr m;
113
114         m = obt_xml_find_node(n->children, "action");
115         while (m) {
116             ObActionsAct *action = actions_parse(m);
117             if (action) o->thenacts = g_slist_append(o->thenacts, action);
118             m = obt_xml_find_node(m->next, "action");
119         }
120     }
121     if ((n = obt_xml_find_node(node, "else"))) {
122         xmlNodePtr m;
123
124         m = obt_xml_find_node(n->children, "action");
125         while (m) {
126             ObActionsAct *action = actions_parse(m);
127             if (action) o->elseacts = g_slist_append(o->elseacts, action);
128             m = obt_xml_find_node(m->next, "action");
129         }
130     }
131
132     return o;
133 }
134
135 static void free_func(gpointer options)
136 {
137     Options *o = options;
138
139     while (o->thenacts) {
140         actions_act_unref(o->thenacts->data);
141         o->thenacts = g_slist_delete_link(o->thenacts, o->thenacts);
142     }
143     while (o->elseacts) {
144         actions_act_unref(o->elseacts->data);
145         o->elseacts = g_slist_delete_link(o->elseacts, o->elseacts);
146     }
147     if (o->matchtitle)
148         g_pattern_spec_free(o->matchtitle);
149     if (o->regextitle)
150         g_regex_unref(o->regextitle);
151     if (o->plaintitle)
152         g_free(o->plaintitle);
153
154     g_slice_free(Options, o);
155 }
156
157 /* Always return FALSE because its not interactive */
158 static gboolean run_func(ObActionsData *data, gpointer options)
159 {
160     Options *o = options;
161     GSList *acts;
162     ObClient *c = data->client;
163
164     if (c &&
165         (!o->shaded_on   ||  c->shaded) &&
166         (!o->shaded_off  || !c->shaded) &&
167         (!o->iconic_on   ||  c->iconic) &&
168         (!o->iconic_off  || !c->iconic) &&
169         (!o->maxhorz_on  ||  c->max_horz) &&
170         (!o->maxhorz_off || !c->max_horz) &&
171         (!o->maxvert_on  ||  c->max_vert) &&
172         (!o->maxvert_off || !c->max_vert) &&
173         (!o->maxfull_on  ||  (c->max_vert && c->max_horz)) &&
174         (!o->maxfull_off || !(c->max_vert && c->max_horz)) &&
175         (!o->focused     ||  (c == focus_client)) &&
176         (!o->unfocused   || !(c == focus_client)) &&
177         (!o->urgent_on   ||  (c->urgent || c->demands_attention)) &&
178         (!o->urgent_off  || !(c->urgent || c->demands_attention)) &&
179         (!o->decor_off   ||  (c->undecorated || !(c->decorations & OB_FRAME_DECOR_TITLEBAR))) &&
180         (!o->decor_on    ||  (!c->undecorated && (c->decorations & OB_FRAME_DECOR_TITLEBAR))) &&
181         (!o->omnipresent_on  || (c->desktop == DESKTOP_ALL)) &&
182         (!o->omnipresent_off || (c->desktop != DESKTOP_ALL)) &&
183         (!o->desktop_current || ((c->desktop == screen_desktop) ||
184                                  (c->desktop == DESKTOP_ALL))) &&
185         (!o->desktop_other   || ((c->desktop != screen_desktop) &&
186                                  (c->desktop != DESKTOP_ALL))) &&
187         (!o->desktop_number  || ((c->desktop == o->desktop_number - 1) ||
188                                  (c->desktop == DESKTOP_ALL))) &&
189         (!o->screendesktop_number || screen_desktop == o->screendesktop_number - 1) &&
190         (!o->matchtitle ||
191          (g_pattern_match_string(o->matchtitle, c->original_title))) &&
192         (!o->regextitle ||
193          (g_regex_match(o->regextitle, c->original_title, 0, NULL))) &&
194         (!o->plaintitle ||
195          (!strcmp(o->plaintitle, c->original_title))))
196     {
197         acts = o->thenacts;
198     }
199     else
200         acts = o->elseacts;
201
202     actions_run_acts(acts, data->uact, data->state,
203                      data->x, data->y, data->button,
204                      data->context, data->client);
205
206     return FALSE;
207 }