add theme previews! yay syscrash!
authorDana Jansens <danakj@orodu.net>
Thu, 31 May 2007 04:11:29 +0000 (04:11 +0000)
committerDana Jansens <danakj@orodu.net>
Thu, 31 May 2007 04:11:29 +0000 (04:11 +0000)
Makefile.am
src/handlers.c
src/main.c
src/main.h
src/preview.c [new file with mode: 0644]
src/preview.h [new file with mode: 0644]

index 4b71903ccb3d75ade4f8943a58dfc0d269bfd398..420aaec42a8db571129ad55ff5a6ae16f0dd8f28 100644 (file)
@@ -42,6 +42,8 @@ src_obconf_SOURCES = \
        src/main.h \
        src/handlers.c \
        src/handlers.h \
+       src/preview.c \
+       src/preview.h \
        src/theme.c \
        src/theme.h \
        src/tree.c \
index b8db2445fd5745fee49fd06691d521df8af9baf6..8c6862d7d1fa8a59346dd77beb0d845a65aceb84 100644 (file)
@@ -35,6 +35,10 @@ static int num_desktops;
 static GList *desktop_names;
 static GtkListStore *theme_store;
 
+static RrFont *active_window_font, *inactive_window_font, *menu_title_font, *menu_item_font, *osd_font;
+
+static gchar *titlelayout;
+
 static void on_desktop_names_cell_edited(GtkCellRendererText *cell,
                                          const gchar *path_string,
                                          const gchar *new_text,
@@ -42,6 +46,7 @@ static void on_desktop_names_cell_edited(GtkCellRendererText *cell,
 
 static void on_theme_names_selection_changed(GtkTreeSelection *sel, 
                                              gpointer data);
+static void handlers_update_theme_previews();
 
 
 void setup_behavior_tab()
@@ -384,6 +389,8 @@ static void reset_theme_names(GtkWidget *w)
     GList *it, *next;
     gint i;
 
+    RrFont *active, *inactive, *menu_t, *menu_i, *osd;
+
     name = tree_get_string("theme/name", "TheBear");
 
     for (it = themes; it; it = g_list_next(it))
@@ -427,7 +434,7 @@ static void reset_theme_names(GtkWidget *w)
         gtk_list_store_append(theme_store, &iter);
         gtk_list_store_set(theme_store, &iter,
                            0, it->data,
-                           1, TRUE,
+                           1, NULL,
                            -1);
 
         if(!strcmp(name, it->data)) {
@@ -454,18 +461,27 @@ void setup_theme_names(GtkWidget *w)
     mapping = TRUE;
 
     /* widget setup */
-    theme_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+    theme_store = gtk_list_store_new(2, G_TYPE_STRING, GDK_TYPE_PIXBUF);
     gtk_tree_view_set_model(GTK_TREE_VIEW(w), GTK_TREE_MODEL(theme_store));
     g_object_unref (theme_store);
 
     gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(w)),
                                 GTK_SELECTION_SINGLE);
 
+    /* text column for the names */
     render = gtk_cell_renderer_text_new();
     column = gtk_tree_view_column_new_with_attributes
         ("Name", render, "text", 0, NULL);
     gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
 
+    /* pixbuf column, for theme previews */
+    render = gtk_cell_renderer_pixbuf_new();
+    g_object_set(render, "xalign", 1.0);
+    column = gtk_tree_view_column_new_with_attributes
+        ("Preview", render, "pixbuf", 1, NULL);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
+
+
     /* setup the selection handler */
     select = gtk_tree_view_get_selection(GTK_TREE_VIEW (w));
     gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
@@ -475,6 +491,8 @@ void setup_theme_names(GtkWidget *w)
 
     reset_theme_names(w);
 
+    handlers_update_theme_previews();
+
     mapping = FALSE;
 }
 
@@ -485,6 +503,7 @@ void setup_title_layout(GtkWidget *w)
     mapping = TRUE;
 
     layout = tree_get_string("theme/titleLayout", "NLIMC");
+    titlelayout = g_strdup(layout);
     gtk_entry_set_text(GTK_ENTRY(w), layout);
     g_free(layout);
 
@@ -513,14 +532,18 @@ void setup_window_border(GtkWidget *w)
     mapping = FALSE;
 }
 
