Add <position> tag to ShowMenu action
[dana/openbox.git] / openbox / actions / showmenu.c
1 #include "openbox/actions.h"
2 #include "openbox/menu.h"
3 #include "openbox/place.h"
4 #include "openbox/geom.h"
5 #include "openbox/screen.h"
6 #include <glib.h>
7
8 typedef struct {
9     gchar         *name;
10     GravityCoord   x, y;
11     ObPlaceMonitor monitor_type;
12     gint           monitor;
13     gboolean       use_position;
14 } Options;
15
16 static gpointer setup_func(xmlNodePtr node);
17 static void     free_func(gpointer options);
18 static gboolean run_func(ObActionsData *data, gpointer options);
19
20 void action_showmenu_startup(void)
21 {
22     actions_register("ShowMenu", setup_func, free_func, run_func);
23 }
24
25 static gpointer setup_func(xmlNodePtr node)
26 {
27     xmlNodePtr n, c;
28     Options *o;
29     gboolean x_pos_given = FALSE;
30
31     o = g_slice_new0(Options);
32     o->monitor = -1;
33
34     if ((n = obt_xml_find_node(node, "menu")))
35         o->name = obt_xml_node_string(n);
36
37     if ((n = obt_xml_find_node(node, "position"))) {
38         if ((c = obt_xml_find_node(n->children, "x"))) {
39             if (!obt_xml_node_contains(c, "default")) {
40                 config_parse_gravity_coord(c, &o->x);
41                 x_pos_given = TRUE;
42             }
43         }
44
45         if (x_pos_given && (c = obt_xml_find_node(n->children, "y"))) {
46             if (!obt_xml_node_contains(c, "default")) {
47                 config_parse_gravity_coord(c, &o->y);
48                 o->use_position = TRUE;
49             }
50         }
51
52         /* unlike client placement, x/y is needed to specify a monitor,
53          * either it's under the mouse or it's in an exact actual position */
54         if (o->use_position && (c = obt_xml_find_node(n->children, "monitor"))) {
55             if (!obt_xml_node_contains(c, "default")) {
56                 gchar *s = obt_xml_node_string(c);
57                 if (!g_ascii_strcasecmp(s, "mouse"))
58                     o->monitor_type = OB_PLACE_MONITOR_MOUSE;
59                 else if (!g_ascii_strcasecmp(s, "active"))
60                     o->monitor_type = OB_PLACE_MONITOR_ACTIVE;
61                 else if (!g_ascii_strcasecmp(s, "primary"))
62                     o->monitor_type = OB_PLACE_MONITOR_PRIMARY;
63                 else if (!g_ascii_strcasecmp(s, "all"))
64                     o->monitor_type = OB_PLACE_MONITOR_ALL;
65                 else
66                     o->monitor = obt_xml_node_int(c) - 1;
67                 g_free(s);
68             }
69         }
70     }
71     return o;
72 }
73
74 static void free_func(gpointer options)
75 {
76     Options *o = options;
77     g_free(o->name);
78     g_slice_free(Options, o);
79 }
80
81 static void calc_position(Options *o, gint *x, gint *y)
82 {
83     gint monitor = -1;
84     const Rect *area;
85     if (o->monitor >= 0)
86         monitor = o->monitor;
87     else switch (o->monitor_type) {
88         case OB_PLACE_MONITOR_ANY:
89         case OB_PLACE_MONITOR_PRIMARY:
90             monitor = screen_monitor_primary(FALSE);
91             break;
92         case OB_PLACE_MONITOR_MOUSE:
93             monitor = screen_monitor_pointer();
94             break;
95         case OB_PLACE_MONITOR_ACTIVE:
96             monitor = screen_monitor_active();
97             break;
98         case OB_PLACE_MONITOR_ALL:
99             monitor = screen_num_monitors;
100             break;
101         default:
102             g_assert_not_reached();
103     }
104     area = screen_physical_area_monitor(monitor);
105
106     if (o->x.center)
107         *x = area->width / 2; /* - client->area.width / 2; */
108     else {
109         *x = o->x.pos;
110         if (o->x.denom)
111             *x = (*x * area->width) / o->x.denom;
112         if (o->x.opposite)
113             *x = area->width /* - frame_size.width */ - *x;
114     }
115
116     if (o->y.center)
117         *y = area->height / 2; /* - client->area.height / 2; */
118     else {
119         *y = o->y.pos;
120         if (o->y.denom)
121             *y = (*y * area->height) / o->y.denom;
122         if (o->y.opposite)
123             *y = area->height /* - frame_size.height */ - *y;
124     }
125
126     *x += area->x;
127     *y += area->y;
128 }
129
130 /* Always return FALSE because its not interactive */
131 static gboolean run_func(ObActionsData *data, gpointer options)
132 {
133     Options *o = options;
134     gint x, y;
135
136     if (o->use_position) {
137         calc_position(o, &x, &y);
138     } else {
139         x = data->x;
140         y = data->y;
141     }
142
143     /* you cannot call ShowMenu from inside a menu */
144     if (data->uact != OB_USER_ACTION_MENU_SELECTION && o->name)
145         menu_show(o->name, x, y, data->button != 0, data->client);
146
147     return FALSE;
148 }