diff --git a/dlls/msi/.gitignore b/dlls/msi/.gitignore index 149e0f203a8..6dd5b9dafc4 100644 --- a/dlls/msi/.gitignore +++ b/dlls/msi/.gitignore @@ -1,6 +1,9 @@ Makefile cond.tab.c cond.tab.h +instabsent.bmp +instadvert.bmp +instlocal.bmp libmsi.def msi.res sql.tab.c diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in index f484e5d9e9d..04ded54ec82 100644 --- a/dlls/msi/Makefile.in +++ b/dlls/msi/Makefile.in @@ -4,7 +4,7 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = msi.dll IMPORTLIB = libmsi.$(IMPLIBEXT) -IMPORTS = shell32 shlwapi cabinet oleaut32 ole32 version user32 gdi32 advapi32 kernel32 +IMPORTS = comctl32 shell32 shlwapi cabinet oleaut32 ole32 version user32 gdi32 advapi32 kernel32 EXTRALIBS = -luuid $(LIBUNICODE) C_SRCS = \ @@ -43,6 +43,11 @@ C_SRCS = \ where.c RC_SRCS = msi.rc +RC_BINSRC = msi.rc +RC_BINARIES = \ + instabsent.bmp \ + instadvert.bmp \ + instlocal.bmp EXTRA_SRCS = sql.y cond.y EXTRA_OBJS = sql.tab.o cond.tab.o diff --git a/dlls/msi/dialog.c b/dlls/msi/dialog.c index 62be1301188..76d4d064d60 100644 --- a/dlls/msi/dialog.c +++ b/dlls/msi/dialog.c @@ -45,6 +45,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(msi); +extern HINSTANCE msi_hInstance; + struct msi_control_tag; typedef struct msi_control_tag msi_control; typedef UINT (*msi_handler)( msi_dialog *, msi_control *, WPARAM ); @@ -1387,9 +1389,145 @@ static UINT msi_dialog_radiogroup_control( msi_dialog *dialog, MSIRECORD *rec ) /******************** Selection Tree ***************************************/ +struct msi_selection_tree_info +{ + msi_dialog *dialog; + HWND hwnd; + WNDPROC oldproc; +}; + static void -msi_dialog_tv_add_child_features( MSIPACKAGE *package, HWND hwnd, - LPCWSTR parent, HTREEITEM hParent ) +msi_seltree_sync_item_state( HWND hwnd, MSIFEATURE *feature, HTREEITEM hItem ) +{ + TVITEMW tvi; + + TRACE("Feature %s -> %d %d %d\n", debugstr_w(feature->Title), + feature->Installed, feature->Action, feature->ActionRequest); + + tvi.mask = TVIF_STATE; + tvi.hItem = hItem; + tvi.state = INDEXTOSTATEIMAGEMASK( feature->Action ); + tvi.stateMask = TVIS_STATEIMAGEMASK; + + SendMessageW( hwnd, TVM_SETITEMW, 0, (LPARAM) &tvi ); +} + +static UINT +msi_seltree_popup_menu( HWND hwnd, INT x, INT y ) +{ + HMENU hMenu; + INT r; + + /* create a menu to display */ + hMenu = CreatePopupMenu(); + + /* FIXME: load strings from resources */ + AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_LOCAL, "Install feature locally"); + AppendMenuA( hMenu, MF_GRAYED, 0x1000, "Install entire feature"); + AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ADVERTISED, "Install on demand"); + AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ABSENT, "Don't install"); + r = TrackPopupMenu( hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, + x, y, 0, hwnd, NULL ); + DestroyMenu( hMenu ); + return r; +} + +static MSIFEATURE * +msi_seltree_feature_from_item( HWND hwnd, HTREEITEM hItem ) +{ + TVITEMW tvi; + + /* get the feature from the item */ + memset( &tvi, 0, sizeof tvi ); + tvi.hItem = hItem; + tvi.mask = TVIF_PARAM | TVIF_HANDLE; + SendMessageW( hwnd, TVM_GETITEMW, 0, (LPARAM) &tvi ); + + return (MSIFEATURE*) tvi.lParam; +} + +static LRESULT +msi_seltree_menu( HWND hwnd, HTREEITEM hItem ) +{ + MSIFEATURE *feature; + union { + RECT rc; + POINT pt[2]; + HTREEITEM hItem; + } u; + UINT r; + + feature = msi_seltree_feature_from_item( hwnd, hItem ); + if (!feature) + { + ERR("item %p feature was NULL\n", hItem); + return 0; + } + + /* get the item's rectangle to put the menu just below it */ + u.hItem = hItem; + SendMessageW( hwnd, TVM_GETITEMRECT, 0, (LPARAM) &u.rc ); + MapWindowPoints( hwnd, NULL, u.pt, 2 ); + + r = msi_seltree_popup_menu( hwnd, u.rc.left, u.rc.top ); + + switch (r) + { + case INSTALLSTATE_LOCAL: + case INSTALLSTATE_ADVERTISED: + case INSTALLSTATE_ABSENT: + feature->ActionRequest = r; + feature->Action = r; + break; + default: + FIXME("select feature and all children\n"); + } + + /* update */ + msi_seltree_sync_item_state( hwnd, feature, hItem ); + + return 0; +} + +static LRESULT WINAPI +MSISelectionTree_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_selection_tree_info *info; + TVHITTESTINFO tvhti; + HRESULT r; + + TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW(hWnd, szButtonData); + + switch( msg ) + { + case WM_LBUTTONDOWN: + tvhti.pt.x = LOWORD( lParam ); + tvhti.pt.y = HIWORD( lParam ); + tvhti.flags = 0; + tvhti.hItem = 0; + r = CallWindowProcW(info->oldproc, hWnd, TVM_HITTEST, 0, (LPARAM) &tvhti ); + if (tvhti.flags & TVHT_ONITEMSTATEICON) + return msi_seltree_menu( hWnd, tvhti.hItem ); + break; + } + + r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam); + + switch( msg ) + { + case WM_NCDESTROY: + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + } + return r; +} + +static void +msi_seltree_add_child_features( MSIPACKAGE *package, HWND hwnd, + LPCWSTR parent, HTREEITEM hParent ) { MSIFEATURE *feature; TVINSERTSTRUCTW tvis; @@ -1406,38 +1544,90 @@ msi_dialog_tv_add_child_features( MSIPACKAGE *package, HWND hwnd, memset( &tvis, 0, sizeof tvis ); tvis.hParent = hParent; tvis.hInsertAfter = TVI_SORT; - if (feature->Title) - { - tvis.u.item.mask = TVIF_TEXT; - tvis.u.item.pszText = feature->Title; - } + tvis.u.item.mask = TVIF_TEXT | TVIF_PARAM; + tvis.u.item.pszText = feature->Title; tvis.u.item.lParam = (LPARAM) feature; + hitem = (HTREEITEM) SendMessageW( hwnd, TVM_INSERTITEMW, 0, (LPARAM) &tvis ); if (!hitem) continue; - msi_dialog_tv_add_child_features( package, hwnd, - feature->Feature, hitem ); + msi_seltree_sync_item_state( hwnd, feature, hitem ); + msi_seltree_add_child_features( package, hwnd, + feature->Feature, hitem ); } } +static void msi_seltree_create_imagelist( HWND hwnd ) +{ + const int bm_width = 32, bm_height = 16, bm_count = 3; + const int bm_resource = 0x1001; + HIMAGELIST himl; + int i; + HBITMAP hbmp; + + himl = ImageList_Create( bm_width, bm_height, FALSE, 4, 0 ); + if (!himl) + { + ERR("failed to create image list\n"); + return; + } + + for (i=0; ipackage; + DWORD style; + struct msi_selection_tree_info *info; - prop = MSI_RecordGetString( rec, 9 ); - val = msi_dup_property( package, prop ); - control = msi_dialog_add_control( dialog, rec, WC_TREEVIEWW, - TVS_HASBUTTONS | WS_GROUP | WS_VSCROLL ); - if (!control) + info = msi_alloc( sizeof *info ); + if (!info) return ERROR_FUNCTION_FAILED; - msi_dialog_tv_add_child_features( package, control->hwnd, NULL, NULL ); + /* create the treeview control */ + prop = MSI_RecordGetString( rec, 9 ); + style = TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT; + style |= WS_GROUP | WS_VSCROLL; + control = msi_dialog_add_control( dialog, rec, WC_TREEVIEWW, style ); + if (!control) + { + msi_free(info); + return ERROR_FUNCTION_FAILED; + } - msi_free( val ); + /* subclass */ + info->dialog = dialog; + info->hwnd = control->hwnd; + info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSISelectionTree_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + /* initialize it */ + msi_seltree_create_imagelist( control->hwnd ); + msi_seltree_add_child_features( package, control->hwnd, NULL, NULL ); return ERROR_SUCCESS; } diff --git a/dlls/msi/msi.rc b/dlls/msi/msi.rc index 718a99a41de..61ed9002d5f 100644 --- a/dlls/msi/msi.rc +++ b/dlls/msi/msi.rc @@ -37,3 +37,90 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL #include "msi_Nl.rc" #include "msi_No.rc" #include "msi_Pt.rc" + +/* BINRES instadvert.bmp */ +0x1001 BITMAP instadvert.bmp +/* { + '42 4D 76 01 00 00 00 00 00 00 76 00 00 00 28 00' + '00 00 20 00 00 00 10 00 00 00 01 00 04 00 00 00' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 70 00 00 07 77 77 77 77' + '77 77 77 77 77 77 77 70 03 33 B3 30 07 77 77 77' + '77 77 77 77 77 77 77 00 B3 33 33 33 00 77 77 77' + '77 77 77 77 77 77 77 03 33 B3 A3 B3 30 77 77 77' + '77 77 07 77 77 77 70 33 33 33 33 33 B3 07 77 77' + '77 70 00 77 77 77 70 33 B3 30 03 33 33 07 77 77' + '77 00 00 07 77 77 70 33 33 00 00 3B 33 07 77 77' + '70 00 00 00 77 77 70 33 B3 30 0B 33 3B 07 77 77' + '00 00 00 00 07 77 70 3B 3A 33 33 33 33 07 77 70' + '00 00 00 00 00 77 77 03 3B 3B 3A 3B 30 77 77 77' + '77 77 77 77 77 77 77 00 33 33 3B 33 00 77 77 77' + '77 77 77 77 77 77 77 70 03 B3 33 30 07 77 77 77' + '77 77 77 77 77 77 77 77 70 00 00 07 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77' +} */ + +/* BINRES instabsent.bmp */ +0x1002 BITMAP instabsent.bmp +/* { + '42 4D 76 01 00 00 00 00 00 00 76 00 00 00 28 00' + '00 00 20 00 00 00 10 00 00 00 01 00 04 00 00 00' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 70 07 77 77 77 77 70 07 77 77' + '77 77 77 77 77 77 70 00 77 77 77 77 00 07 77 77' + '77 77 77 77 77 77 77 00 07 77 77 70 00 77 77 77' + '77 77 77 77 77 77 77 70 00 77 77 00 07 77 77 77' + '77 77 77 77 77 77 77 77 00 07 70 00 77 77 77 77' + '77 77 07 77 77 77 77 77 70 00 00 07 77 77 77 77' + '77 70 00 77 77 77 77 77 77 00 00 77 77 77 77 77' + '77 00 00 07 77 77 77 77 77 00 00 77 77 77 77 77' + '70 00 00 00 77 77 77 77 70 00 00 07 77 77 77 77' + '00 00 00 00 07 77 77 77 00 07 70 00 77 77 77 70' + '00 00 00 00 00 77 77 70 00 77 77 00 07 77 77 77' + '77 77 77 77 77 77 77 00 07 77 77 70 00 77 77 77' + '77 77 77 77 77 77 70 00 77 77 77 77 00 07 77 77' + '77 77 77 77 77 77 70 07 77 77 77 77 70 07 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77' +} */ + +/* BINRES instlocal.bmp */ +0x1003 BITMAP instlocal.bmp +/* { + '42 4D 76 01 00 00 00 00 00 00 76 00 00 00 28 00' + '00 00 20 00 00 00 10 00 00 00 01 00 04 00 00 00' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 70 07 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 0F F0 77 77 77 77 77 77' + '77 77 77 77 77 77 77 70 FF FF 07 77 77 77 77 77' + '77 77 07 77 77 77 77 0F F0 0F F0 77 77 77 77 77' + '77 70 00 77 77 77 77 0F 07 70 FF 07 77 77 77 77' + '77 00 00 07 77 77 77 70 77 77 0F F0 77 77 77 77' + '70 00 00 00 77 77 77 77 77 77 70 FF 07 77 77 77' + '00 00 00 00 07 77 77 77 77 77 77 0F F0 77 77 70' + '00 00 00 00 00 77 77 77 77 77 77 70 FF 07 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 0F F0 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 70 F0 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 07 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77' +} */