MSI: add support for the MSI MaskEdit control

Mike McCormack mike at codeweavers.com
Thu Jun 16 11:52:48 CDT 2005


This should make the Product ID key edit boxes appear and work properly 
in Microsoft Office 2003.

Mike


ChangeLog:
* add support for the MSI MaskEdit control
-------------- next part --------------
Index: dlls/msi/dialog.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/dialog.c,v
retrieving revision 1.23
diff -u -p -r1.23 dialog.c
--- dlls/msi/dialog.c	7 Jun 2005 20:30:02 -0000	1.23
+++ dlls/msi/dialog.c	16 Jun 2005 16:53:29 -0000
@@ -119,6 +119,7 @@ static void msi_dialog_checkbox_sync_sta
 static UINT msi_dialog_button_handler( msi_dialog *, msi_control *, WPARAM );
 static UINT msi_dialog_edit_handler( msi_dialog *, msi_control *, WPARAM );
 static UINT msi_dialog_radiogroup_handler( msi_dialog *, msi_control *, WPARAM param );
+static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog );
 static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
 
 
@@ -594,7 +595,6 @@ static UINT msi_dialog_combo_control( ms
 
 static UINT msi_dialog_edit_control( msi_dialog *dialog, MSIRECORD *rec )
 {
-    const static WCHAR szEdit[] = { 'E','D','I','T',0 };
     msi_control *control;
     LPCWSTR prop;
     LPWSTR val;
@@ -610,6 +610,273 @@ static UINT msi_dialog_edit_control( msi
     return ERROR_SUCCESS;
 }
 
+/******************** Masked Edit ********************************************/
+
+#define MASK_MAX_GROUPS 10
+
+struct msi_mask_group
+{
+    UINT len;
+    UINT ofs;
+    WCHAR type;
+    HWND hwnd;
+};
+
+struct msi_maskedit_info
+{
+    msi_dialog *dialog;
+    WNDPROC oldproc;
+    HWND hwnd;
+    LPWSTR prop;
+    UINT num_chars;
+    UINT num_groups;
+    struct msi_mask_group group[MASK_MAX_GROUPS];
+};
+
+static void msi_mask_control_change( struct msi_maskedit_info *info )
+{
+    LPWSTR val;
+    UINT i, n, r;
+
+    val = HeapAlloc( GetProcessHeap(), 0, (info->num_chars+1)*sizeof(WCHAR) );
+    for( i=0, n=0; i<info->num_groups; i++ )
+    {
+        if( (info->group[i].len + n) > info->num_chars )
+        {
+            ERR("can't fit control %d text into template\n",i);
+            break;
+        }
+        r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 );
+        if( r != info->group[i].len )
+            break;
+        n += r;
+    }
+
+    TRACE("%d/%d controls were good\n", i, info->num_groups);
+
+    if( i == info->num_groups )
+    {
+        TRACE("Set property %s to %s\n",
+              debugstr_w(info->prop), debugstr_w(val) );
+        CharUpperBuffW( val, info->num_chars );
+        MSI_SetPropertyW( info->dialog->package, info->prop, val );
+        msi_dialog_evaluate_control_conditions( info->dialog );
+    }
+    HeapFree( GetProcessHeap(), 0, val );
+}
+
+static LRESULT WINAPI
+MSIMaskedEdit_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    struct msi_maskedit_info *info;
+    HRESULT r;
+
+    TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam);
+
+    info = GetPropW(hWnd, szButtonData);
+
+    r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
+
+    switch( msg )
+    {
+    case WM_COMMAND:
+        if (HIWORD(wParam) == EN_CHANGE)
+            msi_mask_control_change( info );
+        break;
+    case WM_NCDESTROY:
+        HeapFree( GetProcessHeap(), 0, info->prop );
+        HeapFree( GetProcessHeap(), 0, info );
+        RemovePropW( hWnd, szButtonData );
+        break;
+    }
+
+    return r;
+}
+
+/* fish the various bits of the property out and put them in the control */
+static void
+msi_maskedit_set_text( struct msi_maskedit_info *info, LPCWSTR text )
+{
+    LPCWSTR p;
+    UINT i;
+
+    p = text;
+    for( i = 0; i < info->num_groups; i++ )
+    {
+        if( info->group[i].len < lstrlenW( p ) )
+        {
+            LPWSTR chunk = strdupW( p );
+            chunk[ info->group[i].len ] = 0;
+            SetWindowTextW( info->group[i].hwnd, chunk );
+            HeapFree( GetProcessHeap(), 0, chunk );
+        }
+        else
+        {
+            SetWindowTextW( info->group[i].hwnd, p );
+            break;
+        }
+        p += info->group[i].len;
+    }
+}
+
+static struct msi_maskedit_info * msi_dialog_parse_groups( LPCWSTR mask )
+{
+    struct msi_maskedit_info * info = NULL;
+    int i = 0, n = 0, total = 0;
+    LPCWSTR p;
+
+    TRACE("masked control, template %s\n", debugstr_w(mask));
+
+    if( !mask )
+        return info;
+
+    p = strchrW(mask, '<');
+    if( !p )
+        return info;
+
+    info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *info );
+    if( !info )
+        return info;
+
+    p++;
+    for( i=0; i<MASK_MAX_GROUPS; i++ )
+    {
+        /* stop at the end of the string */
+        if( p[0] == 0 || p[0] == '>' )
+            break;
+
+        /* count the number of the same identifier */
+        for( n=0; p[n] == p[0]; n++ )
+            ;
+        info->group[i].ofs = total;
+        info->group[i].type = p[0];
+        if( p[n] == '=' )
+        {
+            n++;
+            total++; /* an extra not part of the group */
+        }
+        info->group[i].len = n;
+        total += n;
+        p += n;
+    }
+
+    TRACE("%d characters in %d groups\n", total, info->num_groups );
+    if( i == MASK_MAX_GROUPS )
+        ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask));
+
+    info->num_chars = total;
+    info->num_groups = i;
+
+    return info;
+}
+
+static void
+msi_maskedit_create_children( struct msi_maskedit_info *info )
+{
+    DWORD width, height, style, wx, ww;
+    LPCWSTR text, font = NULL;
+    RECT rect;
+    HWND hwnd;
+    UINT i;
+
+    style = WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP;
+
+    GetClientRect( info->hwnd, &rect );
+
+    width = rect.right - rect.left;
+    height = rect.bottom - rect.top;
+
+    if( text )
+        font = msi_dialog_get_style( &text );
+
+    for( i = 0; i < info->num_groups; i++ )
+    {
+        wx = (info->group[i].ofs * width) / info->num_chars;
+        ww = (info->group[i].len * width) / info->num_chars;
+
+        hwnd = CreateWindowW( szEdit, NULL, style, wx, 0, ww, height,
+                              info->hwnd, NULL, NULL, NULL );
+        if( !hwnd )
+        {
+            ERR("failed to create mask edit sub window\n");
+            break;
+        }
+
+        SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 );
+
+        msi_dialog_set_font( info->dialog, hwnd,
+                       font ? font : info->dialog->default_font );
+        info->group[i].hwnd = hwnd;
+    }
+}
+
+/* office 2003 uses "73931<````=````=````=````=`````>@@@@@" */
+static UINT msi_dialog_maskedit_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    const static WCHAR pidt[] = {'P','I','D','T','e','m','p','l','a','t','e',0};
+    LPWSTR mask = NULL, title = NULL, val = NULL;
+    struct msi_maskedit_info *info;
+    UINT ret = ERROR_SUCCESS;
+    msi_control *control;
+    LPCWSTR prop;
+
+    mask = load_dynamic_property( dialog->package, pidt, NULL );
+    if( !mask )
+    {
+        ERR("PIDTemplate is empty\n");
+        goto end;
+    }
+
+    info = msi_dialog_parse_groups( mask );
+    if( !info )
+    {
+        ERR("template %s is invalid\n", debugstr_w(mask));
+        goto end;
+    }
+
+    info->dialog = dialog;
+
+    control = msi_dialog_add_control( dialog, rec, szStatic, SS_OWNERDRAW|WS_GROUP );
+    if( !control )
+    {
+        ERR("Failed to create maskedit container\n");
+        ret = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+
+    info->hwnd = control->hwnd;
+
+    /* subclass the static control */
+    info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC,
+                                          (LONG_PTR)MSIMaskedEdit_WndProc );
+    SetPropW( control->hwnd, szButtonData, info );
+
+    prop = MSI_RecordGetString( rec, 9 );
+    if( prop )
+        info->prop = strdupW( prop );
+
+    msi_maskedit_create_children( info );
+
+    if( prop )
+    {
+        val = load_dynamic_property( dialog->package, prop, NULL );
+        if( val )
+        {
+            msi_maskedit_set_text( info, val );
+            HeapFree( GetProcessHeap(), 0, val );
+        }
+    }
+
+end:
+    if( ret != ERROR_SUCCESS )
+        HeapFree( GetProcessHeap(), 0, info );
+    HeapFree( GetProcessHeap(), 0, title );
+    HeapFree( GetProcessHeap(), 0, mask );
+    return ret;
+}
+
+/******************** Path Edit ********************************************/
+
 static UINT msi_dialog_pathedit_control( msi_dialog *dialog, MSIRECORD *rec )
 {
     FIXME("not implemented properly\n");
@@ -705,7 +972,7 @@ struct control_handler msi_dialog_handle
     { szScrollableText, msi_dialog_scrolltext_control },
     { szComboBox, msi_dialog_combo_control },
     { szEdit, msi_dialog_edit_control },
-    { szMaskedEdit, msi_dialog_edit_control },
+    { szMaskedEdit, msi_dialog_maskedit_control },
     { szPathEdit, msi_dialog_pathedit_control },
     { szRadioButtonGroup, msi_dialog_radiogroup_control },
 };


More information about the wine-patches mailing list