mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-12-19 17:55:52 +01:00
509 lines
16 KiB
C++
509 lines
16 KiB
C++
#include <precomp.h>
|
|
#include <wasabicfg.h>
|
|
#include "button.h"
|
|
#include <api/skin/skinparse.h>
|
|
|
|
#include <api/script/scriptmgr.h>
|
|
|
|
#ifdef WASABI_WIDGETS_COMPBUCK
|
|
#include <api/skin/widgets/compbuck2.h>
|
|
#endif
|
|
|
|
#ifdef WASABI_COMPILE_MEDIACORE
|
|
#include <api/core/api_core.h>
|
|
#endif
|
|
|
|
#include <api/service/svcs/svc_action.h>
|
|
|
|
const wchar_t buttonXuiObjectStr[] = L"Button"; // This is the xml tag
|
|
char buttonXuiSvcName[] = "Button xui object"; // this is the name of the xuiservice
|
|
|
|
|
|
ButtonScriptController _buttonController;
|
|
ButtonScriptController *buttonController = &_buttonController;
|
|
|
|
// -- Functions table -------------------------------------
|
|
function_descriptor_struct ButtonScriptController::exportedFunction[] = {
|
|
{L"onActivate", 1, (void*)Wasabi::Button::script_vcpu_onActivate },
|
|
{L"setActivated", 1, (void*)Wasabi::Button::script_vcpu_setActivated },
|
|
{L"getActivated", 0, (void*)Wasabi::Button::script_vcpu_getActivated },
|
|
{L"onLeftClick", 0, (void*)Wasabi::Button::script_vcpu_onLeftClick },
|
|
{L"onRightClick", 0, (void*)Wasabi::Button::script_vcpu_onRightClick },
|
|
{L"leftClick", 0, (void*)Wasabi::Button::script_vcpu_leftClick },
|
|
{L"rightClick", 0, (void*)Wasabi::Button::script_vcpu_rightClick },
|
|
{L"setActivatedNoCallback", 1, (void*)Wasabi::Button::script_vcpu_setActivatedNoCallback },
|
|
|
|
};
|
|
// --------------------------------------------------------
|
|
|
|
const wchar_t *ButtonScriptController::getClassName() {
|
|
return L"Button";
|
|
}
|
|
|
|
const wchar_t *ButtonScriptController::getAncestorClassName() {
|
|
return L"GuiObject";
|
|
}
|
|
|
|
ScriptObject *ButtonScriptController::instantiate() {
|
|
Wasabi::Button *b = new Wasabi::Button;
|
|
ASSERT(b != NULL);
|
|
return b->getScriptObject();
|
|
}
|
|
|
|
void ButtonScriptController::destroy(ScriptObject *o) {
|
|
Wasabi::Button *b = static_cast<Wasabi::Button *>(o->vcpu_getInterface(buttonGuid));
|
|
ASSERT(b != NULL);
|
|
delete b;
|
|
}
|
|
|
|
void *ButtonScriptController::encapsulate(ScriptObject *o) {
|
|
return NULL; // no encapsulation for buttojn yet
|
|
}
|
|
|
|
void ButtonScriptController::deencapsulate(void *o) {
|
|
}
|
|
|
|
int ButtonScriptController::getNumFunctions() {
|
|
return sizeof(exportedFunction) / sizeof(function_descriptor_struct);
|
|
}
|
|
|
|
const function_descriptor_struct *ButtonScriptController::getExportedFunctions() {
|
|
return exportedFunction;
|
|
}
|
|
|
|
GUID ButtonScriptController::getClassGuid() {
|
|
return buttonGuid;
|
|
}
|
|
|
|
XMLParamPair Wasabi::Button::params[] =
|
|
{
|
|
{BUTTON_ACTION, L"ACTION"},
|
|
{BUTTON_ACTIONTARGET, L"ACTION_TARGET"},
|
|
{BUTTON_ACTIVEIMAGE, L"ACTIVEIMAGE"},
|
|
{BUTTON_BORDERS, L"BORDERS"},
|
|
#ifdef WASABI_WIDGETS_COMPBUCK
|
|
{BUTTON_CBTARGET, L"CBTARGET"},
|
|
#endif
|
|
{BUTTON_CENTER_IMAGE, L"CENTER_IMAGE"},
|
|
{BUTTON_DOWNIMAGE, L"DOWNIMAGE"},
|
|
{BUTTON_HOVERIMAGE, L"HOVERIMAGE"},
|
|
{BUTTON_IMAGE, L"IMAGE"},
|
|
{BUTTON_PARAM, L"PARAM"},
|
|
// {BUTTON_RECTRGN, "RECTRGN"},
|
|
{BUTTON_RETCODE, L"RETCODE"},
|
|
{BUTTON_BORDERSTYLE, L"STYLE"},
|
|
{BUTTON_TEXT, L"TEXT"},
|
|
{BUTTON_TEXTCOLOR, L"TEXTCOLOR"},
|
|
{BUTTON_TEXTHOVERCOLOR, L"TEXTDIMMEDCOLOR"},
|
|
{BUTTON_TEXTHOVERCOLOR, L"TEXTHOVERCOLOR"},
|
|
};
|
|
// -----------------------------------------------------------------------
|
|
|
|
Wasabi::Button::Button() {
|
|
disablenextcontextmenu = 0;
|
|
#ifdef WASABI_COMPILE_MEDIACORE
|
|
WASABI_API_MEDIACORE->core_addCallback(0, this);
|
|
#endif
|
|
getScriptObject()->vcpu_setInterface(buttonGuid, (void *)static_cast<Button*>(this));
|
|
getScriptObject()->vcpu_setClassName(L"Button");
|
|
getScriptObject()->vcpu_setController(buttonController);
|
|
cbtarget = NULL;
|
|
setBorders(FALSE);
|
|
xuihandle = newXuiHandle();
|
|
CreateXMLParameters(xuihandle);
|
|
}
|
|
|
|
void Wasabi::Button::CreateXMLParameters(int master_handle)
|
|
{
|
|
//BUTTON_PARENT::CreateXMLParameters(master_handle);
|
|
int numParams = sizeof(params) / sizeof(params[0]);
|
|
hintNumberOfParams(xuihandle, numParams);
|
|
for (int i = 0;i < numParams;i++)
|
|
addParam(xuihandle, params[i], XUI_ATTRIBUTE_IMPLIED);
|
|
}
|
|
|
|
Wasabi::Button::~Button() {
|
|
//int c=getNotifyId();
|
|
#ifdef WASABI_COMPILE_MEDIACORE
|
|
WASABI_API_MEDIACORE->core_delCallback(0, this);
|
|
#endif
|
|
WASABI_API_SYSCB->syscb_deregisterCallback(static_cast<WndCallbackI*>(this));
|
|
}
|
|
|
|
int Wasabi::Button::onInit()
|
|
{
|
|
BUTTON_PARENT::onInit();
|
|
if (!action.isempty())
|
|
setAction(action);
|
|
//FG> this is retarded, and yes, i'm the one who coded it in
|
|
//if (s_normal.isempty() && s_down.isempty() && s_hover.isempty() && s_active.isempty())
|
|
// setRectRgn(1);
|
|
setupBitmaps();
|
|
//int c=getNotifyId();
|
|
setHandleRightClick(TRUE);
|
|
#ifdef WASABI_COMPILE_MEDIACORE
|
|
if (WCSCASEEQLSAFE(actionstr, L"eq_toggle")
|
|
|| WCSCASEEQLSAFE(actionstr, L"eq_auto"))
|
|
{
|
|
if (WCSCASEEQLSAFE(actionstr, L"eq_toggle"))
|
|
corecb_onEQStatusChange(WASABI_API_MEDIACORE->core_getEqStatus(0));
|
|
else corecb_onEQAutoChange(WASABI_API_MEDIACORE->core_getEqAuto(0));
|
|
}
|
|
#endif
|
|
WASABI_API_SYSCB->syscb_registerCallback(static_cast<WndCallbackI*>(this));
|
|
return 1;
|
|
}
|
|
|
|
void Wasabi::Button::setupBitmaps()
|
|
{
|
|
setBitmaps(s_normal, s_down, s_hover, s_active);
|
|
}
|
|
|
|
void Wasabi::Button::setParam(const wchar_t *p)
|
|
{
|
|
param=p;
|
|
}
|
|
|
|
void Wasabi::Button::setAction(const wchar_t *_action)
|
|
{
|
|
actionstr = _action;
|
|
const wchar_t *name;
|
|
int id = SkinParser::getAction(_action, &name);
|
|
actionname = name;
|
|
if (id == ACTION_NONE || !action_target.isempty())
|
|
action = _action;
|
|
else {
|
|
setNotifyId(id);
|
|
action = L"";
|
|
}
|
|
}
|
|
|
|
int Wasabi::Button::setXuiParam(int _xuihandle, int xmlattributeid, const wchar_t *paramname, const wchar_t *strvalue) {
|
|
if (xuihandle == _xuihandle) {
|
|
switch (xmlattributeid) {
|
|
case BUTTON_TEXT: setButtonText(strvalue); return 1;
|
|
case BUTTON_ACTION: action = strvalue; if (isInited()) { setAction(action); } return 1;
|
|
case BUTTON_IMAGE: s_normal = strvalue; if (isInited()) { setBitmaps(s_normal, s_down, s_hover, s_active); } return 1;
|
|
case BUTTON_DOWNIMAGE: s_down = strvalue; if (isInited()) { setBitmaps(s_normal, s_down, s_hover, s_active); } return 1;
|
|
case BUTTON_HOVERIMAGE: s_hover = strvalue; if (isInited()) { setBitmaps(s_normal, s_down, s_hover, s_active); } return 1;
|
|
case BUTTON_ACTIVEIMAGE: s_active = strvalue; if (isInited()) { setBitmaps(s_normal, s_down, s_hover, s_active); }return 1;
|
|
case BUTTON_PARAM: setParam(strvalue); return 1;
|
|
//case BUTTON_RECTRGN: setRectRgn(WTOI(strvalue)); return 1;
|
|
#ifdef WASABI_WIDGETS_COMPBUCK
|
|
case BUTTON_CBTARGET: setCBTarget(strvalue); return 1;
|
|
#endif
|
|
case BUTTON_BORDERS: setBorders(WTOI(strvalue)); return 1;
|
|
case BUTTON_BORDERSTYLE: setBorderStyle(strvalue); return 1;
|
|
case BUTTON_RETCODE: setModalRetCode(WTOI(strvalue)); return 1;
|
|
case BUTTON_ACTIONTARGET: {
|
|
action_target = strvalue;
|
|
if (!actionstr.isempty()) {
|
|
action = actionstr;
|
|
setAction(action);
|
|
}
|
|
return 1;
|
|
}
|
|
case BUTTON_CENTER_IMAGE: setBitmapCenter(WTOI(strvalue)); return 1;
|
|
case BUTTON_TEXTCOLOR: setTextColor(strvalue); return 1;
|
|
case BUTTON_TEXTHOVERCOLOR: setTextHoverColor(strvalue); return 1;
|
|
case BUTTON_TEXTDIMMEDCOLOR: setTextDimmedColor(strvalue); return 1;
|
|
}
|
|
}
|
|
return BUTTON_PARENT::setXuiParam(_xuihandle, xmlattributeid, paramname, strvalue);
|
|
}
|
|
|
|
#ifdef WASABI_WIDGETS_COMPBUCK
|
|
void Wasabi::Button::setCBTarget(const wchar_t *t) {
|
|
cbtarget = ComponentBucket2::getComponentBucket(t);
|
|
}
|
|
#endif
|
|
|
|
const wchar_t *Wasabi::Button::getParam()
|
|
{
|
|
return param;
|
|
}
|
|
|
|
int Wasabi::Button::childNotify(ifc_window *child, int msg, intptr_t param1, intptr_t param2) {
|
|
#ifdef WASABI_COMPILE_WNDMGR
|
|
switch(msg)
|
|
{
|
|
#ifdef _WIN32
|
|
case WM_WA_CONTAINER_TOGGLED:
|
|
if(!param) break;
|
|
if(!WCSICMP((const wchar_t *)param1,param)) setActivatedButton(param2);
|
|
break;
|
|
#else
|
|
#warning port me
|
|
#endif
|
|
#ifdef WASABI_COMPILE_COMPONENTS
|
|
case WM_WA_COMPONENT_TOGGLED:
|
|
if(!param) break;
|
|
GUID *g;
|
|
if(g=SkinParser::getComponentGuid(param)) {
|
|
if(!MEMCMP((GUID *)param1,g,sizeof(GUID))) setActivatedButton(param2);
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
#endif
|
|
return BUTTON_PARENT::childNotify(child, msg, param1, param2);
|
|
}
|
|
|
|
#ifdef WASABI_COMPILE_MEDIACORE
|
|
int Wasabi::Button::corecb_onEQStatusChange(int newval) {
|
|
if(WCSCASEEQLSAFE(actionstr, L"eq_toggle")) setActivatedButton(newval);
|
|
return 0;
|
|
}
|
|
|
|
int Wasabi::Button::corecb_onEQAutoChange(int newval) {
|
|
if(WCSCASEEQLSAFE(actionstr,L"eq_auto")) setActivatedButton(newval);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int Wasabi::Button::onLeftButtonUp(int x, int y) {
|
|
BUTTON_PARENT::onLeftButtonUp(x, y);
|
|
if (!WASABI_API_MAKI->vcpu_getComplete()) {
|
|
#ifdef WASABI_WIDGETS_COMPBUCK
|
|
if (getNotifyId() == ACTION_CB_NEXT) {
|
|
if (cbtarget)
|
|
cbtarget->ComponentBucket2::next_up();
|
|
else
|
|
ComponentBucket2::next_up(getGuiObject()->guiobject_getParentGroup());
|
|
} else if (getNotifyId() == ACTION_CB_PREV) {
|
|
if (cbtarget)
|
|
cbtarget->ComponentBucket2::prev_up();
|
|
else
|
|
ComponentBucket2::prev_up(getGuiObject()->guiobject_getParentGroup());
|
|
} else if (getNotifyId() == ACTION_CB_NEXTPAGE) {
|
|
if (cbtarget)
|
|
cbtarget->ComponentBucket2::next_page();
|
|
else
|
|
ComponentBucket2::next_page(getGuiObject()->guiobject_getParentGroup());
|
|
} else if (getNotifyId() == ACTION_CB_PREVPAGE) {
|
|
if (cbtarget)
|
|
cbtarget->ComponentBucket2::prev_page();
|
|
else
|
|
ComponentBucket2::prev_page(getGuiObject()->guiobject_getParentGroup());
|
|
}
|
|
#endif
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int Wasabi::Button::onLeftButtonDown(int x, int y) {
|
|
BUTTON_PARENT::onLeftButtonDown(x, y);
|
|
if (!WASABI_API_MAKI->vcpu_getComplete()) {
|
|
#ifdef WASABI_WIDGETS_COMPBUCK
|
|
if (getNotifyId() == ACTION_CB_NEXT) {
|
|
if (cbtarget)
|
|
cbtarget->ComponentBucket2::next_down();
|
|
else
|
|
ComponentBucket2::next_down(getGuiObject()->guiobject_getParentGroup());
|
|
} else if (getNotifyId() == ACTION_CB_PREV) {
|
|
if (cbtarget)
|
|
cbtarget->ComponentBucket2::prev_down();
|
|
else
|
|
ComponentBucket2::prev_down(getGuiObject()->guiobject_getParentGroup());
|
|
}
|
|
#endif
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void Wasabi::Button::onLeftPush(int x, int y) {
|
|
BUTTON_PARENT::onLeftPush(x, y);
|
|
script_vcpu_onLeftClick(SCRIPT_CALL, getScriptObject());
|
|
if (!WASABI_API_MAKI->vcpu_getComplete()) {
|
|
if (!action.isempty()) {
|
|
if (!action_target.isempty())
|
|
{
|
|
GuiObject *go = getGuiObject()->guiobject_findObject(action_target);
|
|
if (!go)
|
|
{
|
|
ScriptObject *so = WASABI_API_MAKI->maki_findObject(action_target);
|
|
if (so != NULL)
|
|
go = static_cast<GuiObject *>(so->vcpu_getInterface(guiObjectGuid));
|
|
}
|
|
if (go) {
|
|
ifc_window *w = go->guiobject_getRootWnd();
|
|
if (w) {
|
|
int _x = x;
|
|
int _y = y;
|
|
clientToScreen(&_x, &_y);
|
|
sendAction(w, actionstr, getParam(), _x, _y);
|
|
}
|
|
}
|
|
} else {
|
|
svc_action *act = ActionEnum(actionstr).getNext();
|
|
if (act) {
|
|
int _x = x;
|
|
int _y = y;
|
|
clientToScreen(&_x, &_y);
|
|
act->onAction(actionstr, getParam(), _x, _y, NULL, 0, this);
|
|
SvcEnum::release(act);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int Wasabi::Button::wantAutoContextMenu() {
|
|
int a = disablenextcontextmenu;
|
|
disablenextcontextmenu = 0;
|
|
return !a;
|
|
}
|
|
|
|
void Wasabi::Button::onRightPush(int x, int y) {
|
|
BUTTON_PARENT::onRightPush(x, y);
|
|
script_vcpu_onRightClick(SCRIPT_CALL, getScriptObject());
|
|
if (!WASABI_API_MAKI->vcpu_getComplete()) {
|
|
if (!action.isempty()) {
|
|
if (!action_target.isempty())
|
|
{
|
|
GuiObject *go = getGuiObject()->guiobject_findObject(action_target);
|
|
if (!go) {
|
|
ScriptObject *so = WASABI_API_MAKI->maki_findObject(action_target);
|
|
if (so != NULL)
|
|
go = static_cast<GuiObject *>(so->vcpu_getInterface(guiObjectGuid));
|
|
}
|
|
if (go) {
|
|
ifc_window *w = go->guiobject_getRootWnd();
|
|
if (w) {
|
|
int _x = x;
|
|
int _y = y;
|
|
clientToScreen(&_x, &_y);
|
|
sendAction(w, actionstr, getParam(), _x, _y);
|
|
disablenextcontextmenu = 1;
|
|
}
|
|
}
|
|
} else {
|
|
svc_action *act = ActionEnum(actionstr).getNext();
|
|
if (act) {
|
|
const wchar_t *par = getParam();
|
|
if (par == NULL || *par == 0) par = L"1";
|
|
int _x = x;
|
|
int _y = y;
|
|
clientToScreen(&_x, &_y);
|
|
disablenextcontextmenu = act->onAction(actionstr, par, _x, _y, NULL, 0, this);
|
|
SvcEnum::release(act);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int Wasabi::Button::onActivateButton(int is) {
|
|
BUTTON_PARENT::onActivateButton(is);
|
|
scriptVar _is = SOM::makeVar(SCRIPT_INT);
|
|
SOM::assign(&_is, is);
|
|
script_vcpu_onActivate(SCRIPT_CALL, getScriptObject(), _is);
|
|
return 1;
|
|
}
|
|
|
|
int Wasabi::Button::getPreferences(int what) {
|
|
if (what == SUGGESTED_W) return getWidth();
|
|
if (what == SUGGESTED_H) return getHeight();
|
|
return BUTTON_PARENT::getPreferences(what);
|
|
}
|
|
|
|
int Wasabi::Button::onShowWindow(Container *c, GUID guid, const wchar_t *groupid)
|
|
{
|
|
if(!param) return 1;
|
|
if (groupid != NULL && !WCSICMP(groupid, param))
|
|
{
|
|
setActivatedButton(1);
|
|
return 1;
|
|
}
|
|
#ifdef WASABI_COMPILE_WNDMGR
|
|
GUID *g;
|
|
if (g=SkinParser::getComponentGuid(param))
|
|
{
|
|
if(*g != INVALID_GUID && guid == *g) setActivatedButton(1);
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
int Wasabi::Button::onHideWindow(Container *c, GUID guid, const wchar_t *groupid) {
|
|
if(!param) return 1;
|
|
if (groupid != NULL && !WCSICMP(groupid, param)) {
|
|
setActivatedButton(0);
|
|
return 1;
|
|
}
|
|
#ifdef WASABI_COMPILE_WNDMGR
|
|
GUID *g;
|
|
if (g=SkinParser::getComponentGuid(param)) {
|
|
if(guid == *g) setActivatedButton(0);
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
scriptVar Wasabi::Button::script_vcpu_setActivatedNoCallback(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar v) {
|
|
SCRIPT_FUNCTION_INIT
|
|
ASSERT(SOM::isNumeric(&v));
|
|
Button *b = static_cast<Button *>(o->vcpu_getInterface(buttonGuid));
|
|
if (b) b->setActivatedNoCallback(SOM::makeBoolean(&v));
|
|
RETURN_SCRIPT_VOID;
|
|
}
|
|
|
|
scriptVar Wasabi::Button::script_vcpu_onLeftClick(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
|
|
SCRIPT_FUNCTION_INIT;
|
|
PROCESS_HOOKS0(o, buttonController);
|
|
SCRIPT_FUNCTION_CHECKABORTEVENT;
|
|
SCRIPT_EXEC_EVENT0(o);
|
|
}
|
|
|
|
scriptVar Wasabi::Button::script_vcpu_onRightClick(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
|
|
SCRIPT_FUNCTION_INIT;
|
|
PROCESS_HOOKS0(o, buttonController);
|
|
SCRIPT_FUNCTION_CHECKABORTEVENT;
|
|
SCRIPT_EXEC_EVENT0(o);
|
|
}
|
|
|
|
scriptVar Wasabi::Button::script_vcpu_leftClick(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
|
|
SCRIPT_FUNCTION_INIT;
|
|
RECT r;
|
|
Wasabi::Button *b = static_cast<Button *>(o->vcpu_getInterface(buttonGuid));
|
|
if (b) {
|
|
b->getClientRect(&r);
|
|
b->onLeftPush(r.left, r.top);
|
|
}
|
|
RETURN_SCRIPT_VOID;
|
|
}
|
|
|
|
scriptVar Wasabi::Button::script_vcpu_rightClick(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
|
|
SCRIPT_FUNCTION_INIT;
|
|
RECT r;
|
|
Wasabi::Button *b = static_cast<Button *>(o->vcpu_getInterface(buttonGuid));
|
|
if (b) {
|
|
b->getClientRect(&r);
|
|
b->onRightPush(r.left, r.top);
|
|
}
|
|
RETURN_SCRIPT_VOID;
|
|
}
|
|
|
|
scriptVar Wasabi::Button::script_vcpu_setActivated(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar v) {
|
|
SCRIPT_FUNCTION_INIT
|
|
ASSERT(SOM::isNumeric(&v));
|
|
Wasabi::Button *b = static_cast<Button *>(o->vcpu_getInterface(buttonGuid));
|
|
if (b) b->setActivatedButton(SOM::makeBoolean(&v));
|
|
RETURN_SCRIPT_VOID;
|
|
}
|
|
|
|
scriptVar Wasabi::Button::script_vcpu_getActivated(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
|
|
SCRIPT_FUNCTION_INIT
|
|
Wasabi::Button *b = static_cast<Button *>(o->vcpu_getInterface(buttonGuid));
|
|
if (b) return MAKE_SCRIPT_BOOLEAN(b->getActivatedButton());
|
|
RETURN_SCRIPT_ZERO;
|
|
}
|
|
|
|
scriptVar Wasabi::Button::script_vcpu_onActivate(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar v) {
|
|
SCRIPT_FUNCTION_INIT;
|
|
PROCESS_HOOKS1(o, buttonController, v);
|
|
SCRIPT_FUNCTION_CHECKABORTEVENT;
|
|
SCRIPT_EXEC_EVENT1(o, v);
|
|
}
|
|
|