This is my try for a player search function. I will append my serverbrowser.exe, to see the dialog press CTRL + F. It's at least working on my machine.
The file size has increased drastically, probably because my compiler likes to link statically while microsofts does it dynamically for common libraries. I did not link any more libraries nor include any other files, i just used all the things that were used in the program before so when compiling with the MS compiler the file should again be as small as it used to be.
Screenshot:
Edit: Added a patch for the csv repository 2 posts below.
About the code: I marked everything I edited with "//Edit by Richard" and I will also write down all changes here so it should be easy to apply my changes to other files. One problem is that I use another IDE and another compiler and therefore also needed to do some adaptions to other parts of the code (mostly replace snprintf_s with snprintf), so if anyone wanted to use this he or she probably has to copy the parts I changed manually. The other problem is that I don't know how other people like to organize their source code, so I can't tell for sure where to put declarations and implementations. But I will append my project folder as a zip file so everyone who wants can download the complete source. (Used IDE: CodeBlocks; Compiler: TDM-GCC-64 V4.8 ). Also, I will write down all my changes in this post hoping it will be easy to apply / copy this.
The basic function is (I put this in serverbrowser.cpp, to the end of the file):
//Edit by Richard
//__IN_ sName is the name to search for
//_OUT_ vFound is a vector of a pair containing 1. the key value of the server in g_mServers
// where the player is playing
// and 2. his index in the vPlayers vector
void SearchPlayer(std::string sName, std::vector < std::pair<std::string, int> > * vFound)
{
vFound->clear(); //clear the vector we'll write in
for(int i = 0; sName[i]; i++){
sName[i] = tolower(sName[i]);
}
//make the name we search for lowercase so the search is case insensitive
std::string sPlayerNameCopy;
for (g_mServers_iterator_t iServer = g_mServers.begin(); iServer != g_mServers.end(); iServer++) //loop through all servers
{
for (int iPlayer = 0; iPlayer < iServer->second.vPlayers.size(); iPlayer++) //loop through all players
{
sPlayerNameCopy.assign(iServer->second.vPlayers[iPlayer].sName);
for(int i = 0; sPlayerNameCopy[i]; i++){
sPlayerNameCopy[i] = tolower(sPlayerNameCopy[i]);
}
//also transform the players name to lowercase
if ((sName.length() == 0) or (sPlayerNameCopy.find(sName) != std::string::npos))
//if playername contains the name we're searching for or sName is empty
{
vFound->push_back(std::pair<std::string, int>(iServer->first, iPlayer));
}
}
}
}
this function needs one typedef, I put it in the header of serverbrowser.cpp:
//Edit by Richard
typedef std::map<std::string, serverinfo_t>::iterator g_mServers_iterator_t;
I also declared this function in serverbrowser.h so I can use it in the GUI.cpp:
//Edit by Richard
void SearchPlayer(std::string, std::vector < std::pair<std::string, int> > *);
To implement the GUI I used a simple dialog resource. Therefore, some things need to be defined in resource.h:
//Edit by Richard
#define IDM_SEARCHPLAYER 32778
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif
#define IDD_SEARCHPLAYER 32779
#define IDC_SP_EDIT 32780
#define IDC_SP_LIST 32781
The numbers should be exchangeable as long as they are not used somewhere else in the program.
The dialog itself (I only created it in english until now, so I made it language neutral):
//Edit by Richard
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_SEARCHPLAYER DIALOG 0, 0, 180, 249
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Player Search"
FONT 8, "Ms Shell Dlg"
{
DEFPUSHBUTTON "OK", IDOK, 123, 228, 50, 14
LTEXT "Please enter the player's name:", IDC_STATIC, 7, 7, 132, 8, SS_LEFT
EDITTEXT IDC_SP_EDIT, 7, 20, 166, 14, ES_AUTOHSCROLL
LISTBOX IDC_SP_LIST, 7, 62, 166, 161, WS_TABSTOP | WS_VSCROLL | LBS_NOINTEGRALHEIGHT | LBS_SORT | LBS_NOTIFY
LTEXT "Results: ", IDC_STATIC, 7, 49, 132, 8, SS_LEFT
}
And, of course, the keyboard shortcut needs to be added to the accelerators, I also did this at the top of the file in a languagye unspecific location just for testing:
//Edit by Richard
IDC_SERVERBROWSER ACCELERATORS
BEGIN
"F", IDM_SEARCHPLAYER, VIRTKEY, CONTROL, NOINVERT
END
And, finally, changes to the serverbrowserGUI.cpp:
First, i forward declared a callback function:
EDIT: The mFound_iterator_t typedef is not needed. I used to give back results drom SearchPlayer() in a map but then changed it to a vector where an iterator is not needed anymore!//Edit by Richard
//typedef std::map<std::string, int>::iterator mFound_iterator_t; //not necessary!
static LRESULT CALLBACK SearchPlayerDlg (HWND, UINT, WPARAM, LPARAM);
Then, the message by the accelerator needs to be handeled, so this is put into the "OnCommand" function into the switch:
//Edit by Richard
case IDM_SEARCHPLAYER:
DialogBox(g_hInst, (LPCTSTR)IDD_SEARCHPLAYER, hWnd, (DLGPROC)SearchPlayerDlg);
return TRUE;
And this is the callback function for the dialog:
//Edit by Richard
//Message handler for Player Search Dialog
static LRESULT CALLBACK SearchPlayerDlg (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_SP_EDIT, EN_CHANGE), (LPARAM) GetDlgItem(hDlg, IDC_SP_EDIT));
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
if ((LOWORD(wParam) == IDC_SP_EDIT) and (HIWORD(wParam) == EN_CHANGE))
{
std::string sContentBuffer;
char szNameBuffer[GetWindowTextLength(GetDlgItem(hDlg, IDC_SP_EDIT)) + 1];
std::vector <std::pair<std::string, int> > vFound;
SendMessage(GetDlgItem(hDlg, IDC_SP_LIST), LB_RESETCONTENT, 0, 0);
GetWindowText(GetDlgItem(hDlg, IDC_SP_EDIT), szNameBuffer,
sizeof(szNameBuffer) / sizeof (szNameBuffer[0]));
SearchPlayer(szNameBuffer, &vFound);
for (int i = 0; i < vFound.size(); i++)
{
sContentBuffer.assign(g_mServers[vFound[i].first].vPlayers[vFound[i].second].sName);
sContentBuffer.append (" on ");
sContentBuffer.append (g_mServers[vFound[i].first].sHostName);
int index = SendMessage(GetDlgItem(hDlg, IDC_SP_LIST), LB_ADDSTRING,
0, (LPARAM) sContentBuffer.c_str());
//maybe needed to determine the selected list box item later:
//SendMessage(GetDlgItem(hDlg, IDC_SP_LIST), LB_SETITEMDATA, index, i);
}
return TRUE;
}
break;
}
return FALSE;
}
Hoping that this helps,
Richard