-static void setup_font(GtkWidget *w, const gchar *place)
+static RrFont *setup_font(GtkWidget *w, const gchar *place)
 {
+    RrFont *font;
     gchar *fontstring, *node;
     gchar *name, **names;
     gchar *size;
     gchar *weight;
     gchar *slant;
 
+    RrFontWeight rr_weight = RR_FONTWEIGHT_NORMAL;
+    RrFontSlant rr_slant = RR_FONTSLANT_NORMAL;
+
     mapping = TRUE;
 
     node = g_strdup_printf("theme/font:place=%s/name", place);
@@ -555,6 +578,12 @@ static void setup_font(GtkWidget *w, const gchar *place)
 
     fontstring = g_strdup_printf("%s %s %s %s", name, weight, slant, size);
     gtk_font_button_set_font_name(GTK_FONT_BUTTON(w), fontstring);
+
+    if (!g_ascii_strcasecmp(weight, "Bold")) rr_weight = RR_FONTWEIGHT_BOLD;
+    if (!g_ascii_strcasecmp(slant, "Italic")) rr_slant = RR_FONTSLANT_ITALIC;
+    if (!g_ascii_strcasecmp(slant, "Oblique")) rr_slant = RR_FONTSLANT_OBLIQUE;
+
+    font = RrFontOpen(rrinst, name, atoi(size), rr_weight, rr_slant);
     g_free(fontstring);
     g_free(slant);
     g_free(weight);
@@ -562,31 +591,33 @@ static void setup_font(GtkWidget *w, const gchar *place)
     g_free(name);
 
     mapping = FALSE;
+
+    return font;
 }
 
 void setup_font_active(GtkWidget *w)
 {
-    setup_font(w, "ActiveWindow");
+    active_window_font = setup_font(w, "ActiveWindow");
 }
 
 void setup_font_inactive(GtkWidget *w)
 {
-    setup_font(w, "InactiveWindow");
+    inactive_window_font = setup_font(w, "InactiveWindow");
 }
 
 void setup_font_menu_header(GtkWidget *w)
 {
-    setup_font(w, "MenuHeader");
+    menu_title_font = setup_font(w, "MenuHeader");
 }
 
 void setup_font_menu_item(GtkWidget *w)
 {
-    setup_font(w, "MenuItem");
+    menu_item_font = setup_font(w, "MenuItem");
 }
 
 void setup_font_display(GtkWidget *w)
 {
-    setup_font(w, "OnScreenDisplay");
+    osd_font = setup_font(w, "OnScreenDisplay");
 }
 
 
@@ -680,7 +711,7 @@ void on_window_border_toggled(GtkToggleButton *w, gpointer data)
     tree_set_bool("theme/keepBorder", b);
 }
 
-static void on_font_set(GtkFontButton *w, const gchar *place)
+static RrFont *on_font_set(GtkFontButton *w, const gchar *place)
 {
     gchar *c;
     gchar *font, *node;
@@ -688,6 +719,9 @@ static void on_font_set(GtkFontButton *w, const gchar *place)
     const gchar *bold = NULL;
     const gchar *italic = NULL;
 
+    RrFontWeight weight = RR_FONTWEIGHT_NORMAL;
+    RrFontSlant slant = RR_FONTSLANT_NORMAL;
+
     if (mapping) return;
 
     font = g_strdup(gtk_font_button_get_font_name(w));
@@ -721,32 +755,49 @@ static void on_font_set(GtkFontButton *w, const gchar *place)
     tree_set_string(node, italic);
     g_free(node);
 
+    if (!g_ascii_strcasecmp(bold, "Bold")) weight = RR_FONTWEIGHT_BOLD;
+    if (!g_ascii_strcasecmp(italic, "Italic")) slant = RR_FONTSLANT_ITALIC;
+    if (!g_ascii_strcasecmp(italic, "Oblique")) slant = RR_FONTSLANT_OBLIQUE;
+
+    return RrFontOpen(rrinst, font, atoi(size), weight, slant);
+
     g_free(font);
+
 }
 
 void on_font_active_font_set(GtkFontButton *w, gpointer data)
 {
-    on_font_set(w, "ActiveWindow");
+    RrFontClose(active_window_font);
+    active_window_font = on_font_set(w, "ActiveWindow");
+    handlers_update_theme_previews();
 }
 
 void on_font_inactive_font_set(GtkFontButton *w, gpointer data)
 {
-    on_font_set(w, "InactiveWindow");
+    RrFontClose(inactive_window_font);
+    inactive_window_font = on_font_set(w, "InactiveWindow");
+    handlers_update_theme_previews();
 }
 
 void on_font_menu_header_font_set(GtkFontButton *w, gpointer data)
 {
-    on_font_set(w, "MenuHeader");
+    RrFontClose(menu_title_font);
+    menu_title_font = on_font_set(w, "MenuHeader");
+    handlers_update_theme_previews();
 }
 
 void on_font_menu_item_font_set(GtkFontButton *w, gpointer data)
 {
-    on_font_set(w, "MenuItem");
+    RrFontClose(menu_item_font);
+    menu_item_font = on_font_set(w, "MenuItem");
+    handlers_update_theme_previews();
 }
 
 void on_font_display_font_set(GtkFontButton *w, gpointer data)
 {
-    on_font_set(w, "OnScreenDisplay");
+    RrFontClose(osd_font);
+    osd_font = on_font_set(w, "OnScreenDisplay");
+    handlers_update_theme_previews();
 }
 
 void on_focus_mouse_toggled(GtkToggleButton *w, gpointer data)
