Subversion Repositories Games.Chess Giants

Rev

Rev 21 | Details | Compare with Previous | 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);
124 pmbaty 312
 
313
      return (0); // as MSDN says
1 pmbaty 314
   }
315
 
316
   // else did we click the close button on the title bar ?
317
   else if (message == WM_CLOSE)
124 pmbaty 318
   {
21 pmbaty 319
      DestroyWindow (hWnd); // close the window
124 pmbaty 320
      return (0); // as MSDN says
321
   }
1 pmbaty 322
 
323
   // else are we destroying the window ?
324
   else if (message == WM_DESTROY)
325
   {
326
      KillTimer (hWnd, WINDOW_TIMER_REFRESH); // destroy the timer we used to refresh the window
327
      hThisWnd = NULL; // window is closed
328
 
329
      // now that we're sure the window is closed...
330
      SAFE_free ((void **) &soughtgames); // free the sought games list we know
331
      soughtgame_count = 0; // and reset its count
332
 
333
      SAFE_free ((void **) &gametypes); // free the game types array
334
      gametype_count = 0; // and reset its count
335
 
336
      is_window_sought_validated = true; // remember we closed this window
124 pmbaty 337
      the_board.reevaluate = true; // refresh the GUI buttons if needed
338
      return (0); // as MSDN says
1 pmbaty 339
   }
340
 
341
   // else are we resizing the window ?
342
   else if (message == WM_SIZE)
343
   {
344
      // get the new window size
345
      GetClientRect (hWnd, &client_rect);
346
 
347
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_DOUBLECLICKORNARROWDOWN), NULL, 16, 16, client_rect.right - 32, 16, SWP_NOZORDER);
348
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_GAMEIS),           NULL,   8, 40, 208, 16, SWP_NOZORDER);
349
      SetWindowPos (GetDlgItem (hWnd, WINDOW_COMBOBOX_GAMEIS),       NULL, 224, 38, 128, 20, SWP_NOZORDER);
350
      SetWindowPos (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED), NULL, 384, 38,  96, 20, SWP_NOZORDER);
351
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_LEVELFROM),        NULL, 512, 40, 120, 16, SWP_NOZORDER);
352
      SetWindowPos (GetDlgItem (hWnd, WINDOW_EDITBOX_LEVELFROM),     NULL, 640, 38,  48, 20, SWP_NOZORDER);
353
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_LEVELTO),          NULL, 688, 40,  40, 16, SWP_NOZORDER);
354
      SetWindowPos (GetDlgItem (hWnd, WINDOW_EDITBOX_LEVELTO),       NULL, 736, 38,  48, 20, SWP_NOZORDER);
355
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_YOUCANALSOPOSTYOURSBYCLICKINGHERE), NULL, 16, 64, client_rect.right - 32, 16, SWP_NOZORDER);
356
      SetWindowPos (GetDlgItem (hWnd, WINDOW_LIST_SOUGHTGAMES), NULL, 16, 88, client_rect.right - 32, client_rect.bottom - 104, SWP_NOZORDER);
357
      SetWindowPos (GetDlgItem (hWnd, WINDOW_TEXT_STATUSBAR), NULL, 0, client_rect.bottom - 16, client_rect.right, 16, SWP_NOZORDER);
124 pmbaty 358
 
359
      return (0); // as MSDN says
1 pmbaty 360
   }
361
 
362
   // else are we asking how big/small we can resize ?
363
   else if (message == WM_GETMINMAXINFO)
364
   {
365
      minmax = (MINMAXINFO *) lParam; // get a pointer to the min/max info structure
366
 
367
      minmax->ptMinTrackSize.x = WINDOW_MIN_WIDTH;
368
      minmax->ptMinTrackSize.y = WINDOW_MIN_HEIGHT;
124 pmbaty 369
 
370
      return (0); // as MSDN says
1 pmbaty 371
   }
372
 
373
   // else did we take action on one of the controls ?
374
   else if (message == WM_COMMAND)
375
   {
376
      // was the filter combo box changed ?
377
      if ((wParam_loword == WINDOW_COMBOBOX_GAMEIS) && (wParam_hiword == CBN_SELCHANGE))
378
      {
379
         soughtgames_updated = true; // remember sought games display is to be updated
380
         SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, NULL); // refresh window now
381
      }
382
 
