2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Mike McCormack for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
45 struct msi_control_tag
;
46 typedef struct msi_control_tag msi_control
;
47 typedef UINT (*msi_handler
)( msi_dialog
*, msi_control
*, WPARAM
);
49 struct msi_control_tag
63 typedef struct msi_font_tag
65 struct msi_font_tag
*next
;
73 msi_dialog_event_handler event_handler
;
85 typedef UINT (*msi_dialog_control_func
)( msi_dialog
*dialog
, MSIRECORD
*rec
);
86 struct control_handler
89 msi_dialog_control_func func
;
97 } radio_button_group_descr
;
99 const WCHAR szMsiDialogClass
[] = {
100 'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0
102 const WCHAR szMsiHiddenWindow
[] = {
103 'M','s','i','H','i','d','d','e','n','W','i','n','d','o','w',0 };
104 const static WCHAR szStatic
[] = { 'S','t','a','t','i','c',0 };
105 const static WCHAR szButton
[] = { 'B','U','T','T','O','N', 0 };
106 const static WCHAR szButtonData
[] = { 'M','S','I','D','A','T','A',0 };
107 static const WCHAR szText
[] = { 'T','e','x','t',0 };
108 static const WCHAR szPushButton
[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
109 static const WCHAR szLine
[] = { 'L','i','n','e',0 };
110 static const WCHAR szBitmap
[] = { 'B','i','t','m','a','p',0 };
111 static const WCHAR szCheckBox
[] = { 'C','h','e','c','k','B','o','x',0 };
112 static const WCHAR szScrollableText
[] = {
113 'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 };
114 static const WCHAR szComboBox
[] = { 'C','o','m','b','o','B','o','x',0 };
115 static const WCHAR szEdit
[] = { 'E','d','i','t',0 };
116 static const WCHAR szMaskedEdit
[] = { 'M','a','s','k','e','d','E','d','i','t',0 };
117 static const WCHAR szPathEdit
[] = { 'P','a','t','h','E','d','i','t',0 };
118 static const WCHAR szRadioButtonGroup
[] = {
119 'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 };
120 static const WCHAR szIcon
[] = { 'I','c','o','n',0 };
122 static UINT
msi_dialog_checkbox_handler( msi_dialog
*, msi_control
*, WPARAM
);
123 static void msi_dialog_checkbox_sync_state( msi_dialog
*, msi_control
* );
124 static UINT
msi_dialog_button_handler( msi_dialog
*, msi_control
*, WPARAM
);
125 static UINT
msi_dialog_edit_handler( msi_dialog
*, msi_control
*, WPARAM
);
126 static UINT
msi_dialog_radiogroup_handler( msi_dialog
*, msi_control
*, WPARAM param
);
127 static UINT
msi_dialog_evaluate_control_conditions( msi_dialog
*dialog
);
128 static LRESULT WINAPI
MSIRadioGroup_WndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
131 /* dialog sequencing */
133 #define WM_MSI_DIALOG_CREATE (WM_USER+0x100)
134 #define WM_MSI_DIALOG_DESTROY (WM_USER+0x101)
136 static DWORD uiThreadId
;
137 static HWND hMsiHiddenWindow
;
139 static INT
msi_dialog_scale_unit( msi_dialog
*dialog
, INT val
)
141 return (dialog
->scale
* val
+ 5) / 10;
144 static msi_control
*msi_dialog_find_control( msi_dialog
*dialog
, LPCWSTR name
)
146 msi_control
*control
;
150 LIST_FOR_EACH_ENTRY( control
, &dialog
->controls
, msi_control
, entry
)
151 if( !strcmpW( control
->name
, name
) ) /* FIXME: case sensitive? */
156 static msi_control
*msi_dialog_find_control_by_hwnd( msi_dialog
*dialog
, HWND hwnd
)
158 msi_control
*control
;
160 LIST_FOR_EACH_ENTRY( control
, &dialog
->controls
, msi_control
, entry
)
161 if( hwnd
== control
->hwnd
)
166 static LPWSTR
msi_get_deformatted_field( MSIPACKAGE
*package
, MSIRECORD
*rec
, int field
)
168 LPCWSTR str
= MSI_RecordGetString( rec
, field
);
172 deformat_string( package
, str
, &ret
);
177 * msi_dialog_get_style
179 * Extract the {\style} string from the front of the text to display and
180 * update the pointer.
182 static LPWSTR
msi_dialog_get_style( LPCWSTR p
, LPCWSTR
*rest
)
193 q
= strchrW( p
, '}' );
196 if( *p
== '\\' || *p
== '&' )
199 /* little bit of sanity checking to stop us getting confused with RTF */
201 if( *i
== '}' || *i
== '\\' )
207 ret
= msi_alloc( len
*sizeof(WCHAR
) );
210 memcpy( ret
, p
, len
*sizeof(WCHAR
) );
215 static UINT
msi_dialog_add_font( MSIRECORD
*rec
, LPVOID param
)
217 msi_dialog
*dialog
= param
;
224 /* create a font and add it to the list */
225 name
= MSI_RecordGetString( rec
, 1 );
226 font
= msi_alloc( sizeof *font
+ strlenW( name
)*sizeof (WCHAR
) );
227 strcpyW( font
->name
, name
);
228 font
->next
= dialog
->font_list
;
229 dialog
->font_list
= font
;
231 memset( &lf
, 0, sizeof lf
);
232 face
= MSI_RecordGetString( rec
, 2 );
233 lf
.lfHeight
= MSI_RecordGetInteger( rec
, 3 );
234 style
= MSI_RecordGetInteger( rec
, 5 );
235 if( style
& msidbTextStyleStyleBitsBold
)
236 lf
.lfWeight
= FW_BOLD
;
237 if( style
& msidbTextStyleStyleBitsItalic
)
239 if( style
& msidbTextStyleStyleBitsUnderline
)
240 lf
.lfUnderline
= TRUE
;
241 if( style
& msidbTextStyleStyleBitsStrike
)
242 lf
.lfStrikeOut
= TRUE
;
243 lstrcpynW( lf
.lfFaceName
, face
, LF_FACESIZE
);
245 /* adjust the height */
246 hdc
= GetDC( dialog
->hwnd
);
249 lf
.lfHeight
= -MulDiv(lf
.lfHeight
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
250 ReleaseDC( dialog
->hwnd
, hdc
);
253 font
->hfont
= CreateFontIndirectW( &lf
);
255 TRACE("Adding font style %s\n", debugstr_w(font
->name
) );
257 return ERROR_SUCCESS
;
260 static msi_font
*msi_dialog_find_font( msi_dialog
*dialog
, LPCWSTR name
)
264 for( font
= dialog
->font_list
; font
; font
= font
->next
)
265 if( !strcmpW( font
->name
, name
) ) /* FIXME: case sensitive? */
271 static UINT
msi_dialog_set_font( msi_dialog
*dialog
, HWND hwnd
, LPCWSTR name
)
275 font
= msi_dialog_find_font( dialog
, name
);
277 SendMessageW( hwnd
, WM_SETFONT
, (WPARAM
) font
->hfont
, TRUE
);
279 ERR("No font entry for %s\n", debugstr_w(name
));
280 return ERROR_SUCCESS
;
283 static UINT
msi_dialog_build_font_list( msi_dialog
*dialog
)
285 static const WCHAR query
[] = {
286 'S','E','L','E','C','T',' ','*',' ',
287 'F','R','O','M',' ','`','T','e','x','t','S','t','y','l','e','`',' ',0
290 MSIQUERY
*view
= NULL
;
292 TRACE("dialog %p\n", dialog
);
294 r
= MSI_OpenQuery( dialog
->package
->db
, &view
, query
);
295 if( r
!= ERROR_SUCCESS
)
298 r
= MSI_IterateRecords( view
, NULL
, msi_dialog_add_font
, dialog
);
299 msiobj_release( &view
->hdr
);
304 static msi_control
*msi_dialog_create_window( msi_dialog
*dialog
,
305 MSIRECORD
*rec
, LPCWSTR szCls
, LPCWSTR name
, LPCWSTR text
,
306 DWORD style
, HWND parent
)
308 DWORD x
, y
, width
, height
;
309 LPWSTR font
= NULL
, title_font
= NULL
;
310 LPCWSTR title
= NULL
;
311 msi_control
*control
;
315 control
= msi_alloc( sizeof *control
+ strlenW(name
)*sizeof(WCHAR
) );
316 strcpyW( control
->name
, name
);
317 list_add_head( &dialog
->controls
, &control
->entry
);
318 control
->handler
= NULL
;
319 control
->property
= NULL
;
320 control
->value
= NULL
;
321 control
->hBitmap
= NULL
;
322 control
->hIcon
= NULL
;
323 control
->hDll
= NULL
;
324 control
->tabnext
= strdupW( MSI_RecordGetString( rec
, 11) );
326 x
= MSI_RecordGetInteger( rec
, 4 );
327 y
= MSI_RecordGetInteger( rec
, 5 );
328 width
= MSI_RecordGetInteger( rec
, 6 );
329 height
= MSI_RecordGetInteger( rec
, 7 );
331 x
= msi_dialog_scale_unit( dialog
, x
);
332 y
= msi_dialog_scale_unit( dialog
, y
);
333 width
= msi_dialog_scale_unit( dialog
, width
);
334 height
= msi_dialog_scale_unit( dialog
, height
);
338 deformat_string( dialog
->package
, text
, &title_font
);
339 font
= msi_dialog_get_style( title_font
, &title
);
342 control
->hwnd
= CreateWindowW( szCls
, title
, style
,
343 x
, y
, width
, height
, parent
, NULL
, NULL
, NULL
);
345 TRACE("Dialog %s control %s hwnd %p\n",
346 debugstr_w(dialog
->name
), debugstr_w(text
), control
->hwnd
);
348 msi_dialog_set_font( dialog
, control
->hwnd
,
349 font
? font
: dialog
->default_font
);
351 msi_free( title_font
);
357 static MSIRECORD
*msi_get_binary_record( MSIDATABASE
*db
, LPCWSTR name
)
359 const static WCHAR query
[] = {
360 's','e','l','e','c','t',' ','*',' ',
361 'f','r','o','m',' ','B','i','n','a','r','y',' ',
362 'w','h','e','r','e',' ',
363 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0
366 return MSI_QueryGetRecord( db
, query
, name
);
369 static LPWSTR
msi_create_tmp_path(void)
373 static const WCHAR prefix
[] = { 'm','s','i',0 };
376 r
= GetTempPathW( MAX_PATH
, tmp
);
379 len
= lstrlenW( tmp
) + 20;
380 path
= msi_alloc( len
* sizeof (WCHAR
) );
383 r
= GetTempFileNameW( tmp
, prefix
, 0, path
);
394 static HANDLE
msi_load_image( MSIDATABASE
*db
, LPCWSTR name
, UINT type
,
395 UINT cx
, UINT cy
, UINT flags
)
397 MSIRECORD
*rec
= NULL
;
398 HANDLE himage
= NULL
;
402 TRACE("%p %s %u %u %08x\n", db
, debugstr_w(name
), cx
, cy
, flags
);
404 tmp
= msi_create_tmp_path();
408 rec
= msi_get_binary_record( db
, name
);
411 r
= MSI_RecordStreamToFile( rec
, 2, tmp
);
412 if( r
== ERROR_SUCCESS
)
414 himage
= LoadImageW( 0, tmp
, type
, cx
, cy
, flags
);
417 msiobj_release( &rec
->hdr
);
424 static HICON
msi_load_icon( MSIDATABASE
*db
, LPCWSTR text
, UINT attributes
)
426 DWORD cx
= 0, cy
= 0, flags
;
428 flags
= LR_LOADFROMFILE
| LR_DEFAULTSIZE
;
429 if( attributes
& msidbControlAttributesFixedSize
)
431 flags
&= ~LR_DEFAULTSIZE
;
432 if( attributes
& msidbControlAttributesIconSize16
)
437 if( attributes
& msidbControlAttributesIconSize32
)
442 /* msidbControlAttributesIconSize48 handled by above logic */
444 return msi_load_image( db
, text
, IMAGE_ICON
, cx
, cy
, flags
);
448 /* called from the Control Event subscription code */
449 void msi_dialog_handle_event( msi_dialog
* dialog
, LPCWSTR control
,
450 LPCWSTR attribute
, MSIRECORD
*rec
)
455 ctrl
= msi_dialog_find_control( dialog
, control
);
458 if( lstrcmpW(attribute
, szText
) )
460 text
= MSI_RecordGetString( rec
, 1 );
461 SetWindowTextW( ctrl
->hwnd
, text
);
462 msi_dialog_check_messages( NULL
);
465 static void msi_dialog_map_events(msi_dialog
* dialog
, LPCWSTR control
)
467 static WCHAR Query
[] = {
468 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
469 '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ',
470 'W','H','E','R','E',' ',
471 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
473 '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0
476 LPCWSTR event
, attribute
;
478 row
= MSI_QueryGetRecord( dialog
->package
->db
, Query
, dialog
->name
, control
);
482 event
= MSI_RecordGetString( row
, 3 );
483 attribute
= MSI_RecordGetString( row
, 4 );
484 ControlEvent_SubscribeToEvent( dialog
->package
, event
, control
, attribute
);
485 msiobj_release( &row
->hdr
);
488 /* everything except radio buttons */
489 static msi_control
*msi_dialog_add_control( msi_dialog
*dialog
,
490 MSIRECORD
*rec
, LPCWSTR szCls
, DWORD style
)
495 name
= MSI_RecordGetString( rec
, 2 );
496 attributes
= MSI_RecordGetInteger( rec
, 8 );
497 text
= MSI_RecordGetString( rec
, 10 );
498 if( attributes
& msidbControlAttributesVisible
)
500 if( ~attributes
& msidbControlAttributesEnabled
)
501 style
|= WS_DISABLED
;
503 msi_dialog_map_events(dialog
, name
);
505 return msi_dialog_create_window( dialog
, rec
, szCls
, name
, text
,
506 style
, dialog
->hwnd
);
516 * we don't erase our own background,
517 * so we have to make sure that the parent window redraws first
519 static void msi_text_on_settext( HWND hWnd
)
524 hParent
= GetParent( hWnd
);
525 GetWindowRect( hWnd
, &rc
);
526 MapWindowPoints( NULL
, hParent
, (LPPOINT
) &rc
, 2 );
527 InvalidateRect( hParent
, &rc
, TRUE
);
530 static LRESULT WINAPI
531 MSIText_WndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
533 struct msi_text_info
*info
;
536 TRACE("%p %04x %08x %08lx\n", hWnd
, msg
, wParam
, lParam
);
538 info
= GetPropW(hWnd
, szButtonData
);
540 if( msg
== WM_CTLCOLORSTATIC
&&
541 ( info
->attributes
& msidbControlAttributesTransparent
) )
543 SetBkMode( (HDC
)wParam
, TRANSPARENT
);
544 return (LRESULT
) GetStockObject(NULL_BRUSH
);
547 r
= CallWindowProcW(info
->oldproc
, hWnd
, msg
, wParam
, lParam
);
552 msi_text_on_settext( hWnd
);
556 RemovePropW( hWnd
, szButtonData
);
563 static UINT
msi_dialog_text_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
565 msi_control
*control
;
566 struct msi_text_info
*info
;
568 TRACE("%p %p\n", dialog
, rec
);
570 control
= msi_dialog_add_control( dialog
, rec
, szStatic
, SS_LEFT
| WS_GROUP
);
572 return ERROR_FUNCTION_FAILED
;
574 info
= msi_alloc( sizeof *info
);
576 return ERROR_SUCCESS
;
578 info
->attributes
= MSI_RecordGetInteger( rec
, 8 );
579 if( info
->attributes
& msidbControlAttributesTransparent
)
580 SetWindowLongPtrW( control
->hwnd
, GWL_EXSTYLE
, WS_EX_TRANSPARENT
);
582 info
->oldproc
= (WNDPROC
) SetWindowLongPtrW( control
->hwnd
, GWLP_WNDPROC
,
583 (LONG_PTR
)MSIText_WndProc
);
584 SetPropW( control
->hwnd
, szButtonData
, info
);
586 return ERROR_SUCCESS
;
589 static UINT
msi_dialog_button_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
591 msi_control
*control
;
592 UINT attributes
, style
;
595 TRACE("%p %p\n", dialog
, rec
);
598 attributes
= MSI_RecordGetInteger( rec
, 8 );
599 if( attributes
& msidbControlAttributesIcon
)
602 control
= msi_dialog_add_control( dialog
, rec
, szButton
, style
);
604 return ERROR_FUNCTION_FAILED
;
606 control
->handler
= msi_dialog_button_handler
;
609 text
= msi_get_deformatted_field( dialog
->package
, rec
, 10 );
610 control
->hIcon
= msi_load_icon( dialog
->package
->db
, text
, attributes
);
611 if( attributes
& msidbControlAttributesIcon
)
612 SendMessageW( control
->hwnd
, BM_SETIMAGE
, IMAGE_ICON
, (LPARAM
) control
->hIcon
);
615 return ERROR_SUCCESS
;
618 static LPWSTR
msi_get_checkbox_value( msi_dialog
*dialog
, LPCWSTR prop
)
620 const static WCHAR query
[] = {
621 'S','E','L','E','C','T',' ','*',' ',
622 'F','R','O','M',' ','`','C','h','e','c','k','B','o','x',' ','`',
623 'W','H','E','R','E',' ',
624 '`','P','r','o','p','e','r','t','y','`',' ','=',' ',
627 MSIRECORD
*rec
= NULL
;
630 /* find if there is a value associated with the checkbox */
631 rec
= MSI_QueryGetRecord( dialog
->package
->db
, query
, prop
);
635 ret
= msi_get_deformatted_field( dialog
->package
, rec
, 2 );
641 msiobj_release( &rec
->hdr
);
645 ret
= msi_dup_property( dialog
->package
, prop
);
655 static UINT
msi_dialog_checkbox_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
657 msi_control
*control
;
660 TRACE("%p %p\n", dialog
, rec
);
662 control
= msi_dialog_add_control( dialog
, rec
, szButton
,
663 BS_CHECKBOX
| BS_MULTILINE
| WS_TABSTOP
);
664 control
->handler
= msi_dialog_checkbox_handler
;
665 prop
= MSI_RecordGetString( rec
, 9 );
668 control
->property
= strdupW( prop
);
669 control
->value
= msi_get_checkbox_value( dialog
, prop
);
670 TRACE("control %s value %s\n", debugstr_w(control
->property
),
671 debugstr_w(control
->value
));
673 msi_dialog_checkbox_sync_state( dialog
, control
);
675 return ERROR_SUCCESS
;
678 static UINT
msi_dialog_line_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
680 TRACE("%p %p\n", dialog
, rec
);
682 msi_dialog_add_control( dialog
, rec
, szStatic
, SS_ETCHEDHORZ
| SS_SUNKEN
);
683 return ERROR_SUCCESS
;
686 struct msi_streamin_info
693 static DWORD CALLBACK
694 msi_richedit_stream_in( DWORD_PTR arg
, LPBYTE buffer
, LONG count
, LONG
*pcb
)
696 struct msi_streamin_info
*info
= (struct msi_streamin_info
*) arg
;
698 if( (count
+ info
->offset
) > info
->length
)
699 count
= info
->length
- info
->offset
;
700 memcpy( buffer
, &info
->string
[ info
->offset
], count
);
702 info
->offset
+= count
;
704 TRACE("%ld/%ld\n", info
->offset
, info
->length
);
709 static UINT
msi_dialog_scrolltext_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
711 const static WCHAR szRichEdit20W
[] = {
712 'R','i','c','h','E','d','i','t','2','0','W',0
714 struct msi_streamin_info info
;
715 msi_control
*control
;
721 hRichedit
= LoadLibraryA("riched20");
723 style
= WS_BORDER
| ES_MULTILINE
| WS_VSCROLL
|
724 ES_READONLY
| ES_AUTOVSCROLL
| WS_TABSTOP
;
725 control
= msi_dialog_add_control( dialog
, rec
, szRichEdit20W
, style
);
727 return ERROR_FUNCTION_FAILED
;
729 control
->hDll
= hRichedit
;
731 text
= MSI_RecordGetString( rec
, 10 );
732 info
.string
= strdupWtoA( text
);
734 info
.length
= lstrlenA( info
.string
) + 1;
736 es
.dwCookie
= (DWORD_PTR
) &info
;
738 es
.pfnCallback
= msi_richedit_stream_in
;
740 SendMessageW( control
->hwnd
, EM_STREAMIN
, SF_RTF
, (LPARAM
) &es
);
742 msi_free( info
.string
);
744 return ERROR_SUCCESS
;
747 static UINT
msi_dialog_bitmap_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
749 UINT cx
, cy
, flags
, style
, attributes
;
750 msi_control
*control
;
753 flags
= LR_LOADFROMFILE
;
754 style
= SS_BITMAP
| SS_LEFT
| WS_GROUP
;
756 attributes
= MSI_RecordGetInteger( rec
, 8 );
757 if( attributes
& msidbControlAttributesFixedSize
)
759 flags
|= LR_DEFAULTSIZE
;
760 style
|= SS_CENTERIMAGE
;
763 control
= msi_dialog_add_control( dialog
, rec
, szStatic
, style
);
764 cx
= MSI_RecordGetInteger( rec
, 6 );
765 cy
= MSI_RecordGetInteger( rec
, 7 );
766 cx
= msi_dialog_scale_unit( dialog
, cx
);
767 cy
= msi_dialog_scale_unit( dialog
, cy
);
769 text
= msi_get_deformatted_field( dialog
->package
, rec
, 10 );
770 control
->hBitmap
= msi_load_image( dialog
->package
->db
, text
,
771 IMAGE_BITMAP
, cx
, cy
, flags
);
772 if( control
->hBitmap
)
773 SendMessageW( control
->hwnd
, STM_SETIMAGE
,
774 IMAGE_BITMAP
, (LPARAM
) control
->hBitmap
);
776 ERR("Failed to load bitmap %s\n", debugstr_w(text
));
780 return ERROR_SUCCESS
;
783 static UINT
msi_dialog_icon_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
785 msi_control
*control
;
791 control
= msi_dialog_add_control( dialog
, rec
, szStatic
,
792 SS_ICON
| SS_CENTERIMAGE
| WS_GROUP
);
794 attributes
= MSI_RecordGetInteger( rec
, 8 );
795 text
= msi_get_deformatted_field( dialog
->package
, rec
, 10 );
796 control
->hIcon
= msi_load_icon( dialog
->package
->db
, text
, attributes
);
798 SendMessageW( control
->hwnd
, STM_SETICON
, (WPARAM
) control
->hIcon
, 0 );
800 ERR("Failed to load bitmap %s\n", debugstr_w(text
));
802 return ERROR_SUCCESS
;
805 static UINT
msi_dialog_combo_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
807 static const WCHAR szCombo
[] = { 'C','O','M','B','O','B','O','X',0 };
809 msi_dialog_add_control( dialog
, rec
, szCombo
,
810 SS_BITMAP
| SS_LEFT
| SS_CENTERIMAGE
);
811 return ERROR_SUCCESS
;
814 static UINT
msi_dialog_edit_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
816 msi_control
*control
;
820 control
= msi_dialog_add_control( dialog
, rec
, szEdit
,
821 WS_BORDER
| WS_TABSTOP
);
822 control
->handler
= msi_dialog_edit_handler
;
823 prop
= MSI_RecordGetString( rec
, 9 );
825 control
->property
= strdupW( prop
);
826 val
= msi_dup_property( dialog
->package
, control
->property
);
827 SetWindowTextW( control
->hwnd
, val
);
829 return ERROR_SUCCESS
;
832 /******************** Masked Edit ********************************************/
834 #define MASK_MAX_GROUPS 10
836 struct msi_mask_group
844 struct msi_maskedit_info
852 struct msi_mask_group group
[MASK_MAX_GROUPS
];
855 static void msi_mask_control_change( struct msi_maskedit_info
*info
)
860 val
= msi_alloc( (info
->num_chars
+1)*sizeof(WCHAR
) );
861 for( i
=0, n
=0; i
<info
->num_groups
; i
++ )
863 if( (info
->group
[i
].len
+ n
) > info
->num_chars
)
865 ERR("can't fit control %d text into template\n",i
);
868 r
= GetWindowTextW( info
->group
[i
].hwnd
, &val
[n
], info
->group
[i
].len
+1 );
869 if( r
!= info
->group
[i
].len
)
874 TRACE("%d/%d controls were good\n", i
, info
->num_groups
);
876 if( i
== info
->num_groups
)
878 TRACE("Set property %s to %s\n",
879 debugstr_w(info
->prop
), debugstr_w(val
) );
880 CharUpperBuffW( val
, info
->num_chars
);
881 MSI_SetPropertyW( info
->dialog
->package
, info
->prop
, val
);
882 msi_dialog_evaluate_control_conditions( info
->dialog
);
887 /* now move to the next control if necessary */
888 static VOID
msi_mask_next_control( struct msi_maskedit_info
*info
, HWND hWnd
)
893 for( i
=0; i
<info
->num_groups
; i
++ )
894 if( info
->group
[i
].hwnd
== hWnd
)
897 /* don't move from the last control */
898 if( i
>= (info
->num_groups
-1) )
901 len
= SendMessageW( hWnd
, WM_GETTEXTLENGTH
, 0, 0 );
902 if( len
< info
->group
[i
].len
)
905 hWndNext
= GetNextDlgTabItem( GetParent( hWnd
), hWnd
, FALSE
);
906 SetFocus( hWndNext
);
909 static LRESULT WINAPI
910 MSIMaskedEdit_WndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
912 struct msi_maskedit_info
*info
;
915 TRACE("%p %04x %08x %08lx\n", hWnd
, msg
, wParam
, lParam
);
917 info
= GetPropW(hWnd
, szButtonData
);
919 r
= CallWindowProcW(info
->oldproc
, hWnd
, msg
, wParam
, lParam
);
924 if (HIWORD(wParam
) == EN_CHANGE
)
926 msi_mask_control_change( info
);
927 msi_mask_next_control( info
, (HWND
) lParam
);
931 msi_free( info
->prop
);
933 RemovePropW( hWnd
, szButtonData
);
940 /* fish the various bits of the property out and put them in the control */
942 msi_maskedit_set_text( struct msi_maskedit_info
*info
, LPCWSTR text
)
948 for( i
= 0; i
< info
->num_groups
; i
++ )
950 if( info
->group
[i
].len
< lstrlenW( p
) )
952 LPWSTR chunk
= strdupW( p
);
953 chunk
[ info
->group
[i
].len
] = 0;
954 SetWindowTextW( info
->group
[i
].hwnd
, chunk
);
959 SetWindowTextW( info
->group
[i
].hwnd
, p
);
962 p
+= info
->group
[i
].len
;
966 static struct msi_maskedit_info
* msi_dialog_parse_groups( LPCWSTR mask
)
968 struct msi_maskedit_info
* info
= NULL
;
969 int i
= 0, n
= 0, total
= 0;
972 TRACE("masked control, template %s\n", debugstr_w(mask
));
977 p
= strchrW(mask
, '<');
981 info
= msi_alloc_zero( sizeof *info
);
986 for( i
=0; i
<MASK_MAX_GROUPS
; i
++ )
994 /* stop at the end of the string */
995 if( p
[0] == 0 || p
[0] == '>' )
998 /* count the number of the same identifier */
999 for( n
=0; p
[n
] == p
[0]; n
++ )
1001 info
->group
[i
].ofs
= total
;
1002 info
->group
[i
].type
= p
[0];
1006 total
++; /* an extra not part of the group */
1008 info
->group
[i
].len
= n
;
1013 TRACE("%d characters in %d groups\n", total
, i
);
1014 if( i
== MASK_MAX_GROUPS
)
1015 ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask
));
1017 info
->num_chars
= total
;
1018 info
->num_groups
= i
;
1024 msi_maskedit_create_children( struct msi_maskedit_info
*info
, LPCWSTR font
)
1026 DWORD width
, height
, style
, wx
, ww
;
1031 style
= WS_CHILD
| WS_BORDER
| WS_VISIBLE
| WS_TABSTOP
;
1033 GetClientRect( info
->hwnd
, &rect
);
1035 width
= rect
.right
- rect
.left
;
1036 height
= rect
.bottom
- rect
.top
;
1038 for( i
= 0; i
< info
->num_groups
; i
++ )
1040 wx
= (info
->group
[i
].ofs
* width
) / info
->num_chars
;
1041 ww
= (info
->group
[i
].len
* width
) / info
->num_chars
;
1043 hwnd
= CreateWindowW( szEdit
, NULL
, style
, wx
, 0, ww
, height
,
1044 info
->hwnd
, NULL
, NULL
, NULL
);
1047 ERR("failed to create mask edit sub window\n");
1051 SendMessageW( hwnd
, EM_LIMITTEXT
, info
->group
[i
].len
, 0 );
1053 msi_dialog_set_font( info
->dialog
, hwnd
,
1054 font
?font
:info
->dialog
->default_font
);
1055 info
->group
[i
].hwnd
= hwnd
;
1059 /* office 2003 uses "73931<````=````=````=````=`````>@@@@@" */
1060 static UINT
msi_dialog_maskedit_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
1062 LPWSTR font_mask
, val
= NULL
, font
;
1063 struct msi_maskedit_info
*info
= NULL
;
1064 UINT ret
= ERROR_SUCCESS
;
1065 msi_control
*control
;
1070 font_mask
= msi_get_deformatted_field( dialog
->package
, rec
, 10 );
1071 font
= msi_dialog_get_style( font_mask
, &mask
);
1074 ERR("mask template is empty\n");
1078 info
= msi_dialog_parse_groups( mask
);
1081 ERR("template %s is invalid\n", debugstr_w(mask
));
1085 info
->dialog
= dialog
;
1087 control
= msi_dialog_add_control( dialog
, rec
, szStatic
,
1088 SS_OWNERDRAW
| WS_GROUP
| WS_VISIBLE
);
1091 ERR("Failed to create maskedit container\n");
1092 ret
= ERROR_FUNCTION_FAILED
;
1095 SetWindowLongPtrW( control
->hwnd
, GWL_EXSTYLE
, WS_EX_CONTROLPARENT
);
1097 info
->hwnd
= control
->hwnd
;
1099 /* subclass the static control */
1100 info
->oldproc
= (WNDPROC
) SetWindowLongPtrW( info
->hwnd
, GWLP_WNDPROC
,
1101 (LONG_PTR
)MSIMaskedEdit_WndProc
);
1102 SetPropW( control
->hwnd
, szButtonData
, info
);
1104 prop
= MSI_RecordGetString( rec
, 9 );
1106 info
->prop
= strdupW( prop
);
1108 msi_maskedit_create_children( info
, font
);
1112 val
= msi_dup_property( dialog
->package
, prop
);
1115 msi_maskedit_set_text( info
, val
);
1121 if( ret
!= ERROR_SUCCESS
)
1123 msi_free( font_mask
);
1128 /******************** Path Edit ********************************************/
1130 static UINT
msi_dialog_pathedit_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
1132 FIXME("not implemented properly\n");
1133 return msi_dialog_edit_control( dialog
, rec
);
1136 /* radio buttons are a bit different from normal controls */
1137 static UINT
msi_dialog_create_radiobutton( MSIRECORD
*rec
, LPVOID param
)
1139 radio_button_group_descr
*group
= (radio_button_group_descr
*)param
;
1140 msi_dialog
*dialog
= group
->dialog
;
1141 msi_control
*control
;
1142 LPCWSTR prop
, text
, name
;
1143 DWORD style
, attributes
= group
->attributes
;
1145 style
= WS_CHILD
| BS_AUTORADIOBUTTON
| BS_MULTILINE
| WS_TABSTOP
;
1146 name
= MSI_RecordGetString( rec
, 3 );
1147 text
= MSI_RecordGetString( rec
, 8 );
1148 if( attributes
& 1 )
1149 style
|= WS_VISIBLE
;
1150 if( ~attributes
& 2 )
1151 style
|= WS_DISABLED
;
1153 control
= msi_dialog_create_window( dialog
, rec
, szButton
, name
, text
,
1154 style
, group
->parent
->hwnd
);
1156 return ERROR_FUNCTION_FAILED
;
1157 control
->handler
= msi_dialog_radiogroup_handler
;
1159 prop
= MSI_RecordGetString( rec
, 1 );
1161 control
->property
= strdupW( prop
);
1163 return ERROR_SUCCESS
;
1166 static UINT
msi_dialog_radiogroup_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
1168 static const WCHAR query
[] = {
1169 'S','E','L','E','C','T',' ','*',' ',
1170 'F','R','O','M',' ','R','a','d','i','o','B','u','t','t','o','n',' ',
1171 'W','H','E','R','E',' ',
1172 '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
1175 msi_control
*control
;
1176 MSIQUERY
*view
= NULL
;
1177 radio_button_group_descr group
;
1178 MSIPACKAGE
*package
= dialog
->package
;
1181 prop
= MSI_RecordGetString( rec
, 9 );
1183 TRACE("%p %p %s\n", dialog
, rec
, debugstr_w( prop
));
1185 /* Create parent group box to hold radio buttons */
1186 control
= msi_dialog_add_control( dialog
, rec
, szButton
, BS_OWNERDRAW
|WS_GROUP
);
1188 return ERROR_FUNCTION_FAILED
;
1190 oldproc
= (WNDPROC
) SetWindowLongPtrW( control
->hwnd
, GWLP_WNDPROC
,
1191 (LONG_PTR
)MSIRadioGroup_WndProc
);
1192 SetPropW(control
->hwnd
, szButtonData
, oldproc
);
1193 SetWindowLongPtrW( control
->hwnd
, GWL_EXSTYLE
, WS_EX_CONTROLPARENT
);
1196 control
->property
= strdupW( prop
);
1198 /* query the Radio Button table for all control in this group */
1199 r
= MSI_OpenQuery( package
->db
, &view
, query
, prop
);
1200 if( r
!= ERROR_SUCCESS
)
1202 ERR("query failed for dialog %s radio group %s\n",
1203 debugstr_w(dialog
->name
), debugstr_w(prop
));
1204 return ERROR_INVALID_PARAMETER
;
1207 group
.dialog
= dialog
;
1208 group
.parent
= control
;
1209 group
.attributes
= MSI_RecordGetInteger( rec
, 8 );
1211 r
= MSI_IterateRecords( view
, 0, msi_dialog_create_radiobutton
, &group
);
1212 msiobj_release( &view
->hdr
);
1217 struct control_handler msi_dialog_handler
[] =
1219 { szText
, msi_dialog_text_control
},
1220 { szPushButton
, msi_dialog_button_control
},
1221 { szLine
, msi_dialog_line_control
},
1222 { szBitmap
, msi_dialog_bitmap_control
},
1223 { szCheckBox
, msi_dialog_checkbox_control
},
1224 { szScrollableText
, msi_dialog_scrolltext_control
},
1225 { szComboBox
, msi_dialog_combo_control
},
1226 { szEdit
, msi_dialog_edit_control
},
1227 { szMaskedEdit
, msi_dialog_maskedit_control
},
1228 { szPathEdit
, msi_dialog_pathedit_control
},
1229 { szRadioButtonGroup
, msi_dialog_radiogroup_control
},
1230 { szIcon
, msi_dialog_icon_control
},
1233 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
1235 static UINT
msi_dialog_create_controls( MSIRECORD
*rec
, LPVOID param
)
1237 msi_dialog
*dialog
= param
;
1238 LPCWSTR control_type
;
1241 /* find and call the function that can create this type of control */
1242 control_type
= MSI_RecordGetString( rec
, 3 );
1243 for( i
=0; i
<NUM_CONTROL_TYPES
; i
++ )
1244 if (!strcmpiW( msi_dialog_handler
[i
].control_type
, control_type
))
1246 if( i
!= NUM_CONTROL_TYPES
)
1247 msi_dialog_handler
[i
].func( dialog
, rec
);
1249 ERR("no handler for element type %s\n", debugstr_w(control_type
));
1251 return ERROR_SUCCESS
;
1254 static UINT
msi_dialog_fill_controls( msi_dialog
*dialog
)
1256 static const WCHAR query
[] = {
1257 'S','E','L','E','C','T',' ','*',' ',
1258 'F','R','O','M',' ','C','o','n','t','r','o','l',' ',
1259 'W','H','E','R','E',' ',
1260 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
1262 MSIQUERY
*view
= NULL
;
1263 MSIPACKAGE
*package
= dialog
->package
;
1265 TRACE("%p %s\n", dialog
, debugstr_w(dialog
->name
) );
1267 /* query the Control table for all the elements of the control */
1268 r
= MSI_OpenQuery( package
->db
, &view
, query
, dialog
->name
);
1269 if( r
!= ERROR_SUCCESS
)
1271 ERR("query failed for dialog %s\n", debugstr_w(dialog
->name
));
1272 return ERROR_INVALID_PARAMETER
;
1275 r
= MSI_IterateRecords( view
, 0, msi_dialog_create_controls
, dialog
);
1276 msiobj_release( &view
->hdr
);
1281 static UINT
msi_dialog_set_control_condition( MSIRECORD
*rec
, LPVOID param
)
1283 static const WCHAR szHide
[] = { 'H','i','d','e',0 };
1284 static const WCHAR szShow
[] = { 'S','h','o','w',0 };
1285 static const WCHAR szDisable
[] = { 'D','i','s','a','b','l','e',0 };
1286 static const WCHAR szEnable
[] = { 'E','n','a','b','l','e',0 };
1287 msi_dialog
*dialog
= param
;
1288 msi_control
*control
;
1289 LPCWSTR name
, action
, condition
;
1292 name
= MSI_RecordGetString( rec
, 2 );
1293 action
= MSI_RecordGetString( rec
, 3 );
1294 condition
= MSI_RecordGetString( rec
, 4 );
1295 r
= MSI_EvaluateConditionW( dialog
->package
, condition
);
1296 control
= msi_dialog_find_control( dialog
, name
);
1299 TRACE("%s control %s\n", debugstr_w(action
), debugstr_w(name
));
1301 /* FIXME: case sensitive? */
1302 if(!lstrcmpW(action
, szHide
))
1303 ShowWindow(control
->hwnd
, SW_HIDE
);
1304 else if(!strcmpW(action
, szShow
))
1305 ShowWindow(control
->hwnd
, SW_SHOW
);
1306 else if(!strcmpW(action
, szDisable
))
1307 EnableWindow(control
->hwnd
, FALSE
);
1308 else if(!strcmpW(action
, szEnable
))
1309 EnableWindow(control
->hwnd
, TRUE
);
1311 FIXME("Unhandled action %s\n", debugstr_w(action
));
1314 return ERROR_SUCCESS
;
1317 static UINT
msi_dialog_evaluate_control_conditions( msi_dialog
*dialog
)
1319 static const WCHAR query
[] = {
1320 'S','E','L','E','C','T',' ','*',' ',
1321 'F','R','O','M',' ',
1322 'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
1323 'W','H','E','R','E',' ',
1324 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0
1327 MSIQUERY
*view
= NULL
;
1328 MSIPACKAGE
*package
= dialog
->package
;
1330 TRACE("%p %s\n", dialog
, debugstr_w(dialog
->name
) );
1332 /* query the Control table for all the elements of the control */
1333 r
= MSI_OpenQuery( package
->db
, &view
, query
, dialog
->name
);
1334 if( r
!= ERROR_SUCCESS
)
1336 ERR("query failed for dialog %s\n", debugstr_w(dialog
->name
));
1337 return ERROR_INVALID_PARAMETER
;
1340 r
= MSI_IterateRecords( view
, 0, msi_dialog_set_control_condition
, dialog
);
1341 msiobj_release( &view
->hdr
);
1346 /* figure out the height of 10 point MS Sans Serif */
1347 static INT
msi_dialog_get_sans_serif_height( HWND hwnd
)
1349 static const WCHAR szSansSerif
[] = {
1350 'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
1355 HFONT hFont
, hOldFont
;
1358 hdc
= GetDC( hwnd
);
1361 memset( &lf
, 0, sizeof lf
);
1362 lf
.lfHeight
= MulDiv(10, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1363 strcpyW( lf
.lfFaceName
, szSansSerif
);
1364 hFont
= CreateFontIndirectW(&lf
);
1367 hOldFont
= SelectObject( hdc
, hFont
);
1368 r
= GetTextMetricsW( hdc
, &tm
);
1370 height
= tm
.tmHeight
;
1371 SelectObject( hdc
, hOldFont
);
1372 DeleteObject( hFont
);
1374 ReleaseDC( hwnd
, hdc
);
1379 /* fetch the associated record from the Dialog table */
1380 static MSIRECORD
*msi_get_dialog_record( msi_dialog
*dialog
)
1382 static const WCHAR query
[] = {
1383 'S','E','L','E','C','T',' ','*',' ',
1384 'F','R','O','M',' ','D','i','a','l','o','g',' ',
1385 'W','H','E','R','E',' ',
1386 '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
1387 MSIPACKAGE
*package
= dialog
->package
;
1388 MSIRECORD
*rec
= NULL
;
1390 TRACE("%p %s\n", dialog
, debugstr_w(dialog
->name
) );
1392 rec
= MSI_QueryGetRecord( package
->db
, query
, dialog
->name
);
1394 ERR("query failed for dialog %s\n", debugstr_w(dialog
->name
));
1399 static void msi_dialog_adjust_dialog_size( msi_dialog
*dialog
, LPSIZE sz
)
1404 /* turn the client size into the window rectangle */
1407 rect
.right
= msi_dialog_scale_unit( dialog
, sz
->cx
);
1408 rect
.bottom
= msi_dialog_scale_unit( dialog
, sz
->cy
);
1409 style
= GetWindowLongPtrW( dialog
->hwnd
, GWL_STYLE
);
1410 AdjustWindowRect( &rect
, style
, FALSE
);
1411 sz
->cx
= rect
.right
- rect
.left
;
1412 sz
->cy
= rect
.bottom
- rect
.top
;
1415 static BOOL
msi_control_set_next( msi_control
*control
, msi_control
*next
)
1417 return SetWindowPos( next
->hwnd
, control
->hwnd
, 0, 0, 0, 0,
1418 SWP_NOMOVE
| SWP_NOOWNERZORDER
| SWP_NOREDRAW
|
1419 SWP_NOREPOSITION
| SWP_NOSENDCHANGING
| SWP_NOSIZE
);
1422 static UINT
msi_dialog_set_tab_order( msi_dialog
*dialog
)
1424 msi_control
*control
, *tab_next
;
1426 LIST_FOR_EACH_ENTRY( control
, &dialog
->controls
, msi_control
, entry
)
1428 tab_next
= msi_dialog_find_control( dialog
, control
->tabnext
);
1431 msi_control_set_next( control
, tab_next
);
1434 return ERROR_SUCCESS
;
1437 static void msi_dialog_set_first_control( msi_dialog
* dialog
, LPCWSTR name
)
1439 msi_control
*control
;
1441 control
= msi_dialog_find_control( dialog
, name
);
1443 dialog
->hWndFocus
= control
->hwnd
;
1445 dialog
->hWndFocus
= NULL
;
1448 static LRESULT
msi_dialog_oncreate( HWND hwnd
, LPCREATESTRUCTW cs
)
1450 static const WCHAR df
[] = {
1451 'D','e','f','a','u','l','t','U','I','F','o','n','t',0 };
1452 msi_dialog
*dialog
= (msi_dialog
*) cs
->lpCreateParams
;
1453 MSIRECORD
*rec
= NULL
;
1454 LPWSTR title
= NULL
;
1457 TRACE("%p %p\n", dialog
, dialog
->package
);
1459 dialog
->hwnd
= hwnd
;
1460 SetWindowLongPtrW( hwnd
, GWLP_USERDATA
, (LONG_PTR
) dialog
);
1462 rec
= msi_get_dialog_record( dialog
);
1465 TRACE("No record found for dialog %s\n", debugstr_w(dialog
->name
));
1469 dialog
->scale
= msi_dialog_get_sans_serif_height(dialog
->hwnd
);
1471 size
.cx
= MSI_RecordGetInteger( rec
, 4 );
1472 size
.cy
= MSI_RecordGetInteger( rec
, 5 );
1473 msi_dialog_adjust_dialog_size( dialog
, &size
);
1475 dialog
->attributes
= MSI_RecordGetInteger( rec
, 6 );
1477 dialog
->default_font
= msi_dup_property( dialog
->package
, df
);
1479 title
= msi_get_deformatted_field( dialog
->package
, rec
, 7 );
1480 SetWindowTextW( hwnd
, title
);
1483 SetWindowPos( hwnd
, 0, 0, 0, size
.cx
, size
.cy
,
1484 SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_NOREDRAW
);
1487 msi_dialog_build_font_list( dialog
);
1488 msi_dialog_fill_controls( dialog
);
1489 msi_dialog_evaluate_control_conditions( dialog
);
1490 msi_dialog_set_tab_order( dialog
);
1491 msi_dialog_set_first_control( dialog
, MSI_RecordGetString( rec
, 8 ) );
1492 msiobj_release( &rec
->hdr
);
1497 static UINT
msi_dialog_send_event( msi_dialog
*dialog
, LPCWSTR event
, LPCWSTR arg
)
1499 LPWSTR event_fmt
= NULL
, arg_fmt
= NULL
;
1501 TRACE("Sending control event %s %s\n", debugstr_w(event
), debugstr_w(arg
));
1503 deformat_string( dialog
->package
, event
, &event_fmt
);
1504 deformat_string( dialog
->package
, arg
, &arg_fmt
);
1506 dialog
->event_handler( dialog
->package
, event_fmt
, arg_fmt
, dialog
);
1508 msi_free( event_fmt
);
1509 msi_free( arg_fmt
);
1511 return ERROR_SUCCESS
;
1514 static UINT
msi_dialog_set_property( msi_dialog
*dialog
, LPCWSTR event
, LPCWSTR arg
)
1516 static const WCHAR szNullArg
[] = { '{','}',0 };
1517 LPWSTR p
, prop
, arg_fmt
= NULL
;
1520 len
= strlenW(event
);
1521 prop
= msi_alloc( len
*sizeof(WCHAR
));
1522 strcpyW( prop
, &event
[1] );
1523 p
= strchrW( prop
, ']' );
1524 if( p
&& p
[1] == 0 )
1527 if( strcmpW( szNullArg
, arg
) )
1528 deformat_string( dialog
->package
, arg
, &arg_fmt
);
1529 MSI_SetPropertyW( dialog
->package
, prop
, arg_fmt
);
1530 msi_free( arg_fmt
);
1533 ERR("Badly formatted property string - what happens?\n");
1535 return ERROR_SUCCESS
;
1538 static UINT
msi_dialog_control_event( MSIRECORD
*rec
, LPVOID param
)
1540 msi_dialog
*dialog
= param
;
1541 LPCWSTR condition
, event
, arg
;
1544 condition
= MSI_RecordGetString( rec
, 5 );
1545 r
= MSI_EvaluateConditionW( dialog
->package
, condition
);
1548 event
= MSI_RecordGetString( rec
, 3 );
1549 arg
= MSI_RecordGetString( rec
, 4 );
1550 if( event
[0] == '[' )
1551 msi_dialog_set_property( dialog
, event
, arg
);
1553 msi_dialog_send_event( dialog
, event
, arg
);
1556 return ERROR_SUCCESS
;
1559 static UINT
msi_dialog_button_handler( msi_dialog
*dialog
,
1560 msi_control
*control
, WPARAM param
)
1562 static const WCHAR query
[] = {
1563 'S','E','L','E','C','T',' ','*',' ',
1564 'F','R','O','M',' ','C','o','n','t','r','o','l','E','v','e','n','t',' ',
1565 'W','H','E','R','E',' ',
1566 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
1568 '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
1569 'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0
1571 MSIQUERY
*view
= NULL
;
1574 if( HIWORD(param
) != BN_CLICKED
)
1575 return ERROR_SUCCESS
;
1577 r
= MSI_OpenQuery( dialog
->package
->db
, &view
, query
,
1578 dialog
->name
, control
->name
);
1579 if( r
!= ERROR_SUCCESS
)
1581 ERR("query failed\n");
1585 r
= MSI_IterateRecords( view
, 0, msi_dialog_control_event
, dialog
);
1586 msiobj_release( &view
->hdr
);
1591 static UINT
msi_dialog_get_checkbox_state( msi_dialog
*dialog
,
1592 msi_control
*control
)
1594 WCHAR state
[2] = { 0 };
1597 MSI_GetPropertyW( dialog
->package
, control
->property
, state
, &sz
);
1598 return state
[0] ? 1 : 0;
1601 static void msi_dialog_set_checkbox_state( msi_dialog
*dialog
,
1602 msi_control
*control
, UINT state
)
1604 static const WCHAR szState
[] = { '1', 0 };
1607 /* if uncheck then the property is set to NULL */
1610 MSI_SetPropertyW( dialog
->package
, control
->property
, NULL
);
1614 /* check for a custom state */
1615 if (control
->value
&& control
->value
[0])
1616 val
= control
->value
;
1620 MSI_SetPropertyW( dialog
->package
, control
->property
, val
);
1623 static void msi_dialog_checkbox_sync_state( msi_dialog
*dialog
,
1624 msi_control
*control
)
1628 state
= msi_dialog_get_checkbox_state( dialog
, control
);
1629 SendMessageW( control
->hwnd
, BM_SETCHECK
,
1630 state
? BST_CHECKED
: BST_UNCHECKED
, 0 );
1633 static UINT
msi_dialog_checkbox_handler( msi_dialog
*dialog
,
1634 msi_control
*control
, WPARAM param
)
1638 if( HIWORD(param
) != BN_CLICKED
)
1639 return ERROR_SUCCESS
;
1641 TRACE("clicked checkbox %s, set %s\n", debugstr_w(control
->name
),
1642 debugstr_w(control
->property
));
1644 state
= msi_dialog_get_checkbox_state( dialog
, control
);
1645 state
= state
? 0 : 1;
1646 msi_dialog_set_checkbox_state( dialog
, control
, state
);
1647 msi_dialog_checkbox_sync_state( dialog
, control
);
1649 return msi_dialog_button_handler( dialog
, control
, param
);
1652 static UINT
msi_dialog_edit_handler( msi_dialog
*dialog
,
1653 msi_control
*control
, WPARAM param
)
1658 if( HIWORD(param
) != EN_CHANGE
)
1659 return ERROR_SUCCESS
;
1661 TRACE("edit %s contents changed, set %s\n", debugstr_w(control
->name
),
1662 debugstr_w(control
->property
));
1665 buf
= msi_alloc( sz
*sizeof(WCHAR
) );
1668 r
= GetWindowTextW( control
->hwnd
, buf
, sz
);
1672 buf
= msi_realloc( buf
, sz
*sizeof(WCHAR
) );
1675 MSI_SetPropertyW( dialog
->package
, control
->property
, buf
);
1679 return ERROR_SUCCESS
;
1682 static UINT
msi_dialog_radiogroup_handler( msi_dialog
*dialog
,
1683 msi_control
*control
, WPARAM param
)
1685 if( HIWORD(param
) != BN_CLICKED
)
1686 return ERROR_SUCCESS
;
1688 TRACE("clicked radio button %s, set %s\n", debugstr_w(control
->name
),
1689 debugstr_w(control
->property
));
1691 MSI_SetPropertyW( dialog
->package
, control
->property
, control
->name
);
1693 return msi_dialog_button_handler( dialog
, control
, param
);
1696 static LRESULT
msi_dialog_oncommand( msi_dialog
*dialog
, WPARAM param
, HWND hwnd
)
1698 msi_control
*control
;
1700 TRACE("%p %p %08x\n", dialog
, hwnd
, param
);
1702 control
= msi_dialog_find_control_by_hwnd( dialog
, hwnd
);
1705 if( control
->handler
)
1707 control
->handler( dialog
, control
, param
);
1708 msi_dialog_evaluate_control_conditions( dialog
);
1712 ERR("button click from nowhere\n");
1716 static void msi_dialog_setfocus( msi_dialog
*dialog
)
1718 HWND hwnd
= dialog
->hWndFocus
;
1720 hwnd
= GetNextDlgTabItem( dialog
->hwnd
, hwnd
, TRUE
);
1721 hwnd
= GetNextDlgTabItem( dialog
->hwnd
, hwnd
, FALSE
);
1723 dialog
->hWndFocus
= hwnd
;
1726 static LRESULT WINAPI
MSIDialog_WndProc( HWND hwnd
, UINT msg
,
1727 WPARAM wParam
, LPARAM lParam
)
1729 msi_dialog
*dialog
= (LPVOID
) GetWindowLongPtrW( hwnd
, GWLP_USERDATA
);
1731 TRACE("0x%04x\n", msg
);
1736 return msi_dialog_oncreate( hwnd
, (LPCREATESTRUCTW
)lParam
);
1739 return msi_dialog_oncommand( dialog
, wParam
, (HWND
)lParam
);
1742 if( LOWORD(wParam
) == WA_INACTIVE
)
1743 dialog
->hWndFocus
= GetFocus();
1745 msi_dialog_setfocus( dialog
);
1749 msi_dialog_setfocus( dialog
);
1752 /* bounce back to our subclassed static control */
1753 case WM_CTLCOLORSTATIC
:
1754 return SendMessageW( (HWND
) lParam
, WM_CTLCOLORSTATIC
, wParam
, lParam
);
1757 dialog
->hwnd
= NULL
;
1760 return DefWindowProcW(hwnd
, msg
, wParam
, lParam
);
1763 static LRESULT WINAPI
MSIRadioGroup_WndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1765 WNDPROC oldproc
= (WNDPROC
) GetPropW(hWnd
, szButtonData
);
1767 TRACE("hWnd %p msg %04x wParam 0x%08x lParam 0x%08lx\n", hWnd
, msg
, wParam
, lParam
);
1769 if (msg
== WM_COMMAND
) /* Forward notifications to dialog */
1770 SendMessageW(GetParent(hWnd
), msg
, wParam
, lParam
);
1772 return CallWindowProcW(oldproc
, hWnd
, msg
, wParam
, lParam
);
1775 static LRESULT WINAPI
MSIHiddenWindowProc( HWND hwnd
, UINT msg
,
1776 WPARAM wParam
, LPARAM lParam
)
1778 msi_dialog
*dialog
= (msi_dialog
*) lParam
;
1780 TRACE("%d %p\n", msg
, dialog
);
1784 case WM_MSI_DIALOG_CREATE
:
1785 return msi_dialog_run_message_loop( dialog
);
1786 case WM_MSI_DIALOG_DESTROY
:
1787 msi_dialog_destroy( dialog
);
1790 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
1793 /* functions that interface to other modules within MSI */
1795 msi_dialog
*msi_dialog_create( MSIPACKAGE
* package
, LPCWSTR szDialogName
,
1796 msi_dialog_event_handler event_handler
)
1798 MSIRECORD
*rec
= NULL
;
1801 TRACE("%p %s\n", package
, debugstr_w(szDialogName
));
1803 /* allocate the structure for the dialog to use */
1804 dialog
= msi_alloc_zero( sizeof *dialog
+ sizeof(WCHAR
)*strlenW(szDialogName
) );
1807 strcpyW( dialog
->name
, szDialogName
);
1808 msiobj_addref( &package
->hdr
);
1809 dialog
->package
= package
;
1810 dialog
->event_handler
= event_handler
;
1811 dialog
->finished
= 0;
1812 list_init( &dialog
->controls
);
1814 /* verify that the dialog exists */
1815 rec
= msi_get_dialog_record( dialog
);
1818 msiobj_release( &package
->hdr
);
1822 dialog
->attributes
= MSI_RecordGetInteger( rec
, 6 );
1823 msiobj_release( &rec
->hdr
);
1828 static void msi_process_pending_messages( HWND hdlg
)
1832 while( PeekMessageW( &msg
, 0, 0, 0, PM_REMOVE
) )
1834 if( hdlg
&& IsDialogMessageW( hdlg
, &msg
))
1836 TranslateMessage( &msg
);
1837 DispatchMessageW( &msg
);
1841 void msi_dialog_end_dialog( msi_dialog
*dialog
)
1843 TRACE("%p\n", dialog
);
1844 dialog
->finished
= 1;
1845 PostMessageW(dialog
->hwnd
, WM_NULL
, 0, 0);
1848 void msi_dialog_check_messages( HANDLE handle
)
1852 /* in threads other than the UI thread, block */
1853 if( uiThreadId
!= GetCurrentThreadId() )
1856 WaitForSingleObject( handle
, INFINITE
);
1860 /* there's two choices for the UI thread */
1863 msi_process_pending_messages( NULL
);
1869 * block here until somebody creates a new dialog or
1870 * the handle we're waiting on becomes ready
1872 r
= MsgWaitForMultipleObjects( 1, &handle
, 0, INFINITE
, QS_ALLINPUT
);
1873 if( r
== WAIT_OBJECT_0
)
1878 UINT
msi_dialog_run_message_loop( msi_dialog
*dialog
)
1882 if( !(dialog
->attributes
& msidbDialogAttributesVisible
) )
1883 return ERROR_SUCCESS
;
1885 if( uiThreadId
!= GetCurrentThreadId() )
1886 return SendMessageW( hMsiHiddenWindow
, WM_MSI_DIALOG_CREATE
, 0, (LPARAM
) dialog
);
1888 /* create the dialog window, don't show it yet */
1889 hwnd
= CreateWindowW( szMsiDialogClass
, dialog
->name
, WS_OVERLAPPEDWINDOW
,
1890 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
1891 NULL
, NULL
, NULL
, dialog
);
1894 ERR("Failed to create dialog %s\n", debugstr_w( dialog
->name
));
1895 return ERROR_FUNCTION_FAILED
;
1898 ShowWindow( hwnd
, SW_SHOW
);
1899 UpdateWindow( hwnd
);
1901 if( dialog
->attributes
& msidbDialogAttributesModal
)
1903 while( !dialog
->finished
)
1905 MsgWaitForMultipleObjects( 0, NULL
, 0, INFINITE
, QS_ALLEVENTS
);
1906 msi_process_pending_messages( dialog
->hwnd
);
1910 return ERROR_IO_PENDING
;
1912 return ERROR_SUCCESS
;
1915 void msi_dialog_do_preview( msi_dialog
*dialog
)
1918 dialog
->attributes
|= msidbDialogAttributesVisible
;
1919 dialog
->attributes
&= ~msidbDialogAttributesModal
;
1920 msi_dialog_run_message_loop( dialog
);
1923 void msi_dialog_destroy( msi_dialog
*dialog
)
1925 if( uiThreadId
!= GetCurrentThreadId() )
1927 SendMessageW( hMsiHiddenWindow
, WM_MSI_DIALOG_DESTROY
, 0, (LPARAM
) dialog
);
1932 ShowWindow( dialog
->hwnd
, SW_HIDE
);
1935 DestroyWindow( dialog
->hwnd
);
1937 /* destroy the list of controls */
1938 while( !list_empty( &dialog
->controls
) )
1940 msi_control
*t
= LIST_ENTRY( list_head( &dialog
->controls
),
1941 msi_control
, entry
);
1942 list_remove( &t
->entry
);
1943 /* leave dialog->hwnd - destroying parent destroys child windows */
1944 msi_free( t
->property
);
1945 msi_free( t
->value
);
1947 DeleteObject( t
->hBitmap
);
1949 DestroyIcon( t
->hIcon
);
1950 msi_free( t
->tabnext
);
1953 FreeLibrary( t
->hDll
);
1956 /* destroy the list of fonts */
1957 while( dialog
->font_list
)
1959 msi_font
*t
= dialog
->font_list
;
1960 dialog
->font_list
= t
->next
;
1961 DeleteObject( t
->hfont
);
1964 msi_free( dialog
->default_font
);
1966 msiobj_release( &dialog
->package
->hdr
);
1967 dialog
->package
= NULL
;
1971 BOOL
msi_dialog_register_class( void )
1975 ZeroMemory( &cls
, sizeof cls
);
1976 cls
.lpfnWndProc
= MSIDialog_WndProc
;
1977 cls
.hInstance
= NULL
;
1978 cls
.hIcon
= LoadIconW(0, (LPWSTR
)IDI_APPLICATION
);
1979 cls
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
1980 cls
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
1981 cls
.lpszMenuName
= NULL
;
1982 cls
.lpszClassName
= szMsiDialogClass
;
1984 if( !RegisterClassW( &cls
) )
1987 cls
.lpfnWndProc
= MSIHiddenWindowProc
;
1988 cls
.lpszClassName
= szMsiHiddenWindow
;
1990 if( !RegisterClassW( &cls
) )
1993 uiThreadId
= GetCurrentThreadId();
1995 hMsiHiddenWindow
= CreateWindowW( szMsiHiddenWindow
, NULL
, WS_OVERLAPPED
,
1996 0, 0, 100, 100, NULL
, NULL
, NULL
, NULL
);
1997 if( !hMsiHiddenWindow
)
2003 void msi_dialog_unregister_class( void )
2005 DestroyWindow( hMsiHiddenWindow
);
2006 UnregisterClassW( szMsiDialogClass
, NULL
);