@@ -1170,7 +1221,12 @@ void on_title_layout_changed(GtkEntry *w, gpointer data)
 
     gtk_entry_set_text(w, layout);
     tree_set_string("theme/titleLayout", layout);
+
+    g_free(titlelayout);
+    titlelayout = g_strdup(layout);
     g_free(layout);
+
+    
 }
 
 static void set_desktop_names()
@@ -1356,3 +1412,38 @@ void handlers_install_theme(gchar *path)
         g_free(name);
     }
 }
+
+
+static gboolean update_theme_preview_iterate(gpointer data)
+{
+    GtkListStore *ls = data;
+    static GtkTreeIter iter;
+    static gboolean restart = TRUE;
+    gchar *name;
+
+    if (restart) {
+        if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ls), &iter))
+            return;
+        restart = FALSE;
+    } else {
+        if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(ls), &iter)) {
+            restart = TRUE;
+            return;
+        }
+    }
+
+    gtk_tree_model_get(GTK_TREE_MODEL(ls), &iter, 0, &name, -1);
+
+    gtk_list_store_set(GTK_LIST_STORE(ls), &iter, 1,
+                       preview_theme(name, titlelayout, active_window_font,
+                                     inactive_window_font, menu_title_font,
+                                     menu_item_font, osd_font),
+                       -1);
+}
+
+static void handlers_update_theme_previews()
+{
+    g_idle_remove_by_data(theme_store);
+    g_idle_add_full(G_PRIORITY_LOW, update_theme_preview_iterate,
+                    theme_store, NULL);
+}
index 7afa6155a4d1ded3252a3a0506e7d7bbded5ba3e..d0ca923946cff14d8b503c27e2ca2155682a71cf 100644 (file)
@@ -33,6 +33,7 @@ GtkWidget *mainwin = NULL;
 GladeXML *glade;
 xmlDocPtr doc;
 xmlNodePtr root;
+RrInstance *rrinst;
 
 static gchar *obc_theme_install = NULL;
 static gchar *obc_theme_archive = NULL;
@@ -118,8 +119,6 @@ int main(int argc, char **argv)
         return;
     }
 
-    parse_paths_startup();
-
     p = g_build_filename(GLADEDIR, "obconf.glade", NULL);
     glade = glade_xml_new(p, NULL, NULL);
     g_free(p);
@@ -130,6 +129,9 @@ int main(int argc, char **argv)
         return 1;
     }
 