383
      // else was the rated/unrated combo box changed ?
384
      if ((wParam_loword == WINDOW_COMBOBOX_RATEDUNRATED) && (wParam_hiword == CBN_SELCHANGE))
385
      {
386
         soughtgames_updated = true; // remember sought games display is to be updated
387
         SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, NULL); // refresh window now
388
      }
389
 
390
      // else was either of the rating edit box changed ?
391
      else if ((wParam_loword == WINDOW_EDITBOX_LEVELFROM) && (wParam_hiword == EN_CHANGE))
392
      {
393
         soughtgames_updated = true; // remember sought games display is to be updated
394
         SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, NULL); // refresh window now
395
      }
396
      else if ((wParam_loword == WINDOW_EDITBOX_LEVELTO) && (wParam_hiword == EN_CHANGE))
397
      {
398
         soughtgames_updated = true; // remember sought games display is to be updated
399
         SendMessage (hWnd, WM_TIMER, WINDOW_TIMER_REFRESH, NULL); // refresh window now
400
      }
401
 
402
      // else was it the "seek" hyperlink ?
403
      else if (wParam_loword == WINDOW_TEXT_YOUCANALSOPOSTYOURSBYCLICKINGHERE)
404
         DialogBox_SendSeek (); // if so, open the seek dialog box
405
 
406
      // else was it the status bar hyperlink ?
407
      else if (wParam_loword == WINDOW_TEXT_STATUSBAR)
408
         ShellExecute (NULL, L"open", PROGRAM_URL, NULL, NULL, SW_MAXIMIZE); // open the donation page in the default browser, maximized
124 pmbaty 409
 
410
      return (0); // as MSDN says
1 pmbaty 411
   }
412
 
413
   // else is it a timer event AND is it our refresh timer AND do we need to update the sought games list ?
414
   else if ((message == WM_TIMER) && (wParam == WINDOW_TIMER_REFRESH) && (soughtgames_updated))
