Subversion Repositories Games.Chess Giants

Rev

Rev 21 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
// window_sought.cpp
2
 
3
#include "../common.h"
4
 
5
 
6
// window parameters
7
#define WINDOW_CLASSNAME PROGRAM_NAME L" SoughtGames WndClass"
8
#define WINDOW_DEFAULT_WIDTH 823
9
#define WINDOW_DEFAULT_HEIGHT 600
10
#define WINDOW_MIN_WIDTH 823
11
#define WINDOW_MIN_HEIGHT 200
12
 
13
 
14
// local definitions
15
#define WINDOW_TEXT_DOUBLECLICKORNARROWDOWN 1
16
#define WINDOW_TEXT_GAMEIS 2
17
#define WINDOW_COMBOBOX_GAMEIS 3
18
#define WINDOW_COMBOBOX_RATEDUNRATED 4
19
#define WINDOW_TEXT_LEVELFROM 5
20
#define WINDOW_EDITBOX_LEVELFROM 6
21
#define WINDOW_TEXT_LEVELTO 7
22
#define WINDOW_EDITBOX_LEVELTO 8
23
#define WINDOW_TEXT_YOUCANALSOPOSTYOURSBYCLICKINGHERE 9
24
#define WINDOW_LIST_SOUGHTGAMES 10
25
#define WINDOW_TEXT_STATUSBAR 11
26
#define WINDOW_TIMER_REFRESH 1
27
 
28
 
29
// list view column definition
30
typedef struct listviewcolumn_s
31
{
32
   int width;
33
   int alignment;
34
   bool sort_descending;
35
   wchar_t *text;
36
   HWND hToolTipWnd;
37
} listviewcolumn_t;
38
 
39
 
40
// game types definition
41
typedef struct gametype_s
42
{
43
   wchar_t name[32]; // game type name
44
} gametype_t;
45
 
46
 
47
// global variables used in this module only
48
static bool is_classregistered = false;
49
static int listviewicons[sizeof (handlestatus) / sizeof (handlestatus_t)]; // as big as the handlestatus global array
50
static listviewcolumn_t listviewcolumns[] =
51
{
52
   { 110, LVCFMT_LEFT, false, NULL /*LOCALIZE (L"SoughtGames_ColumnGameType")*/, NULL }, // text address needs to be set at runtime, because it's mallocated
53
   { 75, LVCFMT_LEFT, false, NULL /*LOCALIZE (L"SoughtGames_ColumnRatedUnrated")*/, NULL },
54
   { 75, LVCFMT_LEFT, true, NULL /*LOCALIZE (L"SoughtGames_ColumnInitialTime")*/, NULL },
55
   { 75, LVCFMT_LEFT, false, NULL /*LOCALIZE (L"SoughtGames_ColumnIncrement")*/, NULL },
56
   { 130, LVCFMT_LEFT, false, NULL /*LOCALIZE (L"SoughtGames_ColumnOpponent")*/, NULL },
57
   { 50, LVCFMT_CENTER, false, NULL /*LOCALIZE (L"SoughtGames_ColumnRating")*/, NULL },
58
   { 55, LVCFMT_CENTER, false, NULL /*LOCALIZE (L"SoughtGames_ColumnColor")*/, NULL },
59
   { 50, LVCFMT_RIGHT, false, NULL /*LOCALIZE (L"SoughtGames_ColumnAcceptsFrom")*/, NULL },
60
   { 50, LVCFMT_LEFT, false, NULL /*LOCALIZE (L"SoughtGames_ColumnUpTo")*/, NULL },
61
   { 40, LVCFMT_CENTER, false, NULL /*LOCALIZE (L"SoughtGames_ColumnFilter")*/, NULL },
62
   { 40, LVCFMT_CENTER, false, NULL /*LOCALIZE (L"SoughtGames_ColumnAutomatic")*/, NULL },
63
};
64
static int current_sortcolumn = 2;
65
static gametype_t *gametypes; // mallocated
66
static int gametype_count;
67
static wchar_t temp_string[256];
68
static wchar_t gametype_name[32];
69
//static HWND hThisWnd = NULL;
70
#define hThisWnd hSoughtWnd // shared variable
71
 
72
 
73
// prototypes of local functions
74
static LRESULT CALLBACK WindowProc_ThisWindow (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam);
75
static int CALLBACK CompareProc_ListSoughtGames (LPARAM lParam1, LPARAM lParam2, LPARAM column);
76
static int WINAPI ListView_WndProc (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam);
77
 
78
 
79
void Window_Sought (void)
80
{
81
   // helper function to fire up the child window
82
 
83
   WNDCLASSEX wc;
84
   player_t *network_player;
85
 
86
   // is the window we want to display already displayed ?
87
   if (IsWindow (hThisWnd))
88
      SetForegroundWindow (hThisWnd); // if so, just bring it to front
89
 
90
   // else the way is clear
91
   else
92
   {
93
      // find the network player and make him ask an update from the server
94
      if ((network_player = Player_FindByType (PLAYER_INTERNET)) != NULL)
95
      {
96
         SAFE_free ((void **) &soughtgames); // free the sought games list we know
97
         soughtgame_count = -1; // and reset its count (-1 means "reply not arrived")
98
         Player_SendBuffer_Add (network_player, 1000, L"sought all\n"); // send the sought games update request
99
      }
100
 
101
      // is the window class NOT registered yet ?
102
      if (!is_classregistered)
103
      {
104
         // if so, register the window class once and for all
105
         memset (&wc, 0, sizeof (wc));
106
         wc.cbSize = sizeof (wc);
107
         wc.style = CS_HREDRAW | CS_VREDRAW;
108
         wc.lpfnWndProc = WindowProc_ThisWindow;
109
         wc.hInstance = hAppInstance;
110
         wc.hIcon = LoadIcon (hAppInstance, (wchar_t *) ICON_MAIN);
111
         wc.hCursor = LoadCursor (NULL, IDC_ARROW);
112
         wc.hbrBackground = GetSysColorBrush (COLOR_3DFACE);
113
         wc.lpszClassName = WINDOW_CLASSNAME;
114
         RegisterClassEx (&wc);
115
 
116
         is_classregistered = true; // remember this window class is registered
117
      }
118
 
119
      // create the child window
120
      hThisWnd = CreateWindowEx (WS_EX_CLIENTEDGE, WINDOW_CLASSNAME, LOCALIZE (L"SoughtGames_TitleBuildingList"), WS_OVERLAPPEDWINDOW,
121
                                 CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_DEFAULT_WIDTH, WINDOW_DEFAULT_HEIGHT,
122
                                 hMainWnd, NULL, hAppInstance, NULL);
123
   }
124
 
125
   return; // return as soon as the thread is fired up
126
}
127
 
