Subversion Repositories Games.Chess Giants

Rev

Rev 21 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. // window_chat.cpp
  2.  
  3. #include "../common.h"
  4.  
  5. // WARNING: OLE-enabled RichEdit control needs a C++ compiler!
  6. #include <richole.h>
  7.  
  8.  
  9. // window parameters
  10. #define WINDOW_CLASSNAME PROGRAM_NAME L" Chat WndClass"
  11. #define WINDOW_DEFAULT_WIDTH 520
  12. #define WINDOW_DEFAULT_HEIGHT 400
  13. #define WINDOW_MIN_WIDTH 480
  14. #define WINDOW_MIN_HEIGHT 330
  15.  
  16.  
  17. // local definitions
  18. #define WINDOW_TEXT_QUESTION 1
  19. #define WINDOW_EDITBOX_DISCUSSION 2
  20. #define WINDOW_EDITBOX_MESSAGETOSEND 3
  21. #define WINDOW_BITMAP_OPPONENTSTATUS 4
  22. #define WINDOW_BUTTON_INVITE 5
  23. #define WINDOW_BUTTON_PLAYERINFO 6
  24. #define WINDOW_BUTTON_SEND 7
  25. #define WINDOW_TEXT_STATUSBAR 8
  26. #define WINDOW_TIMER_REFRESH 1
  27.  
  28.  
  29. // RichEdit OLE callback interface structure definition
  30. struct RichEditOLECallback : IRichEditOleCallback
  31. {
  32.    // this structure derives from the IRichEditOleCallback structure, which itself derives
  33.    // from the IUnknown structure. So we define IN THIS VERY ORDER the members of the
  34.    // IUnknown structure first, THEN the members of the IRichEditOleCallback structure,
  35.    // THEN our own members.
  36.  
  37.    // constructor/destructor
  38.    RichEditOLECallback () { reference_count = 0; }
  39.    ~RichEditOLECallback () { }
  40.  
  41.    // members of the IUnknown structure
  42.    ULONG CALLBACK AddRef (void) { reference_count++; return (reference_count); }
  43.    ULONG CALLBACK Release (void) { reference_count--; return (reference_count); }
  44.    HRESULT CALLBACK QueryInterface (REFIID iid, void **ppvObject)
  45.    {
  46.       if ((iid != IID_IUnknown) && (iid != IID_IRichEditOleCallback))
  47.          return (E_NOINTERFACE);
  48.  
  49.       *ppvObject = this;
  50.       reference_count++;
  51.       return (S_OK);
  52.    }
  53.  
  54.    // members of the IRichEditOleCallback structure
  55.    HRESULT CALLBACK ContextSensitiveHelp (BOOL fEnterMode) { return (E_NOTIMPL); }
  56.    HRESULT CALLBACK DeleteObject (IOleObject *lpoleobj) { return (E_NOTIMPL); }
  57.    HRESULT CALLBACK GetClipboardData (CHARRANGE *lpchrg, DWORD reco, IDataObject **lplpdataobj) { return (E_NOTIMPL); }
  58.    HRESULT CALLBACK GetContextMenu (WORD seltype, IOleObject *lpoleobj, CHARRANGE *lpchrg, HMENU *lphmenu) { return (E_NOTIMPL); }
  59.    HRESULT CALLBACK GetDragDropEffect (BOOL fDrag, DWORD grfKeyState, DWORD *pdwEffect) { return (E_NOTIMPL); }
  60.    HRESULT CALLBACK GetInPlaceContext (IOleInPlaceFrame **lplpFrame, IOleInPlaceUIWindow **lplpDoc, tagOIFI *lpFrameInfo) { return (E_NOTIMPL); }
  61.    HRESULT CALLBACK GetNewStorage (IStorage **storage)
  62.    {
  63.       // initialize a storage object in memory
  64.  
  65.       ILockBytes *lock_bytes;
  66.       HRESULT ret;
  67.  
  68.       if ((ret = CreateILockBytesOnHGlobal (NULL, TRUE, &lock_bytes)) != S_OK)
  69.          return (ret);
  70.       if ((ret = StgCreateDocfileOnILockBytes (lock_bytes, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, storage)) != S_OK)
  71.          lock_bytes->Release ();
  72.       return (ret);
  73.    }
  74.    HRESULT CALLBACK QueryAcceptData (IDataObject *lpdataobj, CLIPFORMAT *lpcfFormat, DWORD reco, BOOL fReally, HANDLE hMetaPict) { return (E_NOTIMPL); }
  75.    HRESULT CALLBACK QueryInsertObject (LPCLSID lpclsid, IStorage *storage, LONG cp) { return (S_OK); }
  76.    HRESULT CALLBACK ShowContainerUI (BOOL fShow) { return (E_NOTIMPL); }
  77.  
  78.    // our own data
  79.    ULONG reference_count;
  80. };
  81.  
  82.  
  83. // global variables used in this module only
  84. static bool is_classregistered = false;
  85. static PROC WindowProc_MessageToSend_DefaultProc;
  86. static RichEditOLECallback *richedit_oleinterface = NULL;
  87.  
  88.  
  89. // prototypes of local functions
  90. static LRESULT CALLBACK WindowProc_ThisWindow (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam);
  91. static LRESULT CALLBACK WindowProc_MessageToSend (HWND hDlg, unsigned int message, WPARAM wParam, LPARAM lParam);
  92.  
  93.  
  94. void Window_Chat (int interlocutor_index)
  95. {
  96.    // helper function to fire up the child window
  97.  
  98.    WNDCLASSEX wc;
  99.    player_t *network_player;
  100.  
  101.    // if the REOLECallback class instance hasn't been instantiated yet, do it
  102.    if (richedit_oleinterface == NULL)
  103.       richedit_oleinterface = new RichEditOLECallback;
  104.  
  105.    network_player = Player_FindByType (PLAYER_INTERNET); // quick access to network player
  106.    if (network_player == NULL)
  107.       return; // consistency check
  108.  
  109.    if (lastonlineplayers_time + 5.0f < current_time)
  110.       Player_SendBuffer_Add (network_player, 1000, L"who\n"); // request a players list update
  111.  
  112.    if (!interlocutors[interlocutor_index].is_active)
  113.       return; // consistency check
  114.  
  115.    // is the window class NOT registered yet ?
  116.    if (!is_classregistered)
  117.    {
  118.       // if so, register the window class once and for all
  119.       memset (&wc, 0, sizeof (wc));
  120.       wc.cbSize = sizeof (wc);
  121.       wc.style = CS_HREDRAW | CS_VREDRAW;
  122.       wc.lpfnWndProc = WindowProc_ThisWindow;
  123.       wc.hInstance = hAppInstance;
  124.       wc.hIcon = LoadIcon (hAppInstance, (wchar_t *) ICON_MAIN);
  125.       wc.hCursor = LoadCursor (NULL, IDC_ARROW);
  126.       wc.hbrBackground = GetSysColorBrush (COLOR_3DFACE);
  127.       wc.lpszClassName = WINDOW_CLASSNAME;
  128.       RegisterClassEx (&wc);
  129.  
  130.       is_classregistered = true; // remember this window class is registered
  131.    }
  132.  
  133.    // create the window as an application-level window (not child)
  134.    interlocutors[interlocutor_index].hWnd = CreateWindowEx (WS_EX_CLIENTEDGE, WINDOW_CLASSNAME, L"", WS_OVERLAPPEDWINDOW,
  135.                                                             CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_DEFAULT_WIDTH, WINDOW_DEFAULT_HEIGHT,
  136.                                                             NULL, NULL, hAppInstance, NULL);
  137.    interlocutors[interlocutor_index].update_dialog = true; // and update this interlocutor's dialog
  138.  
  139.    return; // finished, the window is fired up
  140. }
  141.  
  142.  
  143. void Window_Chat_Validated (void)
  144. {
  145.    // callback function called by the main game thread when the window is validated
  146.  
  147.    // remember this callback is no longer to be called
  148.    is_window_chat_validated = false;
  149.  
  150.    return; // finished
  151. }
  152.  
  153.  
  154. static LRESULT CALLBACK WindowProc_ThisWindow (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
  155. {
  156.    // message handler for the child window
  157.  
  158.    unsigned short wParam_hiword;
  159.    unsigned short wParam_loword;
  160.    wchar_t temp_string[256];
  161.    wchar_t *text_buffer;
  162.    char *cstring_buffer;
  163.    interlocutor_t *interlocutor;
  164.    onlineplayer_t *onlineplayer;
  165.    challenge_t *challenge;
  166.    player_t *remote_player;
  167.    player_t *local_player;
  168.    MINMAXINFO *minmax;
  169.    FLASHWINFO flashinfo;
  170.    RECT client_rect;
  171.    HWND hEditWnd;
  172.    int display_picture;
  173.    int player_index;
  174.    int char_index;
  175.    int length;
  176.    bool is_invitable;
  177.  
  178.    // filter out the commonly used message values
  179.    wParam_hiword = HIWORD (wParam);
  180.    wParam_loword = LOWORD (wParam);
  181.  
  182.    // have we just fired up this window ?
  183.    if (message == WM_CREATE)
  184.    {
  185.       // center the window
  186.       CenterWindow (hWnd, hMainWnd);
  187.  
  188.       // populate the window
  189.       CreateWindowEx (0, L"static", L"",
  190.                       WS_CHILD | WS_VISIBLE,
  191.                       0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_QUESTION, hAppInstance, NULL);
  192.  
  193.       LoadLibrary (L"riched20.dll"); // ensure the RichEdit controls DLL is loaded
  194.       hEditWnd = CreateWindowEx (0, L"RichEdit20A", L"", // for some reason, only the ANSI version of this control accepts RTF data :(
  195.                                  WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | ES_SELECTIONBAR | ES_SUNKEN,
  196.                                  0, 0, 0, 0, hWnd, (HMENU) WINDOW_EDITBOX_DISCUSSION, hAppInstance, NULL);
  197.       SendMessage (hEditWnd, EM_SETOLECALLBACK, 0, (LPARAM) richedit_oleinterface);
  198.  
  199.       hEditWnd = CreateWindowEx (WS_EX_CLIENTEDGE, L"edit", L"",
  200.                                  WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL,
  201.                                  0, 0, 0, 0, hWnd, (HMENU) WINDOW_EDITBOX_MESSAGETOSEND, hAppInstance, NULL);
  202.       WindowProc_MessageToSend_DefaultProc = (PROC) GetWindowLong (hEditWnd, GWL_WNDPROC); // save this edit box's window proc
  203.       SetWindowLong (hEditWnd, GWL_WNDPROC, (long) WindowProc_MessageToSend); // replace this edit box's window proc by ours
  204.  
  205.       CreateWindowEx (0, L"static", L"",
  206.                       WS_CHILD | WS_VISIBLE | SS_BITMAP | SS_SUNKEN,
  207.                       0, 0, 0, 0, hWnd, (HMENU) WINDOW_BITMAP_OPPONENTSTATUS, hAppInstance, NULL);
  208.       CreateWindowEx (0, L"button", L"",
  209.                       WS_CHILD | WS_VISIBLE | WS_DISABLED, // initially disabled
  210.                       0, 0, 0, 0, hWnd, (HMENU) WINDOW_BUTTON_INVITE, hAppInstance, NULL);
  211.       CreateWindowEx (0, L"button", L"",
  212.                       WS_CHILD | WS_VISIBLE | WS_DISABLED, // initially disabled
  213.                       0, 0, 0, 0, hWnd, (HMENU) WINDOW_BUTTON_PLAYERINFO, hAppInstance, NULL);
  214.       CreateWindowEx (0, L"button", L"",
  215.                       WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
  216.                       0, 0, 0, 0, hWnd, (HMENU) WINDOW_BUTTON_SEND, hAppInstance, NULL);
  217.       CreateWindowEx (WS_EX_RIGHT, L"static", L"",
  218.                       WS_CHILD | WS_VISIBLE,
  219.                       0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_STATUSBAR, hAppInstance, NULL);
  220.  
  221.       // set the chat text font
  222.       SendMessage (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), WM_SETFONT, (WPARAM) hFontChat, false);
  223.  
  224.       // associate the default GUI font with the leading caption
  225.       SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_QUESTION), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
  226.  
  227.       // associate the default GUI font with the invite button and set its text
  228.       SendMessage (GetDlgItem (hWnd, WINDOW_BUTTON_INVITE), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
  229.       SetDlgItemText (hWnd, WINDOW_BUTTON_INVITE, LOCALIZE (L"Chat_Invite"));
  230.  
  231.       // associate the default GUI font with the player info button and set its text
  232.       SendMessage (GetDlgItem (hWnd, WINDOW_BUTTON_PLAYERINFO), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
  233.       SetDlgItemText (hWnd, WINDOW_BUTTON_PLAYERINFO, LOCALIZE (L"Chat_GetInfo"));
  234.  
  235.       // associate the default GUI font with the send button and set its text
  236.       SendMessage (GetDlgItem (hWnd, WINDOW_BUTTON_SEND), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
  237.       SetDlgItemText (hWnd, WINDOW_BUTTON_SEND, LOCALIZE (L"Button_Send"));
  238.  
  239.       // associate the default GUI font with the status bar and set its text
  240.       SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
  241.       SetDlgItemText (hWnd, WINDOW_TEXT_STATUSBAR, LOCALIZE (L"Chat_StatusBar"));
  242.  
  243.       // refresh the server message area every second
  244.       SetTimer (hWnd, WINDOW_TIMER_REFRESH, 1000, NULL);
  245.       SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, 0); // but call it now
  246.  
  247.       // convert the status bar message to a hyperlink
  248.       ConvertStaticToHyperlink (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR));
  249.  
  250.       // now show the window
  251.       ShowWindow (hWnd, SW_SHOW);
  252.    }
  253.  
  254.    // else did we click the close button on the title bar ?
  255.    else if (message == WM_CLOSE)
  256.       DestroyWindow (hWnd); // if so, destroy this window
  257.  
  258.    // else are we destroying this window ?
  259.    else if (message == WM_DESTROY)
  260.    {
  261.       KillTimer (hWnd, WINDOW_TIMER_REFRESH); // destroy the timer we used to refresh the dialog text
  262.  
  263.       interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
  264.       if (interlocutor != NULL)
  265.          interlocutor->is_active = false; // remember interlocutor has gone away
  266.  
  267.       is_window_chat_validated = true;
  268.    }
  269.  
  270.    // else are we resizing the window ?
  271.    else if (message == WM_SIZE)
  272.    {
  273.       // get the new window size
  274.       GetClientRect (hWnd, &client_rect);
  275.  
  276.       // position the window elements
  277.       SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_QUESTION), NULL, 16, 16, client_rect.right - 148, 16, SWP_NOZORDER);
  278.       SetWindowPos (GetDlgItem (hWnd, WINDOW_EDITBOX_DISCUSSION), NULL, 16, 40, client_rect.right - 148, client_rect.bottom - 136, SWP_NOZORDER);
  279.       SetWindowPos (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), NULL, 16, client_rect.bottom - 80, client_rect.right - 148, 64, SWP_NOZORDER);
  280.       SetWindowPos (GetDlgItem (hWnd, WINDOW_BITMAP_OPPONENTSTATUS), NULL, client_rect.right - 116, 16, 100, 100, SWP_NOZORDER);
  281.       SetWindowPos (GetDlgItem (hWnd, WINDOW_BUTTON_INVITE), NULL, client_rect.right - 116, 124, 100, 32, SWP_NOZORDER);
  282.       SetWindowPos (GetDlgItem (hWnd, WINDOW_BUTTON_PLAYERINFO), NULL, client_rect.right - 116, 162, 100, 32, SWP_NOZORDER);
  283.       SetWindowPos (GetDlgItem (hWnd, WINDOW_BUTTON_SEND), NULL, client_rect.right - 116, client_rect.bottom - 80, 100, 64, SWP_NOZORDER);
  284.       SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR), NULL, 0, client_rect.bottom - 16, client_rect.right, 16, SWP_NOZORDER);
  285.    }
  286.  
  287.    // else are we asking how big/small we can resize ?
  288.    else if (message == WM_GETMINMAXINFO)
  289.    {
  290.       minmax = (MINMAXINFO *) lParam; // get a pointer to the min/max info structure
  291.  
  292.       minmax->ptMinTrackSize.x = WINDOW_MIN_WIDTH;
  293.       minmax->ptMinTrackSize.y = WINDOW_MIN_HEIGHT;
  294.    }
  295.  
  296.    // else did we take action on one of the controls ?
  297.    else if (message == WM_COMMAND)
  298.    {
  299.       // was it the "invite" button ?
  300.       if (wParam_loword == WINDOW_BUTTON_INVITE)
  301.       {
  302.          interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
  303.          if (interlocutor != NULL)
  304.          {
  305.             // player is available for an invitation. Is he already inviting us ?
  306.             challenge = Challenge_Find (interlocutor->nickname);
  307.             if ((challenge != NULL) && challenge->is_active && IsWindow (challenge->hWnd))
  308.                EndDialog (challenge->hWnd, 0); // if so, close the challenge dialog box (this will also make us decline it properly)
  309.  
  310.             DialogBox_SendChallenge (interlocutor->nickname); // fire up the dialog box
  311.          }
  312.       }
  313.  
  314.       // else did we click the "get player info" button ?
  315.       else if (wParam_loword == WINDOW_BUTTON_PLAYERINFO)
  316.       {
  317.          interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
  318.          if (interlocutor != NULL)
  319.             PlayerCard_FindOrCreate (interlocutor->nickname); // fire up the dialog box
  320.       }
  321.  
  322.       // else was it the "send" button ?
  323.       else if (wParam_loword == WINDOW_BUTTON_SEND)
  324.       {
  325.          // grab the text to send from the edit box and empty the box
  326.          length = SendMessage (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), WM_GETTEXTLENGTH, 0, 0); // get text length
  327.          text_buffer = (wchar_t *) SAFE_malloc (length + 1, sizeof (wchar_t), false); // allocate space
  328.          GetDlgItemText (hWnd, WINDOW_EDITBOX_MESSAGETOSEND, text_buffer, length + 1); // copy text
  329.  
  330.          // is there actually something to send ?
  331.          if (text_buffer[0] != 0)
  332.          {
  333.             // drop all accents by converting to 7-bit US-ASCII and then back to wide char
  334.             cstring_buffer = (char *) SAFE_malloc (2 * (length + 1), sizeof (char), false);
  335.             ConvertTo7BitASCII (cstring_buffer, 2 * (length + 1), text_buffer);
  336.             ConvertToWideChar (text_buffer, length + 1, cstring_buffer);
  337.             SAFE_free ((void **) &cstring_buffer);
  338.  
  339.             // convert some dangerous characters
  340.             length = wcslen (text_buffer);
  341.             for (char_index = 0; char_index < length; char_index++)
  342.                if ((text_buffer[char_index] == L'\r') || (text_buffer[char_index] == L'\n'))
  343.                   text_buffer[char_index] = L' ';
  344.                else if (text_buffer[char_index] == L'%')
  345.                   text_buffer[char_index] = L'ยค'; // percent signs are nasty for the varargs, so remove them
  346.  
  347.             // find the local and the network players
  348.             if (((remote_player = Player_FindByType (PLAYER_INTERNET)) != NULL)
  349.                 && ((local_player = Player_FindByType (PLAYER_HUMAN)) != NULL))
  350.             {
  351.                interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
  352.                if (interlocutor != NULL)
  353.                {
  354.                   // fill the send buffer and append chat text to interlocutor's chat window
  355.                   Player_SendBuffer_Add (remote_player, 1000, L"tell %s %s\n", interlocutor->nickname, text_buffer);
  356.                   Interlocutor_Chat (interlocutor, local_player->name, true, text_buffer);
  357.                }
  358.             }
  359.          }
  360.  
  361.          SetDlgItemText (hWnd, WINDOW_EDITBOX_MESSAGETOSEND, L""); // reset text in the window
  362.          SAFE_free ((void **) &text_buffer); // free the memory space we used
  363.       }
  364.  
  365.       // else was it the status bar hyperlink ?
  366.       else if (wParam_loword == WINDOW_TEXT_STATUSBAR)
  367.          ShellExecute (NULL, L"open", PROGRAM_URL, NULL, NULL, SW_MAXIMIZE); // open the donation page in the default browser, maximized
  368.    }
  369.  
  370.    // else is it a timer event AND is it our refresh timer ?
  371.    else if ((message == WM_TIMER) && (wParam == WINDOW_TIMER_REFRESH))
  372.    {
  373.       interlocutor = Interlocutor_FindByWindowHandle (hWnd); // find the interlocutor it is
  374.       if (interlocutor != NULL)
  375.       {
  376.          // find interlocutor in online players list
  377.          for (player_index = 0; player_index < onlineplayer_count; player_index++)
  378.             if (wcscmp (interlocutor->nickname, onlineplayers[player_index].nickname) == 0)
  379.                break; // break as soon as we find it
  380.  
  381.          // have we found it ?
  382.          if (player_index < onlineplayer_count)
  383.          {
  384.             onlineplayer = &onlineplayers[player_index]; // quick access to online player
  385.             remote_player = Player_FindByType (PLAYER_INTERNET); // find the remote player
  386.  
  387.             // is this talkee our opponent AND are we playing a game AND does its status not reflect it yet ?
  388.             if ((remote_player != NULL) && remote_player->is_in_game
  389.                 && (wcscmp (onlineplayer->nickname, remote_player->name) == 0)
  390.                 && ((onlineplayer->handlestatus == HANDLESTATUS_UNDEFINED)
  391.                     || (onlineplayer->handlestatus == HANDLESTATUS_AVAILABLE)
  392.                     || (onlineplayer->handlestatus == HANDLESTATUS_EXAMININGAGAME)
  393.                     || (onlineplayer->handlestatus == HANDLESTATUS_NOTOPENFORAMATCH)
  394.                     || (onlineplayer->handlestatus == HANDLESTATUS_INACTIVEORBUSY)))
  395.                onlineplayer->handlestatus = HANDLESTATUS_INGAME; // if so, remember we're playing against it
  396.  
  397.             display_picture = onlineplayer->handlestatus; // if so, use its current status
  398.          }
  399.          else
  400.             display_picture = HANDLESTATUS_OFFLINE; // else flag it as offline
  401.  
  402.          // do we need to change its status ?
  403.          if (display_picture != interlocutor->current_displaypicture)
  404.          {
  405.             // set window icons (small one for title bar & big one for task manager) and avatar
  406.             SendMessage (hWnd, WM_SETICON, ICON_SMALL, (LPARAM) handlestatus[display_picture].icon);
  407.             SendMessage (hWnd, WM_SETICON, ICON_BIG, (LPARAM) handlestatus[display_picture].icon);
  408.             SendMessage (GetDlgItem (hWnd, WINDOW_BITMAP_OPPONENTSTATUS), STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) handlestatus[display_picture].bitmap);
  409.  
  410.             // set window text
  411.             swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"%s (%s)", interlocutor->nickname, handlestatus[display_picture].text);
  412.             SetWindowText (hWnd, temp_string); // set window title
  413.  
  414.             // set window title and parameters
  415.             if (display_picture == HANDLESTATUS_OFFLINE)
  416.             {
  417.                EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_PLAYERINFO), false); // if so, disable buttons
  418.                EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_INVITE), false);
  419.                EnableWindow (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), false);
  420.                EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_SEND), false);
  421.             }
  422.             else
  423.             {
  424.                if ((display_picture == HANDLESTATUS_OFFLINE) || (display_picture == HANDLESTATUS_INGAME) || (display_picture == HANDLESTATUS_NOTOPENFORAMATCH))
  425.                   is_invitable = false; // this player is NOT invitable
  426.                else
  427.                   is_invitable = true; // all other players are invitable
  428.  
  429.                EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_PLAYERINFO), true); // else enable them
  430.                EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_INVITE), is_invitable);
  431.                EnableWindow (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND), true);
  432.                EnableWindow (GetDlgItem (hWnd, WINDOW_BUTTON_SEND), true);
  433.  
  434.                if (GetForegroundWindow () == hWnd)
  435.                   SetFocus (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND)); // set keyboard focus to the message editbox
  436.             }
  437.  
  438.             interlocutor->current_displaypicture = display_picture; // remember we've done it
  439.          }
  440.  
  441.          // do we need to update dialog ?
  442.          if (interlocutor->update_dialog)
  443.          {
  444.             // set the leading caption
  445.             swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), LOCALIZE (L"Chat_Question"), interlocutor->nickname);
  446.             SetDlgItemText (hWnd, WINDOW_TEXT_QUESTION, temp_string);
  447.  
  448.             // set discussion text and scroll the edit box down
  449.             hEditWnd = GetDlgItem (hWnd, WINDOW_EDITBOX_DISCUSSION);
  450.             SetWindowText (hEditWnd, interlocutor->dialogtext);
  451.             length = GetWindowTextLength (hEditWnd);
  452.             //SetFocus (hEditWnd);
  453.             Edit_SetSel (hEditWnd, length, length);
  454.             Edit_ScrollCaret (hEditWnd);
  455.             SetFocus (GetDlgItem (hWnd, WINDOW_EDITBOX_MESSAGETOSEND));
  456.  
  457.             // if we are not the top level window, flash window
  458.             if (GetForegroundWindow () != hWnd)
  459.             {
  460.                memset (&flashinfo, 0, sizeof (flashinfo));
  461.                flashinfo.cbSize = sizeof (flashinfo);
  462.                flashinfo.hwnd = hWnd;
  463.                flashinfo.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG; // flash until we bring window to foreground
  464.                FlashWindowEx (&flashinfo);
  465.             }
  466.  
  467.             interlocutor->update_dialog = false; // remember we refreshed dialog text
  468.          }
  469.       }
  470.    }
  471.  
  472.    // call the default dialog message processing function to keep things going
  473.    return (DefWindowProc (hWnd, message, wParam, lParam));
  474. }
  475.  
  476.  
  477. static LRESULT CALLBACK WindowProc_MessageToSend (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
  478. {
  479.    // this is the callback function for the edit box control where the user types its message
  480.  
  481.    // was the enter key pressed ?
  482.    if ((message == WM_CHAR) && (wParam == VK_RETURN))
  483.    {
  484.       SendMessage (GetParent (hWnd), WM_COMMAND, WINDOW_BUTTON_SEND, 0); // if so, forward it to our parent
  485.       return (true); // and prevent this keystroke from being treated
  486.    }
  487.  
  488.    // for all other messages, call the default window message processing function
  489.    return ((LRESULT) CallWindowProc ((WNDPROC) WindowProc_MessageToSend_DefaultProc, hWnd, message, wParam, lParam));
  490. }
  491.