415
   {
416
      // get a quick access to the list and the combo box controls
417
      hListWnd = GetDlgItem (hWnd, WINDOW_LIST_SOUGHTGAMES);
418
      hComboBoxWnd = GetDlgItem (hWnd, WINDOW_COMBOBOX_GAMEIS);
419
 
420
      // grab the different filters
421
      soughtgame_gameis = ComboBox_GetCurSel (hComboBoxWnd);
422
      if (soughtgame_gameis > gametype_count)
423
         soughtgame_gameis = 0; // consistency check
424
      if (soughtgame_gameis > 0)
425
         wcscpy_s (gametype_name, WCHAR_SIZEOF (gametype_name), gametypes[soughtgame_gameis - 1].name); // save game type name
426
      else
427
         gametype_name[0] = 0;
428
      soughtgame_ratedunrated = ComboBox_GetCurSel (GetDlgItem (hWnd, WINDOW_COMBOBOX_RATEDUNRATED));
429
      soughtgame_ratingfrom = GetDlgItemInt (hWnd, WINDOW_EDITBOX_LEVELFROM, &result, false);
430
      soughtgame_ratingto = GetDlgItemInt (hWnd, WINDOW_EDITBOX_LEVELTO, &result, false);
431
      if (result == 0)
432
         soughtgame_ratingto = 9999; // if we couldn't read a number, set the default value
433
 
434
      // first, build the game types drop box
435
 
436
      SAFE_free ((void **) &gametypes); // free the game types array
437
      gametype_count = 0; // and reset its count
438
 
439
      // cycle through all sought games...
440
      for (soughtgame_index = 0; soughtgame_index < soughtgame_count; soughtgame_index++)
441
      {
442
         // cycle through all game types we know and see if we find it
443
         for (gametype_index = 0; gametype_index < gametype_count; gametype_index++)
444
            if (wcscmp (gametypes[gametype_index].name, soughtgames[soughtgame_index].game_type) == 0)
445
               break; // break as soon as we find it
446
 
447
         // have we NOT found it ?
448
         if (gametype_index == gametype_count)
449
         {
450
            gametypes = (gametype_t *) SAFE_realloc (gametypes, gametype_count, gametype_count + 1, sizeof (gametype_t), false);
451
            wcscpy_s (gametypes[gametype_count].name, WCHAR_SIZEOF (gametypes[gametype_count].name), soughtgames[soughtgame_index].game_type);
452
            gametype_count++; // if so, reallocate game types array and remember one game type more
453
         }
454
      }
455
 
456
      // now populate the game types combo box
457
      ComboBox_ResetContent (hComboBoxWnd);
458
      ComboBox_AddString (hComboBoxWnd, L"");
459
      for (gametype_index = 0; gametype_index < gametype_count; gametype_index++)
460
         ComboBox_AddString (hComboBoxWnd, gametypes[gametype_index].name);
461
 
462
      // cycle through all the updated game types now...
463
      for (gametype_index = 0; gametype_index < gametype_count; gametype_index++)
464
         if (wcscmp (gametypes[gametype_index].name, gametype_name) == 0)
465
         {
466
            ComboBox_SetCurSel (hComboBoxWnd, gametype_index + 1); // put the selection back
467
            break; // and stop searching as soon as we've found it again
468
         }
469
 
470
      // populate the list control
471
      ListView_DeleteAllItems (hListWnd); // start by emptying it first
472
 
473
      // tell Windows which members of the LVCOLUMN structure we're filling
474
      memset (&lvi, 0, sizeof (lvi));
475
      lvi.mask = LVIF_IMAGE | LVIF_PARAM; // we want to set the image and the item's pointer
476
      for (soughtgame_index = 0; soughtgame_index < soughtgame_count; soughtgame_index++)
477
      {
478
         soughtgame = &soughtgames[soughtgame_index]; // quick access to sought game
479
 
480
         // should item be skipped because of its game type ?
481
         if ((soughtgame_gameis > 0) && (_wcsicmp (soughtgame->game_type, gametype_name) != 0))
482
            continue; // if so, skip it
483
 
484
         // should it be skipped because it's NOT rated and we want it to be, or the other way around ?
485
         if (((soughtgame->rating_type != GAMERATINGTYPE_SUPPORTEDUNRATED) && (soughtgame_ratedunrated == 1))
486
             || ((soughtgame->rating_type != GAMERATINGTYPE_SUPPORTEDRATED) && (soughtgame_ratedunrated == 2)))
487
            continue; // if so, skip it
488
 
489
         // should it be skipped because of its rating ?
490
         if (soughtgame->rating < soughtgame_ratingfrom)
491
            continue; // if so, skip it
492
         else if (soughtgame->rating > soughtgame_ratingto)
493
            continue; // if so, skip it
494
 
495
         // first, attach the right structure to this item
496
         lvi.lParam = (LPARAM) soughtgame;
497
 
498
         // set item's image and name
499
         lvi.iItem = soughtgame_index;
500
         if (soughtgame->rating_type != GAMERATINGTYPE_UNSUPPORTED)
501
            lvi.iImage = listviewicons[HANDLESTATUS_INGAME]; // supported variant
502
         else
503
            lvi.iImage = listviewicons[HANDLESTATUS_NOTOPENFORAMATCH]; // unsupported variant
504
 
505
         insert_index = ListView_InsertItem (hListWnd, &lvi); // add each item to list view
506
 
507
         // set item's substrings
508
 
509
         // game type
510
         ListView_SetItemText (hListWnd, insert_index, 0, soughtgame->game_type);
511
 
512
         // rated/unrated
513
         if (soughtgame->rating_type == GAMERATINGTYPE_SUPPORTEDRATED)
514
            ListView_SetItemText (hListWnd, insert_index, 1, LOCALIZE (L"Rated")) // rated game
515
         else if (soughtgame->rating_type == GAMERATINGTYPE_SUPPORTEDUNRATED)
516
            ListView_SetItemText (hListWnd, insert_index, 1, LOCALIZE (L"Unrated")) // unrated game
517
 
518
         // initial time
519
         if (soughtgame->initial_time > 0.0f)
520
         {
521
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"%.0f %s", soughtgame->initial_time, LOCALIZE (L"Minutes"));
522
            ListView_SetItemText (hListWnd, insert_index, 2, temp_string);
523
         }
524
 
525
         // Fischer increment
526
         if (soughtgame->increment > 0.0f)
527
         {
528
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"%.0f %s", soughtgame->increment, LOCALIZE (L"Seconds"));
529
            ListView_SetItemText (hListWnd, insert_index, 3, temp_string);
530
         }
531
 
532
         // opponent
533
         ListView_SetItemText (hListWnd, insert_index, 4, soughtgame->nickname);
