]> icculus.org git repositories - dana/openbox.git/blob - openbox/action_list_run.c
wip: Add config_parser.c which will provide a nice means to specify config variables...
[dana/openbox.git] / openbox / action_list_run.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    action_list_run.c for the Openbox window manager
4    Copyright (c) 2011        Dana Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "action_list_run.h"
20 #include "action.h"
21 #include "action_filter.h"
22 #include "action_list.h"
23 #include "client.h"
24 #include "client_set.h"
25 #include "event.h"
26 #include "screen.h"
27
28 static gboolean run_list(ObActionList *acts, const ObActionListRun *data,
29                          ObClientSet *set);
30 static gboolean run_filter(ObActionList *acts, const ObActionListRun *data,
31                            ObClientSet *set);
32
33 gboolean action_list_run(ObActionList *acts,
34                          ObUserAction uact,
35                          guint state,
36                          gint x,
37                          gint y,
38                          gint button,
39                          ObFrameContext con,
40                          struct _ObClient *client)
41 {
42     ObActionListRun action_data;
43
44     if (acts == NULL) return FALSE;
45
46     /* Don't save the initial mod state when running things from the menu */
47     if (uact == OB_USER_ACTION_MENU_SELECTION)
48         state = 0;
49     /* If x and y are < 0 then use the current pointer position */
50     if (x < 0 && y < 0)
51         screen_pointer_pos(&x, &y);
52
53     action_data.user_act = uact;
54     action_data.mod_state = state;
55     action_data.pointer_x = x;
56     action_data.pointer_y = y;
57     action_data.pointer_button = button;
58     action_data.pointer_context = con;
59     action_data.target = client;
60     /* if a pointer started the event clicking on a window, it must be under
61        the pointer */
62     action_data.pointer_over = client ? client : client_under_pointer();
63
64     return run_list(acts, &action_data, NULL);
65 }
66
67 static gboolean run_list(ObActionList *acts, const ObActionListRun *data,
68                          ObClientSet *set)
69 {
70     gboolean interactive;
71     ObClientSet *myset;
72
73     if (!acts) return FALSE;
74     if (acts->isfilterset) return run_filter(acts, data, set);
75
76     /* if we're not given a filter, then make a default filter set,
77        but don't pass it on to our siblings in the list. */
78     myset = set;
79     if (!myset) {
80         switch (action_default_filter(acts->u.action)) {
81         case OB_ACTION_DEFAULT_FILTER_SINGLE:
82             myset = client_set_single(data->target); break;
83         case OB_ACTION_DEFAULT_FILTER_EMPTY:
84             myset = client_set_empty(); break;
85         case OB_ACTION_DEFAULT_FILTER_ALL:
86             myset = client_set_all(); break;
87         case OB_NUM_ACTION_DEFAULT_FILTERS:
88         default: g_assert_not_reached();
89         }
90     }
91
92     interactive = action_run(acts->u.action, data, myset);
93     if (set != myset) client_set_destroy(myset);
94
95     if (interactive) return TRUE;
96     return run_list(acts->next, data, set);
97 }
98
99 static gboolean run_filter(ObActionList *acts, const ObActionListRun *data,
100                            ObClientSet *incoming_set)
101 {
102     ObActionListTest *test = acts->u.f.test;
103     ObClientSet *set, *and_set;
104     gboolean prev_and;
105     gboolean interactive;
106
107     /* (a ^ b) | c | (d ^ e ^ f) | (g ^ h)
108
109        - for each test in the filter:
110          1) when we are at the first test, we make the test's set our current
111             set
112          2) when we are between two ORs, we add the test's set to our current
113             set
114          3) when we are to the left of an OR (or at the last test), we
115             intersect our test's set to the and_set, and then add the add_set
116             to our current set
117          4) otherwise, we are to the left of an AND
118             a) if we are to the right of an OR, we make and_set our test's set
119             b) else we are between two ANDs, so we intersect and_set with
120                the test's set
121        - finally, we take the intersection of our created set with the
122          incoming set.
123     */
124
125     g_assert(test != NULL);
126
127     and_set = NULL;
128
129     set = action_filter_set(test->filter, data);
130     prev_and = test->and;
131     test = test->next;
132     while (test) {
133         ObClientSet *const test_set = action_filter_set(test->filter, data);
134
135         if (!prev_and && test->next && !test->and)
136             set = client_set_union(set, test_set);
137         else if (!test->and || !test->next) {
138             if (and_set)
139                 and_set = client_set_intersection(and_set, test_set);
140             else 
141                 and_set = test_set;
142             set = client_set_union(set, and_set);
143             and_set = NULL;
144         }
145         else {
146             if (and_set)
147                 and_set = client_set_intersection(and_set, test_set);
148             else
149                 and_set = test_set;
150         }
151         test = test->next;
152     }
153
154     if (incoming_set) {
155         /* we don't want to destroy the incoming set so make a copy of it */
156         set = client_set_intersection(set, client_set_clone(incoming_set));
157     }
158
159     if (client_set_test_boolean(set))
160         interactive = run_list(acts->u.f.thendo, data, set);
161     else
162         interactive = run_list(acts->u.f.elsedo, data, set);
163     client_set_destroy(set);
164
165     if (interactive) return TRUE;
166     return run_list(acts->next, data, incoming_set);
167 }