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;
206 if (strcmp("SlickBox", theme->name) == 0)
207 printf("y %d parenty %d bh %d height %d menuheight %d parentbottom %d\n",
208 y, background->surface.parenty, bh, height,
209 height - 3*theme->mbwidth,
210 background->surface.parenty + bh);
212 theme_pixmap_paint(background, bw, bh);
213 pixmap = gdk_pixmap_foreign_new(background->pixmap);
214 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
215 gdk_colormap_get_system(),
219 /* draw selected entry */
220 RrMinSize(selected, &tw, &th);
221 selected->surface.parent = background;
222 selected->surface.parentx = PADDING;
223 selected->surface.parenty = PADDING;
224 theme_pixmap_paint(selected, tw, th);
225 pixmap = gdk_pixmap_foreign_new(selected->pixmap);
226 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
227 gdk_colormap_get_system(),
228 0, 0, x + PADDING, y + PADDING,
234 static GdkPixbuf* preview_window(RrTheme *theme, const gchar *titlelayout,
235 gboolean focus, gint width, gint height)
238 RrAppearance *handle;
242 GdkPixbuf *pixbuf = NULL;
245 gint w, label_w, h, x, y;
249 title = focus ? theme->a_focused_title : theme->a_unfocused_title;
252 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
253 gdk_pixbuf_fill(pixbuf,
254 rr_color_pixel(focus ?
255 theme->frame_focused_border_color :
256 theme->frame_unfocused_border_color));
259 w = width - 2*theme->fbwidth;
260 h = theme->title_height;
261 theme_pixmap_paint(title, w, h);
263 x = y = theme->fbwidth;
264 pixmap = gdk_pixmap_foreign_new(title->pixmap);
265 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
266 gdk_colormap_get_system(),
269 /* calculate label width */
270 label_w = width - (theme->paddingx + theme->fbwidth + 1) * 2;
272 for (layout = titlelayout; *layout; layout++) {
275 label_w -= theme->button_size + 2 + theme->paddingx + 1;
282 label_w -= theme->button_size + theme->paddingx + 1;
289 x = theme->paddingx + theme->fbwidth + 1;
290 y += theme->paddingy;
291 for (layout = titlelayout; *layout; layout++) {
293 if (*layout == 'N') {
295 /* set default icon */
296 a->texture[0].type = RR_TEXTURE_RGBA;
297 a->texture[0].data.rgba.width = 48;
298 a->texture[0].data.rgba.height = 48;
299 a->texture[0].data.rgba.alpha = 0xff;
300 a->texture[0].data.rgba.data = theme->def_win_icon;
302 a->surface.parent = title;
303 a->surface.parentx = x - theme->fbwidth;
304 a->surface.parenty = theme->paddingy;
306 w = h = theme->button_size + 2;
308 theme_pixmap_paint(a, w, h);
309 pixmap = gdk_pixmap_foreign_new(a->pixmap);
310 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
311 gdk_colormap_get_system(),
314 x += theme->button_size + 2 + theme->paddingx + 1;
315 } else if (*layout == 'L') { /* label */
316 a = focus ? theme->a_focused_label : theme->a_unfocused_label;
317 a->texture[0].data.text.string = focus ? "Active" : "Inactive";
319 a->surface.parent = title;
320 a->surface.parentx = x - theme->fbwidth;
321 a->surface.parenty = theme->paddingy;
323 h = theme->label_height;
325 theme_pixmap_paint(a, w, h);
326 pixmap = gdk_pixmap_foreign_new(a->pixmap);
327 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
328 gdk_colormap_get_system(),
331 x += w + theme->paddingx + 1;
337 theme->btn_desk->a_focused_unpressed :
338 theme->btn_desk->a_unfocused_unpressed;
342 theme->btn_shade->a_focused_unpressed :
343 theme->btn_shade->a_unfocused_unpressed;
347 theme->btn_iconify->a_focused_unpressed :
348 theme->btn_iconify->a_unfocused_unpressed;
352 theme->btn_max->a_focused_unpressed :
353 theme->btn_max->a_unfocused_unpressed;
357 theme->btn_close->a_focused_unpressed :
358 theme->btn_close->a_unfocused_unpressed;
364 a->surface.parent = title;
365 a->surface.parentx = x - theme->fbwidth;
366 a->surface.parenty = theme->paddingy + 1;
368 w = theme->button_size;
369 h = theme->button_size;
371 theme_pixmap_paint(a, w, h);
372 pixmap = gdk_pixmap_foreign_new(a->pixmap);
373 /* use y + 1 because these buttons should be centered wrt the label
375 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
376 gdk_colormap_get_system(),
377 0, 0, x, y + 1, w, h);
379 x += theme->button_size + theme->paddingx + 1;
383 if (theme->handle_height) {
385 handle = focus ? theme->a_focused_handle : theme->a_unfocused_handle;
386 x = 2*theme->fbwidth + theme->grip_width;
387 y = height - theme->fbwidth - theme->handle_height;
388 w = width - 4*theme->fbwidth - 2*theme->grip_width;
389 h = theme->handle_height;
391 theme_pixmap_paint(handle, w, h);
392 pixmap = gdk_pixmap_foreign_new(handle->pixmap);
393 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
394 gdk_colormap_get_system(),
397 /* openbox handles this drawing stuff differently (it fills the bottom
398 * of the window with the handle), so it avoids this bug where
399 * parentrelative grips are not fully filled. i'm avoiding it slightly
402 theme_pixmap_paint(handle, width, h);
405 a = focus ? theme->a_focused_grip : theme->a_unfocused_grip;
406 a->surface.parent = handle;
409 /* same y and h as handle */
410 w = theme->grip_width;
412 theme_pixmap_paint(a, w, h);
413 pixmap = gdk_pixmap_foreign_new(a->pixmap);
414 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
415 gdk_colormap_get_system(),
419 x = width - theme->fbwidth - theme->grip_width;
420 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
421 gdk_colormap_get_system(),
425 /* title separator colour */
427 y = theme->fbwidth + theme->title_height;
428 w = width - 2*theme->fbwidth;
431 scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
432 gdk_pixbuf_fill(scratch, rr_color_pixel(focus ?
433 theme->title_separator_focused_color :
434 theme->title_separator_unfocused_color));
436 gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
438 /* retarded way of adding client colour */
440 y = theme->title_height + 2*theme->fbwidth;
441 w = width - 2*theme->fbwidth;
442 h = height - theme->title_height - 3*theme->fbwidth -
443 (theme->handle_height ? (theme->fbwidth + theme->handle_height) : 0);
445 scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
446 gdk_pixbuf_fill(scratch, rr_color_pixel(focus ?
447 theme->cb_focused_color :
448 theme->cb_unfocused_color));
450 gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
452 /* clear (no alpha!) the area where the client resides */
453 gdk_pixbuf_fill(scratch, 0xffffffff);
454 gdk_pixbuf_copy_area(scratch, 0, 0,
455 w - 2*theme->cbwidthx,
456 h - 2*theme->cbwidthy,
459 y + theme->cbwidthy);
464 static gint theme_label_width(RrTheme *theme, gboolean active)
470 label = theme->a_focused_label;
471 label->texture[0].data.text.string = "Active";
473 label = theme->a_unfocused_label;
474 label->texture[0].data.text.string = "Inactive";
477 return RrMinWidth(label);
480 static gint theme_window_min_width(RrTheme *theme, const gchar *titlelayout)
482 gint numbuttons = strlen(titlelayout);
483 gint w = 2 * theme->fbwidth + (numbuttons + 3) * (theme->paddingx + 1);
485 if (g_strrstr(titlelayout, "L")) {
487 w += MAX(theme_label_width(theme, TRUE),
488 theme_label_width(theme, FALSE));
491 w += theme->button_size * numbuttons;
496 GdkPixbuf *preview_theme(const gchar *name, const gchar *titlelayout,
497 RrFont *active_window_font,
498 RrFont *inactive_window_font,
499 RrFont *menu_title_font,
500 RrFont *menu_item_font,
501 RrFont *osd_active_font,
502 RrFont *osd_inactive_font)
514 RrTheme *theme = RrThemeNew(rrinst, name, FALSE,
515 active_window_font, inactive_window_font,
516 menu_title_font, menu_item_font,
517 osd_active_font, osd_inactive_font);
521 menu = preview_menu(theme);
523 window_w = theme_window_min_width(theme, titlelayout);
525 menu_w = gdk_pixbuf_get_width(menu);
526 h = gdk_pixbuf_get_height(menu);
528 w = MAX(window_w, menu_w) + 20;
530 /* we don't want windows disappearing on us */
531 if (!window_w) window_w = menu_w;
533 preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
534 w, h + 2*(theme->title_height +5) + 1);
535 gdk_pixbuf_fill(preview, 0); /* clear */
537 window = preview_window(theme, titlelayout, FALSE, window_w, h);
538 gdk_pixbuf_copy_area(window, 0, 0, window_w, h, preview, 20, 0);
540 window = preview_window(theme, titlelayout, TRUE, window_w, h);
541 gdk_pixbuf_copy_area(window, 0, 0, window_w, h,
542 preview, 10, theme->title_height + 5);
544 gdk_pixbuf_copy_area(menu, 0, 0, menu_w, h,
545 preview, 0, 2 * (theme->title_height + 5));