128
 
129
void Window_Sought_Validated (void)
130
{
131
   // callback function called by the main game thread when the window is validated
132
 
133
   // remember this callback is no longer to be called
134
   is_window_sought_validated = false;
135
 
136
   return; // finished, sought answer has been issued
137
}
138
 
139
 
140
static LRESULT CALLBACK WindowProc_ThisWindow (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
141
{
142
   // message handler for the child window
143
 
144
   unsigned short wParam_hiword;
145
   unsigned short wParam_loword;
146
   int soughtgame_gameis;
147
   int soughtgame_ratedunrated;
148
   int soughtgame_ratingfrom;
149
   int soughtgame_ratingto;
150
   int result;
151
   HWND hComboBoxWnd;
152
   HWND hListWnd;
153
   LVCOLUMN lvc;
154
   LVITEM lvi;
155
   HDITEM hdi;
156
   NMLISTVIEW *lv;
157
   NMITEMACTIVATE *clickeditem;
158
   HIMAGELIST imagelist;
159
   MINMAXINFO *minmax;
160
   RECT client_rect;
161
   soughtgame_t *soughtgame;
162
   player_t *network_player;
163
   player_t *local_player;
164
   int soughtgame_index;
165
   int gametype_index;
166
   int column_index;
167
   int insert_index;
168
 
169
   // filter out the commonly used message values
170
   wParam_hiword = HIWORD (wParam);
171
   wParam_loword = LOWORD (wParam);
172
 
173
   // have we just fired up this window ?
174
   if (message == WM_CREATE)
175
   {
176
      // center the window
177
      CenterWindow (hWnd, hMainWnd);
178
 
179
      // populate the window
180
      CreateWindowEx (0, L"static", L"",
181
                      WS_CHILD | WS_VISIBLE,
182
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_DOUBLECLICKORNARROWDOWN, hAppInstance, NULL);
183
      CreateWindowEx (WS_EX_RIGHT, L"static", L"",
184
                      WS_CHILD | WS_VISIBLE,
185
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_GAMEIS, hAppInstance, NULL);
186
      CreateWindowEx (WS_EX_CLIENTEDGE, L"combobox", L"",
187
                      WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
188
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_COMBOBOX_GAMEIS, hAppInstance, NULL);
189
      CreateWindowEx (WS_EX_CLIENTEDGE, L"combobox", L"",
190
                      WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
191
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_COMBOBOX_RATEDUNRATED, hAppInstance, NULL);
192
      CreateWindowEx (WS_EX_RIGHT, L"static", L"",
193
                      WS_CHILD | WS_VISIBLE,
194
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_LEVELFROM, hAppInstance, NULL);
195
      CreateWindowEx (WS_EX_CLIENTEDGE, L"edit", L"",
196
                      WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | SS_CENTERIMAGE,
197
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_EDITBOX_LEVELFROM, hAppInstance, NULL);
198
      CreateWindowEx (WS_EX_RIGHT, L"static", L"",
199
                      WS_CHILD | WS_VISIBLE,
200
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_LEVELTO, hAppInstance, NULL);
201
      CreateWindowEx (WS_EX_CLIENTEDGE, L"edit", L"",
202
                      WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | SS_CENTERIMAGE,
203
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_EDITBOX_LEVELTO, hAppInstance, NULL);
204
      CreateWindowEx (0, L"static", L"",
205
                      WS_CHILD | WS_VISIBLE,
206
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_YOUCANALSOPOSTYOURSBYCLICKINGHERE, hAppInstance, NULL);
207
      CreateWindowEx (WS_EX_STATICEDGE, L"syslistview32", L"",
208
                      WS_CHILD | WS_VISIBLE | WS_TABSTOP | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT,
209
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_LIST_SOUGHTGAMES, hAppInstance, NULL);
210
      CreateWindowEx (WS_EX_RIGHT, L"static", L"",
211
                      WS_CHILD | WS_VISIBLE,
212
                      0, 0, 0, 0, hWnd, (HMENU) WINDOW_TEXT_STATUSBAR, hAppInstance, NULL);
213
 
214
      // prepare the list view : do it before anything that could trigger a fill
215
 
216
      // get a quick access to the list control
217
      hListWnd = GetDlgItem (hWnd, WINDOW_LIST_SOUGHTGAMES);
218
 
219
      // add full row select and header columns rearranging to it
220
      ListView_SetExtendedListViewStyle (hListWnd, ListView_GetExtendedListViewStyle (hListWnd)
221
                                                   | LVS_EX_GRIDLINES | LVS_EX_LABELTIP
222
                                                   | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP);
223
 
224
      // subclass the list view procedure to handle header tooltips and save the old procedure as one of the window's property
225
      SetProp (hListWnd, L"BaseWndProc", (HANDLE) SetWindowLongPtr (hListWnd, GWL_WNDPROC, (long) ListView_WndProc));
226
 
227
      // tell Windows which members of the LVCOLUMN structure we're filling
228
      memset (&lvc, 0, sizeof (lvc));
229
      lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
230
      for (column_index = 0; column_index < sizeof (listviewcolumns) / sizeof (listviewcolumn_t); column_index++)
231
      {
232
         lvc.iSubItem = column_index;
233
         if (column_index == 0) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnGameType");
234
         else if (column_index == 1) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnRatedUnrated");
235
         else if (column_index == 2) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnInitialTime");
236
         else if (column_index == 3) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnIncrement");
237
         else if (column_index == 4) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnOpponent");
238
         else if (column_index == 5) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnRating");
239
         else if (column_index == 6) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnColor");
240
         else if (column_index == 7) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnAcceptsFrom");
241
         else if (column_index == 8) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnUpTo");
242
         else if (column_index == 9) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnFilter");
243
         else if (column_index == 10) lvc.pszText = LOCALIZE (L"SoughtGames_ColumnAutomatic");
244
         lvc.cx = listviewcolumns[column_index].width;
245
         lvc.fmt = listviewcolumns[column_index].alignment;
246
         ListView_InsertColumn (hListWnd, column_index, &lvc); // add each column to list view
247
      }
248
 
249
      // create the listview image list
250
      imagelist = ImageList_Create (16, 16, ILC_COLOR32, sizeof (handlestatus) / sizeof (handlestatus_t), 1); // create an imagelist holding N 32-bit images
251
      for (insert_index = 1; insert_index < sizeof (handlestatus) / sizeof (handlestatus_t); insert_index++)
252
         listviewicons[insert_index] = ImageList_AddIcon (imagelist, handlestatus[insert_index].icon); // add our icons in the image list
253
      ListView_SetImageList (hListWnd, imagelist, LVSIL_SMALL); // associate it with the listview
254
 
255
      // associate the default GUI font with the "double-click or narrow down" message and set its text
256
      SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_DOUBLECLICKORNARROWDOWN), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
257
      SetWindowText (GetDlgItem (hWnd, WINDOW_TEXT_DOUBLECLICKORNARROWDOWN), LOCALIZE (L"SoughtGames_DoubleClickOrNarrowDown"));
258
 
259
      // associate the default GUI font with the "status is" message and set its text
260
      SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_GAMEIS), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
261
      SetWindowText (GetDlgItem (hWnd, WINDOW_TEXT_GAMEIS), LOCALIZE (L"SoughtGames_LookingForGameOfType"));
262
 
263
      // associate the default GUI font with the "game is" combo box
264
      SendMessage (GetDlgItem (hWnd, WINDOW_COMBOBOX_GAMEIS), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
265
 
266
      // reset the game types array
267
      gametypes = NULL;
268
      gametype_count = 0;
269
 
270
      // associate the default GUI font with the "rated/unrated" combo box and populate it
271
      SendMessage (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
272
      ComboBox_ResetContent (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED));
273
      ComboBox_AddString (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED), L"");
274
      ComboBox_AddString (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED), LOCALIZE (L"Unrated"));
