// window_chat.cpp
 
 
 
#include "../common.h"
 
 
 
// WARNING: OLE-enabled RichEdit control needs a C++ compiler!
 
#include <richole.h>
 
 
 
 
 
// window parameters
 
#define WINDOW_CLASSNAME PROGRAM_NAME L" Chat WndClass"
 
#define WINDOW_DEFAULT_WIDTH 520
 
#define WINDOW_DEFAULT_HEIGHT 400
 
#define WINDOW_MIN_WIDTH 480
 
#define WINDOW_MIN_HEIGHT 330
 
 
 
 
 
// local definitions
 
#define WINDOW_TEXT_QUESTION 1
 
#define WINDOW_EDITBOX_DISCUSSION 2
 
#define WINDOW_EDITBOX_MESSAGETOSEND 3
 
#define WINDOW_BITMAP_OPPONENTSTATUS 4
 
#define WINDOW_BUTTON_INVITE 5
 
#define WINDOW_BUTTON_PLAYERINFO 6
 
#define WINDOW_BUTTON_SEND 7
 
#define WINDOW_TEXT_STATUSBAR 8
 
#define WINDOW_TIMER_REFRESH 1
 
 
 
 
 
// RichEdit OLE callback interface structure definition
 
struct RichEditOLECallback : IRichEditOleCallback
 
{
 
   // this structure derives from the IRichEditOleCallback structure, which itself derives
 
   // from the IUnknown structure. So we define IN THIS VERY ORDER the members of the
 
   // IUnknown structure first, THEN the members of the IRichEditOleCallback structure,
 
   // THEN our own members.
 
 
 
   // constructor/destructor
 
   RichEditOLECallback () { reference_count = 0; }
 
   ~RichEditOLECallback () { }
 
 
 
   // members of the IUnknown structure
 
   ULONG CALLBACK AddRef (void) { reference_count++; return (reference_count); }
 
   ULONG CALLBACK Release (void) { reference_count--; return (reference_count); }
 
   HRESULT CALLBACK QueryInterface (REFIID iid, void **ppvObject)
 
   {
 
      if ((iid != IID_IUnknown) && (iid != IID_IRichEditOleCallback))
 
         return (E_NOINTERFACE);
 
 
 
      *ppvObject = this;
 
      reference_count++;
 
      return (S_OK);
 
   }
 
 
 
   // members of the IRichEditOleCallback structure
 
   HRESULT CALLBACK ContextSensitiveHelp (BOOL fEnterMode) { return (E_NOTIMPL); }
 
   HRESULT CALLBACK DeleteObject (IOleObject *lpoleobj) { return (E_NOTIMPL); }
 
   HRESULT CALLBACK GetClipboardData (CHARRANGE *lpchrg, DWORD reco, IDataObject **lplpdataobj) { return (E_NOTIMPL); }
 
   HRESULT CALLBACK GetContextMenu (WORD seltype, IOleObject *lpoleobj, CHARRANGE *lpchrg, HMENU *lphmenu) { return (E_NOTIMPL); }
 
   HRESULT CALLBACK GetDragDropEffect (BOOL fDrag, DWORD grfKeyState, DWORD *pdwEffect) { return (E_NOTIMPL); }
 
   HRESULT CALLBACK GetInPlaceContext (IOleInPlaceFrame **lplpFrame, IOleInPlaceUIWindow **lplpDoc, tagOIFI *lpFrameInfo) { return (E_NOTIMPL); }
 
   HRESULT CALLBACK GetNewStorage (IStorage **storage)
 
   {
 
      // initialize a storage object in memory
 
 
 
      ILockBytes *lock_bytes;
 
      HRESULT ret;
 
 
 
      if ((ret = CreateILockBytesOnHGlobal (NULL, TRUE, &lock_bytes)) != S_OK)
 
         return (ret);
 
      if ((ret = StgCreateDocfileOnILockBytes (lock_bytes, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, storage)) != S_OK)
 
         lock_bytes->Release ();
 
      return (ret);
 
   }
 
   HRESULT CALLBACK QueryAcceptData (IDataObject *lpdataobj, CLIPFORMAT *lpcfFormat, DWORD reco, BOOL fReally, HANDLE hMetaPict) { return (E_NOTIMPL); }
 
