7 #include <openbox/theme.h>
9 #define PADDING 2 /* openbox does it :/ */
11 static void theme_pixmap_paint(RrAppearance *a, gint w, gint h)
13 Pixmap out = RrPaintPixmap(a, w, h);
14 if (out) XFreePixmap(RrDisplay(a->inst), out);
17 static guint32 rr_color_pixel(const RrColor *c)
19 return (guint32)((RrColorRed(c) << 24) | (RrColorGreen(c) << 16)
20 | (RrColorBlue(c) << 8) | 255);
23 /* XXX: Make this more general */
24 static GdkPixbuf* preview_menu(RrTheme *theme)
27 RrAppearance *title_text;
30 RrAppearance *background;
33 RrAppearance *disabled;
34 RrAppearance *selected;
35 RrAppearance *bullet; /* for submenu */
40 /* width and height of the whole menu */
48 /* set up appearances */
49 title = theme->a_menu_title;
51 title_text = theme->a_menu_text_title;
52 title_text->surface.parent = title;
53 title_text->texture[0].data.text.string = "menu";
55 normal = theme->a_menu_text_normal;
56 normal->texture[0].data.text.string = "normal";
58 disabled = theme->a_menu_text_disabled;
59 disabled->texture[0].data.text.string = "disabled";
61 selected = theme->a_menu_text_selected;
62 selected->texture[0].data.text.string = "selected";
64 bullet = theme->a_menu_bullet_normal;
66 /* determine window size */
67 RrMinSize(normal, &width, &th);
68 width += th + PADDING; /* make space for the bullet */
71 width += 2*theme->mbwidth + 2*PADDING;
73 /* get minimum title size */
74 RrMinSize(title, &tw, &title_h);
76 /* size of background behind each text line */
77 bw = width - 2*theme->mbwidth;
78 //title_h += 2*PADDING;
79 title_h = theme->menu_title_height;
81 RrMinSize(normal, &unused, &th);
84 height = title_h + 3*bh + 3*theme->mbwidth;
86 //height += 3*th + 3*theme->mbwidth + 5*PADDING;
89 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
90 gdk_pixbuf_fill(pixbuf, rr_color_pixel(theme->menu_border_color));
93 x = y = theme->mbwidth;
94 theme_pixmap_paint(title, bw, title_h);
97 title_text->surface.parentx = 0;
98 title_text->surface.parenty = 0;
100 theme_pixmap_paint(title_text, bw, title_h);
102 pixmap = gdk_pixmap_foreign_new(title_text->pixmap);
103 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, bw, title_h);
105 /* menu appears after title */
106 y += theme->mbwidth + title_h;
108 /* fill in menu appearance, used as the parent to every menu item's bg */
109 menu = theme->a_menu;
110 th = height - 3*theme->mbwidth - title_h;
111 theme_pixmap_paint(menu, bw, th);
113 pixmap = gdk_pixmap_foreign_new(menu->pixmap);
114 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, bw, th);
116 /* fill in background appearance, used as the parent to text items */
117 background = theme->a_menu_normal;
118 background->surface.parent = menu;
119 background->surface.parentx = 0;
120 background->surface.parenty = 0;
122 /* draw background for normal entry */
123 theme_pixmap_paint(background, bw, bh);
124 pixmap = gdk_pixmap_foreign_new(background->pixmap);
125 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, bw, bh);
127 /* draw normal entry */
128 normal->surface.parent = background;
129 normal->surface.parentx = PADDING;
130 normal->surface.parenty = PADDING;
133 RrMinSize(normal, &tw, &th);
134 theme_pixmap_paint(normal, tw, th);
135 pixmap = gdk_pixmap_foreign_new(normal->pixmap);
136 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, tw, th);
139 RrMinSize(normal, &tw, &th);
140 bullet->surface.parent = background;
141 bullet->surface.parentx = bw - th;
142 bullet->surface.parenty = PADDING;
143 theme_pixmap_paint(bullet, th, th);
144 pixmap = gdk_pixmap_foreign_new(bullet->pixmap);
145 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, width - theme->mbwidth - th, y, th, th);
149 /* draw background for disabled entry */
150 background->surface.parenty = bh;
151 theme_pixmap_paint(background, bw, bh);
152 pixmap = gdk_pixmap_foreign_new(background->pixmap);
153 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x - PADDING, y - PADDING, bw, bh);
155 /* draw disabled entry */
156 RrMinSize(disabled, &tw, &th);
157 disabled->surface.parent = background;
158 disabled->surface.parentx = PADDING;
159 disabled->surface.parenty = PADDING;
160 theme_pixmap_paint(disabled, tw, th);
161 pixmap = gdk_pixmap_foreign_new(disabled->pixmap);
162 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, tw, th);
166 /* draw background for selected entry */
167 background = theme->a_menu_selected;
168 background->surface.parent = menu;
169 background->surface.parentx = 2*bh;
171 theme_pixmap_paint(background, bw, bh);
172 pixmap = gdk_pixmap_foreign_new(background->pixmap);
173 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x - PADDING, y - PADDING, bw, bh);
175 /* draw selected entry */
176 RrMinSize(selected, &tw, &th);
177 selected->surface.parent = background;
178 selected->surface.parentx = PADDING;
179 selected->surface.parenty = PADDING;
180 theme_pixmap_paint(selected, tw, th);
181 pixmap = gdk_pixmap_foreign_new(selected->pixmap);
182 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, tw, th);
187 static GdkPixbuf* preview_window(RrTheme *theme, const gchar *titlelayout, gboolean focus, gint width, gint height)
190 RrAppearance *handle;
194 GdkPixbuf *pixbuf = NULL;
197 gint w, label_w, h, x, y;
201 title = focus ? theme->a_focused_title : theme->a_unfocused_title;
204 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
205 gdk_pixbuf_fill(pixbuf, rr_color_pixel(theme->menu_border_color));
208 w = width - 2*theme->fbwidth;
209 h = theme->title_height;
210 theme_pixmap_paint(title, w, h);
212 x = y = theme->fbwidth;;
213 pixmap = gdk_pixmap_foreign_new(title->pixmap);
214 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
216 /* calculate label width */
217 label_w = width - (theme->paddingx + theme->fbwidth + 1) * 2;
219 for (layout = titlelayout; *layout; layout++) {
222 label_w -= theme->button_size + 2 + theme->paddingx + 1;
229 label_w -= theme->button_size + theme->paddingx + 1;
236 x = theme->paddingx + theme->fbwidth + 1;
237 y += theme->paddingy + 1;
238 for (layout = titlelayout; *layout; layout++) {
240 if (*layout == 'N') {
242 /* set default icon */
243 a->texture[0].type = RR_TEXTURE_RGBA;
244 a->texture[0].data.rgba.width = 48;
245 a->texture[0].data.rgba.height = 48;
246 a->texture[0].data.rgba.data = theme->def_win_icon;
248 a->surface.parent = title;
249 a->surface.parentx = x;
250 a->surface.parenty = theme->paddingy;
252 w = h = theme->button_size + 2;
254 theme_pixmap_paint(a, w, h);
255 pixmap = gdk_pixmap_foreign_new(a->pixmap);
256 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y - 1, w, h);
258 x += theme->button_size + 2 + theme->paddingx + 1;
259 } else if (*layout == 'L') { /* label */
260 a = focus ? theme->a_focused_label : theme->a_unfocused_label;
261 a->texture[0].data.text.string = focus ? "active" : "inactive";
263 a->surface.parent = title;
264 a->surface.parentx = x;
265 a->surface.parenty = theme->paddingy;
267 h = theme->label_height;
269 theme_pixmap_paint(a, w, h);
270 pixmap = gdk_pixmap_foreign_new(a->pixmap);
271 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y - 1, w, h);
273 x += w + theme->paddingx + 1;
278 a = focus ? theme->a_focused_unpressed_desk : theme->a_unfocused_unpressed_desk;
281 a = focus ? theme->a_focused_unpressed_shade : theme->a_unfocused_unpressed_shade;
284 a = focus ? theme->a_focused_unpressed_iconify : theme->a_unfocused_unpressed_iconify;
287 a = focus ? theme->a_focused_unpressed_max : theme->a_unfocused_unpressed_max;
290 a = focus ? theme->a_focused_unpressed_close : theme->a_unfocused_unpressed_close;
296 a->surface.parent = title;
297 a->surface.parentx = x;
298 a->surface.parenty = theme->paddingy + 1;
300 w = theme->button_size;
301 h = theme->button_size;
303 theme_pixmap_paint(a, w, h);
304 pixmap = gdk_pixmap_foreign_new(a->pixmap);
305 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
307 x += theme->button_size + theme->paddingx + 1;
311 if (theme->handle_height) {
313 handle = focus ? theme->a_focused_handle : theme->a_unfocused_handle;
314 x = 2*theme->fbwidth + theme->grip_width;
315 y = height - theme->fbwidth - theme->handle_height;
316 w = width - 4*theme->fbwidth - 2*theme->grip_width;
317 h = theme->handle_height;
319 theme_pixmap_paint(handle, w, h);
320 pixmap = gdk_pixmap_foreign_new(handle->pixmap);
321 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
323 /* openbox handles this drawing stuff differently (it fills the bottom
324 * of the window with the handle), so it avoids this bug where
325 * parentrelative grips are not fully filled. i'm avoiding it slightly
328 theme_pixmap_paint(handle, width, h);
331 a = focus ? theme->a_focused_grip : theme->a_unfocused_grip;
332 a->surface.parent = handle;
335 /* same y and h as handle */
336 w = theme->grip_width;
338 theme_pixmap_paint(a, w, h);
339 pixmap = gdk_pixmap_foreign_new(a->pixmap);
340 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
343 x = width - theme->fbwidth - theme->grip_width;
344 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
347 /* retarded way of adding client colour */
349 y = theme->title_height + 2*theme->fbwidth;
350 w = width - 2*theme->fbwidth;
351 h = height - theme->title_height - 3*theme->fbwidth - (theme->handle_height ? (theme->fbwidth + theme->handle_height) : 0);
353 scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, w, h);
354 gdk_pixbuf_fill(scratch, rr_color_pixel((focus ? theme->cb_focused_color : theme->cb_unfocused_color)));
356 gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
361 static gint theme_label_width(RrTheme *theme, gboolean active)
367 label = theme->a_focused_label;
368 label->texture[0].data.text.string = "active";
370 label = theme->a_unfocused_label;
371 label->texture[0].data.text.string = "inactive";
374 return RrMinWidth(label);
377 static gint theme_window_min_width(RrTheme *theme, const gchar *titlelayout)
379 gint numbuttons = strlen(titlelayout);
380 gint w = 2 * (theme->fbwidth + theme->paddingx + 1);
382 if (g_strrstr(titlelayout, "L")) {
384 w += MAX(theme_label_width(theme, TRUE), theme_label_width(theme, FALSE)) + theme->paddingx + 1;
387 w += (theme->button_size + theme->paddingx + 1) * numbuttons;
392 GdkPixbuf *preview_theme(const gchar *name, const gchar *titlelayout,
393 RrFont *active_window_font,
394 RrFont *inactive_window_font,
395 RrFont *menu_title_font,
396 RrFont *menu_item_font,
409 RrTheme *theme = RrThemeNew(rrinst, name,
410 active_window_font, inactive_window_font,
411 menu_title_font, menu_item_font, osd_font);
413 menu = preview_menu(theme);
415 window_w = theme_window_min_width(theme, titlelayout);
417 menu_w = gdk_pixbuf_get_width(menu);
418 h = gdk_pixbuf_get_height(menu);
420 w = MAX(window_w, menu_w) + 20;
422 preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h + 2*(theme->title_height +5) + 1);
423 gdk_pixbuf_fill(preview, 0); /* clear */
425 window = preview_window(theme, titlelayout, FALSE, window_w, h);
426 gdk_pixbuf_copy_area(window, 0, 0, window_w, h, preview, 20, 0);
428 window = preview_window(theme, titlelayout, TRUE, window_w, h);
429 gdk_pixbuf_copy_area(window, 0, 0, window_w, h, preview, 10, theme->title_height + 5);
431 gdk_pixbuf_copy_area(menu, 0, 0, menu_w, h, preview, 0, 2 * (theme->title_height + 5));