|
|
|
#include <unistd.h>
|
|
|
|
#include "HighScore.h"
|
|
|
|
#include "HighScore.moc"
|
|
|
|
|
|
|
|
|
|
|
|
#include <tqlabel.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
|
|
#include <tdemessagebox.h>
|
|
|
|
#include "tdelocale.h"
|
|
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <kiconloader.h>
|
|
|
|
#include <tqlineedit.h>
|
|
|
|
#include <tqcombobox.h>
|
|
|
|
#include <tdeapplication.h>
|
|
|
|
#include <kpushbutton.h>
|
|
|
|
#include <kstdguiitem.h>
|
|
|
|
|
|
|
|
static const TQString highScoreMagic1_0 = "kmahjongg-scores-v1.0";
|
|
|
|
static const TQString highScoreMagic1_1 = "kmahjongg-scores-v1.1";
|
|
|
|
|
|
|
|
static const char * highScoreFilename = "/kmahjonggHiscores";
|
|
|
|
|
|
|
|
const char * defNames[numScores] = {
|
|
|
|
"David Black",
|
|
|
|
"Mathias Mueller",
|
|
|
|
"Osvaldo Stark",
|
|
|
|
"Steve Taylor",
|
|
|
|
"Clare Brizzolara",
|
|
|
|
"Angela Simpson",
|
|
|
|
"Michael O'Brien",
|
|
|
|
"Kelvin Bell",
|
|
|
|
"Jenifferi O'Keeffe",
|
|
|
|
"Phil Lamdin"
|
|
|
|
};
|
|
|
|
|
|
|
|
int defScores[numScores] =
|
|
|
|
{400, 350, 300, 250, 200, 150, 100, 50, 20, 10};
|
|
|
|
|
|
|
|
const int ages = 59+(59*60)+(2*60*60);
|
|
|
|
int defTimes[numScores] = {ages, ages-1, ages-2, ages-3,
|
|
|
|
ages-4, ages-5, ages-6, ages-7, ages-8, ages-9};
|
|
|
|
|
|
|
|
|
|
|
|
HighScore::HighScore
|
|
|
|
(
|
|
|
|
TQWidget* parent,
|
|
|
|
const char* name
|
|
|
|
)
|
|
|
|
:
|
|
|
|
TQDialog( parent, name, true, 0 )
|
|
|
|
{
|
|
|
|
|
|
|
|
// form the target name
|
|
|
|
|
|
|
|
|
|
|
|
filename = locateLocal("appdata", highScoreFilename);
|
|
|
|
|
|
|
|
TQFont fnt;
|
|
|
|
// Number
|
|
|
|
TQLabel* qtarch_Label_3;
|
|
|
|
qtarch_Label_3 = new TQLabel( this, "Label_3" );
|
|
|
|
qtarch_Label_3->setGeometry( 10, 45, 30, 30 );
|
|
|
|
qtarch_Label_3->setFrameStyle( 50 );
|
|
|
|
qtarch_Label_3->setText( i18n("Pos") );
|
|
|
|
qtarch_Label_3->setAlignment( AlignCenter );
|
|
|
|
fnt = qtarch_Label_3->font();
|
|
|
|
fnt.setBold(true);
|
|
|
|
qtarch_Label_3->setFont(fnt);
|
|
|
|
|
|
|
|
|
|
|
|
// name
|
|
|
|
|
|
|
|
TQLabel* qtarch_Label_4;
|
|
|
|
qtarch_Label_4 = new TQLabel( this, "Label_4" );
|
|
|
|
qtarch_Label_4->setGeometry( 40, 45, 150, 30 );
|
|
|
|
qtarch_Label_4->setFrameStyle( 50 );
|
|
|
|
qtarch_Label_4->setText( i18n("Name") );
|
|
|
|
qtarch_Label_4->setFont(fnt);
|
|
|
|
|
|
|
|
|
|
|
|
// board number
|
|
|
|
TQLabel* boardTitle;
|
|
|
|
boardTitle= new TQLabel( this, "" );
|
|
|
|
boardTitle->setGeometry( 190, 45, 80, 30 );
|
|
|
|
boardTitle->setFrameStyle( 50 );
|
|
|
|
boardTitle->setText( i18n("Board") );
|
|
|
|
boardTitle->setFont(fnt);
|
|
|
|
|
|
|
|
// score
|
|
|
|
TQLabel* qtarch_Label_5;
|
|
|
|
qtarch_Label_5 = new TQLabel( this, "Label_5" );
|
|
|
|
qtarch_Label_5->setGeometry( 270, 45, 70, 30 );
|
|
|
|
qtarch_Label_5->setFrameStyle( 50 );
|
|
|
|
qtarch_Label_5->setText( i18n("Score") );
|
|
|
|
qtarch_Label_5->setFont(fnt);
|
|
|
|
|
|
|
|
// time
|
|
|
|
TQLabel* qtarch_Label_6;
|
|
|
|
qtarch_Label_6 = new TQLabel( this, "Label_6" );
|
|
|
|
qtarch_Label_6->setGeometry( 340, 45, 70, 30 );
|
|
|
|
qtarch_Label_6->setFrameStyle( 50 );
|
|
|
|
qtarch_Label_6->setText( i18n("Time") );
|
|
|
|
qtarch_Label_6->setFont(fnt);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int row=0; row<numScores; row++)
|
|
|
|
addRow(row);
|
|
|
|
|
|
|
|
TQPushButton* qtarch_PushButton_1;
|
|
|
|
qtarch_PushButton_1 = new KPushButton( KStdGuiItem::ok(), this, "PushButton_1" );
|
|
|
|
qtarch_PushButton_1->setGeometry( 110+35, 340+50, 100, 30 );
|
|
|
|
qtarch_PushButton_1->setMinimumSize( 0, 0 );
|
|
|
|
qtarch_PushButton_1->setMaximumSize( 32767, 32767 );
|
|
|
|
qtarch_PushButton_1->setFocusPolicy( TQWidget::TabFocus );
|
|
|
|
qtarch_PushButton_1->setAutoRepeat( false );
|
|
|
|
qtarch_PushButton_1->setDefault(true);
|
|
|
|
|
|
|
|
TQPushButton* resetBtn;
|
|
|
|
resetBtn= new TQPushButton( this, "resetBtn" );
|
|
|
|
resetBtn->setGeometry( 10, 5, 25, 25);
|
|
|
|
resetBtn->setMinimumSize( 0, 0 );
|
|
|
|
resetBtn->setMaximumSize( 32767, 32767 );
|
|
|
|
resetBtn->setFocusPolicy( TQWidget::TabFocus );
|
|
|
|
//resetBtn->setText(i18n( "Reset" ));
|
|
|
|
resetBtn->setAutoRepeat( false );
|
|
|
|
|
|
|
|
TDEIconLoader *loader = TDEGlobal::iconLoader();
|
|
|
|
resetBtn->setPixmap(loader->loadIcon("edit-delete", TDEIcon::Toolbar));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* We create the ediat area for the hi score name entry and move it */
|
|
|
|
/* off screen. it is moved over and placed in position when a */
|
|
|
|
/* new name is added */
|
|
|
|
|
|
|
|
lineEdit = new TQLineEdit(this, "");
|
|
|
|
lineEdit->setGeometry( 50, 40+(20*30), 190, 30 );
|
|
|
|
lineEdit->setFocusPolicy(TQWidget::StrongFocus);
|
|
|
|
lineEdit->setFrame(true);
|
|
|
|
lineEdit->setEchoMode(TQLineEdit::Normal);
|
|
|
|
lineEdit->setText("");
|
|
|
|
|
|
|
|
// the drop down for the board names
|
|
|
|
|
|
|
|
combo = new TQComboBox( false, this, "combo" );
|
|
|
|
combo->setGeometry( 65, 5, 220, 25 );
|
|
|
|
combo->setMinimumSize( 0, 0 );
|
|
|
|
combo->setMaximumSize( 32767, 32767 );
|
|
|
|
combo->setFocusPolicy( TQWidget::StrongFocus );
|
|
|
|
combo->setSizeLimit( 10 );
|
|
|
|
connect( combo, TQT_SIGNAL(activated(int)), TQT_SLOT(selectionChanged(int)) );
|
|
|
|
|
|
|
|
|
|
|
|
resize( 350+70,390+45 );
|
|
|
|
setFixedSize(350+70,390+45);
|
|
|
|
|
|
|
|
tables = NULL;
|
|
|
|
loadTables();
|
|
|
|
currTable = tables;
|
|
|
|
|
|
|
|
setCaption(kapp->makeStdCaption(i18n("Scores")));
|
|
|
|
|
|
|
|
selectedLine = -1;
|
|
|
|
|
|
|
|
connect(lineEdit, TQT_SIGNAL( textChanged(const TQString &)),
|
|
|
|
TQT_SLOT( nameChanged(const TQString &)));
|
|
|
|
|
|
|
|
|
|
|
|
connect(qtarch_PushButton_1, TQT_SIGNAL(clicked()), TQT_SLOT(reject()));
|
|
|
|
connect(resetBtn, TQT_SIGNAL(clicked()), TQT_SLOT(reset()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// free up the table structures
|
|
|
|
|
|
|
|
HighScore::~HighScore()
|
|
|
|
{
|
|
|
|
TableInstance *t, *t1;
|
|
|
|
|
|
|
|
if (tables != NULL) {
|
|
|
|
t = tables;
|
|
|
|
while(t != NULL)
|
|
|
|
{
|
|
|
|
t1 = t;
|
|
|
|
t = t -> next;
|
|
|
|
delete t1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tables = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// return a pointer to a linked list of table entries from the
|
|
|
|
// saved hi score tables file. If no such file exists then
|
|
|
|
// return a single default hiscore table.
|
|
|
|
|
|
|
|
void HighScore::loadTables() {
|
|
|
|
char buff[1024];
|
|
|
|
|
|
|
|
// open the file, on error set up the default table
|
|
|
|
FILE *fp = fopen( TQFile::encodeName(highScoreFile()), "r");
|
|
|
|
if (fp == NULL)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
// check magic
|
|
|
|
fscanf(fp, "%1023s\n", buff);
|
|
|
|
if (highScoreMagic1_1 != buff) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int num;
|
|
|
|
fscanf(fp, "%d\n", &num);
|
|
|
|
|
|
|
|
for (int p=0; p<num; p++) {
|
|
|
|
TableInstance *t = new TableInstance;
|
|
|
|
t->next = tables;
|
|
|
|
|
|
|
|
|
|
|
|
tables = t;
|
|
|
|
|
|
|
|
fgets(buff, sizeof(buff), fp);
|
|
|
|
if (buff[strlen(buff)-1] == '\n')
|
|
|
|
buff[strlen(buff)-1] = '\0';
|
|
|
|
t->name = buff;
|
|
|
|
combo->insertItem(t->name);
|
|
|
|
setComboTo(t->name);
|
|
|
|
for (int e=0; e<numScores; e++) {
|
|
|
|
fscanf(fp, "%ld\n", &(t->entries[e].score));
|
|
|
|
fscanf(fp, "%ld\n", &(t->entries[e].elapsed));
|
|
|
|
fscanf(fp, "%ld\n", &(t->entries[e].board));
|
|
|
|
fgets(buff, sizeof(buff), fp);
|
|
|
|
if (buff[strlen(buff)-1] == '\n')
|
|
|
|
buff[strlen(buff)-1] = '\0';
|
|
|
|
t->entries[e].name=TQString::fromUtf8(buff,-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
selectTable("default");
|
|
|
|
saveTables();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HighScore::saveTables() {
|
|
|
|
|
|
|
|
TableInstance *p;
|
|
|
|
int num = 0;
|
|
|
|
|
|
|
|
// Nothing to do
|
|
|
|
if (tables == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// open the outrput file, with naff error handling
|
|
|
|
FILE *fp = fopen( TQFile::encodeName(highScoreFile()), "w");
|
|
|
|
if (fp == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// count up the number of tables to save
|
|
|
|
for (p=tables; p != NULL; p = p->next)
|
|
|
|
num++;
|
|
|
|
|
|
|
|
// output the file magic
|
|
|
|
fprintf(fp,"%s\n", highScoreMagic1_1.utf8().data());
|
|
|
|
|
|
|
|
// output the count of tables to process
|
|
|
|
fprintf(fp, "%d\n", num);
|
|
|
|
|
|
|
|
// output each table
|
|
|
|
for (p=tables; p != NULL; p = p->next) {
|
|
|
|
fprintf(fp, "%s\n", p->name.utf8().data());
|
|
|
|
for (int e=0; e<numScores; e++) {
|
|
|
|
fprintf(fp,"%ld\n%ld\n%ld\n%s\n",
|
|
|
|
p->entries[e].score,
|
|
|
|
p->entries[e].elapsed,
|
|
|
|
p->entries[e].board,
|
|
|
|
p->entries[e].name.utf8().data());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// traverse the list of hi score tables and set the
|
|
|
|
// current table to the specified board. Create it if it does not
|
|
|
|
// exist.
|
|
|
|
|
|
|
|
void HighScore::selectTable(const TQString &board) {
|
|
|
|
|
|
|
|
TableInstance *pos = tables;
|
|
|
|
|
|
|
|
|
|
|
|
while (pos != NULL) {
|
|
|
|
if (pos->name == board)
|
|
|
|
break;
|
|
|
|
pos = pos->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pos == NULL) {
|
|
|
|
// not found, add new board to the front of the list
|
|
|
|
TableInstance *n = new TableInstance;
|
|
|
|
n->next = tables;
|
|
|
|
n->name = board;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int p =0; p < numScores; p ++) {
|
|
|
|
n->entries[p].name = defNames[p];
|
|
|
|
n->entries[p].score = defScores[p];
|
|
|
|
n->entries[p].board = 928364243l+(p *3);
|
|
|
|
n->entries[p].elapsed = defTimes[p];
|
|
|
|
}
|
|
|
|
tables = n;
|
|
|
|
currTable = n;
|
|
|
|
combo->insertItem(board);
|
|
|
|
setComboTo(board);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
currTable = pos;
|
|
|
|
setComboTo(board);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void HighScore::addRow(int num) {
|
|
|
|
TQFont tmp;
|
|
|
|
|
|
|
|
// game number
|
|
|
|
numbersWidgets[num] = new TQLabel( this);
|
|
|
|
numbersWidgets[num]->setGeometry( 10, 75+(num*30), 30, 30 );
|
|
|
|
numbersWidgets[num]->setFrameStyle( 50 );
|
|
|
|
numbersWidgets[num]->setAlignment( AlignRight | AlignVCenter );
|
|
|
|
numbersWidgets[num]->setNum(num+1);
|
|
|
|
|
|
|
|
|
|
|
|
// name
|
|
|
|
namesWidgets[num] = new TQLabel( this);
|
|
|
|
namesWidgets[num]->setGeometry( 40, 75+(num*30), 150, 30 );
|
|
|
|
namesWidgets[num]->setFrameStyle( 50 );
|
|
|
|
namesWidgets[num]->setAlignment( 289 );
|
|
|
|
|
|
|
|
// board
|
|
|
|
boardWidgets[num] = new TQLabel( this);
|
|
|
|
boardWidgets[num]->setGeometry( 190, 75+(num*30), 80, 30 );
|
|
|
|
boardWidgets[num]->setFrameStyle( 50 );
|
|
|
|
boardWidgets[num]->setAlignment( 289 );
|
|
|
|
|
|
|
|
// score
|
|
|
|
scoresWidgets[num] = new TQLabel( this);
|
|
|
|
scoresWidgets[num]->setGeometry( 270, 75+(num*30), 70, 30 );
|
|
|
|
scoresWidgets[num]->setFrameStyle( 50 );
|
|
|
|
tmp = scoresWidgets[num]->font();
|
|
|
|
tmp.setItalic(true);
|
|
|
|
scoresWidgets[num]->setFont(tmp);
|
|
|
|
|
|
|
|
// elapsed time
|
|
|
|
elapsedWidgets[num] = new TQLabel( this);
|
|
|
|
elapsedWidgets[num]->setGeometry( 270+70, 75+(num*30), 70, 30 );
|
|
|
|
elapsedWidgets[num]->setFrameStyle( 50 );
|
|
|
|
tmp = elapsedWidgets[num]->font();
|
|
|
|
tmp.setItalic(true);
|
|
|
|
elapsedWidgets[num]->setFont(tmp);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void HighScore::copyTableToScreen(const TQString &name) {
|
|
|
|
char buff[256];
|
|
|
|
TQString base;
|
|
|
|
getBoardName(name, base);
|
|
|
|
selectTable(base);
|
|
|
|
for (int p=0; p<numScores;p++) {
|
|
|
|
scoresWidgets[p]->setNum((int)currTable->entries[p].score);
|
|
|
|
namesWidgets[p]->setText(currTable->entries[p].name);
|
|
|
|
boardWidgets[p]->setNum((int)currTable->entries[p].board);
|
|
|
|
|
|
|
|
int e = currTable->entries[p].elapsed;
|
|
|
|
int s = e % 60;
|
|
|
|
e = e-s;
|
|
|
|
int m = (e % (60*60)) / 60;
|
|
|
|
e = e - (e % (60*60));
|
|
|
|
int h = (e % (60*60*60)) / (60*60);
|
|
|
|
sprintf(buff, "%2.2d:%2.2d:%2.2d", h, m , s);
|
|
|
|
elapsedWidgets[p]->setText(buff);
|
|
|
|
|
|
|
|
}
|
|
|
|
repaint(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
int HighScore::exec(TQString &layout) {
|
|
|
|
copyTableToScreen(layout);
|
|
|
|
return(TQDialog::exec());
|
|
|
|
}
|
|
|
|
|
|
|
|
void HighScore::checkHighScore(int s, int e, long gameNum, TQString &name) {
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
TQString board;
|
|
|
|
getBoardName(name, board);
|
|
|
|
|
|
|
|
// make this board name the current one!
|
|
|
|
// creates it if it does not exist
|
|
|
|
selectTable(board);
|
|
|
|
|
|
|
|
|
|
|
|
for (pos=0; pos <numScores; pos++) {
|
|
|
|
if (s > currTable->entries[pos].score) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pos >= numScores) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int move= numScores-1; move >pos; move--) {
|
|
|
|
currTable->entries[move].score = currTable->entries[move-1].score;
|
|
|
|
currTable->entries[move].name = currTable->entries[move-1].name;
|
|
|
|
currTable->entries[move].board = currTable->entries[move-1].board;
|
|
|
|
currTable->entries[move].elapsed = currTable->entries[move-1].elapsed;
|
|
|
|
}
|
|
|
|
|
|
|
|
currTable->entries[pos].score = s;
|
|
|
|
currTable->entries[pos].board = gameNum;
|
|
|
|
currTable->entries[pos].elapsed = e;
|
|
|
|
|
|
|
|
lineEdit->setEnabled(true);
|
|
|
|
lineEdit->setGeometry( 40, 75+(pos*30), 150, 30 );
|
|
|
|
lineEdit->setFocus();
|
|
|
|
lineEdit->setText("");
|
|
|
|
selectedLine = pos;
|
|
|
|
nameChanged("");
|
|
|
|
|
|
|
|
// no board change when entering data
|
|
|
|
combo->setEnabled(false);
|
|
|
|
exec(board);
|
|
|
|
combo->setEnabled(true);
|
|
|
|
|
|
|
|
selectedLine = -1;
|
|
|
|
lineEdit->setGeometry( 40, 75+(20*30), 150, 30);
|
|
|
|
lineEdit->setEnabled(false);
|
|
|
|
|
|
|
|
// sync the hiscore table to disk now
|
|
|
|
saveTables();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void HighScore::nameChanged(const TQString &s) {
|
|
|
|
|
|
|
|
if (selectedLine == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (s.isEmpty())
|
|
|
|
currTable->entries[selectedLine].name =
|
|
|
|
i18n("Anonymous");
|
|
|
|
else
|
|
|
|
currTable->entries[selectedLine].name = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HighScore::getBoardName(TQString in, TQString &out) {
|
|
|
|
|
|
|
|
TQFileInfo fi( in );
|
|
|
|
out = fi.baseName();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HighScore::setComboTo(const TQString &to) {
|
|
|
|
for (int p=0; p<combo->count(); p++) {
|
|
|
|
if (combo->text(p) == to)
|
|
|
|
combo->setCurrentItem(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HighScore::selectionChanged(int ) {
|
|
|
|
copyTableToScreen(combo->currentText());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// reset the high score table. Caution the user
|
|
|
|
// before acting
|
|
|
|
|
|
|
|
|
|
|
|
void HighScore::reset() {
|
|
|
|
|
|
|
|
int res=KMessageBox::warningContinueCancel(this,
|
|
|
|
i18n("Resetting the high scores will "
|
|
|
|
"remove all high score entries "
|
|
|
|
"both in memory and on disk. Do "
|
|
|
|
"you wish to proceed?"),
|
|
|
|
i18n("Reset High Scores"),i18n("Reset"));
|
|
|
|
if (res != KMessageBox::Continue)
|
|
|
|
return ;
|
|
|
|
|
|
|
|
// delete the file
|
|
|
|
res = unlink( TQFile::encodeName(highScoreFile()));
|
|
|
|
|
|
|
|
// wipe ou the in memory list of tables
|
|
|
|
TableInstance *t, *d;
|
|
|
|
|
|
|
|
if (tables != NULL) {
|
|
|
|
t = tables;
|
|
|
|
while (t != NULL) {
|
|
|
|
d = t;
|
|
|
|
t = t->next;
|
|
|
|
d->next=0;
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the list empty
|
|
|
|
tables = NULL;
|
|
|
|
currTable=NULL;
|
|
|
|
|
|
|
|
// clear out the combobox
|
|
|
|
combo->clear();
|
|
|
|
|
|
|
|
// stick in a default
|
|
|
|
selectTable("default");
|
|
|
|
|
|
|
|
// make sure tha the on screen data does not
|
|
|
|
// point to deleted data
|
|
|
|
copyTableToScreen("default");
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString &HighScore::highScoreFile() {
|
|
|
|
return filename;
|
|
|
|
|
|
|
|
}
|