534
 
535
         // opponent's ELO
536
         if (soughtgame->rating > 0)
537
         {
538
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"%d", soughtgame->rating);
539
            ListView_SetItemText (hListWnd, insert_index, 5, temp_string);
540
         }
541
 
542
         // requested color
543
         if (soughtgame->color != COLOR_UNSPECIFIED)
544
         {
545
            if (soughtgame->color == COLOR_BLACK)
546
               swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), LOCALIZE (L"Games_Black"));
547
            else
548
               swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), LOCALIZE (L"Games_White"));
549
            ListView_SetItemText (hListWnd, insert_index, 6, temp_string);
550
         }
551
 
552
         // minimal accepted ELO
553
         if (soughtgame->lowest_accepted > 0)
554
         {
555
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"> %d", soughtgame->lowest_accepted);
556
            ListView_SetItemText (hListWnd, insert_index, 7, temp_string);
557
         }
558
 
559
         // maximal accepted ELO
560
         if ((soughtgame->highest_accepted > 0) && (soughtgame->highest_accepted < 9999))
561
         {
562
            swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), L"< %d", soughtgame->highest_accepted);
563
            ListView_SetItemText (hListWnd, insert_index, 8, temp_string);
564
         }
565
 
566
         // whether this sought game's replies will be filtered
567
         if (soughtgame->formula_checked)
568
            ListView_SetItemText (hListWnd, insert_index, 9, L"×");
569
 
570
         // whether this sought game will start automatically
571
         if (!soughtgame->manual_start)
572
            ListView_SetItemText (hListWnd, insert_index, 10, L"×");
573
      }
574
 
575
      // now sort the list view according to the column we selected and its current sort order
576
      ListView_SortItems (hListWnd, CompareProc_ListSoughtGames, current_sortcolumn);
577
 
578
      // cycle through all columns and reset their sort order
579
      for (column_index = 0; column_index < sizeof (listviewcolumns) / sizeof (listviewcolumn_t); column_index++)
580
      {
581
         memset (&hdi, 0, sizeof (hdi));
582
         hdi.mask = HDI_FORMAT;
583
         Header_GetItem (ListView_GetHeader (hListWnd), column_index, &hdi); // get the column's sort arrow state
584
         hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
585
         if (column_index == current_sortcolumn)
586
            hdi.fmt |= (listviewcolumns[column_index].sort_descending ? HDF_SORTDOWN : HDF_SORTUP);
587
         Header_SetItem (ListView_GetHeader (hListWnd), column_index, &hdi); // update the column's sort arrow state
588
      }
589
 
590
      // now that the display is finished, IF the reply is arrived, set the totals in the window title
591
      if (soughtgame_count >= 0)
592
         SetWindowText (hWnd, LOCALIZE (L"SoughtGames_Title"));
593
 
594
      soughtgames_updated = false; // remember we updated the list (don't do it twice)
124 pmbaty 595
 
596
      return (0); // as MSDN says
1 pmbaty 597
   }
598
 
599
   // else is it a list view message AND is it for this list view ?
600
   else if ((message == WM_NOTIFY) && (wParam == WINDOW_LIST_SOUGHTGAMES))
