mirror of
https://github.com/davatorium/rofi.git
synced 2024-11-18 13:54:36 -05:00
Allow interface to be dynamically changed.
This commit is contained in:
parent
e387105091
commit
c23df70aeb
6 changed files with 219 additions and 93 deletions
|
@ -118,6 +118,8 @@ typedef enum
|
|||
P_POSITION,
|
||||
/** Highlight */
|
||||
P_HIGHLIGHT,
|
||||
/** List */
|
||||
P_LIST,
|
||||
} PropertyType;
|
||||
|
||||
/**
|
||||
|
@ -156,6 +158,34 @@ typedef struct
|
|||
/** Color */
|
||||
ThemeColor color;
|
||||
} ThemeHighlight;
|
||||
|
||||
typedef union {
|
||||
/** integer */
|
||||
int i;
|
||||
/** Double */
|
||||
double f;
|
||||
/** String */
|
||||
char *s;
|
||||
/** boolean */
|
||||
gboolean b;
|
||||
/** Color */
|
||||
ThemeColor color;
|
||||
/** Padding */
|
||||
Padding padding;
|
||||
/** Reference */
|
||||
struct
|
||||
{
|
||||
/** Name */
|
||||
char *name;
|
||||
/** Cached looked up ref */
|
||||
struct Property *ref;
|
||||
} link;
|
||||
/** Highlight Style */
|
||||
ThemeHighlight highlight;
|
||||
/** List */
|
||||
GList *list;
|
||||
} PropertyValue;
|
||||
|
||||
/**
|
||||
* Property structure.
|
||||
*/
|
||||
|
@ -166,31 +196,7 @@ typedef struct Property
|
|||
/** Type of property. */
|
||||
PropertyType type;
|
||||
/** Value */
|
||||
union
|
||||
{
|
||||
/** integer */
|
||||
int i;
|
||||
/** Double */
|
||||
double f;
|
||||
/** String */
|
||||
char *s;
|
||||
/** boolean */
|
||||
gboolean b;
|
||||
/** Color */
|
||||
ThemeColor color;
|
||||
/** Padding */
|
||||
Padding padding;
|
||||
/** Reference */
|
||||
struct
|
||||
{
|
||||
/** Name */
|
||||
char *name;
|
||||
/** Cached looked up ref */
|
||||
struct Property *ref;
|
||||
} link;
|
||||
/** Highlight Style */
|
||||
ThemeHighlight highlight;
|
||||
} value;
|
||||
PropertyValue value;
|
||||
} Property;
|
||||
/**
|
||||
* ThemeWidget.
|
||||
|
@ -445,6 +451,7 @@ ThemeWidget *rofi_theme_find_widget ( const char *name, const char *state, gbool
|
|||
*/
|
||||
Property *rofi_theme_find_property ( ThemeWidget *widget, PropertyType type, const char *property, gboolean exact );
|
||||
|
||||
GList *rofi_theme_get_list ( const widget *widget, const char * property, const char *defaults);
|
||||
/**
|
||||
* Checks if a theme is set, or is empty.
|
||||
* @returns TRUE when empty.
|
||||
|
|
|
@ -211,6 +211,9 @@ S_T_PARENT_RIGHT \)
|
|||
COMMA ,
|
||||
FORWARD_SLASH \/
|
||||
|
||||
LIST_OPEN \[
|
||||
LIST_CLOSE \]
|
||||
|
||||
LS_DASH "dash"
|
||||
LS_SOLID "solid"
|
||||
|
||||
|
@ -220,6 +223,7 @@ CONFIGURATION "configuration"
|
|||
|
||||
%x INCLUDE
|
||||
%x PROPERTIES
|
||||
%x PROPERTIES_LIST
|
||||
%x NAMESTR
|
||||
%x SECTION
|
||||
%x DEFAULTS
|
||||
|
@ -382,7 +386,7 @@ if ( queue == NULL ){
|
|||
|
||||
/* After Namestr/Classstr we want to go to state str, then to { */
|
||||
<INITIAL,SECTION>{WHITESPACE}+ ; // ignore all whitespace
|
||||
<PROPERTIES>{WHITESPACE}+ ; // ignore all whitespace
|
||||
<PROPERTIES,PROPERTIES_LIST>{WHITESPACE}+ ; // ignore all whitespace
|
||||
|
||||
<SECTION>":" { g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); BEGIN(PROPERTIES); return T_PSEP; }
|
||||
<PROPERTIES>";" { BEGIN(GPOINTER_TO_INT ( g_queue_pop_head ( queue ))); return T_PCLOSE;}
|
||||
|
@ -448,7 +452,16 @@ if ( queue == NULL ){
|
|||
/* Fluff */
|
||||
<PROPERTIES>{S_T_PARENT_LEFT} { return T_PARENT_LEFT; }
|
||||
<PROPERTIES>{S_T_PARENT_RIGHT} { return T_PARENT_RIGHT; }
|
||||
<PROPERTIES>{COMMA} { return T_COMMA; }
|
||||
<PROPERTIES,PROPERTIES_LIST>{COMMA} { return T_COMMA; }
|
||||
<PROPERTIES>{LIST_OPEN} {
|
||||
g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) );
|
||||
BEGIN(PROPERTIES_LIST);
|
||||
return T_LIST_OPEN;
|
||||
}
|
||||
<PROPERTIES_LIST>{LIST_CLOSE} {
|
||||
BEGIN(GPOINTER_TO_INT(g_queue_pop_head ( queue )));
|
||||
return T_LIST_CLOSE;
|
||||
}
|
||||
<PROPERTIES>{FORWARD_SLASH} { return T_FORWARD_SLASH; }
|
||||
/* Position */
|
||||
<PROPERTIES>{CENTER} { return T_POS_CENTER; }
|
||||
|
@ -521,7 +534,12 @@ if ( queue == NULL ){
|
|||
<SECTION>. {
|
||||
return T_ERROR_SECTION;
|
||||
}
|
||||
<PROPERTIES>. {
|
||||
<PROPERTIES_LIST>{WORD} {
|
||||
yylval->sval = g_strdup(yytext);
|
||||
return T_ELEMENT;
|
||||
}
|
||||
|
||||
<PROPERTIES,PROPERTIES_LIST>. {
|
||||
return T_ERROR_PROPERTY;
|
||||
}
|
||||
<NAMESTR>. {
|
||||
|
|
|
@ -141,7 +141,7 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b)
|
|||
WindowLocation wloc;
|
||||
ThemeColor colorval;
|
||||
ThemeWidget *theme;
|
||||
GList *name_path;
|
||||
GList *list;
|
||||
Property *property;
|
||||
GHashTable *property_list;
|
||||
Distance distance;
|
||||
|
@ -163,6 +163,7 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b)
|
|||
%token <bval> T_BOOLEAN "Boolean value (true or false)"
|
||||
%token <colorval> T_COLOR "Hexidecimal color value"
|
||||
%token <sval> T_LINK "Reference"
|
||||
%token <sval> T_ELEMENT "Name of element"
|
||||
%token T_POS_CENTER "Center"
|
||||
%token T_POS_EAST "East"
|
||||
%token T_POS_WEST "West"
|
||||
|
@ -198,6 +199,8 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b)
|
|||
%token T_OPTIONAL_COMMA "Optional comma separator (',')"
|
||||
%token T_FORWARD_SLASH "forward slash ('/')"
|
||||
%token T_PERCENT "Percent sign ('%')"
|
||||
%token T_LIST_OPEN "List open ('[')"
|
||||
%token T_LIST_CLOSE "List close (']')"
|
||||
|
||||
%token T_BOPEN "bracket open ('{')"
|
||||
%token T_BCLOSE "bracket close ('}')"
|
||||
|
@ -213,7 +216,7 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b)
|
|||
|
||||
%type <sval> t_entry
|
||||
%type <theme> t_entry_list
|
||||
%type <name_path> t_entry_name_path
|
||||
%type <list> t_entry_name_path
|
||||
%type <property> t_property
|
||||
%type <property_list> t_property_list
|
||||
%type <property_list> t_property_list_optional
|
||||
|
@ -232,6 +235,7 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b)
|
|||
%type <ival> t_property_highlight_styles
|
||||
%type <ival> t_property_highlight_style
|
||||
%type <ival> t_property_line_style
|
||||
%type <list> t_property_element_list
|
||||
%start t_entry_list
|
||||
|
||||
%%
|
||||
|
@ -363,6 +367,19 @@ t_property
|
|||
$$->name = $1;
|
||||
$$->value.color = $3;
|
||||
}
|
||||
| t_property_name T_PSEP T_LIST_OPEN t_property_element_list T_LIST_CLOSE T_PCLOSE {
|
||||
$$ = rofi_theme_property_create ( P_LIST );
|
||||
$$->name = $1;
|
||||
$$->value.list = $4;
|
||||
}
|
||||
;
|
||||
|
||||
/** List of elements */
|
||||
t_property_element_list
|
||||
: T_ELEMENT { $$ = g_list_append ( NULL, $1); }
|
||||
| t_property_element_list T_COMMA T_ELEMENT {
|
||||
$$ = g_list_append ( $1, $3 );
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
|
|
|
@ -591,6 +591,28 @@ Padding rofi_theme_get_padding ( const widget *widget, const char *property, Pad
|
|||
g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property );
|
||||
return pad;
|
||||
}
|
||||
|
||||
GList *rofi_theme_get_list ( const widget *widget, const char * property, const char *defaults )
|
||||
{
|
||||
ThemeWidget *wid2 = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
|
||||
Property *p = rofi_theme_find_property ( wid2, P_LIST, property, TRUE);
|
||||
if ( p ) {
|
||||
if ( p->type == P_LIST ){
|
||||
return g_list_copy_deep ( p->value.list, g_strdup, NULL );
|
||||
}
|
||||
}
|
||||
char **r = defaults?g_strsplit (defaults, ",",0):NULL;
|
||||
if ( r ){
|
||||
GList *l = NULL;
|
||||
for ( int i =0; r[i] != NULL; i++){
|
||||
l = g_list_append(l, r[i]);
|
||||
}
|
||||
g_free(r);
|
||||
return l;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ThemeHighlight rofi_theme_get_highlight ( widget *widget, const char *property, ThemeHighlight th )
|
||||
{
|
||||
ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE );
|
||||
|
|
186
source/view.c
186
source/view.c
|
@ -1578,6 +1578,121 @@ static void rofi_view_listview_mouse_activated_cb ( listview *lv, xcb_button_pre
|
|||
state->skip_absorb = TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void rofi_view_add_widget ( RofiViewState *state, widget *parent_widget, const char *parent, const char *name )
|
||||
{
|
||||
char *defaults = NULL;
|
||||
widget *wid = NULL;
|
||||
char *str= g_strjoin ( "." , parent, name, NULL );
|
||||
char *strbox= g_strjoin ( "." , str, "box",NULL );
|
||||
|
||||
/**
|
||||
* MAINBOX
|
||||
*/
|
||||
if ( strcmp ( name, "mainbox") == 0 ){
|
||||
state->main_box = box_create ( strbox, BOX_VERTICAL );
|
||||
container_add ( (container *)parent_widget, WIDGET ( state->main_box ) );
|
||||
wid = WIDGET ( state->main_box );
|
||||
defaults = "inputbar,message,listview";
|
||||
}
|
||||
/**
|
||||
* INPUTBAR
|
||||
*/
|
||||
else if ( strcmp ( name, "inputbar" ) == 0 ){
|
||||
state->input_bar = box_create ( strbox, BOX_HORIZONTAL );
|
||||
wid = WIDGET( state->input_bar );
|
||||
defaults = "prompt,entry,case-indicator";
|
||||
|
||||
box_add ( (box *)parent_widget, WIDGET ( state->input_bar ), FALSE, 0 );
|
||||
}
|
||||
/**
|
||||
* PROMPT
|
||||
*/
|
||||
else if ( strcmp ( name, "prompt" ) == 0 ){
|
||||
// Prompt box.
|
||||
state->prompt = textbox_create ( str, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "" );
|
||||
rofi_view_update_prompt ( state );
|
||||
box_add ( (box *)parent_widget, WIDGET ( state->prompt ), FALSE, 1 );
|
||||
defaults = NULL;
|
||||
}
|
||||
/**
|
||||
* CASE INDICATOR
|
||||
*/
|
||||
else if ( strcmp ( name, "case-indicator") == 0 ){
|
||||
state->case_indicator = textbox_create ( str, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "*" );
|
||||
// Add small separator between case indicator and text box.
|
||||
box_add ( (box *)parent_widget, WIDGET ( state->case_indicator ), FALSE, 3 );
|
||||
textbox_text ( state->case_indicator, get_matching_state () );
|
||||
}
|
||||
/**
|
||||
* ENTRY BOX
|
||||
*/
|
||||
else if ( strcmp ( name, "entry" ) == 0 ){
|
||||
// Entry box
|
||||
TextboxFlags tfl = TB_EDITABLE;
|
||||
tfl |= ( ( state->menu_flags & MENU_PASSWORD ) == MENU_PASSWORD ) ? TB_PASSWORD : 0;
|
||||
state->text = textbox_create ( str, tfl | TB_AUTOHEIGHT, NORMAL, NULL);
|
||||
box_add ( (box*)parent_widget, WIDGET ( state->text ), TRUE, 2 );
|
||||
}
|
||||
/**
|
||||
* MESSAGE
|
||||
*/
|
||||
else if ( strcmp ( name, "message") == 0 ){
|
||||
char *strmsg= g_strjoin ( "." , str, "textbox",NULL );
|
||||
state->mesg_box = container_create ( strbox );
|
||||
state->mesg_tb = textbox_create ( strmsg, TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, NORMAL, NULL );
|
||||
container_add ( state->mesg_box, WIDGET ( state->mesg_tb ) );
|
||||
rofi_view_reload_message_bar ( state );
|
||||
box_add ( (box*)parent_widget, WIDGET ( state->mesg_box ), FALSE, 2 );
|
||||
g_free(strmsg);
|
||||
}
|
||||
/**
|
||||
* LISTVIEW
|
||||
*/
|
||||
else if ( strcmp ( name, "listview" ) == 0 ) {
|
||||
state->list_view = listview_create ( str, update_callback, state, config.element_height, 0);
|
||||
box_add ( (box*)parent_widget, WIDGET ( state->list_view ), TRUE, 3 );
|
||||
// Set configuration
|
||||
listview_set_multi_select ( state->list_view, ( state->menu_flags & MENU_INDICATOR ) == MENU_INDICATOR );
|
||||
listview_set_scroll_type ( state->list_view, config.scroll_method );
|
||||
listview_set_mouse_activated_cb ( state->list_view, rofi_view_listview_mouse_activated_cb, state );
|
||||
|
||||
int lines = rofi_theme_get_integer ( WIDGET ( state->list_view ), "lines", config.menu_lines );
|
||||
listview_set_num_lines ( state->list_view, lines );
|
||||
listview_set_max_lines ( state->list_view, state->num_lines );
|
||||
}
|
||||
/**
|
||||
* SIDEBAR
|
||||
*/
|
||||
else if ( strcmp( name, "sidebar" ) == 0 ) {
|
||||
state->sidebar_bar = box_create ( strbox, BOX_HORIZONTAL );
|
||||
box_add ( (box*)parent_widget, WIDGET ( state->sidebar_bar ), FALSE, 10 );
|
||||
state->num_modi = rofi_get_num_enabled_modi ();
|
||||
state->modi = g_malloc0 ( state->num_modi * sizeof ( textbox * ) );
|
||||
char *strbutton= g_strjoin ( "." , str, "button",NULL );
|
||||
for ( unsigned int j = 0; j < state->num_modi; j++ ) {
|
||||
const Mode * mode = rofi_get_mode ( j );
|
||||
state->modi[j] = textbox_create ( strbutton, TB_CENTER | TB_AUTOHEIGHT, ( mode == state->sw ) ? HIGHLIGHT : NORMAL,
|
||||
mode_get_display_name ( mode ) );
|
||||
box_add ( state->sidebar_bar, WIDGET ( state->modi[j] ), TRUE, j );
|
||||
widget_set_clicked_handler ( WIDGET ( state->modi[j] ), rofi_view_modi_clicked_cb, state );
|
||||
}
|
||||
g_free(strbutton);
|
||||
}
|
||||
else {
|
||||
g_error("The widget %s does not exists. Invalid layout.", name);
|
||||
}
|
||||
if ( wid ) {
|
||||
GList *list = rofi_theme_get_list ( wid, "children",defaults);
|
||||
for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter )){
|
||||
rofi_view_add_widget ( state, wid, str, (const char *)iter->data );
|
||||
}
|
||||
g_list_free_full ( list, g_free );
|
||||
}
|
||||
g_free(strbox);
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
RofiViewState *rofi_view_create ( Mode *sw,
|
||||
const char *input,
|
||||
MenuFlags menu_flags,
|
||||
|
@ -1605,77 +1720,24 @@ RofiViewState *rofi_view_create ( Mode *sw,
|
|||
// Get active monitor size.
|
||||
TICK_N ( "Get active monitor" );
|
||||
|
||||
|
||||
state->main_window = container_create ( "window.box" );
|
||||
state->main_box = box_create ( "window.mainbox.box", BOX_VERTICAL );
|
||||
container_add ( state->main_window, WIDGET ( state->main_box ) );
|
||||
// Get children.
|
||||
GList *list = rofi_theme_get_list ( WIDGET(state->main_window), "children", "mainbox");
|
||||
for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter )){
|
||||
rofi_view_add_widget ( state, WIDGET(state->main_window), "window", (const char *)iter->data );
|
||||
|
||||
state->input_bar = box_create ( "window.mainbox.inputbar.box", BOX_HORIZONTAL );
|
||||
|
||||
// Only enable widget when sidebar is enabled.
|
||||
if ( config.sidebar_mode ) {
|
||||
state->sidebar_bar = box_create ( "window.mainbox.sidebar.box", BOX_HORIZONTAL );
|
||||
box_add ( state->main_box, WIDGET ( state->sidebar_bar ), FALSE, 10 );
|
||||
state->num_modi = rofi_get_num_enabled_modi ();
|
||||
state->modi = g_malloc0 ( state->num_modi * sizeof ( textbox * ) );
|
||||
for ( unsigned int j = 0; j < state->num_modi; j++ ) {
|
||||
const Mode * mode = rofi_get_mode ( j );
|
||||
state->modi[j] = textbox_create ( "window.mainbox.sidebar.button", TB_CENTER | TB_AUTOHEIGHT, ( mode == state->sw ) ? HIGHLIGHT : NORMAL,
|
||||
mode_get_display_name ( mode ) );
|
||||
box_add ( state->sidebar_bar, WIDGET ( state->modi[j] ), TRUE, j );
|
||||
widget_set_clicked_handler ( WIDGET ( state->modi[j] ), rofi_view_modi_clicked_cb, state );
|
||||
}
|
||||
}
|
||||
g_list_free_full ( list, g_free );
|
||||
|
||||
int location = rofi_theme_get_position ( WIDGET ( state->main_window ), "location", config.location );
|
||||
int end = ( location == WL_SOUTH_EAST || location == WL_SOUTH || location == WL_SOUTH_WEST );
|
||||
box_add ( state->main_box, WIDGET ( state->input_bar ), FALSE, end ? 9 : 0 );
|
||||
|
||||
state->case_indicator = textbox_create ( "window.mainbox.inputbar.case-indicator", TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "*" );
|
||||
// Add small separator between case indicator and text box.
|
||||
box_add ( state->input_bar, WIDGET ( state->case_indicator ), FALSE, 3 );
|
||||
|
||||
// Prompt box.
|
||||
state->prompt = textbox_create ( "window.mainbox.inputbar.prompt", TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "" );
|
||||
rofi_view_update_prompt ( state );
|
||||
box_add ( state->input_bar, WIDGET ( state->prompt ), FALSE, 1 );
|
||||
|
||||
// Entry box
|
||||
TextboxFlags tfl = TB_EDITABLE;
|
||||
tfl |= ( ( menu_flags & MENU_PASSWORD ) == MENU_PASSWORD ) ? TB_PASSWORD : 0;
|
||||
state->text = textbox_create ( "window.mainbox.inputbar.entry", tfl | TB_AUTOHEIGHT, NORMAL, input );
|
||||
|
||||
box_add ( state->input_bar, WIDGET ( state->text ), TRUE, 2 );
|
||||
|
||||
textbox_text ( state->case_indicator, get_matching_state () );
|
||||
state->mesg_box = container_create ( "window.mainbox.message.box" );
|
||||
state->mesg_tb = textbox_create ( "window.mainbox.message.textbox", TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, NORMAL, NULL );
|
||||
container_add ( state->mesg_box, WIDGET ( state->mesg_tb ) );
|
||||
rofi_view_reload_message_bar ( state );
|
||||
box_add ( state->main_box, WIDGET ( state->mesg_box ), FALSE, end ? 8 : 2 );
|
||||
if ( state->text && input) {
|
||||
textbox_text ( state->text, input );
|
||||
}
|
||||
|
||||
state->overlay = textbox_create ( "window.overlay", TB_AUTOWIDTH | TB_AUTOHEIGHT, URGENT, "blaat" );
|
||||
state->overlay->widget.parent = WIDGET ( state->main_window );
|
||||
widget_disable ( WIDGET ( state->overlay ) );
|
||||
|
||||
state->list_view = listview_create ( "window.mainbox.listview", update_callback, state, config.element_height, end );
|
||||
// Set configuration
|
||||
listview_set_multi_select ( state->list_view, ( state->menu_flags & MENU_INDICATOR ) == MENU_INDICATOR );
|
||||
listview_set_scroll_type ( state->list_view, config.scroll_method );
|
||||
listview_set_mouse_activated_cb ( state->list_view, rofi_view_listview_mouse_activated_cb, state );
|
||||
|
||||
int lines = rofi_theme_get_integer ( WIDGET ( state->list_view ), "lines", config.menu_lines );
|
||||
listview_set_num_lines ( state->list_view, lines );
|
||||
listview_set_max_lines ( state->list_view, state->num_lines );
|
||||
|
||||
if ( rofi_theme_get_boolean ( WIDGET ( state->main_window ), "listview-in-inputbar", FALSE)){
|
||||
box_add ( state->input_bar, WIDGET ( state->list_view ), TRUE, 3 );
|
||||
|
||||
Distance d = rofi_theme_get_distance_exact ( WIDGET ( state->text ) , "width", 150);
|
||||
state->text->widget.expand = FALSE;
|
||||
widget_resize ( WIDGET( state->text ), distance_get_pixel ( d, ORIENTATION_HORIZONTAL ), -1);
|
||||
} else {
|
||||
box_add ( state->main_box, WIDGET ( state->list_view ), TRUE, 3 );
|
||||
}
|
||||
|
||||
// filtered list
|
||||
state->line_map = g_malloc0_n ( state->num_lines, sizeof ( unsigned int ) );
|
||||
|
|
|
@ -263,7 +263,7 @@ void box_add ( box *box, widget *child, gboolean expand, int index )
|
|||
child->index = rofi_theme_get_integer_exact ( child, "index", index );
|
||||
child->parent = WIDGET ( box );
|
||||
box->children = g_list_append ( box->children, (void *) child );
|
||||
box->children = g_list_sort ( box->children, box_sort_children );
|
||||
//box->children = g_list_sort ( box->children, box_sort_children );
|
||||
widget_update ( WIDGET ( box ) );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue