MSI: add more dialog controls, do something when they're clicked on

Mike McCormack mike at codeweavers.com
Tue Feb 1 23:09:02 CST 2005


This patch supercedes the previous one.

Mike


ChangeLog:
* add more dialog controls, do something when they're clicked on
-------------- next part --------------
Index: dlls/msi/dialog.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/dialog.c,v
retrieving revision 1.1
diff -u -p -r1.1 dialog.c
--- dlls/msi/dialog.c	28 Jan 2005 12:39:57 -0000	1.1
+++ dlls/msi/dialog.c	2 Feb 2005 05:04:24 -0000
@@ -27,95 +27,202 @@
 #include "wingdi.h"
 #include "msi.h"
 #include "msipriv.h"
+#include "msidefs.h"
 
 #include "wine/debug.h"
 #include "wine/unicode.h"
 
+#include "action.h"
+
 WINE_DEFAULT_DEBUG_CHANNEL(msi);
 
 
 const WCHAR szMsiDialogClass[] = {
     'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0
 };
+const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
+
+struct msi_control_tag;
+typedef struct msi_control_tag msi_control;
+typedef UINT (*msi_click_handler)( msi_dialog *, msi_control * );
+
+struct msi_control_tag
+{
+    struct msi_control_tag *next;
+    HWND hwnd;
+    msi_click_handler click_handler;
+    WCHAR name[1];
+};
+
+typedef struct msi_font_tag
+{
+    struct msi_font_tag *next;
+    HFONT hfont;
+    WCHAR name[1];
+} msi_font;
 
-struct tag_dialog_info
+struct msi_dialog_tag
 {
     MSIPACKAGE *package;
     msi_dialog_event_handler event_handler;
+    BOOL finished;
     INT scale;
+    DWORD attributes;
     HWND hwnd;
+    msi_font *font_list;
+    msi_control *control_list;
     WCHAR name[1];
 };
 
-typedef UINT (*msi_dialog_control_func)( dialog_info *dialog, MSIRECORD *rec );
+typedef UINT (*msi_dialog_control_func)( msi_dialog *dialog, MSIRECORD *rec );
 struct control_handler 
 {
     LPCWSTR control_type;
     msi_dialog_control_func func;
 };
 
-static UINT msi_dialog_text_control( dialog_info *dialog, MSIRECORD *rec )
+INT msi_dialog_scale_unit( msi_dialog *dialog, INT val )
 {
-    DWORD x, y, width, height;
-    LPCWSTR text;
-    const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
-    HWND hwnd;
+    return (dialog->scale * val + 5) / 10;
+}
 