+    parse_paths_startup();
+    rrinst = RrInstanceNew(GDK_DISPLAY(), gdk_x11_get_default_screen());
+
     xmlIndentTreeOutput = 1;
     if (!parse_load_rc(NULL, &doc, &root)) {
         obconf_error("Failed to load an rc.xml. You have probably failed to "
@@ -206,6 +208,7 @@ int main(int argc, char **argv)
 
     gtk_main();
 
+    RrInstanceFree(rrinst);
     parse_paths_shutdown();
 
     xmlFreeDoc(doc);
index 7d1b11999a1d390a91d2d2d23d53553e81b0d608..b560e55399ff8919c90929393bbcbf008af9dd4f 100644 (file)
@@ -19,7 +19,9 @@
 #ifndef obconf__main_h
 #define obconf__main_h
 
-#include "openbox/parse.h"
+#include <openbox/render.h>
+#include <openbox/instance.h>
+#include <openbox/parse.h>
 
 #include <gtk/gtk.h>
 #include <glade/glade-xml.h>
@@ -27,6 +29,7 @@
 extern GladeXML *glade;
 extern xmlDocPtr doc;
 extern xmlNodePtr root;
+extern RrInstance *rrinst;
 extern GtkWidget *mainwin;
 
 void obconf_error(gchar *msg);
diff --git a/src/preview.c b/src/preview.c
new file mode 100644 (file)
index 0000000..8b2a47a
--- /dev/null
@@ -0,0 +1,436 @@
+#include "theme.h"
+#include "main.h"
+#include "tree.h"
+
+#include <string.h>
+
+#include <openbox/theme.h>
+
+#define PADDING 2 /* openbox does it :/ */
+
+static void theme_pixmap_paint(RrAppearance *a, gint w, gint h)
+{
+    Pixmap out = RrPaintPixmap(a, w, h);
+    if (out) XFreePixmap(RrDisplay(a->inst), out);
+}
+
+static guint32 rr_color_pixel(const RrColor *c)
+{
+    return (guint32)((RrColorRed(c) << 24) | (RrColorGreen(c) << 16)
+                     | (RrColorBlue(c) << 8) | 255);
+}
+
+/* XXX: Make this more general */
+static GdkPixbuf* preview_menu(RrTheme *theme)
+{
+    RrAppearance *title;
+    RrAppearance *title_text;
+
+    RrAppearance *menu;
+    RrAppearance *background;
+
+    RrAppearance *normal;
+    RrAppearance *disabled;
+    RrAppearance *selected;
+    RrAppearance *bullet; /* for submenu */
+
+    GdkPixmap *pixmap;
+    GdkPixbuf *pixbuf;
+
+    /* width and height of the whole menu */
+    gint width, height;
+    gint x, y;
+    gint title_h;
+    gint tw, th;
+    gint bw, bh;
+    gint unused;
+
+    /* set up appearances */
+    title = theme->a_menu_title;
+
+    title_text = theme->a_menu_text_title;
+    title_text->surface.parent = title;
+    title_text->texture[0].data.text.string = "menu";
+
+    normal = theme->a_menu_text_normal;
+    normal->texture[0].data.text.string = "normal";
+
+    disabled = theme->a_menu_text_disabled;
+    disabled->texture[0].data.text.string = "disabled";
+
+    selected = theme->a_menu_text_selected;
+    selected->texture[0].data.text.string = "selected";
+
+    bullet = theme->a_menu_bullet_normal;
+
+    /* determine window size */
+    RrMinSize(normal, &width, &th);
+    width += th + PADDING; /* make space for the bullet */
+    //height = th;
+
+    width += 2*theme->mbwidth + 2*PADDING;
+
+    /* get minimum title size */
+    RrMinSize(title, &tw, &title_h);
+
+    /* size of background behind each text line */
+    bw = width - 2*theme->mbwidth;
+    //title_h += 2*PADDING;
+    title_h = theme->menu_title_height;
+
+    RrMinSize(normal, &unused, &th);
+    bh = th + 2*PADDING;
+
+    height = title_h + 3*bh + 3*theme->mbwidth;
+
+    //height += 3*th + 3*theme->mbwidth + 5*PADDING;
+
+    /* set border */
+    pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
+    gdk_pixbuf_fill(pixbuf, rr_color_pixel(theme->menu_border_color));
+
+    /* draw title */
+    x = y = theme->mbwidth;
+    theme_pixmap_paint(title, bw, title_h);
+
+    /* draw title text */
+    title_text->surface.parentx = 0;
+    title_text->surface.parenty = 0;
+
+    theme_pixmap_paint(title_text, bw, title_h);
+
+    pixmap = gdk_pixmap_foreign_new(title_text->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, bw, title_h);
+
+    /* menu appears after title */
+    y += theme->mbwidth + title_h;
+
+    /* fill in menu appearance, used as the parent to every menu item's bg */
+    menu = theme->a_menu;
+    th = height - 3*theme->mbwidth - title_h;
+    theme_pixmap_paint(menu, bw, th);
+
+    pixmap = gdk_pixmap_foreign_new(menu->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, bw, th);
+
+    /* fill in background appearance, used as the parent to text items */
+    background = theme->a_menu_normal;
+    background->surface.parent = menu;
+    background->surface.parentx = 0;
+    background->surface.parenty = 0;
+
+    /* draw background for normal entry */
+    theme_pixmap_paint(background, bw, bh);
+    pixmap = gdk_pixmap_foreign_new(background->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, bw, bh);
+
+    /* draw normal entry */
+    normal->surface.parent = background;
+    normal->surface.parentx = PADDING;
+    normal->surface.parenty = PADDING;
+    x += PADDING;
+    y += PADDING;
+    RrMinSize(normal, &tw, &th);
+    theme_pixmap_paint(normal, tw, th);
+    pixmap = gdk_pixmap_foreign_new(normal->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, tw, th);
+
+    /* draw bullet */
+    RrMinSize(normal, &tw, &th);
+    bullet->surface.parent = background;
+    bullet->surface.parentx = bw - th;
+    bullet->surface.parenty = PADDING;
+    theme_pixmap_paint(bullet, th, th);
+    pixmap = gdk_pixmap_foreign_new(bullet->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, width - theme->mbwidth - th, y, th, th);
+
+    y += th + 2*PADDING;
+
+    /* draw background for disabled entry */
+    background->surface.parenty = bh;
+    theme_pixmap_paint(background, bw, bh);
+    pixmap = gdk_pixmap_foreign_new(background->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x - PADDING, y - PADDING, bw, bh);
+
+    /* draw disabled entry */
+    RrMinSize(disabled, &tw, &th);
+    disabled->surface.parent = background;
+    disabled->surface.parentx = PADDING;
+    disabled->surface.parenty = PADDING;
+    theme_pixmap_paint(disabled, tw, th);
+    pixmap = gdk_pixmap_foreign_new(disabled->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, tw, th);
+
+    y += th + 2*PADDING;
+
+    /* draw background for selected entry */
+    background = theme->a_menu_selected;
+    background->surface.parent = menu;
+    background->surface.parentx = 2*bh;
+
+    theme_pixmap_paint(background, bw, bh);
+    pixmap = gdk_pixmap_foreign_new(background->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x - PADDING, y - PADDING, bw, bh);
+
+    /* draw selected entry */
+    RrMinSize(selected, &tw, &th);
+    selected->surface.parent = background;
+    selected->surface.parentx = PADDING;
+    selected->surface.parenty = PADDING;
+    theme_pixmap_paint(selected, tw, th);
+    pixmap = gdk_pixmap_foreign_new(selected->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, tw, th);
+
+    return pixbuf;
+}
+
+static GdkPixbuf* preview_window(RrTheme *theme, const gchar *titlelayout, gboolean focus, gint width, gint height)
+{
+    RrAppearance *title;
+    RrAppearance *handle;
+    RrAppearance *a;
+
+    GdkPixmap *pixmap;
+    GdkPixbuf *pixbuf = NULL;
+    GdkPixbuf *scratch;
+
+    gint w, label_w, h, x, y;
+
+    const gchar *layout;
+
+    title = focus ? theme->a_focused_title : theme->a_unfocused_title;
+
+    /* set border */
+    pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
+    gdk_pixbuf_fill(pixbuf, rr_color_pixel(theme->menu_border_color));
+
+    /* title */
+    w = width - 2*theme->fbwidth;
+    h = theme->title_height;
+    theme_pixmap_paint(title, w, h);
+
+    x = y = theme->fbwidth;;
+    pixmap = gdk_pixmap_foreign_new(title->pixmap);
+    pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
+
+    /* calculate label width */
+    label_w = width - (theme->paddingx + theme->fbwidth + 1) * 2;
+
+    for (layout = titlelayout; *layout; layout++) {
+        switch (*layout) {
+        case 'N':
+            label_w -= theme->button_size + 2 + theme->paddingx + 1;
+            break;
+        case 'D':
+        case 'S':
+        case 'I':
+        case 'M':
+        case 'C':
+            label_w -= theme->button_size + theme->paddingx + 1;
+            break;
+        default:
+            break;
+        }
+    }
+
+    x = theme->paddingx + theme->fbwidth + 1;
+    y += theme->paddingy + 1;
+    for (layout = titlelayout; *layout; layout++) {
+        /* icon */
+        if (*layout == 'N') {
+            a = theme->a_icon;
+            /* set default icon */
+            a->texture[0].type = RR_TEXTURE_RGBA;
+            a->texture[0].data.rgba.width = 48;
+            a->texture[0].data.rgba.height = 48;
+            a->texture[0].data.rgba.data = theme->def_win_icon;
+
+            a->surface.parent = title;
+            a->surface.parentx = x;
+            a->surface.parenty = theme->paddingy;
+
+            w = h = theme->button_size + 2;
+
+            theme_pixmap_paint(a, w, h);
+            pixmap = gdk_pixmap_foreign_new(a->pixmap);
+            pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y - 1, w, h);
+
+            x += theme->button_size + 2 + theme->paddingx + 1;
+        } else if (*layout == 'L') { /* label */
+            a = focus ? theme->a_focused_label : theme->a_unfocused_label;
+            a->texture[0].data.text.string = focus ? "active" : "inactive";
+
+            a->surface.parent = title;
+            a->surface.parentx = x;
+            a->surface.parenty = theme->paddingy;
+            w = label_w;
+            h = theme->label_height;
+
+            theme_pixmap_paint(a, w, h);
+            pixmap = gdk_pixmap_foreign_new(a->pixmap);
+            pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y - 1, w, h);
+
+            x += w + theme->paddingx + 1;
+        } else {
+            /* buttons */
+            switch (*layout) {
+            case 'D':
+                a = focus ? theme->a_focused_unpressed_desk : theme->a_unfocused_unpressed_desk;
+                break;
+            case 'S':
+                a = focus ? theme->a_focused_unpressed_shade : theme->a_unfocused_unpressed_shade;
+                break;
+            case 'I':
+                a = focus ? theme->a_focused_unpressed_iconify : theme->a_unfocused_unpressed_iconify;
+                break;
+            case 'M':
+                a = focus ? theme->a_focused_unpressed_max : theme->a_unfocused_unpressed_max;
+                break;
+            case 'C':
+                a = focus ? theme->a_focused_unpressed_close : theme->a_unfocused_unpressed_close;
+                break;
+            default:
+                continue;
+            }
+
+            a->surface.parent = title;
+            a->surface.parentx = x;
+            a->surface.parenty = theme->paddingy + 1;
+
+            w = theme->button_size;
+            h = theme->button_size;
+
+            theme_pixmap_paint(a, w, h);
+            pixmap = gdk_pixmap_foreign_new(a->pixmap);
+            pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
+
+            x += theme->button_size + theme->paddingx + 1;
+        }
+    }
+
+    if (theme->handle_height) {
+        /* handle */
+        handle = focus ? theme->a_focused_handle : theme->a_unfocused_handle;
+        x = 2*theme->fbwidth + theme->grip_width;
+        y = height - theme->fbwidth - theme->handle_height;
+        w = width - 4*theme->fbwidth - 2*theme->grip_width;
+        h = theme->handle_height;
+
+        theme_pixmap_paint(handle, w, h);
+        pixmap = gdk_pixmap_foreign_new(handle->pixmap);
+        pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
+
+        /* openbox handles this drawing stuff differently (it fills the bottom
+         * of the window with the handle), so it avoids this bug where
+         * parentrelative grips are not fully filled. i'm avoiding it slightly
+         * differently. */
+
+        theme_pixmap_paint(handle, width, h);
+
+        /* grips */
+        a = focus ? theme->a_focused_grip : theme->a_unfocused_grip;
+        a->surface.parent = handle;
+
+        x = theme->fbwidth;
+        /* same y and h as handle */
+        w = theme->grip_width;
+
+        theme_pixmap_paint(a, w, h);
+        pixmap = gdk_pixmap_foreign_new(a->pixmap);
+        pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
+
+        /* right grip */
+        x = width - theme->fbwidth - theme->grip_width;
+        pixbuf = gdk_pixbuf_get_from_drawable(pixbuf, pixmap, gdk_colormap_get_system(), 0, 0, x, y, w, h);
+    }
+
+    /* retarded way of adding client colour */
+    x = theme->fbwidth;
+    y = theme->title_height + 2*theme->fbwidth;
+    w = width - 2*theme->fbwidth;
+    h = height - theme->title_height - 3*theme->fbwidth - (theme->handle_height ? (theme->fbwidth + theme->handle_height) : 0);
+
+    scratch = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, w, h);
+    gdk_pixbuf_fill(scratch, rr_color_pixel((focus ? theme->cb_focused_color : theme->cb_unfocused_color)));
+
+    gdk_pixbuf_copy_area(scratch, 0, 0, w, h, pixbuf, x, y);
+
+    return pixbuf;
+}
+
+static gint theme_label_width(RrTheme *theme, gboolean active)
+{
+    gint w, h;
+    RrAppearance *label;
+
+    if (active) {
+        label = theme->a_focused_label;
+        label->texture[0].data.text.string = "active";
+    } else {
+        label = theme->a_unfocused_label;
+        label->texture[0].data.text.string = "inactive";
+    }
+
+    return RrMinWidth(label);
+}
+
+static gint theme_window_min_width(RrTheme *theme, const gchar *titlelayout)
+{
+    gint numbuttons = strlen(titlelayout);
+    gint w =  2 * (theme->fbwidth + theme->paddingx + 1);
+
+    if (g_strrstr(titlelayout, "L")) {
+        numbuttons--;
+        w += MAX(theme_label_width(theme, TRUE), theme_label_width(theme, FALSE)) + theme->paddingx + 1;
+    }
+
+    w += (theme->button_size + theme->paddingx + 1) * numbuttons;
+
+    return w;
+}
+
+GdkPixbuf *preview_theme(const gchar *name, const gchar *titlelayout,
+                         RrFont *active_window_font,
+                         RrFont *inactive_window_font,
+                         RrFont *menu_title_font,
+                         RrFont *menu_item_font,
+                         RrFont *osd_font)
+{
+
+    GdkPixbuf *preview;
+    GdkPixbuf *menu;
+    GdkPixbuf *window;
+
+    gint window_w;
+    gint menu_w;
+
+    gint w, h;
+
+    RrTheme *theme = RrThemeNew(rrinst, name,
+                                active_window_font, inactive_window_font,
+                                menu_title_font, menu_item_font, osd_font);
+
+    menu = preview_menu(theme);
+  
+    window_w = theme_window_min_width(theme, titlelayout);
+
+    menu_w = gdk_pixbuf_get_width(menu);
+    h = gdk_pixbuf_get_height(menu);
+
+    w = MAX(window_w, menu_w) + 20;
+  
+    preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h + 2*(theme->title_height +5) + 1);
+    gdk_pixbuf_fill(preview, 0); /* clear */
+
+    window = preview_window(theme, titlelayout, FALSE, window_w, h);
+    gdk_pixbuf_copy_area(window, 0, 0, window_w, h, preview, 20, 0);
+
+    window = preview_window(theme, titlelayout, TRUE, window_w, h);
+    gdk_pixbuf_copy_area(window, 0, 0, window_w, h, preview, 10, theme->title_height + 5);
+
+    gdk_pixbuf_copy_area(menu, 0, 0, menu_w, h, preview, 0, 2 * (theme->title_height + 5));
+
+    RrThemeFree(theme);
+
+    return preview;
+}
diff --git a/src/preview.h b/src/preview.h
new file mode 100644 (file)
index 0000000..defa183
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef obconf__preview_h
+#define obconf__preview_h
+
+#include <glib.h>
+
+#include <openbox/font.h>
+#include <gdk/gdkpixbuf.h>
+
+GdkPixbuf *preview_theme(gchar *name, gchar *titlelayout,
+                         RrFont *active_window_font,
+                         RrFont *inactive_window_font,
+                         RrFont *menu_title_font,
+                         RrFont *menu_item_font,
+                         RrFont *osd_font);
+
+#endif