   HRESULT CALLBACK QueryInsertObject (LPCLSID lpclsid, IStorage *storage, LONG cp) { return (S_OK); }
 
   HRESULT CALLBACK ShowContainerUI (BOOL fShow) { return (E_NOTIMPL); }
 
 
 
   // our own data
 
   ULONG reference_count;
 
};
 
 
 
 
 
// global variables used in this module only
 
static bool is_classregistered = false;
 
static PROC WindowProc_MessageToSend_DefaultProc;
 
static RichEditOLECallback *richedit_oleinterface = NULL;
 
 
 
 
 
// prototypes of local functions
 
static LRESULT CALLBACK WindowProc_ThisWindow (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam);
 
static LRESULT CALLBACK WindowProc_MessageToSend (HWND hDlg, unsigned int message, WPARAM wParam, LPARAM lParam);
 
 
 
 
 
void Window_Chat (int interlocutor_index)
 
{
 
   // helper function to fire up the child window
 
 
 
   WNDCLASSEX wc;
 
   player_t *network_player;
 
 
 
   // if the REOLECallback class instance hasn't been instantiated yet, do it
 
   if (richedit_oleinterface == NULL)
 
      richedit_oleinterface = new RichEditOLECallback;
 
 
 
   network_player = Player_FindByType (PLAYER_INTERNET); // quick access to network player
 
   if (network_player == NULL)
 
      return; // consistency check
 
 
 
   if (lastonlineplayers_time + 5.0f < current_time)
 
      Player_SendBuffer_Add (network_player, 1000, L"who\n"); // request a players list update
 
 
 
   if (!interlocutors[interlocutor_index].is_active)
 
      return; // consistency check
 
 
 
   // is the window class NOT registered yet ?
 
   if (!is_classregistered)
 
   {
 
      // if so, register the window class once and for all
 
      memset (&wc, 0, sizeof (wc));
 
      wc.cbSize = sizeof (wc);
 
      wc.style = CS_HREDRAW | CS_VREDRAW;
 
      wc.lpfnWndProc = WindowProc_ThisWindow;
 
      wc.hInstance = hAppInstance;
 
      wc.hIcon = LoadIcon (hAppInstance, (wchar_t *) ICON_MAIN);
 
      wc.hCursor = LoadCursor (NULL, IDC_ARROW);
 
      wc.hbrBackground = GetSysColorBrush (COLOR_3DFACE);
 
      wc.lpszClassName = WINDOW_CLASSNAME;
 
      RegisterClassEx (&wc);
 
 
 
      is_classregistered = true; // remember this window class is registered
 
   }
 
 
 
   // create the window as an application-level window (not child)
 
   interlocutors[interlocutor_index].hWnd = CreateWindowEx (WS_EX_CLIENTEDGE, WINDOW_CLASSNAME, L"", WS_OVERLAPPEDWINDOW,
 
                                                            CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_DEFAULT_WIDTH, WINDOW_DEFAULT_HEIGHT,
 
                                                            NULL, NULL, hAppInstance, NULL);
 
   interlocutors[interlocutor_index].update_dialog = true; // and update this interlocutor's dialog
 
 
 
   return; // finished, the window is fired up
 
}
 
 
 
 
 
void Window_Chat_Validated (void)
 
{
 
   // callback function called by the main game thread when the window is validated
 
 
 
   // remember this callback is no longer to be called
 
   is_window_chat_validated = false;
 
 
 
   return; // finished
 
}
 
 
 
 
 
