// -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- /* * Copyright 1999-2001 by Eric House (xwords@eehouse.org). All rights reserved. * * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "sys.h" #include "gui.h" #include "ebm_object.h" extern "C" { #include "xptypes.h" #include "strutils.h" #include "mempool.h" } #include "frankids.h" #include "frankplayer.h" #include "frankdict.h" #define PLAYERCOUNT_MENU_ID 2000 #define NUMPLAYERS_POPUP_ID 2001 #define NAME_LABEL_ID 2002 #define ROBOT_LABEL_ID 2003 #define PASSWORD_LABEL_ID 2004 #define OK_BUTTON_ID 2005 #define CANCEL_BUTTON_ID 2006 #define REVERT_BUTTON_ID 2007 #define COUNT_LABEL_ID 2008 #define PLAYERDICT_MENU_ID 2009 #define DICTNAMES_POPUP_ID 2010 #define DICT_LABEL_ID 2011 #define SIZE_LABEL_ID 2012 #define BOARDSIZE_MENU_ID 2013 #define BOARDSIZE_POPUP_ID 2014 #define TIMER_CHECKBOX_ID 2015 #define TIMER_FIELD_ID 2016 #define PHONIES_MENU_ID 2017 #define PHONIES_POPUP_ID 2018 #define OK_BUTTON_COL 35 #define CANCEL_BUTTON_COL 95 #define REVERT_BUTTON_COL 125 /* These must be far enough apart that derivitaves will remain unique; make it 10 for now. Also, there can't be any overlap between their ranges and other ids!!!!*/ #define NAME_BASE 2060 #define ROBOT_BASE 2070 #define PASSWORD_BASE 2080 #define COUNTER_ROW 5 #define LABEL_ROW 20 #define FIRST_ROW (LABEL_ROW+20) #define ROW_OFFSET 18 #define DICTMENU_ROW (FIRST_ROW + (ROW_OFFSET * MAX_NUM_PLAYERS) + 2) #define SIZEMENU_ROW (DICTMENU_ROW + ROW_OFFSET + 1) #define PHONIESMENU_ROW (SIZEMENU_ROW + ROW_OFFSET + 1) #define TIMER_ROW (PHONIESMENU_ROW + ROW_OFFSET + 1) #define BUTTON_ROW (TIMER_ROW + ROW_OFFSET + 3) #define NAME_COL 5 #define NAME_WIDTH 105 #define NAME_HEIGHT 12 #define ROBOT_COL (NAME_COL + NAME_WIDTH + 10) #define PASSWD_WIDTH 20 #define TIME_WIDTH 20 #define PASSWD_HEIGHT NAME_HEIGHT #define PASSWORD_COL (ROBOT_COL + PASSWD_WIDTH + 10) #define TIMER_FIELD_COL 120 /* Put up a window with a list for each player giving name and robotness, * and allowing for setting/changing a password. */ CPlayersWindow::CPlayersWindow( MPFORMAL CurGameInfo* gi, FrankDictList* dlist, BOOL isNew, BOOL allowCancel, BOOL* cancelledP ) : CWindow( PLAYERS_WINDOW_ID, 2, 12, 196, 226, "Game setup", !isNew, FALSE, FALSE ) { fLocalGI = *gi; fGIRef = gi; fDList = dlist; this->resultP = cancelledP; fIsNew = isNew; MPASSIGN( this->mpool, mpool ); /* numplayers counter */ CLabel* label = new CLabel( COUNT_LABEL_ID, "Number of players:" ); this->AddChild( label, NAME_COL, COUNTER_ROW ); this->countMenu = new CMenu(PLAYERCOUNT_MENU_ID, 0, 0, 0, 0, 0 ); this->countMenu->SetNumRows( MAX_NUM_PLAYERS ); char* base = (char*)fNumsBuf; for ( U16 i = 0 ; i < MAX_NUM_PLAYERS; ++i ) { snprintf( base, 2, "%d", i+1 ); this->countMenu->SetRow( i, 2000+i, base ); base += 2; } CPopupTrigger *trigger = new CPopupTrigger( NUMPLAYERS_POPUP_ID, 0, 0, this->countMenu, 0 ); trigger->SetCurrentRow( fLocalGI.nPlayers-1 ); this->AddChild( trigger, NAME_COL+130, COUNTER_ROW ); if ( !isNew ) { DisOrEnable( NUMPLAYERS_POPUP_ID, FALSE ); } /* Column labels */ label = new CLabel( NAME_LABEL_ID, "Name" ); this->AddChild( label, NAME_COL, LABEL_ROW ); label = new CLabel( ROBOT_LABEL_ID, "Rbt" ); this->AddChild( label, ROBOT_COL, LABEL_ROW ); label = new CLabel( PASSWORD_LABEL_ID, "Pwd" ); this->AddChild( label, PASSWORD_COL, LABEL_ROW ); /* build a row of controls for each potential player. Disable those below the point determined by the number of players we have. */ for ( U16 i = 0; i < MAX_NUM_PLAYERS; ++i ) { LocalPlayer* fp = &fLocalGI.players[i]; CTextEdit* name = new CTextEdit( NAME_BASE + i, NAME_WIDTH, NAME_HEIGHT, TEXTOPTION_ONELINE ); name->SetText( (char*)fp->name ); this->AddChild( name, NAME_COL, FIRST_ROW + (ROW_OFFSET*i) ); CCheckbox *robot_check = new CCheckbox( ROBOT_BASE + i, 0, 0, "" ); robot_check->SetDownStatus( fp->isRobot ); this->AddChild( robot_check, ROBOT_COL, FIRST_ROW + (ROW_OFFSET*i) ); CTextEdit* passwd = new CTextEdit( PASSWORD_BASE + i, PASSWD_WIDTH, PASSWD_HEIGHT, TEXTOPTION_PASSWORD | TEXTOPTION_ONELINE); this->AddChild( passwd, PASSWORD_COL, FIRST_ROW + (ROW_OFFSET*i) ); const char* password = (const char*)fp->password; if ( !!password && !!password[0] ) { passwd->SetText( password ); } } this->makeDictMenu(); this->makeSizeMenu(); this->makePhoniesMenu(); /* the timer checkbox */ fTimerEnabled = new CCheckbox( TIMER_CHECKBOX_ID, 0, 0, "Timer enabled" ); fTimerEnabled->SetDownStatus( fLocalGI.timerEnabled ); AddChild( fTimerEnabled, NAME_COL, TIMER_ROW ); if ( !isNew ) { fTimerEnabled->Disable(); } /* the timer field (hidden if checkbox not checked) */ fTimerField = new CTextEdit( TIMER_FIELD_ID, TIME_WIDTH, PASSWD_HEIGHT, TEXTOPTION_ONELINE ); char buf[10]; sprintf( buf, "%d", fLocalGI.gameSeconds / 60 ); fTimerField->SetText( buf ); AddChild( fTimerField, TIMER_FIELD_COL, TIMER_ROW ); if ( !fLocalGI.timerEnabled || !isNew ) { fTimerField->Disable(); } /* the buttons at the bottom */ U16 okCol = OK_BUTTON_COL; CButton* button = new CButton( OK_BUTTON_ID, 0, 0, "Ok" ); if ( !(isNew && allowCancel) ) { U16 buttonWidth = button->GetWidth(); U16 windowWidth = this->GetWidth(); okCol = (windowWidth - buttonWidth) / 2; } this->AddChild( button, okCol, BUTTON_ROW ); if ( isNew && allowCancel ) { button = new CButton( CANCEL_BUTTON_ID, 0, 0, "Cancel" ); this->AddChild( button, CANCEL_BUTTON_COL, BUTTON_ROW ); } adjustVisibility(); XP_DEBUGF( "CPlayersWindow done" ); } // CPlayersWindow CPlayersWindow::~CPlayersWindow() { delete( this->countMenu ); delete( this->dictMenu ); delete( this->sizeMenu ); } /* ~CPlayersWindow */ void CPlayersWindow::DisOrEnable( U16 id, BOOL enable ) { CViewable* child = this->GetChildID( id ); if ( enable ) { XP_DEBUGF( "enabling child id=%d\n", id ); child->Enable(); } else { XP_DEBUGF( "disabling child id=%d\n", id ); child->Disable(); } } /* DisOrEnable */ static BOOL checkAllDigits( CTextEdit* te ) { char* text = te->GetText(); char ch; while ( (ch=*text++) != '\0' ) { if ( !isdigit(ch) ) { return false; } } return true; } /* checkAllDigits */ S32 CPlayersWindow::MsgHandler( MSG_TYPE type, CViewable *from, S32 data ) { S32 result = 0; S32 id; U16 row; switch ( type ) { case MSG_MENU_SELECT: /* the num-players trigger */ XP_DEBUGF( "MSG_MENU_SELECT: data=%ld\n", data ); switch ( from->GetID()) { case NUMPLAYERS_POPUP_ID: row = this->countMenu->GetCurrentRow(); fLocalGI.nPlayers = row + 1; /* GetCurrentRow is 0-based */ adjustVisibility(); GUI_NeedUpdate(); result = 1; break; /* case DICTNAMES_POPUP_ID: */ /* row = this->dictMenu->GetCurrentRow(); */ /* break; */ } break; case MSG_TEXT_CHANGED: if ( (from->GetID() == TIMER_FIELD_ID) && !checkAllDigits( (CTextEdit*)from ) ) { result = TEXTEDIT_PLEASE_UNDO; } break; case MSG_BUTTON_SELECT: result = 1; id = from->GetID(); switch ( id ) { case TIMER_CHECKBOX_ID: DisOrEnable( TIMER_FIELD_ID, fTimerEnabled->GetDownStatus() ); break; case OK_BUTTON_ID: for ( U16 i = 0; i < fLocalGI.nPlayers; ++i ) { copyIDString( NAME_BASE+i, &fLocalGI.players[i].name ); copyIDString( PASSWORD_BASE+i, &fLocalGI.players[i].password ); } if ( !!dictMenu ) { fLocalGI.dictName = copyString( MPPARM(mpool) fDList->GetNthName(dictMenu->GetCurrentRow())); } else { fLocalGI.dictName = (XP_UCHAR*)NULL; } if ( fIsNew ) { fLocalGI.boardSize = 15 - this->sizeMenu->GetCurrentRow(); fLocalGI.phoniesAction = fPhoniesMenu->GetCurrentRow(); } fLocalGI.timerEnabled = fTimerEnabled->GetDownStatus(); if ( fLocalGI.timerEnabled ) { char* text = fTimerField->GetText(); fLocalGI.gameSeconds = atoi(text) * 60; } *fGIRef = fLocalGI; /* copy changes to caller */ case CANCEL_BUTTON_ID: *this->resultP = id == CANCEL_BUTTON_ID; this->Close(); result = 1; break; default: /* probably one of our synthetic IDs */ if ( id >= ROBOT_BASE && id < ROBOT_BASE+MAX_NUM_PLAYERS ) { U16 playerNum = id - ROBOT_BASE; BOOL isRobot = ((CButton*)from)->GetDownStatus(); fLocalGI.players[playerNum].isRobot = isRobot; adjustVisibility(); } else if (id >= NAME_BASE && id < NAME_BASE + MAX_NUM_PLAYERS ){ } else { result = 0; } } default: break; } if ( result == 0 ) { result = CWindow::MsgHandler( type, from, data ); } return result; } // CPlayersWindow::MsgHandler /* This will create a dictionary of the dict listed first in the initial.mom * file */ void CPlayersWindow::makeDictMenu() { XP_U16 nDicts = fDList->GetDictCount(); U16 startRow; if ( !!fLocalGI.dictName ) { startRow = fDList->IndexForName( fLocalGI.dictName); } else { startRow = 0; } XP_ASSERT( nDicts > 0 ); CMenu* menu = new CMenu( PLAYERDICT_MENU_ID, 0, 0, 0, 0, 0 ); menu->SetNumRows( nDicts ); for ( U16 i = 0; i < nDicts; ++i ) { menu->SetRow( i, 3000+i, (char*)fDList->GetNthName(i) ); } CPopupTrigger *trigger = new CPopupTrigger( DICTNAMES_POPUP_ID, 0, 0, menu, 0 ); trigger->SetCurrentRow(startRow); menu->SetCurrentRow(startRow); CLabel* label = new CLabel( DICT_LABEL_ID, "Dictnry:" ); this->AddChild( label, NAME_COL, DICTMENU_ROW ); U16 labelWidth = label->GetWidth(); this->AddChild( trigger, NAME_COL+labelWidth+10, DICTMENU_ROW ); if ( !fIsNew ) { DisOrEnable( DICTNAMES_POPUP_ID, FALSE ); } this->dictMenu = menu; } /* CPlayersWindow::makeDictMenu */ void CPlayersWindow::makeSizeMenu() { CMenu* menu = new CMenu( BOARDSIZE_MENU_ID, 0, 0, 0, 0, 0 ); menu->SetNumRows( NUM_SIZES ); for ( U16 i = 0; i < NUM_SIZES; ++i ) { U16 siz = 15-i; snprintf( (char*)this->sizeNames[i], sizeof(this->sizeNames[i]), "%dx%d", siz, siz ); menu->SetRow( i, 4000+i, (char*)this->sizeNames[i] ); } CPopupTrigger* trigger = new CPopupTrigger( BOARDSIZE_POPUP_ID, 0, 0, menu, 0 ); U16 curSize = 15-fLocalGI.boardSize; trigger->SetCurrentRow(curSize); menu->SetCurrentRow(curSize); CLabel* label = new CLabel( SIZE_LABEL_ID, "Board size:" ); this->AddChild( label, NAME_COL, SIZEMENU_ROW ); U16 labelWidth = label->GetWidth(); this->AddChild( trigger, NAME_COL+labelWidth+10, SIZEMENU_ROW ); if ( !fIsNew ) { DisOrEnable( BOARDSIZE_POPUP_ID, FALSE ); } this->sizeMenu = menu; } /* CPlayersWindow::makeSizeMenu */ void CPlayersWindow::adjustVisibility() { /* disable everything greater than the number of players. Before that, disable passwords if robot */ U16 nPlayers = fLocalGI.nPlayers; for ( U16 i = 0; i < MAX_NUM_PLAYERS; ++i ) { XP_Bool disableAll = i >= nPlayers; BOOL enable; /* name */ enable = !disableAll; DisOrEnable( NAME_BASE + i, enable ); /* robot check */ /* enable's the same as above */ DisOrEnable( ROBOT_BASE + i, enable ); /* passwd */ enable = !disableAll && !fLocalGI.players[i].isRobot; DisOrEnable( PASSWORD_BASE + i, enable ); } } /* adjustVisibility */ void CPlayersWindow::makePhoniesMenu() { CMenu* menu = new CMenu( PHONIES_MENU_ID, 0, 0, 0, 0, 0 ); menu->SetNumRows( 3 ); menu->SetRow( 0, 5000, "Ignore" ); menu->SetRow( 1, 5001, "Warn" ); menu->SetRow( 2, 5002, "Disallow" ); CPopupTrigger* trigger = new CPopupTrigger( PHONIES_POPUP_ID, 0, 0, menu, 0 ); XWPhoniesChoice phoniesAction = fLocalGI.phoniesAction; trigger->SetCurrentRow(phoniesAction); menu->SetCurrentRow(phoniesAction); CLabel* label = new CLabel( SIZE_LABEL_ID, "Phonies:" ); this->AddChild( label, NAME_COL, PHONIESMENU_ROW ); U16 labelWidth = label->GetWidth(); this->AddChild( trigger, NAME_COL+labelWidth+10, PHONIESMENU_ROW ); fPhoniesMenu = menu; } /* CPlayersWindow::makePhoniesMenu */ void CPlayersWindow::copyIDString( U16 id, XP_UCHAR** where ) { if ( *where ) { XP_DEBUGF( "freeing string " ); XP_DEBUGF( "%s\n", *where ); XP_FREE( mpool, *where ); XP_DEBUGF( "done freeing string\n" ); } XP_UCHAR* str = (XP_UCHAR*)NULL; CTextEdit* te = (CTextEdit*)this->GetChildID( id ); XP_UCHAR* name = (XP_UCHAR*)te->GetText(); U16 len = te->TextLength(); if ( len > 0 ) { str = (XP_UCHAR*)XP_MALLOC( mpool, len + 1 ); memcpy( str, name, len ); str[len] = '\0'; } *where = str; } /* CPlayersWindow::copyIDString */