mirror of
https://github.com/davatorium/rofi.git
synced 2024-11-25 13:55:34 -05:00
1684 lines
56 KiB
C
1684 lines
56 KiB
C
/*
|
|
* rofi
|
|
*
|
|
* MIT/X11 License
|
|
* Copyright © 2013-2023 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"
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
// GFile stuff.
|
|
#include "helper.h"
|
|
#include "rofi-icon-fetcher.h"
|
|
#include "rofi-types.h"
|
|
#include "rofi.h"
|
|
#include "settings.h"
|
|
#include "theme-parser.h"
|
|
#include "theme.h"
|
|
#include "view.h"
|
|
#include "widgets/textbox.h"
|
|
#include <gio/gio.h>
|
|
|
|
/**
|
|
* list of config files we parsed.
|
|
*/
|
|
GList *parsed_config_files = NULL;
|
|
|
|
/** cleanup (free) the list of parsed config files. */
|
|
void rofi_theme_free_parsed_files(void) {
|
|
g_list_free_full(parsed_config_files, g_free);
|
|
parsed_config_files = NULL;
|
|
}
|
|
|
|
/**
|
|
* @param is_term if print to terminal
|
|
*
|
|
* print the list of parsed config files.
|
|
*/
|
|
void rofi_theme_print_parsed_files(gboolean is_term) {
|
|
printf("\nParsed files:\n");
|
|
for (GList *iter = g_list_first(parsed_config_files); iter != NULL;
|
|
iter = g_list_next(iter)) {
|
|
printf("\t\u2023 %s%s%s\n", is_term ? color_bold : "",
|
|
(const char *)(iter->data), is_term ? color_reset : "");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
void yyerror(YYLTYPE *yylloc, const char *, const char *);
|
|
static gboolean distance_compare(RofiDistance d, RofiDistance e) {
|
|
// TODO UPDATE
|
|
return d.base.type == e.base.type && d.base.distance == e.base.distance &&
|
|
d.style == e.style;
|
|
}
|
|
|
|
ThemeWidget *rofi_theme_find_or_create_name(ThemeWidget *base,
|
|
const char *name) {
|
|
for (unsigned int i = 0; i < base->num_widgets; i++) {
|
|
if (g_strcmp0(base->widgets[i]->name, name) == 0) {
|
|
return base->widgets[i];
|
|
}
|
|
}
|
|
|
|
base->widgets =
|
|
g_realloc(base->widgets, sizeof(ThemeWidget *) * (base->num_widgets + 1));
|
|
base->widgets[base->num_widgets] = g_slice_new0(ThemeWidget);
|
|
ThemeWidget *retv = base->widgets[base->num_widgets];
|
|
retv->parent = base;
|
|
retv->name = g_strdup(name);
|
|
base->num_widgets++;
|
|
return retv;
|
|
}
|
|
/**
|
|
* Properties
|
|
*/
|
|
Property *rofi_theme_property_create(PropertyType type) {
|
|
Property *retv = g_slice_new0(Property);
|
|
retv->type = type;
|
|
return retv;
|
|
}
|
|
|
|
static RofiDistanceUnit *
|
|
rofi_theme_property_copy_distance_unit(RofiDistanceUnit *unit) {
|
|
RofiDistanceUnit *retv = g_slice_new0(RofiDistanceUnit);
|
|
*retv = *unit;
|
|
if (unit->left) {
|
|
retv->left = rofi_theme_property_copy_distance_unit(unit->left);
|
|
}
|
|
if (unit->right) {
|
|
retv->right = rofi_theme_property_copy_distance_unit(unit->right);
|
|
}
|
|
return retv;
|
|
}
|
|
RofiDistance rofi_theme_property_copy_distance(RofiDistance const distance) {
|
|
RofiDistance retv = distance;
|
|
if (distance.base.left) {
|
|
retv.base.left = rofi_theme_property_copy_distance_unit(distance.base.left);
|
|
}
|
|
if (distance.base.right) {
|
|
retv.base.right =
|
|
rofi_theme_property_copy_distance_unit(distance.base.right);
|
|
}
|
|
return retv;
|
|
}
|
|
|
|
Property *rofi_theme_property_copy(const Property *p,
|
|
G_GNUC_UNUSED void *data) {
|
|
Property *retv = rofi_theme_property_create(p->type);
|
|
retv->name = g_strdup(p->name);
|
|
|
|
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, (GCopyFunc)rofi_theme_property_copy, NULL);
|
|
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) {
|
|
retv->value.link.def_value =
|
|
rofi_theme_property_copy(p->value.link.def_value, NULL);
|
|
}
|
|
break;
|
|
case P_PADDING: {
|
|
retv->value = p->value;
|
|
retv->value.padding.top =
|
|
rofi_theme_property_copy_distance(p->value.padding.top);
|
|
retv->value.padding.left =
|
|
rofi_theme_property_copy_distance(p->value.padding.left);
|
|
retv->value.padding.bottom =
|
|
rofi_theme_property_copy_distance(p->value.padding.bottom);
|
|
retv->value.padding.right =
|
|
rofi_theme_property_copy_distance(p->value.padding.right);
|
|
break;
|
|
}
|
|
case P_IMAGE: {
|
|
retv->value = p->value;
|
|
retv->value.image.url = g_strdup(p->value.image.url);
|
|
retv->value.image.colors = NULL;
|
|
for (GList *l = g_list_first(p->value.image.colors); l;
|
|
l = g_list_next(l)) {
|
|
retv->value.image.colors = g_list_append(
|
|
retv->value.image.colors, g_memdup(l->data, sizeof(ThemeColor)));
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
retv->value = p->value;
|
|
}
|
|
return retv;
|
|
}
|
|
|
|
static void rofi_theme_distance_unit_property_free(RofiDistanceUnit *unit) {
|
|
if (unit->left) {
|
|
rofi_theme_distance_unit_property_free(unit->left);
|
|
unit->left = NULL;
|
|
}
|
|
if (unit->right) {
|
|
rofi_theme_distance_unit_property_free(unit->right);
|
|
unit->right = NULL;
|
|
}
|
|
g_slice_free(RofiDistanceUnit, unit);
|
|
}
|
|
static void rofi_theme_distance_property_free(RofiDistance *distance) {
|
|
if (distance->base.left) {
|
|
rofi_theme_distance_unit_property_free(distance->base.left);
|
|
distance->base.left = NULL;
|
|
}
|
|
if (distance->base.right) {
|
|
rofi_theme_distance_unit_property_free(distance->base.right);
|
|
distance->base.right = NULL;
|
|
}
|
|
}
|
|
|
|
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_LIST) {
|
|
g_list_free_full(p->value.list, (GDestroyNotify)rofi_theme_property_free);
|
|
p->value.list = 0;
|
|
} else if (p->type == P_LINK) {
|
|
g_free(p->value.link.name);
|
|
if (p->value.link.def_value) {
|
|
rofi_theme_property_free(p->value.link.def_value);
|
|
}
|
|
} else if (p->type == P_PADDING) {
|
|
rofi_theme_distance_property_free(&(p->value.padding.top));
|
|
rofi_theme_distance_property_free(&(p->value.padding.right));
|
|
rofi_theme_distance_property_free(&(p->value.padding.bottom));
|
|
rofi_theme_distance_property_free(&(p->value.padding.left));
|
|
} else if (p->type == P_IMAGE) {
|
|
if (p->value.image.url) {
|
|
g_free(p->value.image.url);
|
|
}
|
|
if (p->value.image.colors) {
|
|
g_list_free_full(p->value.image.colors, g_free);
|
|
}
|
|
}
|
|
g_slice_free(Property, p);
|
|
}
|
|
|
|
void rofi_theme_reset(void) {
|
|
rofi_theme_free(rofi_theme);
|
|
rofi_theme = g_slice_new0(ThemeWidget);
|
|
rofi_theme->name = g_strdup("Root");
|
|
}
|
|
|
|
void rofi_theme_free(ThemeWidget *widget) {
|
|
if (widget == NULL) {
|
|
return;
|
|
}
|
|
if (widget->properties) {
|
|
g_hash_table_destroy(widget->properties);
|
|
widget->properties = NULL;
|
|
}
|
|
if (widget->media) {
|
|
g_slice_free(ThemeMedia, widget->media);
|
|
}
|
|
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_slice_free(ThemeWidget, widget);
|
|
}
|
|
|
|
/**
|
|
* print
|
|
*/
|
|
inline static void printf_double(double d) {
|
|
char buf[G_ASCII_DTOSTR_BUF_SIZE + 1] = {
|
|
0,
|
|
};
|
|
g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%.4f", d);
|
|
fputs(buf, stdout);
|
|
}
|
|
|
|
static void rofi_theme_print_distance_unit(RofiDistanceUnit *unit) {
|
|
if (unit->modtype == ROFI_DISTANCE_MODIFIER_GROUP) {
|
|
fputs("( ", stdout);
|
|
}
|
|
if (unit->left) {
|
|
rofi_theme_print_distance_unit(unit->left);
|
|
}
|
|
|
|
if (unit->modtype == ROFI_DISTANCE_MODIFIER_ADD) {
|
|
fputs(" + ", stdout);
|
|
} else if (unit->modtype == ROFI_DISTANCE_MODIFIER_SUBTRACT) {
|
|
fputs(" - ", stdout);
|
|
} else if (unit->modtype == ROFI_DISTANCE_MODIFIER_DIVIDE) {
|
|
fputs(" / ", stdout);
|
|
} else if (unit->modtype == ROFI_DISTANCE_MODIFIER_MULTIPLY) {
|
|
fputs(" * ", stdout);
|
|
} else if (unit->modtype == ROFI_DISTANCE_MODIFIER_MODULO) {
|
|
fputs(" % ", stdout);
|
|
} else if (unit->modtype == ROFI_DISTANCE_MODIFIER_MIN) {
|
|
fputs(" min ", stdout);
|
|
} else if (unit->modtype == ROFI_DISTANCE_MODIFIER_MAX) {
|
|
fputs(" max ", stdout);
|
|
} else if (unit->modtype == ROFI_DISTANCE_MODIFIER_ROUND) {
|
|
fputs(" round ", stdout);
|
|
} else if (unit->modtype == ROFI_DISTANCE_MODIFIER_FLOOR) {
|
|
fputs(" floor ", stdout);
|
|
} else if (unit->modtype == ROFI_DISTANCE_MODIFIER_CEIL) {
|
|
fputs(" ceil ", stdout);
|
|
}
|
|
if (unit->right) {
|
|
rofi_theme_print_distance_unit(unit->right);
|
|
}
|
|
|
|
if (unit->modtype == ROFI_DISTANCE_MODIFIER_NONE) {
|
|
if (unit->type == ROFI_PU_PX) {
|
|
printf("%upx ", (unsigned int)unit->distance);
|
|
} else if (unit->type == ROFI_PU_MM) {
|
|
printf_double(unit->distance);
|
|
fputs("mm ", stdout);
|
|
} else if (unit->type == ROFI_PU_PERCENT) {
|
|
printf_double(unit->distance);
|
|
fputs("% ", stdout);
|
|
} else if (unit->type == ROFI_PU_CH) {
|
|
printf_double(unit->distance);
|
|
fputs("ch ", stdout);
|
|
} else {
|
|
printf_double(unit->distance);
|
|
fputs("em ", stdout);
|
|
}
|
|
}
|
|
if (unit->modtype == ROFI_DISTANCE_MODIFIER_GROUP) {
|
|
fputs(" )", stdout);
|
|
}
|
|
}
|
|
|
|
static void rofi_theme_print_color(ThemeColor color) {
|
|
uint8_t r, g, b;
|
|
g = 255 * color.green;
|
|
r = 255 * color.red;
|
|
b = 255 * color.blue;
|
|
if (color.alpha < 0.00001) {
|
|
printf("transparent");
|
|
return;
|
|
}
|
|
for (uint32_t x = 0; x < num_CSSColors; x++) {
|
|
if (CSSColors[x].r == r && CSSColors[x].g == g && CSSColors[x].b == b) {
|
|
printf("%s", CSSColors[x].name);
|
|
if (color.alpha < 1) {
|
|
printf("/%.0f%%", color.alpha * 100.0);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
printf("rgba ( %.0f, %.0f, %.0f, %.0f %% )", (color.red * 255.0),
|
|
(color.green * 255.0), (color.blue * 255.0), (color.alpha * 100.0));
|
|
}
|
|
static void rofi_theme_print_distance(RofiDistance d) {
|
|
if (d.base.modtype == ROFI_DISTANCE_MODIFIER_GROUP) {
|
|
fputs("calc( ", stdout);
|
|
}
|
|
rofi_theme_print_distance_unit(&(d.base));
|
|
if (d.base.modtype == ROFI_DISTANCE_MODIFIER_GROUP) {
|
|
fputs(")", stdout);
|
|
}
|
|
if (d.style == ROFI_HL_DASH) {
|
|
printf("dash ");
|
|
}
|
|
}
|
|
/** Textual representation of RofiCursorType */
|
|
const char *const RofiCursorTypeStr[3] = {
|
|
"default",
|
|
"pointer",
|
|
"text",
|
|
};
|
|
|
|
static void int_rofi_theme_print_property(Property *p) {
|
|
switch (p->type) {
|
|
case P_LIST:
|
|
printf("[ ");
|
|
for (GList *iter = p->value.list; iter != NULL; iter = g_list_next(iter)) {
|
|
int_rofi_theme_print_property((Property *)iter->data);
|
|
if (iter->next != NULL) {
|
|
printf(",");
|
|
}
|
|
}
|
|
printf(" ]");
|
|
break;
|
|
case P_ORIENTATION:
|
|
printf("%s", (p->value.i == ROFI_ORIENTATION_HORIZONTAL) ? "horizontal"
|
|
: "vertical");
|
|
break;
|
|
case P_CURSOR:
|
|
printf("%s", RofiCursorTypeStr[p->value.i]);
|
|
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_UPPERCASE) {
|
|
printf("uppercase ");
|
|
}
|
|
if (p->value.highlight.style & ROFI_HL_LOWERCASE) {
|
|
printf("lowercase ");
|
|
}
|
|
if (p->value.highlight.style & ROFI_HL_CAPITALIZE) {
|
|
printf("capitalize ");
|
|
}
|
|
if (p->value.highlight.style & ROFI_HL_COLOR) {
|
|
rofi_theme_print_color(p->value.highlight.color);
|
|
}
|
|
break;
|
|
case P_POSITION: {
|
|
switch (p->value.i) {
|
|
case WL_CENTER:
|
|
fputs("center", stdout);
|
|
break;
|
|
case WL_NORTH:
|
|
fputs("north", stdout);
|
|
break;
|
|
case WL_SOUTH:
|
|
fputs("south", stdout);
|
|
break;
|
|
case WL_WEST:
|
|
fputs("west", stdout);
|
|
break;
|
|
case WL_EAST:
|
|
fputs("east", stdout);
|
|
break;
|
|
case WL_NORTH | WL_EAST:
|
|
fputs("northeast", stdout);
|
|
break;
|
|
case WL_SOUTH | WL_EAST:
|
|
fputs("southeast", stdout);
|
|
break;
|
|
case WL_NORTH | WL_WEST:
|
|
fputs("northwest", stdout);
|
|
break;
|
|
case WL_SOUTH | WL_WEST:
|
|
fputs("southwest", stdout);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case P_STRING:
|
|
printf("\"%s\"", p->value.s);
|
|
break;
|
|
case P_INTEGER:
|
|
printf("%d", p->value.i);
|
|
break;
|
|
case P_DOUBLE: {
|
|
char sign = (p->value.f < 0);
|
|
int top = (int)fabs(p->value.f);
|
|
int bottom = (fabs(fmod(p->value.f, 1.0))) * 100;
|
|
printf("%s%d.%02d", sign ? "-" : "", top, bottom);
|
|
break;
|
|
}
|
|
case P_BOOLEAN:
|
|
printf("%s", p->value.b ? "true" : "false");
|
|
break;
|
|
case P_COLOR:
|
|
rofi_theme_print_color(p->value.color);
|
|
break;
|
|
case P_IMAGE: {
|
|
if (p->value.image.type == ROFI_IMAGE_URL) {
|
|
printf("url (\"%s\")", p->value.s);
|
|
} else if (p->value.image.type == ROFI_IMAGE_LINEAR_GRADIENT) {
|
|
printf("linear-gradient ( ");
|
|
guint length = g_list_length(p->value.image.colors);
|
|
guint index = 0;
|
|
for (GList *l = g_list_first(p->value.image.colors); l != NULL;
|
|
l = g_list_next(l)) {
|
|
ThemeColor *color = (ThemeColor *)l->data;
|
|
rofi_theme_print_color(*color);
|
|
index++;
|
|
if (index < length) {
|
|
printf(", ");
|
|
}
|
|
}
|
|
printf(")");
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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(';');
|
|
putchar('\n');
|
|
}
|
|
|
|
void rofi_theme_print_index(ThemeWidget *widget, int index) {
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
if (widget->media) {
|
|
printf("%s {\n", widget->name);
|
|
for (unsigned int i = 0; i < widget->num_widgets; i++) {
|
|
rofi_theme_print_index(widget->widgets[i], index + 4);
|
|
}
|
|
printf("}\n");
|
|
} else {
|
|
if (widget->properties) {
|
|
GList *list = NULL;
|
|
ThemeWidget *w = widget;
|
|
while (w) {
|
|
if (g_strcmp0(w->name, "Root") == 0) {
|
|
break;
|
|
}
|
|
if (w->media) {
|
|
break;
|
|
}
|
|
list = g_list_prepend(list, w->name);
|
|
w = w->parent;
|
|
}
|
|
if (g_list_length(list) > 0) {
|
|
printf("%*s", index, "");
|
|
for (GList *citer = g_list_first(list); citer != NULL;
|
|
citer = g_list_next(citer)) {
|
|
char *name = (char *)citer->data;
|
|
fputs(name, stdout);
|
|
if (citer->prev == NULL && citer->next) {
|
|
putchar(' ');
|
|
} else if (citer->next) {
|
|
putchar('.');
|
|
}
|
|
}
|
|
printf(" {\n");
|
|
} else {
|
|
printf("%*s* {\n", index, "");
|
|
}
|
|
size_t property_name_length = 0;
|
|
g_hash_table_iter_init(&iter, widget->properties);
|
|
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
|
Property *pv = (Property *)value;
|
|
property_name_length = MAX(strlen(pv->name), property_name_length);
|
|
}
|
|
g_hash_table_iter_init(&iter, widget->properties);
|
|
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
|
Property *pv = (Property *)value;
|
|
rofi_theme_print_property_index(property_name_length, index + 4, pv);
|
|
}
|
|
printf("%*s}\n", index, "");
|
|
g_list_free(list);
|
|
}
|
|
for (unsigned int i = 0; i < widget->num_widgets; i++) {
|
|
rofi_theme_print_index(widget->widgets[i], index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void rofi_theme_print(ThemeWidget *widget) {
|
|
if (widget != NULL) {
|
|
printf("/**\n * rofi -dump-theme output.\n * Rofi version: %s\n **/\n",
|
|
PACKAGE_VERSION);
|
|
rofi_theme_print_index(widget, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 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("");
|
|
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);
|
|
char *esc = g_markup_escape_text(s, -1);
|
|
g_string_append_printf(
|
|
str,
|
|
"\tParser error: <span size=\"smaller\" style=\"italic\">%s</span>\n",
|
|
esc);
|
|
g_free(esc);
|
|
if (yylloc->filename != NULL) {
|
|
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);
|
|
}
|
|
g_log("Parser", G_LOG_LEVEL_DEBUG, "Failed to parse theme:\n%s", str->str);
|
|
rofi_add_error_message(str);
|
|
}
|
|
|
|
static void rofi_theme_copy_property_int(G_GNUC_UNUSED gpointer key,
|
|
gpointer value, gpointer user_data) {
|
|
GHashTable *table = (GHashTable *)user_data;
|
|
Property *p = rofi_theme_property_copy((Property *)value, NULL);
|
|
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) {
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Public API
|
|
*/
|
|
|
|
static inline ThemeWidget *rofi_theme_find_single(ThemeWidget *widget,
|
|
const char *name) {
|
|
for (unsigned int j = 0; widget && 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 *tname = g_strdup(name);
|
|
char *saveptr = NULL;
|
|
int found = TRUE;
|
|
for (const char *iter = strtok_r(tname, ".", &saveptr); iter != NULL;
|
|
iter = strtok_r(NULL, ".", &saveptr)) {
|
|
found = FALSE;
|
|
ThemeWidget *f = rofi_theme_find_single(widget, iter);
|
|
if (f != widget) {
|
|
widget = f;
|
|
found = TRUE;
|
|
} else if (exact) {
|
|
break;
|
|
}
|
|
}
|
|
g_free(tname);
|
|
if (!exact || found) {
|
|
return widget;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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.");
|
|
p->value.link.ref = p;
|
|
return;
|
|
}
|
|
|
|
if (rofi_theme->properties &&
|
|
g_hash_table_contains(rofi_theme->properties, name)) {
|
|
Property *pr = g_hash_table_lookup(rofi_theme->properties, name);
|
|
g_info("Resolving link %s found: %s", p->value.link.name, pr->name);
|
|
if (pr->type == P_LINK) {
|
|
if (pr->value.link.ref == NULL) {
|
|
rofi_theme_resolve_link_property(pr, depth + 1);
|
|
}
|
|
if (pr->value.link.ref != pr) {
|
|
p->value.link.ref = pr->value.link.ref;
|
|
return;
|
|
}
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
// 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) {
|
|
while (widget) {
|
|
if (widget->properties &&
|
|
g_hash_table_contains(widget->properties, property)) {
|
|
Property *p = g_hash_table_lookup(widget->properties, property);
|
|
if (p->type == P_INHERIT) {
|
|
return p;
|
|
}
|
|
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) {
|
|
return p->value.link.ref;
|
|
}
|
|
}
|
|
if (p->type == type) {
|
|
return p;
|
|
}
|
|
// 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.",
|
|
property, widget->name, PropertyTypeName[p->type],
|
|
PropertyTypeName[type]);
|
|
}
|
|
if (exact) {
|
|
return NULL;
|
|
}
|
|
// Fall back to defaults.
|
|
widget = widget->parent;
|
|
}
|
|
return NULL;
|
|
}
|
|
ThemeWidget *rofi_config_find_widget(const char *name, const char *state,
|
|
gboolean exact) {
|
|
// First find exact match based on name.
|
|
ThemeWidget *widget = rofi_theme_find_single(rofi_configuration, name);
|
|
widget = rofi_theme_find(widget, state, exact);
|
|
|
|
return widget;
|
|
}
|
|
ThemeWidget *rofi_theme_find_widget(const char *name, const char *state,
|
|
gboolean exact) {
|
|
// First find exact match based on name.
|
|
ThemeWidget *widget = rofi_theme_find_single(rofi_theme, name);
|
|
widget = rofi_theme_find(widget, state, exact);
|
|
|
|
return widget;
|
|
}
|
|
|
|
static int rofi_theme_get_position_inside(Property *p, const widget *widget,
|
|
const char *property, int def) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_POSITION, property, FALSE);
|
|
return rofi_theme_get_position_inside(pv, 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;
|
|
}
|
|
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);
|
|
return rofi_theme_get_position_inside(p, widget, property, def);
|
|
}
|
|
static int rofi_theme_get_integer_inside(Property *p, const widget *widget,
|
|
const char *property, int def) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_INTEGER, property, FALSE);
|
|
return rofi_theme_get_integer_inside(pv, 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;
|
|
}
|
|
int rofi_theme_get_integer(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_INTEGER, property, FALSE);
|
|
return (int)rofi_theme_get_integer_inside(p, widget, property, (double)def);
|
|
}
|
|
static RofiDistance rofi_theme_get_distance_inside(Property *p,
|
|
const widget *widget,
|
|
const char *property,
|
|
int def) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_PADDING, property, FALSE);
|
|
return rofi_theme_get_distance_inside(pv, widget->parent, property,
|
|
def);
|
|
}
|
|
return (RofiDistance){
|
|
.base = {def, ROFI_PU_PX, ROFI_DISTANCE_MODIFIER_NONE, NULL, NULL},
|
|
.style = ROFI_HL_SOLID};
|
|
}
|
|
if (p->type == P_INTEGER) {
|
|
return (RofiDistance){.base = {p->value.i, ROFI_PU_PX,
|
|
ROFI_DISTANCE_MODIFIER_NONE, NULL, NULL},
|
|
.style = ROFI_HL_SOLID};
|
|
}
|
|
return p->value.padding.left;
|
|
}
|
|
g_debug("Theme entry: #%s %s property %s unset.", widget->name,
|
|
widget->state ? widget->state : "", property);
|
|
return (RofiDistance){
|
|
.base = {def, ROFI_PU_PX, ROFI_DISTANCE_MODIFIER_NONE, NULL, NULL},
|
|
.style = ROFI_HL_SOLID};
|
|
}
|
|
RofiDistance rofi_theme_get_distance(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_PADDING, property, FALSE);
|
|
return rofi_theme_get_distance_inside(p, widget, property, def);
|
|
}
|
|
|
|
static int rofi_theme_get_boolean_inside(Property *p, const widget *widget,
|
|
const char *property, int def) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_BOOLEAN, property, FALSE);
|
|
return rofi_theme_get_boolean_inside(pv, widget->parent, property, def);
|
|
}
|
|
return def;
|
|
}
|
|
return p->value.b;
|
|
}
|
|
g_debug("Theme entry: #%s %s property %s unset.", widget->name,
|
|
widget->state ? widget->state : "", property);
|
|
return def;
|
|
}
|
|
int rofi_theme_get_boolean(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_BOOLEAN, property, FALSE);
|
|
return rofi_theme_get_boolean_inside(p, widget, property, def);
|
|
}
|
|
|
|
static RofiOrientation rofi_theme_get_orientation_inside(Property *p,
|
|
const widget *widget,
|
|
const char *property,
|
|
RofiOrientation def) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_ORIENTATION, property, FALSE);
|
|
return rofi_theme_get_orientation_inside(pv, widget->parent, property,
|
|
def);
|
|
}
|
|
return def;
|
|
}
|
|
return p->value.b;
|
|
}
|
|
g_debug("Theme entry: #%s %s property %s unset.", widget->name,
|
|
widget->state ? widget->state : "", property);
|
|
return def;
|
|
}
|
|
RofiOrientation rofi_theme_get_orientation(const widget *widget,
|
|
const char *property,
|
|
RofiOrientation def) {
|
|
ThemeWidget *wid = rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid, P_ORIENTATION, property, FALSE);
|
|
return rofi_theme_get_orientation_inside(p, widget, property, def);
|
|
}
|
|
|
|
static RofiCursorType rofi_theme_get_cursor_type_inside(Property *p,
|
|
const widget *widget,
|
|
const char *property,
|
|
RofiCursorType def) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_CURSOR, property, FALSE);
|
|
return rofi_theme_get_cursor_type_inside(pv, 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;
|
|
}
|
|
RofiCursorType rofi_theme_get_cursor_type(const widget *widget,
|
|
const char *property,
|
|
RofiCursorType def) {
|
|
ThemeWidget *wid = rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid, P_CURSOR, property, FALSE);
|
|
return rofi_theme_get_cursor_type_inside(p, widget, property, def);
|
|
}
|
|
static const char *rofi_theme_get_string_inside(Property *p,
|
|
const widget *widget,
|
|
const char *property,
|
|
const char *def) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_STRING, property, FALSE);
|
|
return rofi_theme_get_string_inside(pv, widget->parent, property, def);
|
|
}
|
|
return def;
|
|
}
|
|
return p->value.s;
|
|
}
|
|
g_debug("Theme entry: #%s %s property %s unset.", widget->name,
|
|
widget->state ? widget->state : "", property);
|
|
return def;
|
|
}
|
|
const char *rofi_theme_get_string(const widget *widget, const char *property,
|
|
const char *def) {
|
|
ThemeWidget *wid = rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid, P_STRING, property, FALSE);
|
|
return rofi_theme_get_string_inside(p, widget, property, def);
|
|
}
|
|
|
|
static double rofi_theme_get_double_integer_fb_inside(Property *p,
|
|
const widget *widget,
|
|
const char *property,
|
|
double def) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_INTEGER, property, FALSE);
|
|
return rofi_theme_get_double_integer_fb_inside(pv, 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;
|
|
}
|
|
static double rofi_theme_get_double_inside(const widget *orig, Property *p,
|
|
const widget *widget,
|
|
const char *property, double def) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_DOUBLE, property, FALSE);
|
|
return rofi_theme_get_double_inside(orig, pv, widget, property, def);
|
|
}
|
|
return def;
|
|
}
|
|
return p->value.f;
|
|
}
|
|
ThemeWidget *wid = rofi_theme_find_widget(orig->name, widget->state, FALSE);
|
|
// Fallback to integer if double is not found.
|
|
p = rofi_theme_find_property(wid, P_INTEGER, property, FALSE);
|
|
return rofi_theme_get_double_integer_fb_inside(p, widget, property, def);
|
|
}
|
|
double rofi_theme_get_double(const widget *widget, const char *property,
|
|
double def) {
|
|
ThemeWidget *wid = rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid, P_DOUBLE, property, FALSE);
|
|
return rofi_theme_get_double_inside(widget, p, widget, property, def);
|
|
}
|
|
static void rofi_theme_get_color_inside(const widget *widget, Property *p,
|
|
const char *property, cairo_t *d) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_COLOR, property, FALSE);
|
|
rofi_theme_get_color_inside(widget->parent, pv, property, d);
|
|
}
|
|
return;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
void rofi_theme_get_color(const widget *widget, const char *property,
|
|
cairo_t *d) {
|
|
ThemeWidget *wid = rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid, P_COLOR, property, FALSE);
|
|
rofi_theme_get_color_inside(widget, p, property, d);
|
|
}
|
|
|
|
static gboolean rofi_theme_get_image_inside(Property *p, const widget *widget,
|
|
const char *property, cairo_t *d) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_IMAGE, property, FALSE);
|
|
return rofi_theme_get_image_inside(pv, widget->parent, property, d);
|
|
}
|
|
return FALSE;
|
|
}
|
|
if (p->value.image.type == ROFI_IMAGE_URL) {
|
|
int wsize = -1;
|
|
int hsize = -1;
|
|
switch (p->value.image.scaling) {
|
|
case ROFI_SCALE_BOTH:
|
|
wsize = widget->w;
|
|
hsize = widget->h;
|
|
break;
|
|
case ROFI_SCALE_WIDTH:
|
|
wsize = widget->w;
|
|
break;
|
|
case ROFI_SCALE_HEIGHT:
|
|
hsize = widget->h;
|
|
break;
|
|
case ROFI_SCALE_NONE:
|
|
default:
|
|
break;
|
|
}
|
|
if (p->value.image.surface_id == 0 || p->value.image.wsize != wsize ||
|
|
p->value.image.hsize != hsize) {
|
|
p->value.image.surface_id =
|
|
rofi_icon_fetcher_query_advanced(p->value.image.url, wsize, hsize);
|
|
p->value.image.wsize = wsize;
|
|
p->value.image.hsize = hsize;
|
|
}
|
|
cairo_surface_t *img = rofi_icon_fetcher_get(p->value.image.surface_id);
|
|
|
|
if (img != NULL) {
|
|
cairo_pattern_t *pat = cairo_pattern_create_for_surface(img);
|
|
cairo_pattern_set_extend(pat, CAIRO_EXTEND_REPEAT);
|
|
cairo_set_source(d, pat);
|
|
cairo_pattern_destroy(pat);
|
|
return TRUE;
|
|
}
|
|
} else if (p->value.image.type == ROFI_IMAGE_LINEAR_GRADIENT) {
|
|
cairo_pattern_t *pat = NULL;
|
|
switch (p->value.image.dir) {
|
|
case ROFI_DIRECTION_RIGHT:
|
|
pat = cairo_pattern_create_linear(0.0, 0.0, widget->w, 0.0);
|
|
break;
|
|
case ROFI_DIRECTION_LEFT:
|
|
pat = cairo_pattern_create_linear(widget->w, 0.0, 0.0, 0.0);
|
|
break;
|
|
case ROFI_DIRECTION_BOTTOM:
|
|
pat = cairo_pattern_create_linear(0.0, 0.0, 0.0, widget->h);
|
|
break;
|
|
case ROFI_DIRECTION_TOP:
|
|
pat = cairo_pattern_create_linear(0.0, widget->h, 0.0, 0.0);
|
|
break;
|
|
case ROFI_DIRECTION_ANGLE: {
|
|
double offsety1 =
|
|
sin(G_PI * 2 * p->value.image.angle) * (widget->h / 2.0);
|
|
double offsetx1 =
|
|
cos(G_PI * 2 * p->value.image.angle) * (widget->w / 2.0);
|
|
pat = cairo_pattern_create_linear(
|
|
widget->w / 2.0 - offsetx1, widget->h / 2.0 - offsety1,
|
|
widget->w / 2.0 + offsetx1, widget->h / 2.0 + offsety1);
|
|
break;
|
|
}
|
|
};
|
|
guint length = g_list_length(p->value.image.colors);
|
|
if (length > 1) {
|
|
length--;
|
|
guint color_index = 0;
|
|
for (GList *l = g_list_first(p->value.image.colors); l != NULL;
|
|
l = g_list_next(l)) {
|
|
ThemeColor *c = (ThemeColor *)(l->data);
|
|
cairo_pattern_add_color_stop_rgba(pat, (color_index) / (double)length,
|
|
c->red, c->green, c->blue,
|
|
c->alpha);
|
|
color_index++;
|
|
}
|
|
cairo_set_source(d, pat);
|
|
cairo_pattern_destroy(pat);
|
|
return TRUE;
|
|
}
|
|
if (length == 1) {
|
|
ThemeColor *c = (ThemeColor *)(p->value.image.colors->data);
|
|
cairo_pattern_add_color_stop_rgba(pat, 0, c->red, c->green, c->blue,
|
|
c->alpha);
|
|
cairo_set_source(d, pat);
|
|
cairo_pattern_destroy(pat);
|
|
return TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
g_debug("Theme entry: #%s %s property %s unset.", widget->name,
|
|
widget->state ? widget->state : "", property);
|
|
}
|
|
return FALSE;
|
|
}
|
|
gboolean rofi_theme_get_image(const widget *widget, const char *property,
|
|
cairo_t *d) {
|
|
ThemeWidget *wid = rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid, P_IMAGE, property, FALSE);
|
|
return rofi_theme_get_image_inside(p, widget, property, d);
|
|
}
|
|
static RofiPadding rofi_theme_get_padding_inside(Property *p,
|
|
const widget *widget,
|
|
const char *property,
|
|
RofiPadding pad) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_PADDING, property, FALSE);
|
|
return rofi_theme_get_padding_inside(pv, widget->parent, property, pad);
|
|
}
|
|
return pad;
|
|
}
|
|
if (p->type == P_PADDING) {
|
|
pad = p->value.padding;
|
|
} else {
|
|
RofiDistance d =
|
|
(RofiDistance){.base = {p->value.i, ROFI_PU_PX,
|
|
ROFI_DISTANCE_MODIFIER_NONE, NULL, NULL},
|
|
.style = ROFI_HL_SOLID};
|
|
return (RofiPadding){d, d, d, d};
|
|
}
|
|
}
|
|
g_debug("Theme entry: #%s %s property %s unset.", widget->name,
|
|
widget->state ? widget->state : "", property);
|
|
return pad;
|
|
}
|
|
RofiPadding rofi_theme_get_padding(const widget *widget, const char *property,
|
|
RofiPadding pad) {
|
|
ThemeWidget *wid = rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid, P_PADDING, property, FALSE);
|
|
return rofi_theme_get_padding_inside(p, widget, property, pad);
|
|
}
|
|
|
|
static GList *rofi_theme_get_list_inside(Property *p, const widget *widget,
|
|
const char *property,
|
|
PropertyType child_type) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_LIST, property, FALSE);
|
|
return rofi_theme_get_list_inside(pv, widget->parent, property,
|
|
child_type);
|
|
}
|
|
} else if (p->type == P_LIST) {
|
|
return p->value.list;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
GList *rofi_theme_get_list_distance(const widget *widget,
|
|
const char *property) {
|
|
ThemeWidget *wid2 =
|
|
rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid2, P_LIST, property, FALSE);
|
|
GList *list = rofi_theme_get_list_inside(p, widget, property, P_PADDING);
|
|
GList *retv = NULL;
|
|
for (GList *iter = g_list_first(list); iter != NULL;
|
|
iter = g_list_next(iter)) {
|
|
Property *prop = (Property *)(iter->data);
|
|
if (prop->type == P_PADDING) {
|
|
RofiDistance *pnew = g_new0(RofiDistance, 1);
|
|
*pnew = prop->value.padding.left;
|
|
retv = g_list_append(retv, pnew);
|
|
} else if (prop->type == P_INTEGER) {
|
|
RofiDistance *pnew = g_new0(RofiDistance, 1);
|
|
RofiDistance d =
|
|
(RofiDistance){.base = {prop->value.i, ROFI_PU_PX,
|
|
ROFI_DISTANCE_MODIFIER_NONE, NULL, NULL},
|
|
.style = ROFI_HL_SOLID};
|
|
*pnew = d;
|
|
retv = g_list_append(retv, pnew);
|
|
} else {
|
|
g_warning("Invalid type detected in list.");
|
|
}
|
|
}
|
|
return retv;
|
|
}
|
|
GList *rofi_theme_get_list_strings(const widget *widget, const char *property) {
|
|
ThemeWidget *wid2 =
|
|
rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid2, P_LIST, property, FALSE);
|
|
GList *list = rofi_theme_get_list_inside(p, widget, property, P_STRING);
|
|
GList *retv = NULL;
|
|
for (GList *iter = g_list_first(list); iter != NULL;
|
|
iter = g_list_next(iter)) {
|
|
Property *prop = (Property *)(iter->data);
|
|
if (prop->type == P_STRING) {
|
|
retv = g_list_append(retv, g_strdup(prop->value.s));
|
|
} else {
|
|
g_warning("Invalid type detected in list.");
|
|
}
|
|
}
|
|
return retv;
|
|
}
|
|
|
|
static RofiHighlightColorStyle
|
|
rofi_theme_get_highlight_inside(Property *p, widget *widget,
|
|
const char *property,
|
|
RofiHighlightColorStyle th) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pv =
|
|
rofi_theme_find_property(parent, P_HIGHLIGHT, property, FALSE);
|
|
return rofi_theme_get_highlight_inside(pv, widget->parent, property,
|
|
th);
|
|
}
|
|
return th;
|
|
} else if (p->type == P_COLOR) {
|
|
th.style = ROFI_HL_NONE | ROFI_HL_COLOR;
|
|
th.color = p->value.color;
|
|
return th;
|
|
}
|
|
|
|
return p->value.highlight;
|
|
} else {
|
|
ThemeWidget *wid =
|
|
rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p2 = rofi_theme_find_property(wid, P_COLOR, property, FALSE);
|
|
if (p2 != NULL) {
|
|
return rofi_theme_get_highlight_inside(p2, widget, property, th);
|
|
}
|
|
return th;
|
|
}
|
|
g_debug("Theme entry: #%s %s property %s unset.", widget->name,
|
|
widget->state ? widget->state : "", property);
|
|
return th;
|
|
}
|
|
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 == NULL) {
|
|
p = rofi_theme_find_property(wid, P_COLOR, property, FALSE);
|
|
}
|
|
return rofi_theme_get_highlight_inside(p, widget, property, th);
|
|
}
|
|
static int get_pixels(RofiDistanceUnit *unit, RofiOrientation ori) {
|
|
int val = unit->distance;
|
|
|
|
if (unit->type == ROFI_PU_EM) {
|
|
val = unit->distance * textbox_get_estimated_char_height();
|
|
} else if (unit->type == ROFI_PU_CH) {
|
|
val = unit->distance * textbox_get_estimated_ch();
|
|
} else if (unit->type == ROFI_PU_PERCENT) {
|
|
if (ori == ROFI_ORIENTATION_VERTICAL) {
|
|
int height = 0;
|
|
rofi_view_get_current_monitor(NULL, &height);
|
|
val = (unit->distance * height) / (100.0);
|
|
} else {
|
|
int width = 0;
|
|
rofi_view_get_current_monitor(&width, NULL);
|
|
val = (unit->distance * width) / (100.0);
|
|
}
|
|
} else if (unit->type == ROFI_PU_MM) {
|
|
val = unit->distance * config.dpi / 25.4;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static int distance_unit_get_pixel(RofiDistanceUnit *unit,
|
|
RofiOrientation ori) {
|
|
switch (unit->modtype) {
|
|
case ROFI_DISTANCE_MODIFIER_GROUP:
|
|
return distance_unit_get_pixel(unit->left, ori);
|
|
break;
|
|
case ROFI_DISTANCE_MODIFIER_ADD:
|
|
return distance_unit_get_pixel(unit->left, ori) +
|
|
distance_unit_get_pixel(unit->right, ori);
|
|
case ROFI_DISTANCE_MODIFIER_SUBTRACT:
|
|
return distance_unit_get_pixel(unit->left, ori) -
|
|
distance_unit_get_pixel(unit->right, ori);
|
|
case ROFI_DISTANCE_MODIFIER_MULTIPLY:
|
|
return distance_unit_get_pixel(unit->left, ori) *
|
|
distance_unit_get_pixel(unit->right, ori);
|
|
case ROFI_DISTANCE_MODIFIER_DIVIDE: {
|
|
int a = distance_unit_get_pixel(unit->left, ori);
|
|
int b = distance_unit_get_pixel(unit->right, ori);
|
|
if (b != 0) {
|
|
return a / b;
|
|
}
|
|
return a;
|
|
}
|
|
case ROFI_DISTANCE_MODIFIER_MODULO: {
|
|
int a = distance_unit_get_pixel(unit->left, ori);
|
|
int b = distance_unit_get_pixel(unit->right, ori);
|
|
if (b != 0) {
|
|
return a % b;
|
|
}
|
|
return 0;
|
|
}
|
|
case ROFI_DISTANCE_MODIFIER_MIN: {
|
|
int a = distance_unit_get_pixel(unit->left, ori);
|
|
int b = distance_unit_get_pixel(unit->right, ori);
|
|
return MIN(a, b);
|
|
}
|
|
case ROFI_DISTANCE_MODIFIER_MAX: {
|
|
int a = distance_unit_get_pixel(unit->left, ori);
|
|
int b = distance_unit_get_pixel(unit->right, ori);
|
|
return MAX(a, b);
|
|
}
|
|
case ROFI_DISTANCE_MODIFIER_ROUND: {
|
|
double a = (double)distance_unit_get_pixel(unit->left, ori);
|
|
double b = (double)distance_unit_get_pixel(unit->right, ori);
|
|
return (int)(round(a / b) * b);
|
|
}
|
|
case ROFI_DISTANCE_MODIFIER_CEIL: {
|
|
double a = (double)distance_unit_get_pixel(unit->left, ori);
|
|
double b = (double)distance_unit_get_pixel(unit->right, ori);
|
|
return (int)(ceil(a / b) * b);
|
|
}
|
|
case ROFI_DISTANCE_MODIFIER_FLOOR: {
|
|
double a = (double)distance_unit_get_pixel(unit->left, ori);
|
|
double b = (double)distance_unit_get_pixel(unit->right, ori);
|
|
return (int)(floor(a / b) * b);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return get_pixels(unit, ori);
|
|
}
|
|
|
|
int distance_get_pixel(RofiDistance d, RofiOrientation ori) {
|
|
return distance_unit_get_pixel(&(d.base), ori);
|
|
}
|
|
|
|
void distance_get_linestyle(RofiDistance d, cairo_t *draw) {
|
|
if (d.style == ROFI_HL_DASH) {
|
|
const double dashes[1] = {4};
|
|
cairo_set_dash(draw, dashes, 1, 0.0);
|
|
} else {
|
|
cairo_set_dash(draw, NULL, 0, 0.0);
|
|
}
|
|
}
|
|
|
|
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);
|
|
parsed_config_files = g_list_append(parsed_config_files, 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);
|
|
if (child->media) {
|
|
w->media = g_slice_new0(ThemeMedia);
|
|
*(w->media) = *(child->media);
|
|
}
|
|
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]);
|
|
}
|
|
}
|
|
|
|
static void rofi_theme_parse_process_conditionals_int(workarea mon,
|
|
ThemeWidget *rwidget) {
|
|
if (rwidget == NULL) {
|
|
return;
|
|
}
|
|
unsigned int i = 0;
|
|
// for (unsigned int i = 0; i < rwidget->num_widgets; i++) {
|
|
while (i < rwidget->num_widgets) {
|
|
ThemeWidget *widget = rwidget->widgets[i];
|
|
if (widget->media != NULL) {
|
|
rwidget->num_widgets--;
|
|
for (unsigned x = i; x < rwidget->num_widgets; x++) {
|
|
rwidget->widgets[x] = rwidget->widgets[x + 1];
|
|
}
|
|
rwidget->widgets[rwidget->num_widgets] = 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(rwidget, 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(rwidget, 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(rwidget, widget->widgets[x]);
|
|
}
|
|
} else {
|
|
}
|
|
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(rwidget, 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(rwidget, 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(rwidget, 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(rwidget, widget->widgets[x]);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case THEME_MEDIA_TYPE_BOOLEAN: {
|
|
if (widget->media->boolv) {
|
|
for (unsigned int x = 0; x < widget->num_widgets; x++) {
|
|
rofi_theme_parse_merge_widgets(rwidget, widget->widgets[x]);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
rofi_theme_free(widget);
|
|
// endif
|
|
} else {
|
|
rofi_theme_parse_process_conditionals_int(mon, widget);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *rofi_theme_widget_get_name(ThemeWidget *wid) {
|
|
GString *str = g_string_new(wid->name);
|
|
for (ThemeWidget *i = wid->parent; i->parent != NULL; i = i->parent) {
|
|
g_string_prepend_c(str, ' ');
|
|
g_string_prepend(str, i->name);
|
|
}
|
|
char *retv = str->str;
|
|
g_string_free(str, FALSE);
|
|
return retv;
|
|
}
|
|
|
|
static void rofi_theme_parse_process_links_int(ThemeWidget *wid) {
|
|
if (wid == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < wid->num_widgets; i++) {
|
|
ThemeWidget *widget = wid->widgets[i];
|
|
rofi_theme_parse_process_links_int(widget);
|
|
if (widget->properties == NULL) {
|
|
continue;
|
|
}
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
g_hash_table_iter_init(&iter, widget->properties);
|
|
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
|
Property *pv = (Property *)value;
|
|
if (pv->type == P_LINK) {
|
|
if (pv->value.link.ref == NULL) {
|
|
rofi_theme_resolve_link_property(pv, 0);
|
|
if (pv->value.link.ref == pv) {
|
|
char *n = rofi_theme_widget_get_name(widget);
|
|
GString *str = g_string_new(NULL);
|
|
g_string_printf(str,
|
|
"Validating the theme failed: the variable '%s' in "
|
|
"`%s { %s: var(%s);}` failed to resolve.",
|
|
pv->value.link.name, n, pv->name,
|
|
pv->value.link.name);
|
|
|
|
rofi_add_warning_message(str);
|
|
g_free(n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void rofi_theme_parse_process_links(void) {
|
|
rofi_theme_parse_process_links_int(rofi_theme);
|
|
}
|
|
|
|
void rofi_theme_parse_process_conditionals(void) {
|
|
workarea mon;
|
|
monitor_active(&mon);
|
|
rofi_theme_parse_process_conditionals_int(mon, rofi_theme);
|
|
}
|
|
|
|
ThemeMediaType rofi_theme_parse_media_type(const char *type) {
|
|
if (g_strcmp0(type, "monitor-id") == 0) {
|
|
return THEME_MEDIA_TYPE_MON_ID;
|
|
}
|
|
if (g_strcmp0(type, "min-width") == 0) {
|
|
return THEME_MEDIA_TYPE_MIN_WIDTH;
|
|
}
|
|
if (g_strcmp0(type, "min-height") == 0) {
|
|
return THEME_MEDIA_TYPE_MIN_HEIGHT;
|
|
}
|
|
if (g_strcmp0(type, "max-width") == 0) {
|
|
return THEME_MEDIA_TYPE_MAX_WIDTH;
|
|
}
|
|
if (g_strcmp0(type, "max-height") == 0) {
|
|
return THEME_MEDIA_TYPE_MAX_HEIGHT;
|
|
}
|
|
if (g_strcmp0(type, "min-aspect-ratio") == 0) {
|
|
return THEME_MEDIA_TYPE_MIN_ASPECT_RATIO;
|
|
}
|
|
if (g_strcmp0(type, "max-aspect-ratio") == 0) {
|
|
return THEME_MEDIA_TYPE_MAX_ASPECT_RATIO;
|
|
}
|
|
if (g_strcmp0(type, "enabled") == 0) {
|
|
return THEME_MEDIA_TYPE_BOOLEAN;
|
|
}
|
|
return THEME_MEDIA_TYPE_INVALID;
|
|
}
|
|
|
|
static gboolean rofi_theme_has_property_inside(Property *p,
|
|
const widget *widget,
|
|
const char *property) {
|
|
if (p) {
|
|
if (p->type == P_INHERIT) {
|
|
if (widget->parent) {
|
|
ThemeWidget *parent =
|
|
rofi_theme_find_widget(widget->parent->name, widget->state, FALSE);
|
|
Property *pp =
|
|
rofi_theme_find_property(parent, P_STRING, property, FALSE);
|
|
return rofi_theme_has_property_inside(pp, widget->parent, property);
|
|
}
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
gboolean rofi_theme_has_property(const widget *widget, const char *property) {
|
|
ThemeWidget *wid = rofi_theme_find_widget(widget->name, widget->state, FALSE);
|
|
Property *p = rofi_theme_find_property(wid, P_STRING, property, FALSE);
|
|
return rofi_theme_has_property_inside(p, widget, property);
|
|
}
|