Subversion Repositories Games.Chess Giants

Rev

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

Rev Author Line No. Line
1 pmbaty 1
// tabcontrol.cpp
2
 
3
#include <windows.h>
4
#include <commctrl.h>
5
 
6
 
7
typedef struct tabpage_s
8
{
9
   HWND hWnd; // window handle of the controls tab page sub-window
10
} tabpage_t;
11
 
12
 
13
typedef struct tabcontrol_s
14
{
15
   HWND hWnd; // window handle of the tab control
16
   tabpage_t *pages; // mallocated array of pages in this tab control
17
   int page_count; // number of pages in this tab control
18
   int activepage_index; // index of the active page
19
   WNDPROC ParentProc; // function pointer to Parent Dialog Proc
20
} tabcontrol_t;
21
 
22
 
23
// prototypes of exported functions
24
void *TabControl_New (HWND hTabControlWnd, WNDPROC ParentProc);
25
void TabControl_AddPage (void *tab_control, wchar_t *page_name, int dialog_id);
26
void TabControl_SelectTab (void *tab_control, int page_index);
27
HWND TabControl_GetItem (void *tab_control, int item_id);
28
void TabControl_Destroy (void *tab_control);
29
bool TabControl_Notify (NMHDR *notification);
30
 
31
 
32
// prototypes of local functions
33
static void TabControl_SetFocusOnFirstTabStop (HWND hWnd);
34
static void TabControl_GetClientRect (HWND hWnd, RECT *client_rect);
35
static int CALLBACK TabPage_DlgProc (HWND hwndDlg, unsigned int message, WPARAM wParam, LPARAM lParam);
36
static void TabPage_MessageLoop (HWND hWnd);
37
 
38
 
39
static void TabControl_SetFocusOnFirstTabStop (HWND hWnd)
40
{
41
   // this function ensures focus on first tab stop when entering a tab page.
42
   // WM_NEXTDLGCTL wparam = 1 shifts focus back to the LAST tabstop in this tabpage.
43
   // Posting a VK_TAB returns focus to the first tab stop and causes it to be recognized
44
   // as the first tabstop by the WM_KEYDOWN sniffer in the TabPageMessageLoop().
45
 
46
   SendMessage (hWnd, WM_NEXTDLGCTL, 1, FALSE);
47
   PostMessage (GetFocus (), WM_KEYDOWN, VK_TAB, 0);
48
 
49
   return;
50
}
51
 
52
 
53
static void TabControl_GetClientRect (HWND hWnd, RECT *client_rect)
54
{
55
   // returns the client rectangle for the tab control under every possible configuration
56
   // (normal tabs, tabs with buttons, vertical tabs, etc.)
57
   // IMPORTANT: the rectangle structure is populated as follows:
58
   // left = left, top = top, right = WIDTH, bottom = HEIGHT
59
 
60
   long tab_style;
61
   RECT tab_rect;
62
 
63
   tab_style = GetWindowLong (hWnd, GWL_STYLE);
64
 
65
   // calculate the tab control's display area
66
   GetWindowRect (hWnd, client_rect);
67
   ScreenToClient (GetParent (hWnd), (POINT *) &client_rect->left);
68
   ScreenToClient (hWnd, (POINT *) &client_rect->right);
69
   TabCtrl_GetItemRect (hWnd, 0, &tab_rect); // the tab itself
70
 
71
   // vertical tabs to the right ?
72
   if ((tab_style & TCS_BOTTOM) && (tab_style & TCS_VERTICAL))
73
   {
74
      client_rect->top += 6; // x coord
75
      client_rect->left += 4; // y coord
76
      client_rect->bottom -= 12; // height
77
      client_rect->right -= (12 + tab_rect.right - tab_rect.left); // width
78
   }
79
 
80
   // vertical tabs to the left ?
81
   else if (tab_style & TCS_VERTICAL)
82
   {
83
      client_rect->top += 6; //x coord
84
      client_rect->left += (4 + tab_rect.right - tab_rect.left); // y coord
85
      client_rect->bottom -= 12; // height
86
      client_rect->right -= (12 + tab_rect.right - tab_rect.left); // width
87
   }
88
 
89
   // horizontal tabs at the bottom ?
90
   else if (tab_style & TCS_BOTTOM)
91
   {
92
      client_rect->top += 6; // x coord
93
      client_rect->left += 4; // y coord
94
      client_rect->bottom -= (16 + tab_rect.bottom - tab_rect.top); // height
95
      client_rect->right -= 12; // width
96
   }
97
 
98
   // else it must be horizontal tabs at the top (default style)
99
   else
100
   {
101
      client_rect->top += (6 + tab_rect.bottom - tab_rect.top); // x coord
102
      client_rect->left += 4; // y coord
103
      client_rect->bottom -= (16 + tab_rect.bottom - tab_rect.top); // height
104
      client_rect->right -= 12; // width
105
   }
106
 
107
   return; // finished populating the rectangle
108
}
109
 
