If the new credentials window is in the advanced mode and the user
[krb5.git] / src / windows / identity / ui / newcredwnd.c
1 /*
2  * Copyright (c) 2005 Massachusetts Institute of Technology
3  * Copyright (c) 2007 Secure Endpoints Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person
6  * obtaining a copy of this software and associated documentation
7  * files (the "Software"), to deal in the Software without
8  * restriction, including without limitation the rights to use, copy,
9  * modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25
26 /* $Id$ */
27
28 /* Include the OEMRESOURCE constants for locating standard icon
29    resources. */
30 #define OEMRESOURCE
31
32 #include<khmapp.h>
33 #include<assert.h>
34
35 ATOM khui_newcredwnd_cls;
36
37 /* forward dcl */
38 static void
39 nc_position_credtext(khui_nc_wnd_data * d);
40
41 /* Common dialog procedure used by the main credential panel
42    (IDD_NC_NEWCRED) and the button bar (IDC_NC_BBAR). */
43
44 static void
45 nc_layout_main_panel(khui_nc_wnd_data * d);
46
47 static void
48 nc_layout_new_cred_window(khui_nc_wnd_data * d);
49
50 static INT_PTR CALLBACK 
51 nc_common_dlg_proc(HWND hwnd,
52                    UINT uMsg,
53                    WPARAM wParam,
54                    LPARAM lParam)
55 {
56     switch(uMsg) {
57     case WM_INITDIALOG:
58         {
59             khui_nc_wnd_data * d;
60
61             d = (khui_nc_wnd_data *) lParam;
62
63 #pragma warning(push)
64 #pragma warning(disable: 4244)
65             SetWindowLongPtr(hwnd, DWLP_USER, lParam);
66 #pragma warning(pop)
67
68             if (d->nc->subtype == KMSG_CRED_PASSWORD) {
69                 ShowWindow(GetDlgItem(hwnd, IDC_NC_ADVANCED),
70                            SW_HIDE);
71             }
72         }
73         return TRUE;
74
75     case WM_COMMAND:
76         {
77             int ctrl_id;
78
79             ctrl_id = LOWORD(wParam);
80             if (ctrl_id < KHUI_CW_ID_MIN ||
81                 ctrl_id > KHUI_CW_ID_MAX) {
82                 /* pump it to the parent */
83                 PostMessage(GetParent(hwnd), WM_COMMAND, wParam, lParam);
84                 return TRUE;
85             } /* else we allow the message to fall through and get
86                  passed into the identity provider's message
87                  handler. */
88         }
89         break;
90
91     case KHUI_WM_NC_NOTIFY:
92         {
93             khui_nc_wnd_data * d;
94             d = (khui_nc_wnd_data *)(LONG_PTR) 
95                 GetWindowLongPtr(hwnd, DWLP_USER);
96             if (d == NULL)
97                 break;
98
99             /* message sent by parent to notify us of something */
100             switch(HIWORD(wParam)) {
101             case WMNC_DIALOG_EXPAND:
102                 /* fallthrough */
103             case WMNC_UPDATE_LAYOUT:
104                 if(hwnd == d->dlg_main) {
105
106                     nc_layout_main_panel(d);
107
108                     return TRUE;
109                 }
110                 break;          /* nop */
111             }
112         }
113         return TRUE;
114     }
115
116     /* check if we have a wnd_data, and if so pass the message on to
117        the identity provider callback. */
118     {
119         khui_nc_wnd_data * d;
120
121         d = (khui_nc_wnd_data *) (LONG_PTR)
122             GetWindowLongPtr(hwnd, DWLP_USER);
123
124         /* TODO: filter out and forward only the messages that
125            originated or pertain to the identity selection
126            controls. */
127         if (d && d->nc && d->nc->ident_cb) {
128             return d->nc->ident_cb(d->nc, WMNC_IDENT_WMSG, hwnd, uMsg, 
129                                    wParam, lParam);
130         }
131     }
132
133     return FALSE;
134 }
135
136 static void
137 nc_notify_clear(khui_nc_wnd_data * d) {
138
139     if (d->notif_type == NC_NOTIFY_NONE)
140         /* there are no notifications anyway. */
141         return;
142
143     if (d->hwnd_notif_label)
144         DestroyWindow(d->hwnd_notif_label);
145
146     if (d->hwnd_notif_aux)
147         DestroyWindow(d->hwnd_notif_aux);
148
149     d->hwnd_notif_label = NULL;
150     d->hwnd_notif_aux = NULL;
151
152     SetRectEmpty(&d->r_notif);
153
154     d->notif_type = NC_NOTIFY_NONE;
155
156     /* Note that we must call nc_layout_main_panel() after calling
157        this to adjust the layout of the main panel.  However we aren't
158        calling it here since we might want to add another set of
159        notifications or make other changes to the main panel content
160        before calling nc_layout_main_panel(). */
161 }
162
163 static void
164 nc_notify_marquee(khui_nc_wnd_data * d, const wchar_t * label) {
165
166 #if (_WIN32_IE >= 0x0600)
167     HDC hdc;
168     size_t length;
169     SIZE label_size;
170 #endif
171
172     RECT r_label;
173     RECT r_mq;
174     RECT r_row;
175     HFONT hfont;
176     HWND hwnd;
177     HDWP hdefer;
178
179     /* Clear the notification area.  We only support one notification
180        at a time. */
181     nc_notify_clear(d);
182
183 #ifdef DEBUG
184     assert(d->dlg_main);
185 #endif
186
187 #if (_WIN32_IE >= 0x0600)
188
189     /* We can only show the marquee control if the comctl32 DLL is
190        version 6.0 or later.  Otherwise we only show the label. */
191
192     if (FAILED(StringCchLength(label, KHUI_MAXCCH_SHORT_DESC, &length))) {
193 #ifdef DEBUG
194         assert(FALSE);
195 #endif
196         length = KHUI_MAXCCH_SHORT_DESC;
197     }
198
199     /* See how big the notification control needs to be. */
200
201     hdc = GetDC(d->dlg_main);
202 #ifdef DEBUG
203     assert(hdc != NULL);
204 #endif
205
206     GetTextExtentPoint32(hdc, label, (int) length, &label_size);
207
208     ReleaseDC(d->dlg_main, hdc);
209
210     CopyRect(&r_row, &d->r_row);
211
212     if (label_size.cx > d->r_e_label.right - d->r_e_label.left) {
213         /* using an entire row */
214         CopyRect(&r_label, &d->r_row);
215         CopyRect(&r_mq, &d->r_n_input);
216         OffsetRect(&r_mq, 0, r_row.bottom - r_row.top);
217         r_row.bottom += r_row.bottom - r_row.top;
218     } else if (label_size.cx > d->r_n_label.right - d->r_n_label.left) {
219         /* using large labels */
220         CopyRect(&r_label, &d->r_e_label);
221         CopyRect(&r_mq, &d->r_e_input);
222     } else {
223         /* normal labels */
224         CopyRect(&r_label, &d->r_n_label);
225         CopyRect(&r_mq, &d->r_n_input);
226     }
227
228     InflateRect(&r_mq, 0, - ((r_mq.bottom - r_mq.top) / 4));
229
230 #else  /* _WIN32_IE < 0x0600 */
231
232     /* We are just showing the label */
233     CopyRect(&r_row, &d->r_row);
234     CopyRect(&r_label, &r_row);
235     SetRectEmpty(&r_mq);
236
237 #endif /* _WIN32_IE >= 0x0600 */
238
239     {
240         long y;
241
242         if (IsRectEmpty(&d->r_custprompt)) {
243             y = d->r_idspec.bottom;
244         } else {
245             y = d->r_custprompt.bottom;
246         }
247
248         OffsetRect(&r_row, d->r_area.left, y);
249         OffsetRect(&r_label, r_row.left, r_row.top);
250         OffsetRect(&r_mq, r_row.left, r_row.top);
251     }
252
253     hfont = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0);
254
255     hdefer = BeginDeferWindowPos(2);
256
257     /* the label */
258     hwnd = CreateWindowEx(0,
259                           L"STATIC",
260                           label,
261                           WS_CHILD | SS_ENDELLIPSIS,
262                           r_label.left, r_label.top,
263                           r_label.right - r_label.left,
264                           r_label.bottom - r_label.top,
265                           d->dlg_main,
266                           NULL, NULL, NULL);
267 #ifdef DEBUG
268     assert(hwnd != NULL);
269 #endif
270     SendMessage(hwnd, WM_SETFONT, (WPARAM) hfont, (LPARAM) TRUE);
271
272     DeferWindowPos(hdefer, hwnd, NULL,
273                    0, 0, 0, 0,
274                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |
275                    SWP_NOSIZE | SWP_SHOWWINDOW);
276
277     d->hwnd_notif_label = hwnd;
278
279     /* and the marquee */
280
281 #if (_WIN32_IE >= 0x0600)
282
283     /* unfortunately, the marquee is only available on comctl32
284        version 6.0 or later.  On previous versions, we only display
285        the message label. */
286
287     hwnd = CreateWindowEx(0,
288                           PROGRESS_CLASS,
289                           L"",
290                           WS_CHILD | PBS_MARQUEE,
291                           r_mq.left, r_mq.top,
292                           r_mq.right - r_mq.left,
293                           r_mq.bottom - r_mq.top,
294                           d->dlg_main,
295                           NULL, NULL, NULL);
296 #ifdef DEBUG
297     assert(hwnd != NULL);
298 #endif
299
300     SendMessage(hwnd, PBM_SETMARQUEE, TRUE, 100);
301
302     DeferWindowPos(hdefer, hwnd, NULL,
303                    0, 0, 0, 0,
304                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |
305                    SWP_NOSIZE | SWP_SHOWWINDOW);
306
307     d->hwnd_notif_aux = hwnd;
308
309 #endif /* _WIN32_IE >= 0x0600 */
310
311     EndDeferWindowPos(hdefer);
312
313     CopyRect(&d->r_notif, &r_row);
314
315     d->notif_type = NC_NOTIFY_MARQUEE;
316
317     /* Note that we must call nc_layout_main_panel() after calling
318        this to adjust the layout of the main panel.  However we aren't
319        calling it here since we might want to add another set of
320        notifications or make other changes to the main panel content
321        before calling nc_layout_main_panel(). */
322 }
323
324 static void
325 nc_notify_message(khui_nc_wnd_data * d,
326                   kherr_severity severity,
327                   const wchar_t * message) {
328
329     SIZE icon_size;
330     LPCTSTR icon_res;
331     HICON h_icon;
332     HWND hwnd;
333     HFONT hfont;
334     HDWP hdefer;
335
336     RECT r_row;
337     RECT r_label;
338     RECT r_icon;
339
340     nc_notify_clear(d);
341
342     icon_size.cx = GetSystemMetrics(SM_CXSMICON);
343     icon_size.cy = GetSystemMetrics(SM_CYSMICON);
344
345     switch(severity) {
346     case KHERR_INFO:
347         icon_res = MAKEINTRESOURCE(OIC_INFORMATION);
348         break;
349
350     case KHERR_WARNING:
351         icon_res = MAKEINTRESOURCE(OIC_WARNING);
352         break;
353
354     case KHERR_ERROR:
355         icon_res = MAKEINTRESOURCE(OIC_ERROR);
356         break;
357
358     default:
359         icon_res = NULL;
360     }
361
362     if (icon_res != NULL) {
363         h_icon = (HICON) LoadImage(NULL,
364                                    icon_res,
365                                    IMAGE_ICON,
366                                    icon_size.cx,
367                                    icon_size.cy,
368                                    LR_DEFAULTCOLOR | LR_SHARED);
369     } else {
370         h_icon = NULL;
371     }
372
373     CopyRect(&r_row, &d->r_row);
374
375 #define CENTERVALUE(w,v) ((w)/2 - (v)/2)
376
377     SetRect(&r_icon,
378             0, CENTERVALUE(r_row.bottom - r_row.top, icon_size.cy),
379             icon_size.cx,
380             CENTERVALUE(r_row.bottom - r_row.top, icon_size.cy) + icon_size.cy);
381
382 #undef CENTERVALUE
383
384     CopyRect(&r_label, &r_row);
385     OffsetRect(&r_label, -r_label.left, -r_label.top);
386     r_label.left += (icon_size.cx * 3) / 2;
387
388     {
389         long y;
390
391         if (IsRectEmpty(&d->r_custprompt)) {
392             y = d->r_idspec.bottom;
393         } else {
394             y = d->r_custprompt.bottom;
395         }
396
397         OffsetRect(&r_row, d->r_area.left, y);
398         OffsetRect(&r_label, r_row.left, r_row.top);
399         OffsetRect(&r_icon, r_row.left, r_row.top);
400     }
401
402     hfont = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0);
403
404     hdefer = BeginDeferWindowPos(2);
405
406     hwnd = CreateWindowEx(0,
407                           L"STATIC",
408                           message,
409                           WS_CHILD | SS_ENDELLIPSIS | SS_CENTER,
410                           r_label.left, r_label.top,
411                           r_label.right - r_label.left,
412                           r_label.bottom - r_label.top,
413                           d->dlg_main,
414                           NULL, NULL, NULL);
415 #ifdef DEBUG
416     assert(hwnd != NULL);
417 #endif
418     SendMessage(hwnd, WM_SETFONT, (WPARAM) hfont, (LPARAM) TRUE);
419
420     DeferWindowPos(hdefer, hwnd, NULL,
421                    0, 0, 0, 0,
422                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |
423                    SWP_NOSIZE | SWP_SHOWWINDOW);
424
425     d->hwnd_notif_label = hwnd;
426
427     hwnd = CreateWindowEx(0,
428                           L"STATIC",
429                           NULL,
430                           WS_CHILD | SS_ICON |
431 #if (_WIN32_IE >= 0x0600)
432                           SS_REALSIZECONTROL
433 #else
434                           0
435 #endif
436                           ,
437                           r_icon.left, r_icon.top,
438                           r_icon.right - r_icon.left,
439                           r_icon.bottom - r_icon.top,
440                           d->dlg_main,
441                           NULL, NULL, NULL);
442 #ifdef DEBUG
443     assert(hwnd != NULL);
444 #endif
445
446     if (h_icon && hwnd)
447         SendMessage(hwnd, STM_SETICON, (WPARAM) h_icon, 0);
448
449     DeferWindowPos(hdefer, hwnd, NULL,
450                    0, 0, 0, 0,
451                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |
452                    SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOZORDER);
453
454     d->hwnd_notif_aux = hwnd;
455
456     EndDeferWindowPos(hdefer);
457
458     CopyRect(&d->r_notif, &r_row);
459
460     d->notif_type = NC_NOTIFY_MESSAGE;
461
462     /* Note that we must call nc_layout_main_panel() after calling
463        this to adjust the layout of the main panel.  However we aren't
464        calling it here since we might want to add another set of
465        notifications or make other changes to the main panel content
466        before calling nc_layout_main_panel(). */
467 }
468
469 static void
470 nc_layout_main_panel(khui_nc_wnd_data * d)
471 {
472     RECT r_main;
473     HWND hw_ct;
474     HWND hw_ct_label;
475     HDWP hdwp;
476     RECT r_used;                /* extent used by identity specifiers,
477                                    custom prompts and notificaiton
478                                    controls. */
479
480     RECT r_wmain;              /* extents of the main window in screen
481                                   coordinates. */
482
483     r_main.left = 0;
484     r_main.top = 0;
485     r_main.bottom = NCDLG_HEIGHT;
486     r_main.right = NCDLG_WIDTH;
487
488     MapDialogRect(d->dlg_main, &r_main);
489
490     CopyRect(&r_used, &d->r_idspec);
491
492     GetWindowRect(d->dlg_main, &r_wmain);
493
494     hdwp = BeginDeferWindowPos(7);
495
496     /* check if the notification area and the custom prompt area are
497        overlapping. */
498
499     if (d->notif_type != NC_NOTIFY_NONE) {
500         long delta_y = 0;
501         RECT r;
502
503         CopyRect(&r, &d->r_custprompt);
504
505         if (IsRectEmpty(&d->r_custprompt)) {
506             /* if there are no custom prompts, then the notification
507                area should be immediately below the identitify
508                specifers. */
509
510             delta_y = d->r_idspec.bottom - d->r_notif.top;
511         } else {
512             /* otherwise, the notification area should be immediately
513                below the custom prompt area */
514
515             delta_y = d->r_custprompt.bottom - d->r_notif.top;
516         }
517
518         if (delta_y != 0) {
519             RECT r_lbl;
520             RECT r_aux;
521
522             if (d->hwnd_notif_label) {
523                 GetWindowRect(d->hwnd_notif_label, &r_lbl);
524                 OffsetRect(&r_lbl, -r_wmain.left, delta_y - r_wmain.top);
525
526                 DeferWindowPos(hdwp, d->hwnd_notif_label, NULL,
527                                r_lbl.left, r_lbl.top, 0, 0,
528                                SWP_NOACTIVATE | SWP_NOOWNERZORDER |
529                                SWP_NOZORDER | SWP_NOSIZE);
530             }
531
532             if (d->hwnd_notif_aux) {
533                 GetWindowRect(d->hwnd_notif_aux, &r_aux);
534                 OffsetRect(&r_aux, -r_wmain.left, delta_y - r_wmain.top);
535
536                 DeferWindowPos(hdwp, d->hwnd_notif_aux, NULL,
537                                r_aux.left, r_aux.top, 0, 0,
538                                SWP_NOACTIVATE | SWP_NOOWNERZORDER |
539                                SWP_NOZORDER | SWP_NOSIZE);
540             }
541
542             OffsetRect(&d->r_notif, 0, delta_y);
543         }
544     }
545
546     if (!IsRectEmpty(&d->r_custprompt)) {
547         r_used.bottom = max(d->r_custprompt.bottom,
548                             r_used.bottom);
549     }
550
551     if (!IsRectEmpty(&d->r_notif)) {
552         r_used.bottom = max(d->r_notif.bottom,
553                             r_used.bottom);
554     }
555
556     if (d->nc->mode == KHUI_NC_MODE_MINI) {
557         RECT r_ok;
558         RECT r_cancel;
559         RECT r_advanced;
560         HWND hw;
561
562         hw = GetDlgItem(d->dlg_main, IDOK);
563 #ifdef DEBUG
564         assert(hw != NULL);
565 #endif
566         GetWindowRect(hw, &r_ok);
567         OffsetRect(&r_ok, -r_wmain.left, -r_ok.top + r_used.bottom);
568
569         DeferWindowPos(hdwp, hw, NULL,
570                        r_ok.left, r_ok.top, 0, 0,
571                        SWP_NOACTIVATE | SWP_NOOWNERZORDER |
572                        SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
573
574         hw = GetDlgItem(d->dlg_main, IDCANCEL);
575 #ifdef DEBUG
576         assert(hw != NULL);
577 #endif
578         GetWindowRect(hw, &r_cancel);
579         OffsetRect(&r_cancel, -r_wmain.left, -r_cancel.top + r_used.bottom);
580
581         DeferWindowPos(hdwp, hw, NULL,
582                        r_cancel.left, r_cancel.top, 0, 0,
583                        SWP_NOACTIVATE | SWP_NOOWNERZORDER |
584                        SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
585
586         hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED);
587 #ifdef DEBUG
588         assert(hw != NULL);
589 #endif
590         GetWindowRect(hw, &r_advanced);
591         OffsetRect(&r_advanced, -r_wmain.left, -r_advanced.top + r_used.bottom);
592
593         DeferWindowPos(hdwp, hw, NULL,
594                        r_advanced.left, r_advanced.top, 0, 0,
595                        SWP_NOACTIVATE | SWP_NOOWNERZORDER |
596                        SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
597
598         /* and now update the extents of the main panel */
599         r_main.bottom = r_used.bottom + (r_ok.bottom - r_ok.top) + d->r_area.top;
600
601         CopyRect(&d->r_main, &r_main);
602
603     } else {
604
605         HWND hw;
606
607         hw = GetDlgItem(d->dlg_main, IDOK);
608 #ifdef DEBUG
609         assert(hw != NULL);
610 #endif
611         if (IsWindowVisible(hw))
612             DeferWindowPos(hdwp, hw, NULL,
613                            0, 0, 0, 0,
614                            SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE |
615                            SWP_NOOWNERZORDER | SWP_NOZORDER);
616
617         hw = GetDlgItem(d->dlg_main, IDCANCEL);
618 #ifdef DEBUG
619         assert(hw != NULL);
620 #endif
621         if (IsWindowVisible(hw))
622             DeferWindowPos(hdwp, hw, NULL,
623                            0, 0, 0, 0,
624                            SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE |
625                            SWP_NOOWNERZORDER | SWP_NOZORDER);
626
627         hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED);
628 #ifdef DEBUG
629         assert(hw != NULL);
630 #endif
631         if (IsWindowVisible(hw))
632             DeferWindowPos(hdwp, hw, NULL,
633                            0, 0, 0, 0,
634                            SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE |
635                            SWP_NOOWNERZORDER | SWP_NOZORDER);
636
637         d->r_credtext.top = r_used.bottom;
638
639         CopyRect(&d->r_main, &r_main);
640     }
641
642     /* now update the layout of the credentials text window */
643
644     hw_ct = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT);
645     hw_ct_label = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT_LABEL);
646 #ifdef DEBUG
647     assert(hw_ct != NULL);
648     assert(hw_ct_label != NULL);
649 #endif
650
651     if (d->nc->mode == KHUI_NC_MODE_MINI ||
652         d->r_credtext.bottom < d->r_credtext.top + d->r_row.bottom * 2) {
653
654         /* either we aren't supposed to show the credentials text
655            window, or we don't have enough room. */
656         if (IsWindowVisible(hw_ct) || IsWindowVisible(hw_ct_label)) {
657
658             DeferWindowPos(hdwp, hw_ct, NULL,
659                            0, 0, 0, 0,
660                            SWP_HIDEWINDOW | SWP_NOOWNERZORDER |
661                            SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
662
663             DeferWindowPos(hdwp, hw_ct_label, NULL,
664                            0, 0, 0, 0,
665                            SWP_HIDEWINDOW | SWP_NOOWNERZORDER |
666                            SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
667
668         }
669
670     } else {
671
672         DeferWindowPos(hdwp,
673                        hw_ct, NULL,
674                        d->r_credtext.left + d->r_n_input.left, /* x */
675                        d->r_credtext.top, /* y */
676                        d->r_n_input.right - d->r_n_input.left, /* width */
677                        d->r_credtext.bottom - d->r_credtext.top, /* height */
678                        SWP_NOACTIVATE | SWP_NOOWNERZORDER | 
679                        SWP_NOZORDER | SWP_SHOWWINDOW);
680
681         DeferWindowPos(hdwp,
682                        hw_ct_label, NULL,
683                        d->r_credtext.left + d->r_n_label.left, /* x */
684                        d->r_credtext.top, /* y */
685                        d->r_n_label.right - d->r_n_label.left, /* width */
686                        d->r_n_label.bottom - d->r_n_label.top, /* height */
687                        SWP_NOACTIVATE | SWP_NOOWNERZORDER |
688                        SWP_NOZORDER | SWP_SHOWWINDOW);
689     }
690
691     EndDeferWindowPos(hdwp);
692
693     /* NOTE: although we updated d->r_main, if the new credentials
694        window is in mini mode, we must call
695        nc_layout_new_cred_window() to adjust the size of the new
696        credentials window to fit the main panel.  We don't do it here
697        because we need to keep these two operations separate. */
698 }
699
700 /* Credential type panel comparison function.  Tabs are sorted based
701    on the following criteria:
702
703    1) By ordinal - Panels with ordinal -1 will be ranked after panels
704       whose ordinal is not -1.
705
706    2) By name - Case insensitive comparison of the name.  If the panel
707       does not have a name (i.e. the ->name member is NULL, it will be
708       ranked after panels which have a name.
709  */
710 static int __cdecl
711 nc_tab_sort_func(const void * v1, const void * v2)
712 {
713     /* v1 and v2 and of type : khui_new_creds_by_type ** */
714     khui_new_creds_by_type *t1, *t2;
715
716     t1 = *((khui_new_creds_by_type **) v1);
717     t2 = *((khui_new_creds_by_type **) v2);
718
719     if(t1->ordinal !=  -1) {
720         if(t2->ordinal != -1) {
721             if(t1->ordinal == t2->ordinal) {
722                 if (t1->name && t2->name)
723                     return _wcsicmp(t1->name, t2->name);
724                 else if (t1->name)
725                     return -1;
726                 else if (t2->name)
727                     return 1;
728                 else
729                     return 0;
730             } else {
731                 /* safe to convert to an int here */
732                 return (int) (t1->ordinal - t2->ordinal);
733             }
734         } else
735             return -1;
736     } else {
737         if(t2->ordinal != -1)
738             return 1;
739         else if (t1->name && t2->name)
740             return wcscmp(t1->name, t2->name);
741         else if (t1->name)
742             return -1;
743         else if (t2->name)
744             return 1;
745         else
746             return 0;
747     }
748 }
749
750 static void 
751 nc_notify_types(khui_new_creds * c, UINT uMsg,
752                 WPARAM wParam, LPARAM lParam, BOOL sync)
753 {
754     khm_size i;
755
756     for(i=0; i<c->n_types; i++) {
757
758         if (c->types[i]->hwnd_panel == NULL)
759             continue;
760
761         if (sync)
762             SendMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam);
763         else
764             PostMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam);
765     }
766 }
767
768 static void
769 nc_clear_password_fields(khui_nc_wnd_data * d)
770 {
771     khm_size i;
772     khm_boolean need_sync = FALSE;
773
774     khui_cw_lock_nc(d->nc);
775
776     for (i=0; i < d->nc->n_prompts; i++) {
777         if ((d->nc->prompts[i]->flags & KHUI_NCPROMPT_FLAG_HIDDEN) &&
778             d->nc->prompts[i]->hwnd_edit) {
779             SetWindowText(d->nc->prompts[i]->hwnd_edit,
780                           L"");
781             need_sync = TRUE;
782         }
783     }
784
785     khui_cw_unlock_nc(d->nc);
786
787     if (need_sync) {
788         khui_cw_sync_prompt_values(d->nc);
789     }
790 }
791
792 /* used by nc_enable_controls */
793
794 struct nc_enum_wnd_data {
795     khui_nc_wnd_data * d;
796     khm_boolean enable;
797 };
798
799 static
800 BOOL CALLBACK
801 nc_enum_wnd_proc(HWND hwnd,
802                  LPARAM lParam)
803 {
804     struct nc_enum_wnd_data * wd;
805
806     wd = (struct nc_enum_wnd_data *) lParam;
807
808     EnableWindow(hwnd, wd->enable);
809
810     return TRUE;
811 }
812
813 static void
814 nc_enable_controls(khui_nc_wnd_data * d, khm_boolean enable)
815 {
816     struct nc_enum_wnd_data wd;
817
818     ZeroMemory(&wd, sizeof(wd));
819
820     wd.d = d;
821     wd.enable = enable;
822
823     EnumChildWindows(d->dlg_main, nc_enum_wnd_proc, (LPARAM) &wd);
824 }
825
826 #define NC_MAXCCH_CREDTEXT 16384
827 #define NC_MAXCB_CREDTEXT (NC_MAXCCH_CREDTEXT * sizeof(wchar_t))
828
829 static void 
830 nc_update_credtext(khui_nc_wnd_data * d) 
831 {
832     wchar_t * ctbuf = NULL;
833     wchar_t * buf;
834     BOOL okEnable = FALSE;
835     BOOL validId = FALSE;
836     HWND hw = NULL;
837     size_t cch = 0;
838
839     ctbuf = PMALLOC(NC_MAXCB_CREDTEXT);
840
841     assert(ctbuf != NULL);
842
843     LoadString(khm_hInstance, IDS_NC_CREDTEXT_TABS, ctbuf, NC_MAXCCH_CREDTEXT);
844     StringCchLength(ctbuf, NC_MAXCCH_CREDTEXT, &cch);
845     buf = ctbuf + cch;
846     nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, 
847                     MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), (LPARAM) d->nc, TRUE);
848
849     /* hopefully all the types have updated their credential texts */
850
851     /* if the dialog is in the mini mode, we have to display
852        exceptions using a notification. */
853     if (d->nc->mode == KHUI_NC_MODE_MINI) {
854         BOOL need_layout = FALSE;
855         if (d->nc->n_identities == 0) {
856
857             /* There are no identities selected. We don't show any
858                notifications here. */
859             if (d->notif_type != NC_NOTIFY_NONE) {
860                 nc_notify_clear(d);
861                 need_layout = TRUE;
862             }
863
864         } else {
865
866             wchar_t id_name[KCDB_IDENT_MAXCCH_NAME];
867             wchar_t format[256];
868             wchar_t msg[ARRAYLENGTH(format) + ARRAYLENGTH(id_name)];
869             khm_size cbbuf;
870             khm_int32 flags;
871
872             kcdb_identity_get_flags(d->nc->identities[0], &flags);
873
874             cbbuf = sizeof(id_name);
875             kcdb_identity_get_name(d->nc->identities[0], id_name, &cbbuf);
876
877             if (flags & KCDB_IDENT_FLAG_INVALID) {
878
879                 /* identity is invalid */
880                 LoadString(khm_hInstance, IDS_NCN_IDENT_INVALID,
881                            format, ARRAYLENGTH(format));
882                 StringCbPrintf(msg, sizeof(msg), format, id_name);
883
884                 nc_notify_message(d, KHERR_ERROR, msg);
885
886                 need_layout = TRUE;
887
888             } else if ((flags & KCDB_IDENT_FLAG_VALID) ||
889                        d->nc->subtype == KMSG_CRED_PASSWORD) {
890                 /* special case: If we are going to change the
891                    password, we don't expect the identity provider to
892                    validate the identity in real time.  As such, we
893                    assume that the identity is valid. */
894  
895                /* identity is valid */
896                 if (d->notif_type != NC_NOTIFY_NONE) {
897                     nc_notify_clear(d);
898                     need_layout = TRUE;
899                 }
900
901             } else if (flags & KCDB_IDENT_FLAG_UNKNOWN) {
902
903                 /* unknown state */
904                 LoadString(khm_hInstance, IDS_NCN_IDENT_UNKNOWN,
905                            format, ARRAYLENGTH(format));
906                 StringCbPrintf(msg, sizeof(msg), format, id_name);
907
908                 nc_notify_message(d, KHERR_WARNING, msg);
909
910                 need_layout = TRUE;
911
912             } else {
913
914                 /* still checking */
915                 LoadString(khm_hInstance, IDS_NCN_IDENT_CHECKING,
916                            format, ARRAYLENGTH(format));
917                 StringCbPrintf(msg, sizeof(msg), format, id_name);
918
919                 nc_notify_marquee(d, msg);
920
921                 need_layout = TRUE;
922
923             }
924         }
925
926         if (need_layout) {
927             nc_layout_main_panel(d);
928             nc_layout_new_cred_window(d);
929         }
930     }
931
932     if(d->nc->n_identities == 1) {
933         wchar_t main_fmt[256];
934         wchar_t id_fmt[256];
935         wchar_t id_name[KCDB_IDENT_MAXCCH_NAME];
936         wchar_t id_string[KCDB_IDENT_MAXCCH_NAME + 256];
937         khm_size cbbuf;
938         khm_int32 flags;
939
940         LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_ONE, 
941                    main_fmt, (int) ARRAYLENGTH(main_fmt));
942
943         cbbuf = sizeof(id_name);
944         kcdb_identity_get_name(d->nc->identities[0], id_name, &cbbuf);
945
946         kcdb_identity_get_flags(d->nc->identities[0], &flags);
947
948         if (flags & KCDB_IDENT_FLAG_INVALID) {
949             LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_INVALID, 
950                        id_fmt, (int) ARRAYLENGTH(id_fmt));
951         } else if(flags & KCDB_IDENT_FLAG_VALID) {
952             LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_VALID, 
953                        id_fmt, (int) ARRAYLENGTH(id_fmt));
954         } else if(flags & KCDB_IDENT_FLAG_UNKNOWN) {
955             LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_UNCHECKED,
956                        id_fmt, (int) ARRAYLENGTH(id_fmt));
957         } else if(d->nc->subtype == KMSG_CRED_NEW_CREDS) {
958             LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_CHECKING, 
959                        id_fmt, (int) ARRAYLENGTH(id_fmt));
960         } else {
961             LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_UNCHECKED, 
962                        id_fmt, (int) ARRAYLENGTH(id_fmt));
963         }
964
965         StringCbPrintf(id_string, sizeof(id_string), id_fmt, id_name);
966
967         StringCbPrintf(buf, NC_MAXCB_CREDTEXT - cch*sizeof(wchar_t), 
968                        main_fmt, id_string);
969
970         if (flags & KCDB_IDENT_FLAG_VALID) {
971             if (flags & KCDB_IDENT_FLAG_DEFAULT)
972                 LoadString(khm_hInstance, IDS_NC_ID_DEF,
973                            id_string, ARRAYLENGTH(id_string));
974             else if (d->nc->set_default)
975                 LoadString(khm_hInstance, IDS_NC_ID_WDEF,
976                            id_string, ARRAYLENGTH(id_string));
977             else
978                 LoadString(khm_hInstance, IDS_NC_ID_NDEF,
979                            id_string, ARRAYLENGTH(id_string));
980
981             StringCbCat(buf, NC_MAXCB_CREDTEXT - cch * sizeof(wchar_t),
982                         id_string);
983         }
984
985     } else if(d->nc->n_identities > 1) {
986         wchar_t *ids_string;
987         khm_size cb_ids_string;
988
989         wchar_t id_name[KCDB_IDENT_MAXCCH_NAME];
990         wchar_t id_fmt[256];
991         wchar_t id_string[KCDB_IDENT_MAXCCH_NAME + 256];
992
993         wchar_t main_fmt[256];
994         khm_size cbbuf;
995
996         LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_MANY, 
997                    main_fmt, (int) ARRAYLENGTH(main_fmt));
998
999         /* we are going to concatenate all the identity names into
1000            a comma separated string */
1001
1002         /* d->nc->n_identities is at least 2 */
1003         ids_string = PMALLOC((KCDB_IDENT_MAXCB_NAME + sizeof(id_fmt)) * 
1004                             (d->nc->n_identities - 1));
1005         cb_ids_string = 
1006             (KCDB_IDENT_MAXCB_NAME + sizeof(id_fmt)) * 
1007             (d->nc->n_identities - 1);
1008
1009         assert(ids_string != NULL);
1010
1011         ids_string[0] = 0;
1012
1013         {
1014             khm_size i;
1015             khm_int32 flags;
1016
1017             for(i=1; i<d->nc->n_identities; i++) {
1018                 if(i>1) {
1019                     StringCbCat(ids_string, cb_ids_string, L",");
1020                 }
1021
1022                 flags = 0;
1023
1024                 cbbuf = sizeof(id_name);
1025                 kcdb_identity_get_name(d->nc->identities[i], id_name, &cbbuf);
1026                 kcdb_identity_get_flags(d->nc->identities[i], &flags);
1027                 if(flags & KCDB_IDENT_FLAG_INVALID) {
1028                     LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_INVALID, 
1029                                id_fmt, (int) ARRAYLENGTH(id_fmt));
1030                 } else if(flags & KCDB_IDENT_FLAG_VALID) {
1031                     LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_VALID, 
1032                                id_fmt, (int) ARRAYLENGTH(id_fmt));
1033                 } else {
1034                     LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_UNCHECKED, 
1035                                id_fmt, (int) ARRAYLENGTH(id_fmt));
1036                 }
1037
1038                 StringCbPrintf(id_string, sizeof(id_string), id_fmt, id_name);
1039                 StringCbCat(ids_string, cb_ids_string, id_string);
1040             }
1041
1042             cbbuf = sizeof(id_name);
1043             kcdb_identity_get_name(d->nc->identities[0], id_name, &cbbuf);
1044             kcdb_identity_get_flags(d->nc->identities[0], &flags);
1045             if(flags & KCDB_IDENT_FLAG_INVALID) {
1046                 LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_INVALID, 
1047                            id_fmt, (int) ARRAYLENGTH(id_fmt));
1048             } else if(flags & KCDB_IDENT_FLAG_VALID) {
1049                 LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_VALID, 
1050                            id_fmt, (int) ARRAYLENGTH(id_fmt));
1051             } else {
1052                 LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_UNCHECKED, 
1053                            id_fmt, (int) ARRAYLENGTH(id_fmt));
1054             }
1055             StringCbPrintf(id_string, sizeof(id_string), id_fmt, id_name);
1056
1057             StringCbPrintf(buf, NC_MAXCB_CREDTEXT - cch*sizeof(wchar_t), 
1058                            main_fmt, id_string, ids_string);
1059
1060             PFREE(ids_string);
1061         }
1062     } else {
1063         LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_NONE, 
1064                    buf, (int)(NC_MAXCCH_CREDTEXT - cch));
1065     }
1066
1067     /* now, append the credtext string from each of the cred types */
1068     {
1069         khm_size i;
1070         size_t cb;
1071         wchar_t * buf;
1072
1073         cb = NC_MAXCB_CREDTEXT;
1074         buf = ctbuf;
1075
1076         for(i=0; i<d->nc->n_types; i++) {
1077             if(d->nc->types[i]->credtext != NULL) {
1078                 StringCbCatEx(buf, cb, 
1079                               d->nc->types[i]->credtext,
1080                               &buf, &cb,
1081                               0);
1082             }
1083         }
1084     }
1085
1086     SetDlgItemText(d->dlg_main, IDC_NC_CREDTEXT, ctbuf);
1087
1088     PFREE(ctbuf);
1089
1090     /* so depending on whether the primary identity was found to be
1091        invalid, we need to disable the Ok button and set the title to
1092        reflect this */
1093
1094     if(d->nc->n_identities > 0) {
1095         khm_int32 flags = 0;
1096
1097         if(KHM_SUCCEEDED(kcdb_identity_get_flags(d->nc->identities[0], 
1098                                                &flags)) &&
1099            (flags & KCDB_IDENT_FLAG_VALID)) {
1100             validId = TRUE;
1101         }
1102     }
1103
1104     if (d->nc->window_title == NULL) {
1105         if(validId) {
1106             wchar_t wpostfix[256];
1107             wchar_t wtitle[KCDB_IDENT_MAXCCH_NAME + 256];
1108             khm_size cbsize;
1109
1110             cbsize = sizeof(wtitle);
1111             kcdb_identity_get_name(d->nc->identities[0], wtitle, &cbsize);
1112
1113             if (d->nc->subtype == KMSG_CRED_PASSWORD)
1114                 LoadString(khm_hInstance, IDS_WTPOST_PASSWORD,
1115                            wpostfix, (int) ARRAYLENGTH(wpostfix));
1116             else
1117                 LoadString(khm_hInstance, IDS_WTPOST_NEW_CREDS, 
1118                            wpostfix, (int) ARRAYLENGTH(wpostfix));
1119
1120             StringCbCat(wtitle, sizeof(wtitle), wpostfix);
1121
1122             SetWindowText(d->nc->hwnd, wtitle);
1123         } else {
1124             wchar_t wtitle[256];
1125
1126             if (d->nc->subtype == KMSG_CRED_PASSWORD)
1127                 LoadString(khm_hInstance, IDS_WT_PASSWORD,
1128                            wtitle, (int) ARRAYLENGTH(wtitle));
1129             else
1130                 LoadString(khm_hInstance, IDS_WT_NEW_CREDS, 
1131                            wtitle, (int) ARRAYLENGTH(wtitle));
1132
1133             SetWindowText(d->nc->hwnd, wtitle);
1134         }
1135     }
1136
1137     if (!(d->nc->response & KHUI_NC_RESPONSE_PROCESSING)) {
1138         if(validId ||
1139            d->nc->subtype == KMSG_CRED_PASSWORD) {
1140             /* TODO: check if all the required fields have valid values
1141                before enabling the Ok button */
1142             okEnable = TRUE;
1143         }
1144
1145         hw = GetDlgItem(d->dlg_main, IDOK);
1146         EnableWindow(hw, okEnable);
1147         hw = GetDlgItem(d->dlg_bb, IDOK);
1148         EnableWindow(hw, okEnable);
1149     }
1150 }
1151
1152 static void
1153 nc_layout_new_cred_window(khui_nc_wnd_data * ncd) {
1154     khui_new_creds * c;
1155     RECT r_main;
1156     RECT r_ncdialog;
1157     HDWP hdefer;
1158
1159     c = ncd->nc;
1160
1161     r_main.left = 0;
1162     r_main.top = 0;
1163     r_main.right = NCDLG_WIDTH;
1164     r_main.bottom = NCDLG_HEIGHT;
1165
1166     MapDialogRect(ncd->dlg_main, &r_main);
1167
1168     hdefer = BeginDeferWindowPos(5);
1169
1170     if (c->mode == KHUI_NC_MODE_MINI) {
1171
1172         if (IsWindowVisible(ncd->tab_wnd)) {
1173             DeferWindowPos(hdefer,
1174                            ncd->tab_wnd, NULL,
1175                            0, 0, 0, 0,
1176                            SWP_HIDEWINDOW |
1177                            SWP_NOMOVE | SWP_NOOWNERZORDER |
1178                            SWP_NOSIZE | SWP_NOZORDER);
1179         }
1180
1181         if (IsWindowVisible(ncd->dlg_bb)) {
1182             DeferWindowPos(hdefer,
1183                            ncd->dlg_bb, NULL,
1184                            0, 0, 0, 0,
1185                            SWP_HIDEWINDOW |
1186                            SWP_NOMOVE | SWP_NOOWNERZORDER |
1187                            SWP_NOSIZE | SWP_NOZORDER);
1188         }
1189
1190         DeferWindowPos(hdefer, ncd->dlg_main, NULL,
1191                        r_main.left, r_main.top,
1192                        r_main.right - r_main.left,
1193                        r_main.bottom - r_main.top,
1194                        SWP_NOACTIVATE | SWP_NOOWNERZORDER |
1195                        SWP_NOZORDER | SWP_SHOWWINDOW);
1196
1197         /* note that the ncd->r_main.bottom may not be the same as
1198            r_main.bottom because ncd->r_main.bottom is set dynamically
1199            depending on custom controls. ncd->r_main is valid only
1200            once nc_layout_main_panel() is called.*/
1201         CopyRect(&ncd->r_required, &ncd->r_main);
1202
1203     } else {
1204         RECT r_tabctrl;
1205         RECT r_displayarea;
1206         RECT r_bbar;
1207         khm_size i;
1208
1209         /* calculate the size of the tab control so that it fits
1210            snugly around the expanded main panel. */
1211         CopyRect(&r_tabctrl, &r_main);
1212         TabCtrl_AdjustRect(ncd->tab_wnd, TRUE, &r_tabctrl);
1213
1214         if (r_tabctrl.left < 0 ||
1215             r_tabctrl.top < 0) {
1216
1217             OffsetRect(&r_tabctrl,
1218                        (r_tabctrl.left < 0)? -r_tabctrl.left : 0,
1219                        (r_tabctrl.top < 0)? -r_tabctrl.top : 0);
1220
1221         }
1222
1223 #ifdef DEBUG
1224         assert(r_tabctrl.left == 0);
1225         assert(r_tabctrl.top == 0);
1226 #endif
1227
1228         OffsetRect(&r_tabctrl, 0, ncd->r_area.top);
1229
1230         /* and now calculate the rectangle where the main panel should
1231            be inside the tab control. */
1232         CopyRect(&r_displayarea, &r_tabctrl);
1233         TabCtrl_AdjustRect(ncd->tab_wnd, FALSE, &r_displayarea);
1234
1235         DeferWindowPos(hdefer,
1236                        ncd->tab_wnd, HWND_BOTTOM,
1237                        r_tabctrl.left, r_tabctrl.top,
1238                        r_tabctrl.right - r_tabctrl.left,
1239                        r_tabctrl.bottom - r_tabctrl.top,
1240                        SWP_NOACTIVATE | SWP_NOOWNERZORDER |
1241                        SWP_SHOWWINDOW);
1242
1243         /* we have to place the button bar just to the right of the
1244            tab panel. */
1245         r_bbar.left = 0;
1246         r_bbar.top = 0;
1247         r_bbar.right = NCDLG_BBAR_WIDTH;
1248         r_bbar.bottom = NCDLG_BBAR_HEIGHT;
1249
1250         MapDialogRect(ncd->dlg_main, &r_bbar);
1251
1252         OffsetRect(&r_bbar, r_tabctrl.right, 0);
1253
1254         DeferWindowPos(hdefer,
1255                        ncd->dlg_bb, NULL,
1256                        r_bbar.left, r_bbar.top,
1257                        r_bbar.right - r_bbar.left,
1258                        r_bbar.bottom - r_bbar.top,
1259                        SWP_NOACTIVATE | SWP_NOOWNERZORDER |
1260                        SWP_NOZORDER | SWP_SHOWWINDOW);
1261
1262         /* move the main panel inside the tab control... */
1263         DeferWindowPos(hdefer,
1264                        ncd->dlg_main, NULL,
1265                        r_displayarea.left, r_displayarea.top,
1266                        r_displayarea.right - r_displayarea.left,
1267                        r_displayarea.bottom - r_displayarea.top,
1268                        SWP_NOACTIVATE | SWP_NOOWNERZORDER |
1269                        SWP_NOZORDER |
1270                        (ncd->current_panel == 0 ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
1271
1272         /* and also move all the credential type panels (if they have
1273            been created) inside the tab control too. */
1274         khui_cw_lock_nc(c);
1275
1276         for (i=0; i < c->n_types; i++) {
1277             if (c->types[i]->hwnd_panel != NULL) {
1278                 DeferWindowPos(hdefer,
1279                                c->types[i]->hwnd_panel, NULL,
1280                                r_displayarea.left, r_displayarea.top,
1281                                r_displayarea.right - r_displayarea.left,
1282                                r_displayarea.bottom - r_displayarea.top,
1283                                SWP_NOACTIVATE | SWP_NOOWNERZORDER |
1284                                SWP_NOZORDER |
1285                                (ncd->current_panel == c->types[i]->ordinal ?
1286                                 SWP_SHOWWINDOW : SWP_HIDEWINDOW));
1287             }
1288         }
1289
1290         khui_cw_unlock_nc(c);
1291
1292         /* then update the required size of the new credentials
1293            dialog. */
1294         ncd->r_required.left = 0;
1295         ncd->r_required.top = 0;
1296         ncd->r_required.right = r_bbar.right;
1297         ncd->r_required.bottom = max(r_tabctrl.bottom, r_bbar.bottom) + ncd->r_area.top;
1298     }
1299
1300     /* commit all the window moves, resizes and hides/shows we did*/
1301     EndDeferWindowPos(hdefer);
1302
1303     /* now we have to see if the client area of the new credentials
1304        window is the right size. */
1305
1306     GetClientRect(c->hwnd, &r_ncdialog);
1307
1308     if (
1309
1310         ((r_ncdialog.right - r_ncdialog.left !=
1311           ncd->r_required.right - ncd->r_required.left)
1312
1313          ||
1314
1315          (r_ncdialog.bottom - r_ncdialog.top !=
1316           ncd->r_required.bottom - ncd->r_required.top))
1317
1318         &&
1319
1320         /* we don't bother if the new creds window is already in the
1321            process of changing the size. */
1322         !ncd->size_changing) {
1323
1324         /* if not, notify the window that the size needs adjusting. */
1325         if (IsWindowVisible(c->hwnd))
1326             PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY,
1327                         MAKEWPARAM(0, WMNC_UPDATE_LAYOUT), 0);
1328         else
1329             SendMessage(c->hwnd, KHUI_WM_NC_NOTIFY,
1330                         MAKEWPARAM(0, WMNC_UPDATE_LAYOUT), 0);
1331     }
1332 }
1333
1334 #define CW_PARAM DWLP_USER
1335
1336 static LRESULT 
1337 nc_handle_wm_create(HWND hwnd,
1338                     UINT uMsg,
1339                     WPARAM wParam,
1340                     LPARAM lParam)
1341 {
1342     LPCREATESTRUCT lpc;
1343     khui_new_creds * c;
1344     khui_nc_wnd_data * ncd;
1345     int x, y;
1346     int width, height;
1347     RECT r;
1348     HFONT hf_main;
1349
1350     lpc = (LPCREATESTRUCT) lParam;
1351
1352     ncd = PMALLOC(sizeof(*ncd));
1353     ZeroMemory(ncd, sizeof(*ncd));
1354
1355     c = (khui_new_creds *) lpc->lpCreateParams;
1356     ncd->nc = c;
1357     c->hwnd = hwnd;
1358
1359 #ifdef DEBUG
1360     assert(c->subtype == KMSG_CRED_NEW_CREDS ||
1361            c->subtype == KMSG_CRED_PASSWORD);
1362 #endif
1363
1364 #pragma warning(push)
1365 #pragma warning(disable: 4244)
1366     SetWindowLongPtr(hwnd, CW_PARAM, (LONG_PTR) ncd);
1367 #pragma warning(pop)
1368
1369     /* first, create the tab control that will house the main dialog
1370        panel as well as the plug-in specific panels */
1371     ncd->tab_wnd = CreateWindowEx(0, /* extended style */
1372                                   WC_TABCONTROL,
1373                                   L"TabControloxxrz", /* window name */
1374                                   TCS_HOTTRACK | TCS_RAGGEDRIGHT |
1375                                   TCS_SINGLELINE | TCS_TABS |
1376                                   WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS,
1377                                   0, 0, 100, 100, /* x,y,width height.
1378                                                      We'll be changing
1379                                                      these later
1380                                                      anyway. */
1381                                   hwnd,
1382                                   (HMENU) IDC_NC_TABS,
1383                                   NULL,
1384                                   0);
1385
1386 #ifdef DEBUG
1387     assert(ncd->tab_wnd != NULL);
1388 #endif
1389
1390     /* try to create the main dialog panel */
1391
1392     ncd->dlg_main = CreateDialogParam(khm_hInstance,
1393                                       MAKEINTRESOURCE(IDD_NC_NEWCRED),
1394                                       hwnd,
1395                                       nc_common_dlg_proc,
1396                                       (LPARAM) ncd);
1397 #ifdef DEBUG
1398     assert(ncd->dlg_main != NULL);
1399 #endif
1400
1401     hf_main = (HFONT) SendMessage(ncd->dlg_main, WM_GETFONT, 0, 0);
1402     if (hf_main)
1403         SendMessage(ncd->tab_wnd, WM_SETFONT, (WPARAM) hf_main, FALSE);
1404
1405     {
1406         RECT r_main;
1407         RECT r_area;
1408         RECT r_row;
1409         HWND hw;
1410             
1411         /* During the operation of the new credentials window, we will
1412            need to dynamically change the layout of the controls as a
1413            result of custom prompting from credentials providers and
1414            identity selectors from identity providers.  In order to
1415            guide the dynamic layout, we pick out a few metrics from
1416            the dialog template for the main panel. The metrics come
1417            from hidden STATIC controls in the dialog template. */
1418
1419         GetWindowRect(ncd->dlg_main, &r_main);
1420
1421         /* IDC_NC_TPL_PANEL spans the full extent of the dialog that
1422            we can populate with custom controls. */
1423         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_PANEL);
1424 #ifdef DEBUG
1425         assert(hw);
1426 #endif
1427         GetWindowRect(hw, &r_area);
1428         OffsetRect(&r_area,-r_main.left, -r_main.top);
1429         CopyRect(&ncd->r_area, &r_area);
1430
1431         /* IDC_NC_TPL_ROW spans the extent of a row of normal sized
1432            custom controls.  A row of custom controls typicall consist
1433            of a text label and an input control. */
1434         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_ROW);
1435 #ifdef DEBUG
1436         assert(hw);
1437 #endif
1438         GetWindowRect(hw, &r);
1439         CopyRect(&r_row, &r);
1440         OffsetRect(&r,-r.left, -r.top);
1441         CopyRect(&ncd->r_row, &r);
1442
1443         /* IDC_NC_TPL_LABEL spans the extent that a normal sized
1444            label.  The control overlaps IDC_NC_TPL_ROW so we can get
1445            coordinates relative to the row extents. */
1446         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_LABEL);
1447 #ifdef DEBUG
1448         assert(hw);
1449 #endif
1450         GetWindowRect(hw, &r);
1451         OffsetRect(&r,-r_row.left, -r_row.top);
1452         CopyRect(&ncd->r_n_label, &r);
1453
1454         /* IDC_NC_TPL_INPUT spans the extent of a normal sized input
1455            control in a custom control row.  The control overlaps
1456            IDC_NC_TPL_ROW so we can get relative coordinates. */
1457         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_INPUT);
1458 #ifdef DEBUG
1459         assert(hw);
1460 #endif
1461         GetWindowRect(hw, &r);
1462         OffsetRect(&r, -r_row.left, -r_row.top);
1463         CopyRect(&ncd->r_n_input, &r);
1464
1465         /* IDC_NC_TPL_ROW_LG spans the extent of a row of large sized
1466            controls. */
1467         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_ROW_LG);
1468 #ifdef DEBUG
1469         assert(hw);
1470 #endif
1471         GetWindowRect(hw, &r_row);
1472
1473         /* IDC_NC_TPL_LABEL_LG is a large sized label.  The control
1474            overlaps IDC_NC_TPL_ROW_LG. */
1475         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_LABEL_LG);
1476 #ifdef DEBUG
1477         assert(hw);
1478 #endif
1479         GetWindowRect(hw, &r);
1480         OffsetRect(&r, -r_row.left, -r_row.top);
1481         CopyRect(&ncd->r_e_label, &r);
1482
1483         /* IDC_NC_TPL_INPUT_LG is a large sized input control.
1484            Overlaps IDC_NC_TPL_ROW_LG. */
1485         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_INPUT_LG);
1486 #ifdef DEBUG
1487         assert(hw);
1488 #endif
1489         GetWindowRect(hw, &r);
1490         OffsetRect(&r, -r_row.left, -r_row.top);
1491         CopyRect(&ncd->r_e_input, &r);
1492
1493         CopyRect(&ncd->r_credtext, &ncd->r_area);
1494         CopyRect(&ncd->r_idspec, &ncd->r_area);
1495
1496         ncd->r_idspec.bottom = ncd->r_idspec.top;
1497
1498         /* And finally the credential text window.  The only metric we
1499            take from here is the Y coordinate of the bottom of the
1500            control since the actual size and position of the
1501            credentials window will change depending on the custom
1502            controls being displayed. */
1503         hw = GetDlgItem(ncd->dlg_main, IDC_NC_CREDTEXT);
1504 #ifdef DEBUG
1505         assert(hw);
1506 #endif
1507         GetWindowRect(hw, &r);
1508         OffsetRect(&r, -r_main.left, -r_main.top);
1509         ncd->r_credtext.bottom = r.bottom;
1510     }
1511
1512     /* if the mode is 'mini'*/
1513     r.left = 0;
1514     r.top = 0;
1515
1516     if(c->mode == KHUI_NC_MODE_MINI) {
1517         r.right = NCDLG_WIDTH;
1518         r.bottom = NCDLG_HEIGHT;
1519     } else {
1520         r.right = NCDLG_WIDTH + NCDLG_BBAR_WIDTH;
1521         r.bottom = NCDLG_BBAR_HEIGHT;
1522     }
1523
1524     MapDialogRect(ncd->dlg_main, &r);
1525
1526     /* position the new credentials dialog */
1527     width = r.right - r.left;
1528     height = r.bottom - r.top;
1529
1530     /* adjust width and height to accomodate NC area */
1531     {
1532         RECT wr,cr;
1533
1534         GetWindowRect(hwnd, &wr);
1535         GetClientRect(hwnd, &cr);
1536
1537         /* the non-client and client areas have already been calculated
1538            at this point.  We just use the difference to adjust the width
1539            and height */
1540         width += (wr.right - wr.left) - (cr.right - cr.left);
1541         height += (wr.bottom - wr.top) - (cr.bottom - cr.top);
1542     }
1543
1544     /* if the parent window is visible, we center the new credentials
1545        dialog over the parent.  Otherwise, we center it on the primary
1546        display. */
1547
1548     if (IsWindowVisible(lpc->hwndParent)) {
1549         GetWindowRect(lpc->hwndParent, &r);
1550     } else {
1551         if(!SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID) &r, 0)) {
1552             /* failover to the window coordinates */
1553             GetWindowRect(lpc->hwndParent, &r);
1554         }
1555     }
1556     x = (r.right + r.left)/2 - width / 2;
1557     y = (r.top + r.bottom)/2 - height / 2;
1558
1559     MoveWindow(hwnd, x, y, width, height, FALSE);
1560
1561     ncd->dlg_bb = CreateDialogParam(khm_hInstance,
1562                                     MAKEINTRESOURCE(IDD_NC_BBAR),
1563                                     hwnd,
1564                                     nc_common_dlg_proc,
1565                                     (LPARAM) ncd);
1566
1567 #ifdef DEBUG
1568     assert(ncd->dlg_bb);
1569 #endif
1570
1571     /* Call the identity provider callback to set the identity
1572        selector controls.  These controls need to be there before we
1573        layout the main panel. */
1574     c->ident_cb(c, WMNC_IDENT_INIT, NULL, 0, 0, (LPARAM) ncd->dlg_main);
1575
1576     if (c->mode == KHUI_NC_MODE_EXPANDED) {
1577         SendMessage(ncd->dlg_main, KHUI_WM_NC_NOTIFY,
1578                     MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0);
1579     } else {
1580         /* we don't call nc_layout_main_panel() if the dialog is
1581            expanded because posting WMNC_DIALOG_EXPAND to the main
1582            panel results in it getting called anyway. */
1583         nc_layout_main_panel(ncd);
1584     }
1585
1586     nc_layout_new_cred_window(ncd);
1587
1588     /* add this to the dialog chain */
1589     khm_add_dialog(hwnd);
1590
1591     return TRUE;
1592 }
1593
1594 /* add a control row supplied by an identity provider */
1595 static void
1596 nc_add_control_row(khui_nc_wnd_data * d, 
1597                    HWND label,
1598                    HWND input,
1599                    khui_control_size size)
1600 {
1601     RECT r_row;
1602     RECT r_label;
1603     RECT r_input;
1604     HFONT hf;
1605     HDWP hdefer;
1606
1607     hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0);
1608     SendMessage(label, WM_SETFONT, (WPARAM) hf, FALSE);
1609     SendMessage(input, WM_SETFONT, (WPARAM) hf, FALSE);
1610
1611     CopyRect(&r_row, &d->r_row);
1612     OffsetRect(&r_row, d->r_idspec.left, d->r_idspec.bottom);
1613
1614     if (size == KHUI_CTRLSIZE_SMALL) {
1615         CopyRect(&r_label, &d->r_n_label);
1616         CopyRect(&r_input, &d->r_n_input);
1617         OffsetRect(&r_label, r_row.left, r_row.top);
1618         OffsetRect(&r_input, r_row.left, r_row.top);
1619     } else if (size == KHUI_CTRLSIZE_HALF) {
1620         CopyRect(&r_label, &d->r_e_label);
1621         CopyRect(&r_input, &d->r_e_input);
1622         OffsetRect(&r_label, r_row.left, r_row.top);
1623         OffsetRect(&r_input, r_row.left, r_row.top);
1624     } else if (size == KHUI_CTRLSIZE_FULL) {
1625         CopyRect(&r_label, &d->r_n_label);
1626         r_label.right = d->r_row.right;
1627         CopyRect(&r_input, &d->r_n_input);
1628         OffsetRect(&r_input, r_row.left, r_row.top);
1629         OffsetRect(&r_input, 0, r_input.bottom);
1630         r_row.bottom += r_input.bottom;
1631         OffsetRect(&r_label, r_row.left, r_row.top);
1632     } else {
1633         SetRectEmpty(&r_label);
1634         SetRectEmpty(&r_input);
1635 #ifdef DEBUG
1636         assert(FALSE);
1637 #endif
1638         return;
1639     }
1640
1641     hdefer = BeginDeferWindowPos(2);
1642
1643     if (label)
1644         DeferWindowPos(hdefer, label,
1645                        ((d->hwnd_last_idspec != NULL)?
1646                         d->hwnd_last_idspec:
1647                         HWND_TOP),
1648                        r_label.left, r_label.top,
1649                        r_label.right - r_label.left,
1650                        r_label.bottom - r_label.top,
1651                        SWP_NOACTIVATE | SWP_NOOWNERZORDER);
1652
1653     if (input)
1654         DeferWindowPos(hdefer, input,
1655                        (label ? label : ((d->hwnd_last_idspec != NULL)?
1656                                          d->hwnd_last_idspec:
1657                                          HWND_TOP)),
1658                        r_input.left, r_input.top,
1659                        r_input.right - r_input.left,
1660                        r_input.bottom - r_input.top,
1661                        SWP_NOACTIVATE | SWP_NOOWNERZORDER);
1662
1663     EndDeferWindowPos(hdefer);
1664
1665     d->hwnd_last_idspec = (input ? input : label);
1666
1667     d->r_idspec.bottom = r_row.bottom;
1668
1669     /* we don't update the layout of the main panel yet, since these
1670        control additions happen before the main panel is displayed.  A
1671        call to nc_layout_main_panel() will be made before the main
1672        panel is shown anyway. */
1673
1674 }
1675
1676
1677 static LRESULT 
1678 nc_handle_wm_destroy(HWND hwnd,
1679                      UINT uMsg,
1680                      WPARAM wParam,
1681                      LPARAM lParam)
1682 {
1683     khui_nc_wnd_data * d;
1684
1685     /* remove self from dialog chain */
1686     khm_del_dialog(hwnd);
1687
1688     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);
1689     if (d == NULL)
1690         return TRUE;
1691
1692     d->nc->ident_cb(d->nc, WMNC_IDENT_EXIT, NULL, 0, 0, 0);
1693
1694     if (d->hwnd_notif_label)
1695         DestroyWindow(d->hwnd_notif_label);
1696     if (d->hwnd_notif_aux)
1697         DestroyWindow(d->hwnd_notif_aux);
1698
1699     if(d->dlg_bb)
1700         DestroyWindow(d->dlg_bb);
1701     if(d->dlg_main)
1702         DestroyWindow(d->dlg_main);
1703
1704     d->dlg_bb = NULL;
1705     d->dlg_main = NULL;
1706
1707     PFREE(d);
1708     SetWindowLongPtr(hwnd, CW_PARAM, 0);
1709
1710     return TRUE;
1711 }
1712
1713 static LRESULT 
1714 nc_handle_wm_command(HWND hwnd,
1715                      UINT uMsg,
1716                      WPARAM wParam,
1717                      LPARAM lParam)
1718 {
1719     khui_nc_wnd_data * d;
1720
1721     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);
1722     if (d == NULL)
1723         return 0;
1724
1725     switch(HIWORD(wParam)) {
1726     case BN_CLICKED:
1727         switch(LOWORD(wParam)) {
1728
1729         case IDOK:
1730             d->nc->result = KHUI_NC_RESULT_PROCESS;
1731
1732             /* fallthrough */
1733
1734         case IDCANCEL:
1735             /* the default value for d->nc->result is set to
1736                KHUI_NC_RESULT_CANCEL */
1737             d->nc->response = KHUI_NC_RESPONSE_PROCESSING;
1738
1739             nc_enable_controls(d, FALSE);
1740
1741             nc_notify_types(d->nc, 
1742                             KHUI_WM_NC_NOTIFY, 
1743                             MAKEWPARAM(0,WMNC_DIALOG_PREPROCESS), 
1744                             (LPARAM) d->nc,
1745                             TRUE);
1746
1747             khui_cw_sync_prompt_values(d->nc);
1748
1749             khm_cred_dispatch_process_message(d->nc);
1750
1751             /* we won't know whether to abort or not until we get
1752                feedback from the plugins, even if the command was
1753                to cancel */
1754             {
1755                 HWND hw;
1756
1757                 hw = GetDlgItem(d->dlg_main, IDOK);
1758                 EnableWindow(hw, FALSE);
1759                 hw = GetDlgItem(d->dlg_main, IDCANCEL);
1760                 EnableWindow(hw, FALSE);
1761                 hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED);
1762                 EnableWindow(hw, FALSE);
1763                 hw = GetDlgItem(d->dlg_bb, IDOK);
1764                 EnableWindow(hw, FALSE);
1765                 hw = GetDlgItem(d->dlg_bb, IDCANCEL);
1766                 EnableWindow(hw, FALSE);
1767             }
1768             return FALSE;
1769
1770         case IDC_NC_HELP:
1771             khm_html_help(hwnd, NULL, HH_HELP_CONTEXT, IDH_ACTION_NEW_ID);
1772             return FALSE;
1773
1774         case IDC_NC_BASIC:
1775         case IDC_NC_ADVANCED: 
1776             /* the Options button in the main window was clicked.  we
1777                respond by expanding the dialog. */
1778             PostMessage(hwnd, KHUI_WM_NC_NOTIFY, 
1779                         MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0);
1780             return FALSE;
1781
1782         case IDC_NC_CREDTEXT: /* credtext link activated */
1783             {
1784                 khui_htwnd_link * l;
1785                 wchar_t sid[KHUI_MAXCCH_HTLINK_FIELD];
1786                 wchar_t sparam[KHUI_MAXCCH_HTLINK_FIELD];
1787                 wchar_t * colon;
1788
1789                 l = (khui_htwnd_link *) lParam;
1790
1791                 /* do we have a valid link? */
1792                 if(l->id == NULL || l->id_len >= ARRAYLENGTH(sid))
1793                     return TRUE; /* nope */
1794
1795                 StringCchCopyN(sid, ARRAYLENGTH(sid), l->id, l->id_len);
1796                 sid[l->id_len] = L'\0'; /* just make sure */
1797
1798                 if(l->param != NULL && 
1799                    l->param_len < ARRAYLENGTH(sparam) &&
1800                    l->param_len > 0) {
1801
1802                     StringCchCopyN(sparam, ARRAYLENGTH(sparam),
1803                                    l->param, l->param_len);
1804                     sparam[l->param_len] = L'\0';
1805
1806                 } else {
1807                     sparam[0] = L'\0';
1808                 }
1809
1810                 /* If the ID is of the form '<credtype>:<link_tag>'
1811                    and <credtype> is a valid name of a credentials
1812                    type that is participating in the credentials
1813                    acquisition process, then we forward the message to
1814                    the panel that is providing the UI for that cred
1815                    type.  We also switch to that panel first, unless
1816                    the link is of the form '<credtype>:!<link_tag>'. */
1817
1818                 colon = wcschr(sid, L':');
1819                 if (colon != NULL) {
1820                     khm_int32 credtype;
1821                     khui_new_creds_by_type * t;
1822
1823                     *colon = L'\0';
1824                     if (KHM_SUCCEEDED(kcdb_credtype_get_id(sid, &credtype)) &&
1825                         KHM_SUCCEEDED(khui_cw_find_type(d->nc, credtype, &t))){
1826                         *colon = L':';
1827
1828                         if (t->ordinal != d->current_panel &&
1829                             *(colon + 1) != L'!')
1830                             PostMessage(hwnd,
1831                                         KHUI_WM_NC_NOTIFY,
1832                                         MAKEWPARAM(t->ordinal,
1833                                                    WMNC_DIALOG_SWITCH_PANEL),
1834                                         0);
1835
1836                         return SendMessage(t->hwnd_panel,
1837                                            KHUI_WM_NC_NOTIFY,
1838                                            MAKEWPARAM(0, WMNC_CREDTEXT_LINK),
1839                                            lParam);
1840                     } else {
1841                         *colon = L':';
1842                     }
1843                 }
1844
1845                 /* if it was for us, then we need to process the message */
1846                 if(!_wcsicmp(sid, CTLINKID_SWITCH_PANEL)) {
1847                     khm_int32 credtype;
1848                     khui_new_creds_by_type * t;
1849
1850                     if (KHM_SUCCEEDED(kcdb_credtype_get_id(sparam, 
1851                                                            &credtype)) &&
1852                         KHM_SUCCEEDED(khui_cw_find_type(d->nc,
1853                                                         credtype, &t))) {
1854                         if (t->ordinal != d->current_panel)
1855                             PostMessage(hwnd,
1856                                         KHUI_WM_NC_NOTIFY,
1857                                         MAKEWPARAM(t->ordinal,
1858                                                    WMNC_DIALOG_SWITCH_PANEL),
1859                                         0);
1860                     }
1861                 } else if (!_wcsicmp(sid, L"NotDef")) {
1862                     d->nc->set_default = FALSE;
1863                     nc_update_credtext(d);
1864                 } else if (!_wcsicmp(sid, L"MakeDef")) {
1865                     d->nc->set_default = TRUE;
1866                     nc_update_credtext(d);
1867                 }
1868             }
1869             return FALSE;
1870
1871 #if 0
1872         case NC_BN_SET_DEF_ID:
1873             {
1874                 d->nc->set_default =
1875                     (IsDlgButtonChecked(d->dlg_main, NC_BN_SET_DEF_ID)
1876                      == BST_CHECKED);
1877             }
1878             return FALSE;
1879 #endif
1880         }
1881         break;
1882     }
1883
1884     return TRUE;
1885 }
1886
1887 static LRESULT nc_handle_wm_moving(HWND hwnd,
1888                                    UINT uMsg,
1889                                    WPARAM wParam,
1890                                    LPARAM lParam)
1891 {
1892     khui_nc_wnd_data * d;
1893
1894     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);
1895     if (d == NULL)
1896         return FALSE;
1897
1898     nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, 
1899                     MAKEWPARAM(0, WMNC_DIALOG_MOVE), (LPARAM) d->nc, TRUE);
1900
1901     return FALSE;
1902 }
1903
1904 static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
1905                                UINT uMsg,
1906                                WPARAM wParam,
1907                                LPARAM lParam)
1908 {
1909     khui_nc_wnd_data * d;
1910     int id;
1911
1912     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);
1913     if (d == NULL)
1914         return FALSE;
1915
1916     switch(HIWORD(wParam)) {
1917
1918     case WMNC_DIALOG_SWITCH_PANEL:
1919         id = LOWORD(wParam);
1920         if(id >= 0 && id <= (int) d->nc->n_types) {
1921             /* one of the tab buttons were pressed */
1922             if(d->current_panel == id) {
1923                 return TRUE; /* nothing to do */
1924             }
1925
1926             d->current_panel = id;
1927
1928             TabCtrl_SetCurSel(d->tab_wnd, id);
1929         }
1930
1931         if(d->nc->mode == KHUI_NC_MODE_EXPANDED) {
1932             nc_layout_new_cred_window(d);
1933             return TRUE;
1934         }
1935         /*else*/
1936         /* fallthrough */
1937
1938     case WMNC_DIALOG_EXPAND:
1939         /* we are switching from basic to advanced or vice versa */
1940
1941         if (d->nc->mode == KHUI_NC_MODE_EXPANDED) {
1942
1943             if (d->current_panel != 0) {
1944                 d->current_panel = 0;
1945                 TabCtrl_SetCurSel(d->tab_wnd, 0);
1946                 nc_layout_new_cred_window(d);
1947             }
1948
1949             d->nc->mode = KHUI_NC_MODE_MINI;
1950         } else {
1951             d->nc->mode = KHUI_NC_MODE_EXPANDED;
1952         }
1953
1954         /* if we are switching to the advanced mode, we clear any
1955            notifications because we now have a credential text area
1956            for that. */
1957         if (d->nc->mode == KHUI_NC_MODE_EXPANDED)
1958             nc_notify_clear(d);
1959
1960         nc_layout_main_panel(d);
1961
1962         nc_layout_new_cred_window(d);
1963
1964         break;
1965
1966     case WMNC_DIALOG_SETUP:
1967
1968         if(d->nc->n_types > 0) {
1969             khm_size i;
1970             for(i=0; i < d->nc->n_types;i++) {
1971
1972                 if (d->nc->types[i]->dlg_proc == NULL) {
1973                     d->nc->types[i]->hwnd_panel = NULL;
1974                 } else {
1975                     /* Create the dialog panel */
1976                     d->nc->types[i]->hwnd_panel = 
1977                         CreateDialogParam(d->nc->types[i]->h_module,
1978                                           d->nc->types[i]->dlg_template,
1979                                           d->nc->hwnd,
1980                                           d->nc->types[i]->dlg_proc,
1981                                           (LPARAM) d->nc);
1982
1983 #ifdef DEBUG
1984                     assert(d->nc->types[i]->hwnd_panel);
1985 #endif
1986                 }
1987             }
1988         }
1989
1990         break;
1991
1992     case WMNC_DIALOG_ACTIVATE:
1993         {
1994             wchar_t wname[KCDB_MAXCCH_NAME];
1995             TCITEM tabitem;
1996             khm_int32 t;
1997
1998             /* About to activate the window. We should add all the
1999                panels to the tab control.  */
2000
2001 #ifdef DEBUG
2002             assert(d->tab_wnd != NULL);
2003 #endif
2004
2005             ZeroMemory(&tabitem, sizeof(tabitem));
2006
2007             tabitem.mask = TCIF_PARAM | TCIF_TEXT;
2008
2009             LoadString(khm_hInstance, IDS_NC_IDENTITY, 
2010                        wname, ARRAYLENGTH(wname));
2011
2012             tabitem.pszText = wname;
2013             tabitem.lParam = 0; /* ordinal */
2014
2015             TabCtrl_InsertItem(d->tab_wnd, 0, &tabitem);
2016
2017             khui_cw_lock_nc(d->nc);
2018
2019             if(d->nc->n_types > 0) {
2020                 khm_size i;
2021
2022                 /* We should sort the tabs first.  See
2023                    nc_tab_sort_func() for sort criteria. */
2024                 qsort(d->nc->types, 
2025                       d->nc->n_types, 
2026                       sizeof(*(d->nc->types)), 
2027                       nc_tab_sort_func);
2028
2029                 for(i=0; i < d->nc->n_types;i++) {
2030
2031                     d->nc->types[i]->ordinal = i + 1;
2032
2033                     if(d->nc->types[i]->name)
2034                         tabitem.pszText = d->nc->types[i]->name;
2035                     else {
2036                         khm_size cbsize;
2037
2038                         cbsize = sizeof(wname);
2039
2040                         if(KHM_FAILED
2041                            (kcdb_credtype_describe
2042                             (d->nc->types[i]->type, 
2043                              wname, 
2044                              &cbsize, 
2045                              KCDB_TS_SHORT))) {
2046
2047 #ifdef DEBUG
2048                             assert(FALSE);
2049 #endif
2050                             wname[0] = L'\0';
2051
2052                         }
2053
2054                         tabitem.pszText = wname;
2055
2056                     }
2057
2058                     tabitem.lParam = d->nc->types[i]->ordinal;
2059
2060                     TabCtrl_InsertItem(d->tab_wnd, d->nc->types[i]->ordinal,
2061                                        &tabitem);
2062                 }
2063             }
2064
2065             khui_cw_unlock_nc(d->nc);
2066
2067             nc_update_credtext(d);
2068
2069             TabCtrl_SetCurSel(d->tab_wnd, 0); /* the first selected
2070                                                  tab is the main
2071                                                  panel. */
2072
2073             /* bring the window to the top, if necessary */
2074             if (KHM_SUCCEEDED(khc_read_int32(NULL,
2075                                              L"CredWindow\\Windows\\NewCred\\ForceToTop",
2076                                              &t)) &&
2077
2078                 t != 0) {
2079                 /* it used to be that the above condition also called
2080                    !khm_is_dialog_active() to find out whether there
2081                    was a dialog active.  If there was, we wouldn't try
2082                    to bring the new cred window to the foreground. But
2083                    that was not the behavior we want. */
2084
2085                 /* if the main window is not visible, then the SetWindowPos()
2086                    call is sufficient to bring the new creds window to the
2087                    top.  However, if the main window is visible but not
2088                    active, the main window needs to be activated before a
2089                    child window can be activated. */
2090                 khm_activate_main_window();
2091
2092                 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
2093                              (SWP_NOMOVE | SWP_NOSIZE));
2094             }
2095
2096             ShowWindow(hwnd, SW_SHOWNOACTIVATE);
2097
2098             /* we don't enable animations until a specific timeout
2099                elapses after showing the window.  We don't need to
2100                animate any size changes if the user has barely had a
2101                chance to notice the original size. This prevents the
2102                new cred window from appearing in an animated state. */
2103             SetTimer(hwnd, NC_TIMER_ENABLEANIMATE, ENABLEANIMATE_TIMEOUT, NULL);
2104
2105             SetFocus(hwnd);
2106
2107             if (d->nc->n_identities == 0)
2108                 break;
2109             /* else */
2110             /*   fallthrough */
2111         }
2112
2113     case WMNC_IDENTITY_CHANGE:
2114         {
2115             BOOL okEnable = FALSE;
2116
2117             nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY,
2118                             MAKEWPARAM(0, WMNC_IDENTITY_CHANGE), (LPARAM) d->nc,
2119                             TRUE);
2120
2121             if (d->nc->subtype == KMSG_CRED_NEW_CREDS &&
2122                 d->nc->n_identities > 0 &&
2123                 d->nc->identities[0]) {
2124                 khm_int32 f = 0;
2125
2126                 kcdb_identity_get_flags(d->nc->identities[0], &f);
2127
2128                 if (!(f & KCDB_IDENT_FLAG_DEFAULT)) {
2129                     d->nc->set_default = FALSE;
2130                 }
2131             }
2132
2133             nc_update_credtext(d);
2134
2135         }
2136         break;
2137
2138     case WMNC_TYPE_STATE:
2139         /* fallthrough */
2140     case WMNC_UPDATE_CREDTEXT:
2141         nc_update_credtext(d);
2142         break;
2143
2144     case WMNC_CLEAR_PROMPTS:
2145         {
2146             khm_size i;
2147
2148             khui_cw_lock_nc(d->nc);
2149
2150             if(d->hwnd_banner != NULL) {
2151                 DestroyWindow(d->hwnd_banner);
2152                 d->hwnd_banner = NULL;
2153             }
2154
2155             if(d->hwnd_name != NULL) {
2156                 DestroyWindow(d->hwnd_name);
2157                 d->hwnd_name = NULL;
2158             }
2159
2160             for(i=0;i<d->nc->n_prompts;i++) {
2161                 if(!(d->nc->prompts[i]->flags & 
2162                      KHUI_NCPROMPT_FLAG_STOCK)) {
2163                     if(d->nc->prompts[i]->hwnd_static != NULL)
2164                         DestroyWindow(d->nc->prompts[i]->hwnd_static);
2165
2166                     if(d->nc->prompts[i]->hwnd_edit != NULL)
2167                         DestroyWindow(d->nc->prompts[i]->hwnd_edit);
2168                 }
2169
2170                 d->nc->prompts[i]->hwnd_static = NULL;
2171                 d->nc->prompts[i]->hwnd_edit = NULL;
2172             }
2173
2174             khui_cw_unlock_nc(d->nc);
2175
2176             SetRectEmpty(&d->r_custprompt);
2177
2178             nc_layout_main_panel(d);
2179
2180             nc_layout_new_cred_window(d);
2181         }
2182         break;
2183
2184     case WMNC_SET_PROMPTS:
2185         {
2186             khm_size i;
2187             int  y;
2188             HWND hw, hw_prev;
2189             HFONT hf, hfold;
2190             HDC hdc;
2191             BOOL use_large_lables = FALSE;
2192
2193             /* we assume that WMNC_CLEAR_PROMPTS has already been
2194                received */
2195
2196 #ifdef DEBUG
2197             assert(IsRectEmpty(&d->r_custprompt));
2198 #endif
2199
2200             khui_cw_lock_nc(d->nc);
2201
2202 #if 0
2203             /* special case, we have one prompt and it is a password
2204                prompt.  very common */
2205             if(d->nc->n_prompts == 1 && 
2206                d->nc->prompts[0]->type == KHUI_NCPROMPT_TYPE_PASSWORD) {
2207
2208                 hw = GetDlgItem(d->dlg_main, IDC_NC_PASSWORD);
2209                 EnableWindow(hw, TRUE);
2210
2211                 d->nc->prompts[0]->flags |= KHUI_NCPROMPT_FLAG_STOCK;
2212                 d->nc->prompts[0]->hwnd_edit = hw;
2213                 d->nc->prompts[0]->hwnd_static = NULL; /* don't care */
2214
2215                 khui_cw_unlock_nc(d->nc);
2216                 break;
2217             }
2218 #endif
2219             /* for everything else */
2220
2221             y = d->r_idspec.bottom;
2222
2223             d->r_custprompt.left = d->r_area.left;
2224             d->r_custprompt.right = d->r_area.right;
2225             d->r_custprompt.top = y;
2226
2227             hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0);
2228
2229             if (d->nc->pname != NULL) {
2230                 hw =
2231                     CreateWindowEx
2232                     (0,
2233                      L"STATIC",
2234                      d->nc->pname,
2235                      SS_SUNKEN | WS_CHILD,
2236                      d->r_area.left, y,
2237                      d->r_row.right, 
2238                      d->r_n_label.bottom - d->r_n_label.top,
2239                      d->dlg_main,
2240                      NULL,
2241                      khm_hInstance,
2242                      NULL);
2243
2244 #ifdef DEBUG
2245                 assert(hw);
2246 #endif
2247                 d->hwnd_name = hw;
2248                 SendMessage(hw, WM_SETFONT, (WPARAM)hf, (LPARAM) TRUE);
2249                 ShowWindow(hw, SW_SHOW);
2250
2251                 y += d->r_n_label.bottom - d->r_n_label.top;
2252             }
2253
2254             if (d->nc->banner != NULL) {
2255                 hw = 
2256                     CreateWindowEx
2257                     (0,
2258                      L"STATIC",
2259                      d->nc->banner,
2260                      WS_CHILD,
2261                      d->r_area.left, y,
2262                      d->r_row.right, d->r_row.bottom,
2263                      d->dlg_main,
2264                      NULL,
2265                      khm_hInstance,
2266                      NULL);
2267 #ifdef DEBUG
2268                 assert(hw);
2269 #endif
2270                 d->hwnd_banner = hw;
2271                 SendMessage(hw, WM_SETFONT, (WPARAM)hf, (LPARAM)TRUE);
2272                 ShowWindow(hw, SW_SHOW);
2273                 y += d->r_row.bottom;
2274             }
2275
2276             hw_prev = d->hwnd_last_idspec;
2277
2278             hdc = GetWindowDC(d->dlg_main);
2279             hfold = SelectObject(hdc,hf);
2280
2281             /* first do a trial run and see if we should use the
2282                larger text labels or not.  This is so that all the
2283                labels and input controls align properly. */
2284             for (i=0; i < d->nc->n_prompts; i++) {
2285                 if (d->nc->prompts[i]->prompt != NULL) {
2286                     SIZE s;
2287
2288                     GetTextExtentPoint32(hdc, 
2289                                          d->nc->prompts[i]->prompt, 
2290                                          (int) wcslen(d->nc->prompts[i]->prompt),
2291                                          &s);
2292
2293                     if(s.cx >= d->r_n_label.right - d->r_n_label.left) {
2294                         use_large_lables = TRUE;
2295                         break;
2296                     }
2297                 }
2298             }
2299
2300             for(i=0; i<d->nc->n_prompts; i++) {
2301                 RECT pr, er;
2302                 SIZE s;
2303                 int dy;
2304
2305                 if(d->nc->prompts[i]->prompt != NULL) {
2306                     GetTextExtentPoint32(hdc, 
2307                                          d->nc->prompts[i]->prompt, 
2308                                          (int) wcslen(d->nc->prompts[i]->prompt),
2309                                          &s);
2310                     if(s.cx < d->r_n_label.right - d->r_n_label.left &&
2311                        !use_large_lables) {
2312                         CopyRect(&pr, &d->r_n_label);
2313                         CopyRect(&er, &d->r_n_input);
2314                         dy = d->r_row.bottom;
2315                     } else if(s.cx <
2316                               d->r_e_label.right - d->r_e_label.left) {
2317                         CopyRect(&pr, &d->r_e_label);
2318                         CopyRect(&er, &d->r_e_input);
2319                         dy = d->r_row.bottom;
2320                     } else {
2321                         /* oops. the prompt doesn't fit in our
2322                            controls.  we need to use up two lines */
2323                         pr.left = 0;
2324                         pr.right = d->r_row.right;
2325                         pr.top = 0;
2326                         pr.bottom = d->r_n_label.bottom - 
2327                             d->r_n_label.top;
2328                         CopyRect(&er, &d->r_n_input);
2329                         OffsetRect(&er, 0, pr.bottom);
2330                         dy = er.bottom + (d->r_row.bottom - 
2331                                           d->r_n_input.bottom);
2332                     }
2333                 } else {
2334                     SetRectEmpty(&pr);
2335                     CopyRect(&er, &d->r_n_input);
2336                     dy = d->r_row.bottom;
2337                 }
2338
2339                 if(IsRectEmpty(&pr)) {
2340                     d->nc->prompts[i]->hwnd_static = NULL;
2341                 } else {
2342                     OffsetRect(&pr, d->r_area.left, y);
2343
2344                     hw = CreateWindowEx
2345                         (0,
2346                          L"STATIC",
2347                          d->nc->prompts[i]->prompt,
2348                          WS_CHILD,
2349                          pr.left, pr.top,
2350                          pr.right - pr.left, pr.bottom - pr.top,
2351                          d->dlg_main,
2352                          NULL,
2353                          khm_hInstance,
2354                          NULL);
2355 #ifdef DEBUG
2356                     assert(hw);
2357 #endif
2358
2359                     SendMessage(hw, WM_SETFONT, 
2360                                 (WPARAM) hf, (LPARAM) TRUE);
2361
2362                     SetWindowPos(hw, hw_prev,
2363                                  0, 0, 0, 0,
2364                                  SWP_NOACTIVATE | SWP_NOMOVE |
2365                                  SWP_NOOWNERZORDER | SWP_NOSIZE |
2366                                  SWP_SHOWWINDOW);
2367
2368                     d->nc->prompts[i]->hwnd_static = hw;
2369                     hw_prev = hw;
2370                 }
2371
2372                 OffsetRect(&er, d->r_area.left, y);
2373
2374                 hw = CreateWindowEx
2375                     (0,
2376                      L"EDIT",
2377                      (d->nc->prompts[i]->def ? 
2378                       d->nc->prompts[i]->def : L""),
2379                      WS_CHILD | WS_TABSTOP |
2380                      WS_BORDER |
2381                      ((d->nc->prompts[i]->flags & 
2382                        KHUI_NCPROMPT_FLAG_HIDDEN)? ES_PASSWORD:0),
2383                      er.left, er.top,
2384                      er.right - er.left, er.bottom - er.top,
2385                      d->dlg_main,
2386                      NULL,
2387                      khm_hInstance,
2388                      NULL);
2389
2390 #ifdef DEBUG
2391                 assert(hw);
2392 #endif
2393
2394                 SendMessage(hw, WM_SETFONT, 
2395                             (WPARAM) hf, (LPARAM) TRUE);
2396
2397                 SetWindowPos(hw, hw_prev,
2398                              0, 0, 0, 0, 
2399                              SWP_NOACTIVATE | SWP_NOMOVE | 
2400                              SWP_NOOWNERZORDER | SWP_NOSIZE | 
2401                              SWP_SHOWWINDOW);
2402
2403                 SendMessage(hw, EM_SETLIMITTEXT,
2404                             KHUI_MAXCCH_PROMPT_VALUE -1,
2405                             0);
2406
2407                 d->nc->prompts[i]->hwnd_edit = hw;
2408
2409                 hw_prev = hw;
2410
2411                 y += dy;
2412             }
2413
2414             if (d->nc->n_prompts > 0 &&
2415                 d->nc->prompts[0]->hwnd_edit) {
2416
2417                 PostMessage(d->dlg_main, WM_NEXTDLGCTL,
2418                             (WPARAM) d->nc->prompts[0]->hwnd_edit,
2419                             MAKELPARAM(TRUE, 0));
2420
2421             }
2422
2423             SelectObject(hdc, hfold);
2424             ReleaseDC(d->dlg_main, hdc);
2425
2426             khui_cw_unlock_nc(d->nc);
2427
2428             d->r_custprompt.bottom = y;
2429
2430             if (d->r_custprompt.bottom == d->r_custprompt.top)
2431                 SetRectEmpty(&d->r_custprompt);
2432
2433             nc_layout_main_panel(d);
2434
2435             nc_layout_new_cred_window(d);
2436         }
2437         break;
2438
2439     case WMNC_DIALOG_PROCESS_COMPLETE:
2440         {
2441             khui_new_creds * nc;
2442
2443             nc = d->nc;
2444
2445             nc->response &= ~KHUI_NC_RESPONSE_PROCESSING;
2446
2447             if(nc->response & KHUI_NC_RESPONSE_NOEXIT) {
2448                 HWND hw;
2449
2450                 nc_enable_controls(d, TRUE);
2451
2452                 /* reset state */
2453                 nc->result = KHUI_NC_RESULT_CANCEL;
2454
2455                 hw = GetDlgItem(d->dlg_main, IDOK);
2456                 EnableWindow(hw, TRUE);
2457                 hw = GetDlgItem(d->dlg_main, IDCANCEL);
2458                 EnableWindow(hw, TRUE);
2459                 hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED);
2460                 EnableWindow(hw, TRUE);
2461                 hw = GetDlgItem(d->dlg_bb, IDOK);
2462                 EnableWindow(hw, TRUE);
2463                 hw = GetDlgItem(d->dlg_bb, IDCANCEL);
2464                 EnableWindow(hw, TRUE);
2465
2466                 nc_clear_password_fields(d);
2467
2468                 return TRUE;
2469             }
2470
2471             DestroyWindow(hwnd);
2472
2473             kmq_post_message(KMSG_CRED, KMSG_CRED_END, 0, (void *) nc);
2474         }
2475         break;
2476
2477         /* MUST be called with SendMessage */
2478     case WMNC_ADD_CONTROL_ROW:
2479         {
2480             khui_control_row * row;
2481
2482             row = (khui_control_row *) lParam;
2483
2484 #ifdef DEBUG
2485             assert(row->label);
2486             assert(row->input);
2487 #endif
2488
2489             nc_add_control_row(d, row->label, row->input, row->size);
2490         }
2491         break;
2492
2493     case WMNC_UPDATE_LAYOUT:
2494         {
2495
2496             RECT r_client;
2497             khm_int32 animate;
2498             khm_int32 steps;
2499             khm_int32 timeout;
2500
2501             /* We are already adjusting the size of the window.  The
2502                next time the timer fires, it will notice if the target
2503                size has changed. */
2504             if (d->size_changing)
2505                 return TRUE;
2506
2507             GetClientRect(hwnd, &r_client);
2508
2509             if ((r_client.right - r_client.left ==
2510                  d->r_required.right - d->r_required.left) &&
2511                 (r_client.bottom - r_client.top ==
2512                  d->r_required.bottom - d->r_required.top)) {
2513
2514                 /* the window is already at the right size */
2515                 return TRUE;
2516
2517             }
2518
2519             if (!IsWindowVisible(hwnd)) {
2520                 /* The window is not visible yet.  There's no need to
2521                    animate anything. */
2522
2523                 animate = FALSE;
2524
2525             } else if (KHM_FAILED(khc_read_int32(NULL,
2526                                                  L"CredWindow\\Windows\\NewCred\\AnimateSizeChanges",
2527                                                  &animate))) {
2528 #ifdef DEBUG
2529                 assert(FALSE);
2530 #endif
2531                 animate = TRUE;
2532             }
2533
2534             /* if we aren't animating the window resize, then we just
2535                do it in one call. */
2536             if (!animate || !d->animation_enabled) {
2537                 RECT r_window;
2538
2539                 CopyRect(&r_window, &d->r_required);
2540                 AdjustWindowRectEx(&r_window, NC_WINDOW_STYLES, FALSE,
2541                                    NC_WINDOW_EX_STYLES);
2542
2543                 SetWindowPos(hwnd, NULL, 0, 0,
2544                              r_window.right - r_window.left,
2545                              r_window.bottom - r_window.top,
2546                              SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |
2547                              SWP_NOZORDER);
2548
2549                 return TRUE;
2550             }
2551
2552             if (KHM_FAILED(khc_read_int32(NULL,
2553                                           L"CredWindow\\Windows\\NewCred\\AnimationSteps",
2554                                           &steps))) {
2555 #ifdef DEBUG
2556                 assert(FALSE);
2557 #endif
2558                 steps = NC_SZ_STEPS_DEF;
2559             } else {
2560
2561                 if (steps < NC_SZ_STEPS_MIN)
2562                     steps = NC_SZ_STEPS_MIN;
2563                 else if (steps > NC_SZ_STEPS_MAX)
2564                     steps = NC_SZ_STEPS_MAX;
2565
2566             }
2567
2568             if (KHM_FAILED(khc_read_int32(NULL,
2569                                           L"CredWindow\\Windows\\NewCred\\AnimationStepTimeout",
2570                                           &timeout))) {
2571 #ifdef DEBUG
2572                 assert(FALSE);
2573 #endif
2574                 timeout = NC_SZ_TIMEOUT_DEF;
2575             } else {
2576
2577                 if (timeout < NC_SZ_TIMEOUT_MIN)
2578                     timeout = NC_SZ_TIMEOUT_MIN;
2579                 else if (timeout > NC_SZ_TIMEOUT_MAX)
2580                     timeout = NC_SZ_TIMEOUT_MAX;
2581
2582             }
2583
2584             CopyRect(&d->sz_ch_source, &r_client);
2585             OffsetRect(&d->sz_ch_source, -d->sz_ch_source.left, -d->sz_ch_source.top);
2586             CopyRect(&d->sz_ch_target, &d->r_required);
2587             OffsetRect(&d->sz_ch_target, -d->sz_ch_target.left, -d->sz_ch_target.top);
2588             d->sz_ch_increment = 0;
2589             d->sz_ch_max = steps;
2590             d->sz_ch_timeout = timeout;
2591             d->size_changing = TRUE;
2592
2593             SetTimer(hwnd, NC_TIMER_SIZER, timeout, NULL);
2594         }
2595         break;
2596     } /* switch(HIWORD(wParam)) */
2597
2598     return TRUE;
2599 }
2600
2601 static LRESULT nc_handle_wm_timer(HWND hwnd,
2602                                   UINT uMsg,
2603                                   WPARAM wParam,
2604                                   LPARAM lParam) {
2605     khui_nc_wnd_data * d;
2606
2607     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);
2608     if (d == NULL)
2609         return FALSE;
2610
2611     if (wParam == NC_TIMER_SIZER) {
2612
2613         RECT r_now;
2614
2615         /* are we done with this sizing operation? */
2616         if (!d->size_changing ||
2617             d->sz_ch_increment >= d->sz_ch_max) {
2618
2619             d->size_changing = FALSE;
2620             KillTimer(hwnd, NC_TIMER_SIZER);
2621             return 0;
2622         }
2623
2624         /* have the requirements changed while we were processing the
2625            sizing operation? */
2626         if ((d->r_required.right - d->r_required.left !=
2627              d->sz_ch_target.right)
2628
2629             ||
2630
2631             (d->r_required.bottom - d->r_required.top !=
2632              d->sz_ch_target.bottom)) {
2633
2634             /* the target size has changed.  we need to restart the
2635                sizing operation. */
2636
2637             RECT r_client;
2638
2639             GetClientRect(hwnd, &r_client);
2640
2641             CopyRect(&d->sz_ch_source, &r_client);
2642             OffsetRect(&d->sz_ch_source, -d->sz_ch_source.left, -d->sz_ch_source.top);
2643             CopyRect(&d->sz_ch_target, &d->r_required);
2644             OffsetRect(&d->sz_ch_target, -d->sz_ch_target.left, -d->sz_ch_target.top);
2645             d->sz_ch_increment = 0;
2646
2647             /* leave the other fields alone */
2648
2649 #ifdef DEBUG
2650             assert(d->sz_ch_max >= NC_SZ_STEPS_MIN);
2651             assert(d->sz_ch_max <= NC_SZ_STEPS_MAX);
2652             assert(d->sz_ch_timeout >= NC_SZ_TIMEOUT_MIN);
2653             assert(d->sz_ch_timeout <= NC_SZ_TIMEOUT_MAX);
2654             assert(d->size_changing);
2655 #endif
2656         }
2657
2658         /* we are going to do the next increment */
2659         d->sz_ch_increment ++;
2660
2661         /* now, figure out the size of the client area for this
2662            step */
2663
2664         r_now.left = 0;
2665         r_now.top = 0;
2666
2667 #define PROPORTION(v1, v2, i, s) (((v1) * ((s) - (i)) + (v2) * (i)) / (s))
2668
2669         r_now.right = PROPORTION(d->sz_ch_source.right, d->sz_ch_target.right,
2670                                  d->sz_ch_increment, d->sz_ch_max);
2671
2672         r_now.bottom = PROPORTION(d->sz_ch_source.bottom, d->sz_ch_target.bottom,
2673                                   d->sz_ch_increment, d->sz_ch_max);
2674
2675 #undef  PROPORTION
2676
2677 #ifdef DEBUG
2678         {
2679             long dx = (r_now.right - d->sz_ch_target.right) *
2680                 (d->sz_ch_source.right - d->sz_ch_target.right);
2681
2682             long dy = (r_now.bottom - d->sz_ch_target.bottom) *
2683                 (d->sz_ch_source.bottom - d->sz_ch_target.bottom);
2684
2685             if (dx < 0 || dy < 0) {
2686                 KillTimer(hwnd, NC_TIMER_SIZER);
2687                 assert(dx >= 0);
2688                 assert(dy >= 0);
2689                 SetTimer(hwnd, NC_TIMER_SIZER, d->sz_ch_timeout, NULL);
2690             }
2691         }
2692 #endif
2693
2694         AdjustWindowRectEx(&r_now, NC_WINDOW_STYLES, FALSE,
2695                            NC_WINDOW_EX_STYLES);
2696
2697         SetWindowPos(hwnd, NULL,
2698                      0, 0,
2699                      r_now.right - r_now.left,
2700                      r_now.bottom - r_now.top,
2701                      SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |
2702                      SWP_NOZORDER);
2703
2704         /* and now we wait for the next timer message */
2705
2706         return 0;
2707     } else if (wParam == NC_TIMER_ENABLEANIMATE) {
2708
2709         d->animation_enabled = TRUE;
2710         KillTimer(hwnd, NC_TIMER_ENABLEANIMATE);
2711     }
2712
2713     return 0;
2714 }
2715
2716 static LRESULT nc_handle_wm_notify(HWND hwnd,
2717                                    UINT uMsg,
2718                                    WPARAM wParam,
2719                                    LPARAM lParam) {
2720
2721     LPNMHDR nmhdr;
2722     khui_nc_wnd_data * d;
2723
2724     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);
2725     if (d == NULL)
2726         return FALSE;
2727
2728     nmhdr = (LPNMHDR) lParam;
2729
2730     if (nmhdr->code == TCN_SELCHANGE) {
2731         /* the current tab has changed. */
2732         int idx;
2733         TCITEM tcitem;
2734
2735         idx = TabCtrl_GetCurSel(d->tab_wnd);
2736         ZeroMemory(&tcitem, sizeof(tcitem));
2737
2738         tcitem.mask = TCIF_PARAM;
2739         TabCtrl_GetItem(d->tab_wnd, idx, &tcitem);
2740
2741         d->current_panel = (int) tcitem.lParam;
2742
2743         nc_layout_new_cred_window(d);
2744
2745         return TRUE;
2746     }
2747
2748     return FALSE;
2749 }
2750
2751 static LRESULT nc_handle_wm_help(HWND hwnd,
2752                                  UINT uMsg,
2753                                  WPARAM wParam,
2754                                  LPARAM lParam) {
2755     static DWORD ctxids[] = {
2756         NC_TS_CTRL_ID_MIN, IDH_NC_TABMAIN,
2757         NC_TS_CTRL_ID_MIN + 1, IDH_NC_TABBUTTON,
2758         NC_TS_CTRL_ID_MIN + 2, IDH_NC_TABBUTTON,
2759         NC_TS_CTRL_ID_MIN + 3, IDH_NC_TABBUTTON,
2760         NC_TS_CTRL_ID_MIN + 4, IDH_NC_TABBUTTON,
2761         NC_TS_CTRL_ID_MIN + 5, IDH_NC_TABBUTTON,
2762         NC_TS_CTRL_ID_MIN + 6, IDH_NC_TABBUTTON,
2763         NC_TS_CTRL_ID_MIN + 7, IDH_NC_TABBUTTON,
2764         IDOK, IDH_NC_OK,
2765         IDCANCEL, IDH_NC_CANCEL,
2766         IDC_NC_HELP, IDH_NC_HELP,
2767         IDC_NC_ADVANCED, IDH_NC_ADVANCED,
2768         IDC_NC_CREDTEXT, IDH_NC_CREDWND,
2769         0
2770     };
2771
2772     HELPINFO * hlp;
2773     HWND hw = NULL;
2774     HWND hw_ctrl;
2775     khui_nc_wnd_data * d;
2776
2777     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);
2778     if (d == NULL)
2779         return FALSE;
2780
2781     hlp = (HELPINFO *) lParam;
2782
2783     if (d->nc->subtype != KMSG_CRED_NEW_CREDS &&
2784         d->nc->subtype != KMSG_CRED_PASSWORD)
2785         return TRUE;
2786
2787     if (hlp->iContextType != HELPINFO_WINDOW)
2788         return TRUE;
2789
2790     if (hlp->hItemHandle != NULL &&
2791         hlp->hItemHandle != hwnd) {
2792         DWORD id;
2793         int i;
2794
2795         hw_ctrl =hlp->hItemHandle;
2796
2797         id = GetWindowLong(hw_ctrl, GWL_ID);
2798         for (i=0; ctxids[i] != 0; i += 2)
2799             if (ctxids[i] == id)
2800                 break;
2801
2802         if (ctxids[i] != 0)
2803             hw = khm_html_help(hw_ctrl,
2804                                ((d->nc->subtype == KMSG_CRED_NEW_CREDS)?
2805                                 L"::popups_newcreds.txt":
2806                                 L"::popups_password.txt"),
2807                                HH_TP_HELP_WM_HELP,
2808                                (DWORD_PTR) ctxids);
2809     }
2810
2811     if (hw == NULL) {
2812         khm_html_help(hwnd, NULL, HH_HELP_CONTEXT,
2813                       ((d->nc->subtype == KMSG_CRED_NEW_CREDS)?
2814                        IDH_ACTION_NEW_ID: IDH_ACTION_PASSWD_ID));
2815     }
2816
2817     return TRUE;
2818 }
2819
2820 static LRESULT CALLBACK nc_window_proc(HWND hwnd,
2821                                        UINT uMsg,
2822                                        WPARAM wParam,
2823                                        LPARAM lParam)
2824 {
2825     switch(uMsg) {
2826     case WM_CREATE:
2827         return nc_handle_wm_create(hwnd, uMsg, wParam, lParam);
2828
2829     case WM_DESTROY:
2830         return nc_handle_wm_destroy(hwnd, uMsg, wParam, lParam);
2831
2832     case WM_COMMAND:
2833         return nc_handle_wm_command(hwnd, uMsg, wParam, lParam);
2834
2835     case WM_NOTIFY:
2836         return nc_handle_wm_notify(hwnd, uMsg, wParam, lParam);
2837
2838     case WM_MOVE:
2839     case WM_MOVING:
2840         return nc_handle_wm_moving(hwnd, uMsg, wParam, lParam);
2841
2842     case WM_TIMER:
2843         return nc_handle_wm_timer(hwnd, uMsg, wParam, lParam);
2844
2845     case WM_HELP:
2846         return nc_handle_wm_help(hwnd, uMsg, wParam, lParam);
2847
2848     case KHUI_WM_NC_NOTIFY:
2849         return nc_handle_wm_nc_notify(hwnd, uMsg, wParam, lParam);
2850     }
2851
2852     /* Note that this is technically a dialog box */
2853     return DefDlgProc(hwnd, uMsg, wParam, lParam);
2854 }
2855
2856 void khm_register_newcredwnd_class(void)
2857 {
2858     WNDCLASSEX wcx;
2859
2860     wcx.cbSize = sizeof(wcx);
2861     wcx.style = CS_DBLCLKS | CS_OWNDC;
2862     wcx.lpfnWndProc = nc_window_proc;
2863     wcx.cbClsExtra = 0;
2864     wcx.cbWndExtra = DLGWINDOWEXTRA + sizeof(LONG_PTR);
2865     wcx.hInstance = khm_hInstance;
2866     wcx.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP));
2867     wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
2868     wcx.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
2869     wcx.lpszMenuName = NULL;
2870     wcx.lpszClassName = KHUI_NEWCREDWND_CLASS;
2871     wcx.hIconSm = NULL;
2872
2873     khui_newcredwnd_cls = RegisterClassEx(&wcx);
2874 }
2875
2876 void khm_unregister_newcredwnd_class(void)
2877 {
2878     UnregisterClass((LPWSTR) khui_newcredwnd_cls, khm_hInstance);
2879 }
2880
2881 HWND khm_create_newcredwnd(HWND parent, khui_new_creds * c)
2882 {
2883     wchar_t wtitle[256];
2884     HWND hwnd;
2885
2886     if (c->window_title == NULL) {
2887         if (c->subtype == KMSG_CRED_PASSWORD)
2888             LoadString(khm_hInstance, 
2889                        IDS_WT_PASSWORD,
2890                        wtitle,
2891                        ARRAYLENGTH(wtitle));
2892         else
2893             LoadString(khm_hInstance, 
2894                        IDS_WT_NEW_CREDS,
2895                        wtitle,
2896                        ARRAYLENGTH(wtitle));
2897     }
2898
2899     hwnd = CreateWindowEx(NC_WINDOW_EX_STYLES,
2900                           MAKEINTATOM(khui_newcredwnd_cls),
2901                           ((c->window_title)?c->window_title: wtitle),
2902                           NC_WINDOW_STYLES,
2903                           0,0,400,400,    /* bogus values.  the window
2904                                              is going to resize and
2905                                              reposition itself
2906                                              anyway */
2907                           parent,
2908                           NULL,
2909                           khm_hInstance,
2910                           (LPVOID) c);
2911
2912 #ifdef DEBUG
2913     assert(hwnd != NULL);
2914 #endif
2915
2916     /* note that the window is not visible yet.  That's because, at
2917        this point we don't know what the panels are */
2918
2919     return hwnd;
2920 }
2921
2922 void khm_prep_newcredwnd(HWND hwnd)
2923 {
2924     SendMessage(hwnd, KHUI_WM_NC_NOTIFY, 
2925                 MAKEWPARAM(0, WMNC_DIALOG_SETUP), 0);
2926 }
2927
2928 void khm_show_newcredwnd(HWND hwnd)
2929 {
2930     /* add all the panels in and prep UI */
2931     PostMessage(hwnd, KHUI_WM_NC_NOTIFY, 
2932                 MAKEWPARAM(0, WMNC_DIALOG_ACTIVATE), 0);
2933 }