make interactive actions a type and not special cases.
[mikachu/openbox.git] / openbox / popup.c
1 #include "popup.h"
2
3 #include "openbox.h"
4 #include "frame.h"
5 #include "client.h"
6 #include "window.h"
7 #include "stacking.h"
8 #include "render/render.h"
9 #include "render/theme.h"
10
11 struct _ObPopup
12 {
13     ObWindow obwin;
14     Window bg;
15
16     Window icon;
17     Window text;
18
19     gboolean hasicon;
20     RrAppearance *a_bg;
21     RrAppearance *a_icon;
22     RrAppearance *a_text;
23     gint gravity;
24     gint x;
25     gint y;
26     gint w;
27     gint h;
28     gboolean mapped;
29 };
30
31 Popup *popup_new(gboolean hasicon)
32 {
33     XSetWindowAttributes attrib;
34     Popup *self = g_new(Popup, 1);
35
36     self->obwin.type = Window_Internal;
37     self->hasicon = hasicon;
38     self->a_text = NULL;
39     self->gravity = NorthWestGravity;
40     self->x = self->y = self->w = self->h = 0;
41     self->mapped = FALSE;
42     self->a_bg = self->a_icon = self->a_text = NULL;
43
44     attrib.override_redirect = True;
45     self->bg = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen),
46                              0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
47                              InputOutput, RrVisual(ob_rr_inst),
48                              CWOverrideRedirect, &attrib);
49     
50     self->text = XCreateWindow(ob_display, self->bg,
51                                0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
52                                InputOutput, RrVisual(ob_rr_inst), 0, NULL);
53
54     if (self->hasicon)
55         self->icon = XCreateWindow(ob_display, self->bg,
56                                    0, 0, 1, 1, 0,
57                                    RrDepth(ob_rr_inst), InputOutput,
58                                    RrVisual(ob_rr_inst), 0, NULL);
59
60     XMapWindow(ob_display, self->text);
61     XMapWindow(ob_display, self->icon);
62
63     stacking_add(INTERNAL_AS_WINDOW(self));
64     return self;
65 }
66
67 void popup_free(Popup *self)
68 {
69     if (self) {
70         XDestroyWindow(ob_display, self->bg);
71         XDestroyWindow(ob_display, self->text);
72         XDestroyWindow(ob_display, self->icon);
73         RrAppearanceFree(self->a_bg);
74         RrAppearanceFree(self->a_icon);
75         RrAppearanceFree(self->a_text);
76         stacking_remove(self);
77         g_free(self);
78     }
79 }
80
81 void popup_position(Popup *self, gint gravity, gint x, gint y)
82 {
83     self->gravity = gravity;
84     self->x = x;
85     self->y = y;
86 }
87
88 void popup_size(Popup *self, gint w, gint h)
89 {
90     self->w = w;
91     self->h = h;
92 }
93
94 void popup_size_to_string(Popup *self, gchar *text)
95 {
96     gint textw, texth;
97     gint iconw;
98
99     if (!self->a_text)
100         self->a_text = RrAppearanceCopy(ob_rr_theme->app_hilite_label);
101
102     self->a_text->texture[0].data.text.string = text;
103     RrMinsize(self->a_text, &textw, &texth);
104     textw += ob_rr_theme->bevel * 2;
105     texth += ob_rr_theme->bevel * 2;
106
107     self->h = texth + ob_rr_theme->bevel * 2;
108     iconw = (self->hasicon ? texth : 0);
109     self->w = textw + iconw + ob_rr_theme->bevel * (self->hasicon ? 3 : 2);
110 }
111
112 void popup_set_text_align(Popup *self, RrJustify align)
113 {
114     if (!self->a_text)
115         self->a_text = RrAppearanceCopy(ob_rr_theme->app_hilite_label);
116
117     self->a_text->texture[0].data.text.justify = align;
118 }
119
120 void popup_show(Popup *self, gchar *text, ObClientIcon *icon)
121 {
122     gint x, y, w, h;
123     gint textw, texth;
124     gint iconw;
125
126     /* create the shit if needed */
127     if (!self->a_bg)
128         self->a_bg = RrAppearanceCopy(ob_rr_theme->app_hilite_bg);
129     if (self->hasicon && !self->a_icon)
130         self->a_icon = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
131     if (!self->a_text)
132         self->a_text = RrAppearanceCopy(ob_rr_theme->app_hilite_label);
133
134     XSetWindowBorderWidth(ob_display, self->bg, ob_rr_theme->bwidth);
135     XSetWindowBorder(ob_display, self->bg, ob_rr_theme->b_color->pixel);
136
137     /* set up the textures */
138     self->a_text->texture[0].data.text.string = text;
139     if (self->hasicon) {
140         if (icon) {
141             self->a_icon->texture[0].type = RR_TEXTURE_RGBA;
142             self->a_icon->texture[0].data.rgba.width = icon->width;
143             self->a_icon->texture[0].data.rgba.height = icon->height;
144             self->a_icon->texture[0].data.rgba.data = icon->data;
145         } else
146             self->a_icon->texture[0].type = RR_TEXTURE_NONE;
147     }
148
149     /* measure the shit out */
150     RrMinsize(self->a_text, &textw, &texth);
151     textw += ob_rr_theme->bevel * 2;
152     texth += ob_rr_theme->bevel * 2;
153
154     /* set the sizes up and reget the text sizes from the calculated
155        outer sizes */
156     if (self->h) {
157         h = self->h;
158         texth = h - (ob_rr_theme->bevel * 2);
159     } else
160         h = texth + ob_rr_theme->bevel * 2;
161     iconw = (self->hasicon ? texth : 0);
162     if (self->w) {
163         w = self->w;
164         textw = w - (iconw + ob_rr_theme->bevel * (self->hasicon ? 3 : 2));
165     } else
166         w = textw + iconw + ob_rr_theme->bevel * (self->hasicon ? 3 : 2);
167     /* sanity checks to avoid crashes! */
168     if (w < 1) w = 1;
169     if (h < 1) h = 1;
170     if (textw < 1) textw = 1;
171     if (texth < 1) texth = 1;
172
173     /* set up the x coord */
174     x = self->x;
175     switch (self->gravity) {
176     case NorthGravity:
177     case CenterGravity:
178     case SouthGravity:
179         x -= w / 2;
180         break;
181     case NorthEastGravity:
182     case EastGravity:
183     case SouthEastGravity:
184         x -= w;
185         break;
186     }
187
188     /* set up the y coord */
189     y = self->y;
190     switch (self->gravity) {
191     case WestGravity:
192     case CenterGravity:
193     case EastGravity:
194         y -= h / 2;
195         break;
196     case SouthWestGravity:
197     case SouthGravity:
198     case SouthEastGravity:
199         y -= h;
200         break;
201     }
202
203     /* set the windows/appearances up */
204     XMoveResizeWindow(ob_display, self->bg, x, y, w, h);
205
206     self->a_text->surface.parent = self->a_bg;
207     self->a_text->surface.parentx = iconw +
208         ob_rr_theme->bevel * (self->hasicon ? 2 : 1);
209     self->a_text->surface.parenty = ob_rr_theme->bevel;
210     XMoveResizeWindow(ob_display, self->text,
211                       iconw + ob_rr_theme->bevel * (self->hasicon ? 2 : 1),
212                       ob_rr_theme->bevel, textw, texth);
213
214     if (self->hasicon) {
215         if (iconw < 1) iconw = 1; /* sanity check for crashes */
216         self->a_icon->surface.parent = self->a_bg;
217         self->a_icon->surface.parentx = ob_rr_theme->bevel;
218         self->a_icon->surface.parenty = ob_rr_theme->bevel;
219         XMoveResizeWindow(ob_display, self->icon,
220                           ob_rr_theme->bevel, ob_rr_theme->bevel,
221                           iconw, texth);
222     }
223
224     RrPaint(self->a_bg, self->bg, w, h);
225     RrPaint(self->a_text, self->text, textw, texth);
226     if (self->hasicon)
227         RrPaint(self->a_icon, self->icon, iconw, texth);
228
229     if (!self->mapped) {
230         XMapWindow(ob_display, self->bg);
231         stacking_raise(INTERNAL_AS_WINDOW(self));
232         self->mapped = TRUE;
233     }
234 }
235
236 void popup_hide(Popup *self)
237 {
238     if (self->mapped) {
239         XUnmapWindow(ob_display, self->bg);
240         self->mapped = FALSE;
241     }
242 }