275
      ComboBox_AddString (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED), LOCALIZE (L"Rated"));
276
//      if (_wcsicmp (options.network.login, L"guest") == 0)
277
//         ComboBox_SetCurSel (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED), 1); // if we log in as guest, select unrated games by default
278
 
279
      // associate the default GUI font with the "level from" message and set its text
280
      SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_LEVELFROM), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
281
      SetWindowText (GetDlgItem (hWnd, WINDOW_TEXT_LEVELFROM), LOCALIZE (L"SoughtGames_LevelFrom"));
282
 
283
      // associate the default GUI font with the "level from" edit box and reset it
284
      SendMessage (GetDlgItem (hWnd, WINDOW_EDITBOX_LEVELFROM), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
285
      SetDlgItemText (hWnd, WINDOW_EDITBOX_LEVELFROM, L"");
286
 
287
      // associate the default GUI font with the "level to" message and set its text
288
      SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_LEVELTO), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
289
      SetWindowText (GetDlgItem (hWnd, WINDOW_TEXT_LEVELTO), LOCALIZE (L"SoughtGames_LevelTo"));
290
 
291
      // associate the default GUI font with the "level to" edit box and reset it
292
      SendMessage (GetDlgItem (hWnd, WINDOW_EDITBOX_LEVELTO), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
293
      SetDlgItemText (hWnd, WINDOW_EDITBOX_LEVELTO, L"");
294
 
295
      // associate the default GUI font with the "you can also post yours" message and set its text, then convert it to a hyperlink
296
      SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_YOUCANALSOPOSTYOURSBYCLICKINGHERE), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
297
      SetWindowText (GetDlgItem (hWnd, WINDOW_TEXT_YOUCANALSOPOSTYOURSBYCLICKINGHERE), LOCALIZE (L"SoughtGames_YouCanAlsoPostYoursByClickingHere"));
298
      ConvertStaticToHyperlink (GetDlgItem (hWnd, WINDOW_TEXT_YOUCANALSOPOSTYOURSBYCLICKINGHERE));
299
 
300
      // associate the default GUI font with the status bar and set its text
301
      SendMessage (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR), WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), false);
302
      SetWindowText (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR), LOCALIZE (L"SoughtGames_StatusBar"));
303
 
304
      // refresh the window every second
305
      SetTimer (hWnd, WINDOW_TIMER_REFRESH, 1000, NULL);
306
 
307
      // convert the status bar message to a hyperlink
308
      ConvertStaticToHyperlink (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR));
309
 
310
      // now show the window
311
      ShowWindow (hWnd, SW_SHOW);
312
   }
313
 
314
   // else did we click the close button on the title bar ?
