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