601
   {
602
      lv = (NMLISTVIEW *) lParam; // quick access to list view
603
 
604
      // is it a click on one of the headers' columns ?
605
      if (lv->hdr.code == LVN_COLUMNCLICK)
606
      {
607
         // cycle through all columns and reset their sort order
608
         for (column_index = 0; column_index < sizeof (listviewcolumns) / sizeof (listviewcolumn_t); column_index++)
609
         {
610
            memset (&hdi, 0, sizeof (hdi));
611
            hdi.mask = HDI_FORMAT;
612
            Header_GetItem (ListView_GetHeader (lv->hdr.hwndFrom), column_index, &hdi); // get the column's sort arrow state
613
            hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
614
            if (column_index == lv->iSubItem)
615
            {
616
               listviewcolumns[column_index].sort_descending ^= true; // revert the sort order for the clicked column
617
               hdi.fmt |= (listviewcolumns[column_index].sort_descending ? HDF_SORTDOWN : HDF_SORTUP);
618
               current_sortcolumn = column_index; // save the current sort column
619
            }
620
            else
621
               listviewcolumns[column_index].sort_descending = false; // reset the sort order for all the other ones
622
            Header_SetItem (ListView_GetHeader (lv->hdr.hwndFrom), column_index, &hdi); // update the column's sort arrow state
623
         }
624
 
625
         // now sort the list view according to the column we selected and its current sort order
626
         ListView_SortItems (lv->hdr.hwndFrom, CompareProc_ListSoughtGames, current_sortcolumn);
627
      }
628
 
629
      // else is it a double-click on one of the elements ?
630
      else if (lv->hdr.code == NM_DBLCLK)
631
      {
632
         clickeditem = (NMITEMACTIVATE *) lParam; // get the item it is
633
 
634
         // is it valid ?
635
         if (clickeditem->iItem != -1)
636
         {
637
            // get which item it is in the listview data
638
            memset (&lvi, 0, sizeof (lvi));
639
            lvi.iItem = clickeditem->iItem;
640
            lvi.mask = LVIF_IMAGE | LVIF_PARAM;
641
            ListView_GetItem (lv->hdr.hwndFrom, &lvi);
642
 
643
            soughtgame = (soughtgame_t *) lvi.lParam; // get the sought game it is
644
 
645
            local_player = Player_FindByType (PLAYER_HUMAN); // find the local player
646
 
647
            // is it NOT ourselves ?
648
            if ((local_player != NULL) && (wcscmp (soughtgame->nickname, local_player->name) != 0))
649
            {
650
               // is this an unsupported chess variant ?
651
               if (lvi.iImage == listviewicons[HANDLESTATUS_NOTOPENFORAMATCH])
652
               {
653
                  messagebox.hWndParent = hWnd;
654
                  wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), soughtgame->game_type);
655
                  wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_UnsupportedChessVariant"));
656
                  messagebox.flags = MB_ICONINFORMATION | MB_OK;
657
                  DialogBox_Message (&messagebox); // display a modeless error message box
658
               }
659
               else
660
               {
661
                  network_player = Player_FindByType (PLAYER_INTERNET); // quick access to network player
662
                  if (network_player == NULL)
663
                     return (0); // consistency check
664
 
665
                  // send the play notification and wait for the reply
666
                  Player_SendBuffer_Add (network_player, 1000, L"play %d\n", soughtgame->id);
667
 
668
                  // if this sought game is set to manual start, send a notification to this player's chat window
669
                  // HACK: don't display if we're likely to be refused because of guest not allowed to play rated games
670
                  if (soughtgame->manual_start && !((soughtgame->rating_type == GAMERATINGTYPE_SUPPORTEDRATED) && (wcscmp (options.network.login, L"guest") == 0)))
671
                     Interlocutor_Notify (Interlocutor_FindOrCreate (soughtgame->nickname), LOCALIZE (L"Chat_InvitationSent"), soughtgame->nickname);
672
               }
673
            }
674
            else
675
               DialogBox_SendSeek (); // else it's our own seek, so open the seek dialog box to modify it
676
         }
677
      }
124 pmbaty 678
 
679
      return (0); // as MSDN says
1 pmbaty 680
   }
681
 
682
   // call the default window message processing function to keep things going
683
   return (DefWindowProc (hWnd, message, wParam, lParam));
684
}
685
 
686
 
