mirror of https://github.com/OpenTTD/OpenTTD
(svn r6708) -Feature: [build aircraft window] added sort options to the list
-Fix r6707: solved an issue where scrollcount was not always set correctlyrelease/0.5
parent
b8e1f70751
commit
407d979236
205
aircraft_gui.c
205
aircraft_gui.c
|
@ -24,14 +24,14 @@
|
||||||
#include "vehicle_gui.h"
|
#include "vehicle_gui.h"
|
||||||
#include "newgrf_engine.h"
|
#include "newgrf_engine.h"
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
#include "strings.h"
|
||||||
|
|
||||||
typedef enum BuildAircraftWidgets {
|
typedef enum BuildAircraftWidgets {
|
||||||
BUILD_AIRCRAFT_WIDGET_CLOSEBOX = 0,
|
BUILD_AIRCRAFT_WIDGET_CLOSEBOX = 0,
|
||||||
BUILD_AIRCRAFT_WIDGET_CAPTION,
|
BUILD_AIRCRAFT_WIDGET_CAPTION,
|
||||||
BUILD_AIRCRAFT_WIDGET_SORT_PLACEHOLDER,
|
BUILD_AIRCRAFT_WIDGET_SORT_ASSENDING_DESENTING,
|
||||||
// BUILD_AIRCRAFT_WIDGET_SORT_ASSENDING_DESENTING,
|
BUILD_AIRCRAFT_WIDGET_SORT_TEXT,
|
||||||
// BUILD_AIRCRAFT_WIDGET_SORT_TEXT,
|
BUILD_AIRCRAFT_WIDGET_SORT_DROPDOWN,
|
||||||
// BUILD_AIRCRAFT_WIDGET_SORT_DROPDOWN,
|
|
||||||
BUILD_AIRCRAFT_WIDGET_LIST,
|
BUILD_AIRCRAFT_WIDGET_LIST,
|
||||||
BUILD_AIRCRAFT_WIDGET_SCROLLBAR,
|
BUILD_AIRCRAFT_WIDGET_SCROLLBAR,
|
||||||
BUILD_AIRCRAFT_WIDGET_PANEL,
|
BUILD_AIRCRAFT_WIDGET_PANEL,
|
||||||
|
@ -46,7 +46,9 @@ typedef enum BuildAircraftWidgets {
|
||||||
static const Widget _new_aircraft_widgets[] = {
|
static const Widget _new_aircraft_widgets[] = {
|
||||||
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
|
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
|
||||||
{ WWT_CAPTION, RESIZE_NONE, 14, 11, 239, 0, 13, STR_A005_NEW_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS },
|
{ WWT_CAPTION, RESIZE_NONE, 14, 11, 239, 0, 13, STR_A005_NEW_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS },
|
||||||
{ WWT_IMGBTN, RESIZE_NONE, 14, 0, 239, 14, 25, 0x0, STR_NULL },
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 80, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP},
|
||||||
|
{ WWT_PANEL, RESIZE_NONE, 14, 81, 227, 14, 25, 0x0, STR_SORT_CRITERIA_TIP},
|
||||||
|
{ WWT_TEXTBTN, RESIZE_NONE, 14, 228, 239, 14, 25, STR_0225, STR_SORT_CRITERIA_TIP},
|
||||||
{ WWT_MATRIX, RESIZE_BOTTOM, 14, 0, 227, 26, 121, 0x401, STR_A025_AIRCRAFT_SELECTION_LIST },
|
{ WWT_MATRIX, RESIZE_BOTTOM, 14, 0, 227, 26, 121, 0x401, STR_A025_AIRCRAFT_SELECTION_LIST },
|
||||||
{ WWT_SCROLLBAR, RESIZE_BOTTOM, 14, 228, 239, 26, 121, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST },
|
{ WWT_SCROLLBAR, RESIZE_BOTTOM, 14, 228, 239, 26, 121, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST },
|
||||||
{ WWT_IMGBTN, RESIZE_TB, 14, 0, 239, 122, 193, 0x0, STR_NULL },
|
{ WWT_IMGBTN, RESIZE_TB, 14, 0, 239, 122, 193, 0x0, STR_NULL },
|
||||||
|
@ -61,6 +63,150 @@ static const Widget _new_aircraft_widgets[] = {
|
||||||
{ WIDGETS_END},
|
{ WIDGETS_END},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool _internal_sort_order; // descending/ascending
|
||||||
|
static byte _last_sort_criteria = 0;
|
||||||
|
static bool _last_sort_order = false;
|
||||||
|
|
||||||
|
typedef int CDECL VehicleSortListingTypeFunction(const void*, const void*);
|
||||||
|
|
||||||
|
static int CDECL AircraftEngineNumberSorter(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const EngineID va = *(const EngineID*)a;
|
||||||
|
const EngineID vb = *(const EngineID*)b;
|
||||||
|
int r = va - vb;
|
||||||
|
|
||||||
|
return _internal_sort_order ? -r : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CDECL AircraftEngineCostSorter(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const int va = AircraftVehInfo(*(const EngineID*)a)->base_cost;
|
||||||
|
const int vb = AircraftVehInfo(*(const EngineID*)b)->base_cost;
|
||||||
|
int r = va - vb;
|
||||||
|
|
||||||
|
return _internal_sort_order ? -r : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CDECL AircraftEngineSpeedSorter(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const int va = AircraftVehInfo(*(const EngineID*)a)->max_speed;
|
||||||
|
const int vb = AircraftVehInfo(*(const EngineID*)b)->max_speed;
|
||||||
|
const int r = va - vb;
|
||||||
|
|
||||||
|
if (r == 0) {
|
||||||
|
/* Use EngineID to sort instead since we want consistent sorting */
|
||||||
|
return AircraftEngineNumberSorter(a, b);
|
||||||
|
}
|
||||||
|
return _internal_sort_order ? -r : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CDECL AircraftEngineIntroDateSorter(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const int va = GetEngine(*(const EngineID*)a)->intro_date;
|
||||||
|
const int vb = GetEngine(*(const EngineID*)b)->intro_date;
|
||||||
|
const int r = va - vb;
|
||||||
|
|
||||||
|
if (r == 0) {
|
||||||
|
/* Use EngineID to sort instead since we want consistent sorting */
|
||||||
|
return AircraftEngineNumberSorter(a, b);
|
||||||
|
}
|
||||||
|
return _internal_sort_order ? -r : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EngineID _last_engine; // cached vehicle to hopefully speed up name-sorting
|
||||||
|
|
||||||
|
static char _bufcache[64]; // used together with _last_vehicle to hopefully speed up stringsorting
|
||||||
|
static int CDECL AircraftEngineNameSorter(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const EngineID va = *(const EngineID*)a;
|
||||||
|
const EngineID vb = *(const EngineID*)b;
|
||||||
|
char buf1[64] = "\0";
|
||||||
|
int r;
|
||||||
|
|
||||||
|
SetDParam(0, GetCustomEngineName(va));
|
||||||
|
GetString(buf1, STR_JUST_STRING);
|
||||||
|
|
||||||
|
if (vb != _last_engine) {
|
||||||
|
_last_engine = vb;
|
||||||
|
_bufcache[0] = '\0';
|
||||||
|
|
||||||
|
SetDParam(0, GetCustomEngineName(vb));
|
||||||
|
GetString(_bufcache, STR_JUST_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = strcmp(buf1, _bufcache); // sort by name
|
||||||
|
|
||||||
|
if (r == 0) {
|
||||||
|
/* Use EngineID to sort instead since we want consistent sorting */
|
||||||
|
return AircraftEngineNumberSorter(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (_internal_sort_order & 1) ? -r : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CDECL AircraftEngineRunningCostSorter(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const int va = AircraftVehInfo(*(const EngineID*)a)->running_cost;
|
||||||
|
const int vb = AircraftVehInfo(*(const EngineID*)b)->running_cost;
|
||||||
|
const int r = va - vb;
|
||||||
|
|
||||||
|
if (r == 0) {
|
||||||
|
/* Use EngineID to sort instead since we want consistent sorting */
|
||||||
|
return AircraftEngineNumberSorter(a, b);
|
||||||
|
}
|
||||||
|
return _internal_sort_order ? -r : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CDECL AircraftEngineReliabilitySorter(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const int va = GetEngine(*(const EngineID*)a)->reliability;
|
||||||
|
const int vb = GetEngine(*(const EngineID*)b)->reliability;
|
||||||
|
const int r = va - vb;
|
||||||
|
|
||||||
|
if (r == 0) {
|
||||||
|
/* Use EngineID to sort instead since we want consistent sorting */
|
||||||
|
return AircraftEngineNumberSorter(a, b);
|
||||||
|
}
|
||||||
|
return _internal_sort_order ? -r : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CDECL AircraftEngineCargoSorter(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const int va = AircraftVehInfo(*(const EngineID*)a)->passenger_capacity;
|
||||||
|
const int vb = AircraftVehInfo(*(const EngineID*)b)->passenger_capacity;
|
||||||
|
const int r = va - vb;
|
||||||
|
|
||||||
|
if (r == 0) {
|
||||||
|
/* Use EngineID to sort instead since we want consistent sorting */
|
||||||
|
return AircraftEngineNumberSorter(a, b);
|
||||||
|
}
|
||||||
|
return _internal_sort_order ? -r : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VehicleSortListingTypeFunction* const _engine_sorter[] = {
|
||||||
|
&AircraftEngineNumberSorter,
|
||||||
|
&AircraftEngineCostSorter,
|
||||||
|
&AircraftEngineSpeedSorter,
|
||||||
|
&AircraftEngineIntroDateSorter,
|
||||||
|
&AircraftEngineNameSorter,
|
||||||
|
&AircraftEngineRunningCostSorter,
|
||||||
|
&AircraftEngineReliabilitySorter,
|
||||||
|
&AircraftEngineCargoSorter,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const StringID _engine_sort_listing[] = {
|
||||||
|
STR_ENGINE_SORT_ENGINE_ID,
|
||||||
|
STR_ENGINE_SORT_COST,
|
||||||
|
STR_SORT_BY_MAX_SPEED,
|
||||||
|
STR_ENGINE_SORT_INTRO_DATE,
|
||||||
|
STR_SORT_BY_DROPDOWN_NAME,
|
||||||
|
STR_ENGINE_SORT_RUNNING_COST,
|
||||||
|
STR_SORT_BY_RELIABILITY,
|
||||||
|
STR_ENGINE_SORT_CARGO_CAPACITY,
|
||||||
|
INVALID_STRING_ID
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw the purchase info details of an aircraft at a given location.
|
* Draw the purchase info details of an aircraft at a given location.
|
||||||
* @param x,y location where to draw the info
|
* @param x,y location where to draw the info
|
||||||
|
@ -219,10 +365,15 @@ static inline uint16 GetEngineArrayLength(Window *w)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SortAircraftBuildList(Window *w)
|
||||||
|
{
|
||||||
|
_internal_sort_order = WP(w,buildtrain_d).decenting_sort_order;
|
||||||
|
qsort((void*)GetEngineArray(w), GetEngineArrayLength(w), sizeof(GetEngineArray(w)[0]),
|
||||||
|
_engine_sorter[WP(w,buildtrain_d).sort_criteria]);
|
||||||
|
}
|
||||||
|
|
||||||
static void DrawBuildAircraftWindow(Window *w)
|
static void DrawBuildAircraftWindow(Window *w)
|
||||||
{
|
{
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
SetWindowWidgetLoweredState(w, BUILD_AIRCRAFT_WIDGET_PLANES, WP(w,buildtrain_d).show_engine_button == 1);
|
SetWindowWidgetLoweredState(w, BUILD_AIRCRAFT_WIDGET_PLANES, WP(w,buildtrain_d).show_engine_button == 1);
|
||||||
SetWindowWidgetLoweredState(w, BUILD_AIRCRAFT_WIDGET_JETS, WP(w,buildtrain_d).show_engine_button == 2);
|
SetWindowWidgetLoweredState(w, BUILD_AIRCRAFT_WIDGET_JETS, WP(w,buildtrain_d).show_engine_button == 2);
|
||||||
SetWindowWidgetLoweredState(w, BUILD_AIRCRAFT_WIDGET_HELICOPTERS, WP(w,buildtrain_d).show_engine_button == 3);
|
SetWindowWidgetLoweredState(w, BUILD_AIRCRAFT_WIDGET_HELICOPTERS, WP(w,buildtrain_d).show_engine_button == 3);
|
||||||
|
@ -247,17 +398,9 @@ static void DrawBuildAircraftWindow(Window *w)
|
||||||
}
|
}
|
||||||
if (!found) WP(w,buildtrain_d).sel_engine = INVALID_ENGINE;
|
if (!found) WP(w,buildtrain_d).sel_engine = INVALID_ENGINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (WP(w,buildtrain_d).show_engine_button) {
|
|
||||||
case 1: count = WP(w, buildtrain_d).list_a_length; break;
|
|
||||||
case 2: count = WP(w, buildtrain_d).list_b_length; break;
|
|
||||||
case 3: count = WP(w, buildtrain_d).list_c_length; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetVScrollCount(w, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetVScrollCount(w, GetEngineArrayLength(w));
|
||||||
DrawWindowWidgets(w);
|
DrawWindowWidgets(w);
|
||||||
|
|
||||||
if (WP(w,buildtrain_d).sel_engine == INVALID_ENGINE && GetEngineArrayLength(w) != 0) {
|
if (WP(w,buildtrain_d).sel_engine == INVALID_ENGINE && GetEngineArrayLength(w) != 0) {
|
||||||
|
@ -285,6 +428,8 @@ static void DrawBuildAircraftWindow(Window *w)
|
||||||
DrawAircraftPurchaseInfo(x, w->widget[BUILD_AIRCRAFT_WIDGET_PANEL].top + 1, selected_id);
|
DrawAircraftPurchaseInfo(x, w->widget[BUILD_AIRCRAFT_WIDGET_PANEL].top + 1, selected_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DrawString(85, 15, _engine_sort_listing[WP(w,buildtrain_d).sort_criteria], 0x10);
|
||||||
|
DoDrawString(WP(w,buildtrain_d).decenting_sort_order ? DOWNARROW : UPARROW, 69, 15, 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BuildAircraftClickEvent(Window *w, WindowEvent *e)
|
static void BuildAircraftClickEvent(Window *w, WindowEvent *e)
|
||||||
|
@ -292,6 +437,13 @@ static void BuildAircraftClickEvent(Window *w, WindowEvent *e)
|
||||||
byte click_state = 0;
|
byte click_state = 0;
|
||||||
|
|
||||||
switch (e->we.click.widget) {
|
switch (e->we.click.widget) {
|
||||||
|
case BUILD_AIRCRAFT_WIDGET_SORT_ASSENDING_DESENTING:
|
||||||
|
WP(w,buildtrain_d).decenting_sort_order = !WP(w,buildtrain_d).decenting_sort_order;
|
||||||
|
_last_sort_order = WP(w,buildtrain_d).decenting_sort_order;
|
||||||
|
SortAircraftBuildList(w);
|
||||||
|
SetWindowDirty(w);
|
||||||
|
break;
|
||||||
|
|
||||||
case BUILD_AIRCRAFT_WIDGET_LIST: {
|
case BUILD_AIRCRAFT_WIDGET_LIST: {
|
||||||
uint i = (e->we.click.pt.y - 26) / 24;
|
uint i = (e->we.click.pt.y - 26) / 24;
|
||||||
if (i < w->vscroll.cap) {
|
if (i < w->vscroll.cap) {
|
||||||
|
@ -304,6 +456,10 @@ static void BuildAircraftClickEvent(Window *w, WindowEvent *e)
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case BUILD_AIRCRAFT_WIDGET_SORT_TEXT: case BUILD_AIRCRAFT_WIDGET_SORT_DROPDOWN:/* Select sorting criteria dropdown menu */
|
||||||
|
ShowDropDownMenu(w, _engine_sort_listing, WP(w,buildtrain_d).sort_criteria, BUILD_AIRCRAFT_WIDGET_SORT_DROPDOWN, 0, 0);
|
||||||
|
return;
|
||||||
|
|
||||||
case BUILD_AIRCRAFT_WIDGET_HELICOPTERS: click_state++;
|
case BUILD_AIRCRAFT_WIDGET_HELICOPTERS: click_state++;
|
||||||
case BUILD_AIRCRAFT_WIDGET_JETS: click_state++;
|
case BUILD_AIRCRAFT_WIDGET_JETS: click_state++;
|
||||||
case BUILD_AIRCRAFT_WIDGET_PLANES: click_state++;
|
case BUILD_AIRCRAFT_WIDGET_PLANES: click_state++;
|
||||||
|
@ -313,6 +469,7 @@ static void BuildAircraftClickEvent(Window *w, WindowEvent *e)
|
||||||
WP(w,buildtrain_d).sel_engine = INVALID_ENGINE;
|
WP(w,buildtrain_d).sel_engine = INVALID_ENGINE;
|
||||||
WP(w,buildtrain_d).show_engine_button = click_state;
|
WP(w,buildtrain_d).show_engine_button = click_state;
|
||||||
w->vscroll.pos = 0;
|
w->vscroll.pos = 0;
|
||||||
|
SortAircraftBuildList(w);
|
||||||
SetWindowDirty(w);
|
SetWindowDirty(w);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -343,8 +500,10 @@ static void BuildAircraftWindowCreate(Window *w)
|
||||||
WP(w, buildtrain_d).list_a = NULL;
|
WP(w, buildtrain_d).list_a = NULL;
|
||||||
WP(w, buildtrain_d).list_b = NULL;
|
WP(w, buildtrain_d).list_b = NULL;
|
||||||
WP(w, buildtrain_d).list_c = NULL;
|
WP(w, buildtrain_d).list_c = NULL;
|
||||||
WP(w, buildtrain_d).data_invalidated = false;
|
WP(w, buildtrain_d).data_invalidated = false;
|
||||||
WP(w, buildtrain_d).sel_engine = INVALID_ENGINE;
|
WP(w, buildtrain_d).sel_engine = INVALID_ENGINE;
|
||||||
|
WP(w, buildtrain_d).sort_criteria = _last_sort_criteria;
|
||||||
|
WP(w, buildtrain_d).decenting_sort_order = _last_sort_order;
|
||||||
|
|
||||||
GenerateBuildList(&WP(w, buildtrain_d).list_a, &WP(w, buildtrain_d).list_a_length,
|
GenerateBuildList(&WP(w, buildtrain_d).list_a, &WP(w, buildtrain_d).list_a_length,
|
||||||
&WP(w, buildtrain_d).list_b, &WP(w, buildtrain_d).list_b_length,
|
&WP(w, buildtrain_d).list_b, &WP(w, buildtrain_d).list_b_length,
|
||||||
|
@ -377,6 +536,7 @@ static void BuildAircraftWindowCreate(Window *w)
|
||||||
WP(w, buildtrain_d).show_engine_button = 1;
|
WP(w, buildtrain_d).show_engine_button = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SortAircraftBuildList(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void NewAircraftWndProc(Window *w, WindowEvent *e)
|
static void NewAircraftWndProc(Window *w, WindowEvent *e)
|
||||||
|
@ -412,6 +572,15 @@ static void NewAircraftWndProc(Window *w, WindowEvent *e)
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
|
||||||
|
if (WP(w,buildtrain_d).sort_criteria != e->we.dropdown.index) {
|
||||||
|
WP(w,buildtrain_d).sort_criteria = e->we.dropdown.index;
|
||||||
|
_last_sort_criteria = e->we.dropdown.index;
|
||||||
|
SortAircraftBuildList(w);
|
||||||
|
}
|
||||||
|
SetWindowDirty(w);
|
||||||
|
break;
|
||||||
|
|
||||||
case WE_RESIZE:
|
case WE_RESIZE:
|
||||||
w->vscroll.cap += e->we.sizing.diff.y / 24;
|
w->vscroll.cap += e->we.sizing.diff.y / 24;
|
||||||
w->widget[BUILD_AIRCRAFT_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
|
w->widget[BUILD_AIRCRAFT_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
|
||||||
|
|
|
@ -387,6 +387,7 @@ STR_ENGINE_SORT_POWER :Power
|
||||||
STR_ENGINE_SORT_INTRO_DATE :Introduction Date
|
STR_ENGINE_SORT_INTRO_DATE :Introduction Date
|
||||||
STR_ENGINE_SORT_RUNNING_COST :Running Cost
|
STR_ENGINE_SORT_RUNNING_COST :Running Cost
|
||||||
STR_ENGINE_SORT_POWER_VS_RUNNING_COST :Power/Running Cost
|
STR_ENGINE_SORT_POWER_VS_RUNNING_COST :Power/Running Cost
|
||||||
|
STR_ENGINE_SORT_CARGO_CAPACITY :Cargo Capacity
|
||||||
STR_NO_WAITING_CARGO :{BLACK}No cargo of any type is waiting
|
STR_NO_WAITING_CARGO :{BLACK}No cargo of any type is waiting
|
||||||
STR_SELECT_ALL_FACILITIES :{BLACK}Select all facilities
|
STR_SELECT_ALL_FACILITIES :{BLACK}Select all facilities
|
||||||
STR_SELECT_ALL_TYPES :{BLACK}Select all cargo types (including no waiting cargo)
|
STR_SELECT_ALL_TYPES :{BLACK}Select all cargo types (including no waiting cargo)
|
||||||
|
|
Loading…
Reference in New Issue