rofi/source/theme.c

1152 lines
41 KiB
C
Raw Normal View History

/*
* rofi
*
* MIT/X11 License
2020-01-01 11:23:12 +00:00
* Copyright © 2013-2020 Qball Cow <qball@gmpclient.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/** Log domain used by the theme engine.*/
#define G_LOG_DOMAIN "Theme"
#include "config.h"
2016-12-09 18:49:49 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// GFile stuff.
#include <gio/gio.h>
2016-12-09 18:49:49 +00:00
#include "theme.h"
#include "theme-parser.h"
2016-12-28 11:21:42 +00:00
#include "helper.h"
#include "settings.h"
2016-12-31 22:27:17 +00:00
#include "widgets/textbox.h"
#include "view.h"
#include "rofi.h"
#include "rofi-types.h"
2016-12-31 22:27:17 +00:00
2017-04-23 13:17:15 +00:00
void yyerror ( YYLTYPE *yylloc, const char *, const char * );
static gboolean distance_compare ( RofiDistance d, RofiDistance e )
2017-01-04 22:11:25 +00:00
{
return d.type == e.type && d.distance == e.distance && d.style == e.style;
2017-01-04 22:11:25 +00:00
}
2016-12-09 18:49:49 +00:00
static gpointer rofi_g_list_strdup ( gconstpointer data, G_GNUC_UNUSED gpointer user_data )
{
return g_strdup ( data );
}
ThemeWidget *rofi_theme_find_or_create_name ( ThemeWidget *base, const char *name )
2016-12-09 18:49:49 +00:00
{
for ( unsigned int i = 0; i < base->num_widgets; i++ ) {
if ( g_strcmp0 ( base->widgets[i]->name, name ) == 0 ) {
2016-12-09 18:49:49 +00:00
return base->widgets[i];
2016-12-12 15:55:31 +00:00
}
2016-12-09 18:49:49 +00:00
}
base->widgets = g_realloc ( base->widgets, sizeof ( ThemeWidget* ) * ( base->num_widgets + 1 ) );
2017-03-04 19:09:19 +00:00
base->widgets[base->num_widgets] = g_slice_new0 ( ThemeWidget );
ThemeWidget *retv = base->widgets[base->num_widgets];
2016-12-09 18:49:49 +00:00
retv->parent = base;
retv->name = g_strdup ( name );
2016-12-09 18:49:49 +00:00
base->num_widgets++;
return retv;
}
/**
* Properties
*/
Property *rofi_theme_property_create ( PropertyType type )
{
2017-03-04 19:09:19 +00:00
Property *retv = g_slice_new0 ( Property );
2016-12-09 18:49:49 +00:00
retv->type = type;
return retv;
}
2017-10-05 15:45:50 +00:00
Property* rofi_theme_property_copy ( Property *p )
{
Property *retv = rofi_theme_property_create ( p->type );
retv->name = g_strdup ( p->name );
2017-10-05 15:45:50 +00:00
switch ( p->type )
{
case P_STRING:
retv->value.s = g_strdup ( p->value.s );
break;
case P_LIST:
retv->value.list = g_list_copy_deep ( p->value.list, rofi_g_list_strdup, NULL );
2017-10-05 15:45:50 +00:00
break;
case P_LINK:
retv->value.link.name = g_strdup ( p->value.link.name );
retv->value.link.ref = NULL;
if ( p->value.link.def_value ){
2019-08-17 18:52:49 +00:00
retv->value.link.def_value = rofi_theme_property_copy(p->value.link.def_value);
}
2017-10-05 15:45:50 +00:00
break;
default:
retv->value = p->value;
}
return retv;
}
2016-12-09 18:49:49 +00:00
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 );
}
else if ( p->type == P_LINK ) {
2017-01-05 17:22:34 +00:00
g_free ( p->value.link.name );
if ( p->value.link.def_value ) {
rofi_theme_property_free ( p->value.link.def_value );
}
2016-12-09 18:49:49 +00:00
}
2017-03-04 19:09:19 +00:00
g_slice_free ( Property, p );
2016-12-09 18:49:49 +00:00
}
/**
* This function is a hack to insert backward support for older theme with the updated listvie structure.
*/
static void rofi_theme_insert_listview_backwards_fix ( void )
{
GHashTable *table= g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, (GDestroyNotify) rofi_theme_property_free );
ThemeWidget *t = rofi_theme_find_or_create_name ( rofi_theme, "element" );
ThemeWidget *tt = rofi_theme_find_or_create_name ( rofi_theme, "element-text" );
ThemeWidget *ti = rofi_theme_find_or_create_name ( rofi_theme, "element-icon" );
// Inherit text color
Property *ptc = rofi_theme_property_create ( P_INHERIT );
ptc->name = g_strdup("text-color");
g_hash_table_replace ( table, ptc->name, ptc );
// Transparent background
Property *ptb = rofi_theme_property_create ( P_COLOR );
ptb->name = g_strdup("background-color");
ptb->value.color.red = 0.0;
ptb->value.color.green = 0.0;
ptb->value.color.blue = 0.0;
ptb->value.color.alpha = 0.0;
g_hash_table_replace ( table, ptb->name, ptb );
rofi_theme_widget_add_properties ( tt, table);
2019-08-17 18:52:49 +00:00
RofiDistance dsize = (RofiDistance){1.2, ROFI_PU_CH, ROFI_HL_SOLID };
Property *pts = rofi_theme_property_create ( P_PADDING );
pts->value.padding.top = pts->value.padding.right = pts->value.padding.bottom = pts->value.padding.left = dsize;
pts->name = g_strdup ( "size" );
g_hash_table_replace ( table, pts->name, pts );
rofi_theme_widget_add_properties ( ti, table);
/** Add spacing between icon and text. */
g_hash_table_destroy ( table );
table= g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, (GDestroyNotify) rofi_theme_property_free );
Property *psp = rofi_theme_property_create ( P_PADDING );
psp->name = g_strdup( "spacing" );
RofiDistance d = (RofiDistance){5, ROFI_PU_PX, ROFI_HL_SOLID };
psp->value.padding = (RofiPadding){d,d,d,d};
g_hash_table_replace ( table, psp->name, psp );
rofi_theme_widget_add_properties ( t, table);
g_hash_table_destroy ( table );
}
void rofi_theme_reset ( void )
{
rofi_theme_free ( rofi_theme );
2017-10-05 15:45:50 +00:00
rofi_theme = g_slice_new0 ( ThemeWidget );
rofi_theme->name = g_strdup ( "Root" );
// Hack to fix backwards compatibility.
rofi_theme_insert_listview_backwards_fix ( );
}
void rofi_theme_free ( ThemeWidget *widget )
2016-12-09 18:49:49 +00:00
{
if ( widget == NULL ) {
2016-12-09 18:49:49 +00:00
return;
}
if ( widget->properties ) {
g_hash_table_destroy ( widget->properties );
widget->properties = NULL;
2016-12-09 18:49:49 +00:00
}
for ( unsigned int i = 0; i < widget->num_widgets; i++ ) {
rofi_theme_free ( widget->widgets[i] );
2016-12-09 18:49:49 +00:00
}
g_free ( widget->widgets );
g_free ( widget->name );
2017-03-04 19:09:19 +00:00
g_slice_free ( ThemeWidget, widget );
2016-12-09 18:49:49 +00:00
}
/**
* print
*/
inline static void printf_double ( double d )
{
char buf[G_ASCII_DTOSTR_BUF_SIZE];
2017-11-07 19:04:07 +00:00
g_ascii_formatd ( buf, G_ASCII_DTOSTR_BUF_SIZE, "%.4lf", d );
fputs ( buf, stdout );
}
static void rofi_theme_print_distance ( RofiDistance d )
2017-01-04 22:11:25 +00:00
{
2017-06-02 14:21:05 +00:00
if ( d.type == ROFI_PU_PX ) {
printf ( "%upx ", (unsigned int) d.distance );
}
2017-06-02 14:21:05 +00:00
else if ( d.type == ROFI_PU_PERCENT ) {
printf_double ( d.distance );
fputs ( "%% ", stdout );
}
2019-08-17 18:52:49 +00:00
else if ( d.type == ROFI_PU_CH ) {
printf_double ( d.distance );
fputs ( "ch ", stdout );
}
else {
printf_double ( d.distance );
fputs ( "em ", stdout );
2017-01-04 22:11:25 +00:00
}
2017-06-02 14:21:05 +00:00
if ( d.style == ROFI_HL_DASH ) {
printf ( "dash " );
2017-01-04 22:11:25 +00:00
}
}
/** Textual representation of Window Location */
2017-03-17 13:27:08 +00:00
const char * const WindowLocationStr[9] = {
"center",
"northwest",
"north",
"northeast",
"east",
"southeast",
"south",
"southwest",
"west"
};
static void int_rofi_theme_print_property ( Property *p )
2016-12-09 18:49:49 +00:00
{
switch ( p->type )
{
case P_LIST:
printf ( "[ " );
for ( GList *iter = p->value.list; iter != NULL; iter = g_list_next ( iter ) ) {
printf ( "%s", (char *) ( iter->data ) );
if ( iter->next != NULL ) {
printf ( "," );
}
2017-06-03 18:45:16 +00:00
}
printf ( " ]" );
break;
case P_ORIENTATION:
printf ( "%s", ( p->value.i == ROFI_ORIENTATION_HORIZONTAL ) ? "horizontal" : "vertical" );
break;
case P_HIGHLIGHT:
if ( p->value.highlight.style & ROFI_HL_BOLD ) {
printf ( "bold " );
}
if ( p->value.highlight.style & ROFI_HL_UNDERLINE ) {
printf ( "underline " );
}
if ( p->value.highlight.style & ROFI_HL_STRIKETHROUGH ) {
printf ( "strikethrough " );
}
if ( p->value.highlight.style & ROFI_HL_ITALIC ) {
printf ( "italic " );
}
if ( p->value.highlight.style & ROFI_HL_COLOR ) {
printf ( "rgba ( %.0f, %.0f, %.0f, %.0f %% )",
( p->value.highlight.color.red * 255.0 ),
( p->value.highlight.color.green * 255.0 ),
( p->value.highlight.color.blue * 255.0 ),
( p->value.highlight.color.alpha * 100.0 ) );
}
break;
case P_POSITION:
printf ( "%s", WindowLocationStr[p->value.i] );
break;
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 ( "rgba ( %.0f, %.0f, %.0f, %.0f %% )",
( p->value.color.red * 255.0 ),
( p->value.color.green * 255.0 ),
( p->value.color.blue * 255.0 ),
( p->value.color.alpha * 100.0 ) );
break;
case P_PADDING:
if ( distance_compare ( p->value.padding.top, p->value.padding.bottom ) &&
distance_compare ( p->value.padding.left, p->value.padding.right ) &&
distance_compare ( p->value.padding.left, p->value.padding.top ) ) {
rofi_theme_print_distance ( p->value.padding.left );
}
else if ( distance_compare ( p->value.padding.top, p->value.padding.bottom ) &&
distance_compare ( p->value.padding.left, p->value.padding.right ) ) {
rofi_theme_print_distance ( p->value.padding.top );
rofi_theme_print_distance ( p->value.padding.left );
}
else if ( !distance_compare ( p->value.padding.top, p->value.padding.bottom ) &&
distance_compare ( p->value.padding.left, p->value.padding.right ) ) {
rofi_theme_print_distance ( p->value.padding.top );
rofi_theme_print_distance ( p->value.padding.left );
rofi_theme_print_distance ( p->value.padding.bottom );
}
else {
rofi_theme_print_distance ( p->value.padding.top );
rofi_theme_print_distance ( p->value.padding.right );
rofi_theme_print_distance ( p->value.padding.bottom );
rofi_theme_print_distance ( p->value.padding.left );
}
break;
case P_LINK:
if ( p->value.link.def_value) {
printf( "var( %s, ", p->value.link.name );
int_rofi_theme_print_property ( p->value.link.def_value );
printf (")");
}else {
printf ( "var(%s)", p->value.link.name );
}
break;
case P_INHERIT:
printf ( "inherit" );
break;
default:
break;
2016-12-09 18:49:49 +00:00
}
}
static void rofi_theme_print_property_index ( size_t pnl, int depth, Property *p )
{
int pl = strlen ( p->name );
printf ( "%*s%s:%*s ", depth, "", p->name, (int) pnl - pl, "" );
int_rofi_theme_print_property ( p );
putchar ( ';' );
2016-12-09 18:49:49 +00:00
putchar ( '\n' );
}
static void rofi_theme_print_index ( ThemeWidget *widget )
2016-12-09 18:49:49 +00:00
{
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;
fputs ( name, stdout );
if ( iter->prev == NULL && iter->next ) {
putchar ( ' ' );
2017-11-07 19:04:07 +00:00
}
else if ( iter->next ) {
putchar ( '.' );
}
}
printf ( " {\n" );
}
else {
2017-01-08 16:50:42 +00:00
index = 4;
printf ( "* {\n" );
}
size_t property_name_length = 0;
g_hash_table_iter_init ( &iter, widget->properties );
while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
Property *p = (Property *) value;
property_name_length = MAX ( strlen ( p->name ), property_name_length );
}
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 ( property_name_length, index, p );
2016-12-09 18:49:49 +00:00
}
printf ( "}\n" );
g_list_free ( list );
2016-12-09 18:49:49 +00:00
}
for ( unsigned int i = 0; i < widget->num_widgets; i++ ) {
rofi_theme_print_index ( widget->widgets[i] );
2016-12-09 18:49:49 +00:00
}
}
void rofi_theme_print ( ThemeWidget *widget )
2016-12-09 18:49:49 +00:00
{
if ( widget != NULL ) {
2017-11-07 19:04:07 +00:00
printf ( "/**\n * rofi -dump-theme output.\n * Rofi version: %s\n **/\n", PACKAGE_VERSION );
rofi_theme_print_index ( widget );
}
2016-12-09 18:49:49 +00:00
}
/**
* Main lex parser.
*/
int yyparse ();
/**
* Destroy the internal of lex parser.
*/
void yylex_destroy ( void );
/**
* Global handle input file to flex parser.
*/
2016-12-09 18:49:49 +00:00
extern FILE* yyin;
/**
* @param yylloc The file location.
2017-01-09 21:40:11 +00:00
* @param what What we are parsing, filename or string.
* @param s Error message string.
*
* Error handler for the lex parser.
*/
void yyerror ( YYLTYPE *yylloc, const char *what, const char* s )
{
char *what_esc = what ? g_markup_escape_text ( what, -1 ) : g_strdup ( "" );
2017-01-09 21:40:11 +00:00
GString *str = g_string_new ( "" );
g_string_printf ( str, "<big><b>Error while parsing theme:</b></big> <i>%s</i>\n", what_esc );
g_free ( what_esc );
2017-01-09 21:40:11 +00:00
char *esc = g_markup_escape_text ( s, -1 );
g_string_append_printf ( str, "\tParser error: <span size=\"smaller\" style=\"italic\">%s</span>\n", esc );
2017-01-09 21:40:11 +00:00
g_free ( esc );
if ( yylloc->filename != NULL ) {
2017-03-17 13:07:11 +00:00
g_string_append_printf ( str, "\tLocation: line %d column %d to line %d column %d.\n" \
"\tFile '%s'\n", yylloc->first_line, yylloc->first_column, yylloc->last_line, yylloc->last_column, yylloc->filename );
}
else {
g_string_append_printf ( str, "\tLocation: line %d column %d to line %d column %d\n", yylloc->first_line, yylloc->first_column, yylloc->last_line, yylloc->last_column );
}
2017-04-04 06:31:25 +00:00
g_log ( "Parser", G_LOG_LEVEL_DEBUG, "Failed to parse theme:\n%s", str->str );
2017-04-04 06:56:19 +00:00
rofi_add_error_message ( str );
2016-12-09 21:16:31 +00:00
}
static void rofi_theme_copy_property_int ( G_GNUC_UNUSED gpointer key, gpointer value, gpointer user_data )
{
GHashTable *table = (GHashTable *) user_data;
2017-10-05 15:45:50 +00:00
Property *p = rofi_theme_property_copy ( (Property *) value );
g_hash_table_replace ( table, p->name, p );
}
void rofi_theme_widget_add_properties ( ThemeWidget *widget, GHashTable *table )
{
if ( table == NULL ) {
return;
}
if ( widget->properties == NULL ) {
2017-10-05 15:45:50 +00:00
widget->properties = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, (GDestroyNotify) rofi_theme_property_free );
}
g_hash_table_foreach ( table, rofi_theme_copy_property_int, widget->properties );
}
2016-12-09 21:16:31 +00:00
/**
* Public API
*/
static inline ThemeWidget *rofi_theme_find_single ( ThemeWidget *widget, const char *name )
2016-12-15 08:46:42 +00:00
{
2017-09-07 18:03:21 +00:00
for ( unsigned int j = 0; widget && j < widget->num_widgets; j++ ) {
if ( g_strcmp0 ( widget->widgets[j]->name, name ) == 0 ) {
2016-12-15 08:46:42 +00:00
return widget->widgets[j];
}
}
return widget;
}
2016-12-09 18:49:49 +00:00
static ThemeWidget *rofi_theme_find ( ThemeWidget *widget, const char *name, const gboolean exact )
2016-12-09 21:16:31 +00:00
{
if ( widget == NULL || name == NULL ) {
2016-12-12 08:11:57 +00:00
return widget;
}
2017-03-17 13:07:11 +00:00
char *tname = g_strdup ( name );
char *saveptr = NULL;
2017-03-17 13:07:11 +00:00
int found = TRUE;
for ( const char *iter = strtok_r ( tname, ".", &saveptr ); iter != NULL; iter = strtok_r ( NULL, ".", &saveptr ) ) {
2016-12-09 21:16:31 +00:00
found = FALSE;
ThemeWidget *f = rofi_theme_find_single ( widget, iter );
if ( f != widget ) {
2016-12-15 08:46:42 +00:00
widget = f;
found = TRUE;
2017-06-03 18:35:50 +00:00
}
else if ( exact ) {
2017-05-27 15:08:46 +00:00
break;
2016-12-09 21:16:31 +00:00
}
}
g_free ( tname );
if ( !exact || found ) {
2016-12-20 08:17:19 +00:00
return widget;
}
else {
2016-12-20 08:17:19 +00:00
return NULL;
}
2016-12-09 21:16:31 +00:00
}
2017-01-05 17:22:34 +00:00
static void rofi_theme_resolve_link_property ( Property *p, int depth )
{
// Set name, remove '@' prefix.
const char *name = p->value.link.name;// + (*(p->value.link.name)== '@'?1:0;
g_info ( "Resolving link to %s", p->value.link.name);
if ( depth > 20 ) {
g_warning ( "Found more then 20 redirects for property. Stopping." );
2017-01-05 17:22:34 +00:00
p->value.link.ref = p;
return;
}
if ( rofi_theme->properties && g_hash_table_contains ( rofi_theme->properties, name ) ) {
2017-01-05 17:22:34 +00:00
Property *pr = g_hash_table_lookup ( rofi_theme->properties, name );
g_info ("Resolving link %s found: %s", p->value.link.name, pr->name);
2017-01-05 17:22:34 +00:00
if ( pr->type == P_LINK ) {
if ( pr->value.link.ref == NULL ) {
rofi_theme_resolve_link_property ( pr, depth + 1 );
2017-01-05 17:22:34 +00:00
}
if ( pr->value.link.ref != pr ) {
2017-01-05 17:22:34 +00:00
p->value.link.ref = pr->value.link.ref;
return;
}
}
else {
2017-01-05 17:22:34 +00:00
p->value.link.ref = pr;
return;
}
}
// No found and we have default value.
if ( p->value.link.def_value ){
p->value.link.ref = p->value.link.def_value;
return;
}
2017-01-05 17:22:34 +00:00
// No found, set ref to self.
p->value.link.ref = p;
}
Property *rofi_theme_find_property ( ThemeWidget *widget, PropertyType type, const char *property, gboolean exact )
2016-12-09 21:16:31 +00:00
{
while ( widget ) {
if ( widget->properties && g_hash_table_contains ( widget->properties, property ) ) {
Property *p = g_hash_table_lookup ( widget->properties, property );
2017-10-05 15:45:50 +00:00
if ( p->type == P_INHERIT ) {
return p;
}
2017-01-05 17:22:34 +00:00
if ( p->type == P_LINK ) {
if ( p->value.link.ref == NULL ) {
// Resolve link.
rofi_theme_resolve_link_property ( p, 0 );
}
if ( p->value.link.ref != NULL && p->value.link.ref->type == type ) {
2017-01-05 17:22:34 +00:00
return p->value.link.ref;
}
}
if ( p->type == type ) {
2016-12-09 21:16:31 +00:00
return p;
}
2017-06-02 14:44:16 +00:00
// RofiPadding and integer can be converted.
if ( p->type == P_INTEGER && type == P_PADDING ) {
return p;
}
g_debug ( "Found property: '%s' on '%s', but type %s does not match expected type %s.",
2017-04-27 20:59:14 +00:00
property, widget->name,
PropertyTypeName[p->type],
PropertyTypeName[type]
);
}
if ( exact ) {
return NULL;
}
// Fall back to defaults.
2017-09-06 20:19:00 +00:00
widget = widget->parent;
2016-12-09 21:16:31 +00:00
}
return NULL;
}
ThemeWidget *rofi_theme_find_widget ( const char *name, const char *state, gboolean exact )
2016-12-09 21:16:31 +00:00
{
2016-12-20 08:17:19 +00:00
// First find exact match based on name.
ThemeWidget *widget = rofi_theme_find_single ( rofi_theme, name );
widget = rofi_theme_find ( widget, state, exact );
2016-12-20 08:17:19 +00:00
return widget;
}
int rofi_theme_get_position ( const widget *widget, const char *property, int def )
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_POSITION, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_position ( widget->parent, property, def );
}
return def;
}
return p->value.i;
}
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
return def;
}
2017-01-04 21:27:27 +00:00
int rofi_theme_get_integer ( const widget *widget, const char *property, int def )
2016-12-20 08:17:19 +00:00
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_INTEGER, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_integer ( widget->parent, property, def );
}
return def;
}
2016-12-09 21:16:31 +00:00
return p->value.i;
}
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
2016-12-09 21:16:31 +00:00
return def;
}
RofiDistance rofi_theme_get_distance ( const widget *widget, const char *property, int def )
2016-12-31 22:27:17 +00:00
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_PADDING, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_distance ( widget->parent, property, def );
}
return (RofiDistance){ def, ROFI_PU_PX, ROFI_HL_SOLID };
}
if ( p->type == P_INTEGER ) {
return (RofiDistance){ p->value.i, ROFI_PU_PX, ROFI_HL_SOLID };
}
else {
2017-01-04 21:27:27 +00:00
return p->value.padding.left;
}
}
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
return (RofiDistance){ def, ROFI_PU_PX, ROFI_HL_SOLID };
2016-12-31 22:27:17 +00:00
}
2016-12-09 21:16:31 +00:00
2017-01-04 21:27:27 +00:00
int rofi_theme_get_boolean ( const widget *widget, const char *property, int def )
2016-12-09 21:16:31 +00:00
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_BOOLEAN, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_boolean ( widget->parent, property, def );
}
return def;
}
2016-12-09 21:16:31 +00:00
return p->value.b;
}
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
2016-12-09 21:16:31 +00:00
return def;
}
RofiOrientation rofi_theme_get_orientation ( const widget *widget, const char *property, RofiOrientation def )
2017-06-02 12:05:19 +00:00
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_ORIENTATION, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_orientation ( widget->parent, property, def );
}
return def;
}
2017-06-02 12:05:19 +00:00
return p->value.b;
}
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
return def;
}
2016-12-09 21:16:31 +00:00
2017-06-03 18:45:16 +00:00
const char *rofi_theme_get_string ( const widget *widget, const char *property, const char *def )
2016-12-09 21:16:31 +00:00
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_STRING, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_string ( widget->parent, property, def );
}
return def;
}
2016-12-09 21:16:31 +00:00
return p->value.s;
}
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
2016-12-09 21:16:31 +00:00
return def;
}
2017-01-04 21:27:27 +00:00
double rofi_theme_get_double ( const widget *widget, const char *property, double def )
2016-12-09 21:16:31 +00:00
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_DOUBLE, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_double ( widget->parent, property, def );
}
return def;
}
return p->value.f;
2016-12-09 21:16:31 +00:00
}
// Fallback to integer if double is not found.
2018-08-08 13:55:13 +00:00
p = rofi_theme_find_property ( wid, P_INTEGER, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_double ( widget->parent, property, def );
}
return def;
}
2018-08-08 13:55:13 +00:00
return (double) p->value.i;
}
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
2016-12-09 21:16:31 +00:00
return def;
2016-12-09 18:49:49 +00:00
}
void rofi_theme_get_color ( const widget *widget, const char *property, cairo_t *d )
2016-12-11 11:19:46 +00:00
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_COLOR, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
rofi_theme_get_color ( widget->parent, property, d );
}
2017-10-05 15:45:50 +00:00
return;
}
2016-12-11 11:19:46 +00:00
cairo_set_source_rgba ( d,
p->value.color.red,
p->value.color.green,
p->value.color.blue,
p->value.color.alpha
);
}
else {
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
2016-12-11 11:19:46 +00:00
}
}
2017-06-02 14:44:16 +00:00
RofiPadding rofi_theme_get_padding ( const widget *widget, const char *property, RofiPadding pad )
2016-12-27 21:19:15 +00:00
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_PADDING, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_padding ( widget->parent, property, pad );
}
return pad;
}
if ( p->type == P_PADDING ) {
2017-01-04 21:27:27 +00:00
pad = p->value.padding;
}
else {
RofiDistance d = (RofiDistance){ p->value.i, ROFI_PU_PX, ROFI_HL_SOLID };
2017-06-02 14:44:16 +00:00
return (RofiPadding){ d, d, d, d };
}
2016-12-27 21:19:15 +00:00
}
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
2016-12-27 21:19:15 +00:00
return pad;
}
GList *rofi_theme_get_list ( const widget *widget, const char * property, const char *defaults )
{
2017-05-27 15:08:46 +00:00
ThemeWidget *wid2 = rofi_theme_find_widget ( widget->name, widget->state, TRUE );
2017-06-03 18:35:50 +00:00
Property *p = rofi_theme_find_property ( wid2, P_LIST, property, TRUE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_list ( widget->parent, property, defaults );
}
2017-10-05 15:45:50 +00:00
}
else if ( p->type == P_LIST ) {
return g_list_copy_deep ( p->value.list, rofi_g_list_strdup, NULL );
}
}
2017-06-03 18:35:50 +00:00
char **r = defaults ? g_strsplit ( defaults, ",", 0 ) : NULL;
if ( r ) {
GList *l = NULL;
2017-06-03 18:35:50 +00:00
for ( int i = 0; r[i] != NULL; i++ ) {
l = g_list_append ( l, r[i] );
}
2017-06-03 18:35:50 +00:00
g_free ( r );
return l;
}
return NULL;
}
RofiHighlightColorStyle rofi_theme_get_highlight ( widget *widget, const char *property, RofiHighlightColorStyle th )
{
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
Property *p = rofi_theme_find_property ( wid, P_HIGHLIGHT, property, FALSE );
if ( p ) {
if ( p->type == P_INHERIT ) {
if ( widget->parent ) {
return rofi_theme_get_highlight ( widget->parent, property, th );
}
return th;
}
return p->value.highlight;
}
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
return th;
}
int distance_get_pixel ( RofiDistance d, RofiOrientation ori )
2016-12-31 22:27:17 +00:00
{
2017-06-02 14:21:05 +00:00
if ( d.type == ROFI_PU_EM ) {
return d.distance * textbox_get_estimated_char_height ();
}
2017-09-05 11:52:21 +00:00
else if ( d.type == ROFI_PU_CH ) {
return d.distance * textbox_get_estimated_ch ();
}
2017-06-02 14:21:05 +00:00
else if ( d.type == ROFI_PU_PERCENT ) {
if ( ori == ROFI_ORIENTATION_VERTICAL ) {
int height = 0;
rofi_view_get_current_monitor ( NULL, &height );
return ( d.distance * height ) / ( 100.0 );
}
else {
int width = 0;
rofi_view_get_current_monitor ( &width, NULL );
return ( d.distance * width ) / ( 100.0 );
}
2016-12-31 22:27:17 +00:00
}
return d.distance;
}
void distance_get_linestyle ( RofiDistance d, cairo_t *draw )
2017-01-04 21:27:27 +00:00
{
2017-06-02 14:21:05 +00:00
if ( d.style == ROFI_HL_DASH ) {
2017-01-04 21:27:27 +00:00
const double dashes[1] = { 4 };
cairo_set_dash ( draw, dashes, 1, 0.0 );
}
else {
cairo_set_dash ( draw, NULL, 0, 0.0 );
2017-01-04 21:27:27 +00:00
}
}
gboolean rofi_theme_is_empty ( void )
{
if ( rofi_theme == NULL ) {
return TRUE;
}
2017-03-17 13:07:11 +00:00
if ( rofi_theme->properties == NULL && rofi_theme->num_widgets == 0 ) {
return TRUE;
}
return FALSE;
}
2017-04-03 10:04:07 +00:00
#ifdef THEME_CONVERTER
static char * rofi_theme_convert_color ( char *col )
{
char *r = g_strstrip ( col );
if ( *r == '#' && strlen ( r ) == 9 ) {
char a1 = r[1];
char a2 = r[2];
r[1] = r[3];
r[2] = r[4];
r[3] = r[5];
r[4] = r[6];
r[5] = r[7];
r[6] = r[8];
r[7] = a1;
r[8] = a2;
}
return r;
}
void rofi_theme_convert_old ( void )
{
{
char *str = g_strdup_printf ( "#window { border: %d; padding: %d;}", config.menu_bw, config.padding );
rofi_theme_parse_string ( str );
g_free ( str );
}
if ( config.color_window ) {
char **retv = g_strsplit ( config.color_window, ",", -1 );
2017-05-11 16:30:44 +00:00
const char * const conf[] = {
"* { background: %s; }",
"* { border-color: %s; }",
"* { separatorcolor: %s; }"
};
2017-09-05 11:52:44 +00:00
for ( int i = 0; retv && i < 3 && retv[i]; i++ ) {
char *str = g_strdup_printf ( conf[i], rofi_theme_convert_color ( retv[i] ) );
rofi_theme_parse_string ( str );
g_free ( str );
}
g_strfreev ( retv );
}
if ( config.color_normal ) {
char **retv = g_strsplit ( config.color_normal, ",", -1 );
2017-05-11 16:30:44 +00:00
const char * const conf[] = {
"* { normal-background: %s; }",
"* { foreground: %s; normal-foreground: @foreground; alternate-normal-foreground: @foreground; }",
"* { alternate-normal-background: %s; }",
"* { selected-normal-background: %s; }",
"* { selected-normal-foreground: %s; }"
};
for ( int i = 0; retv && retv[i] && i < 5; i++ ) {
char *str = g_strdup_printf ( conf[i], rofi_theme_convert_color ( retv[i] ) );
rofi_theme_parse_string ( str );
g_free ( str );
}
g_strfreev ( retv );
}
if ( config.color_urgent ) {
char **retv = g_strsplit ( config.color_urgent, ",", -1 );
2017-05-11 16:30:44 +00:00
const char * const conf[] = {
"* { urgent-background: %s; }",
"* { urgent-foreground: %s; alternate-urgent-foreground: @urgent-foreground;}",
"* { alternate-urgent-background: %s; }",
"* { selected-urgent-background: %s; }",
"* { selected-urgent-foreground: %s; }"
};
for ( int i = 0; retv && retv[i] && i < 5; i++ ) {
char *str = g_strdup_printf ( conf[i], rofi_theme_convert_color ( retv[i] ) );
rofi_theme_parse_string ( str );
g_free ( str );
}
g_strfreev ( retv );
}
if ( config.color_active ) {
char **retv = g_strsplit ( config.color_active, ",", -1 );
2017-05-11 16:30:44 +00:00
const char * const conf[] = {
"* { active-background: %s; }",
"* { active-foreground: %s; alternate-active-foreground: @active-foreground;}",
"* { alternate-active-background: %s; }",
"* { selected-active-background: %s; }",
"* { selected-active-foreground: %s; }"
};
for ( int i = 0; retv && retv[i] && i < 5; i++ ) {
char *str = g_strdup_printf ( conf[i], rofi_theme_convert_color ( retv[i] ) );
rofi_theme_parse_string ( str );
g_free ( str );
}
g_strfreev ( retv );
}
if ( config.separator_style != NULL ) {
if ( g_strcmp0 ( config.separator_style, "none" ) == 0 ) {
2017-09-09 10:07:11 +00:00
const char *const str = "#listview { border: 0px; }";
rofi_theme_parse_string ( str );
const char *const str2 = "#mode-switcher { border: 0px; }";
rofi_theme_parse_string ( str2 );
2017-09-09 10:07:11 +00:00
const char *const str3 = "#message { border: 0px; }";
2017-07-24 14:42:19 +00:00
rofi_theme_parse_string ( str3 );
}
else if ( g_strcmp0 ( config.separator_style, "solid" ) == 0 ) {
2017-09-09 10:07:11 +00:00
const char *const str = "#listview { border: 2px solid 0px 0px 0px; }";
rofi_theme_parse_string ( str );
const char *const str2 = "#mode-switcher { border: 2px solid 0px 0px 0px; }";
rofi_theme_parse_string ( str2 );
2017-09-09 10:07:11 +00:00
const char *const str3 = "#message { border: 2px solid 0px 0px 0px; }";
2017-07-24 14:42:19 +00:00
rofi_theme_parse_string ( str3 );
} /* dash is default */
}
/* Line Margin */
{
2017-09-09 10:07:11 +00:00
char *str = g_strdup_printf ( "#listview { spacing: %dpx;}", config.line_margin );
rofi_theme_parse_string ( str );
g_free ( str );
}
/* Line Padding */
{
2017-09-09 12:50:23 +00:00
char *str = g_strdup_printf ( "#element, inputbar, message { padding: %dpx;}", config.line_padding );
rofi_theme_parse_string ( str );
g_free ( str );
}
if ( config.hide_scrollbar ) {
2017-09-09 10:07:11 +00:00
const char *str = "#listview { scrollbar: false; }";
rofi_theme_parse_string ( str );
}
else {
2017-09-09 10:07:11 +00:00
const char *str = "#listview { scrollbar: true; }";
rofi_theme_parse_string ( str );
2017-09-09 10:07:11 +00:00
char *str2 = g_strdup_printf ( "#scrollbar { handle-width: %dpx; }", config.scrollbar_width );
rofi_theme_parse_string ( str2 );
g_free ( str2 );
}
if ( config.fake_transparency ) {
char *str = g_strdup_printf ( "#window { transparency: \"%s\"; }", config.fake_background );
rofi_theme_parse_string ( str );
g_free ( str );
}
}
2017-04-03 10:04:07 +00:00
#endif // THEME_CONVERTER
char * rofi_theme_parse_prepare_file ( const char *file, const char *parent_file )
{
char *filename = rofi_expand_path ( file );
// If no absolute path specified, expand it.
if ( parent_file != NULL && !g_path_is_absolute ( filename ) ) {
char *basedir = g_path_get_dirname ( parent_file );
char *path = g_build_filename ( basedir, filename, NULL );
g_free ( filename );
filename = path;
g_free ( basedir );
}
GFile *gf = g_file_new_for_path ( filename );
g_free ( filename );
filename = g_file_get_path ( gf );
g_object_unref ( gf );
return filename;
}
void rofi_theme_parse_merge_widgets ( ThemeWidget *parent, ThemeWidget *child )
{
g_assert ( parent != NULL );
g_assert ( child != NULL );
if ( parent == rofi_theme && g_strcmp0(child->name, "*") == 0 ){
rofi_theme_widget_add_properties ( parent, child->properties);
return;
}
ThemeWidget *w = rofi_theme_find_or_create_name ( parent, child->name);
rofi_theme_widget_add_properties ( w, child->properties);
for ( unsigned int i =0; i < child->num_widgets; i++) {
rofi_theme_parse_merge_widgets ( w, child->widgets[i]);
}
}
void rofi_theme_parse_process_conditionals ( void )
{
workarea mon;
monitor_active ( &mon );
if ( rofi_theme == NULL ) return;
for ( unsigned int i = 0; i < rofi_theme->num_widgets; i++ ) {
ThemeWidget *widget = rofi_theme->widgets[i];
if ( widget->media != NULL ) {
switch ( widget->media->type )
{
case THEME_MEDIA_TYPE_MIN_WIDTH:
{
int w = widget->media->value;
if ( mon.w >= w ){
for ( unsigned int x =0; x < widget->num_widgets; x++) {
rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
}
}
break;
}
case THEME_MEDIA_TYPE_MAX_WIDTH:
{
int w = widget->media->value;
if ( mon.w < w ){
for ( unsigned int x =0; x < widget->num_widgets; x++) {
rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
}
}
break;
}
case THEME_MEDIA_TYPE_MIN_HEIGHT:
{
int h = widget->media->value;
if ( mon.h >= h ){
for ( unsigned int x =0; x < widget->num_widgets; x++) {
rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
}
}
break;
}
case THEME_MEDIA_TYPE_MAX_HEIGHT:
{
int h = widget->media->value;
if ( mon.h < h ){
for ( unsigned int x =0; x < widget->num_widgets; x++) {
rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
}
}
break;
}
case THEME_MEDIA_TYPE_MON_ID:
{
if ( mon.monitor_id == widget->media->value ){
for ( unsigned int x =0; x < widget->num_widgets; x++) {
rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
}
}
break;
}
case THEME_MEDIA_TYPE_MIN_ASPECT_RATIO:
{
double r = widget->media->value;
if ( (mon.w/(double)mon.h) >= r ){
for ( unsigned int x =0; x < widget->num_widgets; x++) {
rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
}
}
break;
}
case THEME_MEDIA_TYPE_MAX_ASPECT_RATIO:
{
double r = widget->media->value;
if ( (mon.w/(double)mon.h) < r ){
for ( unsigned int x =0; x < widget->num_widgets; x++) {
rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
}
}
break;
}
default:
{
break;
}
}
}
}
}
ThemeMediaType rofi_theme_parse_media_type ( const char *type )
{
if ( g_strcmp0( type, "monitor-id" ) == 0 ) {
return THEME_MEDIA_TYPE_MON_ID;
} else if ( g_strcmp0 ( type, "min-width") == 0 ) {
return THEME_MEDIA_TYPE_MIN_WIDTH;
} else if ( g_strcmp0 ( type, "min-height") == 0 ) {
return THEME_MEDIA_TYPE_MIN_HEIGHT;
} else if ( g_strcmp0 ( type, "max-width") == 0 ) {
return THEME_MEDIA_TYPE_MAX_WIDTH;
} else if ( g_strcmp0 ( type, "max-height") == 0 ) {
return THEME_MEDIA_TYPE_MAX_HEIGHT;
} else if ( g_strcmp0 ( type, "min-aspect-ratio") == 0 ) {
return THEME_MEDIA_TYPE_MIN_ASPECT_RATIO;
} else if ( g_strcmp0 ( type, "max-aspect-ratio") == 0 ) {
return THEME_MEDIA_TYPE_MAX_ASPECT_RATIO;
}
return THEME_MEDIA_TYPE_INVALID;
}