2007-01-15 12:09:25 -08:00
|
|
|
/*
|
|
|
|
This file is part of Warzone 2100.
|
|
|
|
Copyright (C) 1999-2004 Eidos Interactive
|
|
|
|
Copyright (C) 2005-2007 Warzone Resurrection Project
|
|
|
|
|
|
|
|
Warzone 2100 is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Warzone 2100 is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Warzone 2100; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
2007-06-28 10:47:08 -07:00
|
|
|
/*
|
|
|
|
* Button.c
|
|
|
|
*
|
|
|
|
* Functions for the button widget
|
|
|
|
*/
|
|
|
|
|
2006-06-02 12:34:58 -07:00
|
|
|
#include "lib/framework/frame.h"
|
|
|
|
#include "lib/framework/frameint.h"
|
2007-06-28 10:47:08 -07:00
|
|
|
#include "widget.h"
|
|
|
|
#include "widgint.h"
|
|
|
|
#include "button.h"
|
|
|
|
#include "form.h"
|
|
|
|
#include "tip.h"
|
2006-06-02 12:34:58 -07:00
|
|
|
// FIXME Direct iVis implementation include!
|
2006-08-11 15:08:48 -07:00
|
|
|
#include "lib/ivis_common/rendmode.h"
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
/* Initialise the button module */
|
|
|
|
BOOL buttonStartUp(void)
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Create a button widget data structure */
|
2008-03-21 07:22:15 -07:00
|
|
|
W_BUTTON* buttonCreate(const W_BUTINIT* psInit)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-03-21 07:22:15 -07:00
|
|
|
W_BUTTON* psWidget;
|
|
|
|
|
|
|
|
if (psInit->style & ~(WBUT_PLAIN | WIDG_HIDDEN | WFORM_NOCLICKMOVE
|
|
|
|
| WBUT_NOPRIMARY | WBUT_SECONDARY | WBUT_TXTCENTRE))
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2007-06-04 13:46:30 -07:00
|
|
|
ASSERT(!"unknown button style", "buttonCreate: unknown button style");
|
2008-03-21 07:22:15 -07:00
|
|
|
return NULL;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the required memory */
|
2008-03-21 07:22:15 -07:00
|
|
|
psWidget = (W_BUTTON *)malloc(sizeof(W_BUTTON));
|
|
|
|
if (psWidget == NULL)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2007-06-04 13:46:30 -07:00
|
|
|
debug(LOG_ERROR, "buttonCreate: Out of memory" );
|
2008-03-21 07:22:15 -07:00
|
|
|
abort();
|
|
|
|
return NULL;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
/* Allocate memory for the text and copy it if necessary */
|
|
|
|
if (psInit->pText)
|
|
|
|
{
|
2008-03-21 07:22:15 -07:00
|
|
|
psWidget->pText = psInit->pText;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-21 07:22:15 -07:00
|
|
|
psWidget->pText = NULL;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
/* Allocate the memory for the tip and copy it if necessary */
|
|
|
|
if (psInit->pTip)
|
|
|
|
{
|
2008-03-21 07:22:15 -07:00
|
|
|
psWidget->pTip = psInit->pTip;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-21 07:22:15 -07:00
|
|
|
psWidget->pTip = NULL;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise the structure */
|
2008-03-21 07:22:15 -07:00
|
|
|
psWidget->type = WIDG_BUTTON;
|
|
|
|
psWidget->id = psInit->id;
|
|
|
|
psWidget->formID = psInit->formID;
|
|
|
|
psWidget->style = psInit->style;
|
|
|
|
psWidget->x = psInit->x;
|
|
|
|
psWidget->y = psInit->y;
|
|
|
|
psWidget->width = psInit->width;
|
|
|
|
psWidget->height = psInit->height;
|
|
|
|
psWidget->callback = psInit->pCallback;
|
|
|
|
psWidget->pUserData = psInit->pUserData;
|
|
|
|
psWidget->UserData = psInit->UserData;
|
|
|
|
psWidget->AudioCallback = WidgGetAudioCallback();
|
|
|
|
psWidget->HilightAudioID = WidgGetHilightAudioID();
|
|
|
|
psWidget->ClickedAudioID = WidgGetClickedAudioID();
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
|
|
|
|
if (psInit->pDisplay)
|
|
|
|
{
|
2008-03-21 07:22:15 -07:00
|
|
|
psWidget->display = psInit->pDisplay;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-21 07:22:15 -07:00
|
|
|
psWidget->display = buttonDisplay;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2008-03-21 07:22:15 -07:00
|
|
|
psWidget->FontID = psInit->FontID;
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2008-03-21 07:22:15 -07:00
|
|
|
buttonInitialise(psWidget);
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2008-03-21 07:22:15 -07:00
|
|
|
return psWidget;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Free the memory used by a button */
|
|
|
|
void buttonFree(W_BUTTON *psWidget)
|
|
|
|
{
|
2007-04-03 06:20:41 -07:00
|
|
|
ASSERT( psWidget != NULL,
|
2006-08-23 05:58:48 -07:00
|
|
|
"buttonFree: invalid button pointer" );
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psWidget);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Initialise a button widget before it is run */
|
|
|
|
void buttonInitialise(W_BUTTON *psWidget)
|
|
|
|
{
|
2007-04-03 06:20:41 -07:00
|
|
|
ASSERT( psWidget != NULL,
|
2006-08-23 05:58:48 -07:00
|
|
|
"buttonDisplay: Invalid widget pointer" );
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
psWidget->state = WBUTS_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Get a button's state */
|
|
|
|
UDWORD buttonGetState(W_BUTTON *psButton)
|
|
|
|
{
|
|
|
|
UDWORD State = 0;
|
|
|
|
|
|
|
|
if (psButton->state & WBUTS_GREY)
|
|
|
|
{
|
|
|
|
State |= WBUT_DISABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (psButton->state & WBUTS_LOCKED)
|
|
|
|
{
|
|
|
|
State |= WBUT_LOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (psButton->state & WBUTS_CLICKLOCK)
|
|
|
|
{
|
|
|
|
State |= WBUT_CLICKLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (psButton->state & WBUTS_FLASH)
|
|
|
|
{
|
|
|
|
State |= WBUT_FLASH;
|
|
|
|
}
|
|
|
|
|
|
|
|
return State;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void buttonSetFlash(W_BUTTON *psButton)
|
|
|
|
{
|
|
|
|
psButton->state |= WBUTS_FLASH;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void buttonClearFlash(W_BUTTON *psButton)
|
|
|
|
{
|
|
|
|
psButton->state &= ~WBUTS_FLASH;
|
|
|
|
psButton->state &= ~WBUTS_FLASHON;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Set a button's state */
|
|
|
|
void buttonSetState(W_BUTTON *psButton, UDWORD state)
|
|
|
|
{
|
2006-08-23 05:58:48 -07:00
|
|
|
ASSERT( !((state & WBUT_LOCK) && (state & WBUT_CLICKLOCK)),
|
|
|
|
"widgSetButtonState: Cannot have WBUT_LOCK and WBUT_CLICKLOCK" );
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
if (state & WBUT_DISABLE)
|
|
|
|
{
|
|
|
|
psButton->state |= WBUTS_GREY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
psButton->state &= ~WBUTS_GREY;
|
|
|
|
}
|
|
|
|
if (state & WBUT_LOCK)
|
|
|
|
{
|
|
|
|
psButton->state |= WBUTS_LOCKED;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
psButton->state &= ~WBUTS_LOCKED;
|
|
|
|
}
|
|
|
|
if (state & WBUT_CLICKLOCK)
|
|
|
|
{
|
|
|
|
psButton->state |= WBUTS_CLICKLOCK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
psButton->state &= ~WBUTS_CLICKLOCK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern UDWORD gameTime2;
|
|
|
|
|
|
|
|
/* Run a button widget */
|
|
|
|
void buttonRun(W_BUTTON *psButton)
|
|
|
|
{
|
|
|
|
// (void)psButton;
|
|
|
|
if(psButton->state & WBUTS_FLASH) {
|
|
|
|
if (((gameTime2/250) % 2) == 0) {
|
|
|
|
psButton->state &= ~WBUTS_FLASHON;
|
|
|
|
} else {
|
|
|
|
psButton->state |= WBUTS_FLASHON;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Respond to a mouse click */
|
|
|
|
void buttonClicked(W_BUTTON *psWidget, UDWORD key)
|
|
|
|
{
|
|
|
|
/* Can't click a button if it is disabled or locked down */
|
|
|
|
if (!(psWidget->state & (WBUTS_GREY | WBUTS_LOCKED)))
|
|
|
|
{
|
|
|
|
// Check this is the correct key
|
|
|
|
if ((!(psWidget->style & WBUT_NOPRIMARY) && key == WKEY_PRIMARY) ||
|
|
|
|
((psWidget->style & WBUT_SECONDARY) && key == WKEY_SECONDARY))
|
|
|
|
{
|
|
|
|
if(psWidget->AudioCallback) {
|
|
|
|
psWidget->AudioCallback(psWidget->ClickedAudioID);
|
|
|
|
}
|
|
|
|
psWidget->state &= ~WBUTS_FLASH; // Stop it flashing
|
|
|
|
psWidget->state &= ~WBUTS_FLASHON;
|
|
|
|
psWidget->state |= WBUTS_DOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Kill the tip if there is one */
|
|
|
|
if (psWidget->pTip)
|
|
|
|
{
|
|
|
|
tipStop((WIDGET *)psWidget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Respond to a mouse button up */
|
2008-03-21 08:40:09 -07:00
|
|
|
void buttonReleased(W_SCREEN* psScreen, W_BUTTON* psWidget, UDWORD key)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
|
|
|
if (psWidget->state & WBUTS_DOWN)
|
|
|
|
{
|
|
|
|
// Check this is the correct key
|
|
|
|
if ((!(psWidget->style & WBUT_NOPRIMARY) && key == WKEY_PRIMARY) ||
|
|
|
|
((psWidget->style & WBUT_SECONDARY) && key == WKEY_SECONDARY))
|
|
|
|
{
|
2008-03-21 08:40:09 -07:00
|
|
|
widgSetReturn(psScreen, (WIDGET *)psWidget);
|
2007-06-28 10:47:08 -07:00
|
|
|
psWidget->state &= ~WBUTS_DOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Respond to a mouse moving over a button */
|
|
|
|
void buttonHiLite(W_BUTTON *psWidget, W_CONTEXT *psContext)
|
|
|
|
{
|
|
|
|
psWidget->state |= WBUTS_HILITE;
|
|
|
|
|
|
|
|
if(psWidget->AudioCallback) {
|
|
|
|
psWidget->AudioCallback(psWidget->HilightAudioID);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there is a tip string start the tool tip */
|
|
|
|
if (psWidget->pTip)
|
|
|
|
{
|
|
|
|
tipStart((WIDGET *)psWidget, psWidget->pTip, psContext->psScreen->TipFontID,
|
|
|
|
psContext->psForm->aColours,
|
|
|
|
psWidget->x + psContext->xOffset, psWidget->y + psContext->yOffset,
|
2007-04-09 07:41:22 -07:00
|
|
|
psWidget->width, psWidget->height);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Respond to the mouse moving off a button */
|
|
|
|
void buttonHiLiteLost(W_BUTTON *psWidget)
|
|
|
|
{
|
|
|
|
psWidget->state &= ~(WBUTS_DOWN | WBUTS_HILITE);
|
|
|
|
if (psWidget->pTip)
|
|
|
|
{
|
|
|
|
tipStop((WIDGET *)psWidget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Display a button */
|
2007-12-09 08:09:23 -08:00
|
|
|
void buttonDisplay(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGHT *pColours)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
|
|
|
W_BUTTON *psButton;
|
|
|
|
SDWORD x0,y0,x1,y1, fx,fy,fw;
|
|
|
|
int CurrFontID;
|
|
|
|
|
2007-04-03 06:20:41 -07:00
|
|
|
ASSERT( psWidget != NULL,
|
2006-08-23 05:58:48 -07:00
|
|
|
"buttonDisplay: Invalid widget pointer" );
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
psButton = (W_BUTTON *)psWidget;
|
|
|
|
CurrFontID = psButton->FontID;
|
|
|
|
|
|
|
|
x0=psButton->x + xOffset;
|
|
|
|
y0=psButton->y + yOffset;
|
|
|
|
x1=x0 + psButton->width;
|
|
|
|
y1=y0 + psButton->height;
|
|
|
|
|
|
|
|
if (psButton->state & (WBUTS_DOWN | WBUTS_LOCKED | WBUTS_CLICKLOCK))
|
|
|
|
{
|
|
|
|
/* Display the button down */
|
2007-12-09 13:40:26 -08:00
|
|
|
pie_BoxFill(x0, y0, x1, y1, pColours[WCOL_BKGRND]);
|
2007-11-26 13:04:42 -08:00
|
|
|
iV_Line(x0,y0, x1,y0, pColours[WCOL_DARK]);
|
|
|
|
iV_Line(x0,y0, x0,y1, pColours[WCOL_DARK]);
|
|
|
|
iV_Line(x0,y1, x1,y1, pColours[WCOL_LIGHT]);
|
|
|
|
iV_Line(x1,y1, x1,y0, pColours[WCOL_LIGHT]);
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
if (psButton->pText)
|
|
|
|
{
|
|
|
|
iV_SetFont(psButton->FontID);
|
2007-12-09 08:09:23 -08:00
|
|
|
iV_SetTextColour(pColours[WCOL_TEXT]);
|
2007-06-28 10:47:08 -07:00
|
|
|
fw = iV_GetTextWidth(psButton->pText);
|
|
|
|
if(psButton->style & WBUT_NOCLICKMOVE) {
|
|
|
|
fx = x0 + (psButton->width - fw) / 2 + 1;
|
|
|
|
fy = y0 + 1 + (psButton->height - iV_GetTextLineSize())/2 - iV_GetTextAboveBase();
|
|
|
|
} else {
|
|
|
|
fx = x0 + (psButton->width - fw) / 2;
|
|
|
|
fy = y0 + (psButton->height - iV_GetTextLineSize())/2 - iV_GetTextAboveBase();
|
|
|
|
}
|
|
|
|
iV_DrawText(psButton->pText,fx,fy);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (psButton->state & WBUTS_HILITE)
|
|
|
|
{
|
|
|
|
/* Display the button hilite */
|
2007-11-26 13:04:42 -08:00
|
|
|
iV_Line(x0+3,y0+3, x1-2,y0+3, pColours[WCOL_HILITE]);
|
|
|
|
iV_Line(x0+3,y0+3, x0+3,y1-2, pColours[WCOL_HILITE]);
|
|
|
|
iV_Line(x0+3,y1-2, x1-2,y1-2, pColours[WCOL_HILITE]);
|
|
|
|
iV_Line(x1-2,y1-2, x1-2,y0+3, pColours[WCOL_HILITE]);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (psButton->state & WBUTS_GREY)
|
|
|
|
{
|
|
|
|
/* Display the disabled button */
|
2007-12-09 13:40:26 -08:00
|
|
|
pie_BoxFill(x0, y0, x1, y1, pColours[WCOL_BKGRND]);
|
2007-11-26 13:04:42 -08:00
|
|
|
iV_Line(x0,y0, x1,y0, pColours[WCOL_LIGHT]);
|
|
|
|
iV_Line(x0,y0, x0,y1, pColours[WCOL_LIGHT]);
|
|
|
|
iV_Line(x0,y1, x1,y1, pColours[WCOL_DARK]);
|
|
|
|
iV_Line(x1,y1, x1,y0, pColours[WCOL_DARK]);
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
if (psButton->pText)
|
|
|
|
{
|
|
|
|
iV_SetFont(psButton->FontID);
|
|
|
|
fw = iV_GetTextWidth(psButton->pText);
|
|
|
|
fx = x0 + (psButton->width - fw) / 2;
|
|
|
|
fy = y0 + (psButton->height - iV_GetTextLineSize())/2 - iV_GetTextAboveBase();
|
2007-12-09 08:09:23 -08:00
|
|
|
iV_SetTextColour(pColours[WCOL_LIGHT]);
|
2007-04-09 07:41:22 -07:00
|
|
|
iV_DrawText(psButton->pText, fx+1, fy+1);
|
2007-12-09 08:09:23 -08:00
|
|
|
iV_SetTextColour(pColours[WCOL_DISABLE]);
|
2007-04-09 07:41:22 -07:00
|
|
|
iV_DrawText(psButton->pText, fx, fy);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (psButton->state & WBUTS_HILITE)
|
|
|
|
{
|
|
|
|
/* Display the button hilite */
|
2007-11-26 13:04:42 -08:00
|
|
|
iV_Line(x0+2,y0+2, x1-3,y0+2, pColours[WCOL_HILITE]);
|
|
|
|
iV_Line(x0+2,y0+2, x0+2,y1-3, pColours[WCOL_HILITE]);
|
|
|
|
iV_Line(x0+2,y1-3, x1-3,y1-3, pColours[WCOL_HILITE]);
|
|
|
|
iV_Line(x1-3,y1-3, x1-3,y0+2, pColours[WCOL_HILITE]);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
2006-06-02 12:34:58 -07:00
|
|
|
else
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
|
|
|
/* Display the button up */
|
2007-12-09 13:40:26 -08:00
|
|
|
pie_BoxFill(x0, y0, x1, y1, pColours[WCOL_BKGRND]);
|
2007-11-26 13:04:42 -08:00
|
|
|
iV_Line(x0,y0, x1,y0, pColours[WCOL_LIGHT]);
|
|
|
|
iV_Line(x0,y0, x0,y1, pColours[WCOL_LIGHT]);
|
|
|
|
iV_Line(x0,y1, x1,y1, pColours[WCOL_DARK]);
|
|
|
|
iV_Line(x1,y1, x1,y0, pColours[WCOL_DARK]);
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
//if (0)
|
|
|
|
if (psButton->pText)
|
|
|
|
{
|
|
|
|
iV_SetFont(psButton->FontID);
|
2007-12-09 08:09:23 -08:00
|
|
|
iV_SetTextColour(pColours[WCOL_TEXT]);
|
2007-06-28 10:47:08 -07:00
|
|
|
fw = iV_GetTextWidth(psButton->pText);
|
|
|
|
fx = x0 + (psButton->width - fw) / 2;
|
|
|
|
fy = y0 + (psButton->height - iV_GetTextLineSize())/2 - iV_GetTextAboveBase();
|
2007-04-09 07:41:22 -07:00
|
|
|
iV_DrawText(psButton->pText, fx, fy);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (psButton->state & WBUTS_HILITE)
|
|
|
|
{
|
|
|
|
/* Display the button hilite */
|
2007-11-26 13:04:42 -08:00
|
|
|
iV_Line(x0+2,y0+2, x1-3,y0+2, pColours[WCOL_HILITE]);
|
|
|
|
iV_Line(x0+2,y0+2, x0+2,y1-3, pColours[WCOL_HILITE]);
|
|
|
|
iV_Line(x0+2,y1-3, x1-3,y1-3, pColours[WCOL_HILITE]);
|
|
|
|
iV_Line(x1-3,y1-3, x1-3,y0+2, pColours[WCOL_HILITE]);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|