315
   else if (message == WM_CLOSE)
316
      DestroyWindow (hWnd); // destroy this window
317
 
318
   // else are we destroying the window ?
319
   else if (message == WM_DESTROY)
320
   {
321
      KillTimer (hWnd, WINDOW_TIMER_REFRESH); // destroy the timer we used to refresh the window
322
      hThisWnd = NULL; // window is closed
323
 
324
      // now that we're sure the window is closed...
325
      SAFE_free ((void **) &soughtgames); // free the sought games list we know
326
      soughtgame_count = 0; // and reset its count
327
 
328
      SAFE_free ((void **) &gametypes); // free the game types array
329
      gametype_count = 0; // and reset its count
330
 
331
      is_window_sought_validated = true; // remember we closed this window
332
   }
333
 
334
   // else are we resizing the window ?
335
   else if (message == WM_SIZE)
336
   {
337
      // get the new window size
338
      GetClientRect (hWnd, &client_rect);
339
 
340
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_DOUBLECLICKORNARROWDOWN), NULL, 16, 16, client_rect.right - 32, 16, SWP_NOZORDER);
341
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_GAMEIS),           NULL,   8, 40, 208, 16, SWP_NOZORDER);
342
      SetWindowPos (GetDlgItem (hWnd, WINDOW_COMBOBOX_GAMEIS),       NULL, 224, 38, 128, 20, SWP_NOZORDER);
343
      SetWindowPos (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED), NULL, 384, 38,  96, 20, SWP_NOZORDER);
344
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_LEVELFROM),        NULL, 512, 40, 120, 16, SWP_NOZORDER);
345
      SetWindowPos (GetDlgItem (hWnd, WINDOW_EDITBOX_LEVELFROM),     NULL, 640, 38,  48, 20, SWP_NOZORDER);
346
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_LEVELTO),          NULL, 688, 40,  40, 16, SWP_NOZORDER);
347
      SetWindowPos (GetDlgItem (hWnd, WINDOW_EDITBOX_LEVELTO),       NULL, 736, 38,  48, 20, SWP_NOZORDER);
348
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_YOUCANALSOPOSTYOURSBYCLICKINGHERE), NULL, 16, 64, client_rect.right - 32, 16, SWP_NOZORDER);
349
      SetWindowPos (GetDlgItem (hWnd, WINDOW_LIST_SOUGHTGAMES), NULL, 16, 88, client_rect.right - 32, client_rect.bottom - 104, SWP_NOZORDER);
350
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR), NULL, 0, client_rect.bottom - 16, client_rect.right, 16, SWP_NOZORDER);
351
   }
352
 
353
   // else are we asking how big/small we can resize ?
354
   else if (message == WM_GETMINMAXINFO)
355
   {
356
      minmax = (MINMAXINFO *) lParam; // get a pointer to the min/max info structure
357
 
358
      minmax->ptMinTrackSize.x = WINDOW_MIN_WIDTH;
359
      minmax->ptMinTrackSize.y = WINDOW_MIN_HEIGHT;
360
   }
361
 
362
   // else did we take action on one of the controls ?
363
   else if (message == WM_COMMAND)
364
   {
365
      // was the filter combo box changed ?
366
      if ((wParam_loword == WINDOW_COMBOBOX_GAMEIS) && (wParam_hiword == CBN_SELCHANGE))
367
      {
368
         soughtgames_updated = true; // remember sought games display is to be updated
369
         SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, NULL); // refresh window now
370
      }
371
 
372
      // else was the rated/unrated combo box changed ?
373
      if ((wParam_loword == WINDOW_COMBOBOX_RATEDUNRATED) && (wParam_hiword == CBN_SELCHANGE))
374
      {
375
         soughtgames_updated = true; // remember sought games display is to be updated
376
         SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, NULL); // refresh window now
377
      }
378
 
379
      // else was either of the rating edit box changed ?
380
      else if ((wParam_loword == WINDOW_EDITBOX_LEVELFROM) && (wParam_hiword == EN_CHANGE))
381
      {
382
         soughtgames_updated = true; // remember sought games display is to be updated
383
         SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, NULL); // refresh window now
384
      }
385
      else if ((wParam_loword == WINDOW_EDITBOX_LEVELTO) && (wParam_hiword == EN_CHANGE))
386
      {
387
         soughtgames_updated = true; // remember sought games display is to be updated
388
         SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, NULL); // refresh window now
389
      }
390
 
391
      // else was it the "seek" hyperlink ?
392
      else if (wParam_loword == WINDOW_TEXT_YOUCANALSOPOSTYOURSBYCLICKINGHERE)
393
         DialogBox_SendSeek (); // if so, open the seek dialog box
394
 
395
      // else was it the status bar hyperlink ?
396
      else if (wParam_loword == WINDOW_TEXT_STATUSBAR)
397
         ShellExecute (NULL, L"open", PROGRAM_URL, NULL, NULL, SW_MAXIMIZE); // open the donation page in the default browser, maximized
398
   }
399
 
400
   // else is it a timer event AND is it our refresh timer AND do we need to update the sought games list ?
401
   else if ((message == WM_TIMER) && (wParam == WINDOW_TIMER_REFRESH) && (soughtgames_updated))
402
   {
403
      // get a quick access to the list and the combo box controls
404
      hListWnd = GetDlgItem (hWnd, WINDOW_LIST_SOUGHTGAMES);
405
      hComboBoxWnd = GetDlgItem (hWnd, WINDOW_COMBOBOX_GAMEIS);
406
 
407
      // grab the different filters
408
      soughtgame_gameis = ComboBox_GetCurSel (hComboBoxWnd);
409
      if (soughtgame_gameis > gametype_count)
410
         soughtgame_gameis = 0; // consistency check
411
      if (soughtgame_gameis > 0)
412
         wcscpy_s (gametype_name, WCHAR_SIZEOF (gametype_name), gametypes[soughtgame_gameis - 1].name); // save game type name
413
      else
414
         gametype_name[0] = 0;
415
      soughtgame_ratedunrated = ComboBox_GetCurSel (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED));