110
 
111
static int CALLBACK TabPage_DlgProc (HWND hwndDlg, unsigned int message, WPARAM wParam, LPARAM lParam)
112
{
113
   // window procedure for tab page dialogs
114
 
115
   tabcontrol_t *tab_control;
116
   NMTCKEYDOWN nm;
117
   HWND hWndTab;
118
 
119
   // get a pointer to the tab container window
120
   hWndTab = (HWND) GetWindowLong (hwndDlg, GWL_USERDATA);
121
   if (hWndTab == NULL)
122
      return (0); // consistency check
123
 
124
   // now get a pointer to the tab container's structure
125
   tab_control = (tabcontrol_t *) GetWindowLong (hWndTab, GWL_USERDATA);
126
 
127
   // is this page being initialized ?
128
   if (message == WM_INITDIALOG)
129
      return (DefWindowProc (hwndDlg, WM_INITDIALOG, wParam, lParam)); // if so, don't hook its initialization message
130
 
131
   // else is a control being acted upon in this page ?
132
   else if (message == WM_COMMAND)
133
   {
134
      // forward all commands to the parent proc
135
      tab_control->ParentProc (hwndDlg, message, wParam, lParam);
136
 
137
      // is this WM_COMMAND message NOT a notification to the parent window ?
138
      if (HIWORD (wParam) == 0)
139
      {
140
         // mouse clicks on a control should engage the Message Loop
141
         SetFocus ((HWND) lParam);
142
         TabControl_SetFocusOnFirstTabStop (hwndDlg);
143
         TabPage_MessageLoop (hwndDlg);
144
      }
145
 
146
      SetWindowLongPtr (hwndDlg, DWLP_MSGRESULT, 0);
147
      return (1); // return TRUE so as to notify we handled this message
148
   }
149
 
150
   // else is the mouse clicking something ?
151
   else if (message == WM_LBUTTONDOWN)
152
   {
153
      // simulate keypress access so that everything is handled in the same consistent way
154
      nm.hdr.hwndFrom = tab_control->hWnd;
155
      nm.hdr.idFrom = GetDlgCtrlID (tab_control->hWnd);
156
      nm.hdr.code = TCN_KEYDOWN;
157
      nm.wVKey = (GetWindowLong (tab_control->hWnd, GWL_STYLE) & TCS_VERTICAL ? VK_LEFT : VK_DOWN);
158
      nm.flags = 0;
159
      SendMessage (nm.hdr.hwndFrom, WM_NOTIFY, nm.hdr.idFrom, (LPARAM) &nm);
160
 
161
      SetWindowLongPtr (hwndDlg, DWLP_MSGRESULT, 0);
162
      return (1); // return TRUE so as to notify we handled this message
163
   }
164
 
165
   // forward all other messages to the parent proc
166
   tab_control->ParentProc (hwndDlg, message, wParam, lParam);
167
   return (0);
168
}
169
 
170
/****************************************************************************
171
*     PURPOSE:  Monitor and respond to user keyboard input and system messages.
172
*
173
*     PARAMS:   HWND hwnd - handle to the currently visible tab page
174
*
175
*     COMMENTS: Send PostQuitMessage(0); from any cancel or exit event.
176
*               Failure to do so will leave the process running even after
177
*               application exit.
178
\****************************************************************************/
179
 
