#include #include #include #include #include /* ================================================================ */ /* Our little hierarchy */ typedef enum _CopperClasses { CC_FRAME, CC_CONTENT, CC_TITLEBAR, CC_MENU, CC_TITLE, CC_MINIMIZE, CC_MAXIMIZE, CC_CLOSE, CC_FILLER, CC_LAST } CopperClasses; char *names[] = { "frame", "area", "area", "button", "title", "button", "button", "button", "area", "last" }; CopperClasses parents[] = { CC_LAST, CC_FRAME, CC_FRAME, CC_TITLEBAR, CC_TITLEBAR, CC_TITLEBAR, CC_TITLEBAR, CC_TITLEBAR, CC_TITLEBAR, CC_LAST }; char *copper_classnames[] = { NULL, "content", "titlebar", "menu", NULL, "minimize", "maximize", "close", "filler", NULL }; typedef struct { ccss_node_t basic; CopperClasses copper_class; } CopperNode; CopperNode copper_nodes[CC_LAST]; static char const* get_type (ccss_node_t const *self) { g_warning ("It's %s", self->type_name); return self->type_name; } static ptrdiff_t get_instance (ccss_node_t const *self) { return self->instance; } static char const* get_style (ccss_node_t const *self) { return self->inline_style; } static ccss_node_t* get_container (ccss_node_t const *self) { CopperClasses candidate = parents[((CopperNode*)self)->copper_class]; if (candidate==CC_LAST) return NULL; else /* or should we allocate a new one? */ return (ccss_node_t*) &(copper_nodes[candidate]); } static const char* get_class (ccss_node_t const *self) { return copper_classnames[((CopperNode*)self)->copper_class]; } static ccss_node_class_t copper_node_class = { .get_type = (ccss_node_get_type_f) get_type, .get_instance = (ccss_node_get_instance_f) get_instance, .get_style = (ccss_node_get_style_f) get_style, .get_container = (ccss_node_get_container_f) get_container, .get_class = (ccss_node_get_class_f) get_class }; static void initialise_classes (void) { int i; for (i=0; imax_w && max_w>min_w) w = max_w; if (wdata, NULL); filename = (char*) args->data; if (strcmp (filename, "wm:icon")==0) { /* this is magic */ return g_strdup ("file:///service/website/themecreator.marnanel.org/htdocs/gtk-edit.png"); } #if 0 else if (strncmp (filename, "data:", 5)==0) { return handle_data_url (filename+5); } #endif else if (index (filename, '/') == NULL) { char *md5, *result; md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, filename, -1); result = g_strdup_printf ("file:///service/website/themecreator.marnanel.org/htdocs/w/images/%c/%c%c/%s", md5[0], md5[0], md5[1], filename); g_free (md5); return result; } else { return g_strdup ("file:///service/website/themecreator.marnanel.org/htdocs/gtk-error.png"); } } static char * named (GSList const *args, void *user_data) { char *name = NULL; if (!name) /* fall back to black */ return g_strdup("#00000000"); name = args->data; g_warning ("Looking up named colour %s", name); /* stub: magenta for everything */ return g_strdup("#FF00FFFF"); } static ccss_function_t const _functions[] = { { "url", url, NULL }, { "named", named, NULL }, { NULL } }; static PangoLayout* title_text (ccss_stylesheet_t *stylesheet, cairo_t *cr, char *text) { PangoLayout *layout; PangoAttrList *attrs = NULL; ccss_style_t *style = ccss_stylesheet_query (stylesheet, (ccss_node_t*) &copper_nodes[CC_TITLE]); char *align; ccss_color_t const *colour; layout = pango_cairo_create_layout (cr); attrs = pango_attr_list_new (); /* We have to handle CSS text properties ourselves here because * libccss doesn't know how to render them yet. We don't try * to do all the funky effects like shadows and so on, * unfortunately. */ if (ccss_style_get_property (style, "color", (const ccss_property_base_t**) &colour)) { pango_attr_list_insert (attrs, pango_attr_foreground_new ((int)65535.0*colour->red, (int)65535.0*colour->green, (int)65535.0*colour->blue)); } /* Alignment */ if (ccss_style_get_string (style, "text-align", &align)) { if (strcmp (align, "left")==0) { pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT); } else if (strcmp (align, "center")==0) { pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); } else if (strcmp (align, "right")==0) { pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT); } else /* FIXME: "align" contains garbage if it's undefined; * is there an easy way of checking? */ g_warning ("Unknown alignment: %s", align); } pango_layout_set_attributes (layout, attrs); /* The actual text */ pango_layout_set_text (layout, text, -1); ccss_style_destroy (style); return layout; } static void render_copper (cairo_surface_t *surface, ccss_stylesheet_t *stylesheet) { cairo_t *cr = cairo_create (surface); int x = 0; int y = 0; int w = cairo_image_surface_get_width (surface); int h = cairo_image_surface_get_height (surface); PangoRectangle text_extents; int titlebar_height; CopperClasses left_buttons[] = {CC_MENU, CC_LAST}; CopperClasses right_buttons[] = {CC_CLOSE, CC_MAXIMIZE, CC_MINIMIZE, CC_LAST}; CopperClasses *cursor; int leftpos, rightpos; PangoLayout *layout = title_text (stylesheet, cr, "Badgers"); pango_layout_get_pixel_extents (layout, NULL, &text_extents); reduce_by_padding_borders_and_margins (stylesheet, CC_TITLE, &text_extents.x, &text_extents.y, &text_extents.width, &text_extents.height, TRUE, TRUE); titlebar_height = text_extents.height; reduce_by_padding_borders_and_margins (stylesheet, CC_TITLEBAR, NULL, NULL, NULL, &titlebar_height, TRUE, TRUE); draw_rectangle (stylesheet, cr, CC_FRAME, x, y, w, h, FALSE, FALSE, NULL); reduce_by_padding_borders_and_margins (stylesheet, CC_FRAME, &x, &y, &w, &h, FALSE, FALSE); draw_rectangle (stylesheet, cr, CC_TITLEBAR, x, y, w, titlebar_height, TRUE, FALSE, NULL); draw_rectangle (stylesheet, cr, CC_CONTENT, x, y+titlebar_height, w, h-titlebar_height, TRUE, FALSE, NULL); h = titlebar_height; reduce_by_padding_borders_and_margins (stylesheet, CC_TITLEBAR, &x, &y, &w, &h, TRUE, FALSE); cursor = left_buttons; leftpos = x; while (*cursor != CC_LAST) { leftpos += draw_rectangle (stylesheet, cr, *cursor, leftpos, y, 0, h, TRUE, FALSE, NULL); cursor++; } cursor = right_buttons; rightpos = x+w; while (*cursor != CC_LAST) { rightpos -= draw_rectangle (stylesheet, cr, *cursor, rightpos, y, 0, h, TRUE, TRUE, NULL); cursor++; } switch (pango_layout_get_alignment (layout)) { case PANGO_ALIGN_LEFT: x = leftpos; draw_rectangle (stylesheet, cr, CC_FILLER, leftpos+text_extents.width, y, (rightpos-leftpos)-text_extents.width, h, TRUE, FALSE, NULL); break; case PANGO_ALIGN_CENTER: x = leftpos + ((rightpos-leftpos)/2 - text_extents.width); draw_rectangle (stylesheet, cr, CC_FILLER, leftpos, y, x-leftpos, h, TRUE, FALSE, NULL); draw_rectangle (stylesheet, cr, CC_FILLER, x+text_extents.width, y, rightpos-(x+text_extents.width), h, TRUE, FALSE, NULL); break; case PANGO_ALIGN_RIGHT: x = rightpos - text_extents.width; draw_rectangle (stylesheet, cr, CC_FILLER, leftpos, y, rightpos-(leftpos-text_extents.width), h, TRUE, FALSE, NULL); break; default: g_error ("Unknown alignment"); } draw_rectangle (stylesheet, cr, CC_TITLE, x, y, text_extents.width, h, TRUE, FALSE, layout); g_object_unref (G_OBJECT (layout)); cairo_destroy (cr); } int main (int argc, char **argv) { ccss_grammar_t *grammar; ccss_stylesheet_t *stylesheet; ccss_style_t *style; cairo_surface_t *surface; int image_width = 800; int image_height = 250; initialise_classes (); surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, image_width, image_height); grammar = ccss_cairo_grammar_create (); ccss_grammar_add_functions (grammar, _functions); stylesheet = ccss_grammar_create_stylesheet_from_file (grammar, argv[1], NULL); render_copper (surface, stylesheet); ccss_stylesheet_destroy (stylesheet); ccss_grammar_destroy (grammar); cairo_surface_write_to_png (surface, argv[2]); cairo_surface_destroy (surface); return EXIT_SUCCESS; }