416
      soughtgame_ratingfrom = GetDlgItemInt (hWnd, WINDOW_EDITBOX_LEVELFROM, &result, false);
417
      soughtgame_ratingto = GetDlgItemInt (hWnd, WINDOW_EDITBOX_LEVELTO, &result, false);
418
      if (result == 0)
419
         soughtgame_ratingto = 9999; // if we couldn't read a number, set the default value
420
 
421
      // first, build the game types drop box
422
 
423
      SAFE_free ((void **) &gametypes); // free the game types array
424
      gametype_count = 0; // and reset its count
425
 
426
      // cycle through all sought games...
427
      for (soughtgame_index = 0; soughtgame_index < soughtgame_count; soughtgame_index++)
428
      {
429
         // cycle through all game types we know and see if we find it
430
         for (gametype_index = 0; gametype_index < gametype_count; gametype_index++)
431
            if (wcscmp (gametypes[gametype_index].name, soughtgames[soughtgame_index].game_type) == 0)
432
               break; // break as soon as we find it
433
 
434
         // have we NOT found it ?
435
         if (gametype_index == gametype_count)
436
         {
437
            gametypes = (gametype_t *) SAFE_realloc (gametypes, gametype_count, gametype_count + 1, sizeof (gametype_t), false);
438
            wcscpy_s (gametypes[gametype_count].name, WCHAR_SIZEOF (gametypes[gametype_count].name), soughtgames[soughtgame_index].game_type);
439
            gametype_count++; // if so, reallocate game types array and remember one game type more
440
         }
441
      }
442
 
443
      // now populate the game types combo box
444
      ComboBox_ResetContent (hComboBoxWnd);
445
      ComboBox_AddString (hComboBoxWnd, L"");
446
      for (gametype_index = 0; gametype_index < gametype_count; gametype_index++)
447
         ComboBox_AddString (hComboBoxWnd, gametypes[gametype_index].name);
448
 
449
      // cycle through all the updated game types now...
450
      for (gametype_index = 0; gametype_index < gametype_count; gametype_index++)
451
         if (wcscmp (gametypes[gametype_index].name, gametype_name) == 0)
452
         {
453
            ComboBox_SetCurSel (hComboBoxWnd, gametype_index + 1); // put the selection back
454
            break; // and stop searching as soon as we've found it again
455
         }
456
 
457
      // populate the list control
458
      ListView_DeleteAllItems (hListWnd); // start by emptying it first
459
 
460
      // tell Windows which members of the LVCOLUMN structure we're filling
461
      memset (&lvi, 0, sizeof (lvi));
462
      lvi.mask = LVIF_IMAGE | LVIF_PARAM; // we want to set the image and the item's pointer
463
      for (soughtgame_index = 0; soughtgame_index < soughtgame_count; soughtgame_index++)
464
      {
465
         soughtgame = &soughtgames[soughtgame_index]; // quick access to sought game
466
 
467
         // should item be skipped because of its game type ?
468
         if ((soughtgame_gameis > 0) && (_wcsicmp (soughtgame->game_type, gametype_name) != 0))
469
            continue; // if so, skip it
470
 
471
         // should it be skipped because it's NOT rated and we want it to be, or the other way around ?
472
         if (((soughtgame->rating_type != GAMERATINGTYPE_SUPPORTEDUNRATED) && (soughtgame_ratedunrated == 1))
473
             || ((soughtgame->rating_type != GAMERATINGTYPE_SUPPORTEDRATED) && (soughtgame_ratedunrated == 2)))
474
            continue; // if so, skip it
475
 
476
         // should it be skipped because of its rating ?
477
         if (soughtgame->rating < soughtgame_ratingfrom)
478
            continue; // if so, skip it
479
         else if (soughtgame->rating > soughtgame_ratingto)
480
            continue; // if so, skip it
481
 
482
         // first, attach the right structure to this item
483
         lvi.lParam = (LPARAM) soughtgame;
484
 
485
         // set item's image and name
486
         lvi.iItem = soughtgame_index;
487
         if (soughtgame->rating_type != GAMERATINGTYPE_UNSUPPORTED)
488
            lvi.iImage = listviewicons[HANDLESTATUS_INGAME]; // supported variant
489
         else
490
            lvi.iImage = listviewicons[HANDLESTATUS_NOTOPENFORAMATCH]; // unsupported variant
491
 
492
         insert_index = ListView_InsertItem (hListWnd, &lvi); // add each item to list view
493
 
494
         // set item's substrings
495
 
496
         // game type
497
         ListView_SetItemText (hListWnd, insert_index, 0, soughtgame->game_type);
498
 
499
         // rated/unrated
500
         if (soughtgame->rating_type == GAMERATINGTYPE_SUPPORTEDRATED)
501
            ListView_SetItemText (hListWnd, insert_index, 1, LOCALIZE (L"Rated")) // rated game
502
         else if (soughtgame->rating_type == GAMERATINGTYPE_SUPPORTEDUNRATED)
503
            ListView_SetItemText (hListWnd, insert_index, 1, LOCALIZE (L"Unrated")) // unrated game
504
 
505
         // initial time
506
         if (soughtgame->initial_time > 0.0f)
507
         {
508
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"%.0f %s", soughtgame->initial_time, LOCALIZE (L"Minutes"));
509
            ListView_SetItemText (hListWnd, insert_index, 2, temp_string);
510
         }
511
 
512
         // Fischer increment
513
         if (soughtgame->increment > 0.0f)