180
static void TabPage_MessageLoop (HWND hWnd)
181
{
182
        MSG msg;
183
        int status;
184
        HWND hFirstStop = NULL;
185
   HWND hWndTab;
186
 
187
        while ((status = GetMessage (&msg, NULL, 0, 0)))
188
        {
189
                if (status == -1)
190
                        return; // Exception
191
 
192
      // if this page is being closed, stop the loop
193
                if ((msg.message == WM_SHOWWINDOW) && (msg.wParam == FALSE))
194
                        return;
195
 
196
                //IsDialogMessage() dispatches WM_KEYDOWN to the tab page's child controls
197
                // so we'll sniff them out before they are translated and dispatched.
198
                if ((msg.message == WM_KEYDOWN) && (msg.wParam == VK_TAB))
199
                {
200
                        //Tab each tabstop in a tab page once and then return to to
201
                        // the tabCtl selected tab
202
                        if (hFirstStop == NULL)
203
                                hFirstStop = msg.hwnd;
204
                        else if (hFirstStop == msg.hwnd)
205
                        {
206
                                // Tab off the tab page and stop the loop
207
                                hWndTab = (HWND) GetWindowLong (GetParent (msg.hwnd), GWL_USERDATA);
208
                                if (hWndTab != NULL)
209
                                SetFocus (hWndTab); // tab off the tab page
210
                                return; // and stop the loop
211
                        }
212
                }
213
 
214
      // Perform default dialog message processing using IsDialogMessage...
215
                // Non dialog message handled in the standard way.
216
                if (!IsDialogMessage (hWnd, &msg))
217
                {
218
                        TranslateMessage(&msg);
219
                        DispatchMessage(&msg);
220
                }
221
        }
222
        // If we get here window is closing
223
        PostQuitMessage(0);
224
        return;
225
}
226
 
227
 
