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 <openbox/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 */
90 width += 2*theme->mbwidth + 2*PADDING;
92 /* get minimum title size */
93 RrMinSize(title, &tw, &title_h);
95 /* size of background behind each text line */
96 bw = width - 2*theme->mbwidth;
97 //title_h += 2*PADDING;
98 title_h = theme->menu_title_height;
100 RrMinSize(normal, &unused, &th);
103 height = title_h + 3*bh + 3*theme->mbwidth;
105 //height += 3*th + 3*theme->mbwidth + 5*PADDING;
108 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
109 gdk_pixbuf_fill(pixbuf, rr_color_pixel(theme->menu_border_color));
112 x = y = theme->mbwidth;
113 theme_pixmap_paint(title, bw, title_h);
115 /* draw title text */
116 title_text->surface.parentx = 0;
117 title_text->surface.parenty = 0;
119 theme_pixmap_paint(title_text, bw, title_h);
121 pixmap = gdk_pixmap_foreign_new(title_text->pixmap);
122 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
123 gdk_colormap_get_system(),
124 0, 0, x, y, bw, title_h);
126 /* menu appears after title */
127 y += theme->mbwidth + title_h;
129 /* fill in menu appearance, used as the parent to every menu item's bg */
130 menu = theme->a_menu;
131 th = height - 3*theme->mbwidth - title_h;
132 theme_pixmap_paint(menu, bw, th);
134 pixmap = gdk_pixmap_foreign_new(menu->pixmap);
135 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
136 gdk_colormap_get_system(),
139 /* fill in background appearance, used as the parent to text items */
140 background = theme->a_menu_normal;
141 background->surface.parent = menu;
142 background->surface.parentx = 0;
143 background->surface.parenty = 0;
145 /* draw background for normal entry */
146 theme_pixmap_paint(background, bw, bh);
147 pixmap = gdk_pixmap_foreign_new(background->pixmap);
148 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
149 gdk_colormap_get_system(),
152 /* draw normal entry */
153 normal->surface.parent = background;
154 normal->surface.parentx = PADDING;
155 normal->surface.parenty = PADDING;
158 RrMinSize(normal, &tw, &th);
159 theme_pixmap_paint(normal, tw, th);
160 pixmap = gdk_pixmap_foreign_new(normal->pixmap);
161 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
162 gdk_colormap_get_system(),
166 RrMinSize(normal, &tw, &th);
167 bullet->surface.parent = background;
168 bullet->surface.parentx = bw - th;
169 bullet->surface.parenty = PADDING;
170 theme_pixmap_paint(bullet, th, th);
171 pixmap = gdk_pixmap_foreign_new(bullet->pixmap);
172 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
173 gdk_colormap_get_system(),
174 0, 0, width - theme->mbwidth - th, y,
179 /* draw background for disabled entry */
180 background->surface.parenty = bh;
181 theme_pixmap_paint(background, bw, bh);
182 pixmap = gdk_pixmap_foreign_new(background->pixmap);
183 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
184 gdk_colormap_get_system(),
185 0, 0, x - PADDING, y - PADDING,
188 /* draw disabled entry */
189 RrMinSize(disabled, &tw, &th);
190 disabled->surface.parent = background;
191 disabled->surface.parentx = PADDING;
192 disabled->surface.parenty = PADDING;
193 theme_pixmap_paint(disabled, tw, th);
194 pixmap = gdk_pixmap_foreign_new(disabled->pixmap);
195 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
196 gdk_colormap_get_system(),
201 /* draw background for selected entry */
202 background = theme->a_menu_selected;
203 background->surface.parent = menu;
204 background->surface.parentx = 2*bh;
206 theme_pixmap_paint(background, bw, bh);
207 pixmap = gdk_pixmap_foreign_new(background->pixmap);
208 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
209 gdk_colormap_get_system(),
210 0, 0, x - PADDING, y - PADDING,
213 /* draw selected entry */
214 RrMinSize(selected, &tw, &th);
215 selected->surface.parent = background;
216 selected->surface.parentx = PADDING;
217 selected->surface.parenty = PADDING;
218 theme_pixmap_paint(selected, tw, th);
219 pixmap = gdk_pixmap_foreign_new(selected->pixmap);
220 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
221 gdk_colormap_get_system(),
227 static GdkPixbuf* preview_window(RrTheme *theme, const gchar *titlelayout,
228 gboolean focus, gint width, gint height)
231 RrAppearance *handle;
235 GdkPixbuf *pixbuf = NULL;
238 gint w, label_w, h, x, y;
242 title = focus ? theme->a_focused_title : theme->a_unfocused_title;
245 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
246 gdk_pixbuf_fill(pixbuf, rr_color_pixel(theme->menu_border_color));
249 w = width - 2*theme->fbwidth;
250 h = theme->title_height;
251 theme_pixmap_paint(title, w, h);
253 x = y = theme->fbwidth;;
254 pixmap = gdk_pixmap_foreign_new(title->pixmap);
255 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
256 gdk_colormap_get_system(),
259 /* calculate label width */
260 label_w = width - (theme->paddingx + theme->fbwidth + 1) * 2;
262 for (layout = titlelayout; *layout; layout++) {
265 label_w -= theme->button_size + 2 + theme->paddingx + 1;
272 label_w -= theme->button_size + theme->paddingx + 1;
279 x = theme->paddingx + theme->fbwidth + 1;
280 y += theme->paddingy + 1;
281 for (layout = titlelayout; *layout; layout++) {
283 if (*layout == 'N') {
285 /* set default icon */
286 a->texture[0].type = RR_TEXTURE_RGBA;
287 a->texture[0].data.rgba.width = 48;
288 a->texture[0].data.rgba.height = 48;
289 a->texture[0].data.rgba.alpha = 0xff;
290 a->texture[0].data.rgba.data = theme->def_win_icon;
292 a->surface.parent = title;
293 a->surface.parentx = x;
294 a->surface.parenty = theme->paddingy;
296 w = h = theme->button_size + 2;
298 theme_pixmap_paint(a, w, h);
299 pixmap = gdk_pixmap_foreign_new(a->pixmap);
300 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
301 gdk_colormap_get_system(),
302 0, 0, x, y - 1, w, h);
304 x += theme->button_size + 2 + theme->paddingx + 1;
305 } else if (*layout == 'L') { /* label */
306 a = focus ? theme->a_focused_label : theme->a_unfocused_label;
307 a->texture[0].data.text.string = focus ? "active" : "inactive";
309 a->surface.parent = title;
310 a->surface.parentx = x;
311 a->surface.parenty = theme->paddingy;
313 h = theme->label_height;
315 theme_pixmap_paint(a, w, h);
316 pixmap = gdk_pixmap_foreign_new(a->pixmap);
317 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
318 gdk_colormap_get_system(),
319 0, 0, x, y - 1, w, h);
321 x += w + theme->paddingx + 1;
327 theme->a_focused_unpressed_desk :
328 theme->a_unfocused_unpressed_desk;
332 theme->a_focused_unpressed_shade :
333 theme->a_unfocused_unpressed_shade;
337 theme->a_focused_unpressed_iconify :
338 theme->a_unfocused_unpressed_iconify;
342 theme->a_focused_unpressed_max :
343 theme->a_unfocused_unpressed_max;
347 theme->a_focused_unpressed_close :
348 theme->a_unfocused_unpressed_close;
354 a->surface.parent = title;
355 a->surface.parentx = x;
356 a->surface.parenty = theme->paddingy + 1;
358 w = theme->button_size;
359 h = theme->button_size;
361 theme_pixmap_paint(a, w, h);
362 pixmap = gdk_pixmap_foreign_new(a->pixmap);
363 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
364 gdk_colormap_get_system(),
367 x += theme->button_size + theme->paddingx + 1;
371 if (theme->handle_height) {
373 handle = focus ? theme->a_focused_handle : theme->a_unfocused_handle;
374 x = 2*theme->fbwidth + theme->grip_width;
375 y = height - theme->fbwidth - theme->handle_height;
376 w = width - 4*theme->fbwidth - 2*theme->grip_width;
377 h = theme->handle_height;
379 theme_pixmap_paint(handle, w, h);
380 pixmap = gdk_pixmap_foreign_new(handle->pixmap);
381 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
382 gdk_colormap_get_system(),
385 /* openbox handles this drawing stuff differently (it fills the bottom
386 * of the window with the handle), so it avoids this bug where
387 * parentrelative grips are not fully filled. i'm avoiding it slightly
390 theme_pixmap_paint(handle, width, h);
393 a = focus ? theme->a_focused_grip : theme->a_unfocused_grip;
394 a->surface.parent = handle;
397 /* same y and h as handle */
398 w = theme->grip_width;
400 theme_pixmap_paint(a, w, h);
401 pixmap = gdk_pixmap_foreign_new(a->pixmap);
402 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
403 gdk_colormap_get_system(),
407 x = width - theme->fbwidth - theme->grip_width;
408 pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
409 gdk_colormap_get_system(),
413 /* retarded way of adding client colour */
415 y = theme->title_height + 2*theme->fbwidth;
416 w = width - 2*theme->fbwidth;
417 h = height - theme->title_height - 3*theme->fbwidth -
418 (theme->handle_height ? (theme->fbwidth + theme->handle_height) : 0);
420 scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, w, h);
421 gdk_pixbuf_fill(scratch, rr_color_pixel(focus ?
422 theme->cb_focused_color :
423 theme->cb_unfocused_color));
425 gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
430 static gint theme_label_width(RrTheme *theme, gboolean active)
436 label = theme->a_focused_label;
437 label->texture[0].data.text.string = "active";
439 label = theme->a_unfocused_label;
440 label->texture[0].data.text.string = "inactive";
443 return RrMinWidth(label);
446 static gint theme_window_min_width(RrTheme *theme, const gchar *titlelayout)
448 gint numbuttons = strlen(titlelayout);
449 gint w = 2 * (theme->fbwidth + theme->paddingx + 1);
451 if (g_strrstr(titlelayout, "L")) {
453 w += MAX(theme_label_width(theme, TRUE),
454 theme_label_width(theme, FALSE)) + theme->paddingx + 1;
457 w += (theme->button_size + theme->paddingx + 1) * numbuttons;
462 GdkPixbuf *preview_theme(const gchar *name, const gchar *titlelayout,
463 RrFont *active_window_font,
464 RrFont *inactive_window_font,
465 RrFont *menu_title_font,
466 RrFont *menu_item_font,
479 RrTheme *theme = RrThemeNew(rrinst, name,
480 active_window_font, inactive_window_font,
481 menu_title_font, menu_item_font, osd_font);
483 menu = preview_menu(theme);
485 window_w = theme_window_min_width(theme, titlelayout);
487 menu_w = gdk_pixbuf_get_width(menu);
488 h = gdk_pixbuf_get_height(menu);
490 w = MAX(window_w, menu_w) + 20;
492 /* we don't want windows disappearing on us */
493 if (!window_w) window_w = menu_w;
495 preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
496 w, h + 2*(theme->title_height +5) + 1);
497 gdk_pixbuf_fill(preview, 0); /* clear */
499 window = preview_window(theme, titlelayout, FALSE, window_w, h);
500 gdk_pixbuf_copy_area(window, 0, 0, window_w, h, preview, 20, 0);
502 window = preview_window(theme, titlelayout, TRUE, window_w, h);
503 gdk_pixbuf_copy_area(window, 0, 0, window_w, h,
504 preview, 10, theme->title_height + 5);
506 gdk_pixbuf_copy_area(menu, 0, 0, menu_w, h,
507 preview, 0, 2 * (theme->title_height + 5));