514
         {
515
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"%.0f %s", soughtgame->increment, LOCALIZE (L"Seconds"));
516
            ListView_SetItemText (hListWnd, insert_index, 3, temp_string);
517
         }
518
 
519
         // opponent
520
         ListView_SetItemText (hListWnd, insert_index, 4, soughtgame->nickname);
521
 
522
         // opponent's ELO
523
         if (soughtgame->rating > 0)
524
         {
525
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"%d", soughtgame->rating);
526
            ListView_SetItemText (hListWnd, insert_index, 5, temp_string);
527
         }
528
 
529
         // requested color
530
         if (soughtgame->color != COLOR_UNSPECIFIED)
531
         {
532
            if (soughtgame->color == COLOR_BLACK)
533
               swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), LOCALIZE (L"Games_Black"));
534
            else
535
               swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), LOCALIZE (L"Games_White"));
536
            ListView_SetItemText (hListWnd, insert_index, 6, temp_string);
537
         }
538
 
539
         // minimal accepted ELO
540
         if (soughtgame->lowest_accepted > 0)
541
         {
542
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"> %d", soughtgame->lowest_accepted);
543
            ListView_SetItemText (hListWnd, insert_index, 7, temp_string);
544
         }
545
 
546
         // maximal accepted ELO
547
         if ((soughtgame->highest_accepted > 0) && (soughtgame->highest_accepted < 9999))
548
         {
549
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"< %d", soughtgame->highest_accepted);
550
            ListView_SetItemText (hListWnd, insert_index, 8, temp_string);
551
         }
552
 
553
         // whether this sought game's replies will be filtered
554
         if (soughtgame->formula_checked)
555
            ListView_SetItemText (hListWnd, insert_index, 9, L"×");
556
 
557
         // whether this sought game will start automatically
558
         if (!soughtgame->manual_start)
559
            ListView_SetItemText (hListWnd, insert_index, 10, L"×");
560
      }
561
 
562
      // now sort the list view according to the column we selected and its current sort order
563
      ListView_SortItems (hListWnd, CompareProc_ListSoughtGames, current_sortcolumn);
564
 
565
      // cycle through all columns and reset their sort order
566
      for (column_index = 0; column_index < sizeof (listviewcolumns) / sizeof (listviewcolumn_t); column_index++)
567
      {
568
         memset (&hdi, 0, sizeof (hdi));
569
         hdi.mask = HDI_FORMAT;
570
         Header_GetItem (ListView_GetHeader (hListWnd), column_index, &hdi); // get the column's sort arrow state
571
         hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
572
         if (column_index == current_sortcolumn)
573
            hdi.fmt |= (listviewcolumns[column_index].sort_descending ? HDF_SORTDOWN : HDF_SORTUP);
574
         Header_SetItem (ListView_GetHeader (hListWnd), column_index, &hdi); // update the column's sort arrow state
575
      }
576
 
577
      // now that the display is finished, IF the reply is arrived, set the totals in the window title
578
      if (soughtgame_count >= 0)
579
         SetWindowText (hWnd, LOCALIZE (L"SoughtGames_Title"));
580
 
581
      soughtgames_updated = false; // remember we updated the list (don't do it twice)
582
   }
583
 
584
   // else is it a list view message AND is it for this list view ?
585
   else if ((message == WM_NOTIFY) && (wParam == WINDOW_LIST_SOUGHTGAMES))
586
   {
587
      lv = (NMLISTVIEW *) lParam; // quick access to list view
588
 
589
      // is it a click on one of the headers' columns ?
590
      if (lv->hdr.code == LVN_COLUMNCLICK)
591
      {
592
         // cycle through all columns and reset their sort order
593
         for (column_index = 0; column_index < sizeof (listviewcolumns) / sizeof (listviewcolumn_t); column_index++)
594
         {
595
            memset (&hdi, 0, sizeof (hdi));
596
            hdi.mask = HDI_FORMAT;
597
            Header_GetItem (ListView_GetHeader (lv->hdr.hwndFrom), column_index, &hdi); // get the column's sort arrow state
598
            hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
599
            if (column_index == lv->iSubItem)
600
            {
601
               listviewcolumns[column_index].sort_descending ^= true; // revert the sort order for the clicked column
602
               hdi.fmt |= (listviewcolumns[column_index].sort_descending ? HDF_SORTDOWN : HDF_SORTUP);
603
               current_sortcolumn = column_index; // save the current sort column
604
            }
605
            else
606
               listviewcolumns[column_index].sort_descending = false; // reset the sort order for all the other ones
607
            Header_SetItem (ListView_GetHeader (lv->hdr.hwndFrom), column_index, &hdi); // update the column's sort arrow state
608
         }
609
 
610
         // now sort the list view according to the column we selected and its current sort order
611
         ListView_SortItems (lv->hdr.hwndFrom, CompareProc_ListSoughtGames, current_sortcolumn);
612
      }
613
 
614
      // else is it a double-click on one of the elements ?
615
      else if (lv->hdr.code == NM_DBLCLK)
616
      {
617
         clickeditem = (NMITEMACTIVATE *) lParam; // get the item it is
618
 
619
         // is it valid ?
620
         if (clickeditem->iItem != -1)
621
         {
622
            // get which item it is in the listview data
623
            memset (&lvi, 0, sizeof (lvi));
624
            lvi.iItem = clickeditem->iItem;
625
            lvi.mask = LVIF_IMAGE | LVIF_PARAM;
626
            ListView_GetItem (lv->hdr.hwndFrom, &lvi);
627
 
628
            soughtgame = (soughtgame_t *) lvi.lParam; // get the sought game it is
629
 
630
            local_player = Player_FindByType (PLAYER_HUMAN); // find the local player
631
 
632
            // is it NOT ourselves ?
633
            if ((local_player != NULL) && (wcscmp (soughtgame->nickname, local_player->name) != 0))
634
            {
635
               // is this an unsupported chess variant ?
636
               if (lvi.iImage == listviewicons[HANDLESTATUS_NOTOPENFORAMATCH])
637
               {
638
                  messagebox.hWndParent = hWnd;
639
                  wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), soughtgame->game_type);
640
                  wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_UnsupportedChessVariant"));
641
                  messagebox.flags = MB_ICONINFORMATION | MB_OK;
642
                  DialogBox_Message (&messagebox); // display a modeless error message box
643
               }
644
               else
645
               {
646
                  network_player = Player_FindByType (PLAYER_INTERNET); // quick access to network player
647
                  if (network_player == NULL)
648
                     return (0); // consistency check
649
 
650
                  // send the play notification and wait for the reply
651
                  Player_SendBuffer_Add (network_player, 1000, L"play %d\n", soughtgame->id);
652
 
653
                  // if this sought game is set to manual start, send a notification to this player's chat window
654
                  // HACK: don't display if we're likely to be refused because of guest not allowed to play rated games
655
                  if (soughtgame->manual_start && !((soughtgame->rating_type == GAMERATINGTYPE_SUPPORTEDRATED) && (wcscmp (options.network.login, L"guest") == 0)))
656
                     Interlocutor_Notify (Interlocutor_FindOrCreate (soughtgame->nickname), LOCALIZE (L"Chat_InvitationSent"), soughtgame->nickname);
657
               }
658
            }
659
            else
660
               DialogBox_SendSeek (); // else it's our own seek, so open the seek dialog box to modify it
661
         }