static LRESULT CALLBACK WindowProc_ThisWindow (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
 
{
 
   // message handler for the child window
 
 
 
   unsigned short wParam_hiword;
 
   unsigned short wParam_loword;
 
   wchar_t temp_string[256];
 
   wchar_t *text_buffer;
 
   char *cstring_buffer;
 
   interlocutor_t *interlocutor;
 
   onlineplayer_t *onlineplayer;
 
   challenge_t *challenge;
 
   player_t *remote_player;
 
   player_t *local_player;
 
   MINMAXINFO *minmax;
 
   FLASHWINFO flashinfo;
 
   RECT client_rect;
 
   HWND hEditWnd;
 
   int display_picture;
 
   int player_index;
 
   int char_index;
 
   int length;
 
   bool is_invitable;
 
 
 
   // filter out the commonly used message values
 
   wParam_hiword = HIWORD (wParam);
 
   wParam_loword = LOWORD (wParam);
 
 
 
   // have we just fired up this window ?
 
   if (message == WM_CREATE)
 
   {
 
      // center the window
 
      CenterWindow (hWnd, hMainWnd);
 
 
 
      // populate the window
 
      CreateWindowEx (0, L"static", L"",
 
                      WS_CHILD | WS_VISIBLE,
 
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_QUESTION, hAppInstance, NULL);
 
 
 
      LoadLibrary (L"riched20.dll"); // ensure the RichEdit controls DLL is loaded
 
      hEditWnd = CreateWindowEx (0, L"RichEdit20A", L"", // for some reason, only the ANSI version of this control accepts RTF data :(
 
                                 WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | ES_SELECTIONBAR | ES_SUNKEN, 
 
                                 0, 0, 0, 0, hWnd, (HMENU) WINDOW_EDITBOX_DISCUSSION, hAppInstance, NULL);
 
      SendMessage (hEditWnd, EM_SETOLECALLBACK, 0, (LPARAM) richedit_oleinterface);
 
 
 
      hEditWnd = CreateWindowEx (WS_EX_CLIENTEDGE, L"edit", L"", 
 
                                 WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL, 
 
                                 0, 0, 0, 0, hWnd, (HMENU) WINDOW_EDITBOX_MESSAGETOSEND, hAppInstance, NULL);
 
      WindowProc_MessageToSend_DefaultProc = (PROC) GetWindowLong (hEditWnd, GWL_WNDPROC); // save this edit box's window proc
 
      SetWindowLong (hEditWnd, GWL_WNDPROC, (long) WindowProc_MessageToSend); // replace this edit box's window proc by ours
 
 
 
      CreateWindowEx (0, L"static", L"",
 
                      WS_CHILD | WS_VISIBLE | SS_BITMAP | SS_SUNKEN,
 
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_BITMAP_OPPONENTSTATUS, hAppInstance, NULL);
 
      CreateWindowEx (0, L"button", L"",
 
                      WS_CHILD | WS_VISIBLE | WS_DISABLED, // initially disabled
 
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_BUTTON_INVITE, hAppInstance, NULL);
 
      CreateWindowEx (0, L"button", L"",
 
                      WS_CHILD | WS_VISIBLE | WS_DISABLED, // initially disabled
 
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_BUTTON_PLAYERINFO, hAppInstance, NULL);
 
      CreateWindowEx (0, L"button", L"",
 
                      WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
 
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_BUTTON_SEND, hAppInstance, NULL);
 
      CreateWindowEx (WS_EX_RIGHT, L"static", L"",
 
                      WS_CHILD | WS_VISIBLE,
 
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_STATUSBAR, hAppInstance, NULL);
 
 
 
      // set the chat text font
 
      SendMessage (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), WM_SETFONT, (WPARAM) hFontChat, false);
 
 
 
      // associate the default GUI font with the leading caption
 
      SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_QUESTION), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
 
 
 
      // associate the default GUI font with the invite button and set its text
 
      SendMessage (GetDlgItem (hWnd, WINDOW_BUTTON_INVITE), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
 
      SetDlgItemText (hWnd, WINDOW_BUTTON_INVITE, LOCALIZE (L"Chat_Invite"));
 
 
 
      // associate the default GUI font with the player info button and set its text
 
      SendMessage (GetDlgItem (hWnd, WINDOW_BUTTON_PLAYERINFO), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
 
      SetDlgItemText (hWnd, WINDOW_BUTTON_PLAYERINFO, LOCALIZE (L"Chat_GetInfo"));
 
 
 
      // associate the default GUI font with the send button and set its text
 
      SendMessage (GetDlgItem (hWnd, WINDOW_BUTTON_SEND), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
 
      SetDlgItemText (hWnd, WINDOW_BUTTON_SEND, LOCALIZE (L"Button_Send"));
 
 
 
      // associate the default GUI font with the status bar and set its text
 
      SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
 
      SetDlgItemText (hWnd, WINDOW_TEXT_STATUSBAR, LOCALIZE (L"Chat_StatusBar"));
 
 
 
      // refresh the server message area every second
 
      SetTimer (hWnd, WINDOW_TIMER_REFRESH, 1000, NULL);
 
      SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, 0); // but call it now
 
 
 
      // convert the status bar message to a hyperlink
 
      ConvertStaticToHyperlink (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR));
 
 
 
      // now show the window
 
      ShowWindow (hWnd, SW_SHOW);
 
   }
 
 
 
   // else did we click the close button on the title bar ?
 
   else if (message == WM_CLOSE)
 
      DestroyWindow (hWnd); // close the window
 
 
 
   // else are we destroying this window ?
 
   else if (message == WM_DESTROY)
 
   {
 
      KillTimer (hWnd, WINDOW_TIMER_REFRESH); // destroy the timer we used to refresh the dialog text
 
 
 
      interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
 
      if (interlocutor != NULL)
 
         interlocutor->is_active = false; // remember interlocutor has gone away
 
 
 
      is_window_chat_validated = true;
 
      the_board.reevaluate = true; // refresh the GUI buttons if needed
 
   }
 
 
 
   // else are we resizing the window ?
 
   else if (message == WM_SIZE)
 
   {
 
      // get the new window size
 
      GetClientRect (hWnd, &client_rect);
 
 
 
      // position the window elements
 
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_QUESTION), NULL, 16, 16, client_rect.right - 148, 16, SWP_NOZORDER);
 
      SetWindowPos (GetDlgItem (hWnd, WINDOW_EDITBOX_DISCUSSION), NULL, 16, 40, client_rect.right - 148, client_rect.bottom - 136, SWP_NOZORDER);
 
      SetWindowPos (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), NULL, 16, client_rect.bottom - 80, client_rect.right - 148, 64, SWP_NOZORDER);
 
      SetWindowPos (GetDlgItem (hWnd, WINDOW_BITMAP_OPPONENTSTATUS), NULL, client_rect.right - 116, 16, 100, 100, SWP_NOZORDER);
 
      SetWindowPos (GetDlgItem (hWnd, WINDOW_BUTTON_INVITE), NULL, client_rect.right - 116, 124, 100, 32, SWP_NOZORDER);
 
      SetWindowPos (GetDlgItem (hWnd, WINDOW_BUTTON_PLAYERINFO), NULL, client_rect.right - 116, 162, 100, 32, SWP_NOZORDER);
 
      SetWindowPos (GetDlgItem (hWnd, WINDOW_BUTTON_SEND), NULL, client_rect.right - 116, client_rect.bottom - 80, 100, 64, SWP_NOZORDER);
 
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR), NULL, 0, client_rect.bottom - 16, client_rect.right, 16, SWP_NOZORDER);
 
   }
 
 
 
   // else are we asking how big/small we can resize ?
 
   else if (message == WM_GETMINMAXINFO)
 
   {
 
      minmax = (MINMAXINFO *) lParam; // get a pointer to the min/max info structure
 
 
 
      minmax->ptMinTrackSize.x = WINDOW_MIN_WIDTH;
 
      minmax->ptMinTrackSize.y = WINDOW_MIN_HEIGHT;
 
   }
 
 
 
   // else did we take action on one of the controls ?
 
   else if (message == WM_COMMAND)
 
   {
 
      // was it the "invite" button ?
 
      if (wParam_loword == WINDOW_BUTTON_INVITE)
 
      {
 
         interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
 
         if (interlocutor != NULL)
 
         {
 
            // player is available for an invitation. Is he already inviting us ?
 
            challenge = Challenge_Find (interlocutor->nickname);
 
            if ((challenge != NULL) && challenge->is_active && IsWindow (challenge->hWnd))
 
               EndDialog (challenge->hWnd, 0); // if so, close the challenge dialog box (this will also make us decline it properly)
 
 
 
            DialogBox_SendChallenge (interlocutor->nickname); // fire up the dialog box
 
         }
 
      }
 
 
 
      // else did we click the "get player info" button ?
 
      else if (wParam_loword == WINDOW_BUTTON_PLAYERINFO)
 
      {
 
         interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
 
         if (interlocutor != NULL)
 
            PlayerCard_FindOrCreate (interlocutor->nickname); // fire up the dialog box
 
      }
 
 
 
      // else was it the "send" button ?
 
      else if (wParam_loword == WINDOW_BUTTON_SEND)
 
      {
 
         // grab the text to send from the edit box and empty the box
 
         length = SendMessage (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), WM_GETTEXTLENGTH, 0, 0); // get text length
 
         text_buffer = (wchar_t *) SAFE_malloc (length + 1, sizeof (wchar_t), false); // allocate space
 
         GetDlgItemText (hWnd, WINDOW_EDITBOX_MESSAGETOSEND, text_buffer, length + 1); // copy text
 
 
 
         // is there actually something to send ?
 
         if (text_buffer[0] != 0)
 
         {
 
            // drop all accents by converting to 7-bit US-ASCII and then back to wide char
 
            cstring_buffer = (char *) SAFE_malloc (2 * (length + 1), sizeof (char), false);
 
            ConvertTo7BitASCII (cstring_buffer, 2 * (length + 1), text_buffer);
 
            ConvertToWideChar (text_buffer, length + 1, cstring_buffer);
 
            SAFE_free ((void **) &cstring_buffer);
 
 
 
            // convert some dangerous characters
 
            length = wcslen (text_buffer);
 
            for (char_index = 0; char_index < length; char_index++)
 
               if ((text_buffer[char_index] == L'\r') || (text_buffer[char_index] == L'\n'))
 
                  text_buffer[char_index] = L' ';
 
               else if (text_buffer[char_index] == L'%')
 
                  text_buffer[char_index] = L'ยค'; // percent signs are nasty for the varargs, so remove them
 
 
 
            // find the local and the network players
 
            if (((remote_player = Player_FindByType (PLAYER_INTERNET)) != NULL)
 
                && ((local_player = Player_FindByType (PLAYER_HUMAN)) != NULL))
 
            {
 
               interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
 
               if (interlocutor != NULL)
 
               {
 
                  // fill the send buffer and append chat text to interlocutor's chat window
 
                  Player_SendBuffer_Add (remote_player, 1000, L"tell %s %s\n", interlocutor->nickname, text_buffer);
 
                  Interlocutor_Chat (interlocutor, local_player->name, true, text_buffer);
 
               }
 
            }
 
         }
 
 
 
         SetDlgItemText (hWnd, WINDOW_EDITBOX_MESSAGETOSEND, L""); // reset text in the window
 
         SAFE_free ((void **) &text_buffer); // free the memory space we used
 
      }
 
 
 
      // else was it the status bar hyperlink ?
 
      else if (wParam_loword == WINDOW_TEXT_STATUSBAR)
 
         ShellExecute (NULL, L"open", PROGRAM_URL, NULL, NULL, SW_MAXIMIZE); // open the donation page in the default browser, maximized
 
   }
 
 
 
   // else is it a timer event AND is it our refresh timer ?
 
   else if ((message == WM_TIMER) && (wParam == WINDOW_TIMER_REFRESH))
 
   {
 
      interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
 
      if (interlocutor != NULL)
 
      {
 
         // find interlocutor in online players list
 
         for (player_index = 0; player_index < onlineplayer_count; player_index++)
 
            if (wcscmp (interlocutor->nickname, onlineplayers[player_index].nickname) == 0)
 
               break; // break as soon as we find it
 
 
 
         // have we found it ?
 
         if (player_index < onlineplayer_count)
 
         {
 
            onlineplayer = &onlineplayers[player_index]; // quick access to online player
 
            remote_player = Player_FindByType (PLAYER_INTERNET); // find the remote player
 
 
 
            // is this talkee our opponent AND are we playing a game AND does its status not reflect it yet ?
 
            if ((remote_player != NULL) && remote_player->is_in_game
 
                && (wcscmp (onlineplayer->nickname, remote_player->name) == 0)
 
                && ((onlineplayer->handlestatus == HANDLESTATUS_UNDEFINED)
 
                    || (onlineplayer->handlestatus == HANDLESTATUS_AVAILABLE)
 
                    || (onlineplayer->handlestatus == HANDLESTATUS_EXAMININGAGAME)
 
                    || (onlineplayer->handlestatus == HANDLESTATUS_NOTOPENFORAMATCH)
 
                    || (onlineplayer->handlestatus == HANDLESTATUS_INACTIVEORBUSY)))
 
               onlineplayer->handlestatus = HANDLESTATUS_INGAME; // if so, remember we're playing against it
 
 
 
            display_picture = onlineplayer->handlestatus; // if so, use its current status
 
         }
 
         else
 
            display_picture = HANDLESTATUS_OFFLINE; // else flag it as offline
 
 
 
         // do we need to change its status ?
 
         if (display_picture != interlocutor->current_displaypicture)
 
         {
 
            // set window icons (small one for title bar & big one for task manager) and avatar
 
            SendMessage (hWnd, WM_SETICON, ICON_SMALL, (LPARAM) handlestatus[display_picture].icon);
 
            SendMessage (hWnd, WM_SETICON, ICON_BIG, (LPARAM) handlestatus[display_picture].icon);
 
            SendMessage (GetDlgItem (hWnd, WINDOW_BITMAP_OPPONENTSTATUS), STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) handlestatus[display_picture].bitmap);
 
 
 
            // set window text
 
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"%s (%s)", interlocutor->nickname, handlestatus[display_picture].text);
 
            SetWindowText (hWnd, temp_string); // set window title
 
 
 
            // set window title and parameters
 
            if (display_picture == HANDLESTATUS_OFFLINE)
 
            {
 
               EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_PLAYERINFO), false); // if so, disable buttons
 
               EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_INVITE), false);
 
               EnableWindow (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), false);
 
               EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_SEND), false);
 
            }
 
            else
 
            {
 
               if ((display_picture == HANDLESTATUS_OFFLINE) || (display_picture == HANDLESTATUS_INGAME) || (display_picture == HANDLESTATUS_NOTOPENFORAMATCH))
 
                  is_invitable = false; // this player is NOT invitable
 
               else
 
                  is_invitable = true; // all other players are invitable
 
 
 
               EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_PLAYERINFO), true); // else enable them
 
               EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_INVITE), is_invitable);
 
               EnableWindow (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), true);
 
               EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_SEND), true);
 
 
 
               if (GetForegroundWindow () == hWnd)
 
                  SetFocus (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND)); // set keyboard focus to the message editbox
 
            }
 
 
 
            interlocutor->current_displaypicture = display_picture; // remember we've done it
 
         }
 
 
 
         // do we need to update dialog ?
 
         if (interlocutor->update_dialog)
 
         {
 
            // set the leading caption
 
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), LOCALIZE (L"Chat_Question"), interlocutor->nickname);
 
            SetDlgItemText (hWnd, WINDOW_TEXT_QUESTION, temp_string);
 
 
 
            // set discussion text and scroll the edit box down
 
            hEditWnd = GetDlgItem (hWnd, WINDOW_EDITBOX_DISCUSSION);
 
            SetWindowText (hEditWnd, interlocutor->dialogtext);
 
            length = GetWindowTextLength (hEditWnd);
 
            //SetFocus (hEditWnd);
 
            Edit_SetSel (hEditWnd, length, length);
 
            Edit_ScrollCaret (hEditWnd);
 
            SetFocus (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND));
 
 
 
            // if we are not the top level window, flash window
 
            if (GetForegroundWindow () != hWnd)
 
            {
 
               memset (&flashinfo, 0, sizeof (flashinfo));
 
               flashinfo.cbSize = sizeof (flashinfo);
 
               flashinfo.hwnd = hWnd;
 
               flashinfo.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG; // flash until we bring window to foreground
 
               FlashWindowEx (&flashinfo);
 
            }
 
 
 
            interlocutor->update_dialog = false; // remember we refreshed dialog text
 
         }
 
      }
 
   }
 
 
 
   // call the default dialog message processing function to keep things going
 
   return (DefWindowProc (hWnd, message, wParam, lParam));
 
}
 
 
 
 
 
static LRESULT CALLBACK WindowProc_MessageToSend (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
 
{
 
   // this is the callback function for the edit box control where the user types its message
 
 
 
   // was the enter key pressed ?
 
   if ((message == WM_CHAR) && (wParam == VK_RETURN))
 
   {
 
      SendMessage (GetParent (hWnd), WM_COMMAND, WINDOW_BUTTON_SEND, 0); // if so, forward it to our parent
 
      return (true); // and prevent this keystroke from being treated
 
   }
 
 
 
   // for all other messages, call the default window message processing function
 
   return ((LRESULT) CallWindowProc ((WNDPROC) WindowProc_MessageToSend_DefaultProc, hWnd, message, wParam, lParam));
 
}