NIM Improved Alert Management
authorJeffrey Altman <jaltman@secure-endpoints.com>
Wed, 28 Feb 2007 07:01:21 +0000 (07:01 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Wed, 28 Feb 2007 07:01:21 +0000 (07:01 +0000)
This patch implements the new Alert Management functionality.

Many improvements to avoid race conditions and improve resource
  tracking.

ticket: new
component: windows

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@19189 dc483132-0cff-0310-8789-dd5450dbe970

62 files changed:
src/windows/identity/Makefile
src/windows/identity/apiversion.txt
src/windows/identity/config/Makefile.w2k
src/windows/identity/config/Makefile.w32
src/windows/identity/doc/Makefile
src/windows/identity/help/khhelp.h
src/windows/identity/help/popups_newcreds.txt
src/windows/identity/help/popups_password.txt
src/windows/identity/include/kherror.h
src/windows/identity/include/khlist.h
src/windows/identity/kconfig/api.c
src/windows/identity/kcreddb/credset.c
src/windows/identity/kcreddb/identity.c
src/windows/identity/kcreddb/kcreddb.h
src/windows/identity/kmm/kmm_registrar.c
src/windows/identity/kmm/kmminternal.h
src/windows/identity/kmq/consumer.c
src/windows/identity/kmq/init.c
src/windows/identity/kmq/kmq.h
src/windows/identity/kmq/kmqinternal.h
src/windows/identity/kmq/msgtype.c
src/windows/identity/kmq/publisher.c
src/windows/identity/nidmgrdll/Makefile
src/windows/identity/plugins/krb4/krb4newcreds.c
src/windows/identity/plugins/krb4/lang/en_us/langres.rc
src/windows/identity/plugins/krb5/krb5identpro.c
src/windows/identity/plugins/krb5/krb5newcreds.c
src/windows/identity/ui/addrchange.c
src/windows/identity/ui/appglobal.h
src/windows/identity/ui/cfg_identities_wnd.c
src/windows/identity/ui/configwnd.c
src/windows/identity/ui/credfuncs.c
src/windows/identity/ui/credwnd.c
src/windows/identity/ui/khmapp.h
src/windows/identity/ui/lang/en_us/khapp.rc
src/windows/identity/ui/main.c
src/windows/identity/ui/mainmenu.c
src/windows/identity/ui/mainwnd.c
src/windows/identity/ui/mainwnd.h
src/windows/identity/ui/newcredwnd.c
src/windows/identity/ui/newcredwnd.h
src/windows/identity/ui/notifier.c
src/windows/identity/ui/propertywnd.c
src/windows/identity/ui/reqdaemon.c
src/windows/identity/ui/resource.h
src/windows/identity/ui/uiconfig.csv
src/windows/identity/uilib/Makefile
src/windows/identity/uilib/action.c
src/windows/identity/uilib/actiondef.cfg
src/windows/identity/uilib/alert.c
src/windows/identity/uilib/creddlg.c
src/windows/identity/uilib/intaction.h
src/windows/identity/uilib/khaction.h
src/windows/identity/uilib/khalerts.h
src/windows/identity/uilib/khnewcred.h
src/windows/identity/uilib/khuidefs.h
src/windows/identity/uilib/rescache.c
src/windows/identity/uilib/uibind.c
src/windows/identity/util/hashtable.c
src/windows/identity/util/hashtable.h
src/windows/identity/util/perfstat.c
src/windows/identity/util/perfstat.h

index e8fb501905eed6959c0ddbecff82a77fa50616e9..375bdce15322d05d67ed619423e2d788a8a31a73 100644 (file)
@@ -160,7 +160,7 @@ krb5plugin: plugincommon
        $(ECHO) -- Done with $@\r
 \r
 !ifndef NO_KRB4\r
-doc: krb4plugin\r
+finale: krb4plugin\r
 \r
 krb4plugin: plugincommon\r
        $(ECHO) -- Entering $@\r
index 9681ccc1c84c884f1c4ee19d754d42d19cb15b38..4ff092b8d5d8a54f8c7a09e92e5e3bc25381f1a4 100644 (file)
@@ -220,8 +220,129 @@ Date=(TBD)
 #----------------------------------------------------------------\r
 Version=7\r
 AppVersion=1.1.9.0\r
-Date=(TBD)\r
-# Released with KFW 3.2.0\r
+Date=Feb 16, 2007\r
+# Released with KFW 3.2 Alpha 1\r
 \r
 +KHUI_ACTION_UICB\r
-# Internal action to dispatch a UI callback
\ No newline at end of file
+# Internal action to dispatch a UI callback\r
+\r
++WMNC_UPDATE_LAYOUT\r
+# Used to update the layout and size of the dialogs during a new\r
+# credentials operation.\r
+\r
+- NCDLG_TAB_HEIGHT, NCDLG_TAB_WIDTH\r
+# No longer used\r
+\r
+- NCDLG_BBAR_WIDTH\r
+# Moved to internal header file\r
+\r
++ KCDB_IDENT_FLAG_UNKNOWN\r
+# Used to indicate that an authority could not be contacted to\r
+# determine the validity of an identity.\r
+\r
++ khui_refresh_actions()\r
+# Force a refresh of the application menus and toolbars.\r
+\r
++ khui_action_lock()\r
++ khui_action_unlock()\r
+# Synchronization of action and menu operations.\r
+\r
+! khui_alert\r
+# Structure definition is now internal\r
+\r
+! khui_action\r
+# Structure definition is now internal\r
+\r
++ khui_alert_set_type()\r
++ khui_alert_set_ctx()\r
++ khui_alert_get_response()\r
+# Additional functions to setup an alert.\r
+\r
+! kmq_message\r
+# Added field "aborted"\r
+\r
++ kmq_abort_call()\r
++ kmq_is_call_aborted()\r
+# Added placeholders\r
+\r
+! kmq_message_ref\r
+! kmq_queue\r
+! kmq_msg_subscription\r
+! kmq_msg_type\r
+# Structure definition now internal\r
+\r
+! KMQ_MSG_TYPE_MAX\r
+! KMQ_MAXCCH_TYPE_NAME\r
+! KMQ_MAXCB_TYPE_NAME\r
+! KMQ_MSG_SUB_MAGIC\r
+! KMQ_RCPTTYPE_CB\r
+! KMQ_RCPTTYPE_HWND\r
+! KMQ_QUEUE_FLAG_DELETED\r
+# Macros not internal\r
+\r
+#----------------------------------------------------------------\r
+Version=8\r
+AppVersion=1.1.10.0\r
+Date=(TBD)\r
+# Released with (TBD)\r
+\r
+! hash_add(), hash_del(), hash_lookup(), hash_exist()\r
+# 'key' parameter is now (const void *)\r
+\r
+! struct tag_has_bin, hash_bin\r
+# 'key' member is now (const void *)\r
+\r
+\r
++WMNC_UPDATE_LAYOUT\r
+# Used to update the layout and size of the dialogs during a new\r
+# credentials operation.\r
+\r
+- NCDLG_TAB_HEIGHT, NCDLG_TAB_WIDTH\r
+# No longer used\r
+\r
+- NCDLG_BBAR_WIDTH\r
+# Moved to internal header file\r
+\r
++ KCDB_IDENT_FLAG_UNKNOWN\r
+# Used to indicate that an authority could not be contacted to\r
+# determine the validity of an identity.\r
+\r
++ khui_refresh_actions()\r
+# Force a refresh of the application menus and toolbars.\r
+\r
++ khui_action_lock()\r
++ khui_action_unlock()\r
+# Synchronization of action and menu operations.\r
+\r
+! khui_alert\r
+# Structure definition is now internal\r
+\r
+! khui_action\r
+# Structure definition is now internal\r
+\r
++ khui_alert_set_type()\r
++ khui_alert_set_ctx()\r
++ khui_alert_get_response()\r
+# Additional functions to setup an alert.\r
+\r
+! kmq_message\r
+# Added field "aborted"\r
+\r
++ kmq_abort_call()\r
++ kmq_is_call_aborted()\r
+# Added placeholders\r
+\r
+! kmq_message_ref\r
+! kmq_queue\r
+! kmq_msg_subscription\r
+! kmq_msg_type\r
+# Structure definition now internal\r
+\r
+! KMQ_MSG_TYPE_MAX\r
+! KMQ_MAXCCH_TYPE_NAME\r
+! KMQ_MAXCB_TYPE_NAME\r
+! KMQ_MSG_SUB_MAGIC\r
+! KMQ_RCPTTYPE_CB\r
+! KMQ_RCPTTYPE_HWND\r
+! KMQ_QUEUE_FLAG_DELETED\r
+# Macros not internal\r
index f2977786c52772af1930bb2722867b9b92bdde0d..ffd1fc87221d6e1bff256cf49c38d58d9d3d75ca 100644 (file)
@@ -47,7 +47,7 @@ KHIMAIRA_WIN32_CONFIG=1
 # Version info\r
 NETIDMGR_VERSION_MAJOR=1\r
 NETIDMGR_VERSION_MINOR=1\r
-NETIDMGR_VERSION_PATCH=9\r
+NETIDMGR_VERSION_PATCH=10\r
 NETIDMGR_VERSION_AUX=0\r
 NETIDMGR_RELEASEDESC=\r
 \r
@@ -58,7 +58,7 @@ NETIDMGR_RELEASEDESC=
 #\r
 # Changes to the API version numbers should be documented in\r
 # apiversion.txt at the root of the source tree.\r
-NETIDMGR_VERSION_API=7\r
+NETIDMGR_VERSION_API=8\r
 \r
 # Minimum backwards compatible version.  API versions from\r
 # NETIDMGR_VERSION_API_MINCOMPAT through NETIDMGR_VERSION_API\r
@@ -208,6 +208,10 @@ khcwarn=/Wp64
 khcwarn=$(khcwarn) /WX\r
 !endif\r
 \r
+!if "$(CPU)" == "i386"\r
+khdefines=$(khdefines) -D_USE_32BIT_TIME_T\r
+!endif\r
+\r
 #DEBUG_SYMBOLS\r
 ldebug=$(ldebug) /DEBUG\r
 cdebug=$(cdebug) -Os -Zi\r
index e327a707555e5c1aa368000ef5d40ba4ce9166e5..d2281b6aed1f9faf103a82c31b864ea1061bc54a 100644 (file)
@@ -47,7 +47,7 @@ KHIMAIRA_WIN32_CONFIG=1
 # Version info\r
 NETIDMGR_VERSION_MAJOR=1\r
 NETIDMGR_VERSION_MINOR=1\r
-NETIDMGR_VERSION_PATCH=9\r
+NETIDMGR_VERSION_PATCH=10\r
 NETIDMGR_VERSION_AUX=0\r
 NETIDMGR_RELEASEDESC=\r
 \r
@@ -58,7 +58,7 @@ NETIDMGR_RELEASEDESC=
 #\r
 # Changes to the API version numbers should be documented in\r
 # apiversion.txt at the root of the source tree.\r
-NETIDMGR_VERSION_API=7\r
+NETIDMGR_VERSION_API=8\r
 \r
 # Minimum backwards compatible version.  API versions from\r
 # NETIDMGR_VERSION_API_MINCOMPAT through NETIDMGR_VERSION_API\r
@@ -221,6 +221,10 @@ khcwarn=/Wp64
 khcwarn=$(khcwarn) /WX\r
 !endif\r
 \r
+!if "$(CPU)" == "i386"\r
+khdefines=$(khdefines) -D_USE_32BIT_TIME_T\r
+!endif\r
+\r
 #DEBUG_SYMBOLS\r
 ldebug=$(ldebug) /DEBUG\r
 cdebug=$(cdebug) -Os -Zi\r
index 85003999e223093fa9088d566c3870ac7940ecb2..321d02fa18fdf73a0a5249eecd11213fdc29f1f8 100644 (file)
@@ -64,5 +64,5 @@ CHM_FILE = "$(DOCDIR)\netiddev.chm"
        -$(HHC) $(DOCDIR)\html\index.hhp\r
 \r
 clean::\r
-       $(RMDIR) /s /q $(DOCDIR)\html\r
-       $(RM) $(DOCDIR)\*.*\r
+       if exist "$(DOCDIR)/html" $(RMDIR) /s /q "$(DOCDIR)\html"\r
+       $(RM) "$(DOCDIR)\*.*"\r
index 88f73c0b3e70a046628f4a6b34f0181c4e41a21c..fbfef6047ba6b68c7ca845001a63a6cf1bc8598d 100644 (file)
@@ -28,6 +28,6 @@
 #define IDH_NC_CANCEL              3002\r
 #define IDH_NC_HELP                3003\r
 #define IDH_NC_TABBUTTON           3004\r
-#define IDH_NC_OPTIONS             3005\r
+#define IDH_NC_ADVANCED            3005\r
 #define IDH_NC_TABMAIN             3006\r
 #define IDH_NC_SETDEF              3007\r
index 52c61244bf536e64108c6a3e4577e56e54588017..74a39360e0ba532166a72811f092edd06ced7a82 100644 (file)
@@ -13,7 +13,7 @@ Cancels the new credentials operation.
 .topic IDH_NC_HELP\r
 Provides help for this dialog box.\r
 \r
-.topic IDH_NC_OPTIONS\r
+.topic IDH_NC_ADVANCED\r
 Expands the dialog and allows you to set additional\r
 options for the credentials that will be obtained\r
 by this dialog.\r
index 39583252b5bcd9b7d2b6653766fe9d024ed3ed76..e92b30e918e179a09ae1588db8ef58569e9c59c2 100644 (file)
@@ -10,7 +10,7 @@ Change the password for the selected identity.
 .topic IDH_NC_CANCEL\r
 Cancel the change password operation.\r
 \r
-.topic IDH_NC_OPTIONS\r
+.topic IDH_NC_ADVANCED\r
 Expand the dialog and make the option pages visible for the credential\r
 types for which you will be changing the password for.\r
 \r
index f11e285109b68004e6904569a092ce17f606f889..ae381ff56c56ffc2fa05be81629795a9322543ad 100644 (file)
 /*! \brief An incompatibility was found */\r
 #define KHM_ERROR_INCOMPATIBLE      (KHM_ERROR_BASE + 21)\r
 \r
+/*! \brief The operation was put on hold\r
+\r
+    A request was put on hold or postponed. */\r
+#define KHM_ERROR_HELD              (KHM_ERROR_BASE + 22)\r
+\r
 /*@}*/ /*kherror_codes*/\r
 \r
 /*! \brief Tests whether a return value indicates success */\r
index 2eb85864a68696c5dd1b087c21d40c89f5f880b5..44e0ffb68eade376eb2fda56c7200cc13c745ae4 100644 (file)
     if(!(pq)->head) (pq)->head = (pe);          \\r
     } while(0)\r
 \r
+#define QPUSH(pq, pe)                           \\r
+    do {                                        \\r
+    (pe)->next = NULL;                          \\r
+    (pe)->prev = (pq)->head;                    \\r
+    if((pq)->head) (pq)->head->next = (pe);     \\r
+    if(!(pq)->tail) (pq)->tail = (pe);          \\r
+    (pq)->head = (pe);                          \\r
+    } while (0)\r
+\r
 #define QGET(pq, ppe)                                           \\r
     do {                                                        \\r
     *(ppe) = (pq)->head;                                        \\r
index b3b4eb69166d206e226bb280b4af4f5d9c5aa626..d944a4ce76b98b5c737d40ee80e58fa6241700e5 100644 (file)
@@ -26,6 +26,7 @@
 \r
 #include<shlwapi.h>\r
 #include<kconfiginternal.h>\r
+#include<netidmgr_intver.h>\r
 #include<assert.h>\r
 \r
 kconf_conf_space * conf_root = NULL;\r
@@ -86,6 +87,111 @@ void exit_kconf(void) {
     }\r
 }\r
 \r
+#if defined(DEBUG) && (defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL))\r
+\r
+#include<stdio.h>\r
+\r
+static void\r
+khcint_dump_space(FILE * f, kconf_conf_space * sp) {\r
+\r
+    kconf_conf_space * sc;\r
+\r
+    fprintf(f, "c12\t[%S]\t[%S]\t%d\t0x%x\tWin(%s|%s)|%s\n",\r
+            ((sp->regpath) ? sp->regpath : L"!No Reg path"),\r
+            sp->name,\r
+            (int) sp->refcount,\r
+            (int) sp->flags,\r
+            ((sp->regkey_user)? "HKCU" : ""),\r
+            ((sp->regkey_machine)? "HKLM" : ""),\r
+            ((sp->schema)? "Schema" : ""));\r
+\r
+\r
+    sc = TFIRSTCHILD(sp);\r
+    while(sc) {\r
+\r
+        khcint_dump_space(f, sc);\r
+\r
+        sc = LNEXT(sc);\r
+    }\r
+}\r
+\r
+KHMEXP void KHMAPI\r
+khcint_dump_handles(FILE * f) {\r
+    if (khc_is_config_running()) {\r
+        kconf_handle * h, * sh;\r
+\r
+        EnterCriticalSection(&cs_conf_handle);\r
+        EnterCriticalSection(&cs_conf_global);\r
+\r
+        fprintf(f, "c00\t*** Active handles ***\n");\r
+        fprintf(f, "c01\tHandle\tName\tFlags\tRegpath\n");\r
+\r
+        h = conf_handles;\r
+        while(h) {\r
+            kconf_conf_space * sp;\r
+\r
+            sp = h->space;\r
+\r
+            if (!khc_is_handle(h) || sp == NULL) {\r
+\r
+                fprintf(f, "c02\t!!INVALID HANDLE!!\n");\r
+\r
+            } else {\r
+\r
+                fprintf(f, "c02\t0x%p\t[%S]\t0x%x\t[%S]\n",\r
+                        h,\r
+                        sp->name,\r
+                        h->flags,\r
+                        sp->regpath);\r
+\r
+                sh = khc_shadow(h);\r
+\r
+                while(sh) {\r
+\r
+                    sp = sh->space;\r
+\r
+                    if (!khc_is_handle(sh) || sp == NULL) {\r
+\r
+                        fprintf(f, "c02\t0x%p:Shadow:0x%p\t[!!INVALID HANDLE!!]\n",\r
+                                h, sh);\r
+\r
+                    } else {\r
+\r
+                        fprintf(f, "c02\t0x%p:Shadow:0x%p,[%S]\t0x%x\t[%S]\n",\r
+                                h, sh,\r
+                                sp->name,\r
+                                sh->flags,\r
+                                sp->regpath);\r
+\r
+                    }\r
+\r
+                    sh = khc_shadow(sh);\r
+                }\r
+\r
+            }\r
+\r
+            h = LNEXT(h);\r
+        }\r
+\r
+        fprintf(f, "c03\t------  End ---------\n");\r
+\r
+        fprintf(f, "c10\t*** Active Configuration Spaces ***\n");\r
+        fprintf(f, "c11\tReg path\tName\tRefcount\tFlags\tLayers\n");\r
+\r
+        khcint_dump_space(f, conf_root);\r
+\r
+        fprintf(f, "c13\t------  End ---------\n");\r
+\r
+        LeaveCriticalSection(&cs_conf_global);\r
+        LeaveCriticalSection(&cs_conf_handle);\r
+\r
+    } else {\r
+        fprintf(f, "c00\t------- KHC Configuration not running -------\n");\r
+    }\r
+}\r
+\r
+#endif\r
+\r
 /* obtains cs_conf_handle/cs_conf_global */\r
 kconf_handle * \r
 khcint_handle_from_space(kconf_conf_space * s, khm_int32 flags)\r
@@ -180,6 +286,29 @@ khcint_space_hold(kconf_conf_space * s) {
     LeaveCriticalSection(&cs_conf_global);\r
 }\r
 \r
+/* called with cs_conf_global */\r
+void\r
+khcint_try_free_space(kconf_conf_space * s) {\r
+\r
+    if (TFIRSTCHILD(s) == NULL &&\r
+        s->refcount == 0 &&\r
+        s->schema == NULL) {\r
+\r
+        kconf_conf_space * p;\r
+\r
+        p = TPARENT(s);\r
+\r
+        if (p == NULL)\r
+            return;\r
+\r
+        TDELCHILD(p, s);\r
+\r
+        khcint_free_space(s);\r
+\r
+        khcint_try_free_space(p);\r
+    }\r
+}\r
+\r
 /* obtains cs_conf_global */\r
 void \r
 khcint_space_release(kconf_conf_space * s) {\r
@@ -200,6 +329,15 @@ khcint_space_release(kconf_conf_space * s) {
             (KCONF_SPACE_FLAG_DELETE_M |\r
              KCONF_SPACE_FLAG_DELETE_U)) {\r
             khcint_remove_space(s, s->flags);\r
+        } else {\r
+#ifdef USE_TRY_FREE\r
+            /* even if the refcount is zero, we shouldn't free a\r
+               configuration space just yet since that doesn't play\r
+               well with the configuration space enumeration mechanism\r
+               which expects the spaces to dangle around if there is a\r
+               corresponding registry key or schema. */\r
+            khcint_try_free_space(s);\r
+#endif\r
         }\r
     }\r
 \r
@@ -673,10 +811,10 @@ khcint_free_space(kconf_conf_space * r) {
     if(!r)\r
         return;\r
 \r
-    LPOP(&r->children, &c);\r
+    TPOPCHILD(r, &c);\r
     while(c) {\r
         khcint_free_space(c);\r
-        LPOP(&r->children, &c);\r
+        TPOPCHILD(r, &c);\r
     }\r
 \r
     if(r->name)\r
@@ -847,8 +985,8 @@ khc_open_space(khm_handle parent, const wchar_t * cspace, khm_int32 flags,
         flags |= KCONF_FLAG_USER | KCONF_FLAG_MACHINE | KCONF_FLAG_SCHEMA;\r
 \r
     if(cspace == NULL) {\r
-        khcint_space_release(p);\r
         *result = (khm_handle) khcint_handle_from_space(p, flags);\r
+        khcint_space_release(p);\r
         return KHM_ERROR_SUCCESS;\r
     }\r
 \r
@@ -897,6 +1035,9 @@ khc_open_space(khm_handle parent, const wchar_t * cspace, khm_int32 flags,
     } else\r
         *result = NULL;\r
 \r
+    if (c)\r
+        khcint_space_release(c);\r
+\r
     return rv;\r
 }\r
 \r
@@ -2318,7 +2459,7 @@ khc_unload_schema(khm_handle conf, const kconf_schema * schema)
         return KHM_ERROR_INVALID_PARAM;\r
 \r
     EnterCriticalSection(&cs_conf_global);\r
-    rv = khcint_unload_schema_i(conf, schema, 0, NULL);        \r
+    rv = khcint_unload_schema_i(conf, schema, 0, NULL);\r
     LeaveCriticalSection(&cs_conf_global);\r
 \r
     return rv;\r
index dda9817d3af14e80d870dc49ff0f979ba72caf62..141e17cd22a8719b3c9002a1e6984efcb84d7ba7 100644 (file)
@@ -1047,7 +1047,8 @@ kcdb_credset_unseal(khm_handle credset) {
 }\r
 \r
 \r
-/* wrapper for qsort and also parameter gobbling FSM. */\r
+/* wrapper for qsort and also parameter gobbling FSM.  Access to this\r
+   function is serialized via cs_credset. */\r
 int __cdecl \r
 kcdb_creds_comp_wrapper(const void * a, const void * b)\r
 {\r
@@ -1091,12 +1092,16 @@ kcdb_credset_sort(khm_handle credset,
     assert(!(cs->flags & KCDB_CREDSET_FLAG_ENUM));\r
 #endif\r
 \r
+    EnterCriticalSection(&cs_credset);\r
+\r
     kcdb_creds_comp_wrapper(rock, NULL);\r
     kcdb_creds_comp_wrapper(NULL, (void *) comp);\r
 \r
     qsort(cs->clist, cs->nclist,\r
          sizeof(kcdb_credset_credref), kcdb_creds_comp_wrapper);\r
 \r
+    LeaveCriticalSection(&cs_credset);\r
+\r
     LeaveCriticalSection(&(cs->cs));\r
     return code;\r
 }\r
index 3f6020682ac8124e4f131d10ba82bdfa6e1a8554..326d0258a35ca79650999672f065a47fa700d970 100644 (file)
@@ -55,7 +55,7 @@ kcdb_identity_set_provider(khm_handle sub)
     EnterCriticalSection(&cs_ident);\r
     if (sub != kcdb_ident_sub) {\r
         if(kcdb_ident_sub != NULL) {\r
-            kmq_post_sub_msg(kcdb_ident_sub,\r
+            kmq_send_sub_msg(kcdb_ident_sub,\r
                              KMSG_IDENT,\r
                              KMSG_IDENT_EXIT,\r
                              0,\r
@@ -425,10 +425,12 @@ kcdb_identity_set_flags(khm_handle vid,
 \r
     id->flags = (id->flags & ~mask) | (flag & mask);\r
 \r
-    if (flag & KCDB_IDENT_FLAG_VALID)\r
-        id->flags &= ~KCDB_IDENT_FLAG_INVALID;\r
-    if (flag & KCDB_IDENT_FLAG_INVALID)\r
-        id->flags &= ~KCDB_IDENT_FLAG_VALID;\r
+    if (flag & KCDB_IDENT_FLAG_VALID) {\r
+        id->flags &= ~(KCDB_IDENT_FLAG_INVALID | KCDB_IDENT_FLAG_UNKNOWN);\r
+    }\r
+    if (flag & KCDB_IDENT_FLAG_INVALID) {\r
+        id->flags &= ~(KCDB_IDENT_FLAG_VALID | KCDB_IDENT_FLAG_UNKNOWN);\r
+    }\r
 \r
     newflags = id->flags;\r
 \r
@@ -466,7 +468,9 @@ kcdb_identity_get_flags(khm_handle vid,
 \r
     id = (kcdb_identity *) vid;\r
 \r
+    EnterCriticalSection(&cs_ident);\r
     *flags = id->flags;\r
+    LeaveCriticalSection(&cs_ident);\r
 \r
     return KHM_ERROR_SUCCESS;\r
 }\r
index 060f556acec8d68ab0b7270bddaa9075d2ff495e..4b15a245f61eb9a727b3ac796db33992114abcc6 100644 (file)
@@ -232,11 +232,21 @@ Functions, macros etc. for manipulating identities.
  */\r
 #define KCDB_IDENT_FLAG_STICKY      0x00000800L\r
 \r
+/*! \brief Unknown state\r
+\r
+    The validity of the identity cannot be determined.  This usually\r
+    means that an authority could not be contacted.  This flag is to\r
+    be treated as transient.  If ::KCDB_IDENT_FLAG_INVALID or\r
+    ::KCDB_IDENT_FLAG_VALID is set for the identity, this flag is to\r
+    be ignored.\r
+ */\r
+#define KCDB_IDENT_FLAG_UNKNOWN     0x00001000L\r
+\r
 /*! \brief Read/write flags mask.\r
 \r
     A bitmask that correspond to all the read/write flags in the mask.\r
 */\r
-#define KCDB_IDENT_FLAGMASK_RDWR    0x00000fffL\r
+#define KCDB_IDENT_FLAGMASK_RDWR    0x00001fffL\r
 \r
 /*@}*/\r
 \r
@@ -431,7 +441,9 @@ kcdb_identity_delete(khm_handle id);
     If ::KCDB_IDENT_FLAG_INVALID is set using this function, then the\r
     ::KCDB_IDENT_FLAG_VALID will be automatically reset, and vice\r
     versa.  Resetting either bit does not undo this change, and will\r
-    leave the identity's validity unspecified.\r
+    leave the identity's validity unspecified.  Setting either of\r
+    ::KCDB_IDENT_FLAG_INVALID or ::KCDB_IDENT_FLAG_VALID will\r
+    automatically reset ::KCDB_IDENT_FLAG_UNKNOWN.\r
 \r
     Note that setting or resetting certain flags have other semantic\r
     side-effects:\r
index f93363e5883e14f7c07612639bf8d0844dbcd39f..636e8579f634d7b74f00b99c2c81d77118677179 100644 (file)
@@ -131,6 +131,9 @@ khm_boolean KHMAPI kmmint_reg_cb(khm_int32 msg_type,
   callback routine ( kmmint_reg_cb() ) */\r
 DWORD WINAPI kmmint_registrar(LPVOID lpParameter)\r
 {\r
+\r
+    PDESCTHREAD(L"KMM Registrar", L"KMM");\r
+\r
     tid_registrar = GetCurrentThreadId();\r
 \r
     kmq_subscribe(KMSG_KMM, kmmint_reg_cb);\r
@@ -140,6 +143,9 @@ DWORD WINAPI kmmint_registrar(LPVOID lpParameter)
 \r
     while(KHM_SUCCEEDED(kmq_dispatch(INFINITE)));\r
 \r
+    kmq_unsubscribe(KMSG_KMM, kmmint_reg_cb);\r
+    kmq_unsubscribe(KMSG_SYSTEM, kmmint_reg_cb);\r
+\r
     ExitThread(0);\r
     /* not reached */\r
     return 0;\r
@@ -156,6 +162,8 @@ DWORD WINAPI kmmint_plugin_broker(LPVOID lpParameter)
     DWORD rv = 0;\r
     kmm_plugin_i * p = (kmm_plugin_i *) lpParameter;\r
 \r
+    PDESCTHREAD(p->p.name, L"KMM");\r
+\r
     _begin_task(0);\r
     _report_mr1(KHERR_NONE, MSG_PB_START, _cstr(p->p.name));\r
     _describe();\r
@@ -324,8 +332,13 @@ void kmmint_init_plugin(kmm_plugin_i * p) {
            from the initial attempt to start the plugin.  Undo\r
            the hold we did a few lines earlier. */\r
         kmm_release_plugin(kmm_handle_from_plugin(p));\r
+\r
         /* same for the plugin count for the module. */\r
+#ifdef DEBUG\r
+        assert(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT);\r
+#endif\r
         p->module->plugin_count--;\r
+        p->flags &= ~KMM_PLUGIN_FLAG_IN_MODCOUNT;\r
     }\r
 \r
     p->state = KMM_PLUGIN_STATE_PREINIT;\r
@@ -435,6 +448,10 @@ void kmmint_init_plugin(kmm_plugin_i * p) {
 \r
     } while(FALSE);\r
 \r
+#ifdef DEBUG\r
+    assert(!(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT));\r
+#endif\r
+    p->flags |= KMM_PLUGIN_FLAG_IN_MODCOUNT;\r
     p->module->plugin_count++;\r
 \r
     LeaveCriticalSection(&cs_kmm);\r
@@ -485,9 +502,18 @@ _exit:
                 _dupstr(p->p.name), _int32(p->state));\r
     _end_task();\r
 \r
+\r
+#ifdef ASYNC_PLUGIN_UNLOAD_ON_FAILURE\r
+\r
     kmm_hold_plugin(kmm_handle_from_plugin(p));\r
 \r
     kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_PLUGIN, (void *) p);\r
+\r
+#else\r
+\r
+    kmmint_exit_plugin(p);\r
+\r
+#endif\r
 }\r
 \r
 /*! \internal\r
@@ -522,14 +548,15 @@ void kmmint_exit_plugin(kmm_plugin_i * p) {
     EnterCriticalSection(&cs_kmm);\r
 \r
     /* undo reference count done in kmmint_init_plugin() */\r
-    if(p->state == KMM_PLUGIN_STATE_EXITED ||\r
-       p->state == KMM_PLUGIN_STATE_HOLD) {\r
+    if(p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT) {\r
 \r
         np = --(p->module->plugin_count);\r
+        p->flags &= ~KMM_PLUGIN_FLAG_IN_MODCOUNT;\r
 \r
     } else {\r
-        /* the plugin was never active.  We can't base a module unload\r
-           decision on np */\r
+        /* the plugin was not included in the module's plugin_count.\r
+           We can't base a decision to unload the module based on this\r
+           plugin exiting. */\r
         np = TRUE;\r
     }\r
 \r
@@ -915,16 +942,23 @@ void kmmint_exit_module(kmm_module_i * m) {
         p = kmm_listed_plugins;\r
 \r
         while(p) {\r
-            if(p->module == m) {\r
+            if(p->module == m &&\r
+               (p->flags & KMM_PLUGIN_FLAG_IN_MODCOUNT)) {\r
+\r
                 kmm_hold_plugin(kmm_handle_from_plugin(p));\r
                 kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, \r
                                  KMM_REG_EXIT_PLUGIN, (void *) p);\r
                 np++;\r
+\r
             }\r
 \r
             p = LNEXT(p);\r
         }\r
 \r
+#ifdef DEBUG\r
+        assert(np == m->plugin_count);\r
+#endif\r
+\r
         if(np > 0) {\r
             /*  we have to go back and wait for the plugins to exit.\r
                 when the last plugin exits, it automatically posts\r
@@ -933,6 +967,21 @@ void kmmint_exit_module(kmm_module_i * m) {
             LeaveCriticalSection(&cs_kmm);\r
             return;\r
         }\r
+\r
+    } else {\r
+\r
+#ifdef DEBUG\r
+        assert(m->plugin_count == 0 ||\r
+               m->state == KMM_MODULE_STATE_EXITPLUG);\r
+#endif\r
+\r
+        /* if there are still plug-ins waiting to be unloaded, then we\r
+           have to go back and wait for them to finish.  Once they are\r
+           done, kmmint_exit_module() will get called again. */\r
+        if (m->plugin_count > 0) {\r
+            LeaveCriticalSection(&cs_kmm);\r
+            return;\r
+        }\r
     }\r
 \r
     if(m->flags & KMM_MODULE_FLAG_INITP) {\r
@@ -968,6 +1017,14 @@ void kmmint_exit_module(kmm_module_i * m) {
 \r
     m->h_module = NULL;\r
     m->h_resource = NULL;\r
+\r
+    if (m->flags & KMM_MODULE_FLAG_LOADED) {\r
+#ifdef DEBUG\r
+        assert(kmm_active_modules > 0);\r
+#endif\r
+        kmm_active_modules--;\r
+    }\r
+\r
     m->flags = 0;\r
 \r
     /* release the hold obtained in kmmint_init_module() */\r
@@ -975,13 +1032,6 @@ void kmmint_exit_module(kmm_module_i * m) {
 \r
     /* Last but not least, now see if there are any modules left that\r
        are running. If not, we can safely signal an exit. */\r
-\r
-#ifdef DEBUG\r
-    assert(kmm_active_modules > 0);\r
-#endif\r
-\r
-    kmm_active_modules--;\r
-\r
     if (kmm_active_modules == 0) {\r
         SetEvent(evt_exit);\r
     }\r
index 41eaa73658531fccb5333d82050b44a217bb3a0c..38fce2422f48297cf3bb7fdcfa3a38f30edde83f 100644 (file)
@@ -40,6 +40,9 @@
 #include<khmsgtypes.h>\r
 #include<kherror.h>\r
 #include<kplugin.h>\r
+\r
+#define NOEXPORT\r
+\r
 #include<utils.h>\r
 #include<kconfig.h>\r
 #include<kcreddb.h>\r
@@ -104,6 +107,9 @@ typedef struct kmm_module_i_t {
    (option specified in configuration)*/\r
 #define KMM_MODULE_FLAG_NOUNLOAD    0x00000800\r
 \r
+/* the module has been removed from the active modules list. */\r
+#define KMM_MODULE_FLAG_INACTIVE    0x00001000\r
+\r
 typedef struct kmm_plugin_i_t {\r
     kmm_plugin_reg p;\r
 \r
@@ -142,13 +148,17 @@ typedef struct kmm_plugin_i_t {
 /* the plugin is in the module's plugin list */\r
 #define KMM_PLUGIN_FLAG_IN_MODLIST  0x00000004\r
 \r
+/* the plugin has been included in the pending_plugins count. */\r
 #define KMM_PLUGIN_FLAG_IN_QUEUE    0x00000010\r
 \r
+/* the plugin is included in the module's plugin count */\r
+#define KMM_PLUGIN_FLAG_IN_MODCOUNT 0x00000020\r
+\r
 /* the plugin is disabled by the user\r
     (option specified in configuration) */\r
 /* (this is defined in kmm.h)\r
 \r
- #define KMM_PLUGIN_FLAG_DISABLED    0x0400\r
+ #define KMM_PLUGIN_FLAG_DISABLED   0x00000400\r
 \r
 */\r
 \r
index ed7d548f10841b55fe0138284c42fb9d15dbf64e..d6041779f9d44e23a5e3f4dbc1109f214bc5966b 100644 (file)
@@ -36,6 +36,70 @@ kmq_message_ref * kmq_msg_ref_free = NULL;
 /* ad-hoc subscriptions */\r
 kmq_msg_subscription * kmq_adhoc_subs = NULL;\r
 \r
+#ifdef DEBUG\r
+\r
+#include<stdio.h>\r
+\r
+void\r
+kmqint_dump_consumer(FILE * f) {\r
+    kmq_message_ref * r;\r
+    kmq_msg_subscription * s;\r
+\r
+    int n_free = 0;\r
+    int n_adhoc = 0;\r
+\r
+    EnterCriticalSection(&cs_kmq_msg_ref);\r
+\r
+    fprintf(f, "qc0\t*** Free Message References ***\n");\r
+\r
+    fprintf(f, "qc1\tAddress\n");\r
+\r
+    r = kmq_msg_ref_free;\r
+    while(r) {\r
+        n_free ++;\r
+\r
+        fprintf(f, "qc2\t0x%p\n", r);\r
+\r
+        r = LNEXT(r);\r
+    }\r
+\r
+    fprintf(f, "qc3\tTotal free message references : %d\n", n_free);\r
+\r
+    fprintf(f, "qc4\t--- End ---\n");\r
+\r
+    LeaveCriticalSection(&cs_kmq_msg_ref);\r
+\r
+    EnterCriticalSection(&cs_kmq_global);\r
+\r
+    fprintf(f, "qc5\t*** Adhoc Message Subscriptions ***\n");\r
+\r
+    fprintf(f, "qc6\tAddress\tMsg Type\tRcpt Type\tRcpt\tQueue\n");\r
+\r
+    s = kmq_adhoc_subs;\r
+\r
+    while(s) {\r
+        n_adhoc ++;\r
+\r
+        fprintf(f, "qc7\t0x%p\t%d\t%s\t0x%p\t0x%p\n",\r
+                s,\r
+                s->type,\r
+                (s->rcpt_type == KMQ_RCPTTYPE_CB)?"CALLBACK":"HWND",\r
+                (s->rcpt_type == KMQ_RCPTTYPE_CB) ? (void *) s->recipient.cb: (void *) s->recipient.hwnd,\r
+                s->queue);\r
+\r
+        s = LNEXT(s);\r
+    }\r
+\r
+    fprintf(f, "qc8\tTotal ad-hoc subscriptions : %d\n", n_adhoc);\r
+\r
+    fprintf(f, "qc9\t--- End ---\n");\r
+\r
+    LeaveCriticalSection(&cs_kmq_global);\r
+\r
+}\r
+\r
+#endif\r
+\r
 /*! \internal\r
     \brief Get a message ref object\r
     \note called with cs_kmq_msg_ref held */\r
@@ -89,9 +153,15 @@ kmq_queue * kmqint_get_thread_queue(void) {
     */\r
 void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r) {\r
     EnterCriticalSection(&q->cs);\r
-    QGET(q,r);\r
-    if(QTOP(q))\r
-        SetEvent(q->wait_o);\r
+\r
+    if (q->flags & KMQ_QUEUE_FLAG_DELETED) {\r
+        *r = NULL;\r
+    } else {\r
+        QGET(q,r);\r
+        if(QTOP(q))\r
+            SetEvent(q->wait_o);\r
+    }\r
+\r
     LeaveCriticalSection(&q->cs);\r
 }\r
 \r
@@ -102,6 +172,13 @@ void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r) {
 void kmqint_post_queue(kmq_queue * q, kmq_message *m) {\r
     kmq_message_ref *r;\r
 \r
+    EnterCriticalSection(&q->cs);\r
+    if (q->flags & KMQ_QUEUE_FLAG_DELETED) {\r
+        LeaveCriticalSection(&q->cs);\r
+        return;\r
+    }\r
+    LeaveCriticalSection(&q->cs);\r
+\r
     EnterCriticalSection(&cs_kmq_msg_ref);\r
     r = kmqint_get_message_ref();\r
     LeaveCriticalSection(&cs_kmq_msg_ref);\r
@@ -142,11 +219,8 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send
             if (IsBadCodePtr(s->recipient.cb)) {\r
                 rv = KHM_ERROR_INVALID_OPERATION;\r
             } else {\r
-                if (IsBadCodePtr(s->recipient.cb))\r
-                    rv = KHM_ERROR_INVALID_OPERATION;\r
-                else\r
-                    rv = s->recipient.cb(m->type, m->subtype, \r
-                                         m->uparam, m->vparam);\r
+                rv = s->recipient.cb(m->type, m->subtype, \r
+                                     m->uparam, m->vparam);\r
             }\r
             m->refcount--;\r
             if(KHM_SUCCEEDED(rv))\r
@@ -154,6 +228,14 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send
             else\r
                 m->nFailed++;\r
         } else {\r
+\r
+            EnterCriticalSection(&q->cs);\r
+            if (q->flags & KMQ_QUEUE_FLAG_DELETED) {\r
+                LeaveCriticalSection(&q->cs);\r
+                return;\r
+            }\r
+            LeaveCriticalSection(&q->cs);\r
+\r
             EnterCriticalSection(&cs_kmq_msg_ref);\r
             r = kmqint_get_message_ref();\r
             LeaveCriticalSection(&cs_kmq_msg_ref);\r
@@ -173,8 +255,6 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send
 \r
 #ifdef _WIN32\r
     else if(s->rcpt_type == KMQ_RCPTTYPE_HWND) {\r
-        m->refcount++;\r
-\r
         if(try_send && \r
            GetCurrentThreadId() == GetWindowThreadProcessId(s->recipient.hwnd, \r
                                                             NULL)) {\r
@@ -185,11 +265,24 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send
                message has completed when (m->nCompleted + m->nFailed\r
                == m->nSent).  Therefore, we only increment nSent after\r
                the message is sent. */\r
+\r
+            m->refcount++;\r
+\r
+            /* the kmq_wm_begin()/kmq_wm_end() and kmq_wm_dispatch()\r
+               handlers decrement the reference count on the message\r
+               when they are done. */\r
             SendMessage(s->recipient.hwnd, KMQ_WM_DISPATCH, \r
                         m->type, (LPARAM) m);\r
+\r
             m->nSent++;\r
+\r
         } else {\r
             m->nSent++;\r
+            m->refcount++;\r
+\r
+            /* the kmq_wm_begin()/kmq_wm_end() and kmq_wm_dispatch()\r
+               handlers decrement the reference count on the message\r
+               when they are done. */\r
             PostMessage(s->recipient.hwnd, KMQ_WM_DISPATCH, \r
                         m->type, (LPARAM) m);\r
         }\r
@@ -202,8 +295,6 @@ void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send
            deleted an ad-hoc subscription. */\r
 #ifdef DEBUG\r
         assert(FALSE);\r
-#else\r
-        return;\r
 #endif\r
     }\r
 }\r
@@ -401,6 +492,11 @@ KHMEXP LRESULT KHMAPI kmq_wm_dispatch(LPARAM lparm, kmq_callback_t cb) {
     return TRUE;\r
 }\r
 \r
+KHMEXP khm_boolean KHMAPI kmq_is_call_aborted(void) {\r
+    /* TODO: Implement this */\r
+    return FALSE;\r
+}\r
+\r
 /*! \internal\r
 \r
     \note Obtains ::cs_kmq_global, kmq_queue::cs, ::cs_kmq_msg_ref, ::cs_kmq_msg, \r
@@ -428,6 +524,12 @@ KHMEXP khm_int32 KHMAPI kmq_dispatch(kmq_timer timeout) {
             if (m->err_ctx)\r
                 kherr_push_context(m->err_ctx);\r
 \r
+            /* TODO: before dispatching the message, the message being\r
+               dispatched for this thread needs to be stored so that\r
+               it can be looked up in kmq_is_call_aborted(). This\r
+               needs to happen in kmq_wm_dispatch() and\r
+               kmq_wm_begin() as well. */\r
+\r
             /* dispatch */\r
             rv = r->recipient(m->type, m->subtype, m->uparam, m->vparam);\r
 \r
index 00b8f6f4f26b268a457e585784d677044af8b84b..875e3eaf3a69a4453b569cff3128e301a2680f21 100644 (file)
@@ -115,11 +115,58 @@ void kmqint_detach_this_thread(void) {
 \r
     q = (kmq_queue *) TlsGetValue(kmq_tls_queue);\r
     if(q) {\r
+        kmq_message_ref * r;\r
+        kmq_message * m;\r
+\r
         EnterCriticalSection(&q->cs);\r
-        q->flags |= KMQ_QUEUE_FLAG_DELETED;\r
+\r
+        if (q->flags & KMQ_QUEUE_FLAG_DETACHING) {\r
+#ifdef DEBUG\r
+            assert(FALSE);\r
+#endif\r
+            LeaveCriticalSection(&q->cs);\r
+            return;\r
+        }\r
+\r
+        q->flags |= KMQ_QUEUE_FLAG_DELETED | KMQ_QUEUE_FLAG_DETACHING;\r
+\r
+        QGET(q, &r);\r
+        while(r) {\r
+\r
+            m = r->msg;\r
+\r
+            LeaveCriticalSection(&q->cs);\r
+\r
+            EnterCriticalSection(&cs_kmq_msg);\r
+            EnterCriticalSection(&cs_kmq_msg_ref);\r
+            kmqint_put_message_ref(r);\r
+            LeaveCriticalSection(&cs_kmq_msg_ref);\r
+\r
+            m->nFailed++;\r
+            if(m->nCompleted + m->nFailed == m->nSent) {\r
+                kmqint_put_message(m);\r
+            }\r
+            LeaveCriticalSection(&cs_kmq_msg);\r
+\r
+            EnterCriticalSection(&q->cs);\r
+\r
+            QGET(q, &r);\r
+        }\r
+\r
+        CloseHandle(q->wait_o);\r
+\r
+        q->wait_o = NULL;\r
+\r
+        q->flags &= ~KMQ_QUEUE_FLAG_DETACHING;\r
+        \r
         LeaveCriticalSection(&q->cs);\r
 \r
-        /* TODO: free up the queued messages */\r
+        /* For now, we don't free the queue. */\r
+\r
+        /* TODO: before we can free the queue, we have to go through\r
+           all the message type subscriptions and ad-hoc subscriptions\r
+           and make sure no subscriptions exist which refer to this\r
+           message queue. */\r
     }\r
 }\r
 \r
@@ -139,6 +186,8 @@ DWORD WINAPI kmqint_completion_thread_proc(LPVOID p) {
     kmq_message * m;\r
     kherr_context * ctx;\r
 \r
+    PDESCTHREAD(L"Msg completion thread", L"KMQ");\r
+\r
     EnterCriticalSection(&cs_compl);\r
     do {\r
        \r
@@ -249,3 +298,16 @@ KHMEXP khm_int32 KHMAPI kmq_exit(void) {
 \r
     return KHM_ERROR_SUCCESS;\r
 }\r
+\r
+#ifdef DEBUG\r
+\r
+void kmqint_dump_consumer(FILE * f);\r
+void kmqint_dump_publisher(FILE * f);\r
+\r
+\r
+KHMEXP void KHMAPI kmqint_dump(FILE * f) {\r
+    kmqint_dump_consumer(f);\r
+    kmqint_dump_publisher(f);\r
+}\r
+\r
+#endif\r
index fef6a30969ccb3faae2ca542fdea45e479441959..46ce74f3b2f3b65645fe28ef5769adffae8bdae2 100644 (file)
@@ -112,92 +112,18 @@ typedef struct tag_kmq_message {
 \r
     kherr_context * err_ctx;    /*!< Error context for the message */\r
 \r
-    khm_int32 refcount;\r
+    khm_boolean aborted;        /*!< TRUE if the message has been\r
+                                  aborted. */\r
 \r
-    LDCL(struct tag_kmq_message);\r
+    khm_int32 refcount;         /*!< Internal use */\r
+\r
+    LDCL(struct tag_kmq_message); /*!< Internal use */\r
 } kmq_message;\r
 \r
 /*! \brief A handle to a call\r
  */\r
 typedef kmq_message *kmq_call;\r
 \r
-/*! \brief Message reference */\r
-typedef struct tag_kmq_message_ref {\r
-    kmq_message * msg;          /*!< Message that we are referring\r
-                                  to */\r
-    kmq_callback_t recipient;   /*!< The recipient of the message */\r
-\r
-    LDCL(struct tag_kmq_message_ref);\r
-} kmq_message_ref;\r
-\r
-/*! \brief Message queue\r
-\r
-    Each thread gets its own message queue.  When a message is\r
-    broadcast to which there is a subscriber in a particular thread, a\r
-    reference to the message is placed in the message queue of the\r
-    thread.  The dispatch procedure then dispatches the message as\r
-    described in the message reference.\r
-*/\r
-typedef struct tag_kmq_queue {\r
-    kmq_thread_id thread;       /*!< The thread id  */\r
-\r
-    CRITICAL_SECTION cs;\r
-    HANDLE wait_o;\r
-\r
-    khm_int32 load;             /*!< Number of messages waiting to be\r
-                                  processed on this message queue.  */\r
-    kmq_timer last_post;        /*!< Time the last message was\r
-                                  received */\r
-\r
-    khm_int32 flags;            /*!< Flags.  Currently, it's just KMQ_QUEUE_FLAG_DELETED */\r
-\r
-    /*Q*/\r
-    QDCL(kmq_message_ref);      /*!< Queue of message references  */\r
-\r
-    /*Lnode*/\r
-    LDCL(struct tag_kmq_queue);\r
-} kmq_queue;\r
-\r
-#define KMQ_QUEUE_FLAG_DELETED 0x0008\r
-\r
-/*! \brief Message subscription\r
-\r
-    A subscription binds a recipient with a message type.  These are\r
-    specific to a thread. I.e. a subscription that was made in one\r
-    thread will not receive messages in the context of another thread.\r
-*/\r
-typedef struct tag_kmq_msg_subscription {\r
-    khm_int32 magic;            /*!< Magic number.  Should always be\r
-                                  ::KMQ_MSG_SUB_MAGIC */\r
-    khm_int32 type;             /*!< Type of message */\r
-    khm_int32 rcpt_type;        /*!< Type of recipient.  One of\r
-                                  ::KMQ_RCPTTYPE_CB or\r
-                                  ::KMQ_RCPTTYPE_HWND  */\r
-    union {\r
-        kmq_callback_t cb;      /*!< Callback if the subscription is\r
-                                  of callback type */\r
-        HWND hwnd;              /*!< Window handle if the subscription\r
-                                  is a windows message type */\r
-    } recipient;\r
-\r
-    kmq_queue * queue;          /*!< Associated queue */\r
-\r
-    /*lnode*/\r
-    LDCL(struct tag_kmq_msg_subscription);\r
-} kmq_msg_subscription;\r
-\r
-#define KMQ_MSG_SUB_MAGIC 0x3821b58e\r
-\r
-/*! \brief Callback recipient type\r
-\r
-    The recipient is a callback function */\r
-#define KMQ_RCPTTYPE_CB     1\r
-\r
-/*! \brief Windows recipient type\r
-\r
-    The recipient is a window */\r
-#define KMQ_RCPTTYPE_HWND   2\r
-\r
 /* publishers */\r
 \r
 /*! \brief A completion handler for a message\r
@@ -214,43 +140,14 @@ typedef struct tag_kmq_msg_subscription {
  */\r
 typedef void (KHMAPI *kmq_msg_completion_handler)(kmq_message *);\r
 \r
-/*! \brief A message type\r
- */\r
-typedef struct tag_kmq_msg_type {\r
-    khm_int32 id;               /*!< Identifier for the message\r
-                                  type. */\r
-    kmq_msg_subscription * subs; /*!< The list of subscriptions */\r
-    kmq_msg_completion_handler completion_handler; /*!< Completion\r
-                                  handler for the message type */\r
-\r
-    wchar_t * name;             /*!< Name of the message type for\r
-                                  named types.  Message type names are\r
-                                  language independant. */\r
-\r
-    /*Lnode*/\r
-    LDCL(struct tag_kmq_msg_type);\r
-} kmq_msg_type;\r
-\r
-/*! \brief The maximum number of message types\r
- */\r
-#define KMQ_MSG_TYPE_MAX 255\r
-\r
-/*! \brief Maximum number of characters in a message type name\r
-\r
-    The count includes the terminating NULL\r
- */\r
-#define KMQ_MAXCCH_TYPE_NAME 256\r
-\r
-/*! \brief Maximum number of bytes in a message type name\r
-\r
-    Type count includes the terminating NULL\r
- */\r
-#define KMQ_MAXCB_TYPE_NAME (KMQ_MAXCCH_TYPE_NAME * sizeof(wchar_t))\r
+#ifdef NOEXPORT\r
 \r
 KHMEXP khm_int32 KHMAPI kmq_init(void);\r
 \r
 KHMEXP khm_int32 KHMAPI kmq_exit(void);\r
 \r
+#endif\r
+\r
 /*! \brief Register a message type\r
 \r
     Registers a custom message type.  The \a name parameter specifies\r
@@ -750,6 +647,46 @@ KHMEXP khm_boolean KHMAPI kmq_has_completed(kmq_call call);
 */\r
 KHMEXP khm_int32 KHMAPI kmq_wait(kmq_call call, kmq_timer timeout);\r
 \r
+/*! \brief Abort a call\r
+\r
+    Abort a pending call.  The call handle should have been obtained\r
+    using a prior call to kmq_post_message_ex().\r
+\r
+    Note that this function may not abort the call immediately.  It\r
+    merely marks the message as being in an aborted state.  It is upto\r
+    the individual handlers of the message to check if the message has\r
+    been aborted and act accordingly.\r
+\r
+    The handlers are expected to call kmq_is_call_aborted()\r
+    periodicially during the processing of specially lengthy\r
+    operations during the course of handling a message. That function\r
+    will return \a TRUE if the last dispatched message is now in an\r
+    aborted state.  In which case, the handler is expected to abort\r
+    handling the message and return control to the dispatcher.\r
+ */\r
+KHMEXP khm_int32 KHMAPI kmq_abort_call(kmq_call call);\r
+\r
+/*! \brief Check if the last dispatched message was aborted\r
+\r
+    The sender of a message may abort it using a call to\r
+    kmq_abort_call().  This function checks if the last dispatched\r
+    message was aborted.\r
+\r
+    A handler of a message is expected to call this function\r
+    periodically if handling the message is going to take a specially\r
+    long time (e.g. more than 5 or 10 seconds).  If the message is\r
+    found to be aborted, the handler is expected to abort handling the\r
+    message, perform any necessary cleanup and return control to the\r
+    dispatcher.\r
+\r
+    Doing this allows operations like new credentials acquisition to\r
+    be cleanly aborted by the user if she so wishes.  Otherwise,\r
+    Network Identity Manager has to wait for the message to complete\r
+    processing since it has no means of cleanly terminating an\r
+    executing plug-in thread.\r
+*/\r
+KHMEXP khm_boolean KHMAPI kmq_is_call_aborted(void);\r
+\r
 /*! \brief Sets the completion handler for a specified message type.\r
 \r
     \note Only one completion handler can exist for one message type.\r
index aeaf3366ca0bef11048fd76122929c7448d354f5..1d6196b307258263e7ce035a2ccf72e9e18c7cbb 100644 (file)
 #include<kherror.h>\r
 #include<khmsgtypes.h>\r
 #include<kconfig.h>\r
+\r
+#define NOEXPORT\r
+\r
 #include<utils.h>\r
 #include<strsafe.h>\r
 \r
+\r
+\r
+\r
+/*! \brief Message reference */\r
+typedef struct tag_kmq_message_ref {\r
+    kmq_message * msg;          /*!< Message that we are referring\r
+                                  to */\r
+    kmq_callback_t recipient;   /*!< The recipient of the message */\r
+\r
+    LDCL(struct tag_kmq_message_ref);\r
+} kmq_message_ref;\r
+\r
+\r
+\r
+\r
+/*! \brief Message queue\r
+\r
+    Each thread gets its own message queue.  When a message is\r
+    broadcast to which there is a subscriber in a particular thread, a\r
+    reference to the message is placed in the message queue of the\r
+    thread.  The dispatch procedure then dispatches the message as\r
+    described in the message reference.\r
+*/\r
+typedef struct tag_kmq_queue {\r
+    kmq_thread_id thread;       /*!< The thread id  */\r
+\r
+    CRITICAL_SECTION cs;\r
+    HANDLE wait_o;\r
+\r
+    khm_int32 load;             /*!< Number of messages waiting to be\r
+                                  processed on this message queue.  */\r
+    kmq_timer last_post;        /*!< Time the last message was\r
+                                  received */\r
+\r
+    khm_int32 flags;            /*!< Flags.  Currently, it's just KMQ_QUEUE_FLAG_DELETED */\r
+\r
+    /*Q*/\r
+    QDCL(kmq_message_ref);      /*!< Queue of message references  */\r
+\r
+    /*Lnode*/\r
+    LDCL(struct tag_kmq_queue);\r
+} kmq_queue;\r
+\r
+#define KMQ_QUEUE_FLAG_DELETED   0x00000008\r
+#define KMQ_QUEUE_FLAG_DETACHING 0x00000010\r
+\r
+/*! \brief Message subscription\r
+\r
+    A subscription binds a recipient with a message type.  These are\r
+    specific to a thread. I.e. a subscription that was made in one\r
+    thread will not receive messages in the context of another thread.\r
+*/\r
+typedef struct tag_kmq_msg_subscription {\r
+    khm_int32 magic;            /*!< Magic number.  Should always be\r
+                                  ::KMQ_MSG_SUB_MAGIC */\r
+    khm_int32 type;             /*!< Type of message */\r
+    khm_int32 rcpt_type;        /*!< Type of recipient.  One of\r
+                                  ::KMQ_RCPTTYPE_CB or\r
+                                  ::KMQ_RCPTTYPE_HWND  */\r
+    union {\r
+        kmq_callback_t cb;      /*!< Callback if the subscription is\r
+                                  of callback type */\r
+        HWND hwnd;              /*!< Window handle if the subscription\r
+                                  is a windows message type */\r
+    } recipient;\r
+\r
+    kmq_queue * queue;          /*!< Associated queue */\r
+\r
+    /*lnode*/\r
+    LDCL(struct tag_kmq_msg_subscription);\r
+} kmq_msg_subscription;\r
+\r
+#define KMQ_MSG_SUB_MAGIC 0x3821b58e\r
+\r
+/*! \brief Callback recipient type\r
+\r
+    The recipient is a callback function */\r
+#define KMQ_RCPTTYPE_CB     1\r
+\r
+/*! \brief Windows recipient type\r
+\r
+    The recipient is a window */\r
+#define KMQ_RCPTTYPE_HWND   2\r
+\r
+\r
+\r
+\r
+/*! \brief A message type\r
+ */\r
+typedef struct tag_kmq_msg_type {\r
+    khm_int32 id;               /*!< Identifier for the message\r
+                                  type. */\r
+    kmq_msg_subscription * subs; /*!< The list of subscriptions */\r
+    kmq_msg_completion_handler completion_handler; /*!< Completion\r
+                                  handler for the message type */\r
+\r
+    wchar_t * name;             /*!< Name of the message type for\r
+                                  named types.  Message type names are\r
+                                  language independant. */\r
+\r
+    /*Lnode*/\r
+    LDCL(struct tag_kmq_msg_type);\r
+} kmq_msg_type;\r
+\r
+/*! \brief The maximum number of message types\r
+ */\r
+#define KMQ_MSG_TYPE_MAX 255\r
+\r
+/*! \brief Maximum number of characters in a message type name\r
+\r
+    The count includes the terminating NULL\r
+ */\r
+#define KMQ_MAXCCH_TYPE_NAME 256\r
+\r
+/*! \brief Maximum number of bytes in a message type name\r
+\r
+    Type count includes the terminating NULL\r
+ */\r
+#define KMQ_MAXCB_TYPE_NAME (KMQ_MAXCCH_TYPE_NAME * sizeof(wchar_t))\r
+\r
+\r
+\r
+\r
 #define KMQ_CONF_SPACE_NAME L"KMQ"\r
 #define KMQ_CONF_QUEUE_DEAD_TIMEOUT_NAME L"QueueDeadTimeout"\r
 #define KMQ_CONF_CALL_DEAD_TIMEOUT_NAME L"CallDeadTimeout"\r
@@ -69,6 +195,7 @@ void kmqint_post_queue(kmq_queue * q, kmq_message *m);
 void kmqint_post(kmq_msg_subscription * s, kmq_message * m, khm_boolean try_send);\r
 kmq_queue * kmqint_get_thread_queue(void);\r
 void kmqint_get_queue_message_ref(kmq_queue * q, kmq_message_ref ** r);\r
+void kmqint_put_message_ref(kmq_message_ref * r);\r
 \r
 /* publisher */\r
 extern CRITICAL_SECTION cs_kmq_msg;\r
index 3fd31ec17b3996ef5eea3d76026526145a3fc1b5..1b0868d836c7371436b5bf1878785cc3cb7b5c70 100644 (file)
@@ -81,9 +81,30 @@ int kmqint_notify_msg_completion(kmq_message * m) {
 \r
 /* called with cs_mkq_global && cs_kmq_types held */\r
 void kmqint_free_msg_type(int t) {\r
-    /*TODO: free the message type*/\r
-    /* must set handler to NULL before freeing type */\r
-    /* must set msg_type[t] = NULL before starting to free type */\r
+    kmq_msg_type * pt;\r
+    kmq_msg_subscription * s;\r
+\r
+    pt = msg_types[t];\r
+\r
+    msg_types[t] = NULL;\r
+\r
+    if (pt == NULL)\r
+        return;\r
+\r
+    /* all the subscriptions attached to a message type are owned by\r
+       the message type */\r
+    LPOP(&pt->subs, &s);\r
+    while(s) {\r
+        s->magic = 0;\r
+\r
+        PFREE(s);\r
+\r
+        LPOP(&pt->subs, &s);\r
+    }\r
+\r
+    pt->completion_handler = NULL;\r
+\r
+    PFREE(pt);\r
 }\r
 \r
 /*! \internal\r
index af1f555667e04d7406bc2719c77fa02b1162acdf..66360fb50b0c1f04540e2b3f57d0385162d7a9a9 100644 (file)
@@ -30,6 +30,66 @@ CRITICAL_SECTION cs_kmq_msg;
 kmq_message * msg_free = NULL;\r
 kmq_message * msg_active = NULL;\r
 \r
+#ifdef DEBUG\r
+\r
+#include<stdio.h>\r
+\r
+void\r
+kmqint_dump_publisher(FILE * f) {\r
+\r
+    int n_free = 0;\r
+    int n_active = 0;\r
+    kmq_message * m;\r
+\r
+    EnterCriticalSection(&cs_kmq_msg);\r
+\r
+    fprintf(f, "qp0\t*** Free Messages ***\n");\r
+    fprintf(f, "qp1\tAddress\n");\r
+\r
+    m = msg_free;\r
+    while(m) {\r
+        n_free++;\r
+\r
+        fprintf(f, "qp2\t0x%p\n", m);\r
+\r
+        m = LNEXT(m);\r
+    }\r
+\r
+    fprintf(f, "qp3\tTotal free messages : %d\n", n_free);\r
+\r
+    fprintf(f, "qp4\t*** Active Messages ***\n");\r
+    fprintf(f, "qp5\tAddress\tType\tSubtype\tuParam\tvParam\tnSent\tnCompleted\tnFailed\twait_o\trefcount\n");\r
+\r
+    m = msg_active;\r
+    while(m) {\r
+\r
+        n_active++;\r
+\r
+        fprintf(f, "qp6\t0x%p\t%d\t%d\t0x%x\t0x%p\t%d\t%d\t%d\t0x%p\t%d\n",\r
+                m,\r
+                (int) m->type,\r
+                (int) m->subtype,\r
+                (unsigned int) m->uparam,\r
+                m->vparam,\r
+                (int) m->nSent,\r
+                (int) m->nCompleted,\r
+                (int) m->nFailed,\r
+                (void *) m->wait_o,\r
+                (int) m->refcount);\r
+\r
+        m = LNEXT(m);\r
+    }\r
+\r
+    fprintf(f, "qp7\tTotal number of active messages = %d\n", n_active);\r
+\r
+    fprintf(f, "qp8\t--- End ---\n");\r
+\r
+    LeaveCriticalSection(&cs_kmq_msg);\r
+\r
+}\r
+\r
+#endif\r
+\r
 /*! \internal\r
     \brief Get a message object\r
     \note called with ::cs_kmq_msg held */\r
@@ -181,6 +241,12 @@ kmq_post_message_ex(khm_int32 type, khm_int32 subtype,
     return kmqint_post_message_ex(type, subtype, uparam, blob, call, FALSE);\r
 }\r
 \r
+KHMEXP khm_int32 KHMAPI\r
+kmq_abort_call(kmq_call call)\r
+{\r
+    /* TODO: Implement this */\r
+    return KHM_ERROR_NOT_IMPLEMENTED;\r
+}\r
 \r
 /*! \internal\r
 */\r
@@ -445,15 +511,21 @@ kmq_post_thread_quit_message(kmq_thread_id thread,
     return KHM_ERROR_SUCCESS;\r
 }\r
 \r
-/* TODO:Implement these */\r
 KHMEXP khm_int32 KHMAPI \r
 kmq_get_next_response(kmq_call call, void ** resp) {\r
+    /* TODO: Implement this */\r
     return 0;\r
 }\r
 \r
 KHMEXP khm_boolean KHMAPI \r
 kmq_has_completed(kmq_call call) {\r
-    return (call->nCompleted + call->nFailed == call->nSent);\r
+    khm_boolean completed;\r
+\r
+    EnterCriticalSection(&cs_kmq_msg);\r
+    completed = (call->nCompleted + call->nFailed == call->nSent);\r
+    LeaveCriticalSection(&cs_kmq_msg);\r
+\r
+    return completed;\r
 }\r
 \r
 KHMEXP khm_int32 KHMAPI \r
@@ -481,3 +553,4 @@ kmq_set_completion_handler(khm_int32 type,
     return kmqint_msg_type_set_handler(type, handler);\r
 }\r
 \r
+\r
index d18ac510d0a79d7425ef01529372f9fb0e9af3f6..324097ef3f192924583bf3b18ce3a9a0d90b7c51 100644 (file)
@@ -91,6 +91,7 @@ OBJFILES= \
        $(UIDIR)\acceldef.obj           \
        $(UIDIR)\configui.obj           \
        $(UIDIR)\trackerwnd.obj         \
+       $(UIDIR)\uibind.obj             \
        $(UIDIR)\version.obj
 
 RESFILES= \
index 851f2e85a69a902a598df0cde63c98ebf7586a10..81c16162d2bedc0130160f6303f31099f631852a 100644 (file)
@@ -853,6 +853,9 @@ krb4_msg_newcred(khm_int32 msg_type, khm_int32 msg_subtype,
             if (nct->name)\r
                 PFREE(nct->name);\r
 \r
+            if (nct->credtext)\r
+                PFREE(nct->credtext);\r
+\r
             PFREE(nct);\r
         }\r
         break;\r
index 865f01039d74834070403091fac0b529c7faed0b..374f6ced5bb1c05dd459db1481f8dbcff693c37f 100644 (file)
@@ -53,16 +53,16 @@ END
 //\r
 \r
 IDD_NC_KRB4 DIALOGEX 0, 0, 300, 166\r
-STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU\r
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD\r
 EXSTYLE WS_EX_CONTROLPARENT\r
 FONT 8, "MS Shell Dlg", 400, 0, 0x1\r
 BEGIN\r
     CONTROL         "Kerberos v4 Ticket Options",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | SS_SUNKEN | WS_GROUP,7,7,286,11\r
-    CONTROL         "Obtain Kerberos v4 credentials",IDC_NCK4_OBTAIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,26,97,10\r
+    CONTROL         "Obtain Kerberos v4 credentials",IDC_NCK4_OBTAIN,"Button",BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,7,26,128,10\r
     GROUPBOX        "Obtain Kerberos v4 credentials using",IDC_STATIC,7,43,286,72,WS_GROUP\r
-    CONTROL         "Automatically determine method",IDC_NCK4_AUTO,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,23,58,119,10\r
-    CONTROL         "Kerberos v5 to v4 translation",IDC_NCK4_K524,"Button",BS_AUTORADIOBUTTON,23,76,101,10\r
-    CONTROL         "Password",IDC_NCK4_PWD,"Button",BS_AUTORADIOBUTTON,23,94,47,10\r
+    CONTROL         "Automatically determine method",IDC_NCK4_AUTO,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,23,58,152,10\r
+    CONTROL         "Kerberos v5 to v4 translation",IDC_NCK4_K524,"Button",BS_AUTORADIOBUTTON,23,76,152,10\r
+    CONTROL         "Password",IDC_NCK4_PWD,"Button",BS_AUTORADIOBUTTON,23,94,152,10\r
 END\r
 \r
 IDD_CFG_KRB4 DIALOGEX 0, 0, 255, 182\r
@@ -202,3 +202,4 @@ END
 /////////////////////////////////////////////////////////////////////////////\r
 #endif    // not APSTUDIO_INVOKED\r
 \r
+\r
index 43d6d3d9dd308c3b04b8373f41a7f38ae0b8b1b9..c036eb9762d62a095993e8a447435839af894798 100644 (file)
@@ -1533,6 +1533,8 @@ DWORD WINAPI k5_ccname_monitor_thread(LPVOID lpParameter) {
     wchar_t reg_ccname[KRB5_MAXCCH_CCNAME];\r
     LONG l;\r
 \r
+    PDESCTHREAD(L"Krb5 CCName Monitor", L"Krb5");\r
+\r
     l = RegOpenKeyEx(HKEY_CURRENT_USER,\r
                      L"Software\\MIT\\kerberos5",\r
                      0,\r
index 087d937f4c040b8a2b0fc8ac760dcb9d352cd74c..34cc8bd20753239adcc726789a10db7de74a2ea0 100644 (file)
@@ -1459,6 +1459,24 @@ k5_write_dlg_params(khm_handle conf,
     d->dirty = FALSE;\r
 }\r
 \r
+void \r
+k5_free_kinit_job(void)\r
+{\r
+    if (g_fjob.principal)\r
+        PFREE(g_fjob.principal);\r
+\r
+    if (g_fjob.password)\r
+        PFREE(g_fjob.password);\r
+\r
+    if (g_fjob.identity)\r
+        kcdb_identity_release(g_fjob.identity);\r
+\r
+    if (g_fjob.ccache)\r
+        PFREE(g_fjob.ccache);\r
+\r
+    ZeroMemory(&g_fjob, sizeof(g_fjob));\r
+}\r
+\r
 void \r
 k5_prep_kinit_job(khui_new_creds * nc)\r
 {\r
@@ -1489,8 +1507,9 @@ k5_prep_kinit_job(khui_new_creds * nc)
     kcdb_identity_get_name(ident, idname, &cbbuf);\r
     StringCchLength(idname, ARRAYLENGTH(idname), &size);\r
     size++;\r
-    \r
-    ZeroMemory(&g_fjob, sizeof(g_fjob));\r
+\r
+    k5_free_kinit_job();\r
+\r
     g_fjob.command = FIBER_CMD_KINIT;\r
     g_fjob.nc = nc;\r
     g_fjob.nct = nct;\r
@@ -1571,24 +1590,6 @@ k5_prep_kinit_job(khui_new_creds * nc)
     /* leave identity held, since we added a reference above */\r
 }\r
 \r
-void \r
-k5_free_kinit_job(void)\r
-{\r
-    if (g_fjob.principal)\r
-        PFREE(g_fjob.principal);\r
-\r
-    if (g_fjob.password)\r
-        PFREE(g_fjob.password);\r
-\r
-    if (g_fjob.identity)\r
-        kcdb_identity_release(g_fjob.identity);\r
-\r
-    if (g_fjob.ccache)\r
-        PFREE(g_fjob.ccache);\r
-\r
-    ZeroMemory(&g_fjob, sizeof(g_fjob));\r
-}\r
-\r
 static khm_int32 KHMAPI \r
 k5_find_tgt_filter(khm_handle cred,\r
                    khm_int32 flags,\r
@@ -2014,6 +2015,13 @@ k5_msg_cred_dialog(khm_int32 msg_type,
             if (d->pwd_change)\r
                 return KHM_ERROR_SUCCESS;\r
 \r
+            /* At this point, we assume that the current set of\r
+               prompts is no longer valid.  And since we might not be\r
+               able to come up with a set of prompts until the KDC\r
+               replies (unless we have cached prompts), we remove the\r
+               ones that are already shown. */\r
+            khui_cw_clear_prompts(nc);\r
+\r
             /* if the fiber is already in a kinit, cancel it */\r
             if(g_fjob.state == FIBER_STATE_KINIT) {\r
                 g_fjob.command = FIBER_CMD_CANCEL;\r
@@ -2121,6 +2129,12 @@ k5_msg_cred_dialog(khm_int32 msg_type,
 \r
                     k5_free_kinit_job();\r
 \r
+                    if (is_k5_identpro)\r
+                        kcdb_identity_set_flags(ident,\r
+                                                KCDB_IDENT_FLAG_UNKNOWN,\r
+                                                KCDB_IDENT_FLAG_UNKNOWN);\r
+\r
+\r
                 } else if(g_fjob.state == FIBER_STATE_KINIT) {\r
                     /* this is what we want.  Leave the fiber there. */\r
 \r
@@ -2790,6 +2804,8 @@ k5_msg_cred_dialog(khm_int32 msg_type,
                 PFREE(nct->credtext);\r
 \r
             PFREE(nct);\r
+\r
+            k5_free_kinit_job();\r
         }\r
         break;\r
 \r
index 3e5467ccc469c2dc1b00d92dedb2ae3a591594aa..b37fca5347ce0053dd30163aa0e2b0d7e6f9e0d9 100644 (file)
@@ -39,6 +39,8 @@ addr_change_thread(LPVOID dummy) {
     OVERLAPPED overlap;\r
     DWORD ret;\r
 \r
+    PDESCTHREAD(L"Address change waiter", L"App");\r
+\r
     ZeroMemory(&overlap, sizeof(overlap));\r
 \r
     h_notify = NULL;\r
index 8660de2b47751b8fbcbe220f8791645641c803b0..b25d38247146f743f07c3081d9237729cfd575a7 100644 (file)
@@ -40,14 +40,15 @@ extern khm_version app_version;
 \r
 #define IS_COMMCTL6() (khm_commctl_version >= 0x60000)\r
 \r
-typedef struct tag_khm_startup_options_v1 {\r
+/* The structure used to send command-line options to a remote\r
+   NetIDMgr session for versions prior to 1.2. */\r
+struct tag_khm_startup_options_v1 {\r
     BOOL seen;\r
     BOOL processing;\r
 \r
     BOOL init;\r
     BOOL import;\r
     BOOL renew;\r
-    LONG pending_renewals;\r
     BOOL destroy;\r
 \r
     BOOL autoinit;\r
@@ -55,10 +56,51 @@ typedef struct tag_khm_startup_options_v1 {
     BOOL error_exit;\r
 \r
     BOOL no_main_window;\r
+};\r
+\r
+/* Used on NetIDMgr versions 1.2 and later */\r
+struct tag_khm_startup_options_v2 {\r
+    khm_int32 magic;\r
+    DWORD cb_size;\r
+\r
+    BOOL init;\r
+    BOOL import;\r
+    BOOL renew;\r
+    BOOL destroy;\r
+\r
+    BOOL autoinit;\r
+    BOOL remote_exit;\r
+\r
+    khm_int32 code;\r
+} khm_startup_options_xfer;\r
+\r
+#define STARTUP_OPTIONS_MAGIC 0x1f280e41\r
+\r
+/* Used internally. */\r
+typedef struct tag_khm_startup_options_int {\r
+    BOOL seen;\r
+    BOOL processing;\r
+    BOOL remote;\r
+\r
+    BOOL init;\r
+    BOOL import;\r
+    BOOL renew;\r
+    BOOL destroy;\r
+\r
+    BOOL autoinit;\r
+    BOOL exit;\r
+    BOOL remote_exit;\r
+\r
+    BOOL error_exit;\r
+\r
+    BOOL no_main_window;\r
+\r
+    LONG pending_renewals;\r
 } khm_startup_options;\r
 \r
 extern khm_startup_options khm_startup;\r
 \r
+/* Used to query a remote instance of NetIDMgr for the version. */\r
 typedef struct tag_khm_query_app_version_v1 {\r
     khm_int32 magic;\r
 \r
@@ -94,6 +136,6 @@ WPARAM khm_message_loop_int(khm_boolean * p_exit);
 \r
 #define MAX_RES_STRING 1024\r
 \r
-#define ELIPSIS L"..."\r
+#define ELLIPSIS L"..."\r
 \r
 #endif\r
index 3f92ad83987f9f81ad01a6db842270dfb0313f4f..3ba843f75e629fa1585ebd02e3eb138cdfa7da5f 100644 (file)
@@ -483,10 +483,7 @@ write_params_ident(ident_data * d) {
 \r
 static void\r
 write_params_idents(void) {\r
-#if 0\r
-    int i;\r
-#endif\r
-    khm_handle csp_cw;\r
+    khm_handle csp_cw = NULL;\r
 \r
     if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow",\r
                                      KHM_FLAG_CREATE, &csp_cw))) {\r
@@ -508,7 +505,9 @@ write_params_idents(void) {
             cfg_idents.work.sticky = cfg_idents.saved.sticky;\r
             cfg_idents.applied = TRUE;\r
         }\r
+\r
         khc_close_space(csp_cw);\r
+        csp_cw = NULL;\r
     }\r
 \r
 #if 0\r
@@ -530,7 +529,7 @@ init_idents_data(void) {
     khm_size cb;\r
     int n_tries = 0;\r
     int i;\r
-    khm_handle csp_cw;\r
+    khm_handle csp_cw = NULL;\r
 \r
     if (cfg_idents.valid)\r
         return;\r
@@ -559,6 +558,9 @@ init_idents_data(void) {
         else\r
             cfg_idents.saved.sticky = FALSE;\r
 \r
+        khc_close_space(csp_cw);\r
+        csp_cw = NULL;\r
+\r
     } else {\r
 \r
         cfg_idents.saved.monitor = TRUE;\r
index 660c1fae8e202a7e5a7a29ed1c528ed491dac2f4..03eec9ad5a62201cf48723508f4e689e4d687724 100644 (file)
@@ -784,10 +784,14 @@ cfgui_dlgproc(HWND hwnd,
         khui_delete_bitmap(&d->kbmp_logo);\r
         DeleteObject(d->hbr_white);\r
 \r
+        cfgui_set_wnd_data(hwnd, NULL);\r
+\r
         khm_del_dialog(hwnd);\r
 \r
         SetForegroundWindow(khm_hwnd_main);\r
 \r
+        PFREE(d);\r
+\r
         return FALSE;\r
 \r
     case WM_NOTIFY:\r
@@ -1043,6 +1047,8 @@ void khm_refresh_config(void) {
     if (omenu == NULL)\r
         goto _cleanup;\r
 \r
+    khui_action_lock();\r
+\r
     do {\r
         khm_int32 action;\r
         khm_int32 flags;\r
@@ -1099,8 +1105,11 @@ void khm_refresh_config(void) {
             break;\r
     } while(cfg_r);\r
 \r
-    if (refresh_menu)\r
-        khm_menu_refresh_items();\r
+    khui_action_unlock();\r
+\r
+    if (refresh_menu) {\r
+        khui_refresh_actions();\r
+    }\r
 \r
  _cleanup:\r
     if (cfg_ids)\r
index e70b8526ea512e6ca91da63fb5f8aee5ae244a9d..7b70322126b3830c0d068f099210c3f3ac2feb69 100644 (file)
@@ -156,7 +156,11 @@ khm_cred_wait_for_dialog(DWORD timeout, khm_int32 * result,
     return rv;\r
 }\r
 \r
-/* completion handler for KMSG_CRED messages */\r
+/* Completion handler for KMSG_CRED messages.  We control the overall\r
+   logic of credentials acquisition and other operations here.  Once a\r
+   credentials operation is triggered, each successive message\r
+   completion notification will be used to dispatch the messages for\r
+   the next step in processing the operation. */\r
 void KHMAPI \r
 kmsg_cred_completion(kmq_message *m)\r
 {\r
@@ -183,7 +187,7 @@ kmsg_cred_completion(kmq_message *m)
         nc = (khui_new_creds *) m->vparam;\r
 \r
         /* khm_cred_dispatch_process_message() deals with the case\r
-           where there are not credential types that wants to\r
+           where there are no credential types that wants to\r
            participate in this operation. */\r
         khm_cred_dispatch_process_message(nc);\r
         break;\r
@@ -281,6 +285,8 @@ kmsg_cred_completion(kmq_message *m)
 \r
                 if (nc->subtype == KMSG_CRED_NEW_CREDS) {\r
 \r
+                    khui_alert_set_type(alert, KHUI_ALERTTYPE_ACQUIREFAIL);\r
+\r
                     cb = sizeof(w_idname);\r
                     if (nc->n_identities == 0 ||\r
                         KHM_FAILED(kcdb_identity_get_name(nc->identities[0],\r
@@ -293,10 +299,17 @@ kmsg_cred_completion(kmq_message *m)
                                    ws_tfmt, ARRAYLENGTH(ws_tfmt));\r
                         StringCbPrintf(ws_title, sizeof(ws_title),\r
                                        ws_tfmt, w_idname);\r
+                        khui_alert_set_ctx(alert,\r
+                                           KHUI_SCOPE_IDENT,\r
+                                           nc->identities[0],\r
+                                           KCDB_CREDTYPE_INVALID,\r
+                                           NULL);\r
                     }\r
 \r
                 } else if (nc->subtype == KMSG_CRED_PASSWORD) {\r
 \r
+                    khui_alert_set_type(alert, KHUI_ALERTTYPE_CHPW);\r
+\r
                     cb = sizeof(w_idname);\r
                     if (nc->n_identities == 0 ||\r
                         KHM_FAILED(kcdb_identity_get_name(nc->identities[0],\r
@@ -308,10 +321,17 @@ kmsg_cred_completion(kmq_message *m)
                                    ws_tfmt, ARRAYLENGTH(ws_tfmt));\r
                         StringCbPrintf(ws_title, sizeof(ws_title),\r
                                        ws_tfmt, w_idname);\r
+                        khui_alert_set_ctx(alert,\r
+                                           KHUI_SCOPE_IDENT,\r
+                                           nc->identities[0],\r
+                                           KCDB_CREDTYPE_INVALID,\r
+                                           NULL);\r
                     }\r
 \r
                 } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {\r
 \r
+                    khui_alert_set_type(alert, KHUI_ALERTTYPE_RENEWFAIL);\r
+\r
                     cb = sizeof(w_idname);\r
                     if (nc->ctx.identity == NULL ||\r
                         KHM_FAILED(kcdb_identity_get_name(nc->ctx.identity,\r
@@ -323,6 +343,11 @@ kmsg_cred_completion(kmq_message *m)
                                    ws_tfmt, ARRAYLENGTH(ws_tfmt));\r
                         StringCbPrintf(ws_title, sizeof(ws_title),\r
                                        ws_tfmt, w_idname);\r
+                        khui_alert_set_ctx(alert,\r
+                                           KHUI_SCOPE_IDENT,\r
+                                           nc->ctx.identity,\r
+                                           KCDB_CREDTYPE_INVALID,\r
+                                           NULL);\r
                     }\r
 \r
                 } else {\r
@@ -374,11 +399,6 @@ kmsg_cred_completion(kmq_message *m)
             if (nc->subtype == KMSG_CRED_NEW_CREDS ||\r
                 nc->subtype == KMSG_CRED_PASSWORD) {\r
 \r
-                /*\r
-                if (nc->subtype == KMSG_CRED_NEW_CREDS)\r
-                    khui_context_reset();\r
-                */\r
-\r
                 khm_cred_end_dialog(nc);\r
 \r
             } else if (nc->subtype == KMSG_CRED_RENEW_CREDS) {\r
@@ -1169,6 +1189,7 @@ khm_cred_process_startup_actions(void) {
         /* when we get here, then we are all done with the command\r
            line stuff */\r
         khm_startup.processing = FALSE;\r
+        khm_startup.remote = FALSE;\r
     } while(FALSE);\r
 \r
     if (defident)\r
@@ -1182,7 +1203,9 @@ khm_cred_begin_startup_actions(void) {
     if (khm_startup.seen)\r
         return;\r
 \r
-    if (KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) {\r
+    if (!khm_startup.remote &&\r
+        KHM_SUCCEEDED(khc_open_space(NULL, L"CredWindow", 0, &csp_cw))) {\r
+\r
         khm_int32 t = 0;\r
 \r
         khc_read_int32(csp_cw, L"Autoinit", &t);\r
index 5bdbdb754d088209c2980f6735a1d2b31d825628..a555ebeaa3c02f79392293268cf8a77b44c675ca 100644 (file)
@@ -4618,7 +4618,7 @@ khm_register_credwnd_class(void) {
     wcx.hInstance = khm_hInstance;\r
     wcx.hIcon = NULL;\r
     wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);\r
-    wcx.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);\r
+    wcx.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);\r
     wcx.lpszMenuName = NULL;\r
     wcx.lpszClassName = KHUI_CREDWND_CLASS_NAME;\r
     wcx.hIconSm = NULL;\r
index 203f5b02b0cdb02dcc1288661e4876815bce4ccb..6b79307a29de798b1b93c32329c302a8ca273649 100644 (file)
 #define KHERR_HMODULE khm_hInstance\r
 #define KHERR_FACILITY khm_facility\r
 #define KHERR_FACILITY_ID 3\r
+\r
 #define NOEXPORT\r
 \r
-#include<khdefs.h>\r
-#include<khlist.h>\r
-#include<kherror.h>\r
-#include<kconfig.h>\r
-#include<kcreddb.h>\r
-#include<kmq.h>\r
-#include<khmsgtypes.h>\r
-#include<kmm.h>\r
+#include<netidmgr.h>\r
+\r
 #include<khhelp.h>\r
-#include<khuidefs.h>\r
-#include<utils.h>\r
+#include<intaction.h>\r
+#include<intalert.h>\r
 \r
 #include<resource.h>\r
 #include<credfuncs.h>\r
index 862872daea89abacbd6c295e7f3beb6b6fc9ad04..6fadebb945ff1cdb8ce179f96d5c6441a4ad356a 100644 (file)
@@ -34,7 +34,7 @@ END
 \r
 2 TEXTINCLUDE \r
 BEGIN\r
-    "#include ""afxres.h""\r\n"\r
+    "#include ""afxres.h""\r\0"\r
 END\r
 \r
 3 TEXTINCLUDE \r
@@ -174,28 +174,21 @@ BEGIN
     LTEXT           "TplInput",IDC_NC_TPL_INPUT,54,7,240,13,NOT WS_VISIBLE | WS_BORDER\r
     LTEXT           "TplLabelLg",IDC_NC_TPL_LABEL_LG,7,33,146,10,NOT WS_VISIBLE | WS_BORDER\r
     LTEXT           "TplInputLg",IDC_NC_TPL_INPUT_LG,155,31,139,13,NOT WS_VISIBLE | WS_BORDER\r
-    LTEXT           "&Credentials",IDC_NC_CREDTEXT_LABEL,7,66,41,10,NOT WS_GROUP\r
-    CONTROL         "",IDC_NC_CREDTEXT,"KhmHtWnd",WS_TABSTOP,54,65,240,73,WS_EX_CLIENTEDGE\r
-    PUSHBUTTON      "&Ok",IDOK,57,142,89,18,WS_DISABLED\r
-    PUSHBUTTON      "&Cancel",IDCANCEL,158,142,54,18\r
-    PUSHBUTTON      "&Options >>",IDC_NC_OPTIONS,223,142,71,18\r
+    LTEXT           "&Credentials",IDC_NC_CREDTEXT_LABEL,7,66,41,10,NOT WS_VISIBLE | NOT WS_GROUP\r
+    CONTROL         "",IDC_NC_CREDTEXT,"KhmHtWnd",NOT WS_VISIBLE | WS_TABSTOP,54,65,240,95,WS_EX_CLIENTEDGE\r
+    PUSHBUTTON      "&Ok",IDOK,101,142,89,18,WS_DISABLED\r
+    PUSHBUTTON      "&Cancel",IDCANCEL,198,142,54,18\r
+    PUSHBUTTON      "&>>",IDC_NC_ADVANCED,260,142,34,18\r
 END\r
 \r
-IDD_NC_BBAR DIALOGEX 0, 0, 60, 181\r
-STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU\r
-EXSTYLE WS_EX_CONTROLPARENT\r
-FONT 8, "MS Shell Dlg", 400, 0, 0x1\r
-BEGIN\r
-    DEFPUSHBUTTON   "&Ok",IDOK,0,7,53,41,WS_DISABLED\r
-    PUSHBUTTON      "&Cancel",IDCANCEL,0,58,53,19\r
-    PUSHBUTTON      "&Help",IDC_NC_HELP,0,155,53,19\r
-END\r
-\r
-IDD_NC_TS DIALOGEX 0, 0, 300, 15\r
+IDD_NC_BBAR DIALOGEX 0, 0, 66, 181\r
 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU\r
 EXSTYLE WS_EX_CONTROLPARENT\r
 FONT 8, "MS Shell Dlg", 400, 0, 0x1\r
 BEGIN\r
+    DEFPUSHBUTTON   "&Ok",IDOK,7,7,52,41,WS_DISABLED\r
+    PUSHBUTTON      "&Cancel",IDCANCEL,7,58,52,19\r
+    PUSHBUTTON      "&Help",IDC_NC_HELP,7,155,52,19\r
 END\r
 \r
 IDD_PP_IDENT DIALOGEX 0, 0, 235, 156\r
@@ -414,7 +407,8 @@ BEGIN
 \r
     IDD_NC_BBAR, DIALOG\r
     BEGIN\r
-        RIGHTMARGIN, 53\r
+        LEFTMARGIN, 7\r
+        RIGHTMARGIN, 59\r
         TOPMARGIN, 7\r
         BOTTOMMARGIN, 174\r
     END\r
@@ -560,7 +554,7 @@ STRINGTABLE
 BEGIN\r
     IDS_MENU_OPTIONS        "&Options"\r
     IDS_MENU_HELP           "&Help"\r
-    IDS_ACTION_PROPERTIES   "&Properties ..."\r
+    IDS_ACTION_PROPERTIES   "&Properties"\r
     IDS_ACTION_EXIT         "E&xit"\r
     IDS_CFG_ROOT_NAME       "Network Identity Manager"\r
     IDS_ACTION_SET_DEF_ID   "Set as &default"\r
@@ -568,9 +562,9 @@ BEGIN
     IDS_CFG_ROOT_TITLE      "Network Identity Manager Configuration"\r
     IDS_CFG_GENERAL_SHORT   "General"\r
     IDS_ACTION_NEW_CRED     "&New credentials ..."\r
-    IDS_ACTION_PASSWD_ID    "Change &password ..."\r
+    IDS_ACTION_PASSWD_ID    "Change &password"\r
     IDS_ACTION_CHOOSE_COLS  "View columns"\r
-    IDS_ACTION_DEBUG_WINDOW "Debug window ..."\r
+    IDS_ACTION_DEBUG_WINDOW "Debug window"\r
     IDS_ACTION_VIEW_REFRESH "Refresh view"\r
     IDS_MENU_LAYOUT         "Layout"\r
     IDS_MENU_TOOLBARS       "Toolbars"\r
@@ -582,13 +576,13 @@ BEGIN
     IDS_ACTION_LAYOUT_TYPE  "By type"\r
     IDS_ACTION_LAYOUT_LOC   "By location"\r
     IDS_ACTION_TB_STANDARD  "Standard"\r
-    IDS_ACTION_OPT_KHIM     "General ..."\r
-    IDS_ACTION_OPT_IDENTS   "Identities ..."\r
-    IDS_ACTION_OPT_NOTIF    "Notifications ..."\r
+    IDS_ACTION_OPT_KHIM     "General"\r
+    IDS_ACTION_OPT_IDENTS   "Identities"\r
+    IDS_ACTION_OPT_NOTIF    "Notifications"\r
     IDS_ACTION_HELP_CTX     "Help Index"\r
-    IDS_ACTION_HELP_CONTENTS "Contents ..."\r
-    IDS_ACTION_HELP_INDEX   "Index ..."\r
-    IDS_ACTION_HELP_ABOUT   "About Network Identity Manager ..."\r
+    IDS_ACTION_HELP_CONTENTS "Contents"\r
+    IDS_ACTION_HELP_INDEX   "Index"\r
+    IDS_ACTION_HELP_ABOUT   "About Network Identity Manager"\r
     IDS_CFG_GENERAL_LONG    "General options"\r
     IDS_SAMPLE_STRING       "Wxy"\r
     IDS_NO_CREDS            "<large><center>You currently have no credentials.Click <a id=""NewCreds"">here</a> to obtain new credentials.</center></large>"\r
@@ -607,7 +601,7 @@ BEGIN
     IDS_WTPOST_INIT_CREDS   " - Initial credentials"\r
     IDS_WTPOST_NEW_CREDS    " - New credentials"\r
     IDS_ACTION_RENEW_CRED   "R&enew credentials"\r
-    IDS_ACTION_DESTROY_CRED "De&stroy credentials ..."\r
+    IDS_ACTION_DESTROY_CRED "De&stroy credentials"\r
     IDS_DEFAULT_FONT        "MS Shell Dlg"\r
     IDS_NC_CREDTEXT_TABS    "<settab pos=""15""><settab pos=""30""><settab pos=""45"">"\r
     IDS_NOTIFY_PREFIX       "Network Identity Manager - "\r
@@ -629,7 +623,7 @@ BEGIN
     IDS_PROP_COL_VALUE      "Value"\r
     IDS_NC_NEW_IDENT        "( New identity ... )"\r
     IDS_NC_CREDTEXT_ID_CHECKING "<font color=""grey"">%s (Checking...)</font>"\r
-    IDS_ACTION_OPEN_APP     "Open Network Identity Manager ..."\r
+    IDS_ACTION_OPEN_APP     "Open Network Identity Manager"\r
     IDS_CTX_NEW_IDENT       "Obaining new identity"\r
     IDS_CTX_NEW_CREDS       "Obtaining new credentials"\r
     IDS_CTX_RENEW_CREDS     "Renewing credentials"\r
@@ -688,11 +682,11 @@ BEGIN
     IDS_PISTATE_EXIT        "Stopped"\r
     IDS_CTX_PASSWORD        "Changing password"\r
     IDS_WT_PASSWORD         "Changing password"\r
-    IDS_WTPOST_PASSWORD     "  - Changing password"\r
+    IDS_WTPOST_PASSWORD     " - Changing password"\r
     IDS_CTX_PROC_PASSWORD   "Changing password for %1!s!"\r
     IDS_NC_PWD_FAILED_TITLE "Failed to change password"\r
-    IDS_CMDLINE_HELP        "Command line options for Network Identity Manager are :\n\n-a or --autoinit: Auto initialize credentials\n-i or --kinit: Obtain new credentials\n-d or --destroy: Destroy default identity\n-r or --renew: Renew all credentials"\r
-    IDS_PACTION_NEXT        "Next alert..."\r
+    IDS_CMDLINE_HELP        "Command line options for Network Identity Manager are :\n\n-a or --autoinit: Auto initialize credentials\n-i or --kinit: Obtain new credentials\n-d or --destroy: Destroy default identity\n-r or --renew: Renew all credentials\n-x or --exit: Exit the running instance of Network Identity Manager"\r
+    IDS_PACTION_NEXT        "Next alert"\r
     IDS_ERR_TITLE_NO_IDENTPRO "Cannot proceed without identity provider"\r
 END\r
 \r
@@ -703,7 +697,7 @@ BEGIN
                             "This is quite possibly caused by the identity provider module failing to load properly."\r
     IDS_NC_REN_FAILED_TITLE "Failed to renew credentials"\r
     IDS_CW_DEFAULT          "(Default)"\r
-    IDS_ACTION_OPT_PLUGINS  "Plugins ..."\r
+    IDS_ACTION_OPT_PLUGINS  "Plugins"\r
     IDS_NC_SETDEF           "&Set as default identity"\r
     IDS_NC_ID_DEF           "<p>This identity is the default</p>"\r
     IDS_NC_ID_WDEF          "<p>Will be the default. (<a id=""NotDef"">Don't make default</a>)</p>"\r
@@ -745,7 +739,7 @@ BEGIN
     IDS_APR_SAMPLE_TEXT_NORMAL "Sample text (normal). 01234567890"\r
     IDS_CFG_APPEAR_SHORT    "Appearance"\r
     IDS_CFG_APPEAR_LONG     "Appearance"\r
-    IDS_ACTION_OPT_APPEAR   "Appearance ..."\r
+    IDS_ACTION_OPT_APPEAR   "Appearance"\r
     IDS_APR_SAMPLE_TEXT_SEL "Sample text (selected). 01234567890"\r
     IDS_CFG_IDNAME_INV      "The identity name %s is invalid."\r
     IDS_CFG_IDNAME_PRB      "Can't add new identity %s"\r
@@ -782,13 +776,18 @@ STRINGTABLE
 BEGIN\r
     IDS_NC_REN_FAILED_TITLE_I "Failed to renew creds for %s"\r
     IDS_CFG_IDNAME_NON      "No identity selected.  Please select an identity and try again."\r
-    IDS_MENU_DESTROY_CRED   "Destroy ..."\r
-    IDS_MENU_RENEW_CRED     "Renew ..."\r
+    IDS_MENU_DESTROY_CRED   "Destroy"\r
+    IDS_MENU_RENEW_CRED     "Renew"\r
     IDS_ACTION_DESTROY_ALL  "All identities"\r
     IDS_ACTION_RENEW_ALL    "All identities"\r
     IDS_IDACTION_RENEW      "Renew credentials for %s"\r
     IDS_IDACTION_DESTROY    "Destroy credentials for %s"\r
     IDS_CTX_DESTROY_ID      "Destroying identity %1!s!"\r
+    IDS_NCN_IDENT_INVALID   "Identity %s is invalid."\r
+    IDS_NCN_IDENT_CHECKING  "Checking identity %s ..."\r
+    IDS_NCN_IDENT_UNKNOWN   "Validity of identity %s coudn't be determined."\r
+    IDS_REMOTE_FAIL         "The instance of Network Identity Manager that is already running is not responding to the remote request properly.  Please check if you are running the latest version of Network Identity Manger software included with MIT Kerberos for Windows."\r
+    IDS_REMOTE_FAIL_TITLE   "Failed to communicate with Network Identity Manager"\r
 END\r
 \r
 #endif    // English (U.S.) resources\r
@@ -806,3 +805,4 @@ END
 /////////////////////////////////////////////////////////////////////////////\r
 #endif    // not APSTUDIO_INVOKED\r
 \r
+\r
index c05fa6b7fa4393b5eeef767a80f224108604b775..37ebe125fad42b967bdeab6fff38c03138ed8153 100644 (file)
 \r
 #if DEBUG\r
 #include<assert.h>\r
+\r
+#if defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL)\r
+/* needed for writing out leaked allocation and handle report */\r
+#include<stdio.h>\r
+#endif\r
+\r
 #endif\r
 \r
 HINSTANCE khm_hInstance;\r
@@ -106,6 +112,13 @@ void khm_parse_commandline(void) {
                  !wcscmp(wargs[i], L"-autoinit")) {\r
             khm_startup.autoinit = TRUE;\r
         }\r
+        else if (!wcscmp(wargs[i], L"-x") ||\r
+                 !wcscmp(wargs[i], L"--exit") ||\r
+                 !wcscmp(wargs[i], L"-exit")) {\r
+            khm_startup.exit = TRUE;\r
+            khm_startup.remote_exit = TRUE;\r
+            khm_startup.no_main_window = TRUE;\r
+        }\r
         else {\r
             wchar_t help[2048];\r
 \r
@@ -122,7 +135,8 @@ void khm_parse_commandline(void) {
     /* special: always enable renew when other options aren't specified */\r
     if (!khm_startup.exit &&\r
         !khm_startup.destroy &&\r
-        !khm_startup.init)\r
+        !khm_startup.init &&\r
+        !khm_startup.remote_exit)\r
         khm_startup.renew = TRUE;\r
 }\r
 \r
@@ -283,17 +297,25 @@ void khm_leave_modal(void) {
 \r
     } else {\r
 \r
+        HWND last_dialog = NULL;\r
+\r
         /* we are exiting a modal loop. */\r
 \r
         for (i=0; i < n_khui_dialogs; i++) {\r
             if(khui_dialogs[i].hwnd != khui_modal_dialog) {\r
                 EnableWindow(khui_dialogs[i].hwnd, khui_dialogs[i].active);\r
+                last_dialog = khui_dialogs[i].hwnd;\r
             }\r
         }\r
 \r
         EnableWindow(khm_hwnd_main, TRUE);\r
 \r
         khui_modal_dialog = NULL;\r
+\r
+        if(last_dialog)\r
+            SetActiveWindow(last_dialog);\r
+        else\r
+            SetActiveWindow(khm_hwnd_main);\r
     }\r
 }\r
 \r
@@ -502,6 +524,20 @@ void khm_load_default_modules(void) {
     kmm_load_default_modules();\r
 }\r
 \r
+int version_compare(const khm_version * v1, const khm_version * v2) {\r
+\r
+    if (v1->major != v2->major)\r
+        return ((int)v1->major) - ((int)v2->major);\r
+\r
+    if (v1->minor != v2->minor)\r
+        return ((int)v1->minor) - ((int)v2->minor);\r
+\r
+    if (v1->patch != v2->patch)\r
+        return ((int)v1->patch) - ((int)v2->patch);\r
+\r
+    return ((int)v1->aux - ((int)v2->aux));\r
+}\r
+\r
 int WINAPI WinMain(HINSTANCE hInstance,\r
                    HINSTANCE hPrevInstance,\r
                    LPSTR lpCmdLine,\r
@@ -510,6 +546,7 @@ int WINAPI WinMain(HINSTANCE hInstance,
     int rv = 0;\r
     HANDLE h_appmutex;\r
     BOOL slave = FALSE;\r
+    int mutex_retries = 5;\r
 \r
     khm_hInstance = hInstance;\r
     khm_nCmdShow = nCmdShow;\r
@@ -519,6 +556,11 @@ int WINAPI WinMain(HINSTANCE hInstance,
     if (khm_startup.error_exit)\r
         return 0;\r
 \r
+ _retry_mutex:\r
+\r
+    if (--mutex_retries < 0)\r
+        return 2;\r
+\r
     h_appmutex = CreateMutex(NULL, FALSE, L"Local\\NetIDMgr_GlobalAppMutex");\r
     if (h_appmutex == NULL)\r
         return 5;\r
@@ -531,6 +573,8 @@ int WINAPI WinMain(HINSTANCE hInstance,
 \r
     if(!slave) {\r
 \r
+        PDESCTHREAD(L"UI", L"App");\r
+\r
         /* set this so that we don't accidently invoke an API that\r
            inadvertently puts up the new creds dialog at an\r
            inopportune moment, like, say, during the new creds dialog\r
@@ -545,8 +589,8 @@ int WINAPI WinMain(HINSTANCE hInstance,
         /* we only open a main window if this is the only instance \r
            of the application that is running. */\r
         kmq_init();\r
-        kmm_init();\r
         khm_init_gui();\r
+        kmm_init();\r
 \r
         kmq_set_completion_handler(KMSG_CRED, kmsg_cred_completion);\r
 \r
@@ -574,9 +618,9 @@ int WINAPI WinMain(HINSTANCE hInstance,
 \r
         khm_exit_request_daemon();\r
 \r
+        kmm_exit();\r
         khm_exit_gui();\r
         khm_unregister_window_classes();\r
-        kmm_exit();\r
         kmq_exit();\r
 \r
         CloseHandle(h_appmutex);\r
@@ -588,6 +632,9 @@ int WINAPI WinMain(HINSTANCE hInstance,
         DWORD tid;\r
         void * xfer;\r
         khm_query_app_version query_app_version;\r
+        khm_version v;\r
+        BOOL use_cmd_v2 = TRUE;\r
+        khm_size cb = 0;\r
 \r
         CloseHandle(h_appmutex);\r
 \r
@@ -598,11 +645,21 @@ int WINAPI WinMain(HINSTANCE hInstance,
                 break;\r
 \r
             retries--;\r
+\r
+            /* if the app was just starting, we might have to wait\r
+               till the main window is created. */\r
+\r
             Sleep(1000);\r
         }\r
 \r
-        if (!hwnd)\r
-            return 2;\r
+        if (!hwnd) {\r
+\r
+            /* if the app was just exiting, we might see the mutex but\r
+               not the window.  We go back and check if the mutex is\r
+               still there. */\r
+\r
+            goto _retry_mutex;\r
+        }\r
 \r
         /* first check if the remote instance supports a version\r
            query */\r
@@ -647,6 +704,31 @@ int WINAPI WinMain(HINSTANCE hInstance,
         CloseHandle(hmap);\r
         hmap = NULL;\r
 \r
+        if (query_app_version.magic != KHM_QUERY_APP_VER_MAGIC ||\r
+            query_app_version.code != KHM_ERROR_SUCCESS) {\r
+\r
+            /* We managed to communicate with the remote instance, but\r
+               it didn't send us useful information.  The remote\r
+               instance is not running an actual NetIDMgr instance.\r
+               However, it owns a top level window that was registered\r
+               with our classname.  This instance won't function\r
+               properly if we let it proceed.\r
+            */\r
+\r
+            wchar_t error_msg[1024];\r
+            wchar_t error_title[256];\r
+\r
+            LoadString(khm_hInstance, IDS_REMOTE_FAIL_TITLE,\r
+                       error_title, ARRAYLENGTH(error_title));\r
+            LoadString(khm_hInstance, IDS_REMOTE_FAIL,\r
+                       error_msg, ARRAYLENGTH(error_msg));\r
+\r
+            MessageBox(NULL, error_msg, error_title,\r
+                       MB_OK);\r
+            \r
+            goto done_with_remote;\r
+        }\r
+\r
         if (query_app_version.code == KHM_ERROR_SUCCESS &&\r
             query_app_version.request_swap) {\r
             /* the request for swap was granted.  We can now\r
@@ -656,43 +738,149 @@ int WINAPI WinMain(HINSTANCE hInstance,
             goto _start_app;\r
         }\r
 \r
+        /* Now we can work on sending the command-line to the remote\r
+           instance.  However we need to figure out which version of\r
+           the startup structure it supports. */\r
+        v.major = 1;\r
+        v.minor = 2;\r
+        v.patch = 0;\r
+        v.aux = 0;\r
+\r
+        if (version_compare(&query_app_version.ver_remote, &app_version) == 0 ||\r
+            version_compare(&query_app_version.ver_remote, &v) > 0)\r
+            use_cmd_v2 = TRUE;\r
+        else\r
+            use_cmd_v2 = FALSE;\r
+\r
         StringCbPrintf(mapname, sizeof(mapname),\r
                        COMMANDLINE_MAP_FMT,\r
                        (tid = GetCurrentThreadId()));\r
 \r
+        cb = max(sizeof(struct tag_khm_startup_options_v1),\r
+                 sizeof(struct tag_khm_startup_options_v2));\r
+\r
+        cb = UBOUNDSS(cb, 4096, 4096);\r
+\r
+#ifdef DEBUG\r
+        assert(cb >= 4096);\r
+#endif\r
+\r
         hmap = CreateFileMapping(INVALID_HANDLE_VALUE,\r
                                  NULL,\r
                                  PAGE_READWRITE,\r
                                  0,\r
-                                 4096,\r
+                                 (DWORD) cb,\r
                                  mapname);\r
 \r
         if (hmap == NULL)\r
             return 3;\r
 \r
-        xfer = MapViewOfFile(hmap,\r
-                             FILE_MAP_WRITE,\r
-                             0, 0,\r
-                             sizeof(khm_startup));\r
+        /* make the call */\r
 \r
-        if (xfer) {\r
-            memcpy(xfer, &khm_startup, sizeof(khm_startup));\r
+        if (use_cmd_v2) {\r
+            /* use the v2 structure */\r
+            struct tag_khm_startup_options_v2 v2opt;\r
 \r
-            SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE,\r
-                        0, (LPARAM) tid);\r
+            ZeroMemory(&v2opt, sizeof(v2opt));\r
+\r
+            v2opt.magic = STARTUP_OPTIONS_MAGIC;\r
+            v2opt.cb_size = sizeof(v2opt);\r
+\r
+            v2opt.init = khm_startup.init;\r
+            v2opt.import = khm_startup.import;\r
+            v2opt.renew = khm_startup.renew;\r
+            v2opt.destroy = khm_startup.destroy;\r
+\r
+            v2opt.autoinit = khm_startup.autoinit;\r
+            v2opt.remote_exit = khm_startup.remote_exit;\r
+\r
+            v2opt.code = KHM_ERROR_NOT_IMPLEMENTED;\r
+\r
+            xfer = MapViewOfFile(hmap,\r
+                                 FILE_MAP_WRITE,\r
+                                 0, 0,\r
+                                 sizeof(v2opt));\r
+\r
+            if (xfer) {\r
+                memcpy(xfer, &v2opt, sizeof(v2opt));\r
+\r
+                SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE_V2,\r
+                            0, (LPARAM) tid);\r
+\r
+                memcpy(&v2opt, xfer, sizeof(v2opt));\r
+\r
+                /* If the request looks like it wasn't processed, we\r
+                   fallback to the v1 request. */\r
+\r
+                if (v2opt.code == KHM_ERROR_NOT_IMPLEMENTED)\r
+                    use_cmd_v2 = FALSE;\r
+\r
+                UnmapViewOfFile(xfer);\r
+                xfer = NULL;\r
+            }\r
         }\r
 \r
-        if (xfer)\r
-            UnmapViewOfFile(xfer);\r
+        if (!use_cmd_v2) {\r
+            /* use the v1 structure */\r
+\r
+            struct tag_khm_startup_options_v1 v1opt;\r
+\r
+            ZeroMemory(&v1opt, sizeof(v1opt));\r
+\r
+            v1opt.init = khm_startup.init;\r
+            v1opt.import = khm_startup.import;\r
+            v1opt.renew = khm_startup.renew;\r
+            v1opt.destroy = khm_startup.destroy;\r
+            v1opt.autoinit = khm_startup.autoinit;\r
+\r
+            xfer = MapViewOfFile(hmap,\r
+                                 FILE_MAP_WRITE,\r
+                                 0, 0,\r
+                                 sizeof(v1opt));\r
+\r
+            if (xfer) {\r
+                memcpy(xfer, &v1opt, sizeof(v1opt));\r
+\r
+                SendMessage(hwnd, WM_KHUI_ASSIGN_COMMANDLINE_V1,\r
+                            0, (LPARAM) tid);\r
+\r
+                UnmapViewOfFile(xfer);\r
+                xfer = NULL;\r
+            }\r
+        }\r
+\r
+    done_with_remote:\r
 \r
         if (hmap)\r
             CloseHandle(hmap);\r
     }\r
 \r
-#if defined(DEBUG) && ( defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL))\r
-    /* writes a report of memory leaks to the specified file.  Should\r
-       only be enabled on development versions. */\r
-    PDUMP("memleak.txt");\r
+#if defined(DEBUG) && (defined(KH_BUILD_PRIVATE) || defined(KH_BUILD_SPECIAL))\r
+    {\r
+        FILE * f = NULL;\r
+\r
+        KHMEXP void KHMAPI khcint_dump_handles(FILE * f);\r
+        KHMEXP void KHMAPI perf_dump(FILE * f);\r
+        KHMEXP void KHMAPI kmqint_dump(FILE * f);\r
+\r
+#if _MSC_VER >= 1400\r
+        if (fopen_s(&f, "memleak.txt", "w") != 0)\r
+            goto done_with_dump;\r
+#else\r
+        f = fopen("memleak.txt", "w");\r
+        if (f == NULL)\r
+            goto done_with_dump;\r
+#endif\r
+\r
+        perf_dump(f);\r
+        khcint_dump_handles(f);\r
+        kmqint_dump(f);\r
+\r
+        fclose(f);\r
+\r
+    done_with_dump:\r
+        ;\r
+    }\r
 #endif\r
 \r
     return rv;\r
index c99cafafd26df22776ce8dc8dbcf0353dddfe517..e4267035e05c2711069dcff9b691474752515888 100644 (file)
@@ -166,12 +166,7 @@ static void refresh_menu_item(HMENU hm, khui_action * act,
            to add it. */\r
         mii.fMask = MIIM_STATE;\r
         if (!GetMenuItemInfo(hm, act->cmd, FALSE, &mii)) {\r
-            /* the 1000 is fairly arbitrary, but there should be much\r
-               less menu items on a menu anyway.  If there are that\r
-               many items, the system would be unusable to the extent\r
-               that the order of the items would be the least of our\r
-               worries. */\r
-            add_action_to_menu(hm, act, 1000, flags);\r
+            add_action_to_menu(hm, act, idx, flags);\r
             return;\r
         }\r
 \r
@@ -197,6 +192,7 @@ static void refresh_menu_item(HMENU hm, khui_action * act,
         SetMenuItemInfo(hm, act->cmd, FALSE, &mii);\r
 \r
         def = khui_find_menu(act->cmd);\r
+\r
         if(def) {\r
             MENUITEMINFO mii2;\r
 \r
index 71e09dfacc410a0b5b727cfd1ee81039321e61b5..05e615786cbbd6a754906d7c21b98bc685ee9106 100644 (file)
@@ -220,6 +220,7 @@ khm_main_wnd_proc(HWND hwnd,
         khm_pre_shutdown();\r
         kmq_unsubscribe_hwnd(KMSG_ACT, hwnd);\r
         kmq_unsubscribe_hwnd(KMSG_CRED, hwnd);\r
+        kmq_unsubscribe_hwnd(KMSG_KMM, hwnd);\r
         HtmlHelp(NULL, NULL, HH_CLOSE_ALL, 0);\r
         PostQuitMessage(0);\r
         break;\r
@@ -286,15 +287,15 @@ khm_main_wnd_proc(HWND hwnd,
 \r
         case KHUI_ACTION_EXIT:\r
             DestroyWindow(hwnd);\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_OPEN_APP:\r
             khm_show_main_window();\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_CLOSE_APP:\r
             khm_hide_main_window();\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_OPT_KHIM: {\r
             khui_config_node node = NULL;\r
@@ -302,7 +303,7 @@ khm_main_wnd_proc(HWND hwnd,
             khui_cfg_open(NULL, L"KhmGeneral", &node);\r
             khm_show_config_pane(node);\r
         }\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_OPT_IDENTS: {\r
             khui_config_node node = NULL;\r
@@ -310,7 +311,7 @@ khm_main_wnd_proc(HWND hwnd,
             khui_cfg_open(NULL, L"KhmIdentities", &node);\r
             khm_show_config_pane(node);\r
         }\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_OPT_APPEAR: {\r
             khui_config_node node = NULL;\r
@@ -318,7 +319,7 @@ khm_main_wnd_proc(HWND hwnd,
             khui_cfg_open(NULL, L"KhmAppear", &node);\r
             khm_show_config_pane(node);\r
         }\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_OPT_NOTIF: {\r
             khui_config_node node = NULL;\r
@@ -326,7 +327,7 @@ khm_main_wnd_proc(HWND hwnd,
             khui_cfg_open(NULL, L"KhmNotifications", &node);\r
             khm_show_config_pane(node);\r
         }\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_OPT_PLUGINS: {\r
             khui_config_node node = NULL;\r
@@ -334,27 +335,27 @@ khm_main_wnd_proc(HWND hwnd,
             khui_cfg_open(NULL, L"KhmPlugins", &node);\r
             khm_show_config_pane(node);\r
         }\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_HELP_CTX:\r
             khm_html_help(khm_hwnd_main, NULL, HH_HELP_CONTEXT, IDH_WELCOME);\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_HELP_CONTENTS:\r
             khm_html_help(khm_hwnd_main, NULL, HH_DISPLAY_TOC, 0);\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_HELP_INDEX:\r
             khm_html_help(khm_hwnd_main, NULL, HH_DISPLAY_INDEX, (DWORD_PTR) L"");\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_HELP_ABOUT:\r
             khm_create_about_window();\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_IMPORT:\r
             khm_cred_import();\r
-            break;\r
+            return 0;\r
 \r
         case KHUI_ACTION_PROPERTIES:\r
             /* properties are not handled by the main window.\r
@@ -365,7 +366,7 @@ khm_main_wnd_proc(HWND hwnd,
 \r
         case KHUI_ACTION_UICB:\r
             khm_ui_cb(lParam);\r
-            break;\r
+            return 0;\r
 \r
             /* menu commands */\r
         case KHUI_PACTION_MENU:\r
@@ -445,6 +446,7 @@ khm_main_wnd_proc(HWND hwnd,
                 act = khui_find_action(LOWORD(wParam));\r
                 if (act && act->listener) {\r
                     kmq_post_sub_msg(act->listener, KMSG_ACT, KMSG_ACT_ACTIVATE, act->cmd, NULL);\r
+                    return 0;\r
                 }\r
             }\r
         }\r
@@ -518,6 +520,8 @@ khm_main_wnd_proc(HWND hwnd,
                      MW_RESIZE_TIMER,\r
                      MW_RESIZE_TIMEOUT,\r
                      NULL);\r
+\r
+            return 0;\r
         }\r
         break;\r
 \r
@@ -550,14 +554,19 @@ khm_main_wnd_proc(HWND hwnd,
                 }\r
                 khc_close_space(csp_cw);\r
             }\r
+\r
+            return 0;\r
+\r
         } else if (wParam == MW_REFRESH_TIMER) {\r
             kmq_post_message(KMSG_CRED, KMSG_CRED_REFRESH, 0, 0);\r
+\r
+            return 0;\r
+\r
         }\r
         break;\r
 \r
     case WM_MENUSELECT:\r
         return khm_menu_handle_select(wParam, lParam);\r
-        break;\r
 \r
     case KMQ_WM_DISPATCH:\r
         {\r
@@ -611,13 +620,15 @@ khm_main_wnd_proc(HWND hwnd,
             }\r
             return kmq_wm_end(m, rv);\r
         }\r
-        break;\r
+        return 0;\r
 \r
-    case WM_KHUI_ASSIGN_COMMANDLINE:\r
+    case WM_KHUI_ASSIGN_COMMANDLINE_V1:\r
         {\r
             HANDLE hmap;\r
             void * xfer;\r
             wchar_t mapname[256];\r
+            struct tag_khm_startup_options_v1 * pv1opt;\r
+            int code = KHM_ERROR_SUCCESS;\r
 \r
             StringCbPrintf(mapname, sizeof(mapname),\r
                            COMMANDLINE_MAP_FMT, (DWORD) lParam);\r
@@ -628,27 +639,110 @@ khm_main_wnd_proc(HWND hwnd,
                 return 1;\r
 \r
             xfer = MapViewOfFile(hmap, FILE_MAP_READ, 0, 0,\r
-                                 sizeof(khm_startup));\r
+                                 sizeof(*pv1opt));\r
 \r
             if (xfer) {\r
-                memcpy(&khm_startup, xfer, sizeof(khm_startup));\r
+                pv1opt = (struct tag_khm_startup_options_v1 *) xfer;\r
+\r
+                khm_startup.init = pv1opt->init;\r
+                khm_startup.import = pv1opt->import;\r
+                khm_startup.renew = pv1opt->renew;\r
+                khm_startup.destroy = pv1opt->destroy;\r
+\r
+                khm_startup.autoinit = pv1opt->autoinit;\r
+                khm_startup.error_exit = FALSE;\r
+\r
+                khm_startup.no_main_window = FALSE;\r
+                khm_startup.remote_exit = FALSE;\r
 \r
                 UnmapViewOfFile(xfer);\r
+            } else {\r
+                code = KHM_ERROR_NOT_FOUND;\r
             }\r
 \r
             CloseHandle(hmap);\r
 \r
             if(InSendMessage())\r
-                ReplyMessage(0);\r
+                ReplyMessage(code);\r
 \r
-            khm_startup.exit = FALSE;\r
+            if (code == KHM_ERROR_SUCCESS) {\r
+                khm_startup.exit = FALSE;\r
 \r
-            khm_startup.seen = FALSE;\r
-            khm_startup.processing = FALSE;\r
+                khm_startup.seen = FALSE;\r
+                khm_startup.remote = TRUE;\r
+#ifdef DEBUG\r
+                assert(!khm_startup.processing);\r
+#endif\r
+                khm_startup.processing = FALSE;\r
 \r
-            khm_cred_begin_startup_actions();\r
+                khm_cred_begin_startup_actions();\r
+            }\r
+\r
+            return code;\r
+        }\r
+\r
+    case WM_KHUI_ASSIGN_COMMANDLINE_V2:\r
+        {\r
+            HANDLE hmap;\r
+            void * xfer;\r
+            wchar_t mapname[256];\r
+            struct tag_khm_startup_options_v2 *pv2opt;\r
+            int code = KHM_ERROR_SUCCESS;\r
+\r
+            StringCbPrintf(mapname, sizeof(mapname),\r
+                           COMMANDLINE_MAP_FMT, (DWORD) lParam);\r
+\r
+            hmap = OpenFileMapping(FILE_MAP_WRITE, FALSE, mapname);\r
+\r
+            if (hmap == NULL)\r
+                return 1;\r
+\r
+            xfer = MapViewOfFile(hmap, FILE_MAP_WRITE, 0, 0,\r
+                                 sizeof(*pv2opt));\r
+\r
+            if (xfer) {\r
+                pv2opt = (struct tag_khm_startup_options_v2 *) xfer;\r
+\r
+                if (pv2opt->magic != STARTUP_OPTIONS_MAGIC ||\r
+                    pv2opt->cb_size != sizeof(*pv2opt)) {\r
+                    code = KHM_ERROR_INVALID_PARAM;\r
+                    goto done_with_v2_opt;\r
+                }\r
+\r
+                khm_startup.init = pv2opt->init;\r
+                khm_startup.import = pv2opt->import;\r
+                khm_startup.renew = pv2opt->renew;\r
+                khm_startup.destroy = pv2opt->destroy;\r
+\r
+                khm_startup.autoinit = pv2opt->autoinit;\r
+                khm_startup.exit = pv2opt->remote_exit;\r
+\r
+                pv2opt->code = KHM_ERROR_SUCCESS;\r
+\r
+            done_with_v2_opt:\r
+                UnmapViewOfFile(xfer);\r
+            } else {\r
+                code = KHM_ERROR_NOT_FOUND;\r
+            }\r
+\r
+            CloseHandle(hmap);\r
+\r
+            if(InSendMessage())\r
+                ReplyMessage(code);\r
+\r
+            if (code == KHM_ERROR_SUCCESS) {\r
+                khm_startup.seen = FALSE;\r
+                khm_startup.remote = TRUE;\r
+#ifdef DEBUG\r
+                assert(!khm_startup.processing);\r
+#endif\r
+                khm_startup.processing = FALSE;\r
+\r
+                khm_cred_begin_startup_actions();\r
+            }\r
+\r
+            return code;\r
         }\r
-        break;\r
 \r
     case WM_KHUI_QUERY_APP_VERSION:\r
         {\r
@@ -676,7 +770,7 @@ khm_main_wnd_proc(HWND hwnd,
 \r
             CloseHandle(hmap);\r
         }\r
-        break;\r
+        return 0;\r
 \r
     }\r
     return DefWindowProc(hwnd,uMsg,wParam,lParam);\r
@@ -765,12 +859,80 @@ khm_create_main_window_controls(HWND hwnd_main) {
     khm_hwnd_main_cred = khm_create_credwnd(hwnd_main);\r
 }\r
 \r
+void\r
+khm_adjust_window_dimensions_for_display(RECT * pr) {\r
+\r
+    HMONITOR hmon;\r
+    RECT     rm;\r
+    long x, y, width, height;\r
+\r
+    x = pr->left;\r
+    y = pr->top;\r
+    width = pr->right - pr->left;\r
+    height = pr->bottom - pr->top;\r
+\r
+    /* if the rect doesn't intersect with the display area of any\r
+       monitor, we just default to the primary monitor. */\r
+    hmon = MonitorFromRect(pr, MONITOR_DEFAULTTOPRIMARY);\r
+\r
+    if (hmon == NULL) {\r
+        /* huh? we'll just center this on the primary screen */\r
+        goto nomonitor;\r
+    } else {\r
+        MONITORINFO mi;\r
+\r
+        ZeroMemory(&mi, sizeof(mi));\r
+        mi.cbSize = sizeof(mi);\r
+\r
+        if (!GetMonitorInfo(hmon, &mi))\r
+            goto nomonitor;\r
+\r
+        CopyRect(&rm, &mi.rcWork);\r
+\r
+        goto adjust_dims;\r
+    }\r
+\r
+ nomonitor:\r
+    /* for some reason we couldn't get a handle on a monitor or we\r
+       couldn't get the metrics for that monitor.  We default to\r
+       setting things up on the primary monitor. */\r
+\r
+    SetRectEmpty(&rm);\r
+    if (!SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID) &rm, 0))\r
+        goto done_with_monitor;\r
+\r
+ adjust_dims:\r
+\r
+    if (width > (rm.right - rm.left))\r
+        width = rm.right - rm.left;\r
+    if (height > (rm.bottom - rm.top))\r
+        height = rm.bottom - rm.top;\r
+\r
+    if (x < rm.left)\r
+        x = rm.left;\r
+    if (x + width > rm.right)\r
+        x = rm.right - width;\r
+    if (y < rm.top)\r
+        y = rm.top;\r
+    if (y + height > rm.bottom)\r
+        y = rm.bottom - height;\r
+\r
+ done_with_monitor:\r
+    pr->left = x;\r
+    pr->top = y;\r
+    pr->right = x + width;\r
+    pr->bottom = y + height;\r
+\r
+}\r
+\r
+\r
 void \r
 khm_create_main_window(void) {\r
     wchar_t buf[1024];\r
     khm_handle csp_cw = NULL;\r
     khm_handle csp_mw = NULL;\r
     int x,y,width,height;\r
+    RECT r;\r
 \r
     LoadString(khm_hInstance, IDS_MAIN_WINDOW_TITLE, \r
                buf, ARRAYLENGTH(buf));\r
@@ -817,13 +979,23 @@ khm_create_main_window(void) {
         khc_close_space(csp_cw);\r
     }\r
 \r
+    /* The saved dimensions might not actually be visible if the user\r
+       has changed the resolution of the display or if it's a multiple\r
+       monitor system where the monitor on which the Network Identity\r
+       Manager window was on previously is no longer connected.  We\r
+       have to check for that and adjust the dimensions if needed. */\r
+    SetRect(&r, x, y, x + width, y + height);\r
+    khm_adjust_window_dimensions_for_display(&r);\r
+\r
     khm_hwnd_main = \r
         CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,\r
                        MAKEINTATOM(khm_main_window_class),\r
                        buf,\r
                        WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | \r
                        WS_CLIPSIBLINGS,\r
-                       x, y, width, height,\r
+                       r.left, r.top,\r
+                       r.right - r.left,\r
+                       r.bottom - r.top,\r
                        khm_hwnd_null,\r
                        NULL,\r
                        NULL,\r
index 5d0bdc5640e8a788fb5c2338b3efb4a4aede02ae..221bf4246d5cb01954c0de9273453aa5594ed706 100644 (file)
@@ -55,8 +55,9 @@ khm_main_wnd_proc(HWND hwnd,
                   WPARAM wParam,\r
                   LPARAM lParam);\r
 \r
-#define WM_KHUI_ASSIGN_COMMANDLINE       32808\r
 #define WM_KHUI_QUERY_APP_VERSION        32809\r
+#define WM_KHUI_ASSIGN_COMMANDLINE_V1    32808\r
+#define WM_KHUI_ASSIGN_COMMANDLINE_V2    32810\r
 \r
 #define COMMANDLINE_MAP_FMT              L"Local\\NetIDMgr_Cmdline_%lu"\r
 #define QUERY_APP_VER_MAP_FMT            L"Local\\NetIDMgr_QueryVer_%lu"\r
index 75ebef4d823f90740f63b3dec93d0a46532db861..8265aff75a68b2f414948259eeda302de926b2d9 100644 (file)
 \r
 /* $Id$ */\r
 \r
+/* Include the OEMRESOURCE constants for locating standard icon\r
+   resources. */\r
+#define OEMRESOURCE\r
+\r
 #include<khmapp.h>\r
 #include<assert.h>\r
 \r
@@ -34,8 +38,15 @@ ATOM khui_newcredwnd_cls;
 static void\r
 nc_position_credtext(khui_nc_wnd_data * d);\r
 \r
-/* Common dialog procedure.  Be careful.  This is used by more than\r
-   one dialog. */\r
+/* Common dialog procedure used by the main credential panel\r
+   (IDD_NC_NEWCRED) and the button bar (IDC_NC_BBAR). */\r
+\r
+static void\r
+nc_layout_main_panel(khui_nc_wnd_data * d);\r
+\r
+static void\r
+nc_layout_new_cred_window(khui_nc_wnd_data * d);\r
+\r
 static INT_PTR CALLBACK \r
 nc_common_dlg_proc(HWND hwnd,\r
                    UINT uMsg,\r
@@ -53,8 +64,9 @@ nc_common_dlg_proc(HWND hwnd,
 #pragma warning(disable: 4244)\r
             SetWindowLongPtr(hwnd, DWLP_USER, lParam);\r
 #pragma warning(pop)\r
+\r
             if (d->nc->subtype == KMSG_CRED_PASSWORD) {\r
-                ShowWindow(GetDlgItem(hwnd, IDC_NC_OPTIONS),\r
+                ShowWindow(GetDlgItem(hwnd, IDC_NC_ADVANCED),\r
                            SW_HIDE);\r
             }\r
         }\r
@@ -76,28 +88,6 @@ nc_common_dlg_proc(HWND hwnd,
         }\r
         break;\r
 \r
-#if 0\r
-        /* someday this will be used to draw custom tab buttons.  But\r
-           that's not today */\r
-    case WM_DRAWITEM:\r
-        {\r
-            khui_nc_wnd_data * d;\r
-            int id;\r
-            LPDRAWITEMSTRUCT ds;\r
-\r
-            d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, DWLP_USER);\r
-            id = wParam;\r
-            ds = (LPDRAWITEMSTRUCT) lParam;\r
-\r
-            if(id >= NC_TS_CTRL_ID_MIN && id <= NC_TS_CTRL_ID_MAX) {\r
-                /*TODO: custom draw the buttons */\r
-            }\r
-            else\r
-                return FALSE;\r
-        }\r
-        break;\r
-#endif\r
-\r
     case KHUI_WM_NC_NOTIFY:\r
         {\r
             khui_nc_wnd_data * d;\r
@@ -107,22 +97,15 @@ nc_common_dlg_proc(HWND hwnd,
             /* message sent by parent to notify us of something */\r
             switch(HIWORD(wParam)) {\r
             case WMNC_DIALOG_EXPAND:\r
+                /* fallthrough */\r
+            case WMNC_UPDATE_LAYOUT:\r
                 if(hwnd == d->dlg_main) {\r
-                    HWND hw;\r
-                        \r
-                    if(hw = GetDlgItem(hwnd, IDOK))\r
-                        ShowWindow(hw, SW_HIDE);\r
-                    if(hw = GetDlgItem(hwnd, IDCANCEL))\r
-                        ShowWindow(hw, SW_HIDE);\r
-                    if(hw = GetDlgItem(hwnd, IDC_NC_OPTIONS))\r
-                        ShowWindow(hw, SW_HIDE);\r
 \r
-                    d->r_credtext.bottom = d->r_area.bottom;\r
-\r
-                    nc_position_credtext(d);\r
+                    nc_layout_main_panel(d);\r
 \r
                     return TRUE;\r
                 }\r
+                break;          /* nop */\r
             }\r
         }\r
         return TRUE;\r
@@ -136,6 +119,9 @@ nc_common_dlg_proc(HWND hwnd,
         d = (khui_nc_wnd_data *) (LONG_PTR)\r
             GetWindowLongPtr(hwnd, DWLP_USER);\r
 \r
+        /* TODO: filter out and forward only the messages that\r
+           originated or pertain to the identity selection\r
+           controls. */\r
         if (d && d->nc && d->nc->ident_cb) {\r
             return d->nc->ident_cb(d->nc, WMNC_IDENT_WMSG, hwnd, uMsg, \r
                                    wParam, lParam);\r
@@ -146,51 +132,580 @@ nc_common_dlg_proc(HWND hwnd,
 }\r
 \r
 static void\r
-nc_position_credtext(khui_nc_wnd_data * d)\r
-{\r
-    HWND hw;\r
+nc_notify_clear(khui_nc_wnd_data * d) {\r
+\r
+    if (d->notif_type == NC_NOTIFY_NONE)\r
+        /* there are no notifications anyway. */\r
+        return;\r
+\r
+    if (d->hwnd_notif_label)\r
+        DestroyWindow(d->hwnd_notif_label);\r
+\r
+    if (d->hwnd_notif_aux)\r
+        DestroyWindow(d->hwnd_notif_aux);\r
+\r
+    d->hwnd_notif_label = NULL;\r
+    d->hwnd_notif_aux = NULL;\r
+\r
+    SetRectEmpty(&d->r_notif);\r
+\r
+    d->notif_type = NC_NOTIFY_NONE;\r
+\r
+    /* Note that we must call nc_layout_main_panel() after calling\r
+       this to adjust the layout of the main panel.  However we aren't\r
+       calling it here since we might want to add another set of\r
+       notifications or make other changes to the main panel content\r
+       before calling nc_layout_main_panel(). */\r
+}\r
+\r
+static void\r
+nc_notify_marquee(khui_nc_wnd_data * d, const wchar_t * label) {\r
+\r
+#if (_WIN32_IE >= 0x0600)\r
+    HDC hdc;\r
+    size_t length;\r
+    SIZE label_size;\r
+#endif\r
+\r
+    RECT r_label;\r
+    RECT r_mq;\r
+    RECT r_row;\r
+    HFONT hfont;\r
+    HWND hwnd;\r
+    HDWP hdefer;\r
+\r
+    /* Clear the notification area.  We only support one notification\r
+       at a time. */\r
+    nc_notify_clear(d);\r
 \r
-    hw = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT);\r
 #ifdef DEBUG\r
-    assert(hw);\r
+    assert(d->dlg_main);\r
 #endif\r
 \r
-    if (d->r_credtext.bottom < d->r_credtext.top + d->r_row.bottom * 2) {\r
-        /* not enough room */\r
-        if (d->nc->mode == KHUI_NC_MODE_MINI &&\r
-            d->nc->subtype != KMSG_CRED_PASSWORD) {\r
-            PostMessage(d->nc->hwnd, KHUI_WM_NC_NOTIFY,\r
-                        MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0);\r
-            return;\r
+#if (_WIN32_IE >= 0x0600)\r
+\r
+    /* We can only show the marquee control if the comctl32 DLL is\r
+       version 6.0 or later.  Otherwise we only show the label. */\r
+\r
+    if (FAILED(StringCchLength(label, KHUI_MAXCCH_SHORT_DESC, &length))) {\r
+#ifdef DEBUG\r
+        assert(FALSE);\r
+#endif\r
+        length = KHUI_MAXCCH_SHORT_DESC;\r
+    }\r
+\r
+    /* See how big the notification control needs to be. */\r
+\r
+    hdc = GetDC(d->dlg_main);\r
+#ifdef DEBUG\r
+    assert(hdc != NULL);\r
+#endif\r
+\r
+    GetTextExtentPoint32(hdc, label, (int) length, &label_size);\r
+\r
+    ReleaseDC(d->dlg_main, hdc);\r
+\r
+    CopyRect(&r_row, &d->r_row);\r
+\r
+    if (label_size.cx > d->r_e_label.right - d->r_e_label.left) {\r
+        /* using an entire row */\r
+        CopyRect(&r_label, &d->r_row);\r
+        CopyRect(&r_mq, &d->r_n_input);\r
+        OffsetRect(&r_mq, 0, r_row.bottom - r_row.top);\r
+        r_row.bottom += r_row.bottom - r_row.top;\r
+    } else if (label_size.cx > d->r_n_label.right - d->r_n_label.left) {\r
+        /* using large labels */\r
+        CopyRect(&r_label, &d->r_e_label);\r
+        CopyRect(&r_mq, &d->r_e_input);\r
+    } else {\r
+        /* normal labels */\r
+        CopyRect(&r_label, &d->r_n_label);\r
+        CopyRect(&r_mq, &d->r_n_input);\r
+    }\r
+\r
+    InflateRect(&r_mq, 0, - ((r_mq.bottom - r_mq.top) / 4));\r
+\r
+#else  /* _WIN32_IE < 0x0600 */\r
+\r
+    /* We are just showing the label */\r
+    CopyRect(&r_row, &d->r_row);\r
+    CopyRect(&r_label, &r_row);\r
+    SetRectEmpty(&r_mq);\r
+\r
+#endif /* _WIN32_IE >= 0x0600 */\r
+\r
+    {\r
+        long y;\r
+\r
+        if (IsRectEmpty(&d->r_custprompt)) {\r
+            y = d->r_idspec.bottom;\r
         } else {\r
-            ShowWindow(hw, SW_HIDE);\r
-            return;\r
+            y = d->r_custprompt.bottom;\r
         }\r
+\r
+        OffsetRect(&r_row, d->r_area.left, y);\r
+        OffsetRect(&r_label, r_row.left, r_row.top);\r
+        OffsetRect(&r_mq, r_row.left, r_row.top);\r
+    }\r
+\r
+    hfont = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0);\r
+\r
+    hdefer = BeginDeferWindowPos(2);\r
+\r
+    /* the label */\r
+    hwnd = CreateWindowEx(0,\r
+                          L"STATIC",\r
+                          label,\r
+                          WS_CHILD | SS_ENDELLIPSIS,\r
+                          r_label.left, r_label.top,\r
+                          r_label.right - r_label.left,\r
+                          r_label.bottom - r_label.top,\r
+                          d->dlg_main,\r
+                          NULL, NULL, NULL);\r
+#ifdef DEBUG\r
+    assert(hwnd != NULL);\r
+#endif\r
+    SendMessage(hwnd, WM_SETFONT, (WPARAM) hfont, (LPARAM) TRUE);\r
+\r
+    DeferWindowPos(hdefer, hwnd, NULL,\r
+                   0, 0, 0, 0,\r
+                   SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |\r
+                   SWP_NOSIZE | SWP_SHOWWINDOW);\r
+\r
+    d->hwnd_notif_label = hwnd;\r
+\r
+    /* and the marquee */\r
+\r
+#if (_WIN32_IE >= 0x0600)\r
+\r
+    /* unfortunately, the marquee is only available on comctl32\r
+       version 6.0 or later.  On previous versions, we only display\r
+       the message label. */\r
+\r
+    hwnd = CreateWindowEx(0,\r
+                          PROGRESS_CLASS,\r
+                          L"",\r
+                          WS_CHILD | PBS_MARQUEE,\r
+                          r_mq.left, r_mq.top,\r
+                          r_mq.right - r_mq.left,\r
+                          r_mq.bottom - r_mq.top,\r
+                          d->dlg_main,\r
+                          NULL, NULL, NULL);\r
+#ifdef DEBUG\r
+    assert(hwnd != NULL);\r
+#endif\r
+\r
+    SendMessage(hwnd, PBM_SETMARQUEE, TRUE, 100);\r
+\r
+    DeferWindowPos(hdefer, hwnd, NULL,\r
+                   0, 0, 0, 0,\r
+                   SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |\r
+                   SWP_NOSIZE | SWP_SHOWWINDOW);\r
+\r
+    d->hwnd_notif_aux = hwnd;\r
+\r
+#endif /* _WIN32_IE >= 0x0600 */\r
+\r
+    EndDeferWindowPos(hdefer);\r
+\r
+    CopyRect(&d->r_notif, &r_row);\r
+\r
+    d->notif_type = NC_NOTIFY_MARQUEE;\r
+\r
+    /* Note that we must call nc_layout_main_panel() after calling\r
+       this to adjust the layout of the main panel.  However we aren't\r
+       calling it here since we might want to add another set of\r
+       notifications or make other changes to the main panel content\r
+       before calling nc_layout_main_panel(). */\r
+}\r
+\r
+static void\r
+nc_notify_message(khui_nc_wnd_data * d,\r
+                  kherr_severity severity,\r
+                  const wchar_t * message) {\r
+\r
+    SIZE icon_size;\r
+    LPCTSTR icon_res;\r
+    HICON h_icon;\r
+    HWND hwnd;\r
+    HFONT hfont;\r
+    HDWP hdefer;\r
+\r
+    RECT r_row;\r
+    RECT r_label;\r
+    RECT r_icon;\r
+\r
+    nc_notify_clear(d);\r
+\r
+    icon_size.cx = GetSystemMetrics(SM_CXSMICON);\r
+    icon_size.cy = GetSystemMetrics(SM_CYSMICON);\r
+\r
+    switch(severity) {\r
+    case KHERR_INFO:\r
+        icon_res = MAKEINTRESOURCE(OIC_INFORMATION);\r
+        break;\r
+\r
+    case KHERR_WARNING:\r
+        icon_res = MAKEINTRESOURCE(OIC_WARNING);\r
+        break;\r
+\r
+    case KHERR_ERROR:\r
+        icon_res = MAKEINTRESOURCE(OIC_ERROR);\r
+        break;\r
+\r
+    default:\r
+        icon_res = NULL;\r
+    }\r
+\r
+    if (icon_res != NULL) {\r
+        h_icon = (HICON) LoadImage(NULL,\r
+                                   icon_res,\r
+                                   IMAGE_ICON,\r
+                                   icon_size.cx,\r
+                                   icon_size.cy,\r
+                                   LR_DEFAULTCOLOR | LR_SHARED);\r
     } else {\r
-        ShowWindow(hw, SW_SHOW);\r
+        h_icon = NULL;\r
+    }\r
+\r
+    CopyRect(&r_row, &d->r_row);\r
+\r
+#define CENTERVALUE(w,v) ((w)/2 - (v)/2)\r
+\r
+    SetRect(&r_icon,\r
+            0, CENTERVALUE(r_row.bottom - r_row.top, icon_size.cy),\r
+            icon_size.cx,\r
+            CENTERVALUE(r_row.bottom - r_row.top, icon_size.cy) + icon_size.cy);\r
+\r
+#undef CENTERVALUE\r
+\r
+    CopyRect(&r_label, &r_row);\r
+    OffsetRect(&r_label, -r_label.left, -r_label.top);\r
+    r_label.left += (icon_size.cx * 3) / 2;\r
+\r
+    {\r
+        long y;\r
+\r
+        if (IsRectEmpty(&d->r_custprompt)) {\r
+            y = d->r_idspec.bottom;\r
+        } else {\r
+            y = d->r_custprompt.bottom;\r
+        }\r
+\r
+        OffsetRect(&r_row, d->r_area.left, y);\r
+        OffsetRect(&r_label, r_row.left, r_row.top);\r
+        OffsetRect(&r_icon, r_row.left, r_row.top);\r
     }\r
 \r
-    SetWindowPos(hw, NULL,\r
-                 d->r_credtext.left + d->r_n_input.left, /* x */\r
-                 d->r_credtext.top, /* y */\r
-                 d->r_n_input.right - d->r_n_input.left, /* width */\r
-                 d->r_credtext.bottom - d->r_credtext.top, /* height */\r
-                 SWP_NOACTIVATE | SWP_NOOWNERZORDER | \r
-                 SWP_NOZORDER);\r
-\r
-    hw = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT_LABEL);\r
-\r
-    SetWindowPos(hw, NULL,\r
-                 d->r_credtext.left + d->r_n_label.left, /* x */\r
-                 d->r_credtext.top, /* y */\r
-                 d->r_n_label.right - d->r_n_label.left, /* width */\r
-                 d->r_n_label.bottom - d->r_n_label.top, /* height */\r
-                 SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
-                 SWP_NOZORDER);\r
+    hfont = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0);\r
+\r
+    hdefer = BeginDeferWindowPos(2);\r
+\r
+    hwnd = CreateWindowEx(0,\r
+                          L"STATIC",\r
+                          message,\r
+                          WS_CHILD | SS_ENDELLIPSIS | SS_CENTER,\r
+                          r_label.left, r_label.top,\r
+                          r_label.right - r_label.left,\r
+                          r_label.bottom - r_label.top,\r
+                          d->dlg_main,\r
+                          NULL, NULL, NULL);\r
+#ifdef DEBUG\r
+    assert(hwnd != NULL);\r
+#endif\r
+    SendMessage(hwnd, WM_SETFONT, (WPARAM) hfont, (LPARAM) TRUE);\r
+\r
+    DeferWindowPos(hdefer, hwnd, NULL,\r
+                   0, 0, 0, 0,\r
+                   SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |\r
+                   SWP_NOSIZE | SWP_SHOWWINDOW);\r
+\r
+    d->hwnd_notif_label = hwnd;\r
+\r
+    hwnd = CreateWindowEx(0,\r
+                          L"STATIC",\r
+                          NULL,\r
+                          WS_CHILD | SS_ICON |\r
+#if (_WIN32_IE >= 0x0600)\r
+                          SS_REALSIZECONTROL\r
+#else\r
+                          0\r
+#endif\r
+                          ,\r
+                          r_icon.left, r_icon.top,\r
+                          r_icon.right - r_icon.left,\r
+                          r_icon.bottom - r_icon.top,\r
+                          d->dlg_main,\r
+                          NULL, NULL, NULL);\r
+#ifdef DEBUG\r
+    assert(hwnd != NULL);\r
+#endif\r
+\r
+    if (h_icon && hwnd)\r
+        SendMessage(hwnd, STM_SETICON, (WPARAM) h_icon, 0);\r
+\r
+    DeferWindowPos(hdefer, hwnd, NULL,\r
+                   0, 0, 0, 0,\r
+                   SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |\r
+                   SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOZORDER);\r
+\r
+    d->hwnd_notif_aux = hwnd;\r
+\r
+    EndDeferWindowPos(hdefer);\r
+\r
+    CopyRect(&d->r_notif, &r_row);\r
+\r
+    d->notif_type = NC_NOTIFY_MESSAGE;\r
+\r
+    /* Note that we must call nc_layout_main_panel() after calling\r
+       this to adjust the layout of the main panel.  However we aren't\r
+       calling it here since we might want to add another set of\r
+       notifications or make other changes to the main panel content\r
+       before calling nc_layout_main_panel(). */\r
 }\r
 \r
-/* sorts tab buttons */\r
-static int __cdecl \r
+static void\r
+nc_layout_main_panel(khui_nc_wnd_data * d)\r
+{\r
+    RECT r_main;\r
+    HWND hw_ct;\r
+    HWND hw_ct_label;\r
+    HDWP hdwp;\r
+    RECT r_used;                /* extent used by identity specifiers,\r
+                                   custom prompts and notificaiton\r
+                                   controls. */\r
+\r
+    RECT r_wmain;              /* extents of the main window in screen\r
+                                  coordinates. */\r
+\r
+    r_main.left = 0;\r
+    r_main.top = 0;\r
+    r_main.bottom = NCDLG_HEIGHT;\r
+    r_main.right = NCDLG_WIDTH;\r
+\r
+    MapDialogRect(d->dlg_main, &r_main);\r
+\r
+    CopyRect(&r_used, &d->r_idspec);\r
+\r
+    GetWindowRect(d->dlg_main, &r_wmain);\r
+\r
+    hdwp = BeginDeferWindowPos(7);\r
+\r
+    /* check if the notification area and the custom prompt area are\r
+       overlapping. */\r
+\r
+    if (d->notif_type != NC_NOTIFY_NONE) {\r
+        long delta_y = 0;\r
+        RECT r;\r
+\r
+        CopyRect(&r, &d->r_custprompt);\r
+\r
+        if (IsRectEmpty(&d->r_custprompt)) {\r
+            /* if there are no custom prompts, then the notification\r
+               area should be immediately below the identitify\r
+               specifers. */\r
+\r
+            delta_y = d->r_idspec.bottom - d->r_notif.top;\r
+        } else {\r
+            /* otherwise, the notification area should be immediately\r
+               below the custom prompt area */\r
+\r
+            delta_y = d->r_custprompt.bottom - d->r_notif.top;\r
+        }\r
+\r
+        if (delta_y != 0) {\r
+            RECT r_lbl;\r
+            RECT r_aux;\r
+\r
+            if (d->hwnd_notif_label) {\r
+                GetWindowRect(d->hwnd_notif_label, &r_lbl);\r
+                OffsetRect(&r_lbl, -r_wmain.left, delta_y - r_wmain.top);\r
+\r
+                DeferWindowPos(hdwp, d->hwnd_notif_label, NULL,\r
+                               r_lbl.left, r_lbl.top, 0, 0,\r
+                               SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                               SWP_NOZORDER | SWP_NOSIZE);\r
+            }\r
+\r
+            if (d->hwnd_notif_aux) {\r
+                GetWindowRect(d->hwnd_notif_aux, &r_aux);\r
+                OffsetRect(&r_aux, -r_wmain.left, delta_y - r_wmain.top);\r
+\r
+                DeferWindowPos(hdwp, d->hwnd_notif_aux, NULL,\r
+                               r_aux.left, r_aux.top, 0, 0,\r
+                               SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                               SWP_NOZORDER | SWP_NOSIZE);\r
+            }\r
+\r
+            OffsetRect(&d->r_notif, 0, delta_y);\r
+        }\r
+    }\r
+\r
+    if (!IsRectEmpty(&d->r_custprompt)) {\r
+        r_used.bottom = max(d->r_custprompt.bottom,\r
+                            r_used.bottom);\r
+    }\r
+\r
+    if (!IsRectEmpty(&d->r_notif)) {\r
+        r_used.bottom = max(d->r_notif.bottom,\r
+                            r_used.bottom);\r
+    }\r
+\r
+    if (d->nc->mode == KHUI_NC_MODE_MINI) {\r
+        RECT r_ok;\r
+        RECT r_cancel;\r
+        RECT r_advanced;\r
+        HWND hw;\r
+\r
+        hw = GetDlgItem(d->dlg_main, IDOK);\r
+#ifdef DEBUG\r
+        assert(hw != NULL);\r
+#endif\r
+        GetWindowRect(hw, &r_ok);\r
+        OffsetRect(&r_ok, -r_wmain.left, -r_ok.top + r_used.bottom);\r
+\r
+        DeferWindowPos(hdwp, hw, NULL,\r
+                       r_ok.left, r_ok.top, 0, 0,\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                       SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);\r
+\r
+        hw = GetDlgItem(d->dlg_main, IDCANCEL);\r
+#ifdef DEBUG\r
+        assert(hw != NULL);\r
+#endif\r
+        GetWindowRect(hw, &r_cancel);\r
+        OffsetRect(&r_cancel, -r_wmain.left, -r_cancel.top + r_used.bottom);\r
+\r
+        DeferWindowPos(hdwp, hw, NULL,\r
+                       r_cancel.left, r_cancel.top, 0, 0,\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                       SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);\r
+\r
+        hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED);\r
+#ifdef DEBUG\r
+        assert(hw != NULL);\r
+#endif\r
+        GetWindowRect(hw, &r_advanced);\r
+        OffsetRect(&r_advanced, -r_wmain.left, -r_advanced.top + r_used.bottom);\r
+\r
+        DeferWindowPos(hdwp, hw, NULL,\r
+                       r_advanced.left, r_advanced.top, 0, 0,\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                       SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);\r
+\r
+        /* and now update the extents of the main panel */\r
+        r_main.bottom = r_used.bottom + (r_ok.bottom - r_ok.top) + d->r_area.top;\r
+\r
+        CopyRect(&d->r_main, &r_main);\r
+\r
+    } else {\r
+\r
+        HWND hw;\r
+\r
+        hw = GetDlgItem(d->dlg_main, IDOK);\r
+#ifdef DEBUG\r
+        assert(hw != NULL);\r
+#endif\r
+        if (IsWindowVisible(hw))\r
+            DeferWindowPos(hdwp, hw, NULL,\r
+                           0, 0, 0, 0,\r
+                           SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE |\r
+                           SWP_NOOWNERZORDER | SWP_NOZORDER);\r
+\r
+        hw = GetDlgItem(d->dlg_main, IDCANCEL);\r
+#ifdef DEBUG\r
+        assert(hw != NULL);\r
+#endif\r
+        if (IsWindowVisible(hw))\r
+            DeferWindowPos(hdwp, hw, NULL,\r
+                           0, 0, 0, 0,\r
+                           SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE |\r
+                           SWP_NOOWNERZORDER | SWP_NOZORDER);\r
+\r
+        hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED);\r
+#ifdef DEBUG\r
+        assert(hw != NULL);\r
+#endif\r
+        if (IsWindowVisible(hw))\r
+            DeferWindowPos(hdwp, hw, NULL,\r
+                           0, 0, 0, 0,\r
+                           SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE |\r
+                           SWP_NOOWNERZORDER | SWP_NOZORDER);\r
+\r
+        d->r_credtext.top = r_used.bottom;\r
+\r
+        CopyRect(&d->r_main, &r_main);\r
+    }\r
+\r
+    /* now update the layout of the credentials text window */\r
+\r
+    hw_ct = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT);\r
+    hw_ct_label = GetDlgItem(d->dlg_main, IDC_NC_CREDTEXT_LABEL);\r
+#ifdef DEBUG\r
+    assert(hw_ct != NULL);\r
+    assert(hw_ct_label != NULL);\r
+#endif\r
+\r
+    if (d->nc->mode == KHUI_NC_MODE_MINI ||\r
+        d->r_credtext.bottom < d->r_credtext.top + d->r_row.bottom * 2) {\r
+\r
+        /* either we aren't supposed to show the credentials text\r
+           window, or we don't have enough room. */\r
+        if (IsWindowVisible(hw_ct) || IsWindowVisible(hw_ct_label)) {\r
+\r
+            DeferWindowPos(hdwp, hw_ct, NULL,\r
+                           0, 0, 0, 0,\r
+                           SWP_HIDEWINDOW | SWP_NOOWNERZORDER |\r
+                           SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);\r
+\r
+            DeferWindowPos(hdwp, hw_ct_label, NULL,\r
+                           0, 0, 0, 0,\r
+                           SWP_HIDEWINDOW | SWP_NOOWNERZORDER |\r
+                           SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);\r
+\r
+        }\r
+\r
+    } else {\r
+\r
+        DeferWindowPos(hdwp,\r
+                       hw_ct, NULL,\r
+                       d->r_credtext.left + d->r_n_input.left, /* x */\r
+                       d->r_credtext.top, /* y */\r
+                       d->r_n_input.right - d->r_n_input.left, /* width */\r
+                       d->r_credtext.bottom - d->r_credtext.top, /* height */\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER | \r
+                       SWP_NOZORDER | SWP_SHOWWINDOW);\r
+\r
+        DeferWindowPos(hdwp,\r
+                       hw_ct_label, NULL,\r
+                       d->r_credtext.left + d->r_n_label.left, /* x */\r
+                       d->r_credtext.top, /* y */\r
+                       d->r_n_label.right - d->r_n_label.left, /* width */\r
+                       d->r_n_label.bottom - d->r_n_label.top, /* height */\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                       SWP_NOZORDER | SWP_SHOWWINDOW);\r
+    }\r
+\r
+    EndDeferWindowPos(hdwp);\r
+\r
+    /* NOTE: although we updated d->r_main, if the new credentials\r
+       window is in mini mode, we must call\r
+       nc_layout_new_cred_window() to adjust the size of the new\r
+       credentials window to fit the main panel.  We don't do it here\r
+       because we need to keep these two operations separate. */\r
+}\r
+\r
+/* Credential type panel comparison function.  Tabs are sorted based\r
+   on the following criteria:\r
+\r
+   1) By ordinal - Panels with ordinal -1 will be ranked after panels\r
+      whose ordinal is not -1.\r
+\r
+   2) By name - Case insensitive comparison of the name.  If the panel\r
+      does not have a name (i.e. the ->name member is NULL, it will be\r
+      ranked after panels which have a name.\r
+ */\r
+static int __cdecl\r
 nc_tab_sort_func(const void * v1, const void * v2)\r
 {\r
     /* v1 and v2 and of type : khui_new_creds_by_type ** */\r
@@ -201,11 +716,19 @@ nc_tab_sort_func(const void * v1, const void * v2)
 \r
     if(t1->ordinal !=  -1) {\r
         if(t2->ordinal != -1) {\r
-            if(t1->ordinal == t2->ordinal)\r
-                return wcscmp(t1->name, t2->name);\r
-            else\r
+            if(t1->ordinal == t2->ordinal) {\r
+                if (t1->name && t2->name)\r
+                    return _wcsicmp(t1->name, t2->name);\r
+                else if (t1->name)\r
+                    return -1;\r
+                else if (t2->name)\r
+                    return 1;\r
+                else\r
+                    return 0;\r
+            } else {\r
                 /* safe to convert to an int here */\r
                 return (int) (t1->ordinal - t2->ordinal);\r
+            }\r
         } else\r
             return -1;\r
     } else {\r
@@ -213,30 +736,30 @@ nc_tab_sort_func(const void * v1, const void * v2)
             return 1;\r
         else if (t1->name && t2->name)\r
             return wcscmp(t1->name, t2->name);\r
+        else if (t1->name)\r
+            return -1;\r
+        else if (t2->name)\r
+            return 1;\r
         else\r
             return 0;\r
     }\r
 }\r
 \r
 static void \r
-nc_notify_types_async(khui_new_creds * c, UINT uMsg,\r
-                      WPARAM wParam, LPARAM lParam)\r
+nc_notify_types(khui_new_creds * c, UINT uMsg,\r
+                WPARAM wParam, LPARAM lParam, BOOL sync)\r
 {\r
     khm_size i;\r
 \r
     for(i=0; i<c->n_types; i++) {\r
-        PostMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam);\r
-    }\r
-}\r
 \r
-static void \r
-nc_notify_types(khui_new_creds * c, UINT uMsg,\r
-                WPARAM wParam, LPARAM lParam)\r
-{\r
-    khm_size i;\r
+        if (c->types[i]->hwnd_panel == NULL)\r
+            continue;\r
 \r
-    for(i=0; i<c->n_types; i++) {\r
-        SendMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam);\r
+        if (sync)\r
+            SendMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam);\r
+        else\r
+            PostMessage(c->types[i]->hwnd_panel, uMsg, wParam, lParam);\r
     }\r
 }\r
 \r
@@ -264,6 +787,8 @@ nc_clear_password_fields(khui_nc_wnd_data * d)
     }\r
 }\r
 \r
+/* used by nc_enable_controls */\r
+\r
 struct nc_enum_wnd_data {\r
     khui_nc_wnd_data * d;\r
     khm_boolean enable;\r
@@ -317,9 +842,86 @@ nc_update_credtext(khui_nc_wnd_data * d)
     StringCchLength(ctbuf, NC_MAXCCH_CREDTEXT, &cch);\r
     buf = ctbuf + cch;\r
     nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), (LPARAM) d->nc);\r
+                    MAKEWPARAM(0, WMNC_UPDATE_CREDTEXT), (LPARAM) d->nc, TRUE);\r
 \r
     /* hopefully all the types have updated their credential texts */\r
+\r
+    /* if the dialog is in the mini mode, we have to display\r
+       exceptions using a notification. */\r
+    if (d->nc->mode == KHUI_NC_MODE_MINI) {\r
+        BOOL need_layout = FALSE;\r
+        if (d->nc->n_identities == 0) {\r
+\r
+            /* There are no identities selected. We don't show any\r
+               notifications here. */\r
+            if (d->notif_type != NC_NOTIFY_NONE) {\r
+                nc_notify_clear(d);\r
+                need_layout = TRUE;\r
+            }\r
+\r
+        } else {\r
+\r
+            wchar_t id_name[KCDB_IDENT_MAXCCH_NAME];\r
+            wchar_t format[256];\r
+            wchar_t msg[ARRAYLENGTH(format) + ARRAYLENGTH(id_name)];\r
+            khm_size cbbuf;\r
+            khm_int32 flags;\r
+\r
+            kcdb_identity_get_flags(d->nc->identities[0], &flags);\r
+\r
+            cbbuf = sizeof(id_name);\r
+            kcdb_identity_get_name(d->nc->identities[0], id_name, &cbbuf);\r
+\r
+            if (flags & KCDB_IDENT_FLAG_INVALID) {\r
+\r
+                /* identity is invalid */\r
+                LoadString(khm_hInstance, IDS_NCN_IDENT_INVALID,\r
+                           format, ARRAYLENGTH(format));\r
+                StringCbPrintf(msg, sizeof(msg), format, id_name);\r
+\r
+                nc_notify_message(d, KHERR_ERROR, msg);\r
+\r
+                need_layout = TRUE;\r
+\r
+            } else if (flags & KCDB_IDENT_FLAG_VALID) {\r
\r
+               /* identity is valid */\r
+                if (d->notif_type != NC_NOTIFY_NONE) {\r
+                    nc_notify_clear(d);\r
+                    need_layout = TRUE;\r
+                }\r
+\r
+            } else if (flags & KCDB_IDENT_FLAG_UNKNOWN) {\r
+\r
+                /* unknown state */\r
+                LoadString(khm_hInstance, IDS_NCN_IDENT_UNKNOWN,\r
+                           format, ARRAYLENGTH(format));\r
+                StringCbPrintf(msg, sizeof(msg), format, id_name);\r
+\r
+                nc_notify_message(d, KHERR_WARNING, msg);\r
+\r
+                need_layout = TRUE;\r
+\r
+            } else {\r
+\r
+                /* still checking */\r
+                LoadString(khm_hInstance, IDS_NCN_IDENT_CHECKING,\r
+                           format, ARRAYLENGTH(format));\r
+                StringCbPrintf(msg, sizeof(msg), format, id_name);\r
+\r
+                nc_notify_marquee(d, msg);\r
+\r
+                need_layout = TRUE;\r
+\r
+            }\r
+        }\r
+\r
+        if (need_layout) {\r
+            nc_layout_main_panel(d);\r
+            nc_layout_new_cred_window(d);\r
+        }\r
+    }\r
+\r
     if(d->nc->n_identities == 1) {\r
         wchar_t main_fmt[256];\r
         wchar_t id_fmt[256];\r
@@ -342,6 +944,9 @@ nc_update_credtext(khui_nc_wnd_data * d)
         } else if(flags & KCDB_IDENT_FLAG_VALID) {\r
             LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_VALID, \r
                        id_fmt, (int) ARRAYLENGTH(id_fmt));\r
+        } else if(flags & KCDB_IDENT_FLAG_UNKNOWN) {\r
+            LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_UNCHECKED,\r
+                       id_fmt, (int) ARRAYLENGTH(id_fmt));\r
         } else if(d->nc->subtype == KMSG_CRED_NEW_CREDS) {\r
             LoadString(khm_hInstance, IDS_NC_CREDTEXT_ID_CHECKING, \r
                        id_fmt, (int) ARRAYLENGTH(id_fmt));\r
@@ -498,42 +1103,224 @@ nc_update_credtext(khui_nc_wnd_data * d)
             cbsize = sizeof(wtitle);\r
             kcdb_identity_get_name(d->nc->identities[0], wtitle, &cbsize);\r
 \r
-            if (d->nc->subtype == KMSG_CRED_PASSWORD)\r
-                LoadString(khm_hInstance, IDS_WTPOST_PASSWORD,\r
-                           wpostfix, (int) ARRAYLENGTH(wpostfix));\r
-            else\r
-                LoadString(khm_hInstance, IDS_WTPOST_NEW_CREDS, \r
-                           wpostfix, (int) ARRAYLENGTH(wpostfix));\r
+            if (d->nc->subtype == KMSG_CRED_PASSWORD)\r
+                LoadString(khm_hInstance, IDS_WTPOST_PASSWORD,\r
+                           wpostfix, (int) ARRAYLENGTH(wpostfix));\r
+            else\r
+                LoadString(khm_hInstance, IDS_WTPOST_NEW_CREDS, \r
+                           wpostfix, (int) ARRAYLENGTH(wpostfix));\r
+\r
+            StringCbCat(wtitle, sizeof(wtitle), wpostfix);\r
+\r
+            SetWindowText(d->nc->hwnd, wtitle);\r
+        } else {\r
+            wchar_t wtitle[256];\r
+\r
+            if (d->nc->subtype == KMSG_CRED_PASSWORD)\r
+                LoadString(khm_hInstance, IDS_WT_PASSWORD,\r
+                           wtitle, (int) ARRAYLENGTH(wtitle));\r
+            else\r
+                LoadString(khm_hInstance, IDS_WT_NEW_CREDS, \r
+                           wtitle, (int) ARRAYLENGTH(wtitle));\r
+\r
+            SetWindowText(d->nc->hwnd, wtitle);\r
+        }\r
+    }\r
+\r
+    if (!(d->nc->response & KHUI_NC_RESPONSE_PROCESSING)) {\r
+        if(validId ||\r
+           d->nc->subtype == KMSG_CRED_PASSWORD) {\r
+            /* TODO: check if all the required fields have valid values\r
+               before enabling the Ok button */\r
+            okEnable = TRUE;\r
+        }\r
+\r
+        hw = GetDlgItem(d->dlg_main, IDOK);\r
+        EnableWindow(hw, okEnable);\r
+        hw = GetDlgItem(d->dlg_bb, IDOK);\r
+        EnableWindow(hw, okEnable);\r
+    }\r
+}\r
+\r
+static void\r
+nc_layout_new_cred_window(khui_nc_wnd_data * ncd) {\r
+    khui_new_creds * c;\r
+    RECT r_main;\r
+    RECT r_ncdialog;\r
+    HDWP hdefer;\r
+\r
+    c = ncd->nc;\r
+\r
+    r_main.left = 0;\r
+    r_main.top = 0;\r
+    r_main.right = NCDLG_WIDTH;\r
+    r_main.bottom = NCDLG_HEIGHT;\r
+\r
+    MapDialogRect(ncd->dlg_main, &r_main);\r
+\r
+    hdefer = BeginDeferWindowPos(5);\r
+\r
+    if (c->mode == KHUI_NC_MODE_MINI) {\r
+\r
+        if (IsWindowVisible(ncd->tab_wnd)) {\r
+            DeferWindowPos(hdefer,\r
+                           ncd->tab_wnd, NULL,\r
+                           0, 0, 0, 0,\r
+                           SWP_HIDEWINDOW |\r
+                           SWP_NOMOVE | SWP_NOOWNERZORDER |\r
+                           SWP_NOSIZE | SWP_NOZORDER);\r
+        }\r
+\r
+        if (IsWindowVisible(ncd->dlg_bb)) {\r
+            DeferWindowPos(hdefer,\r
+                           ncd->dlg_bb, NULL,\r
+                           0, 0, 0, 0,\r
+                           SWP_HIDEWINDOW |\r
+                           SWP_NOMOVE | SWP_NOOWNERZORDER |\r
+                           SWP_NOSIZE | SWP_NOZORDER);\r
+        }\r
+\r
+        DeferWindowPos(hdefer, ncd->dlg_main, NULL,\r
+                       r_main.left, r_main.top,\r
+                       r_main.right - r_main.left,\r
+                       r_main.bottom - r_main.top,\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                       SWP_NOZORDER | SWP_SHOWWINDOW);\r
+\r
+        /* note that the ncd->r_main.bottom may not be the same as\r
+           r_main.bottom because ncd->r_main.bottom is set dynamically\r
+           depending on custom controls. ncd->r_main is valid only\r
+           once nc_layout_main_panel() is called.*/\r
+        CopyRect(&ncd->r_required, &ncd->r_main);\r
+\r
+    } else {\r
+        RECT r_tabctrl;\r
+        RECT r_displayarea;\r
+        RECT r_bbar;\r
+        khm_size i;\r
+\r
+        /* calculate the size of the tab control so that it fits\r
+           snugly around the expanded main panel. */\r
+        CopyRect(&r_tabctrl, &r_main);\r
+        TabCtrl_AdjustRect(ncd->tab_wnd, TRUE, &r_tabctrl);\r
+\r
+        if (r_tabctrl.left < 0 ||\r
+            r_tabctrl.top < 0) {\r
+\r
+            OffsetRect(&r_tabctrl,\r
+                       (r_tabctrl.left < 0)? -r_tabctrl.left : 0,\r
+                       (r_tabctrl.top < 0)? -r_tabctrl.top : 0);\r
+\r
+        }\r
+\r
+#ifdef DEBUG\r
+        assert(r_tabctrl.left == 0);\r
+        assert(r_tabctrl.top == 0);\r
+#endif\r
+\r
+        OffsetRect(&r_tabctrl, 0, ncd->r_area.top);\r
+\r
+        /* and now calculate the rectangle where the main panel should\r
+           be inside the tab control. */\r
+        CopyRect(&r_displayarea, &r_tabctrl);\r
+        TabCtrl_AdjustRect(ncd->tab_wnd, FALSE, &r_displayarea);\r
+\r
+        DeferWindowPos(hdefer,\r
+                       ncd->tab_wnd, HWND_BOTTOM,\r
+                       r_tabctrl.left, r_tabctrl.top,\r
+                       r_tabctrl.right - r_tabctrl.left,\r
+                       r_tabctrl.bottom - r_tabctrl.top,\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                       SWP_SHOWWINDOW);\r
+\r
+        /* we have to place the button bar just to the right of the\r
+           tab panel. */\r
+        r_bbar.left = 0;\r
+        r_bbar.top = 0;\r
+        r_bbar.right = NCDLG_BBAR_WIDTH;\r
+        r_bbar.bottom = NCDLG_BBAR_HEIGHT;\r
+\r
+        MapDialogRect(ncd->dlg_main, &r_bbar);\r
+\r
+        OffsetRect(&r_bbar, r_tabctrl.right, 0);\r
+\r
+        DeferWindowPos(hdefer,\r
+                       ncd->dlg_bb, NULL,\r
+                       r_bbar.left, r_bbar.top,\r
+                       r_bbar.right - r_bbar.left,\r
+                       r_bbar.bottom - r_bbar.top,\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                       SWP_NOZORDER | SWP_SHOWWINDOW);\r
+\r
+        /* move the main panel inside the tab control... */\r
+        DeferWindowPos(hdefer,\r
+                       ncd->dlg_main, NULL,\r
+                       r_displayarea.left, r_displayarea.top,\r
+                       r_displayarea.right - r_displayarea.left,\r
+                       r_displayarea.bottom - r_displayarea.top,\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                       SWP_NOZORDER |\r
+                       (ncd->current_panel == 0 ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));\r
+\r
+        /* and also move all the credential type panels (if they have\r
+           been created) inside the tab control too. */\r
+        khui_cw_lock_nc(c);\r
+\r
+        for (i=0; i < c->n_types; i++) {\r
+            if (c->types[i]->hwnd_panel != NULL) {\r
+                DeferWindowPos(hdefer,\r
+                               c->types[i]->hwnd_panel, NULL,\r
+                               r_displayarea.left, r_displayarea.top,\r
+                               r_displayarea.right - r_displayarea.left,\r
+                               r_displayarea.bottom - r_displayarea.top,\r
+                               SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                               SWP_NOZORDER |\r
+                               (ncd->current_panel == c->types[i]->ordinal ?\r
+                                SWP_SHOWWINDOW : SWP_HIDEWINDOW));\r
+            }\r
+        }\r
+\r
+        khui_cw_unlock_nc(c);\r
+\r
+        /* then update the required size of the new credentials\r
+           dialog. */\r
+        ncd->r_required.left = 0;\r
+        ncd->r_required.top = 0;\r
+        ncd->r_required.right = r_bbar.right;\r
+        ncd->r_required.bottom = max(r_tabctrl.bottom, r_bbar.bottom) + ncd->r_area.top;\r
+    }\r
+\r
+    /* commit all the window moves, resizes and hides/shows we did*/\r
+    EndDeferWindowPos(hdefer);\r
+\r
+    /* now we have to see if the client area of the new credentials\r
+       window is the right size. */\r
+\r
+    GetClientRect(c->hwnd, &r_ncdialog);\r
 \r
-            StringCbCat(wtitle, sizeof(wtitle), wpostfix);\r
+    if (\r
 \r
-            SetWindowText(d->nc->hwnd, wtitle);\r
-        } else {\r
-            wchar_t wtitle[256];\r
+        ((r_ncdialog.right - r_ncdialog.left !=\r
+          ncd->r_required.right - ncd->r_required.left)\r
 \r
-            if (d->nc->subtype == KMSG_CRED_PASSWORD)\r
-                LoadString(khm_hInstance, IDS_WT_PASSWORD,\r
-                           wtitle, (int) ARRAYLENGTH(wtitle));\r
-            else\r
-                LoadString(khm_hInstance, IDS_WT_NEW_CREDS, \r
-                           wtitle, (int) ARRAYLENGTH(wtitle));\r
+         ||\r
 \r
-            SetWindowText(d->nc->hwnd, wtitle);\r
-        }\r
-    }\r
+         (r_ncdialog.bottom - r_ncdialog.top !=\r
+          ncd->r_required.bottom - ncd->r_required.top))\r
 \r
-    if (!(d->nc->response & KHUI_NC_RESPONSE_PROCESSING)) {\r
-        if(validId ||\r
-           d->nc->subtype == KMSG_CRED_PASSWORD) {\r
-            /* TODO: check if all the required fields have valid values\r
-               before enabling the Ok button */\r
-            okEnable = TRUE;\r
-        }\r
+        &&\r
 \r
-        hw = GetDlgItem(d->dlg_main, IDOK);\r
-        EnableWindow(hw, okEnable);\r
-        hw = GetDlgItem(d->dlg_bb, IDOK);\r
-        EnableWindow(hw, okEnable);\r
+        /* we don't bother if the new creds window is already in the\r
+           process of changing the size. */\r
+        !ncd->size_changing) {\r
+\r
+        /* if not, notify the window that the size needs adjusting. */\r
+        if (IsWindowVisible(c->hwnd))\r
+            PostMessage(c->hwnd, KHUI_WM_NC_NOTIFY,\r
+                        MAKEWPARAM(0, WMNC_UPDATE_LAYOUT), 0);\r
+        else\r
+            SendMessage(c->hwnd, KHUI_WM_NC_NOTIFY,\r
+                        MAKEWPARAM(0, WMNC_UPDATE_LAYOUT), 0);\r
     }\r
 }\r
 \r
@@ -557,7 +1344,7 @@ nc_handle_wm_create(HWND hwnd,
     int x, y;\r
     int width, height;\r
     RECT r;\r
-    khm_int32 t;\r
+    HFONT hf_main;\r
 \r
     lpc = (LPCREATESTRUCT) lParam;\r
 \r
@@ -568,34 +1355,70 @@ nc_handle_wm_create(HWND hwnd,
     ncd->nc = c;\r
     c->hwnd = hwnd;\r
 \r
+#ifdef DEBUG\r
+    assert(c->subtype == KMSG_CRED_NEW_CREDS ||\r
+           c->subtype == KMSG_CRED_PASSWORD);\r
+#endif\r
+\r
 #pragma warning(push)\r
 #pragma warning(disable: 4244)\r
     SetWindowLongPtr(hwnd, CW_PARAM, (LONG_PTR) ncd);\r
 #pragma warning(pop)\r
 \r
-    /* first try to create the main dialog panel */\r
-    \r
-    assert(c->subtype == KMSG_CRED_NEW_CREDS ||\r
-           c->subtype == KMSG_CRED_PASSWORD);\r
+    /* first, create the tab control that will house the main dialog\r
+       panel as well as the plug-in specific panels */\r
+    ncd->tab_wnd = CreateWindowEx(0, /* extended style */\r
+                                  WC_TABCONTROL,\r
+                                  L"TabControloxxrz", /* window name */\r
+                                  TCS_HOTTRACK | TCS_RAGGEDRIGHT |\r
+                                  TCS_SINGLELINE | TCS_TABS |\r
+                                  WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS,\r
+                                  0, 0, 100, 100, /* x,y,width height.\r
+                                                     We'll be changing\r
+                                                     these later\r
+                                                     anyway. */\r
+                                  hwnd,\r
+                                  (HMENU) IDC_NC_TABS,\r
+                                  NULL,\r
+                                  0);\r
+\r
+#ifdef DEBUG\r
+    assert(ncd->tab_wnd != NULL);\r
+#endif\r
+\r
+    /* try to create the main dialog panel */\r
 \r
     ncd->dlg_main = CreateDialogParam(khm_hInstance,\r
-                                      MAKEINTRESOURCE(IDD_NC_PASSWORD),\r
+                                      MAKEINTRESOURCE(IDD_NC_NEWCRED),\r
                                       hwnd,\r
                                       nc_common_dlg_proc,\r
                                       (LPARAM) ncd);\r
 #ifdef DEBUG\r
-    assert(ncd->dlg_main);\r
+    assert(ncd->dlg_main != NULL);\r
 #endif\r
 \r
+    hf_main = (HFONT) SendMessage(ncd->dlg_main, WM_GETFONT, 0, 0);\r
+    if (hf_main)\r
+        SendMessage(ncd->tab_wnd, WM_SETFONT, (WPARAM) hf_main, FALSE);\r
+\r
     {\r
         RECT r_main;\r
         RECT r_area;\r
         RECT r_row;\r
         HWND hw;\r
             \r
-        /* pick out metrics for use by the custom prompter stuff */\r
+        /* During the operation of the new credentials window, we will\r
+           need to dynamically change the layout of the controls as a\r
+           result of custom prompting from credentials providers and\r
+           identity selectors from identity providers.  In order to\r
+           guide the dynamic layout, we pick out a few metrics from\r
+           the dialog template for the main panel. The metrics come\r
+           from hidden STATIC controls in the dialog template. */\r
+\r
         GetWindowRect(ncd->dlg_main, &r_main);\r
 \r
+        /* IDC_NC_TPL_PANEL spans the full extent of the dialog that\r
+           we can populate with custom controls. */\r
         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_PANEL);\r
 #ifdef DEBUG\r
         assert(hw);\r
@@ -604,6 +1427,9 @@ nc_handle_wm_create(HWND hwnd,
         OffsetRect(&r_area,-r_main.left, -r_main.top);\r
         CopyRect(&ncd->r_area, &r_area);\r
 \r
+        /* IDC_NC_TPL_ROW spans the extent of a row of normal sized\r
+           custom controls.  A row of custom controls typicall consist\r
+           of a text label and an input control. */\r
         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_ROW);\r
 #ifdef DEBUG\r
         assert(hw);\r
@@ -613,6 +1439,9 @@ nc_handle_wm_create(HWND hwnd,
         OffsetRect(&r,-r.left, -r.top);\r
         CopyRect(&ncd->r_row, &r);\r
 \r
+        /* IDC_NC_TPL_LABEL spans the extent that a normal sized\r
+           label.  The control overlaps IDC_NC_TPL_ROW so we can get\r
+           coordinates relative to the row extents. */\r
         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_LABEL);\r
 #ifdef DEBUG\r
         assert(hw);\r
@@ -621,6 +1450,9 @@ nc_handle_wm_create(HWND hwnd,
         OffsetRect(&r,-r_row.left, -r_row.top);\r
         CopyRect(&ncd->r_n_label, &r);\r
 \r
+        /* IDC_NC_TPL_INPUT spans the extent of a normal sized input\r
+           control in a custom control row.  The control overlaps\r
+           IDC_NC_TPL_ROW so we can get relative coordinates. */\r
         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_INPUT);\r
 #ifdef DEBUG\r
         assert(hw);\r
@@ -629,12 +1461,16 @@ nc_handle_wm_create(HWND hwnd,
         OffsetRect(&r, -r_row.left, -r_row.top);\r
         CopyRect(&ncd->r_n_input, &r);\r
 \r
+        /* IDC_NC_TPL_ROW_LG spans the extent of a row of large sized\r
+           controls. */\r
         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_ROW_LG);\r
 #ifdef DEBUG\r
         assert(hw);\r
 #endif\r
         GetWindowRect(hw, &r_row);\r
 \r
+        /* IDC_NC_TPL_LABEL_LG is a large sized label.  The control\r
+           overlaps IDC_NC_TPL_ROW_LG. */\r
         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_LABEL_LG);\r
 #ifdef DEBUG\r
         assert(hw);\r
@@ -643,6 +1479,8 @@ nc_handle_wm_create(HWND hwnd,
         OffsetRect(&r, -r_row.left, -r_row.top);\r
         CopyRect(&ncd->r_e_label, &r);\r
 \r
+        /* IDC_NC_TPL_INPUT_LG is a large sized input control.\r
+           Overlaps IDC_NC_TPL_ROW_LG. */\r
         hw = GetDlgItem(ncd->dlg_main, IDC_NC_TPL_INPUT_LG);\r
 #ifdef DEBUG\r
         assert(hw);\r
@@ -656,6 +1494,11 @@ nc_handle_wm_create(HWND hwnd,
 \r
         ncd->r_idspec.bottom = ncd->r_idspec.top;\r
 \r
+        /* And finally the credential text window.  The only metric we\r
+           take from here is the Y coordinate of the bottom of the\r
+           control since the actual size and position of the\r
+           credentials window will change depending on the custom\r
+           controls being displayed. */\r
         hw = GetDlgItem(ncd->dlg_main, IDC_NC_CREDTEXT);\r
 #ifdef DEBUG\r
         assert(hw);\r
@@ -668,35 +1511,17 @@ nc_handle_wm_create(HWND hwnd,
     /* if the mode is 'mini'*/\r
     r.left = 0;\r
     r.top = 0;\r
+\r
     if(c->mode == KHUI_NC_MODE_MINI) {\r
         r.right = NCDLG_WIDTH;\r
         r.bottom = NCDLG_HEIGHT;\r
     } else {\r
         r.right = NCDLG_WIDTH + NCDLG_BBAR_WIDTH;\r
-        r.bottom = NCDLG_HEIGHT + NCDLG_TAB_HEIGHT;\r
+        r.bottom = NCDLG_BBAR_HEIGHT;\r
     }\r
 \r
     MapDialogRect(ncd->dlg_main, &r);\r
 \r
-    ncd->r_main.left = 0;\r
-    ncd->r_main.top = 0;\r
-    ncd->r_main.right = NCDLG_WIDTH;\r
-    ncd->r_main.bottom = NCDLG_HEIGHT;\r
-\r
-    ncd->r_ts.left = 0;\r
-    ncd->r_ts.top = ncd->r_main.bottom;\r
-    ncd->r_ts.right = ncd->r_main.right;\r
-    ncd->r_ts.bottom = ncd->r_ts.top + NCDLG_TAB_HEIGHT;\r
-\r
-    ncd->r_bb.left = ncd->r_main.right;\r
-    ncd->r_bb.top = 0;\r
-    ncd->r_bb.right = ncd->r_bb.left + NCDLG_BBAR_WIDTH;\r
-    ncd->r_bb.bottom = ncd->r_ts.bottom;\r
-\r
-    MapDialogRect(ncd->dlg_main, &(ncd->r_main));\r
-    MapDialogRect(ncd->dlg_main, &(ncd->r_ts));\r
-    MapDialogRect(ncd->dlg_main, &(ncd->r_bb));\r
-\r
     /* center the new creds window over the main NetIDMgr window */\r
     width = r.right - r.left;\r
     height = r.bottom - r.top;\r
@@ -721,18 +1546,6 @@ nc_handle_wm_create(HWND hwnd,
 \r
     MoveWindow(hwnd, x, y, width, height, FALSE);\r
 \r
-    SetWindowPos(ncd->dlg_main, \r
-                 NULL, \r
-                 ncd->r_main.left, \r
-                 ncd->r_main.top,\r
-                 ncd->r_main.right - ncd->r_main.left,\r
-                 ncd->r_main.bottom - ncd->r_main.top,\r
-                 SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | \r
-                 SWP_NOREDRAW | SWP_NOZORDER);\r
-\r
-    /* IDD_NC_BBAR is the button bar that sits on the right of the\r
-       dialog when the new creds window is in 'expanded' mode. */\r
-\r
     ncd->dlg_bb = CreateDialogParam(khm_hInstance,\r
                                     MAKEINTRESOURCE(IDD_NC_BBAR),\r
                                     hwnd,\r
@@ -743,101 +1556,22 @@ nc_handle_wm_create(HWND hwnd,
     assert(ncd->dlg_bb);\r
 #endif\r
 \r
-    SetWindowPos(ncd->dlg_bb, \r
-                 NULL, \r
-                 ncd->r_bb.left, \r
-                 ncd->r_bb.top,\r
-                 ncd->r_bb.right - ncd->r_bb.left,\r
-                 ncd->r_bb.bottom - ncd->r_bb.top,\r
-                 SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | \r
-                 SWP_NOREDRAW | SWP_NOZORDER);\r
-\r
-    /* IDD_NC_TS is the tab strip that sits below the main panel when\r
-       the new creds window is in 'expanded' mode */\r
-\r
-    ncd->dlg_ts = CreateDialogParam(khm_hInstance,\r
-                                    MAKEINTRESOURCE(IDD_NC_TS),\r
-                                    hwnd,\r
-                                    nc_common_dlg_proc,\r
-                                    (LPARAM) ncd);\r
-\r
-#ifdef DEBUG\r
-    assert(ncd->dlg_ts);\r
-#endif\r
-\r
-    SetWindowPos(ncd->dlg_ts, \r
-                 NULL, \r
-                 ncd->r_ts.left, \r
-                 ncd->r_ts.top,\r
-                 ncd->r_ts.right - ncd->r_ts.left,\r
-                 ncd->r_ts.bottom - ncd->r_ts.top,\r
-                 SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | \r
-                 SWP_NOREDRAW | SWP_NOZORDER);\r
-\r
-    if(c->mode == KHUI_NC_MODE_MINI) {\r
-        /* hide and show stuff */\r
-        ShowWindow(ncd->dlg_main, SW_SHOW);\r
-        ShowWindow(ncd->dlg_bb, SW_HIDE);\r
-        ShowWindow(ncd->dlg_ts, SW_HIDE);\r
-\r
-        nc_position_credtext(ncd);\r
-\r
-    } else {\r
-        /* hide and show stuff */\r
-        ShowWindow(ncd->dlg_main, SW_SHOW);\r
-        ShowWindow(ncd->dlg_bb, SW_SHOW);\r
-        ShowWindow(ncd->dlg_ts, SW_SHOW);\r
-\r
-        PostMessage(ncd->dlg_main, KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0);\r
-    }\r
-\r
     /* Call the identity provider callback to set the identity\r
-       selector controls */\r
+       selector controls.  These controls need to be there before we\r
+       layout the main panel. */\r
     c->ident_cb(c, WMNC_IDENT_INIT, NULL, 0, 0, (LPARAM) ncd->dlg_main);\r
 \r
-#if 0\r
-    {\r
-        HWND hw;\r
-        wchar_t wcaption[64];\r
-\r
-        LoadString(khm_hInstance, IDS_NC_SETDEF, wcaption,\r
-                   ARRAYLENGTH(wcaption));\r
-\r
-        /* Now create the set as default button */\r
-        hw = CreateWindow\r
-            (L"BUTTON",\r
-             wcaption,\r
-             WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX,\r
-             0, 0, 100, 100,\r
-             ncd->dlg_main,\r
-             (HMENU) NC_BN_SET_DEF_ID,\r
-             khm_hInstance,\r
-             NULL);\r
-\r
-        nc_add_control_row(ncd, NULL, hw, KHUI_CTRLSIZE_HALF);\r
+    if (c->mode == KHUI_NC_MODE_EXPANDED) {\r
+        SendMessage(ncd->dlg_main, KHUI_WM_NC_NOTIFY,\r
+                    MAKEWPARAM(0, WMNC_DIALOG_EXPAND), 0);\r
+    } else {\r
+        /* we don't call this if the dialog is expanded because\r
+           posting WMNC_DIALOG_EXPAND to the main panel results in\r
+           this getting called anyway. */\r
+        nc_layout_main_panel(ncd);\r
     }\r
-#endif\r
-    /* we defer the creation of the tab buttons for later */\r
 \r
-    /* bring the window to the top, if necessary */\r
-    if (KHM_SUCCEEDED(khc_read_int32(NULL,\r
-                                     L"CredWindow\\Windows\\NewCred\\ForceToTop",\r
-                                     &t)) &&\r
-        t != 0 &&\r
-        !khm_is_dialog_active()) {\r
-\r
-        /* if the main window is not visible, then the SetWindowPos()\r
-           call is sufficient to bring the new creds window to the\r
-           top.  However, if the main window is visible but not\r
-           active, the main window needs to be activated before a\r
-           child window can be activated. */\r
-        khm_activate_main_window();\r
-\r
-        SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,\r
-                     (SWP_NOMOVE | SWP_NOSIZE));\r
-\r
-    }\r
+    nc_layout_new_cred_window(ncd);\r
 \r
     /* add this to the dialog chain */\r
     khm_add_dialog(hwnd);\r
@@ -845,6 +1579,7 @@ nc_handle_wm_create(HWND hwnd,
     return TRUE;\r
 }\r
 \r
+/* add a control row supplied by an identity provider */\r
 static void\r
 nc_add_control_row(khui_nc_wnd_data * d, \r
                    HWND label,\r
@@ -855,6 +1590,7 @@ nc_add_control_row(khui_nc_wnd_data * d,
     RECT r_label;\r
     RECT r_input;\r
     HFONT hf;\r
+    HDWP hdefer;\r
 \r
     hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0);\r
     SendMessage(label, WM_SETFONT, (WPARAM) hf, FALSE);\r
@@ -890,35 +1626,39 @@ nc_add_control_row(khui_nc_wnd_data * d,
         return;\r
     }\r
 \r
+    hdefer = BeginDeferWindowPos(2);\r
+\r
     if (label)\r
-        SetWindowPos(label,\r
-                     ((d->hwnd_last_idspec != NULL)?\r
-                      d->hwnd_last_idspec:\r
-                      HWND_TOP),\r
-                     r_label.left, r_label.top,\r
-                     r_label.right - r_label.left,\r
-                     r_label.bottom - r_label.top,\r
-                     SWP_DEFERERASE | SWP_NOACTIVATE |\r
-                     SWP_NOOWNERZORDER);\r
+        DeferWindowPos(hdefer, label,\r
+                       ((d->hwnd_last_idspec != NULL)?\r
+                        d->hwnd_last_idspec:\r
+                        HWND_TOP),\r
+                       r_label.left, r_label.top,\r
+                       r_label.right - r_label.left,\r
+                       r_label.bottom - r_label.top,\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER);\r
 \r
     if (input)\r
-        SetWindowPos(input,\r
-                     (label ? label : ((d->hwnd_last_idspec != NULL)?\r
-                                       d->hwnd_last_idspec:\r
-                                       HWND_TOP)),\r
-                     r_input.left, r_input.top,\r
-                     r_input.right - r_input.left,\r
-                     r_input.bottom - r_input.top,\r
-                     SWP_DEFERERASE | SWP_NOACTIVATE |\r
-                     SWP_NOOWNERZORDER);\r
-\r
-    d->hwnd_last_idspec = input;\r
+        DeferWindowPos(hdefer, input,\r
+                       (label ? label : ((d->hwnd_last_idspec != NULL)?\r
+                                         d->hwnd_last_idspec:\r
+                                         HWND_TOP)),\r
+                       r_input.left, r_input.top,\r
+                       r_input.right - r_input.left,\r
+                       r_input.bottom - r_input.top,\r
+                       SWP_NOACTIVATE | SWP_NOOWNERZORDER);\r
+\r
+    EndDeferWindowPos(hdefer);\r
+\r
+    d->hwnd_last_idspec = (input ? input : label);\r
 \r
     d->r_idspec.bottom = r_row.bottom;\r
 \r
-    d->r_credtext.top = r_row.bottom;\r
+    /* we don't update the layout of the main panel yet, since these\r
+       control additions happen before the main panel is displayed.  A\r
+       call to nc_layout_main_panel() will be made before the main\r
+       panel is shown anyway. */\r
 \r
-    nc_position_credtext(d);\r
 }\r
 \r
 \r
@@ -929,7 +1669,6 @@ nc_handle_wm_destroy(HWND hwnd,
                      LPARAM lParam)\r
 {\r
     khui_nc_wnd_data * d;\r
-    khm_size i;\r
 \r
     /* remove self from dialog chain */\r
     khm_del_dialog(hwnd);\r
@@ -938,25 +1677,18 @@ nc_handle_wm_destroy(HWND hwnd,
 \r
     d->nc->ident_cb(d->nc, WMNC_IDENT_EXIT, NULL, 0, 0, 0);\r
 \r
-    if(d->hwnd_tc_main)\r
-        DestroyWindow(d->hwnd_tc_main);\r
-    for(i=0;i<d->nc->n_types;i++) {\r
-        if(d->nc->types[i]->hwnd_tc) {\r
-            DestroyWindow(d->nc->types[i]->hwnd_tc);\r
-            d->nc->types[i]->hwnd_tc = NULL;\r
-        }\r
-    }\r
+    if (d->hwnd_notif_label)\r
+        DestroyWindow(d->hwnd_notif_label);\r
+    if (d->hwnd_notif_aux)\r
+        DestroyWindow(d->hwnd_notif_aux);\r
 \r
     if(d->dlg_bb)\r
         DestroyWindow(d->dlg_bb);\r
     if(d->dlg_main)\r
         DestroyWindow(d->dlg_main);\r
-    if(d->dlg_ts)\r
-        DestroyWindow(d->dlg_ts);\r
 \r
     d->dlg_bb = NULL;\r
     d->dlg_main = NULL;\r
-    d->dlg_ts = NULL;\r
 \r
     PFREE(d);\r
 \r
@@ -970,7 +1702,6 @@ nc_handle_wm_command(HWND hwnd,
                      LPARAM lParam)\r
 {\r
     khui_nc_wnd_data * d;\r
-    int id;\r
 \r
     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);\r
 \r
@@ -993,7 +1724,8 @@ nc_handle_wm_command(HWND hwnd,
             nc_notify_types(d->nc, \r
                             KHUI_WM_NC_NOTIFY, \r
                             MAKEWPARAM(0,WMNC_DIALOG_PREPROCESS), \r
-                            (LPARAM) d->nc);\r
+                            (LPARAM) d->nc,\r
+                            TRUE);\r
 \r
             khui_cw_sync_prompt_values(d->nc);\r
 \r
@@ -1009,7 +1741,7 @@ nc_handle_wm_command(HWND hwnd,
                 EnableWindow(hw, FALSE);\r
                 hw = GetDlgItem(d->dlg_main, IDCANCEL);\r
                 EnableWindow(hw, FALSE);\r
-                hw = GetDlgItem(d->dlg_main, IDC_NC_OPTIONS);\r
+                hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED);\r
                 EnableWindow(hw, FALSE);\r
                 hw = GetDlgItem(d->dlg_bb, IDOK);\r
                 EnableWindow(hw, FALSE);\r
@@ -1022,7 +1754,7 @@ nc_handle_wm_command(HWND hwnd,
             khm_html_help(hwnd, NULL, HH_HELP_CONTEXT, IDH_ACTION_NEW_ID);\r
             return FALSE;\r
 \r
-        case IDC_NC_OPTIONS\r
+        case IDC_NC_ADVANCED\r
             /* the Options button in the main window was clicked.  we\r
                respond by expanding the dialog. */\r
             PostMessage(hwnd, KHUI_WM_NC_NOTIFY, \r
@@ -1075,7 +1807,7 @@ nc_handle_wm_command(HWND hwnd,
                         KHM_SUCCEEDED(khui_cw_find_type(d->nc, credtype, &t))){\r
                         *colon = L':';\r
 \r
-                        if (t->ordinal != d->ctab &&\r
+                        if (t->ordinal != d->current_panel &&\r
                             *(colon + 1) != L'!')\r
                             PostMessage(hwnd,\r
                                         KHUI_WM_NC_NOTIFY,\r
@@ -1101,7 +1833,7 @@ nc_handle_wm_command(HWND hwnd,
                                                            &credtype)) &&\r
                         KHM_SUCCEEDED(khui_cw_find_type(d->nc,\r
                                                         credtype, &t))) {\r
-                        if (t->ordinal != d->ctab)\r
+                        if (t->ordinal != d->current_panel)\r
                             PostMessage(hwnd,\r
                                         KHUI_WM_NC_NOTIFY,\r
                                         MAKEWPARAM(t->ordinal,\r
@@ -1127,17 +1859,6 @@ nc_handle_wm_command(HWND hwnd,
             }\r
             return FALSE;\r
 #endif\r
-\r
-        default:\r
-            /* if one of the tab strip buttons were pressed, then\r
-               we should switch to that panel */\r
-            id = LOWORD(wParam);\r
-            if(id >= NC_TS_CTRL_ID_MIN && id <= NC_TS_CTRL_ID_MAX) {\r
-                id -= NC_TS_CTRL_ID_MIN;\r
-                PostMessage(hwnd, KHUI_WM_NC_NOTIFY, \r
-                            MAKEWPARAM(id, WMNC_DIALOG_SWITCH_PANEL),0);\r
-                return FALSE;\r
-            }\r
         }\r
         break;\r
     }\r
@@ -1155,7 +1876,7 @@ static LRESULT nc_handle_wm_moving(HWND hwnd,
     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);\r
 \r
     nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0, WMNC_DIALOG_MOVE), (LPARAM) d->nc);\r
+                    MAKEWPARAM(0, WMNC_DIALOG_MOVE), (LPARAM) d->nc, TRUE);\r
 \r
     return FALSE;\r
 }\r
@@ -1166,9 +1887,7 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
                                LPARAM lParam)\r
 {\r
     khui_nc_wnd_data * d;\r
-    RECT r;\r
-    int width, height;\r
-    khm_size id;\r
+    int id;\r
 \r
     d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);\r
 \r
@@ -1176,37 +1895,21 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
 \r
     case WMNC_DIALOG_SWITCH_PANEL:\r
         id = LOWORD(wParam);\r
-        if(id >= 0 && id <= d->nc->n_types) {\r
+        if(id >= 0 && id <= (int) d->nc->n_types) {\r
             /* one of the tab buttons were pressed */\r
-            if(d->ctab == id) {\r
-                return TRUE; /* nothign to do */\r
+            if(d->current_panel == id) {\r
+                return TRUE; /* nothing to do */\r
             }\r
 \r
-            if(d->ctab == 0) {\r
-                ShowWindow(d->dlg_main, SW_HIDE);\r
-                SendMessage(d->hwnd_tc_main, \r
-                            BM_SETCHECK, BST_UNCHECKED, 0);\r
-            } else {\r
-                ShowWindow(d->nc->types[d->ctab - 1]->hwnd_panel, SW_HIDE);\r
-                SendMessage(d->nc->types[d->ctab - 1]->hwnd_tc, \r
-                            BM_SETCHECK, BST_UNCHECKED, 0);\r
-            }\r
+            d->current_panel = id;\r
 \r
-            d->ctab = id;\r
-\r
-            if(d->ctab == 0) {\r
-                ShowWindow(d->dlg_main, SW_SHOW);\r
-                SendMessage(d->hwnd_tc_main, \r
-                            BM_SETCHECK, BST_CHECKED, 0);\r
-            } else {\r
-                ShowWindow(d->nc->types[id - 1]->hwnd_panel, SW_SHOW);\r
-                SendMessage(d->nc->types[id - 1]->hwnd_tc, \r
-                            BM_SETCHECK, BST_CHECKED, 0);\r
-            }\r
+            TabCtrl_SetCurSel(d->tab_wnd, id);\r
         }\r
 \r
-        if(d->nc->mode == KHUI_NC_MODE_EXPANDED)\r
+        if(d->nc->mode == KHUI_NC_MODE_EXPANDED) {\r
+            nc_layout_new_cred_window(d);\r
             return TRUE;\r
+        }\r
         /*else*/\r
         /* fallthrough */\r
 \r
@@ -1219,47 +1922,16 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
 \r
         d->nc->mode = KHUI_NC_MODE_EXPANDED;\r
 \r
-        r.top = 0;\r
-        r.left = 0;\r
-        r.right = NCDLG_WIDTH + NCDLG_BBAR_WIDTH;\r
-        r.bottom = NCDLG_HEIGHT + NCDLG_TAB_HEIGHT;\r
-\r
-        MapDialogRect(d->dlg_main, &r);\r
-\r
-        width = r.right - r.left;\r
-        height = r.bottom - r.top;\r
-\r
-        /* adjust width and height to accomodate NC area */\r
-        {\r
-            RECT wr,cr;\r
-\r
-            GetWindowRect(hwnd, &wr);\r
-            GetClientRect(hwnd, &cr);\r
+        nc_notify_clear(d);\r
 \r
-            /* the non-client and client areas have already been\r
-               calculated at this point.  We just use the difference\r
-               to adjust the width and height */\r
-            width += (wr.right - wr.left) - (cr.right - cr.left);\r
-            height += (wr.bottom - wr.top) - (cr.bottom - cr.top);\r
-        }\r
-\r
-        SendMessage(d->dlg_main, \r
-                    KHUI_WM_NC_NOTIFY, \r
-                    MAKEWPARAM(0,WMNC_DIALOG_EXPAND), \r
-                    0);\r
+        nc_layout_main_panel(d);\r
 \r
-        SetWindowPos(hwnd, \r
-                     NULL, \r
-                     0, 0, \r
-                     width, height, \r
-                     SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | \r
-                     SWP_NOZORDER);\r
+        nc_layout_new_cred_window(d);\r
 \r
-        ShowWindow(d->dlg_bb, SW_SHOW);\r
-        ShowWindow(d->dlg_ts, SW_SHOW);\r
         break;\r
 \r
     case WMNC_DIALOG_SETUP:\r
+\r
         if(d->nc->n_types > 0) {\r
             khm_size i;\r
             for(i=0; i < d->nc->n_types;i++) {\r
@@ -1281,150 +1953,79 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
                 }\r
             }\r
         }\r
+\r
         break;\r
 \r
     case WMNC_DIALOG_ACTIVATE:\r
         {\r
-            int x,y,width,height;\r
-            RECT r;\r
-            int id;\r
-            wchar_t wbuf[256];\r
-            HFONT hf;\r
-\r
-            /* now we create all the tab strip controls */\r
-            r.left = 0;\r
-            r.top = 0;\r
-            r.right = NCDLG_TAB_WIDTH;\r
-            r.bottom = NCDLG_TAB_HEIGHT;\r
-            MapDialogRect(d->dlg_main, &r);\r
-\r
-            width = r.right - r.left;\r
-            height = r.bottom - r.top;\r
-\r
-            x = 0;\r
-            y = 0;\r
+            wchar_t wname[KCDB_MAXCCH_NAME];\r
+            TCITEM tabitem;\r
+            khm_int32 t;\r
 \r
-            id = NC_TS_CTRL_ID_MIN;\r
+            /* About to activate the window. We should add all the\r
+               panels to the tab control.  */\r
 \r
-            /* if we have too many buttons than would fit on the\r
-               button bar, we have to adjust the width of the buttons.\r
-               Of course, having too many of them would be bad and\r
-               make the buttons fairly useless.  This is just an\r
-               interim measure. */\r
+#ifdef DEBUG\r
+            assert(d->tab_wnd != NULL);\r
+#endif\r
 \r
-            khui_cw_lock_nc(d->nc);\r
+            ZeroMemory(&tabitem, sizeof(tabitem));\r
 \r
-            GetWindowRect(d->dlg_ts, &r);\r
-            if (x + width * (d->nc->n_types + 1) > (khm_size) (r.right - r.left)) {\r
-                width = (int)(((r.right - r.left) - x) / (d->nc->n_types + 1));\r
-            }\r
+            tabitem.mask = TCIF_PARAM | TCIF_TEXT;\r
 \r
-            /* first, the control for the main panel */\r
             LoadString(khm_hInstance, IDS_NC_IDENTITY, \r
-                       wbuf, ARRAYLENGTH(wbuf));\r
-\r
-            d->hwnd_tc_main = \r
-                CreateWindow(L"BUTTON",\r
-                             wbuf,\r
-                             WS_VISIBLE | WS_CHILD | WS_TABSTOP |\r
-                             BS_PUSHLIKE | BS_CHECKBOX | BS_TEXT,\r
-                             x,y,width,height,\r
-                             d->dlg_ts,\r
-                             (HMENU)(INT_PTR) id,\r
-                             khm_hInstance,\r
-                             NULL);\r
+                       wname, ARRAYLENGTH(wname));\r
 \r
-            hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0);\r
-            SendMessage(d->hwnd_tc_main, WM_SETFONT, (WPARAM) hf, 0);\r
-            SendMessage(d->hwnd_tc_main, BM_SETCHECK, BST_CHECKED, 0);\r
+            tabitem.pszText = wname;\r
+            tabitem.lParam = 0; /* ordinal */\r
+\r
+            TabCtrl_InsertItem(d->tab_wnd, 0, &tabitem);\r
 \r
-            id++;\r
-            x += width;\r
+            khui_cw_lock_nc(d->nc);\r
 \r
             if(d->nc->n_types > 0) {\r
                 khm_size i;\r
-                /* we should sort the tabs first */\r
+\r
+                /* We should sort the tabs first.  See\r
+                   nc_tab_sort_func() for sort criteria. */\r
                 qsort(d->nc->types, \r
                       d->nc->n_types, \r
                       sizeof(*(d->nc->types)), \r
                       nc_tab_sort_func);\r
 \r
                 for(i=0; i < d->nc->n_types;i++) {\r
-                    wchar_t * name = NULL;\r
 \r
                     d->nc->types[i]->ordinal = i + 1;\r
 \r
                     if(d->nc->types[i]->name)\r
-                        name = d->nc->types[i]->name;\r
+                        tabitem.pszText = d->nc->types[i]->name;\r
                     else {\r
                         khm_size cbsize;\r
 \r
-                        if(kcdb_credtype_describe\r
-                           (d->nc->types[i]->type, \r
-                            NULL, \r
-                            &cbsize, \r
-                            KCDB_TS_SHORT) == KHM_ERROR_TOO_LONG) {\r
-\r
-                            name = PMALLOC(cbsize);\r
-                            kcdb_credtype_describe(d->nc->types[i]->type, \r
-                                                   name, \r
-                                                   &cbsize, \r
-                                                   KCDB_TS_SHORT);\r
-                        } else {\r
+                        cbsize = sizeof(wname);\r
+\r
+                        if(KHM_FAILED\r
+                           (kcdb_credtype_describe\r
+                            (d->nc->types[i]->type, \r
+                             wname, \r
+                             &cbsize, \r
+                             KCDB_TS_SHORT))) {\r
+\r
 #ifdef DEBUG\r
                             assert(FALSE);\r
 #endif\r
-                            continue;\r
-                        }\r
-                    }\r
+                            wname[0] = L'\0';\r
 \r
-                    d->nc->types[i]->hwnd_tc = \r
-                        CreateWindow(L"BUTTON",\r
-                                     name,\r
-                                     WS_VISIBLE | WS_CHILD | WS_TABSTOP |\r
-                                     BS_PUSHLIKE | BS_CHECKBOX | BS_TEXT |\r
-                                     ((d->nc->types[i]->hwnd_panel == NULL)? \r
-                                      WS_DISABLED : 0),\r
-                                     x,y,width,height,\r
-                                     d->dlg_ts,\r
-                                     (HMENU)(INT_PTR) id,\r
-                                     khm_hInstance,\r
-                                     NULL);\r
-\r
-                    SendMessage(d->nc->types[i]->hwnd_tc, WM_SETFONT, \r
-                                (WPARAM)hf, 0);\r
-\r
-#if 0\r
-                    if(d->nc->types[i]->flags & KHUI_NCT_FLAG_DISABLED)\r
-                        SendMessage(d->nc->types[i]->hwnd_tc, \r
-                                    BM_SETIMAGE, \r
-                                    IMAGE_ICON, \r
-                                    LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_DISABLED)));\r
-                    else\r
-                        SendMessage(d->nc->types[i]->hwnd_tc, \r
-                                    BM_SETIMAGE, \r
-                                    IMAGE_ICON, \r
-                                    LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_ENABLED)));\r
-#endif\r
+                        }\r
 \r
-                    id++;\r
-                    x += width;\r
+                        tabitem.pszText = wname;\r
 \r
-                    if(!(d->nc->types[i]->name))\r
-                        PFREE(name);\r
+                    }\r
 \r
-                    /* Now set the position of the type panel */\r
-                    ShowWindow(d->nc->types[i]->hwnd_panel, SW_HIDE);\r
-                    SetWindowPos(d->nc->types[i]->hwnd_panel, \r
-                                 NULL,\r
-                                 d->r_main.left, \r
-                                 d->r_main.top,\r
-                                 d->r_main.right - d->r_main.left,\r
-                                 d->r_main.bottom - d->r_main.top,\r
-                                 SWP_DEFERERASE | SWP_NOACTIVATE | \r
-                                 SWP_NOOWNERZORDER | SWP_NOREDRAW | \r
-                                 SWP_NOZORDER);\r
+                    tabitem.lParam = d->nc->types[i]->ordinal;\r
 \r
+                    TabCtrl_InsertItem(d->tab_wnd, d->nc->types[i]->ordinal,\r
+                                       &tabitem);\r
                 }\r
             }\r
 \r
@@ -1432,7 +2033,39 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
 \r
             nc_update_credtext(d);\r
 \r
+            TabCtrl_SetCurSel(d->tab_wnd, 0); /* the first selected\r
+                                                 tab is the main\r
+                                                 panel. */\r
+\r
+            /* bring the window to the top, if necessary */\r
+            if (KHM_SUCCEEDED(khc_read_int32(NULL,\r
+                                             L"CredWindow\\Windows\\NewCred\\ForceToTop",\r
+                                             &t)) &&\r
+\r
+                t != 0 &&\r
+\r
+                !khm_is_dialog_active()) {\r
+\r
+                /* if the main window is not visible, then the SetWindowPos()\r
+                   call is sufficient to bring the new creds window to the\r
+                   top.  However, if the main window is visible but not\r
+                   active, the main window needs to be activated before a\r
+                   child window can be activated. */\r
+                khm_activate_main_window();\r
+\r
+                SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,\r
+                             (SWP_NOMOVE | SWP_NOSIZE));\r
+            }\r
+\r
             ShowWindow(hwnd, SW_SHOWNOACTIVATE);\r
+\r
+            /* we don't enable animations until a specific timeout\r
+               elapses after showing the window.  We don't need to\r
+               animate any size changes if the user has barely had a\r
+               chance to notice the original size. This prevents the\r
+               new cred window from appearing in an animated state. */\r
+            SetTimer(hwnd, NC_TIMER_ENABLEANIMATE, ENABLEANIMATE_TIMEOUT, NULL);\r
+\r
             SetFocus(hwnd);\r
 \r
             if (d->nc->n_identities == 0)\r
@@ -1446,7 +2079,8 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
             BOOL okEnable = FALSE;\r
 \r
             nc_notify_types(d->nc, KHUI_WM_NC_NOTIFY,\r
-                            MAKEWPARAM(0, WMNC_IDENTITY_CHANGE), (LPARAM) d->nc);\r
+                            MAKEWPARAM(0, WMNC_IDENTITY_CHANGE), (LPARAM) d->nc,\r
+                            TRUE);\r
 \r
             if (d->nc->subtype == KMSG_CRED_NEW_CREDS &&\r
                 d->nc->n_identities > 0 &&\r
@@ -1503,9 +2137,11 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
 \r
             khui_cw_unlock_nc(d->nc);\r
 \r
-            d->r_credtext.top = d->r_idspec.bottom;\r
+            SetRectEmpty(&d->r_custprompt);\r
 \r
-            nc_position_credtext(d);\r
+            nc_layout_main_panel(d);\r
+\r
+            nc_layout_new_cred_window(d);\r
         }\r
         break;\r
 \r
@@ -1521,6 +2157,10 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
             /* we assume that WMNC_CLEAR_PROMPTS has already been\r
                received */\r
 \r
+#ifdef DEBUG\r
+            assert(IsRectEmpty(&d->r_custprompt));\r
+#endif\r
+\r
             khui_cw_lock_nc(d->nc);\r
 \r
 #if 0\r
@@ -1542,17 +2182,12 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
 #endif\r
             /* for everything else */\r
 \r
-            /* hide the stock password controls */\r
-#if 0\r
-            /* TAGREMOVE */\r
-            hw = GetDlgItem(d->dlg_main, IDC_NC_PASSWORD);\r
-            ShowWindow(hw, SW_HIDE);\r
-            hw = GetDlgItem(d->dlg_main, IDC_NC_PASSWORD_LABEL);\r
-            ShowWindow(hw, SW_HIDE);\r
-#endif\r
-\r
             y = d->r_idspec.bottom;\r
 \r
+            d->r_custprompt.left = d->r_area.left;\r
+            d->r_custprompt.right = d->r_area.right;\r
+            d->r_custprompt.top = y;\r
+\r
             hf = (HFONT) SendMessage(d->dlg_main, WM_GETFONT, 0, 0);\r
 \r
             if (d->nc->pname != NULL) {\r
@@ -1752,9 +2387,14 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
 \r
             khui_cw_unlock_nc(d->nc);\r
 \r
-            d->r_credtext.top = y;\r
+            d->r_custprompt.bottom = y;\r
+\r
+            if (d->r_custprompt.bottom == d->r_custprompt.top)\r
+                SetRectEmpty(&d->r_custprompt);\r
+\r
+            nc_layout_main_panel(d);\r
 \r
-            nc_position_credtext(d);\r
+            nc_layout_new_cred_window(d);\r
         }\r
         break;\r
 \r
@@ -1778,7 +2418,7 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
                 EnableWindow(hw, TRUE);\r
                 hw = GetDlgItem(d->dlg_main, IDCANCEL);\r
                 EnableWindow(hw, TRUE);\r
-                hw = GetDlgItem(d->dlg_main, IDC_NC_OPTIONS);\r
+                hw = GetDlgItem(d->dlg_main, IDC_NC_ADVANCED);\r
                 EnableWindow(hw, TRUE);\r
                 hw = GetDlgItem(d->dlg_bb, IDOK);\r
                 EnableWindow(hw, TRUE);\r
@@ -1811,11 +2451,260 @@ static LRESULT nc_handle_wm_nc_notify(HWND hwnd,
             nc_add_control_row(d, row->label, row->input, row->size);\r
         }\r
         break;\r
+\r
+    case WMNC_UPDATE_LAYOUT:\r
+        {\r
+\r
+            RECT r_client;\r
+            khm_int32 animate;\r
+            khm_int32 steps;\r
+            khm_int32 timeout;\r
+\r
+            /* We are already adjusting the size of the window.  The\r
+               next time the timer fires, it will notice if the target\r
+               size has changed. */\r
+            if (d->size_changing)\r
+                return TRUE;\r
+\r
+            GetClientRect(hwnd, &r_client);\r
+\r
+            if ((r_client.right - r_client.left ==\r
+                 d->r_required.right - d->r_required.left) &&\r
+                (r_client.bottom - r_client.top ==\r
+                 d->r_required.bottom - d->r_required.top)) {\r
+\r
+                /* the window is already at the right size */\r
+                return TRUE;\r
+\r
+            }\r
+\r
+            if (!IsWindowVisible(hwnd)) {\r
+                /* The window is not visible yet.  There's no need to\r
+                   animate anything. */\r
+\r
+                animate = FALSE;\r
+\r
+            } else if (KHM_FAILED(khc_read_int32(NULL,\r
+                                                 L"CredWindow\\Windows\\NewCred\\AnimateSizeChanges",\r
+                                                 &animate))) {\r
+#ifdef DEBUG\r
+                assert(FALSE);\r
+#endif\r
+                animate = TRUE;\r
+            }\r
+\r
+            /* if we aren't animating the window resize, then we just\r
+               do it in one call. */\r
+            if (!animate || !d->animation_enabled) {\r
+                RECT r_window;\r
+\r
+                CopyRect(&r_window, &d->r_required);\r
+                AdjustWindowRectEx(&r_window, NC_WINDOW_STYLES, FALSE,\r
+                                   NC_WINDOW_EX_STYLES);\r
+\r
+                SetWindowPos(hwnd, NULL, 0, 0,\r
+                             r_window.right - r_window.left,\r
+                             r_window.bottom - r_window.top,\r
+                             SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |\r
+                             SWP_NOZORDER);\r
+\r
+                return TRUE;\r
+            }\r
+\r
+            if (KHM_FAILED(khc_read_int32(NULL,\r
+                                          L"CredWindow\\Windows\\NewCred\\AnimationSteps",\r
+                                          &steps))) {\r
+#ifdef DEBUG\r
+                assert(FALSE);\r
+#endif\r
+                steps = NC_SZ_STEPS_DEF;\r
+            } else {\r
+\r
+                if (steps < NC_SZ_STEPS_MIN)\r
+                    steps = NC_SZ_STEPS_MIN;\r
+                else if (steps > NC_SZ_STEPS_MAX)\r
+                    steps = NC_SZ_STEPS_MAX;\r
+\r
+            }\r
+\r
+            if (KHM_FAILED(khc_read_int32(NULL,\r
+                                          L"CredWindow\\Windows\\NewCred\\AnimationStepTimeout",\r
+                                          &timeout))) {\r
+#ifdef DEBUG\r
+                assert(FALSE);\r
+#endif\r
+                timeout = NC_SZ_TIMEOUT_DEF;\r
+            } else {\r
+\r
+                if (timeout < NC_SZ_TIMEOUT_MIN)\r
+                    timeout = NC_SZ_TIMEOUT_MIN;\r
+                else if (timeout > NC_SZ_TIMEOUT_MAX)\r
+                    timeout = NC_SZ_TIMEOUT_MAX;\r
+\r
+            }\r
+\r
+            CopyRect(&d->sz_ch_source, &r_client);\r
+            OffsetRect(&d->sz_ch_source, -d->sz_ch_source.left, -d->sz_ch_source.top);\r
+            CopyRect(&d->sz_ch_target, &d->r_required);\r
+            OffsetRect(&d->sz_ch_target, -d->sz_ch_target.left, -d->sz_ch_target.top);\r
+            d->sz_ch_increment = 0;\r
+            d->sz_ch_max = steps;\r
+            d->sz_ch_timeout = timeout;\r
+            d->size_changing = TRUE;\r
+\r
+            SetTimer(hwnd, NC_TIMER_SIZER, timeout, NULL);\r
+        }\r
+        break;\r
     } /* switch(HIWORD(wParam)) */\r
 \r
     return TRUE;\r
 }\r
 \r
+static LRESULT nc_handle_wm_timer(HWND hwnd,\r
+                                  UINT uMsg,\r
+                                  WPARAM wParam,\r
+                                  LPARAM lParam) {\r
+    khui_nc_wnd_data * d;\r
+\r
+    d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);\r
+\r
+    if (wParam == NC_TIMER_SIZER) {\r
+\r
+        RECT r_now;\r
+\r
+        /* are we done with this sizing operation? */\r
+        if (!d->size_changing ||\r
+            d->sz_ch_increment >= d->sz_ch_max) {\r
+\r
+            d->size_changing = FALSE;\r
+            KillTimer(hwnd, NC_TIMER_SIZER);\r
+            return 0;\r
+        }\r
+\r
+        /* have the requirements changed while we were processing the\r
+           sizing operation? */\r
+        if ((d->r_required.right - d->r_required.left !=\r
+             d->sz_ch_target.right)\r
+\r
+            ||\r
+\r
+            (d->r_required.bottom - d->r_required.top !=\r
+             d->sz_ch_target.bottom)) {\r
+\r
+            /* the target size has changed.  we need to restart the\r
+               sizing operation. */\r
+\r
+            RECT r_client;\r
+\r
+            GetClientRect(hwnd, &r_client);\r
+\r
+            CopyRect(&d->sz_ch_source, &r_client);\r
+            OffsetRect(&d->sz_ch_source, -d->sz_ch_source.left, -d->sz_ch_source.top);\r
+            CopyRect(&d->sz_ch_target, &d->r_required);\r
+            OffsetRect(&d->sz_ch_target, -d->sz_ch_target.left, -d->sz_ch_target.top);\r
+            d->sz_ch_increment = 0;\r
+\r
+            /* leave the other fields alone */\r
+\r
+#ifdef DEBUG\r
+            assert(d->sz_ch_max >= NC_SZ_STEPS_MIN);\r
+            assert(d->sz_ch_max <= NC_SZ_STEPS_MAX);\r
+            assert(d->sz_ch_timeout >= NC_SZ_TIMEOUT_MIN);\r
+            assert(d->sz_ch_timeout <= NC_SZ_TIMEOUT_MAX);\r
+            assert(d->size_changing);\r
+#endif\r
+        }\r
+\r
+        /* we are going to do the next increment */\r
+        d->sz_ch_increment ++;\r
+\r
+        /* now, figure out the size of the client area for this\r
+           step */\r
+\r
+        r_now.left = 0;\r
+        r_now.top = 0;\r
+\r
+#define PROPORTION(v1, v2, i, s) (((v1) * ((s) - (i)) + (v2) * (i)) / (s))\r
+\r
+        r_now.right = PROPORTION(d->sz_ch_source.right, d->sz_ch_target.right,\r
+                                 d->sz_ch_increment, d->sz_ch_max);\r
+\r
+        r_now.bottom = PROPORTION(d->sz_ch_source.bottom, d->sz_ch_target.bottom,\r
+                                  d->sz_ch_increment, d->sz_ch_max);\r
+\r
+#undef  PROPORTION\r
+\r
+#ifdef DEBUG\r
+        {\r
+            long dx = (r_now.right - d->sz_ch_target.right) *\r
+                (d->sz_ch_source.right - d->sz_ch_target.right);\r
+\r
+            long dy = (r_now.bottom - d->sz_ch_target.bottom) *\r
+                (d->sz_ch_source.bottom - d->sz_ch_target.bottom);\r
+\r
+            if (dx < 0 || dy < 0) {\r
+                KillTimer(hwnd, NC_TIMER_SIZER);\r
+                assert(dx >= 0);\r
+                assert(dy >= 0);\r
+                SetTimer(hwnd, NC_TIMER_SIZER, d->sz_ch_timeout, NULL);\r
+            }\r
+        }\r
+#endif\r
+\r
+        AdjustWindowRectEx(&r_now, NC_WINDOW_STYLES, FALSE,\r
+                           NC_WINDOW_EX_STYLES);\r
+\r
+        SetWindowPos(hwnd, NULL,\r
+                     0, 0,\r
+                     r_now.right - r_now.left,\r
+                     r_now.bottom - r_now.top,\r
+                     SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER |\r
+                     SWP_NOZORDER);\r
+\r
+        /* and now we wait for the next timer message */\r
+\r
+        return 0;\r
+    } else if (wParam == NC_TIMER_ENABLEANIMATE) {\r
+\r
+        d->animation_enabled = TRUE;\r
+        KillTimer(hwnd, NC_TIMER_ENABLEANIMATE);\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+static LRESULT nc_handle_wm_notify(HWND hwnd,\r
+                                   UINT uMsg,\r
+                                   WPARAM wParam,\r
+                                   LPARAM lParam) {\r
+\r
+    LPNMHDR nmhdr;\r
+    khui_nc_wnd_data * d;\r
+\r
+    d = (khui_nc_wnd_data *)(LONG_PTR) GetWindowLongPtr(hwnd, CW_PARAM);\r
+    nmhdr = (LPNMHDR) lParam;\r
+\r
+    if (nmhdr->code == TCN_SELCHANGE) {\r
+        /* the current tab has changed. */\r
+        int idx;\r
+        TCITEM tcitem;\r
+\r
+        idx = TabCtrl_GetCurSel(d->tab_wnd);\r
+        ZeroMemory(&tcitem, sizeof(tcitem));\r
+\r
+        tcitem.mask = TCIF_PARAM;\r
+        TabCtrl_GetItem(d->tab_wnd, idx, &tcitem);\r
+\r
+        d->current_panel = (int) tcitem.lParam;\r
+\r
+        nc_layout_new_cred_window(d);\r
+\r
+        return TRUE;\r
+    }\r
+\r
+    return FALSE;\r
+}\r
+\r
 static LRESULT nc_handle_wm_help(HWND hwnd,\r
                                  UINT uMsg,\r
                                  WPARAM wParam,\r
@@ -1832,7 +2721,7 @@ static LRESULT nc_handle_wm_help(HWND hwnd,
         IDOK, IDH_NC_OK,\r
         IDCANCEL, IDH_NC_CANCEL,\r
         IDC_NC_HELP, IDH_NC_HELP,\r
-        IDC_NC_OPTIONS, IDH_NC_OPTIONS,\r
+        IDC_NC_ADVANCED, IDH_NC_ADVANCED,\r
         IDC_NC_CREDTEXT, IDH_NC_CREDWND,\r
         0\r
     };\r
@@ -1898,10 +2787,16 @@ static LRESULT CALLBACK nc_window_proc(HWND hwnd,
     case WM_COMMAND:\r
         return nc_handle_wm_command(hwnd, uMsg, wParam, lParam);\r
 \r
+    case WM_NOTIFY:\r
+        return nc_handle_wm_notify(hwnd, uMsg, wParam, lParam);\r
+\r
     case WM_MOVE:\r
     case WM_MOVING:\r
         return nc_handle_wm_moving(hwnd, uMsg, wParam, lParam);\r
 \r
+    case WM_TIMER:\r
+        return nc_handle_wm_timer(hwnd, uMsg, wParam, lParam);\r
+\r
     case WM_HELP:\r
         return nc_handle_wm_help(hwnd, uMsg, wParam, lParam);\r
 \r
@@ -1925,7 +2820,7 @@ void khm_register_newcredwnd_class(void)
     wcx.hInstance = khm_hInstance;\r
     wcx.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP));\r
     wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);\r
-    wcx.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);\r
+    wcx.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);\r
     wcx.lpszMenuName = NULL;\r
     wcx.lpszClassName = KHUI_NEWCREDWND_CLASS;\r
     wcx.hIconSm = NULL;\r
@@ -1956,10 +2851,10 @@ HWND khm_create_newcredwnd(HWND parent, khui_new_creds * c)
                        ARRAYLENGTH(wtitle));\r
     }\r
 \r
-    hwnd = CreateWindowEx(WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP,\r
+    hwnd = CreateWindowEx(NC_WINDOW_EX_STYLES,\r
                           MAKEINTATOM(khui_newcredwnd_cls),\r
                           ((c->window_title)?c->window_title: wtitle),\r
-                          WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN,\r
+                          NC_WINDOW_STYLES,\r
                           0,0,400,400,    /* bogus values.  the window\r
                                              is going to resize and\r
                                              reposition itself\r
@@ -1988,6 +2883,6 @@ void khm_prep_newcredwnd(HWND hwnd)
 void khm_show_newcredwnd(HWND hwnd)\r
 {\r
     /* add all the panels in and prep UI */\r
-    SendMessage(hwnd, KHUI_WM_NC_NOTIFY, \r
+    PostMessage(hwnd, KHUI_WM_NC_NOTIFY, \r
                 MAKEWPARAM(0, WMNC_DIALOG_ACTIVATE), 0);\r
 }\r
index 7813e1c19ac40fbd93e72695de21a9b9327f3127..46ac831697bedfe3d364c337bf23c154754e6ae4 100644 (file)
 \r
 #define KHUI_NEWCREDWND_CLASS L"KhmNewCredWnd"\r
 \r
+typedef enum tag_nc_notification_types {\r
+    NC_NOTIFY_NONE = 0,         /* no notification */\r
+    NC_NOTIFY_MARQUEE,          /* marquee type notification */\r
+    NC_NOTIFY_PROGRESS,         /* progress notification */\r
+    NC_NOTIFY_MESSAGE,          /* a message */\r
+} nc_notification_type;\r
+\r
 typedef struct khui_nc_wnd_data_t {\r
     khui_new_creds * nc;\r
 \r
+    /* The tab control */\r
+\r
+    HWND tab_wnd;               /* tab control */\r
+    int current_panel;          /* ordinal of the current panel being\r
+                                   displayed. */\r
+\r
+    /* The main panel */\r
     HWND dlg_main;              /* main dialog */\r
-    RECT r_main;\r
-    HWND dlg_bb;                /* button bar */\r
-    RECT r_bb;\r
-    HWND dlg_ts;                /* tab strip */\r
-    RECT r_ts;\r
+    RECT r_main;                /* the extent of the main panel that\r
+                                   we have used so far.  The rect\r
+                                   includes the size of the area used\r
+                                   by the identity selector controls,\r
+                                   the custom controls added by\r
+                                   credentials providers and the\r
+                                   buttons that may be required when\r
+                                   in the mini mode. */\r
+    RECT r_required;            /* required size of the main window */\r
+\r
+    /* The button bar */\r
 \r
-    khm_size ctab;              /* current tab */\r
+    HWND dlg_bb;                /* button bar */\r
 \r
-    HWND hwnd_tc_main;        /* tab control button for main dialog */\r
+    /* Sizing the new credentials window */\r
+\r
+    BOOL animation_enabled;     /* Flag indicating whether animation\r
+                                   is enabled for the dialg.  If this\r
+                                   flag is off, we don't animate size\r
+                                   changes even if the configuration\r
+                                   says so. */\r
+    BOOL size_changing;         /* flag indicating that the size of\r
+                                   the main window is being\r
+                                   adjusted. */\r
+    RECT sz_ch_source;          /* Source size, from which we are\r
+                                   going towards target size in\r
+                                   sz_ch_max steps. The RECT is self\r
+                                   relative (i.e. left=0 and top=0)*/\r
+    RECT sz_ch_target;          /* If we are doing an incremental size\r
+                                   change, this holds the target size\r
+                                   that we were going for.  Note that\r
+                                   the target size might change while\r
+                                   we are adjusting the size.  So this\r
+                                   helps keep track of whether we need\r
+                                   to start the size change again. The\r
+                                   RECT is self relative (i.e. left=0\r
+                                   and top=0). */\r
+    int  sz_ch_increment;       /* Current step of the incremental\r
+                                   size change operation. */\r
+    int  sz_ch_max;             /* Max number of steps in the size\r
+                                   change operation. */\r
+    int  sz_ch_timeout;         /* Milliseconds between each increment */\r
+\r
+    /* Custom controls and identity specifiers */\r
 \r
     HWND hwnd_banner;           /* static control for banner */\r
     HWND hwnd_name;             /* static control for name */\r
 \r
     HWND hwnd_last_idspec;      /* last identity specifier control */\r
 \r
-    /* metrics for custom prompts and identity specifiers */\r
+    /* Notification windows */\r
+\r
+    nc_notification_type notif_type; /* Type of notification */\r
+    HWND hwnd_notif_label;      /* Label for notifications */\r
+    HWND hwnd_notif_aux;        /* Other control for notifications */\r
+\r
+    /* Areas of the main panel */\r
 \r
     RECT r_idspec;          /* Area used by identity specifiers\r
                                (relative to client) */\r
-    RECT r_row;             /* Metrics for a control row\r
-                               (top=0,left=0,right=width,\r
-                               bottom=height) */\r
+    RECT r_custprompt;      /* Area used by custom controls (relative\r
+                               to client) */\r
+    RECT r_notif;           /* Area used for notifications. */\r
+\r
+    /* Metrics for custom prompts and identity specifiers */\r
+\r
+    RECT r_row;             /* Metrics for a control row (left=0,\r
+                               top=0, right=width, bottom=height) */\r
     RECT r_area;            /* Area available for controls (relative\r
                                to client) */\r
     RECT r_n_label;         /* coords of the static control (relative\r
@@ -77,10 +137,20 @@ HWND khm_create_newcredwnd(HWND parent, khui_new_creds * c);
 void khm_prep_newcredwnd(HWND hwnd);\r
 void khm_show_newcredwnd(HWND hwnd);\r
 \r
+/* Width of the button bar in dialog units */\r
+#define NCDLG_BBAR_WIDTH 66\r
+/* Height of the button bar in dialog units */\r
+#define NCDLG_BBAR_HEIGHT 181\r
+\r
+/* Control identifier for the tab control in the new credentials\r
+   dialog. We declare this here since we will be creating the control\r
+   manually. */\r
+#define IDC_NC_TABS 8001\r
+\r
 /* This is the first control ID that is created in the custom tabstrip\r
    control buttons.  Subsequent buttons get consecutive IDs starting\r
    from this one.  */\r
-#define NC_TS_CTRL_ID_MIN 8001\r
+#define NC_TS_CTRL_ID_MIN 8002\r
 \r
 /* Maximum number of controls */\r
 #define NC_TS_MAX_CTRLS 8\r
@@ -100,4 +170,20 @@ void khm_show_newcredwnd(HWND hwnd);
 /* the maximum control ID that may be used by an identity provider */\r
 #define NC_IS_CTRL_ID_MAX (NC_IS_CTRL_ID_MIN + NC_IS_MAX_CTRLS - 1)\r
 \r
+#define NC_WINDOW_EX_STYLES (WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP)\r
+#define NC_WINDOW_STYLES    (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN)\r
+\r
+#define NC_SZ_STEPS_MIN 3\r
+#define NC_SZ_STEPS_DEF 10\r
+#define NC_SZ_STEPS_MAX 100\r
+\r
+#define NC_SZ_TIMEOUT_MIN 5\r
+#define NC_SZ_TIMEOUT_DEF 10\r
+#define NC_SZ_TIMEOUT_MAX 500\r
+\r
+#define NC_TIMER_SIZER         1001\r
+#define NC_TIMER_ENABLEANIMATE 1002\r
+\r
+#define ENABLEANIMATE_TIMEOUT  400\r
+\r
 #endif\r
index 9804abfae57561cdb01fdbd42a9ce34562a947dc..2d020bde9909376257057e0f692bc1ae6a05d030 100644 (file)
@@ -17,7 +17,7 @@
  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * ACTION OF CONTRACT TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
  * SOFTWARE.\r
  */\r
 \r
 #define KHUI_NOTIFIER_CLASS         L"KhuiNotifierMsgWindowClass"\r
 #define KHUI_ALERTER_CLASS          L"KhuiAlerterWindowClass"\r
+#define KHUI_ALERTBIN_CLASS         L"KhuiAlertBinWindowClass"\r
 \r
 #define KHUI_NOTIFIER_WINDOW        L"KhuiNotifierMsgWindow"\r
 \r
 /* notifier message for notification icon */\r
 #define KHUI_WM_NOTIFIER            WM_COMMAND\r
 \r
-#define KHUI_ALERT_QUEUE_MAX        64\r
+#define DRAWTEXTOPTIONS (DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK)\r
+\r
+/* are we showing an alert? */\r
+#define ALERT_DISPLAYED() (balloon_alert != NULL || khui_alert_windows != NULL)\r
+\r
+/* Forward declarations */\r
+\r
+struct tag_alerter_wnd_data;\r
+typedef struct tag_alerter_wnd_data alerter_wnd_data;\r
+\r
+struct tag_alert_list;\r
+typedef struct tag_alert_list alert_list;\r
+\r
+static khm_int32 \r
+alert_show(khui_alert * a);\r
+\r
+static khm_int32 \r
+alert_show_minimized(khui_alert * a);\r
+\r
+static khm_int32\r
+alert_show_normal(khui_alert * a);\r
+\r
+static khm_int32\r
+alert_show_list(alert_list * alist);\r
+\r
+static khm_int32\r
+alert_enqueue(khui_alert * a);\r
+\r
+static void\r
+check_for_queued_alerts(void);\r
+\r
+static khm_int32\r
+alert_consolidate(alert_list * alist,\r
+                  khui_alert * alert,\r
+                  khm_boolean add_from_queue);\r
+\r
+/* Globals */\r
 \r
 /* window class registration atom for message only notifier window\r
    class */\r
@@ -45,35 +82,57 @@ ATOM atom_notifier = 0;
 \r
 /* window class registration atom for alert windows */\r
 ATOM atom_alerter = 0;\r
+/* window class registration atom for the alert "bin", which is the\r
+   window that holds all the alerts. */\r
+ATOM atom_alert_bin = 0;\r
 \r
 /* notifier message window */\r
 HWND hwnd_notifier = NULL;\r
 \r
 BOOL notifier_ready = FALSE;\r
 \r
-khm_boolean notifier_modal_loop = FALSE;\r
+/* The list of alert windows currently active */\r
+alerter_wnd_data * khui_alert_windows = NULL;\r
 \r
-khui_alert * current_alert = NULL;\r
+/* Notification icon for when there are no alerts to be displayed */\r
+int  iid_normal = IDI_NOTIFY_NONE;\r
+\r
+/* The alert currently being displayed in a balloon */\r
+khui_alert * balloon_alert;\r
+\r
+/**********************************************************************\r
+  Alert Queue\r
+\r
+  The alert queue is the data structure that keeps track of all the\r
+  alerts that are waiting to be displayed.  Alerts will be placed on\r
+  the queue if they cannot be immediately displayed for some reason\r
+  (e.g. another alert is being displayed, or the user is working in\r
+  another window).\r
+***********************************************************************/\r
+\r
+#define KHUI_ALERT_QUEUE_MAX        64\r
 \r
 khui_alert * alert_queue[KHUI_ALERT_QUEUE_MAX];\r
 khm_int32    alert_queue_head = 0;\r
 khm_int32    alert_queue_tail = 0;\r
 \r
-int  iid_normal = IDI_NOTIFY_NONE;\r
-\r
 #define is_alert_queue_empty() (alert_queue_head == alert_queue_tail)\r
 #define is_alert_queue_full()  (((alert_queue_tail + 1) % KHUI_ALERT_QUEUE_MAX) == alert_queue_head)\r
 \r
+/* NOTE: the alert queue functions are unsafe to call from any thread\r
+   other than the UI thread. */\r
+\r
 static void \r
-add_to_alert_queue(khui_alert * a) {\r
+alert_queue_put_alert(khui_alert * a) {\r
     if (is_alert_queue_full()) return;\r
     alert_queue[alert_queue_tail++] = a;\r
     khui_alert_hold(a);\r
     alert_queue_tail %= KHUI_ALERT_QUEUE_MAX;\r
 }\r
 \r
+/* the caller needs to release the alert that's returned  */\r
 static khui_alert * \r
-del_from_alert_queue(void) {\r
+alert_queue_get_alert(void) {\r
     khui_alert * a;\r
 \r
     if (is_alert_queue_empty()) return NULL;\r
@@ -83,57 +142,140 @@ del_from_alert_queue(void) {
     return a;                   /* held */\r
 }\r
 \r
-static khui_alert * \r
-peek_alert_queue(void) {\r
-    if (is_alert_queue_empty()) return NULL;\r
-    return alert_queue[alert_queue_head];\r
+static int\r
+alert_queue_get_size(void) {\r
+    if (is_alert_queue_empty())\r
+        return 0;\r
+\r
+    if (alert_queue_tail < alert_queue_head) {\r
+        return (alert_queue_tail + KHUI_ALERT_QUEUE_MAX - alert_queue_head);\r
+    } else {\r
+        return alert_queue_tail - alert_queue_head;\r
+    }\r
 }\r
 \r
-static void\r
-check_for_queued_alerts(void) {\r
-    if (!is_alert_queue_empty()) {\r
-        khui_alert * a;\r
+static khui_alert *\r
+alert_queue_get_alert_by_pos(int pos) {\r
+    khui_alert * a;\r
 \r
-        a = peek_alert_queue();\r
+    if (is_alert_queue_empty() ||\r
+        pos >= alert_queue_get_size() ||\r
+        pos < 0) {\r
+        return NULL;\r
+    }\r
 \r
-        if (a->title) {\r
-            HICON hi;\r
-            int res;\r
+    a = alert_queue[(alert_queue_head + pos) % KHUI_ALERT_QUEUE_MAX];\r
+    if (a) {\r
+        khui_alert_hold(a);\r
+    }\r
+    return a;\r
+}\r
 \r
-            if (a->severity == KHERR_ERROR)\r
-                res = OIC_ERROR;\r
-            else if (a->severity == KHERR_WARNING)\r
-                res = OIC_WARNING;\r
-            else\r
-                res = OIC_INFORMATION;\r
+static int\r
+alert_queue_delete_alert(khui_alert * a) {\r
+    int idx;\r
+    int succ;\r
 \r
-            hi = LoadImage(0, MAKEINTRESOURCE(res),\r
-                           IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),\r
-                           LR_SHARED);\r
+    idx = alert_queue_head;\r
+    while(idx != alert_queue_tail) {\r
+        if (alert_queue[idx] == a)\r
+            break;\r
 \r
-            khm_statusbar_set_part(KHUI_SBPART_NOTICE,\r
-                                   hi,\r
-                                   a->title);\r
-        }\r
-    } else {\r
-        khm_statusbar_set_part(KHUI_SBPART_NOTICE,\r
-                               NULL, NULL);\r
+        idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX;\r
+    }\r
+\r
+    if (idx == alert_queue_tail)\r
+        return 0;\r
+\r
+#ifdef DEBUG\r
+    assert(alert_queue[idx]);\r
+#endif\r
+    khui_alert_release(alert_queue[idx]);\r
+\r
+    succ = (idx + 1) % KHUI_ALERT_QUEUE_MAX;\r
+    while(succ != alert_queue_tail) {\r
+        alert_queue[idx] = alert_queue[succ];\r
+\r
+        succ = (succ + 1) % KHUI_ALERT_QUEUE_MAX;\r
+        idx = (idx + 1) % KHUI_ALERT_QUEUE_MAX;\r
     }\r
+\r
+    alert_queue_tail = idx;\r
+    return 1;\r
 }\r
 \r
+/* the caller needs to release the alert that's returned */\r
+static khui_alert * \r
+alert_queue_peek(void) {\r
+    khui_alert * a;\r
 \r
-/* forward dcls */\r
-static khm_int32 \r
-alert_show(khui_alert * a);\r
+    if (is_alert_queue_empty())\r
+        return NULL;\r
 \r
-static khm_int32 \r
-alert_show_minimized(khui_alert * a);\r
+    a = alert_queue[alert_queue_head];\r
+    khui_alert_hold(a);\r
 \r
-static khm_int32\r
-alert_show_normal(khui_alert * a);\r
+    return a;\r
+}\r
+\r
+/**********************************************************************\r
+  Alert List\r
+\r
+  A list of alerts.  Currently has a fixed upper limit, but the limit\r
+  is high enough for now.\r
+***********************************************************************/\r
+\r
+typedef struct tag_alert_list {\r
+    khui_alert * alerts[KHUI_ALERT_QUEUE_MAX];\r
+    int          n_alerts;\r
+    wchar_t      title[KHUI_MAXCCH_TITLE];\r
+} alert_list;\r
+\r
+static void\r
+alert_list_init(alert_list * alist) {\r
+    ZeroMemory(alist, sizeof(*alist));\r
+}\r
+\r
+static void\r
+alert_list_set_title(alert_list * alist, wchar_t * title) {\r
+    StringCbCopy(alist->title, sizeof(alist->title), title);\r
+}\r
 \r
 static khm_int32\r
-alert_enqueue(khui_alert * a);\r
+alert_list_add_alert(alert_list * alist,\r
+                     khui_alert * alert) {\r
+\r
+    if (alist->n_alerts == ARRAYLENGTH(alist->alerts))\r
+        return KHM_ERROR_NO_RESOURCES;\r
+\r
+    khui_alert_hold(alert);\r
+    alist->alerts[alist->n_alerts++] = alert;\r
+\r
+    return KHM_ERROR_SUCCESS;\r
+}\r
+\r
+static void\r
+alert_list_destroy(alert_list * alist) {\r
+    int i;\r
+\r
+    for (i=0; i < alist->n_alerts; i++) {\r
+        if (alist->alerts[i] != NULL) {\r
+            khui_alert_release(alist->alerts[i]);\r
+            alist->alerts[i] = NULL;\r
+        }\r
+    }\r
+\r
+    alist->n_alerts = 0;\r
+}\r
+\r
+\r
+/**********************************************************************\r
+  Notifier Window\r
+\r
+  The notifier window manages the notification icon and handles\r
+  KMSG_ALERT messages sent from the UI library.  The window will exist\r
+  for the lifetime of the application.\r
+***********************************************************************/\r
 \r
 /* These are defined for APPVER >= 0x501.  We are defining them here\r
    so that we can build with APPVER = 0x500 and use the same binaries\r
@@ -156,15 +298,6 @@ alert_enqueue(khui_alert * a);
 #endif\r
 \r
 \r
-/**********************************************************************\r
-  Notifier\r
-***********************************************************************\r
-\r
-The notifier is a message only window that listens for notifier\r
-messages.  This window will exist for the lifetime of the application\r
-and will use alerter windows as needed to show application alerts.\r
-*/\r
-\r
 static LRESULT CALLBACK \r
 notifier_wnd_proc(HWND hwnd,\r
                   UINT uMsg,\r
@@ -182,13 +315,29 @@ notifier_wnd_proc(HWND hwnd,
             /* handle notifier messages */\r
             switch(m->subtype) {\r
             case KMSG_ALERT_SHOW:\r
-                rv = alert_show((khui_alert *) m->vparam);\r
-                khui_alert_release((khui_alert *) m->vparam);\r
+                {\r
+                    khui_alert * a;\r
+\r
+                    a = (khui_alert *) m->vparam;\r
+#ifdef DEBUG\r
+                    assert(a != NULL);\r
+#endif\r
+                    rv = alert_show(a);\r
+                    khui_alert_release(a);\r
+                }\r
                 break;\r
 \r
             case KMSG_ALERT_QUEUE:\r
-                rv = alert_enqueue((khui_alert *) m->vparam);\r
-                khui_alert_release((khui_alert *) m->vparam);\r
+                {\r
+                    khui_alert * a;\r
+\r
+                    a = (khui_alert *) m->vparam;\r
+#ifdef DEBUG\r
+                    assert(a != NULL);\r
+#endif\r
+                    rv = alert_enqueue(a);\r
+                    khui_alert_release(a);\r
+                }\r
                 break;\r
 \r
             case KMSG_ALERT_CHECK_QUEUE:\r
@@ -196,15 +345,42 @@ notifier_wnd_proc(HWND hwnd,
                 break;\r
 \r
             case KMSG_ALERT_SHOW_QUEUED:\r
-                if (current_alert == NULL) {\r
-                    khui_alert * a;\r
-\r
-                    a = del_from_alert_queue();\r
-                    if (a) {\r
-                        rv = alert_show(a);\r
-                        check_for_queued_alerts();\r
-                        khui_alert_release(a);\r
+                if (!ALERT_DISPLAYED()) {\r
+\r
+                    /* show next consolidated batch */\r
+                    alert_list alist;\r
+                    int n;\r
+\r
+                    alert_list_init(&alist);\r
+                    n = alert_consolidate(&alist, NULL, TRUE);\r
+\r
+                    if (n) {\r
+                        if (n == 1) {\r
+                            khui_alert_lock(alist.alerts[0]);\r
+\r
+                            if (alist.alerts[0]->title) {\r
+                                alert_list_set_title(&alist, alist.alerts[0]->title);\r
+                            } else {\r
+                                wchar_t title[KHUI_MAXCCH_TITLE];\r
+                                LoadString(khm_hInstance, IDS_ALERT_DEFAULT,\r
+                                           title, ARRAYLENGTH(title));\r
+                                alert_list_set_title(&alist, title);\r
+                            }\r
+\r
+                            khui_alert_unlock(alist.alerts[0]);\r
+                        } else {\r
+                            wchar_t title[KHUI_MAXCCH_TITLE];\r
+                            LoadString(khm_hInstance, IDS_ALERT_DEFAULT,\r
+                                       title, ARRAYLENGTH(title));\r
+                            alert_list_set_title(&alist, title);\r
+                        }\r
+\r
+                        alert_show_list(&alist);\r
                     }\r
+\r
+                    alert_list_destroy(&alist);\r
+\r
+                    check_for_queued_alerts();\r
                 }\r
                 break;\r
 \r
@@ -213,15 +389,20 @@ notifier_wnd_proc(HWND hwnd,
                     khui_alert * a;\r
 \r
                     a = (khui_alert *) m->vparam;\r
+#ifdef DEBUG\r
+                    assert(a != NULL);\r
+#endif\r
+                    khui_alert_lock(a);\r
                     a->flags |= KHUI_ALERT_FLAG_MODAL;\r
+                    khui_alert_unlock(a);\r
+\r
                     rv = alert_show(a);\r
-                    khui_alert_release(a);\r
 \r
                     if (KHM_SUCCEEDED(rv)) {\r
-                        notifier_modal_loop = TRUE;\r
-\r
-                        khm_message_loop_int(&notifier_modal_loop);\r
+                        khm_message_loop_int(&a->displayed);\r
                     }\r
+\r
+                    khui_alert_release(a);\r
                 }\r
                 break;\r
             }\r
@@ -276,26 +457,40 @@ notifier_wnd_proc(HWND hwnd,
             break;\r
 \r
         case NIN_BALLOONUSERCLICK:\r
-            if (current_alert) {\r
-                if ((current_alert->flags & KHUI_ALERT_FLAG_DEFACTION) &&\r
-                    current_alert->n_alert_commands > 0) {\r
+            if (balloon_alert) {\r
+                khm_notify_icon_change(KHERR_NONE);\r
+\r
+                khui_alert_lock(balloon_alert);\r
+\r
+                if ((balloon_alert->flags & KHUI_ALERT_FLAG_DEFACTION) &&\r
+                    (balloon_alert->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON) &&\r
+                    balloon_alert->n_alert_commands > 0) {\r
                     PostMessage(khm_hwnd_main, WM_COMMAND,\r
-                                MAKEWPARAM(current_alert->alert_commands[0], \r
+                                MAKEWPARAM(balloon_alert->alert_commands[0], \r
                                            0),\r
                                 0);\r
-                } else if (current_alert->flags & \r
+                } else if (balloon_alert->flags & \r
                            KHUI_ALERT_FLAG_REQUEST_WINDOW) {\r
                     khm_show_main_window();\r
-                    alert_show_normal(current_alert);\r
+                    alert_show_normal(balloon_alert);\r
                 }\r
+\r
+                khui_alert_unlock(balloon_alert);\r
+                khui_alert_release(balloon_alert);\r
+                balloon_alert = NULL;\r
+            } else {\r
+#ifdef DEBUG\r
+                assert(FALSE);\r
+#endif\r
             }\r
-            /* fallthrough */\r
+            break;\r
+\r
         case NIN_BALLOONHIDE:\r
         case NIN_BALLOONTIMEOUT:\r
             khm_notify_icon_change(KHERR_NONE);\r
-            if (current_alert) {\r
-                khui_alert_release(current_alert);\r
-                current_alert = NULL;\r
+            if (balloon_alert) {\r
+                khui_alert_release(balloon_alert);\r
+                balloon_alert = NULL;\r
             }\r
             break;\r
         }\r
@@ -342,211 +537,1162 @@ khm_register_notifier_wnd_class(void)
   Alerter\r
 **********************************************************************/\r
 \r
-typedef struct tag_alerter_wnd_data {\r
+typedef struct tag_alerter_alert_data {\r
     khui_alert * alert;\r
 \r
+    BOOL         seen;          /* has the user seen this alert? */\r
+\r
+    BOOL         has_commands;  /* we cache the value here.  otherwise\r
+                                   we'll have to get a lock on the\r
+                                   alert each time we have to find out\r
+                                   whether there are any commands for\r
+                                   this alert. */\r
+\r
+    RECT         r_alert;    /* the entire alert, relative to self. */\r
+\r
+    /* the following rects are relative to the top left of r_alert. */\r
+\r
+    RECT         r_title;       /* the title.  deflate by padding to\r
+                                   get the text rect. */\r
+    RECT         r_icon;        /* rect for icon */\r
+    RECT         r_message;     /* rect for the text. no padding\r
+                                   necessary. */\r
+    RECT         r_suggestion;  /* rect for the suggestion.  deflate\r
+                                   by padding to get the suggestion\r
+                                   rect.  The suggestion rect includes\r
+                                   space for the small icon on the\r
+                                   left and padding between the icon\r
+                                   and the text. The size of the small\r
+                                   icon are as per system metrics\r
+                                   SM_C{X,Y}SMICON. Padding is\r
+                                   s_pad.cx vertical. */\r
+\r
+    int          n_cmd_buttons; /* number of command buttons in this alert. */\r
+\r
+    RECT         r_buttons[KHUI_MAX_ALERT_COMMANDS];\r
+                                /* rects for the command buttons. */\r
+\r
+    HWND         hwnd_buttons[KHUI_MAX_ALERT_COMMANDS];\r
+                                /* handles for the command buttons */\r
+\r
+    LDCL(struct tag_alerter_alert_data);\r
+} alerter_alert_data;\r
+\r
+typedef struct tag_alerter_wnd_data {\r
     HWND            hwnd;\r
     HFONT           hfont;\r
 \r
-    BOOL            metrics_done;\r
+    wchar_t         caption[KHUI_MAXCCH_TITLE]; /* the original\r
+                                                   caption for the\r
+                                                   dialog. */\r
 \r
-    HWND            hwnd_buttons[KHUI_MAX_ALERT_COMMANDS];\r
+    HWND            hw_bin;\r
+    HWND            hw_scroll;\r
+    HWND            hw_close;\r
 \r
-    /* various metrics */\r
+    int             scroll_top;\r
 \r
-    /* calculated during WM_CREATE and adjusted during WM_PAINT */\r
-    int             dy_message;\r
-    int             dy_suggestion;\r
+    int             n_cmd_buttons; /* total number of command buttons\r
+                                      in all the alerts being shown in\r
+                                      this dialog. */\r
 \r
+    /* various metrics */\r
     /* calculated during WM_CREATE */\r
-    int             dx_button;\r
-    int             dy_button;\r
-    int             dx_button_incr;\r
-    int             dx_margin;\r
-    int             dy_margin;\r
-    int             dy_bb;\r
-    int             x_message;\r
-    int             dx_message;\r
-    int             dx_icon;\r
-    int             dy_icon;\r
-    int             dx_suggest_pad;\r
-\r
-    /* calculated during WM_CREATE and adjusted during WM_PAINT */\r
-    int             dx_client;\r
-    int             dy_client;\r
-\r
-    /* calculated during WM_PAINT */\r
-    int             y_message;\r
-    int             y_suggestion;\r
-\r
-    LDCL(struct tag_alerter_wnd_data);\r
-} alerter_wnd_data;\r
+    SIZE            s_button;\r
+    SIZE            s_margin;\r
+    RECT            r_text;     /* only .left and .right are used. rest are 0 */\r
+    RECT            r_title;    /* only .left, .right and .bottom are used. .top=0 */\r
+    SIZE            s_icon;\r
+    SIZE            s_pad;\r
 \r
-alerter_wnd_data * khui_alerts = NULL;\r
+    int             cx_wnd;\r
+    int             cy_max_wnd;\r
+\r
+    /* derived from the alert sizes */\r
+    SIZE            s_alerts;\r
+\r
+    QDCL(alerter_alert_data);   /* queue of alerts that are being\r
+                                   shown in this window. */\r
+\r
+    LDCL(struct tag_alerter_wnd_data); /* for adding to\r
+                                          khui_alert_windows list. */\r
+\r
+    int             n_alerts;\r
+\r
+} alerter_wnd_data;\r
 \r
 #define NTF_PARAM DWLP_USER\r
 \r
 /* dialog sizes in base dialog units */\r
 \r
-#define NTF_MARGIN 5\r
-#define NTF_WIDTH 200\r
+#define NTF_MARGIN          5\r
+#define NTF_WIDTH           200\r
+#define NTF_MAXHEIGHT       150\r
 \r
-#define NTF_BB_HEIGHT 15\r
+#define NTF_TITLE_X         NTF_MARGIN\r
+#define NTF_TITLE_WIDTH     (NTF_WIDTH - NTF_MARGIN*2)\r
+#define NTF_TITLE_HEIGHT    10\r
 \r
-#define NTF_ICON_X NTF_MARGIN\r
-#define NTF_ICON_WIDTH 20\r
-#define NTF_ICON_HEIGHT 20\r
+#define NTF_ICON_WIDTH      20\r
+#define NTF_ICON_HEIGHT     20\r
 \r
-#define NTF_MSG_X (NTF_ICON_X + NTF_ICON_WIDTH + NTF_MARGIN)\r
-#define NTF_MSG_WIDTH ((NTF_WIDTH - NTF_MARGIN) - NTF_MSG_X)\r
-#define NTF_MSG_HEIGHT 15\r
+#define NTF_TEXT_X          (NTF_MARGIN + NTF_ICON_WIDTH + NTF_MARGIN)\r
+#define NTF_TEXT_WIDTH      ((NTF_WIDTH - NTF_MARGIN) - NTF_TEXT_X)\r
+#define NTF_TEXT_PAD        2\r
 \r
-#define NTF_SUG_X NTF_MSG_X\r
-#define NTF_SUG_WIDTH NTF_MSG_WIDTH\r
-#define NTF_SUG_HEIGHT NTF_MSG_HEIGHT\r
-#define NTF_SUG_PAD 2\r
+#define NTF_BUTTON_WIDTH    ((NTF_TEXT_WIDTH - (KHUI_MAX_ALERT_COMMANDS - 1)*NTF_MARGIN) / KHUI_MAX_ALERT_COMMANDS)\r
+#define NTF_BUTTON_HEIGHT   15\r
 \r
-#define NTF_BUTTON_X NTF_MSG_X\r
+#define NTF_TIMEOUT 20000\r
 \r
-#define NTF_BUTTON_WIDTH ((NTF_MSG_WIDTH - 3*NTF_MARGIN) / 4)\r
-#define NTF_BUTTON_XINCR  (NTF_BUTTON_WIDTH + NTF_MARGIN)\r
-#define NTF_BUTTON_HEIGHT (NTF_BB_HEIGHT - NTF_MARGIN)\r
+#define ALERT_WINDOW_EX_SYLES (WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP)\r
+#define ALERT_WINDOW_STYLES   (WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN)\r
 \r
-#define NTF_TIMEOUT 20000\r
+/* Control ids */\r
+#define IDC_NTF_ALERTBIN 998\r
+#define IDC_NTF_CLOSE    999\r
 \r
-static khm_int32 \r
-alert_show_minimized(khui_alert * a) {\r
-    wchar_t tbuf[64];\r
-    wchar_t mbuf[256];\r
+#define IDC_NTF_CMDBUTTONS 1001\r
+#define IDC_FROM_IDX(alert, bn) ((alert) * KHUI_MAX_ALERT_COMMANDS + (bn) + IDC_NTF_CMDBUTTONS)\r
+#define ALERT_FROM_IDC(idc)  (((idc) - IDC_NTF_CMDBUTTONS) / KHUI_MAX_ALERT_COMMANDS)\r
+#define BUTTON_FROM_IDC(idc) (((idc) - IDC_NTF_CMDBUTTONS) % KHUI_MAX_ALERT_COMMANDS)\r
 \r
-    if (a->message == NULL)\r
-        return KHM_ERROR_SUCCESS;\r
+/* if the only command in an alert is "Close", we assume that the\r
+   alert has no commands. */\r
+#define ALERT_HAS_CMDS(a) ((a)->n_alert_commands > 1 || ((a)->n_alert_commands == 1 && (a)->alert_commands[0] != KHUI_PACTION_CLOSE))\r
 \r
-    if (a->title == NULL) {\r
-        LoadString(khm_hInstance, IDS_ALERT_DEFAULT,\r
-                   tbuf, ARRAYLENGTH(tbuf));\r
-    } else {\r
-        StringCbCopy(tbuf, sizeof(tbuf), a->title);\r
-    }\r
+static void\r
+add_alert_to_wnd_data(alerter_wnd_data * d,\r
+                      khui_alert * a) {\r
+    alerter_alert_data * adata;\r
 \r
-    if (FAILED(StringCbCopy(mbuf, sizeof(mbuf), a->message)) ||\r
-        (!(a->flags & KHUI_ALERT_FLAG_DEFACTION) &&\r
-         (a->n_alert_commands > 0 ||\r
-          a->suggestion ||\r
-          (a->flags & KHUI_ALERT_FLAG_VALID_ERROR)))) {\r
-        /* if mbuf wasn't big enough, this should have copied a\r
-           truncated version of it */\r
-        size_t cch_m, cch_p;\r
-        wchar_t postfix[256];\r
+    adata = PMALLOC(sizeof(*adata));\r
+    ZeroMemory(adata, sizeof(*adata));\r
 \r
-        cch_p = LoadString(khm_hInstance, IDS_ALERT_MOREINFO, postfix,\r
-                           ARRAYLENGTH(postfix));\r
-        cch_p++;                /* account for NULL */\r
+    adata->alert = a;\r
+    khui_alert_hold(a);\r
 \r
-        StringCchLength(mbuf, ARRAYLENGTH(mbuf), &cch_m);\r
-        cch_m = min(cch_m, ARRAYLENGTH(mbuf) - cch_p);\r
+    khui_alert_lock(a);\r
 \r
-        StringCchCopy(mbuf + cch_m, ARRAYLENGTH(mbuf) - cch_m,\r
-                      postfix);\r
+    a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW;\r
 \r
-        a->flags |= KHUI_ALERT_FLAG_REQUEST_WINDOW;\r
+    a->displayed = TRUE;\r
+\r
+    khui_alert_unlock(a);\r
+\r
+    QPUT(d, adata);\r
+}\r
+\r
+static alerter_wnd_data *\r
+create_alerter_wnd_data(HWND hwnd, alert_list * l) {\r
+    alerter_wnd_data * d;\r
+    int i;\r
+    LONG dlgb;\r
+\r
+    d = PMALLOC(sizeof(*d));\r
+    ZeroMemory(d, sizeof(*d));\r
+\r
+    d->hwnd = hwnd;\r
+\r
+    GetWindowText(hwnd, d->caption, ARRAYLENGTH(d->caption));\r
+\r
+    for (i=0; i < l->n_alerts; i++) {\r
+        add_alert_to_wnd_data(d, l->alerts[i]);\r
     }\r
 \r
-    a->flags |= KHUI_ALERT_FLAG_DISPLAY_BALLOON;\r
+    d->n_alerts = l->n_alerts;\r
 \r
-#if (_WIN32_IE >= 0x0501)\r
-    current_alert = a;\r
-    khui_alert_hold(a);\r
-#endif\r
+    LPUSH(&khui_alert_windows, d);\r
 \r
-    khm_notify_icon_balloon(a->severity,\r
-                             tbuf,\r
-                             mbuf,\r
-                             NTF_TIMEOUT);\r
+    /* Compute a few metrics first */\r
 \r
-    return KHM_ERROR_SUCCESS;\r
+    dlgb = GetDialogBaseUnits();\r
+\r
+#define DLG2SCNX(x) MulDiv((x), LOWORD(dlgb), 4)\r
+#define DLG2SCNY(y) MulDiv((y), HIWORD(dlgb), 8)\r
+\r
+    d->cx_wnd = DLG2SCNX(NTF_WIDTH);\r
+    d->cy_max_wnd = DLG2SCNY(NTF_MAXHEIGHT);\r
+\r
+    d->s_button.cx = DLG2SCNX(NTF_BUTTON_WIDTH);\r
+    d->s_button.cy = DLG2SCNY(NTF_BUTTON_HEIGHT);\r
+\r
+    d->s_margin.cx = DLG2SCNX(NTF_MARGIN);\r
+    d->s_margin.cy = DLG2SCNY(NTF_MARGIN);\r
+\r
+    d->r_text.left = DLG2SCNX(NTF_TEXT_X);\r
+    d->r_text.right = DLG2SCNX(NTF_TEXT_WIDTH + NTF_TEXT_X);\r
+    d->r_text.top = 0;\r
+    d->r_text.bottom = 0;\r
+\r
+    d->r_title.left = DLG2SCNX(NTF_TITLE_X);\r
+    d->r_title.right = DLG2SCNX(NTF_TITLE_X + NTF_TITLE_WIDTH);\r
+    d->r_title.top = 0;\r
+    d->r_title.bottom = DLG2SCNY(NTF_TITLE_HEIGHT);\r
+\r
+    d->s_icon.cx = DLG2SCNX(NTF_ICON_WIDTH);\r
+    d->s_icon.cy = DLG2SCNY(NTF_ICON_HEIGHT);\r
+\r
+    d->s_pad.cx = DLG2SCNX(NTF_TEXT_PAD);\r
+    d->s_pad.cy = DLG2SCNY(NTF_TEXT_PAD);\r
+\r
+#undef DLG2SCNX\r
+#undef DLG2SCNY\r
+\r
+    return d;\r
 }\r
 \r
-static khm_int32 \r
-alert_show_normal(khui_alert * a) {\r
-    HWND hwa;\r
-    wchar_t buf[256];\r
-    wchar_t * title;\r
+static void\r
+layout_alert(HDC hdc, alerter_wnd_data * d,\r
+             alerter_alert_data * adata) {\r
+    RECT r;\r
+    size_t len;\r
+    int y;\r
+    int icon_y;\r
 \r
-    if(a->title == NULL) {\r
-        LoadString(khm_hInstance, IDS_ALERT_DEFAULT, \r
-                   buf, ARRAYLENGTH(buf));\r
-        title = buf;\r
-    } else\r
-        title = a->title;\r
+#ifdef DEBUG\r
+    assert(adata->alert);\r
+#endif\r
 \r
-    /* if we don't have any commands, we just add a "close" button */\r
-    if (a->n_alert_commands == 0) {\r
-        khui_alert_add_command(a, KHUI_PACTION_CLOSE);\r
-    }\r
+    khui_alert_lock(adata->alert);\r
+\r
+    y = 0;\r
+\r
+    /* Title */\r
+\r
+    y += d->s_margin.cy;\r
+\r
+    if (adata->alert->title &&\r
+        wcscmp(adata->alert->title, d->caption)) {\r
+\r
+        CopyRect(&adata->r_title, &d->r_title);\r
+        OffsetRect(&adata->r_title, 0, y);\r
+\r
+        y = adata->r_title.bottom + d->s_margin.cy;\r
+\r
+    } else {\r
+\r
+        SetRectEmpty(&adata->r_title);\r
 \r
-    /* if there are other alerts queued up, we should add a 'Next\r
-       alert...' button that when clicked, would show the next queued\r
-       alert.  However, we only do this if the current alert doesn't\r
-       actually require a command response.  Otherwise, clicking the\r
-       'next' button will be the equivalent of cancelling out of the\r
-       alert without selecting any of the commands. */\r
-    if (!is_alert_queue_empty() &&\r
-        a->n_alert_commands == 1 &&\r
-        a->alert_commands[0] == KHUI_PACTION_CLOSE) {\r
-\r
-        khui_alert_add_command(a, KHUI_PACTION_NEXT);\r
     }\r
 \r
-    /* we don't need to keep track of the window handle\r
+    /* Icon */\r
+\r
+    SetRect(&adata->r_icon, d->s_margin.cx, y,\r
+            d->s_margin.cx + d->s_icon.cx,\r
+            y + d->s_icon.cy);\r
+\r
+    icon_y = adata->r_icon.bottom + d->s_margin.cy; /* the bottom of the icon */\r
+\r
+    /* Message */\r
+\r
+    if (adata->alert->message &&\r
+        SUCCEEDED(StringCchLength(adata->alert->message,\r
+                                  KHUI_MAXCCH_MESSAGE,\r
+                                  &len))) {\r
+\r
+        CopyRect(&r, &d->r_text);\r
+\r
+        DrawTextEx(hdc, adata->alert->message, (int) len,\r
+                   &r,\r
+                   DRAWTEXTOPTIONS,\r
+                   NULL);\r
+\r
+        OffsetRect(&r, 0, y);\r
+        CopyRect(&adata->r_message, &r);\r
+\r
+        y = r.bottom + d->s_margin.cy;\r
+\r
+    } else {\r
+\r
+        SetRectEmpty(&adata->r_message);\r
+\r
+    }\r
+\r
+    /* Suggestion */\r
+\r
+    if (adata->alert->suggestion &&\r
+        SUCCEEDED(StringCchLength(adata->alert->suggestion,\r
+                                  KHUI_MAXCCH_SUGGESTION,\r
+                                  &len))) {\r
+        int pad = d->s_pad.cx + GetSystemMetrics(SM_CXSMICON);\r
+\r
+        CopyRect(&r, &d->r_text);\r
+        r.left += pad;\r
+\r
+        DrawTextEx(hdc, adata->alert->suggestion, (int) len,\r
+                   &r,\r
+                   DRAWTEXTOPTIONS,\r
+                   NULL);\r
+\r
+        r.left -= pad;\r
+\r
+        InflateRect(&r, d->s_pad.cx, d->s_pad.cy);\r
+        OffsetRect(&r, 0, -r.top + y);\r
+        CopyRect(&adata->r_suggestion, &r);\r
+\r
+        y = r.bottom + d->s_margin.cy;\r
+\r
+    } else {\r
+\r
+        SetRectEmpty(&adata->r_suggestion);\r
+\r
+    }\r
+\r
+    y = max(y, icon_y);\r
+\r
+    /* Buttons */\r
+\r
+    if (ALERT_HAS_CMDS(adata->alert)) {\r
+        khm_int32 i;\r
+        int x;\r
+\r
+        adata->has_commands = TRUE;\r
+\r
+        x = d->r_text.left;\r
+\r
+#ifdef DEBUG\r
+        assert(adata->alert->n_alert_commands <= KHUI_MAX_ALERT_COMMANDS);\r
+#endif\r
+\r
+        for (i=0; i < adata->alert->n_alert_commands; i++) {\r
+            SetRect(&adata->r_buttons[i], x, y, x + d->s_button.cx, y + d->s_button.cy);\r
+\r
+            x += d->s_button.cx + d->s_margin.cx;\r
+        }\r
+\r
+        y += d->s_button.cy + d->s_margin.cy;\r
+    }\r
+\r
+    khui_alert_unlock(adata->alert);\r
+\r
+    /* Now set the rect for the whole alert */\r
+    SetRect(&adata->r_alert, 0, 0, d->cx_wnd, y);\r
+\r
+}\r
+\r
+static void\r
+estimate_alerter_wnd_sizes(alerter_wnd_data * d) {\r
+    HDC hdc;\r
+    HFONT hf_old;\r
+    int height = 0;\r
+\r
+    alerter_alert_data * adata;\r
+\r
+    hdc = GetDC(d->hwnd);\r
+#ifdef DEBUG\r
+    assert(hdc);\r
+#endif\r
+\r
+    if (d->hfont == NULL)\r
+        d->hfont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);\r
+\r
+#ifdef DEBUG\r
+    assert(d->hfont);\r
+#endif\r
+\r
+    hf_old = SelectFont(hdc, d->hfont);\r
+\r
+    adata = QTOP(d);\r
+    while(adata) {\r
+        layout_alert(hdc, d, adata);\r
+\r
+        height += adata->r_alert.bottom;\r
+\r
+        adata = QNEXT(adata);\r
+    }\r
+\r
+    SelectFont(hdc, hf_old);\r
+    ReleaseDC(d->hwnd, hdc);\r
+\r
+    d->s_alerts.cx = d->cx_wnd;\r
+    d->s_alerts.cy = height;\r
+}\r
+\r
+static void\r
+layout_command_buttons(alerter_wnd_data * d) {\r
+\r
+    alerter_alert_data * adata;\r
+    HDWP hdefer;\r
+    int y;\r
+\r
+    hdefer = BeginDeferWindowPos(d->n_cmd_buttons);\r
+\r
+    y = 0;\r
+    adata = QTOP(d);\r
+    while (adata) {\r
+        RECT r;\r
+        int i;\r
+\r
+        if (!adata->has_commands)\r
+            goto done;\r
+\r
+        for (i=0; i < adata->n_cmd_buttons; i++) {\r
+            if (IsRectEmpty(&adata->r_buttons[i]) ||\r
+                adata->hwnd_buttons[i] == NULL) {\r
+#ifdef DEBUG\r
+                assert(FALSE);\r
+#endif\r
+                continue;\r
+            }\r
+\r
+            CopyRect(&r, &adata->r_buttons[i]);\r
+            OffsetRect(&r, 0, y - d->scroll_top);\r
+\r
+            DeferWindowPos(hdefer,\r
+                           adata->hwnd_buttons[i], NULL,\r
+                           r.left, r.top, 0, 0,\r
+                           SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER |\r
+                           SWP_NOSIZE);\r
+        }\r
+\r
+    done:\r
+        y += adata->r_alert.bottom;\r
+        adata = QNEXT(adata);\r
+    }\r
+\r
+    EndDeferWindowPos(hdefer);\r
+}\r
+\r
+static void\r
+setup_alerter_window_controls(alerter_wnd_data * d) {\r
+\r
+    RECT r_alerts;\r
+    RECT r_window;\r
+    RECT r_client;\r
+    RECT r_parent;\r
+    HWND hw_parent;\r
+    BOOL close_button = FALSE;\r
+    BOOL scrollbar = FALSE;\r
+    BOOL redraw_scollbar = FALSE;\r
+\r
+    /* estimate_alerter_wnd_sizes() must be called before calling\r
+       this. */\r
+#ifdef DEBUG\r
+    assert(d->s_alerts.cy > 0);\r
+#endif\r
+\r
+    r_alerts.left = 0;\r
+    r_alerts.top = 0;\r
+    r_alerts.right = d->cx_wnd;\r
+\r
+    if (d->s_alerts.cy > d->cy_max_wnd) {\r
+\r
+        BOOL redraw = FALSE;\r
+\r
+        r_alerts.right += GetSystemMetrics(SM_CXVSCROLL);\r
+        r_alerts.bottom = d->cy_max_wnd;\r
+\r
+        CopyRect(&r_client, &r_alerts);\r
+        r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;\r
+        close_button = TRUE;\r
+\r
+        if (d->scroll_top > d->s_alerts.cy - d->cy_max_wnd)\r
+            d->scroll_top = d->s_alerts.cy - d->cy_max_wnd;\r
+\r
+        scrollbar = TRUE;\r
+    } else {\r
+        r_alerts.bottom = d->s_alerts.cy;\r
+\r
+        CopyRect(&r_client, &r_alerts);\r
+\r
+        if (d->n_alerts == 1) {\r
+\r
+            if (!QTOP(d)->has_commands) {\r
+                r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;\r
+                close_button = TRUE;\r
+            }\r
+\r
+        } else {\r
+\r
+            r_client.bottom += d->s_margin.cy * 2 + d->s_button.cy;\r
+            close_button = TRUE;\r
+        }\r
+\r
+        d->scroll_top = 0;\r
+    }\r
+\r
+    if (d->hw_bin == NULL) {\r
+        d->hw_bin = CreateWindowEx(WS_EX_CONTROLPARENT,\r
+                                   MAKEINTATOM(atom_alert_bin),\r
+                                   L"",\r
+                                   WS_CHILD | WS_CLIPCHILDREN |\r
+                                   WS_VISIBLE |\r
+                                   ((scrollbar)? WS_VSCROLL : 0),\r
+                                   r_alerts.left, r_alerts.top,\r
+                                   r_alerts.right - r_alerts.left,\r
+                                   r_alerts.bottom - r_alerts.top,\r
+                                   d->hwnd,\r
+                                   (HMENU) IDC_NTF_ALERTBIN,\r
+                                   khm_hInstance,\r
+                                   (LPVOID) d);\r
+    } else {\r
+        redraw_scollbar = TRUE;\r
+        SetWindowLongPtr(d->hw_bin, GWL_STYLE,\r
+                         WS_CHILD | WS_CLIPCHILDREN |\r
+                         WS_VISIBLE |\r
+                         ((scrollbar)? WS_VSCROLL : 0));\r
+        SetWindowPos(d->hw_bin, NULL,\r
+                     r_alerts.left, r_alerts.top,\r
+                     r_alerts.right - r_alerts.left,\r
+                     r_alerts.bottom - r_alerts.top,\r
+                     SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);\r
+    }\r
+\r
+    if (scrollbar) {\r
+        SCROLLINFO si;\r
+\r
+        ZeroMemory(&si, sizeof(si));\r
+        si.cbSize = sizeof(si);\r
+        si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;\r
+        si.nMin = 0;\r
+        si.nMax = d->s_alerts.cy;\r
+        si.nPage = d->cy_max_wnd;\r
+        si.nPos = d->scroll_top;\r
+\r
+        SetScrollInfo(d->hw_bin, SB_VERT, &si, redraw_scollbar);\r
+    }\r
+\r
+    /* create the action buttons */\r
+    {\r
+        alerter_alert_data * adata;\r
+        int y;\r
+        int idx;\r
+        HWND last_window = HWND_TOP;\r
+        int n_buttons = 0;\r
+\r
+        idx = 0;\r
+        y = - d->scroll_top;\r
+        adata = QTOP(d);\r
+        while(adata) {\r
+            if (adata->has_commands) {\r
+                int i;\r
+                wchar_t caption[KHUI_MAXCCH_SHORT_DESC];\r
+                khui_action * action;\r
+                RECT r;\r
+\r
+                khui_alert_lock(adata->alert);\r
+\r
+                adata->n_cmd_buttons = adata->alert->n_alert_commands;\r
+\r
+                for (i=0; i < adata->alert->n_alert_commands; i++) {\r
+\r
+                    n_buttons ++;\r
+\r
+                    if (adata->hwnd_buttons[i] != NULL) {\r
+                        /* already there */\r
+                        CopyRect(&r, &adata->r_buttons[i]);\r
+                        OffsetRect(&r, 0, y);\r
+\r
+                        SetWindowPos(adata->hwnd_buttons[i], last_window,\r
+                                     r.left, r.top,\r
+                                     r.right - r.left,\r
+                                     r.bottom - r.top,\r
+                                     SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                                     SWP_SHOWWINDOW);\r
+\r
+                        last_window = adata->hwnd_buttons[i];\r
+\r
+                        continue;\r
+                    }\r
+\r
+                    action = khui_find_action(adata->alert->alert_commands[i]);\r
+\r
+                    if (action == NULL) {\r
+#ifdef DEBUG\r
+                        assert(FALSE);\r
+#endif\r
+                        continue;\r
+                    }\r
+\r
+                    if (action->caption)\r
+                        StringCbCopy(caption, sizeof(caption), action->caption);\r
+                    else if (action->is_caption)\r
+                        LoadString(khm_hInstance, action->is_caption, caption,\r
+                                   ARRAYLENGTH(caption));\r
+                    else {\r
+#ifdef DEBUG\r
+                        assert(FALSE);\r
+#endif\r
+                        caption[0] = L'\0';\r
+                    }\r
+\r
+                    CopyRect(&r, &adata->r_buttons[i]);\r
+                    OffsetRect(&r, 0, y);\r
+\r
+                    adata->hwnd_buttons[i] =\r
+                        CreateWindowEx(0,\r
+                                       L"BUTTON",\r
+                                       caption,\r
+                                       WS_CHILD | WS_TABSTOP,\r
+                                       r.left, r.top,\r
+                                       r.right - r.left,\r
+                                       r.bottom - r.top,\r
+                                       d->hw_bin,\r
+                                       (HMENU) (INT_PTR) IDC_FROM_IDX(idx, i),\r
+                                       khm_hInstance,\r
+                                       NULL);\r
+#ifdef DEBUG\r
+                    assert(adata->hwnd_buttons[i]);\r
+#endif\r
+                    SetWindowPos(adata->hwnd_buttons[i], last_window,\r
+                                 0, 0, 0, 0,\r
+                                 SWP_NOACTIVATE | SWP_NOOWNERZORDER |\r
+                                 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);\r
+                    last_window = adata->hwnd_buttons[i];\r
+                }\r
+\r
+                khui_alert_unlock(adata->alert);\r
+            }\r
+\r
+            y += adata->r_alert.bottom;\r
+            adata = QNEXT(adata);\r
+        }\r
+\r
+        d->n_cmd_buttons = n_buttons;\r
+    }\r
+\r
+    if (close_button) {\r
+        if (d->hw_close == NULL) {\r
+            khui_action * close_action;\r
+            wchar_t caption[256];\r
+\r
+            close_action = khui_find_action(KHUI_PACTION_CLOSE);\r
+#ifdef DEBUG\r
+            assert(close_action);\r
+#endif\r
+            if (close_action->caption)\r
+                StringCbCopy(caption, sizeof(caption), close_action->caption);\r
+            else if (close_action->is_caption)\r
+                LoadString(khm_hInstance, close_action->is_caption, caption,\r
+                           ARRAYLENGTH(caption));\r
+            else {\r
+#ifdef DEBUG\r
+                assert(FALSE);\r
+#endif\r
+                caption[0] = L'\0';\r
+            }\r
+\r
+            d->hw_close = CreateWindowEx(0,\r
+                                         L"BUTTON",\r
+                                         caption,\r
+                                         WS_CHILD | BS_DEFPUSHBUTTON,\r
+                                         0,0,100,100,\r
+                                         d->hwnd,\r
+                                         (HMENU) IDC_NTF_CLOSE,\r
+                                         khm_hInstance,\r
+                                         NULL);\r
+\r
+#ifdef DEBUG\r
+            assert(d->hw_close);\r
+            assert(d->hfont);\r
+#endif\r
+            if (d->hfont)\r
+                SendMessage(d->hw_close, WM_SETFONT, (WPARAM) d->hfont, FALSE);\r
+        }\r
+\r
+        {\r
+            int x,y,width,height;\r
+\r
+            x = d->r_text.left;\r
+            y = r_client.bottom - (d->s_margin.cy + d->s_button.cy);\r
+            width = d->s_button.cx;\r
+            height = d->s_button.cy;\r
+\r
+            SetWindowPos(d->hw_close, NULL,\r
+                         x, y, width, height,\r
+                         SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER |\r
+                         SWP_SHOWWINDOW);\r
+        }\r
+    }\r
+\r
+    CopyRect(&r_window, &r_client);\r
+    AdjustWindowRectEx(&r_window, ALERT_WINDOW_STYLES,\r
+                       FALSE, ALERT_WINDOW_EX_SYLES);\r
+    OffsetRect(&r_window, -r_window.left, -r_window.top);\r
+\r
+    /* center the window above the parent window. */\r
+\r
+    hw_parent = GetWindow(d->hwnd, GW_OWNER);\r
+    GetWindowRect(hw_parent, &r_parent);\r
+\r
+    {\r
+        int x,y;\r
+\r
+        x = (r_parent.left + r_parent.right - (r_window.right - r_window.left)) / 2;\r
+        y = (r_parent.top + r_parent.bottom - (r_window.bottom - r_window.top)) / 2;\r
+\r
+        SetWindowPos(d->hwnd,\r
+                     HWND_TOP,\r
+                     x, y,\r
+                     r_window.right - r_window.left,\r
+                     r_window.bottom - r_window.top,\r
+                     SWP_SHOWWINDOW | SWP_NOOWNERZORDER);\r
+\r
+    }\r
+}\r
+\r
+static void\r
+destroy_alerter_wnd_data(alerter_wnd_data * d) {\r
+    alerter_alert_data * adata;\r
+\r
+    LDELETE(&khui_alert_windows, d);\r
+\r
+    QGET(d, &adata);\r
+    while(adata) {\r
+\r
+        if (adata->alert) {\r
+\r
+            khui_alert_lock(adata->alert);\r
+\r
+            adata->alert->flags &= ~KHUI_ALERT_FLAG_DISPLAY_WINDOW;\r
+\r
+            adata->alert->displayed = FALSE;\r
+\r
+            khui_alert_unlock(adata->alert);\r
+\r
+            khui_alert_release(adata->alert);\r
+            adata->alert = NULL;\r
+        }\r
+\r
+        PFREE(adata);\r
+\r
+        QGET(d, &adata);\r
+    }\r
+\r
+    PFREE(d);\r
+}\r
+\r
+/* both ref and to_add must be locked and held */\r
+static khm_boolean\r
+alert_can_consolidate(khui_alert * ref,\r
+                      khui_alert * to_add,\r
+                      alert_list * alist) {\r
+\r
+    /* first check if we can add anything */\r
+    if (alist->n_alerts == ARRAYLENGTH(alist->alerts))\r
+        return FALSE;\r
+\r
+#ifdef DEBUG\r
+    assert(to_add != NULL);\r
+#endif\r
+\r
+    if (ref == NULL) {\r
+        /* we are testing whether to_add should be added to the alist\r
+           on its own. */\r
+        if ((to_add->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&\r
+            !(to_add->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW)) {\r
+            /* already displayed */\r
+            return FALSE;\r
+        }\r
+\r
+        if ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |\r
+                              KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON) {\r
+            /* needs to be shown in a balloon */\r
+            return FALSE;\r
+        }\r
+\r
+        return TRUE;\r
+    }\r
+\r
+    /* if the ref or to_add are marked for modal, then we can't\r
+       consolidate them */\r
+    if ((ref->flags & KHUI_ALERT_FLAG_MODAL) ||\r
+        (to_add->flags & KHUI_ALERT_FLAG_MODAL))\r
+        return FALSE;\r
+\r
+    /* also, if either of them have requested to be exclusively shown\r
+       in a balloon, then we can't consolidate them. */\r
+    if (((ref->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |\r
+                        KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON)\r
+\r
+        ||\r
+\r
+        ((to_add->flags & (KHUI_ALERT_FLAG_REQUEST_BALLOON |\r
+                           KHUI_ALERT_FLAG_REQUEST_WINDOW)) == KHUI_ALERT_FLAG_REQUEST_BALLOON))\r
+        return FALSE;\r
+\r
+    /* for now, all we check if whether they are of the same type. */\r
+    if (ref->alert_type != KHUI_ALERTTYPE_NONE &&\r
+        ref->alert_type == to_add->alert_type)\r
+        return TRUE;\r
+    else\r
+        return FALSE;\r
+}\r
+\r
+/* the return value is the number of alerts added to alist */\r
+static khm_int32\r
+alert_consolidate(alert_list * alist,\r
+                  khui_alert * alert,\r
+                  khm_boolean add_from_queue) {\r
+\r
+    khui_alert * listtop;\r
+    int queue_size = 0;\r
+    int i;\r
+    khm_int32 n_added = 0;\r
+\r
+#ifdef DEBUG\r
+    assert(alist);\r
+#endif\r
+\r
+    if (alist->n_alerts == ARRAYLENGTH(alist->alerts)) {\r
+        /* can't add anything */\r
+\r
+        return 0;\r
+    }\r
+\r
+    /* if the list is empty, we just add one alert */\r
+    if (alist->n_alerts == 0) {\r
+\r
+        if (alert) {\r
+            khui_alert_lock(alert);\r
+            if (alert_can_consolidate(NULL, alert, alist)) {\r
+                alert_list_add_alert(alist, alert);\r
+                n_added ++;\r
+                alert = NULL;\r
+            }\r
+            khui_alert_unlock(alert);\r
+        }\r
+\r
+        if (n_added == 0 && add_from_queue) {\r
+            khui_alert * q;\r
+            int s, i;\r
+\r
+            s = alert_queue_get_size();\r
+            for (i=0; i < s && n_added == 0; i++) {\r
+                q = alert_queue_get_alert_by_pos(i);\r
+                if (q) {\r
+                    khui_alert_lock(q);\r
+                    if (alert_can_consolidate(NULL, q, alist)) {\r
+                        alert_list_add_alert(alist, q);\r
+                        n_added++;\r
+                        alert_queue_delete_alert(q);\r
+                    }\r
+                    khui_alert_unlock(q);\r
+                    khui_alert_release(q);\r
+                }\r
+            }\r
+        }\r
+\r
+        if (n_added == 0) {\r
+            /* nothing to add */\r
+            return 0;\r
+        }\r
+    }\r
+\r
+    /* at this point, the alert list is not empty */\r
+#ifdef DEBUG\r
+    assert(alist->n_alerts != 0);\r
+    assert(alist->alerts[0]);\r
+#endif\r
+\r
+    listtop = alist->alerts[0];\r
+    khui_alert_hold(listtop);\r
+    khui_alert_lock(listtop);\r
+\r
+    queue_size = alert_queue_get_size();\r
+\r
+    if (alert) {\r
+        khui_alert_lock(alert);\r
+        if (alert_can_consolidate(listtop, alert, alist)) {\r
+            alert_list_add_alert(alist, alert);\r
+            n_added ++;\r
+        }\r
+        khui_alert_unlock(alert);\r
+    }\r
+\r
+    if (add_from_queue) {\r
+        for (i=0; i < queue_size; i++) {\r
+            khui_alert * a;\r
+\r
+            a = alert_queue_get_alert_by_pos(i);\r
+            if (a == NULL)\r
+                continue;\r
+\r
+            khui_alert_lock(a);\r
+            if (alert_can_consolidate(listtop, a, alist)) {\r
+                alert_queue_delete_alert(a);\r
+                alert_list_add_alert(alist, a);\r
+                n_added ++;\r
+                queue_size = alert_queue_get_size();\r
+            }\r
+            khui_alert_unlock(a);\r
+            khui_alert_release(a);\r
+        }\r
+    }\r
+\r
+    khui_alert_unlock(listtop);\r
+    khui_alert_release(listtop);\r
+\r
+    return n_added;\r
+}\r
+\r
+static khm_int32\r
+alert_check_consolidate_window(alerter_wnd_data * d, khui_alert * a) {\r
+    alert_list alist;\r
+    alerter_alert_data * adata;\r
+    int n_added;\r
+\r
+    alert_list_init(&alist);\r
+\r
+    adata = QTOP(d);\r
+    while(adata) {\r
+\r
+#ifdef DEBUG\r
+        assert(adata->alert);\r
+#endif\r
+        alert_list_add_alert(&alist, adata->alert);\r
+\r
+        adata = QNEXT(adata);\r
+    }\r
+\r
+    n_added = alert_consolidate(&alist, a, FALSE);\r
+\r
+    alert_list_destroy(&alist);\r
+\r
+    return n_added;\r
+}\r
+\r
+static khm_int32 \r
+alert_show_minimized(khui_alert * a) {\r
+    wchar_t tbuf[64];           /* corresponds to NOTIFYICONDATA::szInfoTitle[] */\r
+    wchar_t mbuf[256];          /* corresponds to NOTIFYICONDATA::szInfo[] */\r
+\r
+#ifdef DEBUG\r
+    assert(a);\r
+#endif\r
+    if (a == NULL)\r
+        return KHM_ERROR_INVALID_PARAM;\r
+\r
+    khui_alert_lock(a);\r
+\r
+    if (a->message == NULL)\r
+        goto done;\r
+\r
+    if (a->title == NULL) {\r
+        LoadString(khm_hInstance, IDS_ALERT_DEFAULT,\r
+                   tbuf, ARRAYLENGTH(tbuf));\r
+    } else {\r
+        StringCbCopy(tbuf, sizeof(tbuf), a->title);\r
+    }\r
+\r
+    if (FAILED(StringCbCopy(mbuf, sizeof(mbuf), a->message)) ||\r
+        (!(a->flags & KHUI_ALERT_FLAG_DEFACTION) &&\r
+         (a->n_alert_commands > 0 ||\r
+          a->suggestion ||\r
+          (a->flags & KHUI_ALERT_FLAG_VALID_ERROR)))) {\r
+        /* if mbuf wasn't big enough, this should have copied a\r
+           truncated version of it */\r
+        size_t cch_m, cch_p;\r
+        wchar_t postfix[256];\r
+\r
+        cch_p = LoadString(khm_hInstance, IDS_ALERT_MOREINFO, postfix,\r
+                           ARRAYLENGTH(postfix));\r
+        cch_p++;                /* account for NULL */\r
+\r
+        StringCchLength(mbuf, ARRAYLENGTH(mbuf), &cch_m);\r
+        cch_m = min(cch_m, ARRAYLENGTH(mbuf) - cch_p);\r
+\r
+        StringCchCopy(mbuf + cch_m, ARRAYLENGTH(mbuf) - cch_m,\r
+                      postfix);\r
+\r
+        a->flags |= KHUI_ALERT_FLAG_REQUEST_WINDOW;\r
+    }\r
+\r
+    a->flags |= KHUI_ALERT_FLAG_DISPLAY_BALLOON;\r
+\r
+#if (_WIN32_IE >= 0x0501)\r
+    if (balloon_alert) {\r
+        khui_alert_release(balloon_alert);\r
+        balloon_alert = NULL;\r
+    }\r
+\r
+    balloon_alert = a;\r
+    khui_alert_hold(a);\r
+#endif\r
+\r
+    khm_notify_icon_balloon(a->severity,\r
+                            tbuf,\r
+                            mbuf,\r
+                            NTF_TIMEOUT);\r
+\r
+ done:\r
+    khui_alert_unlock(a);\r
+\r
+    return KHM_ERROR_SUCCESS;\r
+}\r
+\r
+static khm_int32 \r
+alert_show_normal(khui_alert * a) {\r
+    wchar_t buf[256];\r
+    wchar_t * title;\r
+    alert_list alist;\r
+\r
+    khui_alert_lock(a);\r
+\r
+    if(a->title == NULL) {\r
+        LoadString(khm_hInstance, IDS_ALERT_DEFAULT, \r
+                   buf, ARRAYLENGTH(buf));\r
+        title = buf;\r
+    } else\r
+        title = a->title;\r
+\r
+    khui_alert_unlock(a);\r
+\r
+    alert_list_init(&alist);\r
+    alert_list_set_title(&alist, title);\r
+    alert_list_add_alert(&alist, a);\r
+\r
+    alert_show_list(&alist);\r
+\r
+    alert_list_destroy(&alist);\r
+\r
+    return KHM_ERROR_SUCCESS;\r
+}\r
+\r
+static khm_int32\r
+alert_show_list(alert_list * alist) {\r
+    HWND hwa;\r
+\r
+    /* we don't need to keep track of the window handle\r
        because the window procedure adds it to the dialog\r
        list automatically */\r
 \r
-    hwa = \r
-        CreateWindowEx(WS_EX_DLGMODALFRAME | WS_EX_CONTEXTHELP,\r
-                       MAKEINTATOM(atom_alerter),\r
-                       title,\r
-                       WS_DLGFRAME | WS_POPUPWINDOW | WS_CLIPCHILDREN,\r
-                       0, 0, 300, 300, // bogus values\r
-                       khm_hwnd_main,\r
-                       (HMENU) NULL,\r
-                       khm_hInstance,\r
-                       (LPVOID) a);\r
+    hwa = \r
+        CreateWindowEx(ALERT_WINDOW_EX_SYLES,\r
+                       MAKEINTATOM(atom_alerter),\r
+                       alist->title,\r
+                       ALERT_WINDOW_STYLES,\r
+                       0, 0, 300, 300, // bogus values\r
+                       khm_hwnd_main,\r
+                       (HMENU) NULL,\r
+                       khm_hInstance,\r
+                       (LPVOID) alist);\r
+\r
+    ShowWindow(hwa, SW_SHOW);\r
+\r
+    return (hwa != NULL);\r
+}\r
+\r
+static khm_int32 \r
+alert_show(khui_alert * a) {\r
+    khm_boolean show_normal = FALSE;\r
+    khm_boolean show_mini = FALSE;\r
+\r
+    khui_alert_lock(a);\r
+\r
+    /* is there an alert already?  If so, we just enqueue the message\r
+       and let it sit. */\r
+    if (ALERT_DISPLAYED() &&\r
+        !(a->flags & KHUI_ALERT_FLAG_MODAL)) {\r
+        khm_int32 rv;\r
+        alerter_wnd_data * wdata;\r
+\r
+        khui_alert_unlock(a);\r
+\r
+        /* if there are any alerter windows displayed, check if this\r
+           alert can be consolidated with any of them.  If so, we\r
+           should consolidate it.  Otherwise, just enqueue it. */\r
+        for(wdata = khui_alert_windows;\r
+            wdata;\r
+            wdata = LNEXT(wdata)) {\r
+            if (alert_check_consolidate_window(wdata, a)) {\r
+\r
+                add_alert_to_wnd_data(wdata, a);\r
+                estimate_alerter_wnd_sizes(wdata);\r
+                setup_alerter_window_controls(wdata);\r
+\r
+                return KHM_ERROR_SUCCESS;\r
+\r
+            }\r
+        }\r
+\r
+        rv = alert_enqueue(a);\r
+\r
+        if (KHM_SUCCEEDED(rv))\r
+            return KHM_ERROR_HELD;\r
+        else\r
+            return rv;\r
+    }\r
+\r
+    if((a->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW) ||\r
+       ((a->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&\r
+        !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW))) {\r
+\r
+        /* The alert has already been displayed. */\r
+\r
+        show_normal = FALSE;\r
+        show_mini = FALSE;\r
+\r
+    } else {\r
+\r
+        if(a->err_context != NULL ||\r
+           a->err_event != NULL) {\r
+            a->flags |= KHUI_ALERT_FLAG_VALID_ERROR;\r
+        }\r
+\r
+        /* depending on the state of the main window, we\r
+           need to either show a window or a balloon */\r
+        if ((a->flags & KHUI_ALERT_FLAG_MODAL) ||\r
+            (khm_is_main_window_active() &&\r
+             !(a->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON)) ||\r
+            (a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW)) {\r
 \r
-    ShowWindow(hwa, SW_SHOW);\r
+            show_normal = TRUE;\r
 \r
-    return KHM_ERROR_SUCCESS;\r
-}\r
+        } else {\r
 \r
-static khm_int32 \r
-alert_show(khui_alert * a) {\r
-    /* is there an alert already?  If so, we just enqueue the message\r
-       and let it sit. */\r
-    if (current_alert) {\r
-        return alert_enqueue(a);\r
+            show_mini = TRUE;\r
+\r
+        }\r
     }\r
 \r
-    /* the window has already been shown */\r
-    if((a->flags & KHUI_ALERT_FLAG_DISPLAY_WINDOW) ||\r
-        ((a->flags & KHUI_ALERT_FLAG_DISPLAY_BALLOON) &&\r
-         !(a->flags & KHUI_ALERT_FLAG_REQUEST_WINDOW)))\r
+    khui_alert_unlock(a);\r
+\r
+    if (show_normal)\r
+        return alert_show_normal(a);\r
+    else if (show_mini)\r
+        return alert_show_minimized(a);\r
+    else\r
         return KHM_ERROR_SUCCESS;\r
+}\r
+\r
+static void\r
+check_for_queued_alerts(void) {\r
+    if (!is_alert_queue_empty()) {\r
+        khui_alert * a;\r
+\r
+        a = alert_queue_peek();\r
 \r
-    if(a->err_context != NULL ||\r
-       a->err_event != NULL) {\r
         khui_alert_lock(a);\r
-        a->flags |= KHUI_ALERT_FLAG_VALID_ERROR;\r
+\r
+        if (a->title) {\r
+            HICON hi;\r
+            int res;\r
+\r
+            if (a->severity == KHERR_ERROR)\r
+                res = OIC_ERROR;\r
+            else if (a->severity == KHERR_WARNING)\r
+                res = OIC_WARNING;\r
+            else\r
+                res = OIC_INFORMATION;\r
+\r
+            hi = LoadImage(0, MAKEINTRESOURCE(res),\r
+                           IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),\r
+                           LR_SHARED);\r
+\r
+            khm_statusbar_set_part(KHUI_SBPART_NOTICE,\r
+                                   hi,\r
+                                   a->title);\r
+        }\r
+\r
         khui_alert_unlock(a);\r
-    }\r
+        khui_alert_release(a);\r
 \r
-    /* depending on the state of the main window, we\r
-       need to either show a window or a balloon */\r
-    if ((a->flags & KHUI_ALERT_FLAG_MODAL) ||\r
-        (khm_is_main_window_active() &&\r
-         !(a->flags & KHUI_ALERT_FLAG_REQUEST_BALLOON)))\r
-        return alert_show_normal(a);\r
-    else\r
-        return alert_show_minimized(a);\r
+    } else {\r
+        khm_statusbar_set_part(KHUI_SBPART_NOTICE,\r
+                               NULL, NULL);\r
+    }\r
 }\r
 \r
 static khm_int32\r
@@ -554,7 +1700,7 @@ alert_enqueue(khui_alert * a) {
     if (is_alert_queue_full())\r
         return KHM_ERROR_NO_RESOURCES;\r
 \r
-    add_to_alert_queue(a);\r
+    alert_queue_put_alert(a);\r
     check_for_queued_alerts();\r
 \r
     return KHM_ERROR_SUCCESS;\r
@@ -570,29 +1716,14 @@ alerter_wnd_proc(HWND hwnd,
     switch(uMsg) {\r
     case WM_CREATE:\r
         {\r
-            LONG dlgb;\r
-            HWND hwnd_parent;\r
-            RECT r_parent;\r
-            POINT pos;\r
-            SIZE s;\r
             LPCREATESTRUCT lpcs;\r
-            khui_alert * a;\r
+            alert_list * alist;\r
             alerter_wnd_data * d;\r
 \r
             lpcs = (LPCREATESTRUCT) lParam;\r
-            a = (khui_alert *) lpcs->lpCreateParams;\r
-            khui_alert_hold(a);\r
-\r
-            d = PMALLOC(sizeof(*d));\r
-            ZeroMemory(d, sizeof(*d));\r
-\r
-            d->alert = a;\r
-            d->hwnd = hwnd;\r
+            alist = (alert_list *) lpcs->lpCreateParams;\r
 \r
-            khui_alert_lock(a);\r
-\r
-            a->flags |= KHUI_ALERT_FLAG_DISPLAY_WINDOW;\r
-            LPUSH(&khui_alerts, d);\r
+            d = create_alerter_wnd_data(hwnd, alist);\r
 \r
 #pragma warning(push)\r
 #pragma warning(disable: 4244)\r
@@ -602,444 +1733,282 @@ alerter_wnd_proc(HWND hwnd,
             khm_add_dialog(hwnd);\r
             khm_enter_modal(hwnd);\r
 \r
-            /* now figure out the size and position of the window */\r
-\r
-            hwnd_parent = GetWindow(hwnd, GW_OWNER);\r
-            GetWindowRect(hwnd_parent, &r_parent);\r
-\r
-            dlgb = GetDialogBaseUnits();\r
+            estimate_alerter_wnd_sizes(d);\r
+            setup_alerter_window_controls(d);\r
 \r
-#define DLG2SCNX(x) MulDiv((x), LOWORD(dlgb), 4)\r
-#define DLG2SCNY(y) MulDiv((y), HIWORD(dlgb), 8)\r
-\r
-            d->dx_margin = DLG2SCNX(NTF_MARGIN);\r
-            d->dy_margin = DLG2SCNY(NTF_MARGIN);\r
-\r
-            d->x_message = DLG2SCNX(NTF_MSG_X);\r
-            d->dx_message = DLG2SCNX(NTF_MSG_WIDTH);\r
-\r
-            if (a->message) {\r
-                d->dy_message = DLG2SCNY(NTF_MSG_HEIGHT);\r
-            }\r
-\r
-            if (a->suggestion) {\r
-                d->dy_suggestion = DLG2SCNY(NTF_SUG_HEIGHT);\r
-                d->dx_suggest_pad = DLG2SCNX(NTF_SUG_PAD);\r
+            if (d->hw_close) {\r
+                SetFocus(d->hw_close);\r
             }\r
 \r
-            d->dy_bb = DLG2SCNY(NTF_BB_HEIGHT);\r
-            d->dx_button = DLG2SCNX(NTF_BUTTON_WIDTH);\r
-            d->dy_button = DLG2SCNY(NTF_BUTTON_HEIGHT);\r
-            d->dx_button_incr = DLG2SCNX(NTF_BUTTON_XINCR);\r
-\r
-            d->dx_icon = DLG2SCNX(NTF_ICON_WIDTH);\r
-            d->dy_icon = DLG2SCNY(NTF_ICON_HEIGHT);\r
+            return TRUE;\r
+        }\r
+        break; /* not reached */\r
 \r
-            d->dx_client = DLG2SCNX(NTF_WIDTH);\r
-            d->dy_client = max(d->dy_icon,\r
-                               d->dy_message +\r
-                               ((d->dy_suggestion > 0)?\r
-                                (d->dy_suggestion + d->dy_margin):\r
-                                0)) +\r
-                d->dy_margin * 3 + d->dy_bb;\r
+    case WM_DESTROY:\r
+        {\r
+            alerter_wnd_data * d;\r
 \r
-            /* adjust for client rect */\r
-            s.cx = d->dx_client;\r
-            s.cy = d->dy_client;\r
+            /* khm_leave_modal() could be here, but instead it is in\r
+               the WM_COMMAND handler.  This is because the modal loop\r
+               has to be exited before DestroyWindow() is issued. */\r
+            //khm_leave_modal();\r
+            khm_del_dialog(hwnd);\r
 \r
-            {\r
-                RECT c_r;\r
-                RECT w_r;\r
+            d = (alerter_wnd_data *)(LONG_PTR) \r
+                GetWindowLongPtr(hwnd, NTF_PARAM);\r
 \r
-                GetWindowRect(hwnd, &w_r);\r
-                GetClientRect(hwnd, &c_r);\r
+            destroy_alerter_wnd_data(d);\r
 \r
-                s.cx += (w_r.right - w_r.left) - (c_r.right - c_r.left);\r
-                s.cy += (w_r.bottom - w_r.top) - (c_r.bottom - c_r.top);\r
-            }\r
+            return TRUE;\r
+        }\r
+        break;\r
 \r
-            pos.x = (r_parent.left + r_parent.right - s.cx) / 2;\r
-            pos.y = (r_parent.top + r_parent.bottom - s.cy) / 2;\r
+#if 0\r
+    case WM_PAINT:\r
+        {\r
+            RECT r_update;\r
+            PAINTSTRUCT ps;\r
+            HDC hdc;\r
+            alerter_wnd_data * d;\r
 \r
-            SetWindowPos(hwnd,\r
-                         HWND_TOP,\r
-                         pos.x, pos.y,\r
-                         s.cx, s.cy,\r
-                         SWP_SHOWWINDOW);\r
+            if(!GetUpdateRect(hwnd, &r_update, TRUE))\r
+                return FALSE;\r
 \r
-            {\r
-                LOGFONT lf;\r
-                HDC hdc_dt;\r
-\r
-                hdc_dt = GetDC(NULL);\r
-\r
-                lf.lfHeight = -MulDiv(8, \r
-                                      GetDeviceCaps(hdc_dt, LOGPIXELSY), \r
-                                      72);\r
-                lf.lfWidth = 0;\r
-                lf.lfEscapement = 0;\r
-                lf.lfOrientation = 0;\r
-                lf.lfWeight = FW_NORMAL;\r
-                lf.lfItalic = FALSE;\r
-                lf.lfUnderline = FALSE;\r
-                lf.lfStrikeOut = FALSE;\r
-                lf.lfCharSet = DEFAULT_CHARSET;\r
-                lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
-                lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
-                lf.lfQuality = DEFAULT_QUALITY;\r
-                lf.lfPitchAndFamily = DEFAULT_PITCH;\r
-\r
-                LoadString(khm_hInstance, IDS_DEFAULT_FONT, \r
-                           lf.lfFaceName, ARRAYLENGTH(lf.lfFaceName));\r
-\r
-                d->hfont = CreateFontIndirect(&lf);\r
-\r
-                ReleaseDC(NULL, hdc_dt);\r
-            }\r
+            d = (alerter_wnd_data *)(LONG_PTR)\r
+                GetWindowLongPtr(hwnd, NTF_PARAM);\r
 \r
-            /* create dialog controls now */\r
-            {\r
-                int x,y;\r
-                int width, height;\r
-                int i;\r
+            hdc = BeginPaint(hwnd, &ps);\r
 \r
-                x = d->x_message;\r
-                y = d->dy_client - d->dy_bb;\r
-                width = d->dx_button;\r
-                height = d->dy_button;\r
+            EndPaint(hwnd, &ps);\r
 \r
-                for(i=0; i<a->n_alert_commands; i++) {\r
-                    wchar_t caption[256];\r
-                    khui_action * action;\r
-                    HWND hw_button;\r
+            return FALSE;\r
+        }\r
+        break; /* not reached */\r
+#endif\r
 \r
-                    if(a->alert_commands[i] == 0)\r
-                        continue;\r
+    case WM_COMMAND:\r
+        {\r
+            alerter_wnd_data * d;\r
 \r
-                    action = khui_find_action(a->alert_commands[i]);\r
-                    if(action == NULL)\r
-                        continue;\r
+            d = (alerter_wnd_data *)(LONG_PTR) \r
+                GetWindowLongPtr(hwnd, NTF_PARAM);\r
 \r
-                    LoadString(khm_hInstance, action->is_caption, \r
-                               caption, ARRAYLENGTH(caption));\r
-                        \r
-                    hw_button = \r
-                        CreateWindowEx(0,\r
-                                       L"BUTTON",\r
-                                       caption,\r
-                                       WS_VISIBLE | WS_CHILD |\r
-                                       /* the first button is the default */\r
-                                       ((i==0)? BS_DEFPUSHBUTTON: 0),\r
-                                       x,y,width,height,\r
-                                       hwnd,\r
-                                       (HMENU)(INT_PTR) (action->cmd),\r
-                                       khm_hInstance,\r
-                                       NULL);\r
+            if(HIWORD(wParam) == BN_CLICKED) {\r
+                if (LOWORD(wParam) == IDC_NTF_CLOSE ||\r
+                    LOWORD(wParam) == KHUI_PACTION_NEXT) {\r
 \r
-                    SendMessage(hw_button, WM_SETFONT, \r
-                                (WPARAM) d->hfont, MAKELPARAM(TRUE, 0));\r
+                    khm_leave_modal();\r
 \r
-                    d->hwnd_buttons[i] = hw_button;\r
+                    DestroyWindow(hwnd);\r
 \r
-                    x += d->dx_button_incr;\r
+#ifdef FORLATER\r
+                    if (LOWORD(wParam) == KHUI_PACTION_NEXT) {\r
+                        kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW_QUEUED, 0, 0);\r
+                    }\r
+#endif\r
+                    return 0;\r
                 }\r
             }\r
+        }\r
+        break;\r
+    }\r
 \r
-            if (d->hwnd_buttons[0])\r
-                SetFocus(d->hwnd_buttons[0]);\r
+    return DefDlgProc(hwnd, uMsg, wParam, lParam);\r
+    //return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
+}\r
 \r
-            khm_notify_icon_change(a->severity);\r
+static LRESULT CALLBACK \r
+alert_bin_wnd_proc(HWND hwnd,\r
+                   UINT uMsg,\r
+                   WPARAM wParam,\r
+                   LPARAM lParam)\r
+{\r
+    BOOL in_printclient = FALSE;\r
 \r
-            khui_alert_unlock(a);\r
+    switch(uMsg) {\r
+    case WM_CREATE:\r
+        {\r
+            LPCREATESTRUCT lpcs;\r
+            alerter_wnd_data * d;\r
 \r
-            d->metrics_done = FALSE;\r
-                \r
-            return TRUE;\r
+            lpcs = (LPCREATESTRUCT) lParam;\r
+            d = (alerter_wnd_data *) lpcs->lpCreateParams;\r
+\r
+#pragma warning(push)\r
+#pragma warning(disable: 4244)\r
+            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) d);\r
+#pragma warning(pop)\r
         }\r
-        break; /* not reached */\r
+        return 0;\r
 \r
-    case WM_DESTROY:\r
+    case WM_PRINTCLIENT:\r
+        in_printclient = TRUE;\r
+        /* fallthrough */\r
+    case WM_PAINT:\r
         {\r
+            HDC hdc;\r
+            PAINTSTRUCT ps;\r
+            RECT r;\r
+            HFONT hf_old;\r
+            int y;\r
             alerter_wnd_data * d;\r
+            alerter_alert_data * adata;\r
+            size_t len;\r
 \r
-            /* khm_leave_modal() could be here, but instead it is in\r
-               the WM_COMMAND handler.  This is because the modal loop\r
-               has to be exited before DestroyWindow() is issued. */\r
-            //khm_leave_modal();\r
-            khm_del_dialog(hwnd);\r
+            d = (alerter_wnd_data *) (LONG_PTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+#ifdef DEBUG\r
+            assert(d);\r
+#endif\r
 \r
-            d = (alerter_wnd_data *)(LONG_PTR) \r
-                GetWindowLongPtr(hwnd, NTF_PARAM);\r
+            if (in_printclient) {\r
+                hdc = (HDC) wParam;\r
+            } else {\r
+                hdc = BeginPaint(hwnd, &ps);\r
+            }\r
 \r
-            LDELETE(&khui_alerts, d);\r
+#ifdef DEBUG\r
+            assert(hdc);\r
+            assert(d->hfont);\r
+#endif\r
 \r
-            khui_alert_lock(d->alert);\r
-            d->alert->flags &= ~KHUI_ALERT_FLAG_DISPLAY_WINDOW;\r
-            if (d->alert->flags & KHUI_ALERT_FLAG_MODAL)\r
-                notifier_modal_loop = FALSE;\r
-            khui_alert_unlock(d->alert);\r
+            if (in_printclient || ps.fErase) {\r
+                HBRUSH hb_background;\r
 \r
-            khui_alert_release(d->alert);\r
+                hb_background = GetSysColorBrush(COLOR_BTNFACE);\r
 \r
-            DeleteObject(d->hfont);\r
+                GetClientRect(hwnd, &r);\r
+                FillRect(hdc, &r, hb_background);\r
+            }\r
 \r
-            PFREE(d);\r
+            SetBkMode(hdc, TRANSPARENT);\r
 \r
-            khm_notify_icon_change(KHERR_NONE);\r
+            hf_old = SelectFont(hdc, d->hfont);\r
 \r
-            return TRUE;\r
-        }\r
-        break;\r
+            y = -d->scroll_top;\r
 \r
-    case WM_PAINT:\r
-        {\r
-            RECT r_update;\r
-            PAINTSTRUCT ps;\r
-            HDC hdc;\r
-            LONG dlgb;\r
-            alerter_wnd_data * d;\r
-            HFONT hf_old;\r
-            BOOL need_resize = FALSE;\r
+            /* go through the alerts and display them */\r
+            adata = QTOP(d);\r
+            while(adata) {\r
+                khui_alert * a;\r
 \r
-            if(!GetUpdateRect(hwnd, &r_update, TRUE))\r
-                return FALSE;\r
+                a = adata->alert;\r
+#ifdef DEBUG\r
+                assert(a != NULL);\r
+#endif\r
+                khui_alert_lock(a);\r
 \r
-            d = (alerter_wnd_data *)(LONG_PTR)\r
-                GetWindowLongPtr(hwnd, NTF_PARAM);\r
+                /* if the alert has a title and it's different from\r
+                   the original caption for the alert dialog, we have\r
+                   to display the title. */\r
+                if (a->title &&\r
+                    wcscmp(a->title, d->caption)) {\r
 \r
-            dlgb = GetDialogBaseUnits();\r
+                    CopyRect(&r, &adata->r_title);\r
+                    OffsetRect(&r, 0, y);\r
 \r
-            hdc = BeginPaint(hwnd, &ps);\r
+                    StringCchLength(a->title, KHUI_MAXCCH_TITLE, &len);\r
 \r
-            hf_old = SelectFont(hdc, d->hfont);\r
+                    DrawEdge(hdc, &r, EDGE_RAISED, BF_RECT | BF_MIDDLE);\r
 \r
-            khui_alert_lock(d->alert);\r
+                    InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy);\r
 \r
-            // draw the severity icon\r
-            {\r
-                HICON hicon;\r
-                int x,y;\r
-                int iid;\r
-\r
-                /* GOINGHERE! If the metrics for the window haven't\r
-                   been calculated yet, then calculate them.  If the\r
-                   hight needs to be expanded, then do that and wait\r
-                   for the next repaint cycle.  Also move the button\r
-                   controls down. */\r
-                x = d->dx_margin;\r
-                y = d->dy_margin;\r
-\r
-                if(d->alert->severity == KHERR_ERROR)\r
-                    iid = OIC_HAND;\r
-                else if(d->alert->severity == KHERR_WARNING)\r
-                    iid = OIC_BANG;\r
-                else\r
-                    iid = OIC_NOTE;\r
+                    DrawText(hdc, a->title, (int) len, &r,\r
+                             DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);\r
+                }\r
 \r
-                hicon = LoadImage(NULL, \r
-                                  MAKEINTRESOURCE(iid), \r
-                                  IMAGE_ICON,\r
-                                  GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),\r
-                                  LR_SHARED);\r
+                {\r
+                    HICON hicon;\r
+                    int iid;\r
+\r
+                    CopyRect(&r, &adata->r_icon);\r
+                    OffsetRect(&r, 0, y);\r
+\r
+                    if(a->severity == KHERR_ERROR)\r
+                        iid = OIC_HAND;\r
+                    else if(a->severity == KHERR_WARNING)\r
+                        iid = OIC_BANG;\r
+                    else\r
+                        iid = OIC_NOTE;\r
+\r
+                    hicon = (HICON) LoadImage(NULL, \r
+                                              MAKEINTRESOURCE(iid), \r
+                                              IMAGE_ICON,\r
+                                              GetSystemMetrics(SM_CXICON),\r
+                                              GetSystemMetrics(SM_CYICON),\r
+                                              LR_SHARED);\r
+\r
+                    DrawIcon(hdc, r.left, r.top, hicon);\r
+                }\r
 \r
-                DrawIcon(hdc, x, y, hicon);\r
-            }\r
+                if (a->message) {\r
 \r
-            // draw the message\r
-            if(d->alert->message) {\r
-                RECT r;\r
-                int width;\r
-                int height;\r
-                size_t cch;\r
-\r
-                r.left = d->x_message;\r
-                r.top = d->dy_margin;\r
-                width = d->dx_message;\r
-                r.right = r.left + width;\r
-                height = d->dy_message;\r
-                r.bottom = r.top + height;\r
-\r
-                StringCchLength(d->alert->message, \r
-                                KHUI_MAXCCH_MESSAGE, &cch);\r
-                \r
-                height = DrawText(hdc,\r
-                                  d->alert->message,\r
-                                  (int) cch,\r
-                                  &r,\r
-                                  DT_WORDBREAK |\r
-                                  DT_CALCRECT);\r
-\r
-                if (height > d->dy_message) {\r
-                    d->dy_message = height;\r
-                    need_resize = TRUE;\r
-                } else {\r
-                    DrawText(hdc,\r
-                             d->alert->message,\r
-                             (int) cch,\r
-                             &r,\r
-                             DT_WORDBREAK);\r
-                }\r
+                    CopyRect(&r, &adata->r_message);\r
+                    OffsetRect(&r, 0, y);\r
 \r
-                d->y_message = r.top;\r
-            }\r
+                    StringCchLength(a->message, KHUI_MAXCCH_MESSAGE, &len);\r
 \r
-            // and the suggestion\r
-            if (d->alert->suggestion) {\r
-                RECT r, ro;\r
-                int height;\r
-                size_t cch;\r
-                HICON h_sug_ico;\r
-\r
-                r.left = d->x_message;\r
-                r.top = d->y_message + d->dy_message + d->dy_margin;\r
-                r.right = r.left + d->dx_message;\r
-                r.bottom = r.top + d->dy_suggestion;\r
-\r
-                CopyRect(&ro, &r);\r
-\r
-                // adjust for icon and padding\r
-                r.left += GetSystemMetrics(SM_CXSMICON) + d->dx_suggest_pad * 2;\r
-                r.top += d->dx_suggest_pad;\r
-                r.right -= d->dx_suggest_pad;\r
-                r.bottom -= d->dx_suggest_pad;\r
-\r
-                StringCchLength(d->alert->suggestion,\r
-                                KHUI_MAXCCH_SUGGESTION, &cch);\r
-\r
-                height = DrawText(hdc,\r
-                                  d->alert->suggestion,\r
-                                  (int) cch,\r
-                                  &r,\r
-                                  DT_WORDBREAK |\r
-                                  DT_CALCRECT);\r
-\r
-                if (height > d->dy_suggestion) {\r
-                    d->dy_suggestion = height;\r
-                    need_resize = TRUE;\r
-                } else {\r
-                    int old_bk_mode;\r
-\r
-                    ro.bottom = r.bottom + d->dx_suggest_pad;\r
-\r
-                    FillRect(hdc, &ro, (HBRUSH) (COLOR_INFOBK + 1));\r
-                    DrawEdge(hdc, &ro, EDGE_SUNKEN, BF_FLAT | BF_RECT);\r
-\r
-                    h_sug_ico = \r
-                        LoadImage(0,\r
-                                  MAKEINTRESOURCE(OIC_INFORMATION),\r
-                                  IMAGE_ICON,\r
-                                  GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),\r
-                                  LR_SHARED);\r
-\r
-                    assert(h_sug_ico != NULL);\r
-\r
-                    DrawIconEx(hdc, \r
-                               ro.left + d->dx_suggest_pad, \r
-                               ro.top + d->dx_suggest_pad, \r
-                               h_sug_ico,\r
-                               GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),\r
-                               0, NULL,\r
-                               DI_NORMAL);\r
-\r
-                    old_bk_mode = SetBkMode(hdc, TRANSPARENT);\r
-\r
-                    DrawText(hdc,\r
-                             d->alert->suggestion,\r
-                             (int) cch,\r
-                             &r,\r
+                    DrawText(hdc, a->message, (int) len, &r,\r
                              DT_WORDBREAK);\r
-\r
-                    SetBkMode(hdc, old_bk_mode);\r
                 }\r
 \r
-                d->y_suggestion = r.top;\r
-            }\r
+                if (a->suggestion) {\r
+                    HICON hicon;\r
 \r
-            khui_alert_unlock(d->alert);\r
+                    CopyRect(&r, &adata->r_suggestion);\r
+                    OffsetRect(&r, 0, y);\r
 \r
-            SelectObject(hdc, hf_old);\r
+                    DrawEdge(hdc, &r, EDGE_SUNKEN, BF_RECT | BF_MIDDLE);\r
 \r
-            EndPaint(hwnd, &ps);\r
+                    InflateRect(&r, -d->s_pad.cx, -d->s_pad.cy);\r
 \r
-            if (need_resize) {\r
-                RECT r;\r
-                int x,y;\r
-                int width, height;\r
-                int i;\r
-                \r
-                GetClientRect(hwnd, &r);\r
+                    hicon = (HICON) LoadImage(NULL,\r
+                                              MAKEINTRESOURCE(OIC_NOTE),\r
+                                              IMAGE_ICON,\r
+                                              GetSystemMetrics(SM_CXSMICON),\r
+                                              GetSystemMetrics(SM_CYSMICON),\r
+                                              LR_SHARED);\r
 \r
-                height = max(d->dy_icon,\r
-                             d->dy_message +\r
-                             ((d->dy_suggestion > 0)?\r
-                              (d->dy_suggestion + d->dy_margin):\r
-                              0)) +\r
-                    d->dy_margin * 3 + d->dy_bb;\r
-                r.bottom = r.top + height;\r
-\r
-                d->dy_client = height;\r
-\r
-                AdjustWindowRectEx(&r,\r
-                                   GetWindowLongPtr(hwnd, GWL_STYLE),\r
-                                   FALSE,\r
-                                   GetWindowLongPtr(hwnd, GWL_EXSTYLE));\r
-\r
-                SetWindowPos(hwnd,\r
-                             NULL,\r
-                             0, 0,\r
-                             r.right - r.left,\r
-                             r.bottom - r.top,\r
-                             SWP_NOACTIVATE | SWP_NOCOPYBITS |\r
-                             SWP_NOMOVE | SWP_NOOWNERZORDER |\r
-                             SWP_NOZORDER);\r
-\r
-                InvalidateRect(hwnd, NULL, TRUE);\r
-\r
-                x = d->x_message;\r
-                y = d->dy_client - d->dy_bb;\r
-                width = d->dx_button;\r
-                height = d->dy_button;\r
-\r
-                for(i=0; i<d->alert->n_alert_commands; i++) {\r
-                    MoveWindow(d->hwnd_buttons[i],\r
-                               x,y,\r
-                               width,height,\r
-                               TRUE);\r
-\r
-                    x += d->dx_button_incr;\r
-                }\r
-            }\r
+                    DrawIcon(hdc, r.left, r.top, hicon);\r
 \r
-            return FALSE;\r
-        }\r
-        break; /* not reached */\r
+                    r.left += d->s_pad.cx + GetSystemMetrics(SM_CXSMICON);\r
 \r
-    case WM_COMMAND:\r
-        {\r
-            alerter_wnd_data * d;\r
+                    StringCchLength(a->suggestion, KHUI_MAXCCH_SUGGESTION, &len);\r
 \r
-            d = (alerter_wnd_data *)(LONG_PTR) \r
-                GetWindowLongPtr(hwnd, NTF_PARAM);\r
+                    DrawText(hdc, a->suggestion, (int) len, &r,\r
+                             DT_WORDBREAK);\r
+                }\r
+                khui_alert_unlock(a);\r
 \r
-            if(HIWORD(wParam) == BN_CLICKED) {\r
-                khui_alert_lock(d->alert);\r
-                d->alert->response = LOWORD(wParam);\r
-                khui_alert_unlock(d->alert);\r
+                y += adata->r_alert.bottom;\r
 \r
-                khm_leave_modal();\r
+                adata = QNEXT(adata);\r
+            }\r
 \r
-                DestroyWindow(hwnd);\r
+            SelectFont(hdc, hf_old);\r
 \r
-                if (LOWORD(wParam) == KHUI_PACTION_NEXT)\r
-                    kmq_post_message(KMSG_ALERT, KMSG_ALERT_SHOW_QUEUED, 0, 0);\r
-                return 0;\r
+            if (!in_printclient) {\r
+                EndPaint(hwnd, &ps);\r
             }\r
         }\r
+        return 0;\r
+\r
+    case WM_VSCROLL:\r
+        {\r
+        }\r
+        return 0;\r
+\r
+    case WM_COMMAND:\r
+        {\r
+        }\r
         break;\r
+\r
+    case WM_DESTROY:\r
+        {\r
+        }\r
+        return 0;\r
     }\r
 \r
-    return DefDlgProc(hwnd, uMsg, wParam, lParam);\r
-    //return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
+    return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
 }\r
 \r
 ATOM khm_register_alerter_wnd_class(void)\r
@@ -1061,7 +2030,7 @@ ATOM khm_register_alerter_wnd_class(void)
     wcx.hInstance = khm_hInstance;\r
     wcx.hIcon = LoadIcon(khm_hInstance, MAKEINTRESOURCE(IDI_MAIN_APP));\r
     wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));\r
-    wcx.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);\r
+    wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);\r
     wcx.lpszMenuName = NULL;\r
     wcx.lpszClassName = KHUI_ALERTER_CLASS;\r
     wcx.hIconSm = NULL;\r
@@ -1071,6 +2040,31 @@ ATOM khm_register_alerter_wnd_class(void)
     return atom_alerter;\r
 }\r
 \r
+ATOM khm_register_alert_bin_wnd_class(void)\r
+{\r
+    WNDCLASSEX wcx;\r
+\r
+    ZeroMemory(&wcx, sizeof(wcx));\r
+\r
+    wcx.cbSize = sizeof(wcx);\r
+    wcx.style = CS_OWNDC;\r
+\r
+    wcx.lpfnWndProc = alert_bin_wnd_proc;\r
+    wcx.cbClsExtra = 0;\r
+    wcx.cbWndExtra = sizeof(LONG_PTR);\r
+    wcx.hInstance = khm_hInstance;\r
+    wcx.hIcon = NULL;\r
+    wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));\r
+    wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);\r
+    wcx.lpszMenuName = NULL;\r
+    wcx.lpszClassName = KHUI_ALERTBIN_CLASS;\r
+    wcx.hIconSm = NULL;\r
+\r
+    atom_alert_bin = RegisterClassEx(&wcx);\r
+\r
+    return atom_alert_bin;\r
+}\r
+\r
 /**********************************************************************\r
   Notification Icon\r
 ***********************************************************************/\r
@@ -1140,19 +2134,20 @@ khm_notify_icon_balloon(khm_int32 severity,
         /* too long? */\r
         StringCchCopyN(ni.szInfo, ARRAYLENGTH(ni.szInfo),\r
                        msg, \r
-                       ARRAYLENGTH(ni.szInfo) - ARRAYLENGTH(ELIPSIS));\r
+                       ARRAYLENGTH(ni.szInfo) - ARRAYLENGTH(ELLIPSIS));\r
         StringCchCat(ni.szInfo, ARRAYLENGTH(ni.szInfo),\r
-                     ELIPSIS);\r
+                     ELLIPSIS);\r
     }\r
 \r
     if (FAILED(StringCbCopy(ni.szInfoTitle, sizeof(ni.szInfoTitle), \r
                             title))) {\r
         StringCchCopyN(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle),\r
                        title, \r
-                       ARRAYLENGTH(ni.szInfoTitle) - ARRAYLENGTH(ELIPSIS));\r
+                       ARRAYLENGTH(ni.szInfoTitle) - ARRAYLENGTH(ELLIPSIS));\r
         StringCchCat(ni.szInfoTitle, ARRAYLENGTH(ni.szInfoTitle),\r
-                     ELIPSIS);\r
+                     ELLIPSIS);\r
     }\r
+\r
     ni.uTimeout = timeout;\r
 \r
     Shell_NotifyIcon(NIM_MODIFY, &ni);\r
@@ -1177,7 +2172,7 @@ void khm_notify_icon_expstate(enum khm_notif_expstate expseverity) {
 \r
     iid_normal = new_iid;\r
 \r
-    if (current_alert == NULL)\r
+    if (balloon_alert == NULL)\r
         khm_notify_icon_change(KHERR_NONE);\r
 }\r
 \r
@@ -1239,6 +2234,9 @@ void khm_init_notifier(void)
     if(!khm_register_alerter_wnd_class())\r
         return;\r
 \r
+    if(!khm_register_alert_bin_wnd_class())\r
+        return;\r
+\r
     hwnd_notifier = CreateWindowEx(0,\r
                                    MAKEINTATOM(atom_notifier),\r
                                    KHUI_NOTIFIER_WINDOW,\r
@@ -1255,12 +2253,11 @@ void khm_init_notifier(void)
         notifier_ready = TRUE;\r
 \r
         khm_notify_icon_add();\r
-    }\r
+    } else {\r
 #ifdef DEBUG\r
-    else {\r
         assert(hwnd_notifier != NULL);\r
-    }\r
 #endif\r
+    }\r
     khm_timer_init();\r
 \r
     khm_addr_change_notifier_init();\r
@@ -1290,5 +2287,10 @@ void khm_exit_notifier(void)
         atom_alerter = 0;\r
     }\r
 \r
+    if(atom_alert_bin != 0) {\r
+        UnregisterClass(MAKEINTATOM(atom_alert_bin), khm_hInstance);\r
+        atom_alert_bin = 0;\r
+    }\r
+\r
     notifier_ready = FALSE;\r
 }\r
index 79a6cc35a03b32dea41be62057b1d146ccb1d4ea..4061cd78d25b7f84414c5d2ea87d9f742c088c62 100644 (file)
@@ -230,7 +230,7 @@ khm_int32 khm_register_propertywnd_class(void)
     wcx.hInstance = khm_hInstance;\r
     wcx.hIcon = NULL;\r
     wcx.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);\r
-    wcx.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);\r
+    wcx.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);\r
     wcx.lpszMenuName = NULL;\r
     wcx.lpszClassName = KHUI_PROPERTYWND_CLASS_NAME;\r
     wcx.hIconSm = NULL;\r
index b95f02daf788793d84fcef6fc46bdd3f15eb5233..e72e3e22b81c9c8831927707b4cc0903284cc6c3 100644 (file)
@@ -350,6 +350,8 @@ khm_reqdaemon_thread_proc(LPVOID vparam) {
     DWORD dw;\r
 #endif\r
 \r
+    PDESCTHREAD(L"Remote Request Daemon", L"App");\r
+\r
     khm_register_reqdaemonwnd_class();\r
 \r
 #ifdef DEBUG\r
index f430fe0b08e59152ed672982edcd4bbde5e525b9..69d35b31602d870dc72916a24974fbf9c9aab42a 100644 (file)
 #define IDD_NC_NEWCRED                  162\r
 #define IDD_NC_BBAR                     163\r
 #define IDS_ALERT_NOSEL_TITLE           163\r
-#define IDD_NC_TS                       164\r
 #define IDS_ALERT_NOSEL                 164\r
 #define IDI_ENABLED                     165\r
 #define IDS_NC_CREDTEXT_ID_VALID        165\r
 #define IDS_IDACTION_RENEW              294\r
 #define IDS_IDACTION_DESTROY            295\r
 #define IDS_CTX_DESTROY_ID              296\r
+#define IDS_NCN_IDENT_INVALID           297\r
+#define IDS_NCN_IDENT_CHECKING          298\r
+#define IDS_NCN_IDENT_UNKNOWN           299\r
+#define IDS_REMOTE_FAIL                 300\r
+#define IDS_REMOTE_FAIL_TITLE           301\r
 #define IDC_NC_USERNAME                 1007\r
 #define IDC_NC_PASSWORD                 1008\r
 #define IDC_NC_CREDTEXT_LABEL           1009\r
 #define IDC_NC_CREDTEXT                 1012\r
 #define IDC_NC_HELP                     1017\r
 #define IDC_NC_OPTIONS                  1019\r
+#define IDC_NC_ADVANCED                 1019\r
 #define IDC_PP_IDNAME                   1026\r
 #define IDC_PP_IDDEF                    1027\r
 #define IDC_PP_IDSEARCH                 1028\r
index 9f5e5e4b37bc14af31bb77fc5a0ba18fa60288a2..1cde800559d89fb44acd3b1ccc2ccfd0820cdeaa 100644 (file)
@@ -37,6 +37,9 @@ CredWindow,KC_SPACE,0,Options for the credentials window
     Main,KC_ENDSPACE,0,\r
     NewCred,KC_SPACE,0,New credentials window\r
       ForceToTop,KC_INT32,1,Force new creds window to the top\r
+      AnimateSizeChanges,KC_INT32,1,Animate the new creds window when the size needs changing.\r
+      AnimationSteps,KC_INT32,7,Number of steps in size-change animation\r
+      AnimationStepTimeout,KC_INT32,40,Number of milliseconds to wait between each step of the size-change animation\r
     NewCred,KC_ENDSPACE,0,\r
   Windows,KC_ENDSPACE,0,\r
   Views,KC_SPACE,0,Preconfigured views for credentials\r
index 6a9fb53ba78f344dcb0907e1f7cd5c270432e136..5686db52384b881dfcb525b578d38305d13f6ed9 100644 (file)
@@ -52,7 +52,8 @@ INCFILES=                     \
        $(INCDIR)\khconfigui.h  \\r
        $(INCDIR)\khtracker.h   \\r
        $(INCDIR)\khremote.h    \\r
-       $(INCDIR)\intaction.h\r
+       $(INCDIR)\intaction.h   \\r
+       $(INCDIR)\intalert.h\r
 \r
 $(OBJ)\actiondef.c: actions.csv actiondef.cfg\r
        $(CCSV) $** $@\r
index e38726879fccaffc88c02adbe6876c82cbbcd49b..7c42889de9a801e00dd6d553ba8397c919692042 100644 (file)
@@ -261,6 +261,21 @@ khui_exit_actions(void) {
     DeleteCriticalSection(&cs_actions);\r
 }\r
 \r
+KHMEXP void KHMAPI\r
+khui_refresh_actions(void) {\r
+    kmq_post_message(KMSG_ACT, KMSG_ACT_REFRESH, 0, 0);\r
+}\r
+\r
+KHMEXP void KHMAPI\r
+khui_action_lock(void) {\r
+    EnterCriticalSection(&cs_actions);\r
+}\r
+\r
+KHMEXP void KHMAPI\r
+khui_action_unlock(void) {\r
+    LeaveCriticalSection(&cs_actions);\r
+}\r
+\r
 KHMEXP khm_int32 KHMAPI\r
 khui_action_create(const wchar_t * name,\r
                    const wchar_t * caption,\r
@@ -350,29 +365,36 @@ khui_action_create(const wchar_t * name,
 KHMEXP void * KHMAPI\r
 khui_action_get_data(khm_int32 action) {\r
     khui_action * act;\r
+    void * data;\r
 \r
+    EnterCriticalSection(&cs_actions);\r
     act = khui_find_action(action);\r
-\r
-    if (act == NULL)\r
-        return NULL;\r
+    if (act == NULL || (act->state & KHUI_ACTIONSTATE_DELETED))\r
+        data = NULL;\r
     else\r
-        return act->data;\r
+        data = act->data;\r
+    LeaveCriticalSection(&cs_actions);\r
+\r
+    return data;\r
 }\r
 \r
 KHMEXP void KHMAPI\r
 khui_action_delete(khm_int32 action) {\r
     khui_action * act;\r
 \r
+    EnterCriticalSection(&cs_actions);\r
+\r
     act = khui_find_action(action);\r
 \r
-    if (act == NULL)\r
+    if (act == NULL) {\r
+        LeaveCriticalSection(&cs_actions);\r
         return;\r
+    }\r
 \r
     /* for the moment, even when the action is deleted, we don't free\r
        up the block of memory used by the khui_action structure.  When\r
        a new action is created, it will reuse deleted action\r
        structures. */\r
-    EnterCriticalSection(&cs_actions);\r
     act->state |= KHUI_ACTIONSTATE_DELETED;\r
     if (act->name)\r
         PFREE(act->name);\r
@@ -470,6 +492,8 @@ khui_menu_dup(khui_menu_def * src)
     size_t i;\r
     size_t n;\r
 \r
+    EnterCriticalSection(&cs_actions);\r
+\r
     d = khui_menu_create(src->cmd);\r
 \r
     if (!(src->state & KHUI_MENUSTATE_ALLOCD))\r
@@ -485,6 +509,8 @@ khui_menu_dup(khui_menu_def * src)
         }\r
     }\r
 \r
+    LeaveCriticalSection(&cs_actions);\r
+\r
     return d;\r
 }\r
 \r
@@ -504,13 +530,13 @@ khui_menu_delete(khui_menu_def * d)
     }\r
 \r
     EnterCriticalSection(&cs_actions);\r
+\r
     for (i=0; i < khui_n_cust_menus; i++) {\r
         if (khui_cust_menus[i] == d) {\r
             khui_cust_menus[i] = NULL;\r
             break;\r
         }\r
     }\r
-    LeaveCriticalSection(&cs_actions);\r
 \r
     for(i=0; i< (int) d->n_items; i++) {\r
         if(d->items[i].flags & KHUI_ACTIONREF_FREE_PACTION)\r
@@ -520,6 +546,8 @@ khui_menu_delete(khui_menu_def * d)
     if(d->items)\r
         PFREE(d->items);\r
     PFREE(d);\r
+\r
+    LeaveCriticalSection(&cs_actions);\r
 }\r
 \r
 static void\r
@@ -563,6 +591,9 @@ menu_const_to_allocd(khui_menu_def * d)
 KHMEXP void KHMAPI\r
 khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 action, khm_int32 flags)\r
 {\r
+\r
+    EnterCriticalSection(&cs_actions);\r
+\r
     if (!(d->state & KHUI_MENUSTATE_ALLOCD))\r
         menu_const_to_allocd(d);\r
 \r
@@ -584,6 +615,8 @@ khui_menu_insert_action(khui_menu_def * d, khm_size idx, khm_int32 action, khm_i
         d->items[idx].flags |= KHUI_ACTIONREF_SEP;\r
 \r
     d->n_items++;\r
+\r
+    LeaveCriticalSection(&cs_actions);\r
 }\r
 \r
 KHMEXP void KHMAPI\r
@@ -593,6 +626,8 @@ khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * paction,
     if (paction == NULL)\r
         return;\r
 \r
+    EnterCriticalSection(&cs_actions);\r
+\r
     if (!(d->state & KHUI_MENUSTATE_ALLOCD))\r
         menu_const_to_allocd(d);\r
 \r
@@ -611,50 +646,72 @@ khui_menu_insert_paction(khui_menu_def * d, khm_size idx, khui_action * paction,
     d->items[idx].p_action = paction;\r
 \r
     d->n_items++;\r
+\r
+    LeaveCriticalSection(&cs_actions);\r
 }\r
 \r
 KHMEXP void KHMAPI\r
 khui_menu_remove_action(khui_menu_def * d, khm_size idx) {\r
 \r
+    EnterCriticalSection(&cs_actions);\r
+\r
     if (!(d->state & KHUI_MENUSTATE_ALLOCD))\r
         menu_const_to_allocd(d);\r
 \r
     assert(d->state & KHUI_MENUSTATE_ALLOCD);\r
 \r
-    if (idx < 0 || idx >= d->n_items)\r
-        return;\r
+    if (idx >= 0 && idx < d->n_items) {\r
+\r
+        if (idx < d->n_items - 1) {\r
+            memmove(&d->items[idx], &d->items[idx + 1],\r
+                    ((d->n_items - 1) - idx) * sizeof(d->items[0]));\r
+        }\r
+\r
+        d->n_items--;\r
 \r
-    if (idx < d->n_items - 1) {\r
-        memmove(&d->items[idx], &d->items[idx + 1],\r
-                ((d->n_items - 1) - idx) * sizeof(d->items[0]));\r
     }\r
 \r
-    d->n_items--;\r
+    LeaveCriticalSection(&cs_actions);\r
 }\r
 \r
 KHMEXP khm_size KHMAPI\r
 khui_menu_get_size(khui_menu_def * d) {\r
 \r
+    khm_size size;\r
+\r
+    EnterCriticalSection(&cs_actions);\r
+\r
     if (d->state & KHUI_MENUSTATE_ALLOCD)\r
-        return d->n_items;\r
+        size = d->n_items;\r
     else\r
-        return khui_action_list_length(d->items);\r
+        size = khui_action_list_length(d->items);\r
+\r
+    LeaveCriticalSection(&cs_actions);\r
+\r
+    return size;\r
 }\r
 \r
 KHMEXP khui_action_ref *\r
 khui_menu_get_action(khui_menu_def * d, khm_size idx) {\r
 \r
+    khui_action_ref * act = NULL;\r
     khm_size n;\r
 \r
+    EnterCriticalSection(&cs_actions);\r
+\r
     if (d->state & KHUI_MENUSTATE_ALLOCD)\r
         n = d->n_items;\r
     else\r
         n = khui_action_list_length(d->items);\r
 \r
     if (idx < 0 || idx >= n)\r
-        return NULL;\r
+        act = NULL;\r
+    else\r
+        act = &d->items[idx];\r
+\r
+    LeaveCriticalSection(&cs_actions);\r
 \r
-    return &d->items[idx];\r
+    return act;\r
 }\r
 \r
 KHMEXP khui_menu_def * KHMAPI\r
@@ -663,6 +720,9 @@ khui_find_menu(khm_int32 id) {
     int i;\r
 \r
     if (id < KHUI_USERACTION_BASE) {\r
+\r
+        /* the list of system menus are considered immutable. */\r
+\r
         d = khui_all_menus;\r
         for(i=0;i<khui_n_all_menus;i++) {\r
             if(id == d[i].cmd)\r
@@ -707,7 +767,7 @@ khui_find_action(khm_int32 id) {
 #ifdef DEBUG\r
         assert(!act || act->cmd == id);\r
 #endif\r
-        if (act && act->state & KHUI_ACTIONSTATE_DELETED)\r
+        if (act && (act->state & KHUI_ACTIONSTATE_DELETED))\r
             act = NULL;\r
     }\r
     LeaveCriticalSection(&cs_actions);\r
@@ -732,30 +792,43 @@ khui_find_named_action(const wchar_t * name) {
             return &act[i];\r
     }\r
 \r
+    act = NULL;\r
+\r
+    EnterCriticalSection(&cs_actions);\r
+\r
     pact = khui_cust_actions;\r
     for(i=0;i<khui_n_cust_actions;i++) {\r
         if(!pact[i] || !pact[i]->name)\r
             continue;\r
 \r
         if(!wcscmp(pact[i]->name, name)) {\r
-            if (pact[i]->state & KHUI_ACTIONSTATE_DELETED)\r
-                return NULL;\r
-            else\r
-                return pact[i];\r
+\r
+            if (!(pact[i]->state & KHUI_ACTIONSTATE_DELETED)) {\r
+                act = pact[i];\r
+            }\r
+            break;\r
         }\r
     }\r
 \r
-    return NULL;\r
+    LeaveCriticalSection(&cs_actions);\r
+\r
+    return act;\r
 }\r
 \r
 KHMEXP size_t KHMAPI\r
 khui_action_list_length(khui_action_ref * ref) {\r
     size_t c = 0;\r
+\r
+    EnterCriticalSection(&cs_actions);\r
+\r
     while(ref && ref->action != KHUI_MENU_END &&\r
           !(ref->flags & KHUI_ACTIONREF_END)) {\r
         c++;\r
         ref++;\r
     }\r
+\r
+    LeaveCriticalSection(&cs_actions);\r
+\r
     return c;\r
 }\r
 \r
@@ -765,6 +838,8 @@ khui_check_radio_action(khui_menu_def * d, khm_int32 cmd)
     khui_action_ref * r;\r
     khui_action * act;\r
 \r
+    EnterCriticalSection(&cs_actions);\r
+\r
     r = d->items;\r
     while(r && r->action != KHUI_MENU_END &&\r
           (!(d->state & KHUI_MENUSTATE_ALLOCD) || (r - d->items) < (int) d->n_items)) {\r
@@ -783,6 +858,8 @@ khui_check_radio_action(khui_menu_def * d, khm_int32 cmd)
         r++;\r
     }\r
 \r
+    LeaveCriticalSection(&cs_actions);\r
+\r
     kmq_post_message(KMSG_ACT, KMSG_ACT_CHECK, 0, 0);\r
 }\r
 \r
@@ -794,12 +871,18 @@ khui_check_action(khm_int32 cmd, khm_boolean check) {
     if (!act)\r
         return;\r
 \r
+    EnterCriticalSection(&cs_actions);\r
+\r
     if (check && !(act->state & KHUI_ACTIONSTATE_CHECKED))\r
         act->state |= KHUI_ACTIONSTATE_CHECKED;\r
     else if (!check && (act->state & KHUI_ACTIONSTATE_CHECKED))\r
         act->state &= ~KHUI_ACTIONSTATE_CHECKED;\r
-    else\r
+    else {\r
+        LeaveCriticalSection(&cs_actions);\r
         return;\r
+    }\r
+\r
+    LeaveCriticalSection(&cs_actions);\r
 \r
     kmq_post_message(KMSG_ACT, KMSG_ACT_CHECK, 0, 0);\r
 }\r
@@ -811,6 +894,8 @@ khui_enable_actions(khui_menu_def * d, khm_boolean enable)
     int delta = FALSE;\r
     khui_action * act;\r
 \r
+    EnterCriticalSection(&cs_actions);\r
+\r
     r = d->items;\r
     while(r && r->action != KHUI_MENU_END &&\r
           (!(d->state & KHUI_MENUSTATE_ALLOCD) || (r - d->items) < (int) d->n_items)) {\r
@@ -834,6 +919,8 @@ khui_enable_actions(khui_menu_def * d, khm_boolean enable)
         r++;\r
     }\r
 \r
+    LeaveCriticalSection(&cs_actions);\r
+\r
     if(delta) {\r
         kmq_post_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0);\r
     }\r
@@ -847,12 +934,18 @@ khui_enable_action(khm_int32 cmd, khm_boolean enable) {
     if (!act)\r
         return;\r
 \r
+    EnterCriticalSection(&cs_actions);\r
+\r
     if (enable && (act->state & KHUI_ACTIONSTATE_DISABLED)) {\r
         act->state &= ~KHUI_ACTIONSTATE_DISABLED;\r
     } else if (!enable && !(act->state & KHUI_ACTIONSTATE_DISABLED)) {\r
         act->state |= KHUI_ACTIONSTATE_DISABLED;\r
-    } else\r
+    } else {\r
+        LeaveCriticalSection(&cs_actions);\r
         return;\r
+    }\r
+\r
+    LeaveCriticalSection(&cs_actions);\r
 \r
     kmq_post_message(KMSG_ACT, KMSG_ACT_ENABLE, 0, 0);\r
 }\r
index 0627a67e5a468be3b8be223d6b79534d0866f3db..561335257f0dcfc72232209674e4b737a049beae 100644 (file)
@@ -33,6 +33,7 @@ Do not modify directly.
 \r
 #include<khuidefs.h>\r
 #include<khhelp.h>\r
+#include<intaction.h>\r
 #include"../ui/resource.h"\r
 \r
 khui_action khui_actions [] = {\r
index 96436543a7eee76a1cb92a27f937337c7e901e9b..e398690e92f19fca8d22cadce81aca6498cca3e4 100644 (file)
@@ -26,6 +26,7 @@
 \r
 #include<khuidefs.h>\r
 #include<utils.h>\r
+#include<intalert.h>\r
 #include<assert.h>\r
 \r
 #include<strsafe.h>\r
@@ -62,7 +63,11 @@ khui_alert_create_empty(khui_alert ** result)
     /* set defaults */\r
     a->severity = KHERR_INFO;\r
     a->flags = KHUI_ALERT_FLAG_FREE_STRUCT;\r
-    \r
+    a->alert_type = KHUI_ALERTTYPE_NONE;\r
+    khui_context_create(&a->ctx, KHUI_SCOPE_NONE,\r
+                        NULL, KCDB_CREDTYPE_INVALID,\r
+                        NULL);\r
+\r
     khui_alert_hold(a);\r
     EnterCriticalSection(&cs_alerts);\r
     LPUSH(&kh_alerts, a);\r
@@ -253,6 +258,57 @@ khui_alert_add_command(khui_alert * alert, khm_int32 command_id)
     return rv;\r
 }\r
 \r
+KHMEXP khm_int32 KHMAPI\r
+khui_alert_set_type(khui_alert * alert, khui_alert_type alert_type)\r
+{\r
+    khm_int32 rv = KHM_ERROR_SUCCESS;\r
+\r
+    assert(alert->magic == KHUI_ALERT_MAGIC);\r
+\r
+    EnterCriticalSection(&cs_alerts);\r
+    alert->alert_type = alert_type;\r
+    LeaveCriticalSection(&cs_alerts);\r
+\r
+    return rv;\r
+}\r
+\r
+KHMEXP khm_int32 KHMAPI\r
+khui_alert_set_ctx(khui_alert * alert,\r
+                   khui_scope scope,\r
+                   khm_handle identity,\r
+                   khm_int32 cred_type,\r
+                   khm_handle cred)\r
+{\r
+    khm_int32 rv = KHM_ERROR_SUCCESS;\r
+\r
+    assert(alert->magic == KHUI_ALERT_MAGIC);\r
+\r
+    EnterCriticalSection(&cs_alerts);\r
+    khui_context_release(&alert->ctx);\r
+    khui_context_create(&alert->ctx,\r
+                        scope,\r
+                        identity,\r
+                        cred_type,\r
+                        cred);\r
+    LeaveCriticalSection(&cs_alerts);\r
+\r
+    return rv;\r
+}\r
+\r
+KHMEXP khm_int32 KHMAPI\r
+khui_alert_get_response(khui_alert * alert)\r
+{\r
+    khm_int32 response = 0;\r
+\r
+    assert(alert->magic == KHUI_ALERT_MAGIC);\r
+\r
+    EnterCriticalSection(&cs_alerts);\r
+    response = alert->response;\r
+    LeaveCriticalSection(&cs_alerts);\r
+\r
+    return response;\r
+}\r
+\r
 KHMEXP khm_int32 KHMAPI \r
 khui_alert_show(khui_alert * alert)\r
 {\r
@@ -347,6 +403,9 @@ free_alert(khui_alert * alert)
         alert->suggestion = NULL;\r
         alert->flags &= ~KHUI_ALERT_FLAG_FREE_SUGGEST;\r
     }\r
+\r
+    khui_context_release(&alert->ctx);\r
+\r
     if(alert->flags & KHUI_ALERT_FLAG_FREE_STRUCT) {\r
         alert->flags &= ~KHUI_ALERT_FLAG_FREE_STRUCT;\r
         alert->magic = 0;\r
index 1d36d3c5bdce29c4ae919953211bc313ab851b0c..c89a446203f0813aca80bcee18a700fae468b0a7 100644 (file)
@@ -418,6 +418,9 @@ cw_free_prompts(khui_new_creds * c)
 KHMEXP khm_int32 KHMAPI \r
 khui_cw_clear_prompts(khui_new_creds * c)\r
 {\r
+    /* the WMNC_CLEAR_PROMPT message needs to be sent before freeing\r
+       the prompts, because the prompts structure still holds the\r
+       window handles for the custom prompt controls. */\r
     SendMessage(c->hwnd, KHUI_WM_NC_NOTIFY, \r
                 MAKEWPARAM(0,WMNC_CLEAR_PROMPTS), (LPARAM) c);\r
 \r
index 2b4a66a1f26fb954ff322d6202e9dfb108343018..44348b9bbb85d07d26a720d72f5e3e02af863a0a 100644 (file)
@@ -41,4 +41,68 @@ typedef struct tag_khui_ui_callback_data {
 \r
 #define KHUI_UICBDATA_MAGIC 0x8a08572a\r
 \r
+/*! \addtogroup khui_actions\r
+@{ */\r
+\r
+/*! \brief An action */\r
+typedef struct tag_khui_action {\r
+    khm_int32 cmd;            /*!< action identifier */\r
+    khm_int32 type;           /*!< combination of KHUI_ACTIONTYPE_* */\r
+    wchar_t * name;           /*!< name for named actions.  NULL if\r
+                                not named. */\r
+\r
+    /* The following fields are only for use by NetIDMgr */\r
+    khm_int16 ib_normal;      /*!< (internal) normal bitmap (index) (toolbar sized icon) */\r
+    khm_int16 ib_hot;         /*!< (internal) hot bitmap (index) (toolbar sized icon) */\r
+    khm_int16 ib_disabled;    /*!< (internal) disabled bitmap (index) (toolbar sized icon) */\r
+\r
+    khm_int16 ib_icon;        /*!< (internal) index of small (16x16) icon (for menu) (small icon) */\r
+    khm_int16 ib_icon_dis;    /*!< (internal) index of disabled (greyed) icon (small icon) */\r
+\r
+    khm_int16 is_caption;     /*!< (internal) index of string resource for caption */\r
+    khm_int16 is_tooltip;     /*!< (internal) same for description / tooltip */\r
+    khm_int16 ih_topic;       /*!< (internal) help topic */\r
+\r
+    /* The following fields are specified for custom actions */\r
+    wchar_t * caption;        /*!< Caption (localized) (limited by\r
+                                  KHUI_MAXCCH_SHORT_DESC).  The\r
+                                  caption is used for representing the\r
+                                  action in menus and toolbars. */\r
+    wchar_t * tooltip;        /*!< Tooltip (localized) (limited by\r
+                                  KHUI_MAXCCH_SHORT_DESC).  If this is\r
+                                  specified, whenever the user hovers\r
+                                  over the menu item or toolbar button\r
+                                  representing the action, the tooltip\r
+                                  will be displayed either on a\r
+                                  tooltip window or in the status\r
+                                  bar. */\r
+    khm_handle listener;      /*!< Listener of this action.  Should be\r
+                                  a handle to a message\r
+                                  subscription. When the action is\r
+                                  invoked, a message of type\r
+                                  ::KMSG_ACT and subtype\r
+                                  ::KMSG_ACT_ACTIVATE will be posted\r
+                                  to this subscriber. The \a uparam\r
+                                  parameter of the message will have\r
+                                  the identifier of the action. */\r
+    void *    data;           /*!< User data for custom action.  This\r
+                                  field is not used by the UI library.\r
+                                  It is reserved for plugins to store\r
+                                  data that is specific for this\r
+                                  action.  The data that's passed in\r
+                                  in the \a userdata parameter to\r
+                                  khui_action_create() will be stored\r
+                                  here and can be retrieved by calling\r
+                                  khui_action_get_data(). */\r
+    void *    reserved1;      /*!< Reserved. */\r
+    void *    reserved2;      /*!< Reserved. */\r
+    void *    reserved3;      /*!< Reserved. */\r
+\r
+    /* For all actions */\r
+    int state;                /*!< current state. combination of\r
+                                  KHUI_ACTIONSTATE_* */\r
+} khui_action;\r
+\r
+/*@}*/\r
+\r
 #endif\r
index e8f6cd64a7541300517b4bf431300c429b3be2e4..b36ea098ce98fa530aed88ccc7810ecb50ca53d2 100644 (file)
 /*! \defgroup khui_actions Actions\r
   @{*/\r
 \r
-/*! \brief An action */\r
-typedef struct tag_khui_action {\r
-    khm_int32 cmd;            /*!< action identifier */\r
-    khm_int32 type;           /*!< combination of KHUI_ACTIONTYPE_* */\r
-    wchar_t * name;           /*!< name for named actions.  NULL if\r
-                                not named. */\r
-\r
-    /* The following fields are only for use by NetIDMgr */\r
-    khm_int16 ib_normal;      /*!< (internal) normal bitmap (index) (toolbar sized icon) */\r
-    khm_int16 ib_hot;         /*!< (internal) hot bitmap (index) (toolbar sized icon) */\r
-    khm_int16 ib_disabled;    /*!< (internal) disabled bitmap (index) (toolbar sized icon) */\r
-\r
-    khm_int16 ib_icon;        /*!< (internal) index of small (16x16) icon (for menu) (small icon) */\r
-    khm_int16 ib_icon_dis;    /*!< (internal) index of disabled (greyed) icon (small icon) */\r
-\r
-    khm_int16 is_caption;     /*!< (internal) index of string resource for caption */\r
-    khm_int16 is_tooltip;     /*!< (internal) same for description / tooltip */\r
-    khm_int16 ih_topic;       /*!< (internal) help topic */\r
-\r
-    /* The following fields are specified for custom actions */\r
-    wchar_t * caption;        /*!< Caption (localized) (limited by\r
-                                  KHUI_MAXCCH_SHORT_DESC).  The\r
-                                  caption is used for representing the\r
-                                  action in menus and toolbars. */\r
-    wchar_t * tooltip;        /*!< Tooltip (localized) (limited by\r
-                                  KHUI_MAXCCH_SHORT_DESC).  If this is\r
-                                  specified, whenever the user hovers\r
-                                  over the menu item or toolbar button\r
-                                  representing the action, the tooltip\r
-                                  will be displayed either on a\r
-                                  tooltip window or in the status\r
-                                  bar. */\r
-    khm_handle listener;      /*!< Listener of this action.  Should be\r
-                                  a handle to a message\r
-                                  subscription. When the action is\r
-                                  invoked, a message of type\r
-                                  ::KMSG_ACT and subtype\r
-                                  ::KMSG_ACT_ACTIVATE will be posted\r
-                                  to this subscriber. The \a uparam\r
-                                  parameter of the message will have\r
-                                  the identifier of the action. */\r
-    void *    data;           /*!< User data for custom action.  This\r
-                                  field is not used by the UI library.\r
-                                  It is reserved for plugins to store\r
-                                  data that is specific for this\r
-                                  action.  The data that's passed in\r
-                                  in the \a userdata parameter to\r
-                                  khui_action_create() will be stored\r
-                                  here and can be retrieved by calling\r
-                                  khui_action_get_data(). */\r
-    void *    reserved1;      /*!< Reserved. */\r
-    void *    reserved2;      /*!< Reserved. */\r
-    void *    reserved3;      /*!< Reserved. */\r
-\r
-    /* For all actions */\r
-    int state;                /*!< current state. combination of\r
-                                  KHUI_ACTIONSTATE_* */\r
-} khui_action;\r
+struct tag_khui_action;\r
+typedef struct tag_khui_action khui_action;\r
 \r
 /*! \brief Unknown action type\r
 \r
@@ -343,6 +287,40 @@ extern int khui_n_all_menus;
 \r
 /* functions */\r
 \r
+/*! \brief Refresh the global action table\r
+\r
+    Changes to system menus and toolbars may not be immediately\r
+    reflected in the user interface.  Calling this function forces the\r
+    UI to reparse the action tables and menus and refresh the\r
+    application menu bar and toolbars.\r
+\r
+ */\r
+KHMEXP void KHMAPI\r
+khui_refresh_actions(void);\r
+\r
+/*! \brief Lock the action and menu tables\r
+\r
+    This function, along with khui_action_unlock() is used to prevent\r
+    changes from being made to shared menus and actions while they are\r
+    being updated.  In particular, changes to shared menus usually\r
+    need to be done in a batch and may suffer corruption of other\r
+    threads access or modify the menu while one thread is updating it.\r
+    Operations on shared menus should always be done with the actions\r
+    locked.\r
+*/\r
+KHMEXP void KHMAPI\r
+khui_action_lock(void);\r
+\r
+/*! \brief Unlock the action and menu tables\r
+\r
+    Unlocks the action and menu tables after a call to\r
+    khui_action_lock().\r
+\r
+    \see khui_action_lock()\r
+ */\r
+KHMEXP void KHMAPI\r
+khui_action_unlock(void);\r
+\r
 /*! \brief Create a new menu\r
 \r
     Creates a new menu.  The returned data structure must be freed by\r
@@ -887,9 +865,28 @@ KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref);
 \r
 /*! \brief Create a new action\r
 \r
+    Creates a new custom action.  The created custom action can be\r
+    added to menus, toolbars and can be triggered by\r
+    khui_action_trigger().\r
+\r
+    When the action is triggered as a result of the user selecting a\r
+    menu item, a toolbar item or as a result of calling\r
+    khui_action_trigger(), the subscription identified by \a hsub will\r
+    received a message of type ::KMSG_ACT, subtype\r
+    ::KMSG_ACT_ACTIVATE.  The \a uparam for the message will be the\r
+    action identifier that was returned by khui_action_create().  The\r
+    \a vparam of the message will currently be set to \a NULL.\r
+\r
+    Actions can optionally be named.  The name is not actively used by\r
+    the Network Identity Manager framework, but can be used to label\r
+    actions so that they can be looked up later using\r
+    khui_find_named_action().\r
+\r
     \param[in] name Name for a named action.  The name must be unique\r
-        among all registered actions. (limited by KHUI_MAXCCH_NAME)\r
+        among all registered actions. (limited by KHUI_MAXCCH_NAME).\r
         (Optional. Set to NULL if the action is not a named action.)\r
+        See \a note below for additional restrictions on the name of\r
+        the action.\r
 \r
     \param[in] caption The localized caption for the action.  This\r
         will be shown in menus, toolbars and buttons when the action\r
@@ -900,11 +897,7 @@ KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref);
         by KHUI_MAXCCH_SHORT_DESC) (Optional, set to NULL if there is\r
         no tooltip associated with the action)\r
 \r
-    \param[in] hsub The subscription that is notified when the action\r
-        is triggered. (Optional) The subscription can be created with\r
-        kmq_create_subscription().  The handle will be released when\r
-        it is no longer needed.  Hence, the caller should not release\r
-        it.\r
+    \param[in] userdata A custom value.\r
 \r
     \param[in] type The type of the action.  Currently it should be\r
         set to either ::KHUI_ACTIONTYPE_TRIGGER or\r
@@ -912,7 +905,11 @@ KHMEXP size_t KHMAPI khui_action_list_length(khui_action_ref * ref);
         initial state will be unchecked.  Use khui_check_action()\r
         function to change the checked state of the action.\r
 \r
-    \param[in] userdata A custom value.\r
+    \param[in] hsub The subscription that is notified when the action\r
+        is triggered. (Optional) The subscription must be created with\r
+        kmq_create_subscription().  The handle will be released when\r
+        it is no longer needed.  Hence, the caller should not release\r
+        it.\r
 \r
     \return The identifier of the new action or zero if the action\r
         could not be created.\r
index f10ffa198bdf70a9daa5a8564ece43f4eda9d420..2bbc7d0da1f8206a41877c79684378ac2b15faf9 100644 (file)
 /*!\defgroup khui_alert Alerter and Error Reporting\r
 @{*/\r
 \r
-#define KHUI_MAX_ALERT_COMMANDS  4\r
-\r
-/*! \brief An alert\r
-\r
-    Describes an alert message that will be shown to the user in a\r
-    variety of ways depending on the state of the NetIDMgr\r
-    application.\r
- */\r
-typedef struct tag_khui_alert {\r
-    khm_int32           magic;  /*!< Magic number. Always set to\r
-                                  KHUI_ALERT_MAGIC */\r
-\r
-    khm_int32           severity; /*!< The severity of the alert.  One\r
-                                    of KHERR_ERROR, KHERR_WARNING or\r
-                                    KHERR_INFO.  The default is\r
-                                    KHERR_INFO.  Do not set directly.\r
-                                    Use khui_alert_set_severity(). */\r
-\r
-    khm_int32           alert_commands[KHUI_MAX_ALERT_COMMANDS];\r
-                                /*!< The command buttons associated\r
-                                  with the alert.  Use\r
-                                  khui_alert_add_command() to add a\r
-                                  command.  The buttons will appear in\r
-                                  the order in which they were added.\r
-                                  The first button will be the\r
-                                  default.  Each command should be a\r
-                                  known action identifier. */\r
-    khm_int32           n_alert_commands;\r
-\r
-    wchar_t *           title;  /*!< The title of the alert.  Subject\r
-                                  to ::KHUI_MAXCCH_TITLE.  Use\r
-                                  khui_alert_set_title() to set.  Do\r
-                                  not modify directly. */\r
-\r
-    wchar_t *           message; /*!< The main message of the alert.\r
-                                   Subject to ::KHUI_MAXCCH_MESSAGE.\r
-                                   Use khui_alert_set_message() to\r
-                                   set.  Do not modify direcly. */\r
-\r
-    wchar_t *           suggestion; /*!< A suggestion.  Appears below\r
-                                      the message text. Use\r
-                                      khui_alert_set_suggestion() to\r
-                                      set.  Do not modify directly. */\r
-\r
-#ifdef _WIN32\r
-    POINT               target;\r
-#endif\r
-\r
-    khm_int32           flags;  /*!< combination of\r
-                                 ::khui_alert_flags.  Do not modify\r
-                                 directly. */\r
-\r
-    kherr_context *     err_context; \r
-                                /*!< If non-NULL at the time the alert\r
-                                  window is shown, this indicates that\r
-                                  the alert window should provide an\r
-                                  error viewer for the given error\r
-                                  context. */\r
+struct tag_khui_alert;\r
+typedef struct tag_khui_alert khui_alert;\r
 \r
-    kherr_event *       err_event; \r
-                                /*!< If non-NULL at the time the alert\r
-                                  window is shown, this indicates that\r
-                                  the alert window should provide an\r
-                                  error viewer for the given error\r
-                                  event.  If an \a err_context is also\r
-                                  given, the error viewer for the\r
-                                  context will be below this error. */\r
-\r
-    khm_int32           response; \r
-                                /*!< Once the alert is displayed to\r
-                                  the user, when the user clicks one\r
-                                  of the command buttons, the command\r
-                                  ID will be assigned here. */\r
-\r
-    int                 refcount; /* internal */\r
-\r
-    LDCL(struct tag_khui_alert); /* internal */\r
-} khui_alert;\r
-\r
-#define KHUI_ALERT_MAGIC 0x48c39ce9\r
+#define KHUI_MAX_ALERT_COMMANDS  4\r
 \r
 /*! \brief Maximum number of characters in title including terminating NULL\r
  */\r
@@ -186,6 +110,27 @@ enum khui_alert_flags {
     /*!< Bit mask of flags that can be set by khui_alert_set_flags() */\r
 };\r
 \r
+/*! \brief Alert types\r
+\r
+    These types can be set with khui_alert_set_type() to indicate\r
+    which type of alert this is.  The types defined here are\r
+    identified by the Network Identity Manager and will receive\r
+    special handling whereever appropriate.\r
+\r
+    The type is a hint to the application and will not guarantee a\r
+    particular behavior.\r
+ */\r
+typedef enum tag_khui_alert_types {\r
+    KHUI_ALERTTYPE_NONE = 0,    /*!< No specific alert type */\r
+    KHUI_ALERTTYPE_PLUGIN,      /*!< Plug-in or module load related\r
+                                  alert */\r
+    KHUI_ALERTTYPE_EXPIRE,      /*!< Credential or identity expiration\r
+                                  warning */\r
+    KHUI_ALERTTYPE_RENEWFAIL,   /*!< Failed to renew credentials */\r
+    KHUI_ALERTTYPE_ACQUIREFAIL, /*!< Failed to acquire credentials */\r
+    KHUI_ALERTTYPE_CHPW,        /*!< Failed to change password */\r
+} khui_alert_type;\r
+\r
 /*! \brief Create an empty alert object\r
 \r
     The returned result is a held pointer to a ::khui_alert object.\r
@@ -267,12 +212,39 @@ khui_alert_clear_commands(khui_alert * alert);
 \r
 /*! \brief Add a command to an alert object\r
 \r
-    The command ID should be a valid registered command.\r
+    The command ID should be a valid registered action.\r
  */\r
 KHMEXP khm_int32 KHMAPI \r
 khui_alert_add_command(khui_alert * alert, \r
                        khm_int32 command_id);\r
 \r
+/*! \brief Set the type of alert\r
+ */\r
+KHMEXP khm_int32 KHMAPI\r
+khui_alert_set_type(khui_alert * alert,\r
+                    khui_alert_type type);\r
+\r
+/*! \brief Set the action context for the alert */\r
+KHMEXP khm_int32 KHMAPI\r
+khui_alert_set_ctx(khui_alert * alert,\r
+                   khui_scope scope,\r
+                   khm_handle identity,\r
+                   khm_int32 cred_type,\r
+                   khm_handle cred);\r
+\r
+/*! \brief Get the response code from an alert\r
+\r
+    Once an alert has been displayed to the user, the user may choose\r
+    a command from the list of commands provided in the alert (see\r
+    khui_alert_add_command() ).  This function can retrieve the\r
+    selected command from the alert.\r
+\r
+    \return The selected command or \a 0 if no commands were selected.\r
+ */\r
+KHMEXP khm_int32 KHMAPI\r
+khui_alert_get_response(khui_alert * alert);\r
+\r
+\r
 /*! \brief Display an alert\r
 \r
     The alert must have a valid \a severity, \a title and a \a message\r
@@ -317,7 +289,6 @@ khui_alert_show(khui_alert * alert);
     This function always opens an alert window (never shows a\r
     balloon).\r
 \r
-    \note Should only be called from the UI thread.\r
  */\r
 KHMEXP khm_int32 KHMAPI\r
 khui_alert_show_modal(khui_alert * alert);\r
@@ -377,7 +348,7 @@ khui_alert_release(khui_alert * alert);
         disallows access to all other alerts as well.\r
 \r
     \note Calling khui_alert_lock() is only necessary if you are\r
-        modifying the ::khui_alert structure directly.  Calling any of\r
+        accessing the ::khui_alert structure directly.  Calling any of\r
         the khui_alert_* functions to modify the alert does not\r
         require obtaining a lock, as they perform synchronization\r
         internally.\r
index 1785d5972cafa49363543665a74c11f99fb215e9..45a565cde15bf7dce580edc4d648d91e4f334113 100644 (file)
@@ -146,6 +146,10 @@ enum khui_wm_nc_notifications {
     WMNC_ADD_CONTROL_ROW,\r
     /*!< Add a row of controls to a new cred dialog.  This is an\r
       internal message. */\r
+\r
+    WMNC_UPDATE_LAYOUT,\r
+    /*!< Update the layout of a dialog or window.  This is an internal\r
+      message. */\r
 };\r
 \r
 /*! \brief Plugins can use WMNC_NOTIFY message codes from here on up\r
@@ -551,13 +555,6 @@ typedef struct tag_khui_new_creds_by_type {
 /*! \brief Height of a new creds dialog panel in dialog units*/\r
 #define NCDLG_HEIGHT    166\r
 \r
-/*! \brief Width of the button bar in dialog units */\r
-#define NCDLG_BBAR_WIDTH 60\r
-/*! \brief Height of a tab button in dialog units */\r
-#define NCDLG_TAB_HEIGHT 15\r
-/*! \brief Width of a tab button in dialog units */\r
-#define NCDLG_TAB_WIDTH 60\r
-\r
 /*! \brief A custom prompt */\r
 typedef struct tag_khui_new_creds_prompt {\r
     khm_size    index;          /*!< Set to the zero based index\r
@@ -944,7 +941,7 @@ khui_cw_set_response(khui_new_creds * c,
 \r
 /*! \brief Check whether a specified credential type panel succeeded\r
 \r
-    This is called during the processing of KMSG_CRED_DIALOG_PROCESS\r
+    This is called during the processing of ::KMSG_CRED_DIALOG_PROCESS\r
     to determine whether a specified credential type succeeded in\r
     obtaining credentials.  The credential type that is being queried\r
     should have also been listed as a dependency when adding the\r
index 845f781d29b0eac494186ad10c2a1929e307bb40..9b3855614c7a945389664759e65ed1de254d191e 100644 (file)
@@ -37,7 +37,6 @@
 \r
 #include<khaction.h>\r
 #include<khactiondef.h>\r
-#include<khrescache.h>\r
 #include<khhtlink.h>\r
 #include<khnewcred.h>\r
 #include<khprops.h>\r
 #include<khconfigui.h>\r
 #include<khtracker.h>\r
 \r
+#ifdef NOEXPORT\r
+#include<khrescache.h>\r
+#endif\r
+\r
 #include<khremote.h>\r
 \r
 /*! \internal */\r
@@ -115,9 +118,23 @@ typedef khm_int32
     any windows it wishes to create and interact with the user\r
     directly.\r
 \r
-    Note that when the plug-in creates any windows, it should specify\r
-    the window handle provided via the \a hwnd_main_wnd parameter as\r
-    the parent window.\r
+    The callback function would be called synchronously.\r
+    khui_request_UI_callback() will not return until the user\r
+    interface processes the request and calls the callback function.\r
+    The return code of khui_request_UI_callback() will be the return\r
+    code of the callback.\r
+\r
+    \param[in] cb The callback function which will be called from the\r
+        user interface thread.\r
+\r
+    \param[in] rock An arbitrary parameter which will be passed into\r
+        the callback function.\r
+\r
+    \return The return value of \a cb.\r
+\r
+    \note When the plug-in creates any windows, it should specify the\r
+        window handle provided via the \a hwnd_main_wnd parameter as\r
+        the parent window.\r
 \r
     \see ::khm_ui_callback\r
  */\r
index 6d52591423b1cbc66ba57734b755f827544ab355..b84608a8e45b2a12d4075cd6bf363fee48dc9df0 100644 (file)
@@ -24,6 +24,8 @@
 \r
 /* $Id$ */\r
 \r
+#define NOEXPORT\r
+\r
 #include<khuidefs.h>\r
 #include<utils.h>\r
 \r
index ac5e136265973f16906066f46a814e4b055974d5..11da4e4bfa8368af47ec9f87b5f334d821063f24 100644 (file)
@@ -53,6 +53,7 @@ khui_request_UI_callback(khm_ui_callback cb, void * rock) {
                 MAKEWPARAM(KHUI_ACTION_UICB, 0),\r
                 (LPARAM) &cbdata);\r
 \r
-    return KHM_ERROR_SUCCESS;\r
+    return cbdata.rv;\r
 }\r
 \r
+\r
index 1a4233d518a48246c5fc63c785dec8e2b12d089d..ddf7b1bf8f02826b26125d77a31fc5a7161f9f98 100644 (file)
@@ -63,10 +63,13 @@ KHMEXP void KHMAPI hash_del_hashtable(hashtable * h) {
         }\r
     }\r
 \r
+    if (h->bins)\r
+        PFREE(h->bins);\r
+\r
     PFREE(h);\r
 }\r
 \r
-KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data) {\r
+KHMEXP void KHMAPI hash_add(hashtable * h, const void * key, void * data) {\r
     int hv;\r
     hash_bin * b;\r
 \r
@@ -97,7 +100,7 @@ KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data) {
     }\r
 }\r
 \r
-KHMEXP void KHMAPI hash_del(hashtable * h, void * key) {\r
+KHMEXP void KHMAPI hash_del(hashtable * h, const void * key) {\r
     hash_bin * b;\r
     int hv;\r
 \r
@@ -117,7 +120,7 @@ KHMEXP void KHMAPI hash_del(hashtable * h, void * key) {
     }\r
 }\r
 \r
-KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key) {\r
+KHMEXP void * KHMAPI hash_lookup(hashtable * h, const void * key) {\r
     hash_bin * b;\r
     int hv;\r
 \r
@@ -135,7 +138,7 @@ KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key) {
     return NULL;\r
 }\r
 \r
-KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, void * key) {\r
+KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, const void * key) {\r
     hash_bin * b;\r
     int hv;\r
 \r
index fc7d829f2fcdf38cbcec427bcc226106690bd688..1a3fb4b86ecfb2d589fec37a449a0a3909cb3461 100644 (file)
@@ -83,7 +83,7 @@ typedef void (*del_ref_function_t)(const void *key, void *data);
 \r
 typedef struct tag_hash_bin {\r
     void * data;\r
-    void * key;\r
+    const void * key;\r
 \r
     LDCL(struct tag_hash_bin);\r
 } hash_bin;\r
@@ -147,7 +147,7 @@ KHMEXP void KHMAPI hash_del_hashtable(hashtable * h);
     \note Not thread-safe.  Applications must serialize calls that\r
         reference the same hashtable.\r
  */\r
-KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data);\r
+KHMEXP void KHMAPI hash_add(hashtable * h, const void * key, void * data);\r
 \r
 /*! \brief Delete an object from a hashtable\r
 \r
@@ -161,7 +161,7 @@ KHMEXP void KHMAPI hash_add(hashtable * h, void * key, void * data);
     \note Not thread-safe.  Applications must serialize calls that\r
         reference the same hashtable.\r
  */\r
-KHMEXP void KHMAPI hash_del(hashtable * h, void * key);\r
+KHMEXP void KHMAPI hash_del(hashtable * h, const void * key);\r
 \r
 /*! \brief Resolve and association\r
 \r
@@ -175,7 +175,7 @@ KHMEXP void KHMAPI hash_del(hashtable * h, void * key);
     \note Not thread-safe.  Applications must serialize calls that\r
         reference the same hashtable.\r
  */\r
-KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key);\r
+KHMEXP void * KHMAPI hash_lookup(hashtable * h, const void * key);\r
 \r
 /*! \brief Check for the presence of an association\r
 \r
@@ -190,7 +190,7 @@ KHMEXP void * KHMAPI hash_lookup(hashtable * h, void * key);
     \note Not thead-safe.  Application must serialize calls that\r
         reference the same hashtable.\r
  */\r
-KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, void * key);\r
+KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, const void * key);\r
 \r
 /*! \brief Compute a hashvalue for a unicode string\r
 \r
@@ -202,6 +202,10 @@ KHMEXP khm_boolean KHMAPI hash_exist(hashtable * h, void * key);
 \r
     \param[in] str A pointer to a NULL terminated wchar_t string cast\r
         as (void *).\r
+\r
+    \note This function does not check the length of the string \a\r
+        str.  If the string is not \a NULL terminated, the behavior is\r
+        undefined.\r
  */\r
 KHMEXP khm_int32 hash_string(const void *str);\r
 \r
@@ -214,6 +218,10 @@ KHMEXP khm_int32 hash_string(const void *str);
         as (void *).\r
     \param[in] vs2 A pointer to a NULL terminated wchar_t string cast\r
         as (void *).\r
+\r
+    \note This function does not check the length of the strings \a\r
+        vs1 and \a vs2.  If the strings are not NULL terminated, the\r
+        behavior is undefined.\r
  */\r
 KHMEXP khm_int32 hash_string_comp(const void *vs1, const void *vs2);\r
 \r
index 4d7e8fa7c476f1e235e773dda639165f6d9daa58..cc27d93bd0f2429e29176fa37d59f7228b5a3da9 100644 (file)
@@ -37,7 +37,7 @@
 #define HASHPTR(p) (((size_t) (p)) % HASHSIZE)\r
 \r
 typedef struct tag_allocation {\r
-    char   file[8];\r
+    const char * file;\r
     int    line;\r
     size_t size;\r
     void * ptr;\r
@@ -54,10 +54,49 @@ static allocation * next_alloc = NULL;
 static size_t       idx_next_alloc = 0;\r
 static allocation * free_alloc = NULL;\r
 \r
+typedef struct tag_thread_info {\r
+#ifdef _WIN32\r
+    DWORD thread;\r
+#else\r
+#error Unsupported platform\r
+#endif\r
+    wchar_t name[128];\r
+    wchar_t creator[128];\r
+\r
+    const char * file;\r
+    int line;\r
+\r
+    LDCL(struct tag_thread_info);\r
+} thread_info;\r
+\r
+static thread_info * threads = NULL;\r
+\r
+static hashtable fn_hash;\r
+\r
 static CRITICAL_SECTION cs_alloc;\r
 static LONG ctr = 0;\r
 static int  perf_ready = 0;\r
 \r
+static DWORD init_thread = 0;\r
+\r
+static khm_int32 hash_stringA(const void * vs) {\r
+    /* DJB algorithm */\r
+\r
+    khm_int32 hv = 13331;\r
+    char * c;\r
+\r
+    for (c = (char *) vs; *c; c++) {\r
+        hv = ((hv << 5) + hv) + (khm_int32) *c;\r
+    }\r
+\r
+    return (hv & KHM_INT32_MAX);\r
+}\r
+\r
+static khm_int32 hash_string_compA(const void * vs1,\r
+                                   const void * vs2) {\r
+    return strcmp((const char *) vs1, (const char *) vs2);\r
+}\r
+\r
 static void perf_once(void) {\r
     if (InterlockedIncrement(&ctr) == 1) {\r
         InitializeCriticalSection(&cs_alloc);\r
@@ -68,9 +107,18 @@ static void perf_once(void) {
         idx_next_alloc = 0;\r
         free_alloc = NULL;\r
 \r
+        ZeroMemory(&fn_hash, sizeof(fn_hash));\r
+        fn_hash.n = 13;\r
+        fn_hash.hash = hash_stringA;\r
+        fn_hash.comp = hash_string_compA;\r
+        fn_hash.bins = calloc(sizeof(hash_bin *), fn_hash.n);\r
+\r
         perf_ready = 1;\r
     } else {\r
-        while(!perf_ready) {\r
+        DWORD this_thread = GetCurrentThreadId();\r
+\r
+        while(!perf_ready &&\r
+              init_thread != this_thread) {\r
             Sleep(0);           /* relinquish control to the thread\r
                                    that is initializing the alloc\r
                                    data. */\r
@@ -99,7 +147,7 @@ static allocation * get_allocation(void) {
 #define MAXCB_STR 32768\r
 \r
 KHMEXP wchar_t *\r
-perf_wcsdup(char * file, int line, const wchar_t * str) {\r
+perf_wcsdup(const char * file, int line, const wchar_t * str) {\r
     size_t cb;\r
     wchar_t * dest;\r
 \r
@@ -114,7 +162,7 @@ perf_wcsdup(char * file, int line, const wchar_t * str) {
 }\r
 \r
 KHMEXP char *\r
-perf_strdup(char * file, int line, const char * str) {\r
+perf_strdup(const char * file, int line, const char * str) {\r
     size_t cb;\r
     char * dest;\r
 \r
@@ -129,7 +177,7 @@ perf_strdup(char * file, int line, const char * str) {
 }\r
 \r
 KHMEXP void *\r
-perf_calloc(char * file, int line, size_t num, size_t size) {\r
+perf_calloc(const char * file, int line, size_t num, size_t size) {\r
     void * ptr;\r
     size_t tsize;\r
 \r
@@ -145,10 +193,11 @@ perf_calloc(char * file, int line, size_t num, size_t size) {
 }\r
 \r
 KHMEXP void * \r
-perf_malloc(char * file, int line, size_t s) {\r
+perf_malloc(const char * file, int line, size_t s) {\r
     allocation * a;\r
     void * ptr;\r
     size_t h;\r
+    char * fn_copy = NULL;\r
 \r
     perf_once();\r
 \r
@@ -164,7 +213,33 @@ perf_malloc(char * file, int line, size_t s) {
     if (file[0] == '.' && file[1] == '\\')\r
         file += 2;\r
 \r
-    StringCbCopyA(a->file, sizeof(a->file), file);\r
+    fn_copy = hash_lookup(&fn_hash, file);\r
+    if (fn_copy == NULL) {\r
+\r
+        size_t cblen = 0;\r
+        if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char),\r
+                                   &cblen)))\r
+            fn_copy = NULL;\r
+        else {\r
+            fn_copy = malloc(cblen + sizeof(char));\r
+            if (fn_copy) {\r
+                hash_bin * b;\r
+                int hv;\r
+\r
+                StringCbCopyA(fn_copy, cblen + sizeof(char), file);\r
+\r
+                hv = fn_hash.hash(fn_copy) % fn_hash.n;\r
+\r
+                b = malloc(sizeof(*b));\r
+                b->data = fn_copy;\r
+                b->key = fn_copy;\r
+                LINIT(b);\r
+                LPUSH(&fn_hash.bins[hv], b);\r
+            }\r
+        }\r
+    }\r
+\r
+    a->file = fn_copy;\r
     a->line = line;\r
     a->size = s;\r
     a->ptr = ptr;\r
@@ -181,7 +256,7 @@ perf_malloc(char * file, int line, size_t s) {
 }\r
 \r
 KHMEXP void *\r
-perf_realloc(char * file, int line, void * data, size_t s) {\r
+perf_realloc(const char * file, int line, void * data, size_t s) {\r
     void * n_data;\r
     allocation * a;\r
     size_t h;\r
@@ -237,41 +312,100 @@ perf_free  (void * b) {
     LeaveCriticalSection(&cs_alloc);\r
 }\r
 \r
-KHMEXP void\r
-perf_dump(char * file) {\r
-    FILE * f;\r
+KHMEXP void KHMAPI\r
+perf_dump(FILE * f) {\r
     size_t i;\r
     allocation * a;\r
     size_t total = 0;\r
+    thread_info * t;\r
 \r
     perf_once();\r
 \r
     EnterCriticalSection(&cs_alloc);\r
-#if _MSC_VER >= 1400\r
-    if (fopen_s(&f, file, "w"))\r
-        return;\r
-#else\r
-    f = fopen(file, "w");\r
-    if (!f)\r
-        return;\r
-#endif\r
 \r
-    fprintf(f, "Leaked allocations list ....\n");\r
-    fprintf(f, "File\tLine\tThread\tSize\n");\r
+    fprintf(f, "p00\t*** Threads ***\n");\r
+    fprintf(f, "p00\tFile\tLine\tThread\tName\tCreated by\n");\r
+\r
+    for (t = threads; t; t = LNEXT(t)) {\r
+        fprintf(f, "p01\t%s\t%6d\t%6d\t%S\t%S\n",\r
+                t->file, t->line, t->thread,\r
+                t->name, t->creator);\r
+    }\r
+\r
+    fprintf(f, "p02\t--- End Threads ---\n");\r
+\r
+    fprintf(f, "p10\t*** Leaked allocations list ***\n");\r
+    fprintf(f, "p11\tFile\tLine\tThread\tSize\tAddress\n");\r
 \r
     for (i=0; i < HASHSIZE; i++) {\r
         for (a = ht[i]; a; a = LNEXT(a)) {\r
-            fprintf(f, "%s\t%6d\t%6d\t%6d\n", a->file, a->line,\r
-                   a->thread, a->size);\r
+            fprintf(f, "p12\t%s\t%6d\t%6d\t%6d\t0x%p\n", a->file, a->line,\r
+                   a->thread, a->size, a->ptr);\r
             total += a->size;\r
         }\r
     }\r
 \r
-    fprintf(f, "----------------------------------------\n");\r
-    fprintf(f, "Total\t\t%d\n", total);\r
-    fprintf(f, "----------------- End ------------------\n");\r
+    fprintf(f, "p20\t----------------------------------------\n");\r
+    fprintf(f, "p21\tTotal\t\t%d\n", total);\r
+    fprintf(f, "p22\t----------------- End ------------------\n");\r
+\r
+    LeaveCriticalSection(&cs_alloc);\r
+}\r
+\r
+KHMEXP void\r
+perf_set_thread_desc(const char * file, int line,\r
+                     const wchar_t * name, const wchar_t * creator) {\r
+    thread_info * t;\r
+    char * fn_copy;\r
+\r
+    perf_once();\r
+\r
+    t = malloc(sizeof(*t));\r
+    ZeroMemory(t, sizeof(*t));\r
+\r
+#ifdef _WIN32\r
+    t->thread = GetCurrentThreadId();\r
+#else\r
+#error Unsupported platform\r
+#endif\r
+\r
+    StringCbCopy(t->name, sizeof(t->name), name);\r
+    if (creator)\r
+        StringCbCopy(t->creator, sizeof(t->creator), creator);\r
+\r
+    if (file[0] == '.' && file[1] == '\\')\r
+        file += 2;\r
+\r
+    EnterCriticalSection(&cs_alloc);\r
+\r
+    fn_copy = hash_lookup(&fn_hash, file);\r
+    if (fn_copy == NULL) {\r
+        size_t cblen = 0;\r
+        if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char),\r
+                                   &cblen)))\r
+            fn_copy = NULL;\r
+        else {\r
+            fn_copy = malloc(cblen + sizeof(char));\r
+            if (fn_copy) {\r
+                hash_bin * b;\r
+                int hv;\r
+\r
+                StringCbCopyA(fn_copy, cblen + sizeof(char), file);\r
+\r
+                hv = fn_hash.hash(fn_copy) % fn_hash.n;\r
+\r
+                b = malloc(sizeof(*b));\r
+                b->data = fn_copy;\r
+                b->key = fn_copy;\r
+                LINIT(b);\r
+                LPUSH(&fn_hash.bins[hv], b);\r
+            }\r
+        }\r
+    }\r
 \r
-    fclose(f);\r
+    t->file = fn_copy;\r
+    t->line = line;\r
 \r
+    LPUSH(&threads, t);\r
     LeaveCriticalSection(&cs_alloc);\r
 }\r
index d8d5951b3671bf49252c1f01f3abaff0eab3d2a6..a4a0f2750f361d85357817d17d7cc1c5bf4849d7 100644 (file)
@@ -37,6 +37,7 @@
 #define PDUMP(f)   perf_dump(f)\r
 #define PWCSDUP(s) perf_wcsdup(__FILE__,__LINE__,s)\r
 #define PSTRDUP(s) perf_strdup(__FILE__,__LINE__,s)\r
+#define PDESCTHREAD(n,c) perf_set_thread_desc(__FILE__,__LINE__,n,c);\r
 #else\r
 #define PMALLOC(s) malloc(s)\r
 #define PCALLOC(n,s) calloc(n,s)\r
 #define PDUMP(f)   ((void) 0)\r
 #define PWCSDUP(s) wcsdup(s)\r
 #define PSTRDUP(s) strdup(s)\r
+#define PDESCTHREAD(n,c)\r
 #endif\r
 \r
 KHMEXP void *\r
-perf_malloc(char * file, int line, size_t s);\r
+perf_malloc(const char * file, int line, size_t s);\r
 \r
 KHMEXP void *\r
-perf_realloc(char * file, int line, void * data, size_t s);\r
+perf_realloc(const char * file, int line, void * data, size_t s);\r
 \r
 KHMEXP void\r
 perf_free  (void * b);\r
 \r
-KHMEXP void\r
-perf_dump  (char * filename);\r
-\r
 KHMEXP wchar_t *\r
-perf_wcsdup(char * file, int line, const wchar_t * str);\r
+perf_wcsdup(const char * file, int line, const wchar_t * str);\r
 \r
 KHMEXP char *\r
-perf_strdup(char * file, int line, const char * str);\r
+perf_strdup(const char * file, int line, const char * str);\r
 \r
 KHMEXP void *\r
-perf_calloc(char * file, int line, size_t num, size_t size);\r
+perf_calloc(const char * file, int line, size_t num, size_t size);\r
+\r
+KHMEXP void\r
+perf_set_thread_desc(const char * file, int line,\r
+                     const wchar_t * name, const wchar_t * creator);\r
 \r
 #endif\r