662
      }
663
   }
664
 
665
   // call the default window message processing function to keep things going
666
   return (DefWindowProc (hWnd, message, wParam, lParam));
667
}
668
 
669
 
670
static int CALLBACK CompareProc_ListSoughtGames (LPARAM lParam1, LPARAM lParam2, LPARAM column)
671
{
672
   // callback function that tells whether the lParam1 listview element comes before lParam2 in the
673
   // sort order of the specified column
674
 
675
   wchar_t *string_to_compare1;
676
   wchar_t *string_to_compare2;
677
 
678
   // game type
679
   if (column == 0)
680
   {
681
      string_to_compare1 = ((soughtgame_t *) lParam1)->game_type;
682
      string_to_compare2 = ((soughtgame_t *) lParam2)->game_type;
683
   }
684
 
685
   // rated/unrated
686
   else if (column == 1)
687
   {
688
      if (((soughtgame_t *) lParam1)->rating_type == GAMERATINGTYPE_SUPPORTEDRATED)
689
         string_to_compare1 = LOCALIZE (L"Rated");
690
      else if (((soughtgame_t *) lParam1)->rating_type == GAMERATINGTYPE_SUPPORTEDUNRATED)
691
         string_to_compare1 = LOCALIZE (L"Unrated");
692
      else
693
         string_to_compare1 = L"";
694
 
695
      if (((soughtgame_t *) lParam2)->rating_type == GAMERATINGTYPE_SUPPORTEDRATED)
696
         string_to_compare2 = LOCALIZE (L"Rated");
697
      else if (((soughtgame_t *) lParam2)->rating_type == GAMERATINGTYPE_SUPPORTEDUNRATED)
698
         string_to_compare2 = LOCALIZE (L"Unrated");
699
      else
700
         string_to_compare2 = L"";
701
   }
702
 
703
   // initial time
704
   else if (column == 2)
705
   {
706
      if (listviewcolumns[column].sort_descending)
707
         return (((soughtgame_t *) lParam1)->initial_time <= ((soughtgame_t *) lParam2)->initial_time);
708
      else
709
         return (((soughtgame_t *) lParam1)->initial_time >= ((soughtgame_t *) lParam2)->initial_time);
710
   }
711
 
712
   // increment
713
   else if (column == 3)
714
   {
715
      if (listviewcolumns[column].sort_descending)
716
         return (((soughtgame_t *) lParam1)->increment <= ((soughtgame_t *) lParam2)->increment);
717
      else
718
         return (((soughtgame_t *) lParam1)->increment >= ((soughtgame_t *) lParam2)->increment);
719
   }
720
 
721
   // opponent
722
   else if (column == 4)
723
   {
724
      string_to_compare1 = ((soughtgame_t *) lParam1)->nickname;
725
      string_to_compare2 = ((soughtgame_t *) lParam2)->nickname;
726
   }
727
 
728
   // rating
729
   else if (column == 5)
730
   {
731
      if (listviewcolumns[column].sort_descending)
732
         return (((soughtgame_t *) lParam1)->rating <= ((soughtgame_t *) lParam2)->rating);
733
      else
734
         return (((soughtgame_t *) lParam1)->rating >= ((soughtgame_t *) lParam2)->rating);
735
   }
736
 
737
   // color
738
   else if (column == 6)
739
   {
740
      if (((soughtgame_t *) lParam1)->color == COLOR_BLACK)
741
         string_to_compare1 = LOCALIZE (L"BlackMoves");
742
      else if (((soughtgame_t *) lParam1)->color == COLOR_WHITE)
743
         string_to_compare1 = LOCALIZE (L"WhiteMoves");
744
      else
745
         string_to_compare1 = L"";
746
 
747
      if (((soughtgame_t *) lParam2)->color == COLOR_BLACK)
748
         string_to_compare2 = LOCALIZE (L"BlackMoves");
749
      else if (((soughtgame_t *) lParam2)->color == COLOR_WHITE)
750
         string_to_compare2 = LOCALIZE (L"WhiteMoves");
751
      else
752
         string_to_compare2 = L"";
753
   }
754
 
755
   // accepts from
756
   else if (column == 7)
757
   {
758
      if (listviewcolumns[column].sort_descending)
759
         return (((soughtgame_t *) lParam1)->lowest_accepted <= ((soughtgame_t *) lParam2)->lowest_accepted);
760
      else
761
         return (((soughtgame_t *) lParam1)->lowest_accepted >= ((soughtgame_t *) lParam2)->lowest_accepted);
762
   }
763
 
764
   // up to
765
   else if (column == 8)
766
   {
767
      if (listviewcolumns[column].sort_descending)
768
         return (((soughtgame_t *) lParam1)->highest_accepted <= ((soughtgame_t *) lParam2)->highest_accepted);
769
      else
770
         return (((soughtgame_t *) lParam1)->highest_accepted >= ((soughtgame_t *) lParam2)->highest_accepted);
771
   }
772
 
773
   // filter
774
   else if (column == 9)
775
   {
776
      string_to_compare1 = (((soughtgame_t *) lParam1)->formula_checked ? L"×" : L" ");
777
      string_to_compare2 = (((soughtgame_t *) lParam2)->formula_checked ? L"×" : L" ");
778
   }
779
 
780
   // automatic
781
   else if (column == 10)
782
   {
783
      string_to_compare1 = (((soughtgame_t *) lParam1)->manual_start ? L" " : L"×");
784
      string_to_compare2 = (((soughtgame_t *) lParam2)->manual_start ? L" " : L"×");
785
   }
786
 
787
   // which order do we want this column to be sorted ?
788
   if (listviewcolumns[column].sort_descending)
789
      return (_wcsicmp (string_to_compare1, string_to_compare2)); // normal order
790
   else
791
      return (-_wcsicmp (string_to_compare1, string_to_compare2)); // reverse order
792
}
793
 
