#include #include #include #include #include "theme.h" #include "lexer/theme-parser.h" #include "helper.h" #include "settings.h" #include "widgets/textbox.h" void yyerror ( YYLTYPE *ylloc, const char *); ThemeWidget *rofi_theme_find_or_create_class ( ThemeWidget *base, const char *class ) { for ( unsigned int i = 0; i < base->num_widgets;i++){ if ( g_strcmp0(base->widgets[i]->name, class) == 0 ){ return base->widgets[i]; } } base->widgets = g_realloc ( base->widgets, sizeof(ThemeWidget*)*(base->num_widgets+1)); base->widgets[base->num_widgets] = g_malloc0(sizeof(ThemeWidget)); ThemeWidget *retv = base->widgets[base->num_widgets]; retv->parent = base; retv->name = g_strdup(class); base->num_widgets++; return retv; } /** * Properties */ Property *rofi_theme_property_create ( PropertyType type ) { Property *retv = g_malloc0 ( sizeof(Property) ); retv->type = type; return retv; } void rofi_theme_property_free ( Property *p ) { if ( p == NULL ) { return; } g_free ( p->name ); if ( p->type == P_STRING ) { g_free ( p->value.s ); } g_free(p); } void rofi_theme_free ( ThemeWidget *widget ) { if ( widget == NULL ){ return; } if ( widget->properties ) { g_hash_table_destroy ( widget->properties ); } for ( unsigned int i = 0; i < widget->num_widgets; i++ ){ rofi_theme_free ( widget->widgets[i] ); } g_free ( widget->widgets ); g_free ( widget->name ); g_free ( widget ); } /** * print */ static void rofi_theme_print_property_index ( int depth, Property *p ) { printf("%*s%s: ", depth, "", p->name ); switch ( p->type ) { case P_STRING: printf("\"%s\";", p->value.s); break; case P_INTEGER: printf("%d;", p->value.i); break; case P_DOUBLE: printf("%.2f;", p->value.f); break; case P_BOOLEAN: printf("%s;", p->value.b?"true":"false"); break; case P_COLOR: printf("#%02X%02X%02X%02X;", (unsigned char)(p->value.color.alpha*255.0), (unsigned char)(p->value.color.red*255.0), (unsigned char)(p->value.color.green*255.0), (unsigned char)(p->value.color.blue*255.0)); break; case P_PADDING: if ( p->value.padding.left.type == PW_PX ) { printf("%upx ", (int)p->value.padding.left.distance ); } else { printf("%fem ", p->value.padding.left.distance ); } if ( p->value.padding.right.type == PW_PX ) { printf("%upx ", (int)p->value.padding.right.distance ); } else { printf("%fem ", p->value.padding.right.distance ); } if ( p->value.padding.top.type == PW_PX ) { printf("%upx ", (int)p->value.padding.top.distance ); } else { printf("%fem ", p->value.padding.top.distance ); } if ( p->value.padding.bottom.type == PW_PX ) { printf("%upx ", (int)p->value.padding.bottom.distance ); } else { printf("%fem ", p->value.padding.bottom.distance ); } printf(";\n"); } putchar ( '\n' ); } static void rofi_theme_print_index ( ThemeWidget *widget ) { GHashTableIter iter; gpointer key, value; if ( widget->properties ){ int index = 0; GList *list = NULL; ThemeWidget *w = widget; while ( w){ if ( g_strcmp0(w->name,"Root") == 0 ) { break; } list = g_list_prepend ( list, w->name ); w = w->parent; } if ( g_list_length ( list ) > 0 ) { index = 4; for ( GList *iter = g_list_first ( list ); iter != NULL; iter = g_list_next ( iter ) ) { char *name = (char *)iter->data; if ( iter->prev == NULL && name[0] != '@' ){ putchar ( '#' ); } fputs(name, stdout); if ( name[0] == '@' ) { putchar(' '); } else { if ( iter->next ) { putchar('.'); } } } printf(" {\n"); } g_hash_table_iter_init (&iter, widget->properties); while (g_hash_table_iter_next (&iter, &key, &value)) { Property *p = (Property*)value; rofi_theme_print_property_index ( index, p ); } if ( g_list_length ( list ) > 0 ) { printf("}\n"); } g_list_free ( list ); } for ( unsigned int i = 0; i < widget->num_widgets;i++){ rofi_theme_print_index ( widget->widgets[i] ); } } void rofi_theme_print ( ThemeWidget *widget ) { rofi_theme_print_index ( widget ); } /** * Main lex parser. */ int yyparse(); /** * Destroy the internal of lex parser. */ void yylex_destroy( void ); /** * Global handle input file to flex parser. */ extern FILE* yyin; /** * @param yylloc The file location. * @param s Error message string. * * Error handler for the lex parser. */ void yyerror(YYLTYPE *yylloc, const char* s) { fprintf(stderr, "Parse error: %s\n", s); fprintf(stderr, "From line %d column %d to line %d column %d\n", yylloc->first_line, yylloc->first_column, yylloc->last_line, yylloc->last_column); exit(EXIT_FAILURE); } static gboolean rofi_theme_steal_property_int ( gpointer key, gpointer value, gpointer user_data) { GHashTable *table = (GHashTable*)user_data; g_hash_table_replace ( table, key, value); return TRUE; } void rofi_theme_widget_add_properties ( ThemeWidget *widget, GHashTable *table ) { if ( table == NULL ) { return; } if ( widget->properties == NULL ){ widget->properties = table; return; } g_hash_table_foreach_steal ( table, rofi_theme_steal_property_int, widget->properties ); g_hash_table_destroy ( table ); } /** * Public API */ static ThemeWidget *rofi_theme_find_single ( ThemeWidget *widget, const char *name) { for ( unsigned int j = 0; j < widget->num_widgets;j++){ if ( g_strcmp0(widget->widgets[j]->name, name ) == 0 ){ return widget->widgets[j]; } } return widget; } static ThemeWidget *rofi_theme_find ( ThemeWidget *widget , const char *name, const gboolean exact ) { if ( widget == NULL || name == NULL ) { return widget; } char **names = g_strsplit ( name, "." , 0 ); int found = TRUE; for ( unsigned int i = 0; found && names && names[i]; i++ ){ found = FALSE; ThemeWidget *f = rofi_theme_find_single ( widget, names[i]); if ( f != widget ){ widget = f; found = TRUE; } } g_strfreev(names); if ( !exact || found ){ return widget; } else { return NULL; } } static Property *rofi_theme_find_property ( ThemeWidget *widget, PropertyType type, const char *property ) { while ( widget ) { if ( widget->properties && g_hash_table_contains ( widget->properties, property) ) { Property *p = g_hash_table_lookup ( widget->properties, property); if ( p->type == type ){ return p; } } widget = widget->parent; } return NULL; } static ThemeWidget *rofi_theme_find_widget ( const char *wclass, const char *name, const char *state ) { // First find exact match based on name. ThemeWidget *widget = rofi_theme_find ( rofi_theme, name, TRUE ); widget = rofi_theme_find ( widget, state, TRUE ); if ( widget == NULL ){ // Fall back to class widget = rofi_theme_find ( rofi_theme, wclass, TRUE); widget = rofi_theme_find ( widget, state, TRUE ); } if ( widget == NULL ){ // Fuzzy finder widget = rofi_theme_find ( rofi_theme, name, FALSE ); if ( widget == rofi_theme ){ widget = rofi_theme_find ( widget, wclass, FALSE ); } widget = rofi_theme_find ( widget, state, FALSE ); } return widget; } int rofi_theme_get_integer ( const char *wclass, const char *name, const char *state, const char *property, int def ) { ThemeWidget *widget = rofi_theme_find_widget ( wclass, name, state ); Property *p = rofi_theme_find_property ( widget, P_INTEGER, property ); if ( p ){ return p->value.i; } return def; } Distance rofi_theme_get_distance ( const char *wclass, const char *name, const char *state, const char *property, int def ) { ThemeWidget *widget = rofi_theme_find_widget ( wclass, name, state ); Property *p = rofi_theme_find_property ( widget, P_PADDING, property ); if ( p ){ return p->value.padding.left; } // Fall back to px if no metric is given. p = rofi_theme_find_property ( widget, P_INTEGER, property ); if ( p ){ return (Distance){(double)p->value.i, PW_PX}; } return (Distance){def, PW_PX}; } int rofi_theme_get_boolean ( const char *wclass, const char *name, const char *state, const char *property, int def ) { ThemeWidget *widget = rofi_theme_find_widget ( wclass, name, state ); Property *p = rofi_theme_find_property ( widget, P_BOOLEAN, property ); if ( p ){ return p->value.b; } return def; } char *rofi_theme_get_string ( const char *wclass, const char *name, const char *state, const char *property, char *def ) { ThemeWidget *widget = rofi_theme_find_widget ( wclass, name, state ); Property *p = rofi_theme_find_property ( widget, P_STRING, property ); if ( p ){ return p->value.s; } return def; } double rofi_theme_get_double ( const char *wclass, const char *name, const char *state, const char *property, double def ) { ThemeWidget *widget = rofi_theme_find_widget ( wclass, name, state ); Property *p = rofi_theme_find_property ( widget, P_DOUBLE, property ); if ( p ){ return p->value.b; } return def; } void rofi_theme_get_color ( const char *wclass, const char *name, const char *state, const char *property, cairo_t *d) { ThemeWidget *widget = rofi_theme_find_widget ( wclass, name, state ); Property *p = rofi_theme_find_property ( widget, P_COLOR, property ); if ( p ){ cairo_set_source_rgba ( d, p->value.color.red, p->value.color.green, p->value.color.blue, p->value.color.alpha ); } } Padding rofi_theme_get_padding ( const char *wclass, const char *name, const char *state, const char *property, Padding pad ) { ThemeWidget *widget = rofi_theme_find_widget ( wclass, name, state ); Property *p = rofi_theme_find_property ( widget, P_PADDING, property ); if ( p ){ pad = p->value.padding; }else { p = rofi_theme_find_property ( widget, P_INTEGER, property ); if ( p ){ Distance d = (Distance){p->value.i, PW_PX}; pad = (Padding){d,d,d,d}; } } return pad; } int distance_get_pixel ( Distance d ) { if ( d.type == PW_EM ){ return d.distance*textbox_get_estimated_char_height(); } return d.distance; } #ifdef THEME_CONVERTER static Property* rofi_theme_convert_get_color ( const char *color, const char *name ) { Color c = color_get ( color ); Property *p = rofi_theme_property_create ( P_COLOR ); p->name = g_strdup(name); p->value.color.alpha = c.alpha; p->value.color.red = c.red; p->value.color.green = c.green; p->value.color.blue = c.blue; return p; } static GHashTable * rofi_theme_convert_create_property_ht ( void ) { return g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, (GDestroyNotify)rofi_theme_property_free ); } void rofi_theme_convert_old_theme ( void ) { if ( rofi_theme != NULL ){ return; } rofi_theme = (ThemeWidget*)g_malloc0 ( sizeof ( ThemeWidget ) ); rofi_theme->name = g_strdup ( "Root" ); rofi_theme->properties = rofi_theme_convert_create_property_ht ( ); { // Spacing ThemeWidget *box_widget = rofi_theme_find_or_create_class ( rofi_theme , "@box" ); box_widget->properties = rofi_theme_convert_create_property_ht ( ); Property *p = rofi_theme_property_create ( P_INTEGER ); p->name = g_strdup("spacing"); p->value.i = config.padding; g_hash_table_replace ( box_widget->properties, p->name, p ); } { // Spacing ThemeWidget *listview_widget = rofi_theme_find_or_create_class ( rofi_theme , "@listview" ); listview_widget->properties = rofi_theme_convert_create_property_ht ( ); Property *p = rofi_theme_property_create ( P_INTEGER ); p->name = g_strdup("spacing"); p->value.i = config.padding; g_hash_table_replace ( listview_widget->properties, p->name, p ); } { // Border width. ThemeWidget *window_widget = rofi_theme_find_or_create_class ( rofi_theme , "@window" ); window_widget->properties = rofi_theme_convert_create_property_ht ( ); Property *p = rofi_theme_property_create ( P_INTEGER ); p->name = g_strdup("border-width"); p->value.i = config.menu_bw; g_hash_table_replace ( window_widget->properties, p->name, p ); // Padding p = rofi_theme_property_create ( P_INTEGER ); p->name = g_strdup("padding"); p->value.i = config.padding; g_hash_table_replace ( window_widget->properties, p->name, p ); } { gchar **vals = g_strsplit ( config.color_window, ",", 3 ); if ( vals != NULL ){ if ( vals[0] != NULL ) { Property *p = rofi_theme_convert_get_color ( vals[0], "background" ); g_hash_table_replace ( rofi_theme->properties, p->name, p ); if ( vals[1] != NULL ) { p = rofi_theme_convert_get_color ( vals[1], "foreground" ); g_hash_table_replace ( rofi_theme->properties, p->name, p ); if ( vals[2] != NULL ) { ThemeWidget *widget = rofi_theme_find_or_create_class ( rofi_theme , "@separator" ); widget->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[1], "foreground" ); g_hash_table_replace ( widget->properties, p->name, p ); } } } } g_strfreev ( vals ); { Property *p = NULL; ThemeWidget *widget = rofi_theme_find_or_create_class ( rofi_theme , "@textbox" ); ThemeWidget *wnormal = rofi_theme_find_or_create_class ( widget, "normal" ); ThemeWidget *wselected = rofi_theme_find_or_create_class ( widget, "selected" ); ThemeWidget *walternate = rofi_theme_find_or_create_class ( widget, "alternate" ); gchar **vals = g_strsplit ( config.color_normal, ",", 5 ); if ( g_strv_length (vals) == 5 ) { ThemeWidget *wnn = rofi_theme_find_or_create_class ( wnormal, "normal" ); wnn->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[0], "background" ); g_hash_table_replace ( wnn->properties, p->name, p ); p = rofi_theme_convert_get_color ( vals[1], "foreground" ); g_hash_table_replace ( wnn->properties, p->name, p ); ThemeWidget *wsl = rofi_theme_find_or_create_class ( wselected, "normal" ); wsl->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[3], "background" ); g_hash_table_replace ( wsl->properties, p->name, p ); p = rofi_theme_convert_get_color ( vals[4], "foreground" ); g_hash_table_replace ( wsl->properties, p->name, p ); ThemeWidget *wal = rofi_theme_find_or_create_class ( walternate, "normal" ); wal->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[2], "background" ); g_hash_table_replace ( wal->properties, p->name, p ); p = rofi_theme_convert_get_color ( vals[1], "foreground" ); g_hash_table_replace ( wal->properties, p->name, p ); } g_strfreev ( vals ); vals = g_strsplit ( config.color_urgent, ",", 5 ); if ( g_strv_length (vals) == 5 ) { ThemeWidget *wnn = rofi_theme_find_or_create_class ( wnormal, "urgent" ); wnn->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[0], "background" ); g_hash_table_replace ( wnn->properties, p->name, p ); p = rofi_theme_convert_get_color ( vals[1], "foreground" ); g_hash_table_replace ( wnn->properties, p->name, p ); ThemeWidget *wsl = rofi_theme_find_or_create_class ( wselected, "urgent" ); wsl->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[3], "background" ); g_hash_table_replace ( wsl->properties, p->name, p ); p = rofi_theme_convert_get_color ( vals[4], "foreground" ); g_hash_table_replace ( wsl->properties, p->name, p ); ThemeWidget *wal = rofi_theme_find_or_create_class ( walternate, "urgent" ); wal->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[2], "background" ); g_hash_table_replace ( wal->properties, p->name, p ); p = rofi_theme_convert_get_color ( vals[1], "foreground" ); g_hash_table_replace ( wal->properties, p->name, p ); } g_strfreev ( vals ); vals = g_strsplit ( config.color_active, ",", 5 ); if ( g_strv_length (vals) == 5 ) { ThemeWidget *wnn = rofi_theme_find_or_create_class ( wnormal, "active" ); wnn->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[0], "background" ); g_hash_table_replace ( wnn->properties, p->name, p ); p = rofi_theme_convert_get_color ( vals[1], "foreground" ); g_hash_table_replace ( wnn->properties, p->name, p ); ThemeWidget *wsl = rofi_theme_find_or_create_class ( wselected, "active" ); wsl->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[3], "background" ); g_hash_table_replace ( wsl->properties, p->name, p ); p = rofi_theme_convert_get_color ( vals[4], "foreground" ); g_hash_table_replace ( wsl->properties, p->name, p ); ThemeWidget *wal = rofi_theme_find_or_create_class ( walternate, "active" ); wal->properties = rofi_theme_convert_create_property_ht (); p = rofi_theme_convert_get_color ( vals[2], "background" ); g_hash_table_replace ( wal->properties, p->name, p ); p = rofi_theme_convert_get_color ( vals[1], "foreground" ); g_hash_table_replace ( wal->properties, p->name, p ); } g_strfreev ( vals ); } } } void rofi_theme_parse_file ( const char *file ) { char *filename = rofi_expand_path ( file ); yyin = fopen ( filename, "rb"); if ( yyin == NULL ){ fprintf(stderr, "Failed to open file: %s: '%s'\n", filename, strerror ( errno ) ); g_free(filename); return; } while ( yyparse() ); yylex_destroy(); g_free(filename); } #endif