1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 preview.c for ObConf, the configuration tool for Openbox
4 Copyright (c) 2007 Javeed Shaikh
5 Copyright (c) 2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
26 #include <obrender/theme.h>
28 #define PADDING 2 /* openbox does it :/ */
30 static void theme_pixmap_paint(RrAppearance *a, gint w, gint h)
32 Pixmap out = RrPaintPixmap(a, w, h);
33 if (out) XFreePixmap(RrDisplay(a->inst), out);
36 static guint32 rr_color_pixel(const RrColor *c)
38 return (guint32)((RrColorRed(c) << 24) + (RrColorGreen(c) << 16) +
39 + (RrColorBlue(c) << 8) + 0xff);
42 /* XXX: Make this more general */
43 static GdkPixbuf* preview_menu(RrTheme *theme)
46 RrAppearance *title_text;
49 RrAppearance *background;
52 RrAppearance *disabled;
53 RrAppearance *selected;
54 RrAppearance *bullet; /* for submenu */
57 GdkPixbuf *pixbuf, *tmp_pixbuf;
59 /* width and height of the whole menu */
67 /* set up appearances */
68 title = theme->a_menu_title;
70 title_text = theme->a_menu_text_title;
71 title_text->surface.parent = title;
72 title_text->texture[0].data.text.string = "Menu";
74 normal = theme->a_menu_text_normal;
75 normal->texture[0].data.text.string = "Normal";
77 disabled = theme->a_menu_text_disabled;
78 disabled->texture[0].data.text.string = "Disabled";
80 selected = theme->a_menu_text_selected;
81 selected->texture[0].data.text.string = "Selected";
83 bullet = theme->a_menu_bullet_normal;
85 /* determine window size */
86 RrMinSize(normal, &width, &th);
87 width += th + PADDING; /* make space for the bullet */
89 width += 2*theme->mbwidth + 2*PADDING;
91 /* get minimum title size */
92 RrMinSize(title, &tw, &title_h);
94 /* size of background behind each text line */
95 bw = width - 2*theme->mbwidth;
96 //title_h += 2*PADDING;
97 title_h = theme->menu_title_height;
99 RrMinSize(normal, &unused, &th);
102 height = title_h + 3*bh + 3*theme->mbwidth;
105 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
106 gdk_pixbuf_fill(pixbuf, rr_color_pixel(theme->menu_border_color));
107 tmp_pixbuf = gdk_pixbuf_copy(pixbuf);
109 /* menu appears after inside the border */
110 x = y = theme->mbwidth;
112 /* fill in menu appearance, used as the parent to every menu item's bg */
113 menu = theme->a_menu;
114 th = height - 2 * theme->mbwidth;
115 theme_pixmap_paint(menu, bw, th);
117 /* draw title, it appears at the top of the menu background */
118 title->surface.parent = theme->a_menu;
119 title->surface.parentx = 0;
120 title->surface.parenty = 0;
121 theme_pixmap_paint(title, bw, title_h);
123 /* draw title text */
124 title_text->surface.parentx = 0;
125 title_text->surface.parenty = 0;
127 theme_pixmap_paint(title_text, bw, title_h);
129 pixmap = gdk_pixmap_foreign_new(title_text->pixmap);
130 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
131 gdk_colormap_get_system(),
134 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, bw, title_h, pixbuf, x, y);
136 y += title_h + theme->mbwidth;
138 /* fill in background appearance, used as the parent to text items */
139 background = theme->a_menu_normal;
140 background->surface.parent = menu;
141 background->surface.parentx = x - theme->mbwidth;
142 background->surface.parenty = y - theme->mbwidth;
144 /* draw background for normal entry */
145 theme_pixmap_paint(background, bw, bh);
146 pixmap = gdk_pixmap_foreign_new(background->pixmap);
147 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
148 gdk_colormap_get_system(),
151 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, bw, bh, pixbuf, x, y);
153 /* draw normal entry */
154 normal->surface.parent = background;
155 normal->surface.parentx = PADDING;
156 normal->surface.parenty = PADDING;
157 RrMinSize(normal, &tw, &th);
158 theme_pixmap_paint(normal, tw, th);
159 pixmap = gdk_pixmap_foreign_new(normal->pixmap);
160 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
161 gdk_colormap_get_system(),
164 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, tw, th, pixbuf,
165 x + PADDING, y + PADDING);
168 RrMinSize(normal, &tw, &th);
169 bullet->surface.parent = background;
170 bullet->surface.parentx = bw - th;
171 bullet->surface.parenty = PADDING;
172 theme_pixmap_paint(bullet, th, th);
173 pixmap = gdk_pixmap_foreign_new(bullet->pixmap);
174 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
175 gdk_colormap_get_system(),
178 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, th, th, pixbuf,
179 width - theme->mbwidth - th, y + PADDING);
183 /* draw background for disabled entry */
184 background->surface.parent = menu;
185 background->surface.parentx = x - theme->mbwidth;
186 background->surface.parenty = y - theme->mbwidth;
187 theme_pixmap_paint(background, bw, bh);
188 pixmap = gdk_pixmap_foreign_new(background->pixmap);
189 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
190 gdk_colormap_get_system(),
193 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, bw, bh, pixbuf, x, y);
195 /* draw disabled entry */
196 RrMinSize(disabled, &tw, &th);
197 disabled->surface.parent = background;
198 disabled->surface.parentx = PADDING;
199 disabled->surface.parenty = PADDING;
200 theme_pixmap_paint(disabled, tw, th);
201 pixmap = gdk_pixmap_foreign_new(disabled->pixmap);
202 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
203 gdk_colormap_get_system(),
206 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, tw, th, pixbuf,
207 x + PADDING, y + PADDING);
211 /* draw background for selected entry */
212 background = theme->a_menu_selected;
213 background->surface.parent = menu;
214 background->surface.parentx = x - theme->mbwidth;
215 background->surface.parenty = y - theme->mbwidth;
217 theme_pixmap_paint(background, bw, bh);
218 pixmap = gdk_pixmap_foreign_new(background->pixmap);
219 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
220 gdk_colormap_get_system(),
223 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, bw, bh, pixbuf, x, y);
225 /* draw selected entry */
226 RrMinSize(selected, &tw, &th);
227 selected->surface.parent = background;
228 selected->surface.parentx = PADDING;
229 selected->surface.parenty = PADDING;
230 theme_pixmap_paint(selected, tw, th);
231 pixmap = gdk_pixmap_foreign_new(selected->pixmap);
232 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
233 gdk_colormap_get_system(),
236 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, tw, th, pixbuf,
237 x + PADDING, y + PADDING);
238 g_object_unref(tmp_pixbuf);
243 static GdkPixbuf* preview_window(RrTheme *theme, const gchar *titlelayout,
244 gboolean focus, gint width, gint height)
247 RrAppearance *handle;
251 GdkPixbuf *pixbuf = NULL, *tmp_pixbuf = NULL;
254 gint w, label_w, h, x, y;
258 title = focus ? theme->a_focused_title : theme->a_unfocused_title;
261 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
262 gdk_pixbuf_fill(pixbuf,
263 rr_color_pixel(focus ?
264 theme->frame_focused_border_color :
265 theme->frame_unfocused_border_color));
266 tmp_pixbuf = gdk_pixbuf_copy(pixbuf);
269 w = width - 2*theme->fbwidth;
270 h = theme->title_height;
271 theme_pixmap_paint(title, w, h);
273 x = y = theme->fbwidth;
274 pixmap = gdk_pixmap_foreign_new(title->pixmap);
275 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
276 gdk_colormap_get_system(),
278 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, w, h, pixbuf, x, y);
280 /* calculate label width */
281 label_w = width - (theme->paddingx + theme->fbwidth + 1) * 2;
283 for (layout = titlelayout; *layout; layout++) {
286 label_w -= theme->button_size + 2 + theme->paddingx + 1;
293 label_w -= theme->button_size + theme->paddingx + 1;
300 x = theme->paddingx + theme->fbwidth + 1;
301 y += theme->paddingy;
302 for (layout = titlelayout; *layout; layout++) {
304 if (*layout == 'N') {
306 /* set default icon */
307 a->texture[0].type = RR_TEXTURE_RGBA;
308 a->texture[0].data.rgba.width = 48;
309 a->texture[0].data.rgba.height = 48;
310 a->texture[0].data.rgba.alpha = 0xff;
311 a->texture[0].data.rgba.data = theme->def_win_icon;
313 a->surface.parent = title;
314 a->surface.parentx = x - theme->fbwidth;
315 a->surface.parenty = theme->paddingy;
317 w = h = theme->button_size + 2;
319 theme_pixmap_paint(a, w, h);
320 pixmap = gdk_pixmap_foreign_new(a->pixmap);
321 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
322 gdk_colormap_get_system(),
324 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, w, h, pixbuf, x, y);
326 x += theme->button_size + 2 + theme->paddingx + 1;
327 } else if (*layout == 'L') { /* label */
328 a = focus ? theme->a_focused_label : theme->a_unfocused_label;
329 a->texture[0].data.text.string = focus ? "Active" : "Inactive";
331 a->surface.parent = title;
332 a->surface.parentx = x - theme->fbwidth;
333 a->surface.parenty = theme->paddingy;
335 h = theme->label_height;
337 theme_pixmap_paint(a, w, h);
338 pixmap = gdk_pixmap_foreign_new(a->pixmap);
339 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
340 gdk_colormap_get_system(),
342 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, w, h, pixbuf, x, y);
344 x += w + theme->paddingx + 1;
350 theme->btn_desk->a_focused_unpressed :
351 theme->btn_desk->a_unfocused_unpressed;
355 theme->btn_shade->a_focused_unpressed :
356 theme->btn_shade->a_unfocused_unpressed;
360 theme->btn_iconify->a_focused_unpressed :
361 theme->btn_iconify->a_unfocused_unpressed;
365 theme->btn_max->a_focused_unpressed :
366 theme->btn_max->a_unfocused_unpressed;
370 theme->btn_close->a_focused_unpressed :
371 theme->btn_close->a_unfocused_unpressed;
377 a->surface.parent = title;
378 a->surface.parentx = x - theme->fbwidth;
379 a->surface.parenty = theme->paddingy + 1;
381 w = theme->button_size;
382 h = theme->button_size;
384 theme_pixmap_paint(a, w, h);
385 pixmap = gdk_pixmap_foreign_new(a->pixmap);
386 /* use y + 1 because these buttons should be centered wrt the label
388 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
389 gdk_colormap_get_system(),
391 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, w, h, pixbuf, x, y + 1);
393 x += theme->button_size + theme->paddingx + 1;
397 if (theme->handle_height) {
399 handle = focus ? theme->a_focused_handle : theme->a_unfocused_handle;
400 x = 2*theme->fbwidth + theme->grip_width;
401 y = height - theme->fbwidth - theme->handle_height;
402 w = width - 4*theme->fbwidth - 2*theme->grip_width;
403 h = theme->handle_height;
405 theme_pixmap_paint(handle, w, h);
406 pixmap = gdk_pixmap_foreign_new(handle->pixmap);
407 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
408 gdk_colormap_get_system(),
410 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, w, h, pixbuf, x, y);
412 /* openbox handles this drawing stuff differently (it fills the bottom
413 * of the window with the handle), so it avoids this bug where
414 * parentrelative grips are not fully filled. i'm avoiding it slightly
417 theme_pixmap_paint(handle, width, h);
420 a = focus ? theme->a_focused_grip : theme->a_unfocused_grip;
421 a->surface.parent = handle;
424 /* same y and h as handle */
425 w = theme->grip_width;
427 theme_pixmap_paint(a, w, h);
428 pixmap = gdk_pixmap_foreign_new(a->pixmap);
429 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
430 gdk_colormap_get_system(),
432 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, w, h, pixbuf, x, y);
435 x = width - theme->fbwidth - theme->grip_width;
436 tmp_pixbuf = gdk_pixbuf_get_from_drawable(tmp_pixbuf, pixmap,
437 gdk_colormap_get_system(),
439 gdk_pixbuf_copy_area(tmp_pixbuf, 0, 0, w, h, pixbuf, x, y);
442 /* title separator colour */
444 y = theme->fbwidth + theme->title_height;
445 w = width - 2*theme->fbwidth;
448 scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
449 gdk_pixbuf_fill(scratch, rr_color_pixel(focus ?
450 theme->title_separator_focused_color :
451 theme->title_separator_unfocused_color));
453 gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
455 /* retarded way of adding client colour */
457 y = theme->title_height + 2*theme->fbwidth;
458 w = width - 2*theme->fbwidth;
459 h = height - theme->title_height - 3*theme->fbwidth -
460 (theme->handle_height ? (theme->fbwidth + theme->handle_height) : 0);
462 scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
463 gdk_pixbuf_fill(scratch, rr_color_pixel(focus ?
464 theme->cb_focused_color :
465 theme->cb_unfocused_color));
467 gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
469 /* clear (no alpha!) the area where the client resides */
470 gdk_pixbuf_fill(scratch, 0xffffffff);
471 gdk_pixbuf_copy_area(scratch, 0, 0,
472 w - 2*theme->cbwidthx,
473 h - 2*theme->cbwidthy,
476 y + theme->cbwidthy);
477 g_object_unref(scratch);
482 static gint theme_label_width(RrTheme *theme, gboolean active)
488 label = theme->a_focused_label;
489 label->texture[0].data.text.string = "Active";
491 label = theme->a_unfocused_label;
492 label->texture[0].data.text.string = "Inactive";
495 return RrMinWidth(label);
498 static gint theme_window_min_width(RrTheme *theme, const gchar *titlelayout)
500 gint numbuttons = strlen(titlelayout);
501 gint w = 2 * theme->fbwidth + (numbuttons + 3) * (theme->paddingx + 1);
503 if (g_strrstr(titlelayout, "L")) {
505 w += MAX(theme_label_width(theme, TRUE),
506 theme_label_width(theme, FALSE));
509 w += theme->button_size * numbuttons;
514 GdkPixbuf *preview_theme(const gchar *name, const gchar *titlelayout,
515 RrFont *active_window_font,
516 RrFont *inactive_window_font,
517 RrFont *menu_title_font,
518 RrFont *menu_item_font,
519 RrFont *osd_active_font,
520 RrFont *osd_inactive_font)
532 RrTheme *theme = RrThemeNew(rrinst, name, FALSE,
533 active_window_font, inactive_window_font,
534 menu_title_font, menu_item_font,
535 osd_active_font, osd_inactive_font);
539 menu = preview_menu(theme);
541 window_w = theme_window_min_width(theme, titlelayout);
543 menu_w = gdk_pixbuf_get_width(menu);
544 h = gdk_pixbuf_get_height(menu);
546 w = MAX(window_w, menu_w) + 20;
548 /* we don't want windows disappearing on us */
549 if (!window_w) window_w = menu_w;
551 preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
552 w, h + 2*(theme->title_height +5) + 1);
553 gdk_pixbuf_fill(preview, 0); /* clear */
555 window = preview_window(theme, titlelayout, FALSE, window_w, h);
556 gdk_pixbuf_copy_area(window, 0, 0, window_w, h, preview, 20, 0);
557 g_object_unref(window);
559 window = preview_window(theme, titlelayout, TRUE, window_w, h);
560 gdk_pixbuf_copy_area(window, 0, 0, window_w, h,
561 preview, 10, theme->title_height + 5);
562 g_object_unref(window);
564 gdk_pixbuf_copy_area(menu, 0, 0, menu_w, h,
565 preview, 0, 2 * (theme->title_height + 5));
566 g_object_unref(menu);