794
 
795
static int WINAPI ListView_WndProc (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
796
{
797
   // callback that subclasses the original ListView window procedure, so that we can hook
798
   // some of its messages
799
 
800
   static bool tooltips_initialized = false;
801
   static bool update_tooltips = false;
802
   WNDPROC BaseWndProc;
803
   TOOLINFO toolinfo;
804
   HWND hHeaderWnd;
805
   int column_index;
806
 
807
   // get a pointer to the base window procedure (it was stored as a window property)
808
   BaseWndProc = (WNDPROC) GetProp (hWnd, L"BaseWndProc");
809
   if (BaseWndProc == NULL)
810
      return (DefWindowProc (hWnd, message, wParam, lParam)); // consistency check
811
 
812
   // is the mouse moving around ?
813
   if (message == WM_MOUSEMOVE)
814
   {
815
      // do the tooltips need to be created ?
816
      if (!tooltips_initialized)
817
      {
818
         hHeaderWnd = (HWND) SendMessage (hWnd, LVM_GETHEADER, 0, 0); // get listview header
819
 
820
         // add a tooltip for each column
821
         for (column_index = 0; column_index < sizeof (listviewcolumns) / sizeof (listviewcolumn_t); column_index++)
822
         {
823
            // create the tooltip and set its window topmost
824
            listviewcolumns[column_index].hToolTipWnd = CreateWindowEx (WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
825
                                                                        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
826
                                                                        hHeaderWnd, NULL, hAppInstance, NULL);
827
 
828
            // associate the tooltip with the tool
829
            memset (&toolinfo, 0, sizeof (toolinfo));
830
            toolinfo.cbSize = sizeof (toolinfo);
831
            toolinfo.uFlags = TTF_SUBCLASS;
832
            toolinfo.hwnd = hHeaderWnd; // this tooltip works on the list header window handle
833
            toolinfo.uId = column_index; // tooltip ID will be column ID
834
            toolinfo.hinst = hAppInstance;
835
            toolinfo.lpszText = listviewcolumns[column_index].text; // tooltip text
836
            Header_GetItemRect (hHeaderWnd, column_index, &toolinfo.rect); // get header's item rectangle
837
            SendMessage (listviewcolumns[column_index].hToolTipWnd, TTM_ADDTOOL, 0, (LPARAM) &toolinfo);
838
         }
839
 
840
         tooltips_initialized = true; // do this only once
841
      }
842
 
843
      // else do the tooltips need to be updated ?
844
      else if (update_tooltips)
845
      {
846
         hHeaderWnd = (HWND) SendMessage (hWnd, LVM_GETHEADER, 0, 0); // get listview header
847
 
848
         // cycle through all columns
849
         for (column_index = 0; column_index < sizeof (listviewcolumns) / sizeof (listviewcolumn_t); column_index++)
850
         {
851
            // update the tooltip rectangle
852
            memset (&toolinfo, 0, sizeof (toolinfo));
853
            toolinfo.cbSize = sizeof (toolinfo);
854
            toolinfo.hwnd = hHeaderWnd; // this tooltip works on the list header window handle
855
            toolinfo.uId = column_index; // tooltip ID is column ID
856
            Header_GetItemRect (hHeaderWnd, column_index, &toolinfo.rect); // get header's item rectangle
857
            SendMessage (listviewcolumns[column_index].hToolTipWnd, TTM_NEWTOOLRECT, 0, (LPARAM) &toolinfo);
858
         }
859
 
860
         update_tooltips = false; // do this only once
861
      }
862
   }
863
 
864
   // else has the user finished dragging/resizing a column header ?
865
   else if ((message == WM_NOTIFY) && ((((NMHDR *) lParam)->code == HDN_ENDTRACK) || (((NMHDR *) lParam)->code == HDN_ENDDRAG)))
866
      update_tooltips = true; // if so, remember to update tooltips on the next mouse move
867
 
868
   // in any case, forward all messages to the original ListView hook procedure
869
   return (CallWindowProc (BaseWndProc, hWnd, message, wParam, lParam));
870
}