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
#if GTK_CHECK_VERSION(3, 0, 0)
32
#define PADDING 2 /* openbox does it :/ */
36
GdkPixbuf *preview_theme(const gchar *name, const gchar *titlelayout,
37
RrFont *active_window_font,
38
RrFont *inactive_window_font,
39
RrFont *menu_title_font,
40
RrFont *menu_item_font,
41
RrFont *osd_active_font,
42
RrFont *osd_inactive_font);
46
static void theme_pixmap_paint(RrAppearance *a, gint w, gint h)
48
Pixmap out = RrPaintPixmap(a, w, h);
49
if (out) XFreePixmap(RrDisplay(a->inst), out);
52
static guint32 rr_color_pixel(const RrColor *c)
54
return (guint32)((RrColorRed(c) << 24) + (RrColorGreen(c) << 16) +
55
+ (RrColorBlue(c) << 8) + 0xff);
58
/* XXX: Make this more general */
59
static GdkPixbuf* preview_menu(RrTheme *theme)
62
RrAppearance *title_text;
65
RrAppearance *background;
68
RrAppearance *disabled;
69
RrAppearance *selected;
70
RrAppearance *bullet; /* for submenu */
71
#if GTK_CHECK_VERSION(3, 0, 0)
72
cairo_surface_t *surface;
73
Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
79
/* width and height of the whole menu */
87
/* set up appearances */
88
title = theme->a_menu_title;
90
title_text = theme->a_menu_text_title;
91
title_text->surface.parent = title;
92
title_text->texture[0].data.text.string = "Menu";
94
normal = theme->a_menu_text_normal;
95
normal->texture[0].data.text.string = "Normal";
97
disabled = theme->a_menu_text_disabled;
98
disabled->texture[0].data.text.string = "Disabled";
100
selected = theme->a_menu_text_selected;
101
selected->texture[0].data.text.string = "Selected";
103
bullet = theme->a_menu_bullet_normal;
105
/* determine window size */
106
RrMinSize(normal, &width, &th);
107
width += th + PADDING; /* make space for the bullet */
110
width += 2*theme->mbwidth + 2*PADDING;
112
/* get minimum title size */
113
RrMinSize(title, &tw, &title_h);
115
/* size of background behind each text line */
116
bw = width - 2*theme->mbwidth;
117
//title_h += 2*PADDING;
118
title_h = theme->menu_title_height;
120
RrMinSize(normal, &unused, &th);
123
height = title_h + 3*bh + 3*theme->mbwidth;
125
//height += 3*th + 3*theme->mbwidth + 5*PADDING;
128
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
129
gdk_pixbuf_fill(pixbuf, rr_color_pixel(theme->menu_border_color));
132
x = y = theme->mbwidth;
133
theme_pixmap_paint(title, bw, title_h);
135
/* draw title text */
136
title_text->surface.parentx = 0;
137
title_text->surface.parenty = 0;
139
theme_pixmap_paint(title_text, bw, title_h);
141
#if GTK_CHECK_VERSION(3, 0, 0)
142
surface = cairo_xlib_surface_create(dpy,
144
DefaultVisual(dpy, 0),
148
pixbuf = gdk_pixbuf_get_from_surface(surface,
154
cairo_surface_destroy(surface);
157
pixmap = gdk_pixmap_foreign_new(title_text->pixmap);
158
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
159
gdk_colormap_get_system(),
160
0, 0, x, y, bw, title_h);
162
/* menu appears after title */
163
y += theme->mbwidth + title_h;
165
/* fill in menu appearance, used as the parent to every menu item's bg */
166
menu = theme->a_menu;
167
th = height - 3*theme->mbwidth - title_h;
168
theme_pixmap_paint(menu, bw, th);
170
#if GTK_CHECK_VERSION(3, 0, 0)
171
surface = cairo_xlib_surface_create(dpy,
173
DefaultVisual(dpy, 0),
177
pixbuf = gdk_pixbuf_get_from_surface(surface,
183
cairo_surface_destroy(surface);
186
pixmap = gdk_pixmap_foreign_new(menu->pixmap);
187
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
188
gdk_colormap_get_system(),
192
/* fill in background appearance, used as the parent to text items */
193
background = theme->a_menu_normal;
194
background->surface.parent = menu;
195
background->surface.parentx = 0;
196
background->surface.parenty = 0;
198
/* draw background for normal entry */
199
theme_pixmap_paint(background, bw, bh);
200
#if GTK_CHECK_VERSION(3, 0, 0)
201
surface = cairo_xlib_surface_create(dpy,
203
DefaultVisual(dpy, 0),
207
pixbuf = gdk_pixbuf_get_from_surface(surface,
213
cairo_surface_destroy(surface);
216
pixmap = gdk_pixmap_foreign_new(background->pixmap);
217
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
218
gdk_colormap_get_system(),
222
/* draw normal entry */
223
normal->surface.parent = background;
224
normal->surface.parentx = PADDING;
225
normal->surface.parenty = PADDING;
228
RrMinSize(normal, &tw, &th);
229
theme_pixmap_paint(normal, tw, th);
230
#if GTK_CHECK_VERSION(3, 0, 0)
231
surface = cairo_xlib_surface_create(dpy,
233
DefaultVisual(dpy, 0),
237
pixbuf = gdk_pixbuf_get_from_surface(surface,
243
cairo_surface_destroy(surface);
246
pixmap = gdk_pixmap_foreign_new(normal->pixmap);
247
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
248
gdk_colormap_get_system(),
253
RrMinSize(normal, &tw, &th);
254
bullet->surface.parent = background;
255
bullet->surface.parentx = bw - th;
256
bullet->surface.parenty = PADDING;
257
theme_pixmap_paint(bullet, th, th);
258
#if GTK_CHECK_VERSION(3, 0, 0)
259
surface = cairo_xlib_surface_create(dpy,
261
DefaultVisual(dpy, 0),
262
width - theme->mbwidth - th,
265
pixbuf = gdk_pixbuf_get_from_surface(surface,
271
cairo_surface_destroy(surface);
274
pixmap = gdk_pixmap_foreign_new(bullet->pixmap);
275
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
276
gdk_colormap_get_system(),
277
0, 0, width - theme->mbwidth - th, y,
282
/* draw background for disabled entry */
283
background->surface.parenty = bh;
284
theme_pixmap_paint(background, bw, bh);
285
#if GTK_CHECK_VERSION(3, 0, 0)
286
surface = cairo_xlib_surface_create(dpy,
288
DefaultVisual(dpy, 0),
292
pixbuf = gdk_pixbuf_get_from_surface(surface,
298
cairo_surface_destroy(surface);
301
pixmap = gdk_pixmap_foreign_new(background->pixmap);
302
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
303
gdk_colormap_get_system(),
304
0, 0, x - PADDING, y - PADDING,
307
/* draw disabled entry */
308
RrMinSize(disabled, &tw, &th);
309
disabled->surface.parent = background;
310
disabled->surface.parentx = PADDING;
311
disabled->surface.parenty = PADDING;
312
theme_pixmap_paint(disabled, tw, th);
313
#if GTK_CHECK_VERSION(3, 0, 0)
314
surface = cairo_xlib_surface_create(dpy,
316
DefaultVisual(dpy, 0),
320
pixbuf = gdk_pixbuf_get_from_surface(surface,
326
cairo_surface_destroy(surface);
329
pixmap = gdk_pixmap_foreign_new(disabled->pixmap);
330
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
331
gdk_colormap_get_system(),
336
/* draw background for selected entry */
337
background = theme->a_menu_selected;
338
background->surface.parent = menu;
339
background->surface.parentx = 2*bh;
341
theme_pixmap_paint(background, bw, bh);
342
#if GTK_CHECK_VERSION(3, 0, 0)
343
surface = cairo_xlib_surface_create(dpy,
345
DefaultVisual(dpy, 0),
349
pixbuf = gdk_pixbuf_get_from_surface(surface,
355
cairo_surface_destroy(surface);
358
pixmap = gdk_pixmap_foreign_new(background->pixmap);
359
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
360
gdk_colormap_get_system(),
361
0, 0, x - PADDING, y - PADDING,
365
/* draw selected entry */
366
RrMinSize(selected, &tw, &th);
367
selected->surface.parent = background;
368
selected->surface.parentx = PADDING;
369
selected->surface.parenty = PADDING;
370
theme_pixmap_paint(selected, tw, th);
371
#if GTK_CHECK_VERSION(3, 0, 0)
372
surface = cairo_xlib_surface_create(dpy,
374
DefaultVisual(dpy, 0),
378
pixbuf = gdk_pixbuf_get_from_surface(surface,
384
cairo_surface_destroy(surface);
387
pixmap = gdk_pixmap_foreign_new(selected->pixmap);
388
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
389
gdk_colormap_get_system(),
395
static GdkPixbuf* preview_window(RrTheme *theme, const gchar *titlelayout,
396
gboolean focus, gint width, gint height)
399
RrAppearance *handle;
401
#if GTK_CHECK_VERSION(3, 0, 0)
402
cairo_surface_t *surface;
403
Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
407
GdkPixbuf *pixbuf = NULL;
410
gint w, label_w, h, x, y;
414
title = focus ? theme->a_focused_title : theme->a_unfocused_title;
417
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
418
gdk_pixbuf_fill(pixbuf,
419
rr_color_pixel(focus ?
420
theme->frame_focused_border_color :
421
theme->frame_unfocused_border_color));
424
w = width - 2*theme->fbwidth;
425
h = theme->title_height;
426
theme_pixmap_paint(title, w, h);
428
x = y = theme->fbwidth;
429
#if GTK_CHECK_VERSION(3, 0, 0)
430
surface = cairo_xlib_surface_create(dpy,
432
DefaultVisual(dpy, 0),
436
pixbuf = gdk_pixbuf_get_from_surface(surface,
442
cairo_surface_destroy(surface);
445
pixmap = gdk_pixmap_foreign_new(title->pixmap);
446
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
447
gdk_colormap_get_system(),
450
/* calculate label width */
451
label_w = width - (theme->paddingx + theme->fbwidth + 1) * 2;
453
for (layout = titlelayout; *layout; layout++) {
456
label_w -= theme->button_size + 2 + theme->paddingx + 1;
463
label_w -= theme->button_size + theme->paddingx + 1;
470
x = theme->paddingx + theme->fbwidth + 1;
471
y += theme->paddingy;
472
for (layout = titlelayout; *layout; layout++) {
474
if (*layout == 'N') {
476
/* set default icon */
477
a->texture[0].type = RR_TEXTURE_RGBA;
478
a->texture[0].data.rgba.width = 48;
479
a->texture[0].data.rgba.height = 48;
480
a->texture[0].data.rgba.alpha = 0xff;
481
a->texture[0].data.rgba.data = theme->def_win_icon;
483
a->surface.parent = title;
484
a->surface.parentx = x - theme->fbwidth;
485
a->surface.parenty = theme->paddingy;
487
w = h = theme->button_size + 2;
489
theme_pixmap_paint(a, w, h);
490
#if GTK_CHECK_VERSION(3, 0, 0)
491
surface = cairo_xlib_surface_create(dpy,
493
DefaultVisual(dpy, 0),
497
pixbuf = gdk_pixbuf_get_from_surface(surface,
503
cairo_surface_destroy(surface);
506
pixmap = gdk_pixmap_foreign_new(a->pixmap);
507
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
508
gdk_colormap_get_system(),
512
x += theme->button_size + 2 + theme->paddingx + 1;
513
} else if (*layout == 'L') { /* label */
514
a = focus ? theme->a_focused_label : theme->a_unfocused_label;
515
a->texture[0].data.text.string = focus ? "Active" : "Inactive";
517
a->surface.parent = title;
518
a->surface.parentx = x - theme->fbwidth;
519
a->surface.parenty = theme->paddingy;
521
h = theme->label_height;
523
theme_pixmap_paint(a, w, h);
524
#if GTK_CHECK_VERSION(3, 0, 0)
525
surface = cairo_xlib_surface_create(dpy,
527
DefaultVisual(dpy, 0),
531
pixbuf = gdk_pixbuf_get_from_surface(surface,
537
cairo_surface_destroy(surface);
540
pixmap = gdk_pixmap_foreign_new(a->pixmap);
541
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
542
gdk_colormap_get_system(),
546
x += w + theme->paddingx + 1;
552
theme->a_focused_unpressed_desk :
553
theme->a_unfocused_unpressed_desk;
557
theme->a_focused_unpressed_shade :
558
theme->a_unfocused_unpressed_shade;
562
theme->a_focused_unpressed_iconify :
563
theme->a_unfocused_unpressed_iconify;
567
theme->a_focused_unpressed_max :
568
theme->a_unfocused_unpressed_max;
572
theme->a_focused_unpressed_close :
573
theme->a_unfocused_unpressed_close;
579
a->surface.parent = title;
580
a->surface.parentx = x - theme->fbwidth;
581
a->surface.parenty = theme->paddingy + 1;
583
w = theme->button_size;
584
h = theme->button_size;
586
theme_pixmap_paint(a, w, h);
587
#if GTK_CHECK_VERSION(3, 0, 0)
588
surface = cairo_xlib_surface_create(dpy,
590
DefaultVisual(dpy, 0),
594
pixbuf = gdk_pixbuf_get_from_surface(surface,
600
cairo_surface_destroy(surface);
603
pixmap = gdk_pixmap_foreign_new(a->pixmap);
604
/* use y + 1 because these buttons should be centered wrt the label
606
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
607
gdk_colormap_get_system(),
608
0, 0, x, y + 1, w, h);
611
x += theme->button_size + theme->paddingx + 1;
615
if (theme->handle_height) {
617
handle = focus ? theme->a_focused_handle : theme->a_unfocused_handle;
618
x = 2*theme->fbwidth + theme->grip_width;
619
y = height - theme->fbwidth - theme->handle_height;
620
w = width - 4*theme->fbwidth - 2*theme->grip_width;
621
h = theme->handle_height;
623
theme_pixmap_paint(handle, w, h);
624
#if GTK_CHECK_VERSION(3, 0, 0)
625
surface = cairo_xlib_surface_create(dpy,
627
DefaultVisual(dpy, 0),
631
pixbuf = gdk_pixbuf_get_from_surface(surface,
637
cairo_surface_destroy(surface);
640
pixmap = gdk_pixmap_foreign_new(handle->pixmap);
641
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
642
gdk_colormap_get_system(),
646
/* openbox handles this drawing stuff differently (it fills the bottom
647
* of the window with the handle), so it avoids this bug where
648
* parentrelative grips are not fully filled. i'm avoiding it slightly
651
theme_pixmap_paint(handle, width, h);
654
a = focus ? theme->a_focused_grip : theme->a_unfocused_grip;
655
a->surface.parent = handle;
658
/* same y and h as handle */
659
w = theme->grip_width;
661
theme_pixmap_paint(a, w, h);
662
#if GTK_CHECK_VERSION(3, 0, 0)
663
surface = cairo_xlib_surface_create(dpy,
665
DefaultVisual(dpy, 0),
669
pixbuf = gdk_pixbuf_get_from_surface(surface,
675
cairo_surface_destroy(surface);
678
pixmap = gdk_pixmap_foreign_new(a->pixmap);
679
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
680
gdk_colormap_get_system(),
685
x = width - theme->fbwidth - theme->grip_width;
686
#if GTK_CHECK_VERSION(3, 0, 0)
687
surface = cairo_xlib_surface_create(dpy,
689
DefaultVisual(dpy, 0),
693
pixbuf = gdk_pixbuf_get_from_surface(surface,
699
cairo_surface_destroy(surface);
702
pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap,
703
gdk_colormap_get_system(),
708
/* title separator colour */
710
y = theme->fbwidth + theme->title_height;
711
w = width - 2*theme->fbwidth;
714
scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
715
gdk_pixbuf_fill(scratch, rr_color_pixel(focus ?
716
theme->title_separator_focused_color :
717
theme->title_separator_unfocused_color));
719
gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
721
/* retarded way of adding client colour */
723
y = theme->title_height + 2*theme->fbwidth;
724
w = width - 2*theme->fbwidth;
725
h = height - theme->title_height - 3*theme->fbwidth -
726
(theme->handle_height ? (theme->fbwidth + theme->handle_height) : 0);
728
scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
729
gdk_pixbuf_fill(scratch, rr_color_pixel(focus ?
730
theme->cb_focused_color :
731
theme->cb_unfocused_color));
733
gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
735
/* clear (no alpha!) the area where the client resides */
736
gdk_pixbuf_fill(scratch, 0xffffffff);
737
gdk_pixbuf_copy_area(scratch, 0, 0,
738
w - 2*theme->cbwidthx,
739
h - 2*theme->cbwidthy,
742
y + theme->cbwidthy);
747
static gint theme_label_width(RrTheme *theme, gboolean active)
752
label = theme->a_focused_label;
753
label->texture[0].data.text.string = "Active";
755
label = theme->a_unfocused_label;
756
label->texture[0].data.text.string = "Inactive";
759
return RrMinWidth(label);
762
static gint theme_window_min_width(RrTheme *theme, const gchar *titlelayout)
764
gint numbuttons = strlen(titlelayout);
765
gint w = 2 * theme->fbwidth + (numbuttons + 3) * (theme->paddingx + 1);
767
if (g_strrstr(titlelayout, "L")) {
769
w += MAX(theme_label_width(theme, TRUE),
770
theme_label_width(theme, FALSE));
773
w += theme->button_size * numbuttons;
778
GdkPixbuf *preview_theme(const gchar *name, const gchar *titlelayout,
779
RrFont *active_window_font,
780
RrFont *inactive_window_font,
781
RrFont *menu_title_font,
782
RrFont *menu_item_font,
783
RrFont *osd_active_font,
784
RrFont *osd_inactive_font)
796
RrTheme *theme = RrThemeNew(rrinst, name, FALSE,
797
active_window_font, inactive_window_font,
798
menu_title_font, menu_item_font,
799
osd_active_font, osd_inactive_font);
803
menu = preview_menu(theme);
805
window_w = theme_window_min_width(theme, titlelayout);
807
menu_w = gdk_pixbuf_get_width(menu);
808
h = gdk_pixbuf_get_height(menu);
810
w = MAX(window_w, menu_w) + 20;
812
/* we don't want windows disappearing on us */
813
if (!window_w) window_w = menu_w;
815
preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
816
w, h + 2*(theme->title_height +5) + 1);
817
gdk_pixbuf_fill(preview, 0); /* clear */
819
window = preview_window(theme, titlelayout, FALSE, window_w, h);
820
gdk_pixbuf_copy_area(window, 0, 0, window_w, h, preview, 20, 0);
822
window = preview_window(theme, titlelayout, TRUE, window_w, h);
823
gdk_pixbuf_copy_area(window, 0, 0, window_w, h,
824
preview, 10, theme->title_height + 5);
826
gdk_pixbuf_copy_area(menu, 0, 0, menu_w, h,
827
preview, 0, 2 * (theme->title_height + 5));