687
static int CALLBACK CompareProc_ListSoughtGames (LPARAM lParam1, LPARAM lParam2, LPARAM column)
688
{
689
   // callback function that tells whether the lParam1 listview element comes before lParam2 in the
690
   // sort order of the specified column
691
 
692
   wchar_t *string_to_compare1;
693
   wchar_t *string_to_compare2;
694
 
695
   // game type
696
   if (column == 0)
697
   {
698
      string_to_compare1 = ((soughtgame_t *) lParam1)->game_type;
699
      string_to_compare2 = ((soughtgame_t *) lParam2)->game_type;
700
   }
701
 
702
   // rated/unrated
703
   else if (column == 1)
704
   {
705
      if (((soughtgame_t *) lParam1)->rating_type == GAMERATINGTYPE_SUPPORTEDRATED)
706
         string_to_compare1 = LOCALIZE (L"Rated");
707
      else if (((soughtgame_t *) lParam1)->rating_type == GAMERATINGTYPE_SUPPORTEDUNRATED)
708
         string_to_compare1 = LOCALIZE (L"Unrated");
709
      else
710
         string_to_compare1 = L"";
711
 
712
      if (((soughtgame_t *) lParam2)->rating_type == GAMERATINGTYPE_SUPPORTEDRATED)
713
         string_to_compare2 = LOCALIZE (L"Rated");
714
      else if (((soughtgame_t *) lParam2)->rating_type == GAMERATINGTYPE_SUPPORTEDUNRATED)
715
         string_to_compare2 = LOCALIZE (L"Unrated");
716
      else
717
         string_to_compare2 = L"";
718
   }
719
 
720
   // initial time
721
   else if (column == 2)
722
   {
723
      if (listviewcolumns[column].sort_descending)
724
         return (((soughtgame_t *) lParam1)->initial_time <= ((soughtgame_t *) lParam2)->initial_time);
725
      else
726
         return (((soughtgame_t *) lParam1)->initial_time >= ((soughtgame_t *) lParam2)->initial_time);
727
   }
728
 
729
   // increment
730
   else if (column == 3)
731
   {
732
      if (listviewcolumns[column].sort_descending)
733
         return (((soughtgame_t *) lParam1)->increment <= ((soughtgame_t *) lParam2)->increment);
734
      else
735
         return (((soughtgame_t *) lParam1)->increment >= ((soughtgame_t *) lParam2)->increment);
736
   }
737
 
738
   // opponent
739
   else if (column == 4)
740
   {
741
      string_to_compare1 = ((soughtgame_t *) lParam1)->nickname;
742
      string_to_compare2 = ((soughtgame_t *) lParam2)->nickname;
743
   }
744
 
745
   // rating
746
   else if (column == 5)
747
   {
748
      if (listviewcolumns[column].sort_descending)
749
         return (((soughtgame_t *) lParam1)->rating <= ((soughtgame_t *) lParam2)->rating);
750
      else
751
         return (((soughtgame_t *) lParam1)->rating >= ((soughtgame_t *) lParam2)->rating);
752
   }
753
 
754
   // color
755
   else if (column == 6)
756
   {
757
      if (((soughtgame_t *) lParam1)->color == COLOR_BLACK)
758
         string_to_compare1 = LOCALIZE (L"BlackMoves");
759
      else if (((soughtgame_t *) lParam1)->color == COLOR_WHITE)
760
         string_to_compare1 = LOCALIZE (L"WhiteMoves");
761
      else
762
         string_to_compare1 = L"";
763
 
764
      if (((soughtgame_t *) lParam2)->color == COLOR_BLACK)
765
         string_to_compare2 = LOCALIZE (L"BlackMoves");
766
      else if (((soughtgame_t *) lParam2)->color == COLOR_WHITE)
767
         string_to_compare2 = LOCALIZE (L"WhiteMoves");
768
      else
769
         string_to_compare2 = L"";
770
   }
771
 
772
   // accepts from
773
   else if (column == 7)
774
   {
775
      if (listviewcolumns[column].sort_descending)
776
         return (((soughtgame_t *) lParam1)->lowest_accepted <= ((soughtgame_t *) lParam2)->lowest_accepted);
777
      else
778
         return (((soughtgame_t *) lParam1)->lowest_accepted >= ((soughtgame_t *) lParam2)->lowest_accepted);
779
   }
780
 
781
   // up to
782
   else if (column == 8)
783
   {
784
      if (listviewcolumns[column].sort_descending)
785
         return (((soughtgame_t *) lParam1)->highest_accepted <= ((soughtgame_t *) lParam2)->highest_accepted);
786
      else
787
         return (((soughtgame_t *) lParam1)->highest_accepted >= ((soughtgame_t *) lParam2)->highest_accepted);
788
   }
789
 
790
   // filter
791
   else if (column == 9)
792
   {
793
      string_to_compare1 = (((soughtgame_t *) lParam1)->formula_checked ? L"×" : L" ");
794
      string_to_compare2 = (((soughtgame_t *) lParam2)->formula_checked ? L"×" : L" ");
795
   }
796
 
797
   // automatic
798
   else if (column == 10)
799
   {
800
      string_to_compare1 = (((soughtgame_t *) lParam1)->manual_start ? L" " : L"×");
801
      string_to_compare2 = (((soughtgame_t *) lParam2)->manual_start ? L" " : L"×");
802
   }
803
 
804
   // which order do we want this column to be sorted ?
805
   if (listviewcolumns[column].sort_descending)
806
      return (_wcsicmp (string_to_compare1, string_to_compare2)); // normal order
807
   else
808
      return (-_wcsicmp (string_to_compare1, string_to_compare2)); // reverse order
809
}
810
 