228
bool TabControl_Notify (NMHDR *pnm)
229
{
230
   // handle WM_NOTIFY messages. Returns TRUE if message was handled.
231
 
232
   tabcontrol_t *tab_control;
233
   int currentpage_index;
234
   TC_KEYDOWN *tk;
235
 
236
   // get the tab control structure
237
   tab_control = (tabcontrol_t *) GetWindowLong (pnm->hwndFrom, GWL_USERDATA);
238
 
239
   // is it a "key down" notification ?
240
   if (pnm->code == TCN_KEYDOWN)
241
   {
242
      // handle key presses in the tab control (but not the tab pages)
243
      tk = (TC_KEYDOWN *) pnm;
244
 
245
      // is there more than one tab ?
246
      if (TabCtrl_GetItemCount (tk->hdr.hwndFrom) > 1)
247
      {
248
         currentpage_index = TabCtrl_GetCurSel (tk->hdr.hwndFrom);
249
 
250
         // are tabs vertical ?
251
         if (GetWindowLong (tab_control->hWnd, GWL_STYLE) & TCS_VERTICAL)
252
         {
253
            if ((tk->wVKey == VK_PRIOR) && (currentpage_index > 0))
254
            {
255
               TabCtrl_SetCurSel (tk->hdr.hwndFrom, currentpage_index - 1); // select the previous page
256
               TabCtrl_SetCurFocus (tk->hdr.hwndFrom, currentpage_index - 1);
257
            }
258
            else if ((tk->wVKey == VK_UP) && (currentpage_index > 0))
259
            {
260
               TabCtrl_SetCurSel (tk->hdr.hwndFrom, currentpage_index - 1); // select the previous page
261
               TabCtrl_SetCurFocus (tk->hdr.hwndFrom, currentpage_index);
262
            }
263
            else if (tk->wVKey == VK_NEXT)
264
            {
265
               TabCtrl_SetCurSel (tk->hdr.hwndFrom, currentpage_index + 1); // select the next page
266
               TabCtrl_SetCurFocus (tk->hdr.hwndFrom, currentpage_index + 1);
267
            }
268
            else if (tk->wVKey == VK_DOWN)
269
            {
270
               TabCtrl_SetCurSel (tk->hdr.hwndFrom, currentpage_index + 1); // select the next page
271
               TabCtrl_SetCurFocus (tk->hdr.hwndFrom, currentpage_index);
272
            }
273
            else if ((tk->wVKey == VK_LEFT) || (tk->wVKey == VK_RIGHT))
274
            {
275
               SetFocus (tab_control->pages[currentpage_index].hWnd); // navigate within selected child tab page
276
               TabControl_SetFocusOnFirstTabStop (tab_control->pages[currentpage_index].hWnd);
277
               TabPage_MessageLoop (tab_control->pages[currentpage_index].hWnd);
278
            }
279
         }
280
 
281
         // else tabs must be horizontal (default style)
282
         else
283
         {
284
            if ((tk->wVKey == VK_NEXT) && (currentpage_index > 0))
285
            {
286
               TabCtrl_SetCurSel (tk->hdr.hwndFrom, currentpage_index - 1); // select the previous page
287
               TabCtrl_SetCurFocus (tk->hdr.hwndFrom, currentpage_index - 1);
288
            }
289
            else if ((tk->wVKey == VK_LEFT) && (currentpage_index > 0))
290
            {
291
               TabCtrl_SetCurSel (tk->hdr.hwndFrom, currentpage_index - 1); // select the previous page
292
               TabCtrl_SetCurFocus (tk->hdr.hwndFrom, currentpage_index);
293
            }
294
            else if (tk->wVKey == VK_PRIOR)
295
            {
296
               TabCtrl_SetCurSel (tk->hdr.hwndFrom, currentpage_index + 1); // select the next page
297
               TabCtrl_SetCurFocus (tk->hdr.hwndFrom, currentpage_index + 1);
298
            }
299
            else if (tk->wVKey == VK_RIGHT)
300
            {
301
               TabCtrl_SetCurSel (tk->hdr.hwndFrom, currentpage_index + 1); // select the next page
302
               TabCtrl_SetCurFocus (tk->hdr.hwndFrom, currentpage_index);
303
            }
304
            else if ((tk->wVKey == VK_UP) || (tk->wVKey == VK_DOWN))
305
            {
306
               SetFocus (tab_control->pages[currentpage_index].hWnd); // navigate within selected child tab page
307
               TabControl_SetFocusOnFirstTabStop (tab_control->pages[currentpage_index].hWnd);
308
               TabPage_MessageLoop (tab_control->pages[currentpage_index].hWnd);
309
            }
310
         }
311
      }
312
 
313
      return (false); // we didn't handle the message, return FALSE
314
   }
315
 
316
   // else is it a tab page change notification ?
317
   else if (pnm->code == TCN_SELCHANGE)
318
   {
319
      // handle TCN_SELCHANGE notification, which is sent when a tab has been pressed.
320
      currentpage_index = TabCtrl_GetCurSel (tab_control->hWnd); // get the new current tab
321
      ShowWindow (tab_control->pages[tab_control->activepage_index].hWnd, SW_HIDE); // hide the current child dialog box, if any
322
 
323
      // ShowWindow() does not seem to post the WM_SHOWWINDOW message to the tab page. Since we use
324
      // the hiding of the window as an indication to stop the message loop, let's post it explicitly
325
      PostMessage (tab_control->pages[tab_control->activepage_index].hWnd, WM_SHOWWINDOW, FALSE, 0);
326
      ShowWindow (tab_control->pages[currentpage_index].hWnd, SW_SHOWNORMAL); // show the new child dialog box
327
      tab_control->activepage_index = currentpage_index; // save the current child
328
 
329
      return (true); // message was handled, return TRUE
330
   }
331
 
332
   return (false); // we didn't handle the message, return FALSE
333
}
334
 
335
 
336
void *TabControl_New (HWND hTabControlWnd, WNDPROC ParentProc)
337
{
338
   // initialize the hTabControlWnd control handle as a tab control, and return an opaque pointer to it
339
 
340
   tabcontrol_t *the_control;
341
 
342
   the_control = (tabcontrol_t *) malloc (sizeof (tabcontrol_t));
343
   the_control->hWnd = hTabControlWnd; // save tab control's window handle
344
   the_control->ParentProc = ParentProc; // save tab control parent's window procedure
345
   the_control->pages = NULL;
346
   the_control->page_count = 0;
347
   the_control->activepage_index = -1;
348
   SetWindowLong (the_control->hWnd, GWL_USERDATA, (LONG) the_control); // save tab control structure address to GWL_USERDATA
349
 
350
   return (the_control); // tab control is created, return an opaque pointer to it
351
}
352
 
