96243139f3eedcbbb537c66d337cb376adfa2ed8
[openafs.git] / src / WINNT / afsapplib / dialog.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 }
14
15 #include <windows.h>
16 #include <windowsx.h>
17 #include <commctrl.h>
18 #include <stdlib.h>
19 #include <WINNT/subclass.h>
20 #include <WINNT/talocale.h>
21 #include <WINNT/dialog.h>
22 #include <WINNT/fastlist.h>
23
24 #ifdef FORWARD_WM_COMMAND
25 #undef FORWARD_WM_COMMAND
26 #endif
27 #define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) \
28     (fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))
29
30
31 /*
32  * MISCELLANEOUS ______________________________________________________________
33  *
34  */
35
36 #ifndef REALLOC
37 #define REALLOC(_a,_c,_r,_i) DialogReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
38 BOOL DialogReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
39 {
40    LPVOID pNew;
41    size_t cNew;
42
43    if (cReq <= *pcTarget)
44       return TRUE;
45
46    if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
47       return FALSE;
48
49    if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
50       return FALSE;
51    memset (pNew, 0x00, cbElement * cNew);
52
53    if (*pcTarget != 0)
54       {
55       memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
56       Free (*ppTarget);
57       }
58
59    *ppTarget = pNew;
60    *pcTarget = cNew;
61    return TRUE;
62 }
63 #endif
64
65
66 void DialogGetRectInParent (HWND hWnd, RECT *pr)
67 {
68    POINT pt;
69
70    GetWindowRect (hWnd, pr);
71
72    pr->right -= pr->left;
73    pr->bottom -= pr->top;       // right/bottom == width/height for now
74
75    pt.x = pr->left;
76    pt.y = pr->top;
77
78    ScreenToClient (GetParent (hWnd), &pt);
79
80    pr->left = pt.x;
81    pr->top = pt.y;
82    pr->right += pr->left;
83    pr->bottom += pr->top;
84 }
85
86
87 /*
88  * PROPERTY SHEETS ____________________________________________________________
89  *
90  */
91
92 BOOL PropSheet_HandleNotify (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
93 {
94    switch (msg)
95       {
96       case WM_NOTIFY:
97          switch (((NMHDR FAR *)lp)->code)
98             {
99             case PSN_KILLACTIVE:
100                FORWARD_WM_COMMAND (hDlg, IDOK, 0, 0, SendMessage);
101                return TRUE;
102             case PSN_APPLY:
103                FORWARD_WM_COMMAND (hDlg, IDAPPLY, 0, 0, SendMessage);    
104                return TRUE;
105             case PSN_HELP:
106                FORWARD_WM_COMMAND (hDlg, IDHELP, 0, 0, SendMessage);
107                return TRUE;
108             case PSN_SETACTIVE:
109                FORWARD_WM_COMMAND (hDlg, IDINIT, wp, lp, SendMessage);
110                return TRUE;
111             case PSN_RESET:
112                FORWARD_WM_COMMAND (hDlg, IDCANCEL, 0, 0, SendMessage);
113                return TRUE;
114             }
115          break;
116       }
117
118    return FALSE;
119 }
120
121
122 static struct
123    {
124    BOOL fInUse;
125    HWND hSheet;
126    LPPROPSHEET psh;
127    size_t cRef;
128    } *aPropSheets = NULL;
129
130 static size_t cPropSheets = 0;
131
132
133 BOOL CALLBACK PropTab_HookProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
134 {
135    if (PropSheet_HandleNotify (hDlg, msg, wp, lp))
136       return TRUE;
137
138    HWND hSheet = GetParent (hDlg);
139    HWND hTab = GetDlgItem (hSheet, IDC_PROPSHEET_TABCTRL);
140
141    if (IsWindow (hTab))
142       {
143       for (size_t ii = 0; ii < cPropSheets; ++ii)
144          {
145          if (aPropSheets[ii].fInUse && (aPropSheets[ii].hSheet == hSheet))
146             break;
147          }
148       if (ii >= cPropSheets)
149          {
150          for (size_t ii = 0; ii < cPropSheets; ++ii)
151             {
152             if (aPropSheets[ii].fInUse && !aPropSheets[ii].hSheet)
153                {
154                aPropSheets[ii].hSheet = hSheet;
155                break;
156                }
157             }
158          }
159       if (ii < cPropSheets)
160          {
161          for (size_t iTab = 0; iTab < aPropSheets[ii].psh->cTabs; ++iTab)
162             {
163             if (aPropSheets[ii].psh->aTabs[iTab].hDlg == hDlg)
164                break;
165             }
166          if (iTab == aPropSheets[ii].psh->cTabs)
167             {
168             iTab = (size_t)TabCtrl_GetCurSel (hTab);
169             }
170          if (iTab < aPropSheets[ii].psh->cTabs)
171             {
172             aPropSheets[ii].psh->aTabs[iTab].hDlg = hDlg;
173             BOOL rc = CallWindowProc ((WNDPROC)(aPropSheets[ ii ].psh->aTabs[ iTab ].dlgproc), hDlg, msg, wp, lp);
174
175             switch (msg)
176                {
177                case WM_INITDIALOG:
178                   ++ aPropSheets[ ii ].cRef;
179                   break;
180
181                case WM_DESTROY:
182                   if (!(-- aPropSheets[ ii ].cRef))
183                      {
184                      PropSheet_Free (aPropSheets[ ii ].psh);
185                      aPropSheets[ ii ].fInUse = FALSE;
186                      }
187                   break;
188                }
189
190             return rc;
191             }
192          }
193       }
194
195    return DefWindowProc (hDlg, msg, wp, lp);
196
197 }
198
199
200 LPPROPSHEET PropSheet_Create (LONG idsTitle, BOOL fContextHelp, HWND hParent, LPARAM lp)
201 {
202    LPPROPSHEET psh = New (PROPSHEET);
203    memset (psh, 0x00, sizeof(PROPSHEET));
204    psh->sh.dwSize = sizeof(PROPSHEETHEADER);
205    psh->sh.dwFlags = PSH_MODELESS | ((fContextHelp) ? PSH_HASHELP : 0);
206    psh->sh.hwndParent = hParent;
207    psh->sh.hInstance = THIS_HINST;
208    psh->sh.pszCaption = (HIWORD(idsTitle)) ? (LPTSTR)idsTitle : FormatString(TEXT("%1"),TEXT("%m"),idsTitle);
209    psh->fMadeCaption = (HIWORD(idsTitle)) ? FALSE : TRUE;
210    psh->lpUser = lp;
211    return psh;
212 }
213
214 LPPROPSHEET PropSheet_Create (int idsTitle, BOOL fContextHelp, HWND hParent, LPARAM lp)
215 {
216    return PropSheet_Create ((LONG)idsTitle, fContextHelp, hParent, lp);
217 }
218
219 LPPROPSHEET PropSheet_Create (LPTSTR pszTitle, BOOL fContextHelp, HWND hParent, LPARAM lp)
220 {
221    return PropSheet_Create ((LONG)pszTitle, fContextHelp, hParent, lp);
222 }
223
224
225 BOOL PropSheet_AddTab (LPPROPSHEET psh, LONG idsTitle, int idd, DLGPROC dlgproc, LPARAM lpUser, BOOL fHelpButton, BOOL fStartPage)
226 {
227    TCHAR szTitle[ cchRESOURCE ];
228
229    PROPSHEETPAGE psp;
230    memset (&psp, 0x00, sizeof(psp));
231    psp.dwSize = sizeof(PROPSHEETPAGE);
232    psp.dwFlags = PSP_DEFAULT | PSP_DLGINDIRECT | (fHelpButton ? PSP_HASHELP : 0);
233    psp.pResource = TaLocale_GetDialogResource (idd, &psp.hInstance);
234    psp.pfnDlgProc = (DLGPROC)PropTab_HookProc;
235    psp.lParam = lpUser;
236
237    // When first creating the PROPSHEET structure, we had to guess about
238    // which module the tabs would be coming from. Now that we have at least
239    // one tab, we can correct that guess.
240    //
241    psh->sh.hInstance = psp.hInstance;
242
243    if (HIWORD(idsTitle))
244       {
245       psp.pszTitle = (LPTSTR)idsTitle;
246       psp.dwFlags |= PSP_USETITLE;
247       }
248    else if (idsTitle != 0)
249       {
250       GetString (szTitle, idsTitle);
251       psp.pszTitle = szTitle;
252       psp.dwFlags |= PSP_USETITLE;
253       }
254
255    HPROPSHEETPAGE hp;
256    if ((hp = CreatePropertySheetPage (&psp)) == 0)
257       return FALSE;
258
259    if (!REALLOC( psh->sh.phpage, psh->sh.nPages, 1+psh->sh.nPages, 1))
260       return FALSE;
261
262    if (!REALLOC( psh->aTabs, psh->cTabs, psh->sh.nPages, 1))
263       return FALSE;
264
265    psh->aTabs[ psh->sh.nPages-1 ].dlgproc = dlgproc;
266    psh->aTabs[ psh->sh.nPages-1 ].lpUser = lpUser;
267    psh->aTabs[ psh->sh.nPages-1 ].hDlg = 0;
268
269    psh->sh.phpage[ psh->sh.nPages-1 ] = hp;
270    if (fStartPage)
271       psh->sh.nStartPage = psh->sh.nPages-1;
272
273    return TRUE;
274 }
275
276 BOOL PropSheet_AddTab (LPPROPSHEET psh, int idsTitle, int idd, DLGPROC dlgproc, LPARAM lpUser, BOOL fHelpButton, BOOL fStartPage)
277 {
278    return PropSheet_AddTab (psh, (LONG)idsTitle, idd, dlgproc, lpUser, fHelpButton, fStartPage);
279 }
280
281 BOOL PropSheet_AddTab (LPPROPSHEET psh, LPTSTR pszTitle, int idd, DLGPROC dlgproc, LPARAM lpUser, BOOL fHelpButton, BOOL fStartPage)
282 {
283    return PropSheet_AddTab (psh, (LONG)pszTitle, idd, dlgproc, lpUser, fHelpButton, fStartPage);
284 }
285
286
287 void PropSheet_NotifyAllTabs (LPPROPSHEET psh, HWND hDlg, UINT msg)
288 {
289    for (size_t ii = 0; ii < psh->cTabs; ++ii)
290       {
291       if (psh->aTabs[ ii ].dlgproc)
292          {
293          CallWindowProc ((WNDPROC)(psh->aTabs[ ii ].dlgproc), hDlg, msg, (WPARAM)psh, psh->aTabs[ ii ].lpUser);
294          }
295       }
296 }
297
298
299 BOOL CALLBACK PropSheet_HookProc (HWND hSheet, UINT msg, WPARAM wp, LPARAM lp)
300 {
301    PVOID oldproc = Subclass_FindNextHook (hSheet, PropSheet_HookProc);
302
303    BOOL rc;
304    if (oldproc)
305       rc = CallWindowProc ((WNDPROC)oldproc, hSheet, msg, wp, lp);
306    else
307       rc = DefWindowProc (hSheet, msg, wp, lp);
308
309    switch (msg)
310       {
311       case WM_DESTROY:
312          size_t ii;
313          for (ii = 0; ii < cPropSheets; ++ii)
314             {
315             if (aPropSheets[ii].fInUse && (aPropSheets[ii].hSheet == hSheet))
316                break;
317             }
318          if (ii < cPropSheets)
319             {
320             for (size_t iTab = 0; iTab < aPropSheets[ii].psh->cTabs; ++iTab)
321                {
322                if ( (aPropSheets[ii].psh->aTabs[iTab].hDlg) &&
323                     (IsWindow (aPropSheets[ii].psh->aTabs[iTab].hDlg)) &&
324                     (aPropSheets[ii].psh->aTabs[iTab].dlgproc != NULL) )
325                   {
326                   DestroyWindow (aPropSheets[ii].psh->aTabs[iTab].hDlg);
327                   }
328                }
329
330             PropSheet_NotifyAllTabs (aPropSheets[ ii ].psh, hSheet, WM_DESTROY_SHEET);
331
332             if (!(-- aPropSheets[ ii ].cRef))
333                {
334                PropSheet_Free (aPropSheets[ ii ].psh);
335                aPropSheets[ ii ].fInUse = FALSE;
336                }
337             }
338
339          Subclass_RemoveHook (hSheet, PropSheet_HookProc);
340          break;
341       }
342
343    return rc;
344 }
345
346
347 LPARAM PropSheet_FindTabParam (HWND hTab)
348 {
349    for (size_t ii = 0; ii < cPropSheets; ++ii)
350       {
351       if (aPropSheets[ii].fInUse && (aPropSheets[ii].hSheet == hTab))
352          return aPropSheets[ii].psh->lpUser;
353       }
354
355    HWND hSheet = GetParent (hTab);
356    for (ii = 0; ii < cPropSheets; ++ii)
357       {
358       if (aPropSheets[ii].fInUse && (aPropSheets[ii].hSheet == hSheet))
359          break;
360       }
361    if (ii < cPropSheets)
362       {
363       for (size_t iTab = 0; iTab < aPropSheets[ ii ].psh->cTabs; ++iTab)
364          {
365          if (hTab == aPropSheets[ ii ].psh->aTabs[ iTab ].hDlg)
366             return aPropSheets[ ii ].psh->aTabs[ iTab ].lpUser;
367          }
368       }
369
370    return 0;
371 }
372
373
374 BOOL PropSheet_ShowModal (LPPROPSHEET psh, void (*fnPump)(MSG *lpm))
375 {
376    HWND hSheet;
377    if ((hSheet = PropSheet_ShowModeless (psh, SW_SHOW)) == NULL)
378       return FALSE;
379
380    BOOL fWasEnabled = TRUE;
381    HWND hParent = psh->sh.hwndParent;
382    if (hParent && IsWindow (hParent))
383       {
384       fWasEnabled = IsWindowEnabled (hParent);
385       EnableWindow (hParent, FALSE);
386       }
387
388    MSG msg;
389    while (GetMessage (&msg, NULL, 0, 0))
390       {
391       if (IsMemoryManagerMessage (&msg))
392          continue;
393
394       if (!PropSheet_GetCurrentPageHwnd (hSheet))
395          {
396          if (hParent && IsWindow (hParent))
397             EnableWindow (hParent, fWasEnabled);
398          DestroyWindow (hSheet);
399          }
400
401       if (!IsWindow (hSheet))
402          {
403          if (fnPump)
404             (*fnPump)(&msg);
405          else
406             {
407             TranslateMessage (&msg);
408             DispatchMessage (&msg);
409             }
410          break;
411          }
412
413       if (PropSheet_IsDialogMessage (hSheet, &msg))
414          continue;
415
416       if (fnPump)
417          (*fnPump)(&msg);
418       else
419          {
420          TranslateMessage (&msg);
421          DispatchMessage (&msg);
422          }
423       }
424
425    if (hParent && IsWindow (hParent))
426       EnableWindow (hParent, fWasEnabled);
427
428    return TRUE;
429 }
430
431
432 HWND PropSheet_ShowModeless (LPPROPSHEET psh, int nCmdShow)
433 {
434    // Write down that we're opening a property sheet. Since we
435    // don't know the window handle yet, we'll set it to zero--
436    // when PropSheetTab_HookProc gets called for a property sheet
437    // that doesn't seem to be listed, it will write down hSheet for us.
438    // This way, we get the aPropSheets[] array filled in--complete with
439    // window handle--*before* the tabs themselves get any messages.
440    // Otherwise, we'd have to wait until the WM_INITDIALOG/IDINIT
441    // messages were finished.
442    //
443    for (size_t ii = 0; ii < cPropSheets; ++ii)
444       {
445       if (!aPropSheets[ ii ].fInUse)
446          break;
447       }
448    if (!REALLOC (aPropSheets, cPropSheets, 1+ii, 4))
449       return NULL;
450    aPropSheets[ ii ].fInUse = TRUE;
451    aPropSheets[ ii ].hSheet = 0;
452    aPropSheets[ ii ].psh = psh;
453    aPropSheets[ ii ].cRef = 1;
454
455    // Open the property sheet dialog.
456    //
457    psh->sh.dwFlags |= PSH_MODELESS;
458
459    HWND hSheet;
460    if ((hSheet = (HWND)PropertySheet (&psh->sh)) == NULL)
461       return NULL;
462
463    // Tell all tabs (not just the first one) that the sheet has been
464    // created. We'll also want to hook the sheet itself here, so we can
465    // clean up when it's destroyed.
466    //
467    Subclass_AddHook (hSheet, PropSheet_HookProc);
468    PropSheet_NotifyAllTabs (psh, hSheet, WM_INITDIALOG_SHEET);
469    ShowWindow (hSheet, nCmdShow);
470
471    return hSheet;
472 }
473
474
475 void PropSheet_Free (LPPROPSHEET psh)
476 {
477    if (psh)
478       {
479       if (psh->sh.phpage)
480          {
481          Free (psh->sh.phpage);
482          }
483       if (psh->fMadeCaption && psh->sh.pszCaption)
484          {
485          FreeString (psh->sh.pszCaption);
486          }
487       if (psh->aTabs)
488          {
489          Free (psh->aTabs);
490          }
491
492       Delete (psh);
493       }
494 }
495
496
497 HWND PropSheet_FindTabWindow (LPPROPSHEET psh, DLGPROC dlgproc)
498 {
499    if (psh)
500       {
501       for (size_t iTab = 0; iTab < psh->cTabs; ++iTab)
502          {
503          if (dlgproc == psh->aTabs[ iTab ].dlgproc)
504             return psh->aTabs[ iTab ].hDlg;
505          }
506       }
507    return NULL;
508 }
509
510
511 void PropSheetChanged (HWND hDlg)
512 {
513    PropSheet_Changed (GetParent(hDlg), hDlg);
514 }
515
516
517 void TabCtrl_SwitchToTab (HWND hTab, int iTab)
518 {
519    TabCtrl_SetCurSel (hTab, iTab);
520
521    NMHDR nm;
522    nm.hwndFrom = hTab;
523    nm.idFrom = IDC_PROPSHEET_TABCTRL;
524    nm.code = TCN_SELCHANGE;
525    SendMessage (GetParent (hTab), WM_NOTIFY, 0, (LPARAM)&nm);
526 }
527
528
529 /*
530  * LISTVIEW ___________________________________________________________________
531  *
532  */
533
534 LPARAM FL_StartChange (HWND hList, BOOL fReset)
535 {
536    LPARAM dwSelected = FL_GetSelectedData (hList);
537    FastList_Begin (hList);
538    if (fReset)
539       FastList_RemoveAll (hList);
540    return dwSelected;
541 }
542
543
544 void FL_EndChange (HWND hList, LPARAM lpToSelect)
545 {
546    if (lpToSelect != 0)
547       FL_SetSelectedByData (hList, lpToSelect);
548    FastList_EndAll (hList);
549 }
550
551
552 void FL_ResizeColumns (HWND hList, WORD nCols, WORD *acx)
553 {
554    for (WORD ii = 0; ii < nCols; ++ii)
555       {
556       FASTLISTCOLUMN flc;
557       FastList_GetColumn (hList, ii, &flc);
558
559       flc.cxWidth = acx[ ii ];
560       FastList_SetColumn (hList, ii, &flc);
561       }
562 }
563
564
565 void cdecl FL_SetColumns (HWND hList, WORD nCols, WORD *acx, ...)
566 {
567    va_list   arg;
568    va_start (arg, acx);
569
570    for (WORD ii = 0; ii < nCols; ii++)
571       {
572       FASTLISTCOLUMN flc;
573       GetString (flc.szText, va_arg (arg, WORD));
574       flc.dwFlags = FLCF_JUSTIFY_LEFT;
575       flc.cxWidth = acx[ ii ];            // width of the column, in pixels
576       FastList_SetColumn (hList, ii, &flc);
577       }
578 }
579
580
581 void FL_GetColumns (HWND hList, WORD nCols, WORD *acx)
582 {
583    for (WORD ii = 0; ii < nCols; ii++)
584       {
585       FASTLISTCOLUMN flc;
586       FastList_GetColumn (hList, ii, &flc);
587       acx[ ii ] = (WORD)flc.cxWidth;
588       }
589 }
590
591
592 HLISTITEM cdecl FL_AddItem (HWND hList, WORD nCols, LPARAM lp, int iImage1, ...)
593 {
594    va_list   arg;
595    va_start (arg, iImage1);
596
597    FASTLISTADDITEM flai;
598    memset (&flai, 0x00, sizeof(flai));
599    flai.hParent = NULL;
600    flai.iFirstImage = iImage1;
601    flai.iSecondImage = IMAGE_NOIMAGE;
602    flai.pszText = va_arg (arg, LPTSTR);
603    flai.lParam = lp;
604    flai.dwFlags = 0;
605
606    FastList_Begin (hList);
607
608    HLISTITEM hItem;
609    if ((hItem = FastList_AddItem (hList, &flai)) != NULL)
610       {
611       for (WORD ii = 1; ii < nCols; ii++)
612          {
613          FastList_SetItemText (hList, hItem, ii, va_arg (arg, LPTSTR));
614          }
615       }
616
617    FastList_End (hList);
618    return hItem;
619 }
620
621
622 void FL_GetItemText (HWND hList, HLISTITEM hItem, int iCol, LPTSTR psz)
623 {
624    LPCTSTR pszText = FastList_GetItemText (hList, hItem, iCol);
625    if (!pszText)
626       *psz = TEXT('\0');
627    else
628       lstrcpy (psz, pszText);
629 }
630
631
632 void FL_SetItemText (HWND hList, HLISTITEM hItem, int iCol, LPCTSTR psz)
633 {
634    FastList_SetItemText (hList, hItem, iCol, psz);
635 }
636
637
638 void FL_SetSelectedByData (HWND hList, LPARAM lp)
639 {
640    HLISTITEM hItem;
641    if ((hItem = FastList_FindItem (hList, lp)) != NULL)
642       {
643       FL_SetSelected (hList, hItem);
644       }
645 }
646
647
648 void FL_SetSelected (HWND hList, HLISTITEM hItem)
649 {
650    FastList_Begin (hList);
651    FastList_SelectNone (hList);
652    if (hItem)
653       {
654       FastList_SelectItem (hList, hItem, TRUE);
655       FastList_EnsureVisible (hList, hItem);
656       }
657    FastList_SetFocus (hList, hItem);
658    FastList_End (hList);
659 }
660
661
662 HLISTITEM FL_GetSelected (HWND hList, HLISTITEM hItem)
663 {
664    return FastList_FindNextSelected (hList, hItem);
665 }
666
667
668 HLISTITEM FL_GetFocused (HWND hList)
669 {
670    return FastList_GetFocus (hList);
671 }
672
673
674 LPARAM FL_GetFocusedData (HWND hList)
675 {
676    HLISTITEM hItem;
677    if ((hItem = FastList_GetFocus (hList)) == NULL)
678       return 0;
679
680    return FastList_GetItemParam (hList, hItem);
681 }
682
683
684 LPARAM FL_GetData (HWND hList, HLISTITEM hItem)
685 {
686    return FastList_GetItemParam (hList, hItem);
687 }
688
689
690 HLISTITEM FL_GetIndex (HWND hList, LPARAM lp)
691 {
692    return FastList_FindItem (hList, lp);
693 }
694
695
696 LPARAM FL_GetSelectedData (HWND hList)
697 {
698    HLISTITEM hItem;
699    if ((hItem = FastList_FindFirstSelected (hList)) == NULL)
700       return 0;
701
702    return FastList_GetItemParam (hList, hItem);
703 }
704
705
706 void FL_RestoreView (HWND hList, LPVIEWINFO lpvi)
707 {
708    FastList_Begin (hList);
709
710    for (size_t cColumns = FastList_GetColumnCount(hList); cColumns; --cColumns)
711       {
712       FastList_RemoveColumn (hList, cColumns-1);
713       }
714
715    for (size_t ii = 0; ii < lpvi->nColsShown; ++ii)
716       {
717       if (lpvi->aColumns[ ii ] >= lpvi->nColsAvail)
718          continue;
719
720       size_t iColumn = lpvi->aColumns[ ii ];
721
722       FASTLISTCOLUMN flc;
723       GetString (flc.szText, lpvi->idsColumns[ iColumn ]);
724
725       flc.dwFlags = (lpvi->cxColumns[ iColumn ] & COLUMN_RIGHTJUST) ? FLCF_JUSTIFY_RIGHT : (lpvi->cxColumns[ iColumn ] & COLUMN_CENTERJUST) ? FLCF_JUSTIFY_CENTER : FLCF_JUSTIFY_LEFT;
726       flc.cxWidth = (lpvi->cxColumns[ iColumn ] & (~COLUMN_JUSTMASK));
727       FastList_SetColumn (hList, ii, &flc);
728       }
729
730    // Restore the fastlist's sort style
731    //
732    FastList_SetSortStyle (hList, (lpvi->iSort & (~COLUMN_SORTREV)), !!(lpvi->iSort & COLUMN_SORTREV));
733
734    // Switch the FastList into the proper mode--Large Icons,
735    // Small Icons, List or Details.
736    //
737    LONG dw = GetWindowLong (hList, GWL_STYLE);
738    dw &= ~FLS_VIEW_MASK;
739    dw |= lpvi->lvsView;
740    SetWindowLong (hList, GWL_STYLE, dw);
741
742    FastList_End (hList);
743 }
744
745
746 void FL_StoreView (HWND hList, LPVIEWINFO lpvi)
747 {
748    // First, remember what mode the the FastList is in--Large,
749    // Small, List, Tree, or TreeList.
750    //
751    lpvi->lvsView  = GetWindowLong (hList, GWL_STYLE);
752    lpvi->lvsView &= FLS_VIEW_MASK;
753
754    // Record the fastlist's sort style
755    //
756    int iCol;
757    BOOL fRev;
758    FastList_GetSortStyle (hList, &iCol, &fRev);
759    lpvi->iSort = (size_t)iCol;
760    if (fRev)
761       lpvi->iSort |= COLUMN_SORTREV;
762
763    // Then remember the columns' widths; we expect that the columns
764    // which are shown (ie, nColsShown and aColumns[]) are up-to-date
765    // already.
766    //
767    if (lpvi->lvsView & FLS_VIEW_LIST)
768       {
769       for (size_t ii = 0; ii < lpvi->nColsShown; ++ii)
770          {
771          if (lpvi->aColumns[ ii ] >= lpvi->nColsAvail)
772             continue;
773
774          size_t iColumn = lpvi->aColumns[ii];
775
776          int dwJust = (lpvi->cxColumns[ iColumn ] & COLUMN_JUSTMASK);
777
778          FASTLISTCOLUMN flc;
779          FastList_GetColumn (hList, ii, &flc);
780          lpvi->cxColumns[ iColumn ] = flc.cxWidth | dwJust;
781          }
782       }
783 }
784
785
786 HLISTITEM cdecl FL_AddItem (HWND hList, LPVIEWINFO lpvi, LPARAM lp, int iImage1, ...)
787 {
788    va_list   arg;
789    va_start (arg, iImage1);
790
791    LPTSTR apszColumns[ nCOLUMNS_MAX ];
792    for (size_t iColumn = 0; iColumn < lpvi->nColsAvail; ++iColumn)
793       {
794       apszColumns[ iColumn ] = va_arg (arg, LPTSTR);
795       }
796
797    FastList_Begin (hList);
798
799    HLISTITEM hItem = NULL;
800    for (size_t ii = 0; ii < lpvi->nColsShown; ++ii)
801       {
802       if ((iColumn = lpvi->aColumns[ ii ]) >= lpvi->nColsAvail)
803          continue;
804
805       if (ii == 0)
806          {
807          FASTLISTADDITEM flai;
808          memset (&flai, 0x00, sizeof(flai));
809          flai.hParent = NULL;
810          flai.iFirstImage = iImage1;
811          flai.iSecondImage = IMAGE_NOIMAGE;
812          flai.pszText = apszColumns[ iColumn ];
813          flai.lParam = lp;
814          flai.dwFlags = 0;
815
816          hItem = FastList_AddItem (hList, &flai);
817          }
818       else
819          {
820          FastList_SetItemText (hList, hItem, ii, apszColumns[ iColumn ]);
821          }
822       }
823
824    FastList_End (hList);
825    return hItem;
826 }
827
828
829 BOOL FL_HitTestForHeaderBar (HWND hList, POINT ptClient)
830 {
831    for (HWND hHeader = GetWindow (hList, GW_CHILD);
832         hHeader != NULL;
833         hHeader = GetWindow (hHeader, GW_HWNDNEXT))
834       {
835       TCHAR szClassName[ cchRESOURCE ];
836
837       if (GetClassName (hHeader, szClassName, cchRESOURCE))
838          {
839          if (!lstrcmp (szClassName, WC_HEADER))
840             break;
841          }
842       }
843
844    if (hHeader && IsWindow(hHeader) && IsWindowVisible(hHeader))
845       {
846       RECT rHeader;
847       DialogGetRectInParent (hHeader, &rHeader);
848       if (PtInRect (&rHeader, ptClient))
849          return TRUE;
850       }
851
852    return FALSE;
853 }
854
855
856
857 /*
858  * LISTVIEW ___________________________________________________________________
859  *
860  */
861
862 LPARAM LV_StartChange (HWND hList, BOOL fReset)
863 {
864    LPARAM dwSelected = LV_GetSelectedData (hList);
865    SendMessage (hList, WM_SETREDRAW, FALSE, 0);
866    if (fReset)
867       ListView_DeleteAllItems (hList);
868    return dwSelected;
869 }
870
871
872 void LV_EndChange (HWND hList, LPARAM lpToSelect)
873 {
874    SendMessage (hList, WM_SETREDRAW, TRUE, 0);
875
876    if (lpToSelect != (LPARAM)NULL)
877       LV_SetSelectedByData (hList, lpToSelect);
878 }
879
880
881 void LV_ResizeColumns (HWND hList, WORD nCols, WORD *acx)
882 {
883    for (WORD ii = 0; ii < nCols; ++ii)
884       {
885       ListView_SetColumnWidth (hList, ii, acx[ ii ]);
886       }
887 }
888
889 void cdecl LV_SetColumns (HWND hList, WORD nCols, WORD *acx, ...)
890 {
891    va_list   arg;
892    va_start (arg, acx);
893
894    for (WORD ii = 0; ii < nCols; ii++)
895       {
896       LV_COLUMN lvC;
897       TCHAR     text[ cchRESOURCE ];
898
899       GetString (text, va_arg (arg, WORD));
900
901       lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
902       lvC.fmt = LVCFMT_LEFT;
903       lvC.pszText = text;
904       lvC.cx = acx[ ii ];            // width of the column, in pixels
905       lvC.iSubItem = ii;
906       ListView_InsertColumn (hList, ii, &lvC);
907       }
908 }
909
910
911 void LV_GetColumns (HWND hList, WORD nCols, WORD *acx)
912 {
913    for (WORD ii = 0; ii < nCols; ii++)
914       {
915       acx[ ii ] = ListView_GetColumnWidth (hList, ii);
916       }
917 }
918
919
920 void cdecl LV_AddItem (HWND hList, WORD nCols, int index, LPARAM lp, int iImage, ...)
921 {
922    LV_ITEM   lvI;
923    va_list   arg;
924    va_start (arg, iImage);
925
926    lvI.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
927    lvI.state = 0;
928    lvI.stateMask = 0;
929    lvI.iItem = index;
930    lvI.iSubItem = 0;
931    lvI.pszText = va_arg (arg, LPTSTR);
932    lvI.cchTextMax = cchRESOURCE;
933    lvI.iImage = iImage;
934    lvI.lParam = lp;
935
936    if ((index == INDEX_SORT) && (lvI.pszText != LPSTR_TEXTCALLBACK))
937       {
938       lvI.iItem = LV_PickInsertionPoint (hList, lvI.pszText);
939       }
940
941    DWORD dw = ListView_InsertItem (hList, &lvI);
942
943    for (WORD ii = 1; ii < nCols; ii++)
944       {
945       ListView_SetItemText (hList, dw, ii, va_arg (arg, LPTSTR));
946       }
947 }
948
949
950 void cdecl LV_GetItemText (HWND hList, int index, short col, LPTSTR psz)
951 {
952    *psz = TEXT('\0');
953    ListView_GetItemText (hList, index, col, (LPTSTR)psz, cchRESOURCE);
954 }
955
956
957 void cdecl LV_SetItemText (HWND hList, int index, short col, LPCTSTR psz)
958 {
959    ListView_SetItemText (hList, index, col, (LPTSTR)psz);
960 }
961
962
963 void LV_SetSelectedByData (HWND hList, LPARAM lp)
964 {
965    int index;
966
967    if ((index = LV_GetIndex (hList, lp)) != -1)
968       {
969       LV_SetSelected (hList, index);
970       }
971 }
972
973
974 void LV_SetSelected (HWND hList, int index)
975 {
976    ListView_EnsureVisible (hList, index, FALSE);
977
978    ListView_SetItemState (hList, index, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
979 }
980
981
982 int LV_GetSelected (HWND hList, int index)
983 {
984    return ListView_GetNextItem (hList, index, LVNI_SELECTED);
985 }
986
987
988 int LV_GetFocused (HWND hList)
989 {
990    return ListView_GetNextItem (hList, -1, LVNI_FOCUSED);
991 }
992
993
994 LPARAM LV_GetFocusedData (HWND hList)
995 {
996    int idxFocus;
997
998    if ((idxFocus = LV_GetFocused (hList)) == -1)
999       return (LPARAM)0;
1000
1001    return LV_GetData (hList, idxFocus);
1002 }
1003
1004
1005 LPARAM LV_GetData (HWND hList, int index)
1006 {
1007    LV_ITEM lvI;
1008
1009    memset (&lvI, 0x00, sizeof(LV_ITEM));
1010    lvI.mask = LVIF_PARAM;
1011    lvI.iItem = index;
1012
1013    if (! ListView_GetItem (hList, &lvI))
1014       return (LPARAM)0;
1015
1016    return lvI.lParam;
1017 }
1018
1019
1020 int LV_GetIndex (HWND hList, LPARAM lp)
1021 {
1022    LV_FINDINFO  lvfi;
1023
1024    memset (&lvfi, 0x00, sizeof(lvfi));
1025
1026    lvfi.flags = LVFI_PARAM;
1027    lvfi.lParam = lp;
1028
1029    return ListView_FindItem (hList, -1, &lvfi);
1030 }
1031
1032
1033 LPARAM LV_GetSelectedData (HWND hList)
1034 {
1035    int index;
1036
1037    if ((index = LV_GetSelected (hList)) == -1)
1038       return (LPARAM)0;
1039
1040    return LV_GetData (hList, index);
1041 }
1042
1043
1044 void LV_RestoreView (HWND hList, LPVIEWINFO lpvi)
1045 {
1046    // Add columns to the listview; remember that we'll have to remove any
1047    // existing columns first.
1048    //
1049    while (ListView_DeleteColumn (hList, 0))
1050       ;
1051
1052    for (size_t ii = 0; ii < lpvi->nColsShown; ++ii)
1053       {
1054       if (lpvi->aColumns[ ii ] >= lpvi->nColsAvail)
1055          continue;
1056
1057       size_t iColumn = lpvi->aColumns[ii];
1058
1059       LV_COLUMN lvc;
1060
1061       TCHAR szText[ cchRESOURCE ];
1062       GetString (szText, lpvi->idsColumns[ iColumn ]);
1063
1064       lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
1065       lvc.fmt = (lpvi->cxColumns[ iColumn ] & COLUMN_RIGHTJUST) ? LVCFMT_RIGHT : (lpvi->cxColumns[ iColumn ] & COLUMN_CENTERJUST) ? LVCFMT_CENTER : LVCFMT_LEFT;
1066       lvc.pszText = szText;
1067       lvc.cx = (lpvi->cxColumns[ iColumn ] & (~COLUMN_JUSTMASK));
1068       lvc.iSubItem = ii;
1069       ListView_InsertColumn (hList, ii, &lvc);
1070       }
1071
1072    // Switch the ListView into the proper mode--Large Icons,
1073    // Small Icons, List or Details.
1074    //
1075    LONG dw = GetWindowLong (hList, GWL_STYLE);
1076    dw &= ~LVS_TYPEMASK;
1077    dw |= lpvi->lvsView;
1078    dw |= LVS_AUTOARRANGE;
1079    SetWindowLong (hList, GWL_STYLE, dw);
1080    ListView_Arrange (hList, LVA_DEFAULT);
1081 }
1082
1083
1084 void LV_StoreView (HWND hList, LPVIEWINFO lpvi)
1085 {
1086    // First, remember what mod the the ListView is in--Large Icons,
1087    // Small Icons, List or Details.
1088    //
1089    lpvi->lvsView  = GetWindowLong (hList, GWL_STYLE);
1090    lpvi->lvsView &= LVS_TYPEMASK;
1091
1092    // Then remember the columns' widths; we expect that the columns
1093    // which are shown (ie, nColsShown and aColumns[]) are up-to-date
1094    // already.
1095    //
1096    if (lpvi->lvsView == LVS_REPORT)
1097       {
1098       for (size_t ii = 0; ii < lpvi->nColsShown; ++ii)
1099          {
1100          if (lpvi->aColumns[ ii ] >= lpvi->nColsAvail)
1101             continue;
1102
1103          size_t iColumn = lpvi->aColumns[ii];
1104
1105          int dwJust = (lpvi->cxColumns[ iColumn ] & COLUMN_JUSTMASK);
1106
1107          lpvi->cxColumns[ iColumn ] = ListView_GetColumnWidth (hList, ii);
1108          lpvi->cxColumns[ iColumn ] |= dwJust;
1109          }
1110       }
1111 }
1112
1113
1114 typedef struct // VIEWSORTINFO
1115    {
1116    HWND hList;
1117    LPVIEWINFO lpvi;
1118    size_t iColSort;
1119    BOOL fAscending;
1120    } VIEWSORTINFO, *LPVIEWSORTINFO;
1121
1122 BOOL CALLBACK LV_SortView_Numeric (LPARAM lp1, LPARAM lp2, LPARAM lpSort)
1123 {
1124    LPVIEWSORTINFO lpvsi = (LPVIEWSORTINFO)lpSort;
1125    TCHAR szText[ cchRESOURCE ];
1126    double d1;
1127    double d2;
1128
1129    int ii1 = LV_GetIndex (lpvsi->hList, lp1);
1130    int ii2 = LV_GetIndex (lpvsi->hList, lp2);
1131
1132    LV_GetItemText (lpvsi->hList, ii1, lpvsi->iColSort, szText);
1133    d1 = atof (szText);
1134
1135    LV_GetItemText (lpvsi->hList, ii2, lpvsi->iColSort, szText);
1136    d2 = atof (szText);
1137
1138    if (lpvsi->fAscending)
1139       return (d2 <  d1) ? ((BOOL)-1) : (d2 == d1) ? ((BOOL)0) : (BOOL)1;
1140    else
1141       return (d1 <  d2) ? ((BOOL)-1) : (d1 == d2) ? ((BOOL)0) : (BOOL)1;
1142 }
1143
1144
1145 BOOL CALLBACK LV_SortView_Alphabetic (LPARAM lp1, LPARAM lp2, LPARAM lpSort)
1146 {
1147    LPVIEWSORTINFO lpvsi = (LPVIEWSORTINFO)lpSort;
1148    TCHAR szText1[ cchRESOURCE ];
1149    TCHAR szText2[ cchRESOURCE ];
1150
1151    int ii1 = LV_GetIndex (lpvsi->hList, lp1);
1152    int ii2 = LV_GetIndex (lpvsi->hList, lp2);
1153
1154    LV_GetItemText (lpvsi->hList, ii1, lpvsi->iColSort, szText1);
1155    LV_GetItemText (lpvsi->hList, ii2, lpvsi->iColSort, szText2);
1156
1157    if (lpvsi->fAscending)
1158       return lstrcmp (szText2, szText1);
1159    else
1160       return lstrcmp (szText1, szText2);
1161 }
1162
1163
1164 void LV_SortView (HWND hList, LPVIEWINFO lpvi)
1165 {
1166    for (size_t iColSort = 0; iColSort < lpvi->nColsShown; ++iColSort)
1167       {
1168       if ((lpvi->iSort & (~COLUMN_SORTREV)) == lpvi->aColumns[ iColSort ])
1169          break;
1170       }
1171
1172    if (iColSort < lpvi->nColsShown)
1173       {
1174       VIEWSORTINFO vsi;
1175       vsi.hList = hList;
1176       vsi.lpvi = lpvi;
1177       vsi.iColSort = iColSort;
1178       vsi.fAscending = (lpvi->iSort & COLUMN_SORTREV) ? TRUE : FALSE;
1179
1180       if (lpvi->cxColumns[ lpvi->iSort ] & COLUMN_RIGHTJUST)
1181          ListView_SortItems (hList, (PFNLVCOMPARE)LV_SortView_Numeric, (LPARAM)&vsi);
1182       else
1183          ListView_SortItems (hList, (PFNLVCOMPARE)LV_SortView_Alphabetic, (LPARAM)&vsi);
1184       }
1185 }
1186
1187
1188 void cdecl LV_AddItem (HWND hList, LPVIEWINFO lpvi, int index, LPARAM lp, int iImage, ...)
1189 {
1190    va_list   arg;
1191    va_start (arg, iImage);
1192
1193    LPTSTR apszColumns[ nCOLUMNS_MAX ];
1194    for (size_t iColumn = 0; iColumn < lpvi->nColsAvail; ++iColumn)
1195       {
1196       apszColumns[ iColumn ] = va_arg (arg, LPTSTR);
1197       }
1198
1199    if ((index == INDEX_SORT) && (iColumn) && (apszColumns[0] != LPSTR_TEXTCALLBACK))
1200       {
1201       index = LV_PickInsertionPoint (hList, apszColumns[0]);
1202       }
1203
1204    DWORD dw;
1205    for (size_t ii = 0; ii < lpvi->nColsShown; ++ii)
1206       {
1207       if ((iColumn = lpvi->aColumns[ ii ]) >= lpvi->nColsAvail)
1208          continue;
1209
1210       if (ii == 0)
1211          {
1212          LV_ITEM lvitem;
1213          lvitem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
1214          lvitem.state = 0;
1215          lvitem.stateMask = 0;
1216          lvitem.iItem = index;
1217          lvitem.iSubItem = 0;
1218          lvitem.pszText = apszColumns[ iColumn ];
1219          lvitem.cchTextMax = cchRESOURCE;
1220          lvitem.iImage = iImage;
1221          lvitem.lParam = lp;
1222
1223          dw = ListView_InsertItem (hList, &lvitem);
1224          }
1225       else
1226          {
1227          ListView_SetItemText (hList, dw, ii, apszColumns[ iColumn ]);
1228          }
1229       }
1230 }
1231
1232
1233 BOOL LV_HitTestForHeaderBar (HWND hList, POINT ptClient)
1234 {
1235    for (HWND hHeader = GetWindow (hList, GW_CHILD);
1236         hHeader != NULL;
1237         hHeader = GetWindow (hHeader, GW_HWNDNEXT))
1238       {
1239       TCHAR szClassName[ cchRESOURCE ];
1240
1241       if (GetClassName (hHeader, szClassName, cchRESOURCE))
1242          {
1243          if (!lstrcmp (szClassName, WC_HEADER))
1244             break;
1245          }
1246       }
1247
1248    if (hHeader && IsWindow(hHeader) && IsWindowVisible(hHeader))
1249       {
1250       RECT rHeader;
1251       DialogGetRectInParent (hHeader, &rHeader);
1252       if (PtInRect (&rHeader, ptClient))
1253          return TRUE;
1254       }
1255
1256    return FALSE;
1257 }
1258
1259
1260 int LV_PickInsertionPoint (HWND hList, LPTSTR pszNewItem, int (__stdcall *fnSort)(LPCTSTR, LPCTSTR))
1261 {
1262    int iItemLow  = 0;
1263    int iItemHigh = ListView_GetItemCount (hList) -1;
1264
1265    if (fnSort == 0)
1266       fnSort = lstrcmpi;
1267
1268    while (iItemLow <= iItemHigh)
1269       {
1270       int iItemTest = iItemLow + (iItemHigh - iItemLow) / 2;
1271
1272       TCHAR szItemTest[ 256 ];
1273       ListView_GetItemText (hList, iItemTest, 0, szItemTest, 256);
1274
1275       int iCompare = (*fnSort)(pszNewItem, szItemTest);
1276       if (iCompare <= 0)
1277          {
1278          if ((iItemHigh = iItemTest-1) < iItemLow)
1279             return iItemHigh +1;
1280          }
1281       if (iCompare >= 0)
1282          {
1283          if ((iItemLow = iItemTest+1) > iItemHigh)
1284             return iItemLow;
1285          }
1286       }
1287
1288    return 0;
1289 }
1290
1291
1292
1293 /*
1294  * COMBO BOXES ________________________________________________________________
1295  *
1296  */
1297
1298 LPARAM CB_StartChange (HWND hCombo, BOOL fReset)
1299 {
1300    LPARAM dwSelected = CB_GetSelectedData (hCombo);
1301    SendMessage (hCombo, WM_SETREDRAW, FALSE, 0);
1302    if (fReset)
1303       SendMessage (hCombo, CB_RESETCONTENT, 0, 0);
1304    return dwSelected;
1305 }
1306
1307
1308 void CB_EndChange (HWND hCombo, LPARAM lpToSelect)
1309 {
1310    SendMessage (hCombo, WM_SETREDRAW, TRUE, 0);
1311
1312    if (lpToSelect != (LPARAM)NULL)
1313       CB_SetSelectedByData (hCombo, lpToSelect);
1314 }
1315
1316
1317 UINT CB_AddItem (HWND hCombo, int nsz, LPARAM lp)
1318 {
1319    LPTSTR  psz;
1320    UINT    rc;
1321
1322    psz = FormatString (nsz);
1323    rc = CB_AddItem (hCombo, psz, lp);
1324    FreeString (psz);
1325
1326    return rc;
1327 }
1328
1329
1330 UINT CB_AddItem (HWND hCombo, LPCTSTR psz, LPARAM lp)
1331 {
1332    UINT index;
1333    if ((index = (UINT)SendMessage (hCombo, CB_ADDSTRING, 0, (LPARAM)psz)) != (UINT)-1)
1334       {
1335       SendMessage (hCombo, CB_SETITEMDATA, index, lp);
1336       }
1337    return index;
1338 }
1339
1340
1341 void CB_SetSelected (HWND hCombo, UINT index)
1342 {
1343    SendMessage (hCombo, CB_SETCURSEL, index, 0);
1344 }
1345
1346
1347 void CB_SetSelectedByData (HWND hCombo, LPARAM lpMatch)
1348 {
1349    CB_SetSelected (hCombo, CB_GetIndex (hCombo, lpMatch));
1350 }
1351
1352
1353 UINT CB_GetSelected (HWND hCombo)
1354 {
1355    return (UINT)SendMessage (hCombo, CB_GETCURSEL, 0, 0);
1356 }
1357
1358
1359 LPARAM CB_GetSelectedData (HWND hCombo)
1360 {
1361    UINT  index;
1362
1363    if ((index = CB_GetSelected (hCombo)) == -1)
1364       return 0;
1365
1366    return CB_GetData (hCombo, index);
1367 }
1368
1369
1370 LPARAM CB_GetData (HWND hCombo, UINT index)
1371 {
1372    return (LPARAM)SendMessage (hCombo, CB_GETITEMDATA, index, 0);
1373 }
1374
1375
1376 UINT CB_GetIndex (HWND hCombo, LPARAM lpMatch)
1377 {
1378    UINT  idxMax = (UINT)SendMessage (hCombo, CB_GETCOUNT, 0, 0);
1379
1380    for (UINT index = 0; index < idxMax; index++)
1381       {
1382       if (SendMessage (hCombo, CB_GETITEMDATA, index, 0) == lpMatch)
1383          return index;
1384       }
1385
1386    return (UINT)-1;
1387 }
1388
1389
1390 /*
1391  * LIST BOXES _________________________________________________________________
1392  *
1393  */
1394
1395 LPARAM LB_StartChange (HWND hList, BOOL fReset)
1396 {
1397    LPARAM dwSelected = LB_GetSelectedData (hList);
1398    SendMessage (hList, WM_SETREDRAW, FALSE, 0);
1399    if (fReset)
1400       SendMessage (hList, LB_RESETCONTENT, 0, 0);
1401    return dwSelected;
1402 }
1403
1404
1405 void LB_EndChange (HWND hList, LPARAM lpToSelect)
1406 {
1407    SendMessage (hList, WM_SETREDRAW, TRUE, 0);
1408
1409    if (lpToSelect != (LPARAM)NULL)
1410       LB_SetSelectedByData (hList, lpToSelect);
1411 }
1412
1413
1414 UINT LB_AddItem (HWND hList, int nsz, LPARAM lp)
1415 {
1416    LPTSTR  psz;
1417    UINT    rc;
1418
1419    psz = FormatString (nsz);
1420    rc = LB_AddItem (hList, psz, lp);
1421    FreeString (psz);
1422
1423    return rc;
1424 }
1425
1426
1427 UINT LB_AddItem (HWND hList, LPCTSTR psz, LPARAM lp)
1428 {
1429    UINT  index;
1430    if ((index = (UINT)SendMessage (hList, LB_ADDSTRING, 0, (LPARAM)psz)) != -1)
1431       {
1432       SendMessage (hList, LB_SETITEMDATA, index, lp);
1433       }
1434    return index;
1435 }
1436
1437
1438 void LB_EnsureVisible (HWND hList, UINT index)
1439 {
1440    int cyItem;
1441    if ((cyItem = SendMessage (hList, LB_GETITEMHEIGHT, 0, 0)) != 0)
1442       {
1443       RECT rClient;
1444       GetClientRect (hList, &rClient);
1445       int cWindow;
1446       if ((cWindow = cyRECT(rClient) / cyItem) == 0)
1447          cWindow = 1;
1448
1449       int idxTop = SendMessage (hList, LB_GETTOPINDEX, 0, 0);
1450       if (index < (UINT)idxTop)
1451          {
1452          SendMessage (hList, LB_SETTOPINDEX, index, 0);
1453          }
1454       else if (index >= (UINT)(idxTop +cWindow))
1455          {
1456          SendMessage (hList, LB_SETTOPINDEX, index -cWindow +1, 0);
1457          }
1458       }
1459 }
1460
1461
1462 void LB_SetSelected (HWND hList, UINT index)
1463 {
1464    SendMessage (hList, LB_SETCURSEL, index, 0);
1465    LB_EnsureVisible (hList, index);
1466 }
1467
1468
1469 void LB_SetSelectedByData (HWND hList, LPARAM lpMatch)
1470 {
1471    LB_SetSelected (hList, LB_GetIndex (hList, lpMatch));
1472 }
1473
1474
1475 UINT LB_GetSelected (HWND hList)
1476 {
1477    return (UINT)SendMessage (hList, LB_GETCURSEL, 0, 0);
1478 }
1479
1480
1481 LPARAM LB_GetSelectedData (HWND hList)
1482 {
1483    UINT  index;
1484
1485    if ((index = LB_GetSelected (hList)) == (UINT)-1)
1486       return 0;
1487
1488    return LB_GetData (hList, index);
1489 }
1490
1491
1492 LPARAM LB_GetData (HWND hList, UINT index)
1493 {
1494    return (LPARAM)SendMessage (hList, LB_GETITEMDATA, index, 0);
1495 }
1496
1497
1498 UINT LB_GetIndex (HWND hList, LPARAM lpMatch)
1499 {
1500    UINT  idxMax = (UINT)SendMessage (hList, LB_GETCOUNT, 0, 0);
1501
1502    for (UINT index = 0; index < idxMax; index++)
1503       {
1504       if (SendMessage (hList, LB_GETITEMDATA, index, 0) == lpMatch)
1505          return index;
1506       }
1507
1508    return (UINT)-1;
1509 }
1510
1511
1512 WORD LB_GetStringExtent (HWND hList, LPTSTR pszString)
1513 {
1514    HDC hdc = GetDC (hList);
1515
1516    SIZE sz;
1517    GetTextExtentPoint32 (hdc, pszString, lstrlen(pszString), &sz);
1518
1519    ReleaseDC (hList, hdc);
1520    return (WORD)( sz.cx + 2 * GetSystemMetrics (SM_CXBORDER) );
1521 }
1522
1523
1524 BOOL CALLBACK ListBox_HScrollHook (HWND hList, UINT msg, WPARAM wp, LPARAM lp)
1525 {
1526    PVOID oldProc = Subclass_FindNextHook (hList, ListBox_HScrollHook);
1527
1528    switch (msg)
1529       {
1530       case WM_DESTROY:
1531          Subclass_RemoveHook (hList, ListBox_HScrollHook);
1532          break;
1533
1534       case LB_ADDSTRING:
1535          {
1536          LPTSTR pszString = (LPTSTR)lp;
1537          WORD cxString = LB_GetStringExtent (hList, pszString);
1538          WORD cxExtent = (WORD)SendMessage (hList, LB_GETHORIZONTALEXTENT, 0, 0);
1539          if (cxString > cxExtent)
1540             SendMessage (hList, LB_SETHORIZONTALEXTENT, (WPARAM)cxString, 0);
1541          }
1542          break;
1543
1544       case LB_DELETESTRING:
1545          {
1546          int iItemSkip = (int)wp;
1547          int iItemMax = SendMessage (hList, LB_GETCOUNT, 0, 0);
1548          int cchMax = 0;
1549
1550          for (int iItem = 0; iItem < iItemMax; iItem++)
1551             {
1552             if (iItem == iItemSkip)
1553                continue;
1554             int cch = SendMessage (hList, LB_GETTEXTLEN, (WPARAM)iItem, 0);
1555             cchMax = max (cch, cchMax);
1556             }
1557
1558          WORD cxExtent = (WORD)SendMessage (hList, LB_GETHORIZONTALEXTENT, 0, 0);
1559          WORD cxStringMax = 0;
1560          LPTSTR pszString = AllocateString (cchMax +1);
1561
1562          for (iItem = 0; iItem < iItemMax; iItem++)
1563             {
1564             if (iItem == iItemSkip)
1565                continue;
1566             SendMessage (hList, LB_GETTEXT, (WPARAM)iItem, (LPARAM)pszString);
1567             WORD cxString = LB_GetStringExtent (hList, pszString);
1568             cxStringMax = max (cxString, cxStringMax);
1569             }
1570
1571          FreeString (pszString);
1572          SendMessage (hList, LB_SETHORIZONTALEXTENT, (WPARAM)cxStringMax, 0);
1573          }
1574          break;
1575       }
1576
1577    if (oldProc)
1578       return CallWindowProc ((WNDPROC)oldProc, hList, msg, wp, lp);
1579    else
1580       return DefWindowProc (hList, msg, wp, lp);
1581 }
1582
1583
1584 void LB_EnableHScroll (HWND hList)
1585 {
1586    Subclass_AddHook (hList, ListBox_HScrollHook);
1587 }
1588
1589
1590 /*
1591  * TREEVIEWS __________________________________________________________________
1592  *
1593  */
1594
1595 LPARAM TV_GetData (HWND hTree, HTREEITEM hti)
1596 {
1597    TV_ITEM tvi;
1598    tvi.lParam = 0;
1599    tvi.hItem = hti;
1600    tvi.mask = TVIF_PARAM;
1601
1602    if (!TreeView_GetItem (hTree, &tvi))
1603       return (LPARAM)NULL;
1604
1605    return tvi.lParam;
1606 }
1607
1608
1609 LPARAM TV_GetSelectedData (HWND hTree)
1610 {
1611    HTREEITEM hti = TreeView_GetSelection (hTree);
1612
1613    if (hti == NULL)
1614       return NULL;
1615
1616    return TV_GetData (hTree, hti);
1617 }
1618
1619
1620 LPARAM TV_StartChange (HWND hTree, BOOL fReset)
1621 {
1622    LPARAM dwSelected = TV_GetSelectedData (hTree);
1623    SendMessage (hTree, WM_SETREDRAW, FALSE, 0);
1624    if (fReset)
1625       TreeView_DeleteAllItems (hTree);
1626    return dwSelected;
1627 }
1628
1629
1630 void TV_EndChange (HWND hTree, LPARAM lpToSelect)
1631 {
1632    SendMessage (hTree, WM_SETREDRAW, TRUE, 0);
1633
1634    if (lpToSelect != NULL)
1635       {
1636       HTREEITEM hti = TV_RecursiveFind (hTree, TVI_ROOT, lpToSelect);
1637       if (hti != NULL)
1638          TreeView_SelectItem (hTree, hti);
1639       }
1640 }
1641
1642
1643 HTREEITEM TV_RecursiveFind (HWND hTree, HTREEITEM htiRoot, LPARAM lpToFind)
1644 {
1645    for (HTREEITEM hti = (htiRoot == TVI_ROOT) ? TreeView_GetRoot (hTree) : TreeView_GetChild (hTree, htiRoot);
1646         hti != NULL;
1647         hti = TreeView_GetNextSibling (hTree, hti))
1648       {
1649       if (lpToFind == TV_GetData (hTree, hti))
1650          return hti;
1651
1652       HTREEITEM htiFound;
1653       if ((htiFound = TV_RecursiveFind (hTree, hti, lpToFind)) != NULL)
1654          return htiFound;
1655       }
1656
1657    return NULL;
1658 }
1659
1660
1661 /*
1662  * COMMON DIALOGS _____________________________________________________________
1663  *
1664  */
1665
1666 BOOL Browse_Open (HWND hParent, LPTSTR pszFilename, LPTSTR pszLastDirectory, int idsFilter, int iddTemplate, LPOFNHOOKPROC lpfnHook, DWORD lCustData)
1667 {
1668    TCHAR szFilter[ cchRESOURCE ];
1669    GetString (szFilter, idsFilter);
1670    TCHAR chFilter = szFilter[ lstrlen(szFilter)-1 ];
1671    for (LPTSTR pszFilter = szFilter;
1672         (*pszFilter) && ((pszFilter = (LPTSTR)lstrchr (pszFilter, chFilter)) != NULL);
1673         ++pszFilter)
1674       {
1675       *pszFilter = TEXT('\0');
1676       }
1677
1678    OPENFILENAME ofn;
1679    memset (&ofn, 0x00, sizeof(ofn));
1680    ofn.lStructSize = sizeof(ofn);
1681    ofn.hwndOwner = hParent;
1682    ofn.hInstance = THIS_HINST;
1683    ofn.lpstrFilter = szFilter;
1684    ofn.nFilterIndex = 1;
1685    ofn.lpstrFile = pszFilename;
1686    ofn.nMaxFile = MAX_PATH;
1687    ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
1688    ofn.lCustData = lCustData;
1689
1690    if (iddTemplate != 0)
1691       {
1692       ofn.lpTemplateName = MAKEINTRESOURCE( iddTemplate );
1693       ofn.Flags |= OFN_ENABLETEMPLATE | OFN_EXPLORER;
1694       }
1695    if (lpfnHook != NULL)
1696       {
1697       ofn.lpfnHook = lpfnHook;
1698       ofn.Flags |= OFN_ENABLEHOOK | OFN_EXPLORER;
1699       }
1700
1701    TCHAR szPath[ MAX_PATH ];
1702    GetCurrentDirectory (MAX_PATH, szPath);
1703    if (pszLastDirectory && *pszLastDirectory)
1704       SetCurrentDirectory (pszLastDirectory);
1705
1706    BOOL rc = GetOpenFileName (&ofn);
1707
1708    if (pszLastDirectory)
1709       GetCurrentDirectory (MAX_PATH, pszLastDirectory);
1710    SetCurrentDirectory (szPath);
1711
1712    return rc;
1713 }
1714
1715
1716 BOOL Browse_Save (HWND hParent, LPTSTR pszFilename, LPTSTR pszLastDirectory, int idsFilter, int iddTemplate, LPOFNHOOKPROC lpfnHook, DWORD lCustData)
1717 {
1718    TCHAR szFilter[ cchRESOURCE ];
1719    GetString (szFilter, idsFilter);
1720    TCHAR chFilter = szFilter[ lstrlen(szFilter)-1 ];
1721    for (LPTSTR pszFilter = szFilter;
1722         (*pszFilter) && ((pszFilter = (LPTSTR)lstrchr (pszFilter, chFilter)) != NULL);
1723         ++pszFilter)
1724       {
1725       *pszFilter = TEXT('\0');
1726       }
1727
1728    OPENFILENAME sfn;
1729    memset (&sfn, 0x00, sizeof(sfn));
1730    sfn.lStructSize = sizeof(sfn);
1731    sfn.hwndOwner = hParent;
1732    sfn.hInstance = THIS_HINST;
1733    sfn.lpstrFilter = szFilter;
1734    sfn.nFilterIndex = 1;
1735    sfn.lpstrFile = pszFilename;
1736    sfn.nMaxFile = MAX_PATH;
1737    sfn.Flags = OFN_HIDEREADONLY | OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
1738    sfn.lCustData = lCustData;
1739
1740    if (iddTemplate != 0)
1741       {
1742       sfn.lpTemplateName = MAKEINTRESOURCE( iddTemplate );
1743       sfn.Flags |= OFN_ENABLETEMPLATE | OFN_EXPLORER;
1744       }
1745    if (lpfnHook != NULL)
1746       {
1747       sfn.lpfnHook = lpfnHook;
1748       sfn.Flags |= OFN_ENABLEHOOK | OFN_EXPLORER;
1749       }
1750
1751    TCHAR szPath[ MAX_PATH ];
1752    GetCurrentDirectory (MAX_PATH, szPath);
1753    if (pszLastDirectory && *pszLastDirectory)
1754       SetCurrentDirectory (pszLastDirectory);
1755
1756    BOOL rc = GetSaveFileName (&sfn);
1757
1758    if (pszLastDirectory)
1759       GetCurrentDirectory (MAX_PATH, pszLastDirectory);
1760    SetCurrentDirectory (szPath);
1761
1762    return rc;
1763 }
1764
1765
1766 /*
1767  * HOURGLASS CURSOR ETC _______________________________________________________
1768  *
1769  */
1770
1771 static LONG nHourGlassRequests = 0;
1772
1773 void StartHourGlass (void)
1774 {
1775    if ((++nHourGlassRequests) == 1)
1776       {
1777       SetCursor (LoadCursor (NULL, IDC_WAIT));
1778       }
1779 }
1780
1781 void StopHourGlass (void)
1782 {
1783    if ((nHourGlassRequests == 0) || ((--nHourGlassRequests) == 0))
1784       {
1785       SetCursor (LoadCursor (NULL, IDC_ARROW));
1786       }
1787 }
1788
1789
1790
1791 void DisplayContextMenu (HMENU hm, POINT ptScreen, HWND hParent)
1792 {
1793    HMENU hmDummy = CreateMenu();
1794    InsertMenu (hmDummy, 0, MF_POPUP, (UINT)hm, NULL);
1795
1796    TrackPopupMenu (GetSubMenu (hmDummy, 0),
1797                    TPM_LEFTALIGN | TPM_RIGHTBUTTON,
1798                    ptScreen.x, ptScreen.y,
1799                    0, hParent, NULL);
1800
1801    DestroyMenu (hmDummy);
1802 }
1803
1804
1805 size_t CountChildren (HWND hParent, LPTSTR pszClass)
1806 {
1807    size_t nFound = 0;
1808
1809    for (HWND hChild = GetWindow (hParent, GW_CHILD);
1810         hChild != NULL;
1811         hChild = GetWindow (hChild, GW_HWNDNEXT))
1812       {
1813       TCHAR szClassName[ cchRESOURCE ];
1814
1815       if (GetClassName (hChild, szClassName, cchRESOURCE))
1816          {
1817          if (!lstrcmp (szClassName, pszClass))
1818             ++nFound;
1819          }
1820       }
1821
1822    return nFound;
1823 }
1824
1825
1826 WORD NextControlID (HWND hParent)
1827 {
1828    WORD wNext = 1;
1829
1830    for (HWND hChild = GetWindow (hParent, GW_CHILD);
1831         hChild != NULL;
1832         hChild = GetWindow (hChild, GW_HWNDNEXT))
1833       {
1834       LONG wChild = GetWindowLong (hChild, GWL_ID);
1835       if ((wChild < 0) || (wChild >= 0xF000))
1836          continue;
1837
1838       wNext = max( wNext, (WORD) wChild+1 );
1839       }
1840
1841    return wNext;
1842 }
1843
1844
1845 BOOL IsAncestor (HWND hParent, HWND hChild)
1846 {
1847    for ( ; hChild; hChild = GetParent(hChild))
1848       {
1849       if (hParent == hChild)
1850          return TRUE;
1851       }
1852    return FALSE;
1853 }
1854
1855
1856 HWND GetTabChild (HWND hTab)
1857 {
1858    for (HWND hChild = GetWindow (hTab, GW_CHILD);
1859         hChild != NULL;
1860         hChild = GetWindow (hChild, GW_HWNDNEXT))
1861       {
1862       TCHAR szClassName[ cchRESOURCE ];
1863
1864       if (GetClassName (hChild, szClassName, cchRESOURCE))
1865          {
1866          if (!lstrcmp (szClassName, TEXT("#32770"))) // WC_DIALOG
1867             return hChild;
1868          }
1869       }
1870
1871    return NULL;
1872 }
1873
1874
1875 HWND GetLastDlgTabItem (HWND hDlg)
1876 {
1877    HWND hFirst = NULL;
1878    HWND hLast = NULL;
1879
1880    for (HWND hSearch = GetNextDlgTabItem (hDlg, NULL, FALSE);
1881         (hSearch != NULL) && (hSearch != hFirst);
1882         hSearch = GetNextDlgTabItem (hDlg, hSearch, FALSE))
1883       {
1884       if (!hFirst)
1885          hFirst = hSearch;
1886       hLast = hSearch;
1887       }
1888
1889    return hLast;
1890 }
1891
1892
1893 BOOL IsPropSheet (HWND hSheet)
1894 {
1895    HWND hTab;
1896    if ((hTab = GetDlgItem (hSheet, IDC_PROPSHEET_TABCTRL)) == NULL)
1897       return FALSE;
1898
1899    TCHAR szClassName[ cchRESOURCE ];
1900    if (!GetClassName (hTab, szClassName, cchRESOURCE))
1901       return FALSE;
1902
1903    if (lstrcmp (szClassName, WC_TABCONTROL))
1904       return FALSE;
1905
1906    return TRUE;
1907 }
1908