811
 
812
static int WINAPI ListView_WndProc (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
813
{
814
   // callback that subclasses the original ListView window procedure, so that we can hook
815
   // some of its messages
816
 
817
   static bool tooltips_initialized = false;
818
   static bool update_tooltips = false;
819
   WNDPROC BaseWndProc;
820
   TOOLINFO toolinfo;
821
   HWND hHeaderWnd;
822
   int column_index;
823
 
824
   // get a pointer to the base window procedure (it was stored as a window property)
825
   BaseWndProc = (WNDPROC) GetProp (hWnd, L"BaseWndProc");
826
   if (BaseWndProc == NULL)
827
      return (DefWindowProc (hWnd, message, wParam, lParam)); // consistency check
828
 
829
   // is the mouse moving around ?
830
   if (message == WM_MOUSEMOVE)
831
   {
832
      // do the tooltips need to be created ?
833
      if (!tooltips_initialized)
834
      {
835
         hHeaderWnd = (HWND) SendMessage (hWnd, LVM_GETHEADER, 0, 0); // get listview header
836
 
837
         // add a tooltip for each column
838
         for (column_index = 0; column_index < sizeof (listviewcolumns) / sizeof (listviewcolumn_t); column_index++)
839
         {
840
            // create the tooltip and set its window topmost
841
            listviewcolumns[column_index].hToolTipWnd = CreateWindowEx (WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
842
                                                                        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
843
                                                                        hHeaderWnd, NULL, hAppInstance, NULL);
844
 
845
            // associate the tooltip with the tool
846
            memset (&toolinfo, 0, sizeof (toolinfo));
847
            toolinfo.cbSize = sizeof (toolinfo);
848
            toolinfo.uFlags = TTF_SUBCLASS;
849
            toolinfo.hwnd = hHeaderWnd; // this tooltip works on the list header window handle
850
            toolinfo.uId = column_index; // tooltip ID will be column ID
851
            toolinfo.hinst = hAppInstance;
852
            toolinfo.lpszText = listviewcolumns[column_index].text; // tooltip text
853
            Header_GetItemRect (hHeaderWnd, column_index, &toolinfo.rect); // get header's item rectangle
854
            SendMessage (listviewcolumns[column_index].hToolTipWnd, TTM_ADDTOOL, 0, (LPARAM) &toolinfo);
855
         }
856
 
857
         tooltips_initialized = true; // do this only once
858
      }
859
 
860
      // else do the tooltips need to be updated ?
861
      else if (update_tooltips)
862
      {
863
         hHeaderWnd = (HWND) SendMessage (hWnd, LVM_GETHEADER, 0, 0); // get listview header
864
 
865
         // cycle through all columns
866
         for (column_index = 0; column_index < sizeof (listviewcolumns) / sizeof (listviewcolumn_t); column_index++)
867
         {
868
            // update the tooltip rectangle
869
            memset (&toolinfo, 0, sizeof (toolinfo));
870
            toolinfo.cbSize = sizeof (toolinfo);
871
            toolinfo.hwnd = hHeaderWnd; // this tooltip works on the list header window handle
872
            toolinfo.uId = column_index; // tooltip ID is column ID
873
            Header_GetItemRect (hHeaderWnd, column_index, &toolinfo.rect); // get header's item rectangle
874
            SendMessage (listviewcolumns[column_index].hToolTipWnd, TTM_NEWTOOLRECT, 0, (LPARAM) &toolinfo);
875
         }
876
 
877
         update_tooltips = false; // do this only once
878
      }
879
   }
880
 
881
   // else has the user finished dragging/resizing a column header ?
882
   else if ((message == WM_NOTIFY) && ((((NMHDR *) lParam)->code == HDN_ENDTRACK) || (((NMHDR *) lParam)->code == HDN_ENDDRAG)))
883
      update_tooltips = true; // if so, remember to update tooltips on the next mouse move
884
 
885
   // in any case, forward all messages to the original ListView hook procedure
886
   return (CallWindowProc (BaseWndProc, hWnd, message, wParam, lParam));
887
}