353
 
354
void TabControl_AddPage (void *tab_control, wchar_t *page_name, int dialog_id)
355
{
356
   // add a page to a tab control from a dialog ID
357
 
358
   tabcontrol_t *the_control;
359
   RECT client_rect;
360
   TCITEM tie;
361
 
362
   the_control = (tabcontrol_t *) tab_control; // quick access to tab control
363
 
364
   // get tab control's client rectangle
365
   TabControl_GetClientRect (the_control->hWnd, &client_rect); // left, top, width, height
366
 
367
   // resize pages array based on number of pages
368
   the_control->pages = (tabpage_t *) realloc (the_control->pages, (the_control->page_count + 1) * sizeof (tabpage_t *));
369
 
370
   // Add a tab for each name in tabnames (list ends with 0)
371
   tie.mask = TCIF_TEXT | TCIF_IMAGE;
372
   tie.iImage = -1;
373
   tie.pszText = page_name;
374
   TabCtrl_InsertItem (the_control->hWnd, the_control->page_count, &tie);
375
 
376
   // create the page dialog with the tab parent's as parent, and save this tab's "real" parent, which is our tab container, in GWL_USERDATA
377
   the_control->pages[the_control->page_count].hWnd = CreateDialog (GetModuleHandle (NULL), MAKEINTRESOURCE (dialog_id), GetParent (the_control->hWnd), (DLGPROC) TabPage_DlgProc);
378
   SetWindowLong (the_control->pages[the_control->page_count].hWnd, GWL_USERDATA, (LONG) the_control->hWnd);
379
   SetWindowPos (the_control->pages[the_control->page_count].hWnd, HWND_TOP, client_rect.left, client_rect.top, client_rect.right, client_rect.bottom, 0);
380
   the_control->page_count++; // tab control has now one page more
381
 
382
   // save the current visible page and show it
383
   the_control->activepage_index = 0;
384
   ShowWindow (the_control->pages[the_control->activepage_index].hWnd, SW_SHOW);
385
 
386
   return; // finished, page has been added
387
}
388
 
389
 
390
void TabControl_SelectTab (void *tab_control, int page_index)
391
{
392
   tabcontrol_t *the_control;
393
   NMHDR nmh;
394
 
395
   the_control = (tabcontrol_t *) tab_control; // quick access to tab control
396
 
397
   TabCtrl_SetCurSel (the_control->hWnd, page_index);
398
   nmh.hwndFrom = the_control->hWnd;
399
   nmh.idFrom = GetDlgCtrlID (the_control->hWnd);
400
   nmh.code = TCN_SELCHANGE;
401
 
402
   SendMessage (the_control->hWnd, WM_NOTIFY, (WPARAM) nmh.idFrom, (LPARAM) &nmh);
403
   return;
404
}
405
 
406
 
407
HWND TabControl_GetItem (void *tab_control, int item_id)
408
{
409
   // helper function to set a tab control item's text
410
 
411
   tabcontrol_t *the_control;
412
   int page_index;
413
 
414
   the_control = (tabcontrol_t *) tab_control; // quick access to tab control
415
 
416
   // cycle through all pages and return the one which has the desired item
417
   for (page_index = 0; page_index < the_control->page_count; page_index++)
418
      if (IsWindow (GetDlgItem (the_control->pages[page_index].hWnd, item_id)))
419
         return (GetDlgItem (the_control->pages[page_index].hWnd, item_id));
420
 
421
   return (NULL); // this item was not found in tab control
422
}
423
 
424
 
425
void TabControl_Destroy (void *tab_control)
426
{
427
   // destroy the tab page dialogs and free the list of pointers to the dialogs
428
 
429
   tabcontrol_t *the_control;
430
   int page_index;
431
 
432
   the_control = (tabcontrol_t *) tab_control; // quick access to tab control
433
 
434
   // destroy each window of this tab successively
435
   for (page_index = 0; page_index < the_control->page_count; page_index++)
436
      DestroyWindow (the_control->pages[page_index].hWnd);
437
 
438
   free (the_control->pages);
439
   free (the_control);
440
   return; // finished, the tab control structure's allocated elements are freed
441
}