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