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 */
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));
108 /* menu appears after inside the border */
109 x = y = theme->mbwidth;
111 /* fill in menu appearance, used as the parent to every menu item's bg */
112 menu = theme->a_menu;
113 th = height - 2 * theme->mbwidth;
114 theme_pixmap_paint(menu, bw, th);
116 /* draw title, it appears at the top of the menu background */
117 title->surface.parent = theme->a_menu;
118 title->surface.parentx = 0;
119 title->surface.parenty = 0;
120 theme_pixmap_paint(title, bw, title_h);
122 /* draw title text */
123 title_text->surface.parentx = 0;
124 title_text->surface.parenty = 0;
126 theme_pixmap_paint(title_text, bw, title_h);
128 pixmap = gdk_pixmap_foreign_new(title_text->pixmap);
129 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
130 gdk_colormap_get_system(),
131 0, 0, x, y, bw, title_h);
133 y += title_h + theme->mbwidth;
135 /* fill in background appearance, used as the parent to text items */
136 background = theme->a_menu_normal;
137 background->surface.parent = menu;
138 background->surface.parentx = x - theme->mbwidth;
139 background->surface.parenty = y - theme->mbwidth;
141 /* draw background for normal entry */
142 theme_pixmap_paint(background, bw, bh);
143 pixmap = gdk_pixmap_foreign_new(background->pixmap);
144 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
145 gdk_colormap_get_system(),
148 /* draw normal entry */
149 normal->surface.parent = background;
150 normal->surface.parentx = PADDING;
151 normal->surface.parenty = PADDING;
152 RrMinSize(normal, &tw, &th);
153 theme_pixmap_paint(normal, tw, th);
154 pixmap = gdk_pixmap_foreign_new(normal->pixmap);
155 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
156 gdk_colormap_get_system(),
157 0, 0, x + PADDING, y + PADDING,
161 RrMinSize(normal, &tw, &th);
162 bullet->surface.parent = background;
163 bullet->surface.parentx = bw - th;
164 bullet->surface.parenty = PADDING;
165 theme_pixmap_paint(bullet, th, th);
166 pixmap = gdk_pixmap_foreign_new(bullet->pixmap);
167 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
168 gdk_colormap_get_system(),
170 width - theme->mbwidth - th,
176 /* draw background for disabled entry */
177 background->surface.parent = menu;
178 background->surface.parentx = x - theme->mbwidth;
179 background->surface.parenty = y - theme->mbwidth;
180 theme_pixmap_paint(background, bw, bh);
181 pixmap = gdk_pixmap_foreign_new(background->pixmap);
182 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
183 gdk_colormap_get_system(),
187 /* draw disabled entry */
188 RrMinSize(disabled, &tw, &th);
189 disabled->surface.parent = background;
190 disabled->surface.parentx = PADDING;
191 disabled->surface.parenty = PADDING;
192 theme_pixmap_paint(disabled, tw, th);
193 pixmap = gdk_pixmap_foreign_new(disabled->pixmap);
194 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
195 gdk_colormap_get_system(),
196 0, 0, x + PADDING, y + PADDING,
201 /* draw background for selected entry */
202 background = theme->a_menu_selected;
203 background->surface.parent = menu;
204 background->surface.parentx = x - theme->mbwidth;
205 background->surface.parenty = y - theme->mbwidth;
207 theme_pixmap_paint(background, bw, bh);
208 pixmap = gdk_pixmap_foreign_new(background->pixmap);
209 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
210 gdk_colormap_get_system(),
214 /* draw selected entry */
215 RrMinSize(selected, &tw, &th);
216 selected->surface.parent = background;
217 selected->surface.parentx = PADDING;
218 selected->surface.parenty = PADDING;
219 theme_pixmap_paint(selected, tw, th);
220 pixmap = gdk_pixmap_foreign_new(selected->pixmap);
221 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
222 gdk_colormap_get_system(),
223 0, 0, x + PADDING, y + PADDING,
229 static GdkPixbuf* preview_window(RrTheme *theme, const gchar *titlelayout,
230 gboolean focus, gint width, gint height)
233 RrAppearance *handle;
237 GdkPixbuf *pixbuf = NULL;
240 gint w, label_w, h, x, y;
244 title = focus ? theme->a_focused_title : theme->a_unfocused_title;
247 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
248 gdk_pixbuf_fill(pixbuf,
249 rr_color_pixel(focus ?
250 theme->frame_focused_border_color :
251 theme->frame_unfocused_border_color));
254 w = width - 2*theme->fbwidth;
255 h = theme->title_height;
256 theme_pixmap_paint(title, w, h);
258 x = y = theme->fbwidth;
259 pixmap = gdk_pixmap_foreign_new(title->pixmap);
260 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
261 gdk_colormap_get_system(),
264 /* calculate label width */
265 label_w = width - (theme->paddingx + theme->fbwidth + 1) * 2;
267 for (layout = titlelayout; *layout; layout++) {
270 label_w -= theme->button_size + 2 + theme->paddingx + 1;
277 label_w -= theme->button_size + theme->paddingx + 1;
284 x = theme->paddingx + theme->fbwidth + 1;
285 y += theme->paddingy;
286 for (layout = titlelayout; *layout; layout++) {
288 if (*layout == 'N') {
290 /* set default icon */
291 a->texture[0].type = RR_TEXTURE_RGBA;
292 a->texture[0].data.rgba.width = 48;
293 a->texture[0].data.rgba.height = 48;
294 a->texture[0].data.rgba.alpha = 0xff;
295 a->texture[0].data.rgba.data = theme->def_win_icon;
297 a->surface.parent = title;
298 a->surface.parentx = x - theme->fbwidth;
299 a->surface.parenty = theme->paddingy;
301 w = h = theme->button_size + 2;
303 theme_pixmap_paint(a, w, h);
304 pixmap = gdk_pixmap_foreign_new(a->pixmap);
305 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
306 gdk_colormap_get_system(),
309 x += theme->button_size + 2 + theme->paddingx + 1;
310 } else if (*layout == 'L') { /* label */
311 a = focus ? theme->a_focused_label : theme->a_unfocused_label;
312 a->texture[0].data.text.string = focus ? "Active" : "Inactive";
314 a->surface.parent = title;
315 a->surface.parentx = x - theme->fbwidth;
316 a->surface.parenty = theme->paddingy;
318 h = theme->label_height;
320 theme_pixmap_paint(a, w, h);
321 pixmap = gdk_pixmap_foreign_new(a->pixmap);
322 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
323 gdk_colormap_get_system(),
326 x += w + theme->paddingx + 1;
332 theme->btn_desk->a_focused_unpressed :
333 theme->btn_desk->a_unfocused_unpressed;
337 theme->btn_shade->a_focused_unpressed :
338 theme->btn_shade->a_unfocused_unpressed;
342 theme->btn_iconify->a_focused_unpressed :
343 theme->btn_iconify->a_unfocused_unpressed;
347 theme->btn_max->a_focused_unpressed :
348 theme->btn_max->a_unfocused_unpressed;
352 theme->btn_close->a_focused_unpressed :
353 theme->btn_close->a_unfocused_unpressed;
359 a->surface.parent = title;
360 a->surface.parentx = x - theme->fbwidth;
361 a->surface.parenty = theme->paddingy + 1;
363 w = theme->button_size;
364 h = theme->button_size;
366 theme_pixmap_paint(a, w, h);
367 pixmap = gdk_pixmap_foreign_new(a->pixmap);
368 /* use y + 1 because these buttons should be centered wrt the label
370 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
371 gdk_colormap_get_system(),
372 0, 0, x, y + 1, w, h);
374 x += theme->button_size + theme->paddingx + 1;
378 if (theme->handle_height) {
380 handle = focus ? theme->a_focused_handle : theme->a_unfocused_handle;
381 x = 2*theme->fbwidth + theme->grip_width;
382 y = height - theme->fbwidth - theme->handle_height;
383 w = width - 4*theme->fbwidth - 2*theme->grip_width;
384 h = theme->handle_height;
386 theme_pixmap_paint(handle, w, h);
387 pixmap = gdk_pixmap_foreign_new(handle->pixmap);
388 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
389 gdk_colormap_get_system(),
392 /* openbox handles this drawing stuff differently (it fills the bottom
393 * of the window with the handle), so it avoids this bug where
394 * parentrelative grips are not fully filled. i'm avoiding it slightly
397 theme_pixmap_paint(handle, width, h);
400 a = focus ? theme->a_focused_grip : theme->a_unfocused_grip;
401 a->surface.parent = handle;
404 /* same y and h as handle */
405 w = theme->grip_width;
407 theme_pixmap_paint(a, w, h);
408 pixmap = gdk_pixmap_foreign_new(a->pixmap);
409 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
410 gdk_colormap_get_system(),
414 x = width - theme->fbwidth - theme->grip_width;
415 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
416 gdk_colormap_get_system(),
420 /* title separator colour */
422 y = theme->fbwidth + theme->title_height;
423 w = width - 2*theme->fbwidth;
426 scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
427 gdk_pixbuf_fill(scratch, rr_color_pixel(focus ?
428 theme->title_separator_focused_color :
429 theme->title_separator_unfocused_color));
431 gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
433 /* retarded way of adding client colour */
435 y = theme->title_height + 2*theme->fbwidth;
436 w = width - 2*theme->fbwidth;
437 h = height - theme->title_height - 3*theme->fbwidth -
438 (theme->handle_height ? (theme->fbwidth + theme->handle_height) : 0);
440 scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
441 gdk_pixbuf_fill(scratch, rr_color_pixel(focus ?
442 theme->cb_focused_color :
443 theme->cb_unfocused_color));
445 gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
447 /* clear (no alpha!) the area where the client resides */
448 gdk_pixbuf_fill(scratch, 0xffffffff);
449 gdk_pixbuf_copy_area(scratch, 0, 0,
450 w - 2*theme->cbwidthx,
451 h - 2*theme->cbwidthy,
454 y + theme->cbwidthy);
459 static gint theme_label_width(RrTheme *theme, gboolean active)
465 label = theme->a_focused_label;
466 label->texture[0].data.text.string = "Active";
468 label = theme->a_unfocused_label;
469 label->texture[0].data.text.string = "Inactive";
472 return RrMinWidth(label);
475 static gint theme_window_min_width(RrTheme *theme, const gchar *titlelayout)
477 gint numbuttons = strlen(titlelayout);
478 gint w = 2 * theme->fbwidth + (numbuttons + 3) * (theme->paddingx + 1);
480 if (g_strrstr(titlelayout, "L")) {
482 w += MAX(theme_label_width(theme, TRUE),
483 theme_label_width(theme, FALSE));
486 w += theme->button_size * numbuttons;
491 GdkPixbuf *preview_theme(const gchar *name, const gchar *titlelayout,
492 RrFont *active_window_font,
493 RrFont *inactive_window_font,
494 RrFont *menu_title_font,
495 RrFont *menu_item_font,
496 RrFont *osd_active_font,
497 RrFont *osd_inactive_font)
509 RrTheme *theme = RrThemeNew(rrinst, name, FALSE,
510 active_window_font, inactive_window_font,
511 menu_title_font, menu_item_font,
512 osd_active_font, osd_inactive_font);
516 menu = preview_menu(theme);
518 window_w = theme_window_min_width(theme, titlelayout);
520 menu_w = gdk_pixbuf_get_width(menu);
521 h = gdk_pixbuf_get_height(menu);
523 w = MAX(window_w, menu_w) + 20;
525 /* we don't want windows disappearing on us */
526 if (!window_w) window_w = menu_w;
528 preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
529 w, h + 2*(theme->title_height +5) + 1);
530 gdk_pixbuf_fill(preview, 0); /* clear */
532 window = preview_window(theme, titlelayout, FALSE, window_w, h);
533 gdk_pixbuf_copy_area(window, 0, 0, window_w, h, preview, 20, 0);
535 window = preview_window(theme, titlelayout, TRUE, window_w, h);
536 gdk_pixbuf_copy_area(window, 0, 0, window_w, h,
537 preview, 10, theme->title_height + 5);
539 gdk_pixbuf_copy_area(menu, 0, 0, menu_w, h,
540 preview, 0, 2 * (theme->title_height + 5));