-    TRACE("%p %p\n", dialog, rec);
+/*
+ * msi_dialog_get_style
+ *
+ * Extract the {\style} string from the front of the text to display and
+ *  update the pointer.
+ */
+static LPWSTR msi_dialog_get_style( LPCWSTR *text )
+{
+    LPWSTR ret = NULL;
+    LPCWSTR p = *text, q;
+    DWORD len;
+
+    if( *p++ != '{' )
+        return ret;
+    q = strchrW( p, '}' );
+    if( !q )
+        return ret;
+    *text = ++q;
+    if( *p++ != '\\' )
+        return ret;
+    len = q - p;
+    
+    ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+    if( !ret )
+        return ret;
+    strncpyW( ret, p, len );
+    ret[len-1] = 0;
+    return ret;
+}
+
+static UINT msi_dialog_add_font( MSIRECORD *rec, LPVOID param )
+{
+    msi_dialog *dialog = param;
+    msi_font *font;
+    LPCWSTR face, name;
+    LOGFONTW lf;
+    INT style;
+    HDC hdc;
 
-    x = MSI_RecordGetInteger( rec, 4 );
-    y = MSI_RecordGetInteger( rec, 5 );
-    width = MSI_RecordGetInteger( rec, 6 );
-    height = MSI_RecordGetInteger( rec, 7 );
-    text = MSI_RecordGetString( rec, 10 );
+    /* create a font and add it to the list */
+    name = MSI_RecordGetString( rec, 1 );
+    font = HeapAlloc( GetProcessHeap(), 0,
+                      sizeof *font + strlenW( name )*sizeof (WCHAR) );
+    strcpyW( font->name, name );
+    font->next = dialog->font_list;
+    dialog->font_list = font;
+
+    memset( &lf, 0, sizeof lf );
+    face = MSI_RecordGetString( rec, 2 );
+    lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
+    style = MSI_RecordGetInteger( rec, 5 );
+    if( style & 1 )
+        lf.lfWeight = FW_BOLD;
+    if( style & 2 )
+        lf.lfItalic = TRUE;
+    if( style & 4 )
+        lf.lfUnderline = TRUE;
+    if( style & 8 )
+        lf.lfStrikeOut = TRUE;
+    lstrcpynW( lf.lfFaceName, face, LF_FACESIZE );
 
-    x = (dialog->scale * x) / 10;
-    y = (dialog->scale * y) / 10;
-    width = (dialog->scale * width) / 10;
-    height = (dialog->scale * height) / 10;
+    /* adjust the height */
+    hdc = GetDC( dialog->hwnd );
+    if (hdc)
+    {
+        lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+        ReleaseDC( dialog->hwnd, hdc );
+    }
 
-    hwnd = CreateWindowW( szStatic, text, WS_CHILD | WS_VISIBLE |WS_GROUP,
-                          x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
-    if (!hwnd)
-        ERR("Failed to create hwnd\n");
+    font->hfont = CreateFontIndirectW( &lf );
+
+    TRACE("Adding font style %s\n", debugstr_w(font->name) );
 
     return ERROR_SUCCESS;
 }
 
-static UINT msi_dialog_button_control( dialog_info *dialog, MSIRECORD *rec )
+static msi_font *msi_dialog_find_font( msi_dialog *dialog, LPCWSTR name )
 {
-    const static WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
-    DWORD x, y, width, height;
-    LPCWSTR text;
-    HWND hwnd;
+    msi_font *font;
 
-    TRACE("%p %p\n", dialog, rec);
-
-    x = MSI_RecordGetInteger( rec, 4 );
-    y = MSI_RecordGetInteger( rec, 5 );
-    width = MSI_RecordGetInteger( rec, 6 );
-    height = MSI_RecordGetInteger( rec, 7 );
-    text = MSI_RecordGetString( rec, 10 );
+    for( font = dialog->font_list; font; font = font->next )
+        if( !strcmpW( font->name, name ) )  /* FIXME: case sensitive? */
+            break;
 
-    x = (dialog->scale * x) / 10;
-    y = (dialog->scale * y) / 10;
-    width = (dialog->scale * width) / 10;
-    height = (dialog->scale * height) / 10;
+    return font;
+}
 
-    hwnd = CreateWindowW( szButton, text, WS_CHILD | WS_VISIBLE |WS_GROUP,
-                          x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
+static UINT msi_dialog_set_font( msi_dialog *dialog, HWND hwnd, LPCWSTR name )
+{
+    msi_font *font;
 
+    font = msi_dialog_find_font( dialog, name );
+    if( font )
+        SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE );
+    else
+        ERR("No font entry for %s\n", debugstr_w(name));
     return ERROR_SUCCESS;
 }
 
-static UINT msi_dialog_line_control( dialog_info *dialog, MSIRECORD *rec )
+static UINT msi_dialog_build_font_list( msi_dialog *dialog )
 {
-    DWORD x, y, width, height;
-    LPCWSTR text;
-    const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
-    HWND hwnd;
+    static const WCHAR query[] = {
+      'S','E','L','E','C','T',' ','*',' ',
+      'F','R','O','M',' ','`','T','e','x','t','S','t','y','l','e','`',' ',0
+    };
+    UINT r;
+    MSIQUERY *view = NULL;
 
-    TRACE("%p %p\n", dialog, rec);
+    TRACE("dialog %p\n", dialog );
+
+    r = MSI_OpenQuery( dialog->package->db, &view, query );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = MSI_IterateRecords( view, NULL, msi_dialog_add_font, dialog );
+    msiobj_release( &view->hdr );
+
+    return r;
+}
+
+static msi_control *msi_dialog_add_control( msi_dialog *dialog,
+                MSIRECORD *rec, LPCWSTR szCls, DWORD style )
+{
+    DWORD x, y, width, height;
+    LPCWSTR text, name;
+    LPWSTR font, title = NULL;
+    msi_control *control = NULL;
+
+    style |= WS_CHILD | WS_VISIBLE | WS_GROUP;
+
+    name = MSI_RecordGetString( rec, 2 );
+    control = HeapAlloc( GetProcessHeap(), 0,
+                         sizeof *control + strlenW(name)*sizeof(WCHAR) );
+    strcpyW( control->name, name );
+    control->next = dialog->control_list;
+    dialog->control_list = control;
+    control->click_handler = NULL;
 
     x = MSI_RecordGetInteger( rec, 4 );
     y = MSI_RecordGetInteger( rec, 5 );
@@ -123,74 +230,92 @@ static UINT msi_dialog_line_control( dia
     height = MSI_RecordGetInteger( rec, 7 );
     text = MSI_RecordGetString( rec, 10 );
 
-    x = (dialog->scale * x) / 10;
-    y = (dialog->scale * y) / 10;
-    width = (dialog->scale * width) / 10;
-    height = (dialog->scale * height) / 10;
+    TRACE("Dialog %s control %s\n", debugstr_w(dialog->name), debugstr_w(text));
 
-    hwnd = CreateWindowW( szStatic, text, WS_CHILD | WS_VISIBLE |WS_GROUP |
-                          SS_ETCHEDHORZ |SS_SUNKEN,
+    x = msi_dialog_scale_unit( dialog, x );
+    y = msi_dialog_scale_unit( dialog, y );
+    width = msi_dialog_scale_unit( dialog, width );
+    height = msi_dialog_scale_unit( dialog, height );
+
+    font = msi_dialog_get_style( &text );
+    deformat_string( dialog->package, text, &title );
+    control->hwnd = CreateWindowW( szCls, title, style,
                           x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
-    if (!hwnd)
-        ERR("Failed to create hwnd\n");
+    if( font )
+    {
+        msi_dialog_set_font( dialog, control->hwnd, font );
+        HeapFree( GetProcessHeap(), 0, font );
+    }
+    if( title )
+        HeapFree( GetProcessHeap(), 0, font );
+    return control;
+}
+
+static UINT msi_dialog_text_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    TRACE("%p %p\n", dialog, rec);
 
+    msi_dialog_add_control( dialog, rec, szStatic, 0 );
     return ERROR_SUCCESS;
 }
 
-#if 0
-static UINT msi_load_picture( MSIDATABASE *db, LPCWSTR name, HBITMAP *hbm )
+static UINT msi_dialog_button_click( msi_dialog *dialog, msi_control *control );
+
+static UINT msi_dialog_button_control( msi_dialog *dialog, MSIRECORD *rec )
 {
-    IPicture *pic = NULL;
-    IPersistFile *pf = NULL;
-    IStream *stm = NULL;
-    HRESULT r;
+    const static WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
+    msi_control *control;
+
+    TRACE("%p %p\n", dialog, rec);
 
-    r = CoCreateObject( &CLSID_Picture, NULL, CLSCTX_INPROC_SERVER,
-                        &IID_IPicture, (LPVOID*)&pic );
-    if (FAILED(r))
-        return ERROR_FUNCTION_FAILED;
+    control = msi_dialog_add_control( dialog, rec, szButton, 0 );
+    control->click_handler = msi_dialog_button_click;
 
-    r = IPicture_QueryInterface( pic, &IID_IPersistFile, (LPVOID*) &pf );
-    if (SUCCEEDED(r) )
-    {
-        r = IPersistFile_Load( pf, stm );
-        IPersistFile_Release( pf );
-    }
+    return ERROR_SUCCESS;
+}
 
-    if (SUCCEEDED(r) )
-        IPicture_get_Handle( pic, hbm );
-    IPicture_Release( pic );
+static UINT msi_dialog_checkbox_click( msi_dialog *dialog, msi_control *control );
 
-    return r;
+static UINT msi_dialog_checkbox_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    const static WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
+    msi_control *control;
+
+    TRACE("%p %p\n", dialog, rec);
+
+    control = msi_dialog_add_control( dialog, rec, szButton,
+                                      BS_CHECKBOX | BS_MULTILINE );
+    control->click_handler = msi_dialog_checkbox_click;
+
+    return ERROR_SUCCESS;
 }
-#endif
 
-static UINT msi_dialog_bitmap_control( dialog_info *dialog, MSIRECORD *rec )
+static UINT msi_dialog_line_control( msi_dialog *dialog, MSIRECORD *rec )
 {
-    DWORD x, y, width, height;
-    LPCWSTR text;
-    const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
-    HWND hwnd;
+    TRACE("%p %p\n", dialog, rec);
+
+    msi_dialog_add_control( dialog, rec, szStatic, SS_ETCHEDHORZ | SS_SUNKEN );
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_scrolltext_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    const static WCHAR szEdit[] = { 'E','D','I','T',0 };
 
     TRACE("%p %p\n", dialog, rec);
 
-    x = MSI_RecordGetInteger( rec, 4 );
-    y = MSI_RecordGetInteger( rec, 5 );
-    width = MSI_RecordGetInteger( rec, 6 );
-    height = MSI_RecordGetInteger( rec, 7 );
-    text = MSI_RecordGetString( rec, 10 );
+    msi_dialog_add_control( dialog, rec, szEdit,
+                 ES_MULTILINE | WS_VSCROLL | ES_READONLY | ES_AUTOVSCROLL );
 
-    x = (dialog->scale * x) / 10;
-    y = (dialog->scale * y) / 10;
-    width = (dialog->scale * width) / 10;
-    height = (dialog->scale * height) / 10;
+    return ERROR_SUCCESS;
+}
 
-    hwnd = CreateWindowW( szStatic, text, WS_CHILD | WS_VISIBLE |WS_GROUP | WS_DISABLED |
-                          SS_BITMAP | SS_LEFT | SS_CENTERIMAGE,
-                          x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
-    if (!hwnd)
-        ERR("Failed to create hwnd\n");
+static UINT msi_dialog_bitmap_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    TRACE("%p %p\n", dialog, rec);
 
+    msi_dialog_add_control( dialog, rec, szStatic,
+                            SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
     return ERROR_SUCCESS;
 }
 
@@ -198,6 +323,9 @@ static const WCHAR szText[] = { 'T','e',
 static const WCHAR szButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
 static const WCHAR szLine[] = { 'L','i','n','e',0 };
 static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 };
+static const WCHAR szCheckBox[] = { 'C','h','e','c','k','B','o','x',0 };
+static const WCHAR szScrollableText[] = {
+    'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 };
 
 struct control_handler msi_dialog_handler[] =
 {
@@ -205,41 +333,15 @@ struct control_handler msi_dialog_handle
     { szButton, msi_dialog_button_control },
     { szLine, msi_dialog_line_control },
     { szBitmap, msi_dialog_bitmap_control },
+    { szCheckBox, msi_dialog_checkbox_control },
+    { szScrollableText, msi_dialog_scrolltext_control },
 };
 
 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
 
-typedef UINT (*record_func)( MSIRECORD *rec, LPVOID param );
-
-static UINT msi_iterate_records( MSIQUERY *view, record_func func, LPVOID param )
-{
-    MSIRECORD *rec = NULL;
-    UINT r;
-
-    r = MSI_ViewExecute( view, NULL );
-    if( r != ERROR_SUCCESS )
-        return r;
-
-    /* iterate a query */
-    while( 1 )
-    {
-        r = MSI_ViewFetch( view, &rec );
-        if( r != ERROR_SUCCESS )
-            break;
-        r = func( rec, param );
-        msiobj_release( &rec->hdr );
-        if( r != ERROR_SUCCESS )
-            break;
-    }
-
-    MSI_ViewClose( view );
-
-    return ERROR_SUCCESS;
-}
-
 static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param )
 {
-    dialog_info *dialog = param;
+    msi_dialog *dialog = param;
     LPCWSTR control_type;
     UINT i;
 
@@ -256,7 +358,7 @@ static UINT msi_dialog_create_controls( 
     return ERROR_SUCCESS;
 }
 
-static UINT msi_dialog_fill_controls( dialog_info *dialog, LPCWSTR name )
+static UINT msi_dialog_fill_controls( msi_dialog *dialog )
 {
     static const WCHAR query[] = {
         'S','E','L','E','C','T',' ','*',' ',
@@ -267,17 +369,102 @@ static UINT msi_dialog_fill_controls( di
     MSIQUERY *view = NULL;
     MSIPACKAGE *package = dialog->package;
 
-    TRACE("%p %s\n", dialog, debugstr_w(name) );
+    TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
+
+    /* query the Control table for all the elements of the control */
+    r = MSI_OpenQuery( package->db, &view, query, dialog->name );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    r = MSI_IterateRecords( view, 0, msi_dialog_create_controls, dialog );
+    msiobj_release( &view->hdr );
+
+    return r;
+}
+
+static msi_control *msi_dialog_find_control( msi_dialog *dialog, LPCWSTR name )
+{
+    msi_control *control;
+
+    for( control = dialog->control_list; control; control = control->next )
+        if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */
+            break;
+    return control;
+}
+
+static msi_control *msi_dialog_find_control_by_hwnd( msi_dialog *dialog, HWND hwnd )
+{
+    msi_control *control;
+
+    for( control = dialog->control_list; control; control = control->next )
+        if( hwnd == control->hwnd )
+            break;
+    return control;
+}
+
+static UINT msi_dialog_set_control_condition( MSIRECORD *rec, LPVOID param )
+{
+    static const WCHAR szHide[] = { 'H','i','d','e',0 };
+    static const WCHAR szShow[] = { 'S','h','o','w',0 };
+    static const WCHAR szDisable[] = { 'D','i','s','a','b','l','e',0 };
+    static const WCHAR szEnable[] = { 'E','n','a','b','l','e',0 };
+    msi_dialog *dialog = param;
+    msi_control *control;
+    LPCWSTR name, action, condition;
+    UINT r;
+
+    name = MSI_RecordGetString( rec, 2 );
+    action = MSI_RecordGetString( rec, 3 );
+    condition = MSI_RecordGetString( rec, 4 );
+    r = MSI_EvaluateConditionW( dialog->package, condition );
+    control = msi_dialog_find_control( dialog, name );
+    if( r && control )
+    {
+        TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
+
+        /* FIXME: case sensitive? */
+        if(!strcmpW(action, szHide))
+            ShowWindow(control->hwnd, SW_HIDE);
+        else if(!strcmpW(action, szShow))
+            ShowWindow(control->hwnd, SW_SHOW);
+        else if(!strcmpW(action, szDisable))
+            EnableWindow(control->hwnd, FALSE);
+        else if(!strcmpW(action, szEnable))
+            EnableWindow(control->hwnd, FALSE);
+        else
+            FIXME("Unhandled action %s\n", debugstr_w(action));
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog )
+{
+    static const WCHAR query[] = {
+      'S','E','L','E','C','T',' ','*',' ',
+      'F','R','O','M',' ',
+        'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
+      'W','H','E','R','E',' ',
+        '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0
+    };
+    UINT r;
+    MSIQUERY *view = NULL;
+    MSIPACKAGE *package = dialog->package;
+
+    TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
 
     /* query the Control table for all the elements of the control */
-    r = MSI_OpenQuery( package->db, &view, query, name );
+    r = MSI_OpenQuery( package->db, &view, query, dialog->name );
     if( r != ERROR_SUCCESS )
     {
-        ERR("query failed for dialog %s\n", debugstr_w(name));
+        ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
         return ERROR_INVALID_PARAMETER;
     }
 
-    r = msi_iterate_records( view, msi_dialog_create_controls, dialog );
+    r = MSI_IterateRecords( view, 0, msi_dialog_set_control_condition, dialog );
     msiobj_release( &view->hdr );
 
     return r;
@@ -299,7 +486,7 @@ static INT msi_dialog_get_sans_serif_hei
     if (hdc)
     {
         memset( &lf, 0, sizeof lf );
-        lf.lfHeight = -MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+        lf.lfHeight = MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
         strcpyW( lf.lfFaceName, szSansSerif );
         hFont = CreateFontIndirectW(&lf);
         if (hFont)
@@ -323,12 +510,13 @@ static LRESULT msi_dialog_oncreate( HWND
         'F','R','O','M',' ','D','i','a','l','o','g',' ',
         'W','H','E','R','E',' ',
            '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
-    dialog_info *dialog = (dialog_info*) cs->lpCreateParams;
+    msi_dialog *dialog = (msi_dialog*) cs->lpCreateParams;
     MSIPACKAGE *package = dialog->package;
     MSIQUERY *view = NULL;
     MSIRECORD *rec = NULL;
     DWORD width, height;
-    LPCWSTR title;
+    LPCWSTR text;
+    LPWSTR title = NULL;
     UINT r;
 
     TRACE("%p %p\n", dialog, package);
@@ -341,7 +529,7 @@ static LRESULT msi_dialog_oncreate( HWND
     if( r != ERROR_SUCCESS )
     {
         ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
-        return 1;
+        return -1;
     }
     MSI_ViewExecute( view, NULL );
     MSI_ViewFetch( view, &rec );
@@ -350,42 +538,152 @@ static LRESULT msi_dialog_oncreate( HWND
 
     if( !rec )
     {
-        ERR("No record found for dialog %s\n", debugstr_w(dialog->name));
-        return 1;
+        TRACE("No record found for dialog %s\n", debugstr_w(dialog->name));
+        return -1;
     }
 
     dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd);
 
     width = MSI_RecordGetInteger( rec, 4 );
     height = MSI_RecordGetInteger( rec, 5 );
-    title = MSI_RecordGetString( rec, 7 );
+    dialog->attributes = MSI_RecordGetInteger( rec, 6 );
+    text = MSI_RecordGetString( rec, 7 );
 
-    width = (dialog->scale * width) / 10;
-    height = (dialog->scale * height) / 10;
+    width = msi_dialog_scale_unit( dialog, width );
+    height = msi_dialog_scale_unit( dialog, height ) + 25; /* FIXME */
 
+    deformat_string( dialog->package, text, &title );
     SetWindowTextW( hwnd, title );
     SetWindowPos( hwnd, 0, 0, 0, width, height,
                   SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW );
 
+    if( title )
+        HeapFree( GetProcessHeap(), 0, title );
     msiobj_release( &rec->hdr );
 
-    msi_dialog_fill_controls( dialog, dialog->name );
+    msi_dialog_build_font_list( dialog );
+    msi_dialog_fill_controls( dialog );
+    msi_dialog_evaluate_control_conditions( dialog );
 
     return 0;
 }
 
-static LRESULT msi_dialog_handle_click( dialog_info *dialog,
-                                     DWORD id, HWND handle )
+static UINT msi_dialog_send_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
 {
-    TRACE("BN_CLICKED %08lx %p\n", id, handle);
+    LPWSTR event_fmt = NULL, arg_fmt = NULL;
+
+    TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
+
+    deformat_string( dialog->package, event, &event_fmt );
+    deformat_string( dialog->package, arg, &arg_fmt );
+
+    dialog->event_handler( dialog->package, event_fmt, arg_fmt, dialog );
+
+    HeapFree( GetProcessHeap(), 0, event_fmt );
+    HeapFree( GetProcessHeap(), 0, arg_fmt );
+
+    return ERROR_SUCCESS;
+}
 
+static UINT msi_dialog_set_property( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
+{
+    static const WCHAR szNullArg[] = { '{','}',0 };
+    LPWSTR p, prop, arg_fmt = NULL;
+    UINT len;
+
+    len = strlenW(event);
+    prop = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR));
+    strcpyW( prop, &event[1] );
+    p = strchrW( prop, ']' );
+    if( p && p[1] == 0 )
+    {
+        *p = 0;
+        if( strcmpW( szNullArg, arg ) )
+            deformat_string( dialog->package, arg, &arg_fmt );
+        MSI_SetPropertyW( dialog->package, prop, arg_fmt );
+    }
+    else
+        ERR("Badly formatted property string - what happens?\n");
+    HeapFree( GetProcessHeap(), 0, prop );
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_control_event( MSIRECORD *rec, LPVOID param )
+{
+    msi_dialog *dialog = param;
+    LPCWSTR condition, event, arg;
+    UINT r;
+
+    condition = MSI_RecordGetString( rec, 5 );
+    r = MSI_EvaluateConditionW( dialog->package, condition );
+    if( r )
+    {
+        event = MSI_RecordGetString( rec, 3 );
+        arg = MSI_RecordGetString( rec, 4 );
+        if( event[0] == '[' )
+            msi_dialog_set_property( dialog, event, arg );
+        else
+            msi_dialog_send_event( dialog, event, arg );
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_button_click( msi_dialog *dialog, msi_control *control )
+{
+    static const WCHAR query[] = {
+      'S','E','L','E','C','T',' ','*',' ',
+      'F','R','O','M',' ','C','o','n','t','r','o','l','E','v','e','n','t',' ',
+      'W','H','E','R','E',' ',
+         '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
+      'A','N','D',' ',
+         '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
+      'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0
+    };
+    MSIQUERY *view = NULL;
+    UINT r;
+
+    r = MSI_OpenQuery( dialog->package->db, &view, query,
+                       dialog->name, control->name );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("query failed\n");
+        return 0;
+    }
+
+    r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog );
+    msiobj_release( &view->hdr );
+
+    return r;
+}
+
+static UINT msi_dialog_checkbox_click( msi_dialog *dialog, msi_control *control )
+{
+    FIXME("clicked checkbox %s\n", debugstr_w(control->name));
+    return ERROR_SUCCESS;
+}
+
+static LRESULT msi_dialog_handle_click( msi_dialog *dialog, HWND hwnd )
+{
+    msi_control *control;
+
+    TRACE("BN_CLICKED %p %p\n", dialog, hwnd);
+
+    control = msi_dialog_find_control_by_hwnd( dialog, hwnd );
+    if( control )
+    {
+        if( control->click_handler )
+            control->click_handler( dialog, control );
+    }
+    else
+        ERR("button click from nowhere\n");
     return 0;
 }
 
 static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg,
                 WPARAM wParam, LPARAM lParam )
 {
-    dialog_info *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA );
+    msi_dialog *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA );
 
     switch (msg)
     {
@@ -394,7 +692,7 @@ static LRESULT WINAPI MSIDialog_WndProc(
 
     case WM_COMMAND:
         if( HIWORD(wParam) == BN_CLICKED )
-            return msi_dialog_handle_click( dialog, LOWORD(wParam), (HWND)lParam );
+            return msi_dialog_handle_click( dialog, (HWND)lParam );
         break;
 
     case WM_DESTROY:
@@ -406,10 +704,10 @@ static LRESULT WINAPI MSIDialog_WndProc(
 
 /* functions that interface to other modules within MSI */
 
-dialog_info *msi_dialog_create( MSIPACKAGE* package, LPCWSTR szDialogName,
+msi_dialog *msi_dialog_create( MSIPACKAGE* package, LPCWSTR szDialogName,
                                 msi_dialog_event_handler event_handler )
 {
-    dialog_info *dialog;
+    msi_dialog *dialog;
     HWND hwnd;
 
     TRACE("%p %s\n", package, debugstr_w(szDialogName));
@@ -422,28 +720,97 @@ dialog_info *msi_dialog_create( MSIPACKA
     strcpyW( dialog->name, szDialogName );
     dialog->package = package;
     dialog->event_handler = event_handler;
-    msiobj_addref( &package->hdr );
 
-    /* create and show the dialog window */
+    /* create the dialog window, don't show it yet */
     hwnd = CreateWindowW( szMsiDialogClass, szDialogName, WS_OVERLAPPEDWINDOW,
                      CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                      NULL, NULL, NULL, dialog );
     if( !hwnd )
     {
+        ERR("Failed to create dialog %s\n", debugstr_w( szDialogName ));
         msi_dialog_destroy( dialog );
         return NULL;
     }
-    ShowWindow( hwnd, SW_SHOW );
-    UpdateWindow( hwnd );
 
     return dialog;
 }
 
-void msi_dialog_destroy( dialog_info *dialog )
+void msi_dialog_end_dialog( msi_dialog *dialog )
+{
+    dialog->finished = 1;
+}
+
+UINT msi_dialog_run_message_loop( msi_dialog *dialog )
+{
+    MSG msg;
+
+    if( dialog->attributes & msidbDialogAttributesVisible )
+    {
+        ShowWindow( dialog->hwnd, SW_SHOW );
+        UpdateWindow( dialog->hwnd );
+    }
+
+    if( dialog->attributes & msidbDialogAttributesModal )
+    {
+        while( !dialog->finished && GetMessageW( &msg, 0, 0, 0 ) )
+        {
+            TranslateMessage( &msg );
+            DispatchMessageW( &msg );
+        }
+    }
+    else
+        return ERROR_IO_PENDING;
+
+    return ERROR_SUCCESS;
+}
+
+void msi_dialog_check_messages( msi_dialog *dialog )
+{
+    MSG msg;
+
+    if( dialog->finished )
+        return;
+
+    while( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ) )
+    {
+        TranslateMessage( &msg );
+        DispatchMessageW( &msg );
+    }
+}
+
+void msi_dialog_do_preview( msi_dialog *dialog )
+{
+    dialog->attributes |= msidbDialogAttributesVisible;
+    dialog->attributes &= ~msidbDialogAttributesModal;
+    msi_dialog_run_message_loop( dialog );
+}
+
+void msi_dialog_destroy( msi_dialog *dialog )
 {
     if( dialog->hwnd )
+        ShowWindow( dialog->hwnd, SW_HIDE );
+    
+    /* destroy the list of controls */
+    while( dialog->control_list )
+    {
+        msi_control *t = dialog->control_list;
+        dialog->control_list = t->next;
+        /* leave dialog->hwnd - destroying parent destroys child windows */
+        HeapFree( GetProcessHeap(), 0, t );
+    }
+
+    /* destroy the list of fonts */
+    while( dialog->font_list )
+    {
+        msi_font *t = dialog->font_list;
+        dialog->font_list = t->next;
+        DeleteObject( t->hfont );
+        HeapFree( GetProcessHeap(), 0, t );
+    }
+
+    if( dialog->hwnd )
         DestroyWindow( dialog->hwnd );
-    msiobj_release( &dialog->package->hdr );
+
     dialog->package = NULL;
     HeapFree( GetProcessHeap(), 0, dialog );
 }
Index: dlls/msi/msipriv.h
===================================================================
RCS file: /home/wine/wine/dlls/msi/msipriv.h,v
retrieving revision 1.44
diff -u -p -r1.44 msipriv.h
--- dlls/msi/msipriv.h	28 Jan 2005 12:39:57 -0000	1.44
+++ dlls/msi/msipriv.h	2 Feb 2005 05:04:24 -0000
@@ -182,6 +182,9 @@ struct tagMSIVIEW
     MSIVIEWOPS   *ops;
 };
 
+struct msi_dialog_tag;
+typedef struct msi_dialog_tag msi_dialog;
+
 typedef struct tagMSIPACKAGE
 {
     MSIOBJECTHDR hdr;
@@ -209,16 +212,15 @@ typedef struct tagMSIPACKAGE
     LPWSTR PackagePath;
 
     UINT CurrentInstallState;
+    msi_dialog *dialog;
+    LPWSTR next_dialog;
 } MSIPACKAGE;
 
-struct tag_dialog_info;
-typedef struct tag_dialog_info dialog_info;
-
 typedef struct tagMSIPREVIEW
 {
     MSIOBJECTHDR hdr;
     MSIPACKAGE *package;
-    dialog_info *dialog;
+    msi_dialog *dialog;
 } MSIPREVIEW;
 
 #define MSIHANDLETYPE_ANY 0
@@ -299,6 +301,7 @@ extern UINT read_raw_stream_data( MSIDAT
 /* action internals */
 extern UINT ACTION_DoTopLevelINSTALL( MSIPACKAGE *, LPCWSTR, LPCWSTR );
 extern void ACTION_free_package_structures( MSIPACKAGE* );
+extern UINT ACTION_DialogBox( MSIPACKAGE*, LPCWSTR);
 
 /* record internals */
 extern UINT MSI_RecordSetIStream( MSIRECORD *, unsigned int, IStream *);
@@ -322,6 +325,8 @@ extern void enum_stream_names( IStorage 
 extern UINT MSI_OpenDatabaseW( LPCWSTR, LPCWSTR, MSIDATABASE ** );
 extern UINT MSI_DatabaseOpenViewW(MSIDATABASE *, LPCWSTR, MSIQUERY ** );
 extern UINT MSI_OpenQuery( MSIDATABASE *, MSIQUERY **, LPCWSTR, ... );
+typedef UINT (*record_func)( MSIRECORD *rec, LPVOID param );
+extern UINT MSI_IterateRecords( MSIQUERY *, DWORD *, record_func, LPVOID );
 
 /* view internals */
 extern UINT MSI_ViewExecute( MSIQUERY*, MSIRECORD * );
@@ -359,9 +364,13 @@ extern UINT MSIREG_OpenProductsKey(LPCWS
 extern UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
 
 /* msi dialog interface */
-typedef VOID (*msi_dialog_event_handler)( MSIPACKAGE*, LPCWSTR, LPCWSTR, HWND );
-extern dialog_info *msi_dialog_create( MSIPACKAGE*, LPCWSTR, msi_dialog_event_handler );
-extern void msi_dialog_destroy( dialog_info* );
+typedef VOID (*msi_dialog_event_handler)( MSIPACKAGE*, LPCWSTR, LPCWSTR, msi_dialog* );
+extern msi_dialog *msi_dialog_create( MSIPACKAGE*, LPCWSTR, msi_dialog_event_handler );
+extern UINT msi_dialog_run_message_loop( msi_dialog* );
+extern void msi_dialog_end_dialog( msi_dialog* );
+extern void msi_dialog_check_messages( msi_dialog* );
+extern void msi_dialog_do_preview( msi_dialog* );
+extern void msi_dialog_destroy( msi_dialog* );
 extern void msi_dialog_register_class( void );
 extern void msi_dialog_unregister_class( void );
 
Index: dlls/msi/msiquery.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/msiquery.c,v
retrieving revision 1.20
diff -u -p -r1.20 msiquery.c
--- dlls/msi/msiquery.c	21 Jan 2005 16:19:11 -0000	1.20
+++ dlls/msi/msiquery.c	2 Feb 2005 05:04:25 -0000
@@ -182,6 +182,40 @@ UINT MSI_OpenQuery( MSIDATABASE *db, MSI
     return rc;
 }
 
+UINT MSI_IterateRecords( MSIQUERY *view, DWORD *count,
+                         record_func func, LPVOID param )
+{
+    MSIRECORD *rec = NULL;
+    UINT r, n = 0, max = 0;
+
+    r = MSI_ViewExecute( view, NULL );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    if( count )
+        max = *count;
+
+    /* iterate a query */
+    for( n = 0; (max == 0) || (n < max); n++ )
+    {
+        r = MSI_ViewFetch( view, &rec );
+        if( r != ERROR_SUCCESS )
+            break;
+        r = func( rec, param );
+        msiobj_release( &rec->hdr );
+        if( r != ERROR_SUCCESS )
+            break;
+        n++;
+    }
+
+    MSI_ViewClose( view );
+
+    if( count )
+        *count = n;
+
+    return r;
+}
+
 UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
               LPCWSTR szQuery, MSIHANDLE *phView)
 {
Index: dlls/msi/package.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/package.c,v
retrieving revision 1.34
diff -u -p -r1.34 package.c
--- dlls/msi/package.c	28 Jan 2005 12:39:57 -0000	1.34
+++ dlls/msi/package.c	2 Feb 2005 05:04:25 -0000
@@ -53,6 +53,8 @@ void MSI_FreePackage( MSIOBJECTHDR *arg)
 {
     MSIPACKAGE *package= (MSIPACKAGE*) arg;
 
+    if( package->dialog )
+        msi_dialog_destroy( package->dialog );
     ACTION_free_package_structures(package);
 
     msiobj_release( &package->db->hdr );
@@ -378,6 +380,8 @@ MSIPACKAGE *MSI_CreatePackage( MSIDATABA
         package->loaded_files = 0;
         package->ActionFormat = NULL;
         package->LastAction = NULL;
+        package->dialog = NULL;
+        package->next_dialog = NULL;
 
         /* OK, here is where we do a slew of things to the database to 
          * prep for all that is to come as a package */
Index: dlls/msi/preview.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/preview.c,v
retrieving revision 1.2
diff -u -p -r1.2 preview.c
--- dlls/msi/preview.c	28 Jan 2005 12:39:57 -0000	1.2
+++ dlls/msi/preview.c	2 Feb 2005 05:04:25 -0000
@@ -82,7 +82,7 @@ UINT WINAPI MsiEnableUIPreview( MSIHANDL
 }
 
 static VOID preview_event_handler( MSIPACKAGE *package, LPCWSTR event,
-                                   LPCWSTR argument, HWND dialog )
+                                   LPCWSTR argument, msi_dialog *dialog )
 {
     MESSAGE("Preview dialog event '%s' (arg='%s')\n",
             debugstr_w( event ), debugstr_w( argument ));
@@ -90,7 +90,7 @@ static VOID preview_event_handler( MSIPA
 
 UINT MSI_PreviewDialogW( MSIPREVIEW *preview, LPCWSTR szDialogName )
 {
-    dialog_info *dialog = NULL;
+    msi_dialog *dialog = NULL;
     UINT r = ERROR_SUCCESS;
 
     if( preview->dialog )
@@ -101,7 +101,9 @@ UINT MSI_PreviewDialogW( MSIPREVIEW *pre
     {
         dialog = msi_dialog_create( preview->package, szDialogName,
                                     preview_event_handler );
-        if( !dialog )
+        if( dialog )
+            msi_dialog_do_preview( dialog );
+        else
             r = ERROR_FUNCTION_FAILED;
     }
     preview->dialog = dialog;


More information about the wine-patches mailing list