Make the MSI icon control work.
[wine/wine64.git] / dlls / msi / dialog.c
blobe327f068eacad0aef41e7227d1afa7c9dfb83a13
1 /*
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
21 #define COBJMACROS
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "winnls.h"
29 #include "wingdi.h"
30 #include "msi.h"
31 #include "msipriv.h"
32 #include "msidefs.h"
33 #include "ocidl.h"
34 #include "olectl.h"
35 #include "richedit.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
40 #include "action.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
51 struct msi_control_tag *next;
52 HWND hwnd;
53 msi_handler handler;
54 LPWSTR property;
55 LPWSTR value;
56 IPicture *pic;
57 HICON hIcon;
58 WCHAR name[1];
61 typedef struct msi_font_tag
63 struct msi_font_tag *next;
64 HFONT hfont;
65 WCHAR name[1];
66 } msi_font;
68 struct msi_dialog_tag
70 MSIPACKAGE *package;
71 msi_dialog_event_handler event_handler;
72 BOOL finished;
73 INT scale;
74 DWORD attributes;
75 HWND hwnd;
76 LPWSTR default_font;
77 msi_font *font_list;
78 msi_control *control_list;
79 WCHAR name[1];
82 typedef UINT (*msi_dialog_control_func)( msi_dialog *dialog, MSIRECORD *rec );
83 struct control_handler
85 LPCWSTR control_type;
86 msi_dialog_control_func func;
89 typedef struct
91 msi_dialog* dialog;
92 msi_control *parent;
93 DWORD attributes;
94 } radio_button_group_descr;
96 const WCHAR szMsiDialogClass[] = {
97 'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0
99 const WCHAR szMsiHiddenWindow[] = {
100 'M','s','i','H','i','d','d','e','n','W','i','n','d','o','w',0 };
101 const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
102 const static WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
103 const static WCHAR szButtonData[] = { 'M','S','I','D','A','T','A',0 };
104 static const WCHAR szText[] = { 'T','e','x','t',0 };
105 static const WCHAR szPushButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
106 static const WCHAR szLine[] = { 'L','i','n','e',0 };
107 static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 };
108 static const WCHAR szCheckBox[] = { 'C','h','e','c','k','B','o','x',0 };
109 static const WCHAR szScrollableText[] = {
110 'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 };
111 static const WCHAR szComboBox[] = { 'C','o','m','b','o','B','o','x',0 };
112 static const WCHAR szEdit[] = { 'E','d','i','t',0 };
113 static const WCHAR szMaskedEdit[] = { 'M','a','s','k','e','d','E','d','i','t',0 };
114 static const WCHAR szPathEdit[] = { 'P','a','t','h','E','d','i','t',0 };
115 static const WCHAR szRadioButtonGroup[] = {
116 'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 };
117 static const WCHAR szIcon[] = { 'I','c','o','n',0 };
119 static UINT msi_dialog_checkbox_handler( msi_dialog *, msi_control *, WPARAM );
120 static void msi_dialog_checkbox_sync_state( msi_dialog *, msi_control * );
121 static UINT msi_dialog_button_handler( msi_dialog *, msi_control *, WPARAM );
122 static UINT msi_dialog_edit_handler( msi_dialog *, msi_control *, WPARAM );
123 static UINT msi_dialog_radiogroup_handler( msi_dialog *, msi_control *, WPARAM param );
124 static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog );
125 static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
128 /* dialog sequencing */
130 #define WM_MSI_DIALOG_CREATE (WM_USER+0x100)
131 #define WM_MSI_DIALOG_DESTROY (WM_USER+0x101)
133 static DWORD uiThreadId;
134 static HWND hMsiHiddenWindow;
135 static HMODULE hRichedit;
137 static INT msi_dialog_scale_unit( msi_dialog *dialog, INT val )
139 return (dialog->scale * val + 5) / 10;
142 static msi_control *msi_dialog_find_control( msi_dialog *dialog, LPCWSTR name )
144 msi_control *control;
146 for( control = dialog->control_list; control; control = control->next )
147 if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */
148 break;
149 return control;
152 static msi_control *msi_dialog_find_control_by_hwnd( msi_dialog *dialog, HWND hwnd )
154 msi_control *control;
156 for( control = dialog->control_list; control; control = control->next )
157 if( hwnd == control->hwnd )
158 break;
159 return control;
163 * msi_dialog_get_style
165 * Extract the {\style} string from the front of the text to display and
166 * update the pointer.
168 static LPWSTR msi_dialog_get_style( LPCWSTR *text )
170 LPWSTR ret = NULL;
171 LPCWSTR p = *text, q;
172 DWORD len;
174 if( *p++ != '{' )
175 return ret;
176 q = strchrW( p, '}' );
177 if( !q )
178 return ret;
179 *text = ++q;
180 if( *p++ != '\\' )
181 return ret;
182 len = q - p;
184 ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
185 if( !ret )
186 return ret;
187 memcpy( ret, p, len*sizeof(WCHAR) );
188 ret[len-1] = 0;
189 return ret;
192 static UINT msi_dialog_add_font( MSIRECORD *rec, LPVOID param )
194 msi_dialog *dialog = param;
195 msi_font *font;
196 LPCWSTR face, name;
197 LOGFONTW lf;
198 INT style;
199 HDC hdc;
201 /* create a font and add it to the list */
202 name = MSI_RecordGetString( rec, 1 );
203 font = HeapAlloc( GetProcessHeap(), 0,
204 sizeof *font + strlenW( name )*sizeof (WCHAR) );
205 strcpyW( font->name, name );
206 font->next = dialog->font_list;
207 dialog->font_list = font;
209 memset( &lf, 0, sizeof lf );
210 face = MSI_RecordGetString( rec, 2 );
211 lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
212 style = MSI_RecordGetInteger( rec, 5 );
213 if( style & msidbTextStyleStyleBitsBold )
214 lf.lfWeight = FW_BOLD;
215 if( style & msidbTextStyleStyleBitsItalic )
216 lf.lfItalic = TRUE;
217 if( style & msidbTextStyleStyleBitsUnderline )
218 lf.lfUnderline = TRUE;
219 if( style & msidbTextStyleStyleBitsStrike )
220 lf.lfStrikeOut = TRUE;
221 lstrcpynW( lf.lfFaceName, face, LF_FACESIZE );
223 /* adjust the height */
224 hdc = GetDC( dialog->hwnd );
225 if (hdc)
227 lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
228 ReleaseDC( dialog->hwnd, hdc );
231 font->hfont = CreateFontIndirectW( &lf );
233 TRACE("Adding font style %s\n", debugstr_w(font->name) );
235 return ERROR_SUCCESS;
238 static msi_font *msi_dialog_find_font( msi_dialog *dialog, LPCWSTR name )
240 msi_font *font;
242 for( font = dialog->font_list; font; font = font->next )
243 if( !strcmpW( font->name, name ) ) /* FIXME: case sensitive? */
244 break;
246 return font;
249 static UINT msi_dialog_set_font( msi_dialog *dialog, HWND hwnd, LPCWSTR name )
251 msi_font *font;
253 font = msi_dialog_find_font( dialog, name );
254 if( font )
255 SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE );
256 else
257 ERR("No font entry for %s\n", debugstr_w(name));
258 return ERROR_SUCCESS;
261 static UINT msi_dialog_build_font_list( msi_dialog *dialog )
263 static const WCHAR query[] = {
264 'S','E','L','E','C','T',' ','*',' ',
265 'F','R','O','M',' ','`','T','e','x','t','S','t','y','l','e','`',' ',0
267 UINT r;
268 MSIQUERY *view = NULL;
270 TRACE("dialog %p\n", dialog );
272 r = MSI_OpenQuery( dialog->package->db, &view, query );
273 if( r != ERROR_SUCCESS )
274 return r;
276 r = MSI_IterateRecords( view, NULL, msi_dialog_add_font, dialog );
277 msiobj_release( &view->hdr );
279 return r;
282 static msi_control *msi_dialog_create_window( msi_dialog *dialog,
283 MSIRECORD *rec, LPCWSTR szCls, LPCWSTR name, LPCWSTR text,
284 DWORD style, HWND parent )
286 DWORD x, y, width, height;
287 LPWSTR font = NULL, title = NULL;
288 msi_control *control;
290 style |= WS_CHILD;
292 control = HeapAlloc( GetProcessHeap(), 0,
293 sizeof *control + strlenW(name)*sizeof(WCHAR) );
294 strcpyW( control->name, name );
295 control->next = dialog->control_list;
296 dialog->control_list = control;
297 control->handler = NULL;
298 control->property = NULL;
299 control->value = NULL;
300 control->pic = NULL;
301 control->hIcon = NULL;
303 x = MSI_RecordGetInteger( rec, 4 );
304 y = MSI_RecordGetInteger( rec, 5 );
305 width = MSI_RecordGetInteger( rec, 6 );
306 height = MSI_RecordGetInteger( rec, 7 );
308 x = msi_dialog_scale_unit( dialog, x );
309 y = msi_dialog_scale_unit( dialog, y );
310 width = msi_dialog_scale_unit( dialog, width );
311 height = msi_dialog_scale_unit( dialog, height );
313 if( text )
315 font = msi_dialog_get_style( &text );
316 deformat_string( dialog->package, text, &title );
319 control->hwnd = CreateWindowW( szCls, title, style,
320 x, y, width, height, parent, NULL, NULL, NULL );
322 TRACE("Dialog %s control %s hwnd %p\n",
323 debugstr_w(dialog->name), debugstr_w(text), control->hwnd );
325 msi_dialog_set_font( dialog, control->hwnd,
326 font ? font : dialog->default_font );
328 HeapFree( GetProcessHeap(), 0, font );
329 HeapFree( GetProcessHeap(), 0, title );
331 return control;
334 /* called from the Control Event subscription code */
335 void msi_dialog_handle_event( msi_dialog* dialog, LPCWSTR control,
336 LPCWSTR attribute, MSIRECORD *rec )
338 msi_control* ctrl;
339 LPCWSTR text;
341 ctrl = msi_dialog_find_control( dialog, control );
342 if (!ctrl)
343 return;
344 if( lstrcmpW(attribute, szText) )
345 return;
346 text = MSI_RecordGetString( rec , 1 );
347 SetWindowTextW( ctrl->hwnd, text );
350 static void msi_dialog_map_events(msi_dialog* dialog, LPCWSTR control)
352 static WCHAR Query[] = {
353 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
354 '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ',
355 'W','H','E','R','E',' ',
356 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
357 'A','N','D',' ',
358 '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0
360 MSIRECORD *row;
361 LPCWSTR event, attribute;
363 row = MSI_QueryGetRecord( dialog->package->db, Query, dialog->name, control );
364 if (!row)
365 return;
367 event = MSI_RecordGetString( row, 3 );
368 attribute = MSI_RecordGetString( row, 4 );
369 ControlEvent_SubscribeToEvent( dialog->package, event, control, attribute );
370 msiobj_release( &row->hdr );
373 /* everything except radio buttons */
374 static msi_control *msi_dialog_add_control( msi_dialog *dialog,
375 MSIRECORD *rec, LPCWSTR szCls, DWORD style )
377 DWORD attributes;
378 LPCWSTR text, name;
380 name = MSI_RecordGetString( rec, 2 );
381 attributes = MSI_RecordGetInteger( rec, 8 );
382 text = MSI_RecordGetString( rec, 10 );
383 if( attributes & 1 )
384 style |= WS_VISIBLE;
385 if( ~attributes & 2 )
386 style |= WS_DISABLED;
388 msi_dialog_map_events(dialog, name);
390 return msi_dialog_create_window( dialog, rec, szCls, name, text,
391 style, dialog->hwnd );
394 static UINT msi_dialog_text_control( msi_dialog *dialog, MSIRECORD *rec )
396 TRACE("%p %p\n", dialog, rec);
398 msi_dialog_add_control( dialog, rec, szStatic, 0 );
399 return ERROR_SUCCESS;
402 static UINT msi_dialog_button_control( msi_dialog *dialog, MSIRECORD *rec )
404 msi_control *control;
406 TRACE("%p %p\n", dialog, rec);
408 control = msi_dialog_add_control( dialog, rec, szButton, WS_TABSTOP );
409 control->handler = msi_dialog_button_handler;
411 return ERROR_SUCCESS;
414 static LPWSTR msi_get_checkbox_value( msi_dialog *dialog, LPCWSTR prop )
416 const static WCHAR query[] = {
417 'S','E','L','E','C','T',' ','*',' ',
418 'F','R','O','M',' ','`','C','h','e','c','k','B','o','x',' ','`',
419 'W','H','E','R','E',' ',
420 '`','P','r','o','p','e','r','t','y','`',' ','=',' ',
421 '\'','%','s','\'',0
423 MSIRECORD *rec = NULL;
424 LPCWSTR val = NULL;
425 LPWSTR ret = NULL;
427 /* find if there is a value associated with the checkbox */
428 rec = MSI_QueryGetRecord( dialog->package->db, query, prop );
429 if (!rec)
430 return ret;
432 val = MSI_RecordGetString( rec, 2 );
433 if (val)
435 deformat_string( dialog->package, val, &ret );
436 if( ret && !ret[0] )
438 HeapFree( GetProcessHeap(), 0, ret );
439 ret = NULL;
442 msiobj_release( &rec->hdr );
443 if (ret)
444 return ret;
446 ret = load_dynamic_property(dialog->package, prop, NULL);
447 if( ret && !ret[0] )
449 HeapFree( GetProcessHeap(), 0, ret );
450 ret = NULL;
453 return ret;
456 static UINT msi_dialog_checkbox_control( msi_dialog *dialog, MSIRECORD *rec )
458 msi_control *control;
459 LPCWSTR prop;
461 TRACE("%p %p\n", dialog, rec);
463 control = msi_dialog_add_control( dialog, rec, szButton,
464 BS_CHECKBOX | BS_MULTILINE | WS_TABSTOP );
465 control->handler = msi_dialog_checkbox_handler;
466 prop = MSI_RecordGetString( rec, 9 );
467 if( prop )
469 control->property = strdupW( prop );
470 control->value = msi_get_checkbox_value( dialog, prop );
471 TRACE("control %s value %s\n", debugstr_w(control->property),
472 debugstr_w(control->value));
474 msi_dialog_checkbox_sync_state( dialog, control );
476 return ERROR_SUCCESS;
479 static UINT msi_dialog_line_control( msi_dialog *dialog, MSIRECORD *rec )
481 TRACE("%p %p\n", dialog, rec);
483 msi_dialog_add_control( dialog, rec, szStatic, SS_ETCHEDHORZ | SS_SUNKEN );
484 return ERROR_SUCCESS;
487 struct msi_streamin_info
489 LPSTR string;
490 DWORD offset;
491 DWORD length;
494 static DWORD CALLBACK
495 msi_richedit_stream_in( DWORD_PTR arg, LPBYTE buffer, LONG count, LONG *pcb )
497 struct msi_streamin_info *info = (struct msi_streamin_info*) arg;
499 if( (count + info->offset) > info->length )
500 count = info->length - info->offset;
501 memcpy( buffer, &info->string[ info->offset ], count );
502 *pcb = count;
503 info->offset += count;
505 TRACE("%ld/%ld\n", info->offset, info->length);
507 return 0;
510 static UINT msi_dialog_scrolltext_control( msi_dialog *dialog, MSIRECORD *rec )
512 const static WCHAR szRichEdit20W[] = {
513 'R','i','c','h','E','d','i','t','2','0','W',0
515 struct msi_streamin_info info;
516 msi_control *control;
517 LPCWSTR text;
518 EDITSTREAM es;
519 DWORD style;
521 style = WS_BORDER | ES_MULTILINE | WS_VSCROLL |
522 ES_READONLY | ES_AUTOVSCROLL | WS_TABSTOP;
523 control = msi_dialog_add_control( dialog, rec, szRichEdit20W, style );
525 text = MSI_RecordGetString( rec, 10 );
526 info.string = strdupWtoA( text );
527 info.offset = 0;
528 info.length = lstrlenA( info.string ) + 1;
530 es.dwCookie = (DWORD_PTR) &info;
531 es.dwError = 0;
532 es.pfnCallback = msi_richedit_stream_in;
534 SendMessageW( control->hwnd, EM_STREAMIN, SF_RTF, (LPARAM) &es );
536 HeapFree( GetProcessHeap(), 0, info.string );
538 return ERROR_SUCCESS;
541 static MSIRECORD *msi_get_binary_record( MSIDATABASE *db, LPCWSTR name )
543 const static WCHAR query[] = {
544 's','e','l','e','c','t',' ','*',' ',
545 'f','r','o','m',' ','B','i','n','a','r','y',' ',
546 'w','h','e','r','e',' ',
547 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0
550 return MSI_QueryGetRecord( db, query, name );
553 static UINT msi_load_bitmap( MSIDATABASE *db, LPCWSTR name, IPicture **pic )
555 MSIRECORD *rec = NULL;
556 IStream *stm = NULL;
557 UINT r;
559 rec = msi_get_binary_record( db, name );
560 if( !rec )
561 return ERROR_FUNCTION_FAILED;
563 r = MSI_RecordGetIStream( rec, 2, &stm );
564 msiobj_release( &rec->hdr );
565 if( r != ERROR_SUCCESS )
566 return r;
568 r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (LPVOID*) pic );
569 IStream_Release( stm );
570 if( FAILED( r ) )
571 return ERROR_FUNCTION_FAILED;
573 return ERROR_SUCCESS;
576 static UINT msi_dialog_bitmap_control( msi_dialog *dialog, MSIRECORD *rec )
578 IPicture *pic = NULL;
579 msi_control *control;
580 OLE_HANDLE hBitmap = 0;
581 LPCWSTR text;
582 UINT r;
584 control = msi_dialog_add_control( dialog, rec, szStatic,
585 SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
586 text = MSI_RecordGetString( rec, 10 );
587 r = msi_load_bitmap( dialog->package->db, text, &pic );
588 if( r == ERROR_SUCCESS )
590 r = IPicture_get_Handle( pic, &hBitmap );
591 if( SUCCEEDED( r ) )
592 SendMessageW( control->hwnd, STM_SETIMAGE, IMAGE_BITMAP, hBitmap );
593 control->pic = pic;
596 return ERROR_SUCCESS;
599 static LPWSTR msi_create_tmp_path(void)
601 WCHAR tmp[MAX_PATH];
602 LPWSTR path = NULL;
603 static const WCHAR prefix[] = { 'm','s','i',0 };
604 DWORD len, r;
606 r = GetTempPathW( MAX_PATH, tmp );
607 if( !r )
608 return path;
609 len = lstrlenW( tmp ) + 20;
610 path = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
611 if( path )
613 r = GetTempFileNameW( tmp, prefix, 0, path );
614 if (!r)
616 HeapFree( GetProcessHeap(), 0, path );
617 path = NULL;
620 return path;
623 static UINT
624 msi_load_icon( MSIDATABASE *db, LPCWSTR name, DWORD attributes, HICON *picon )
626 UINT r = ERROR_FUNCTION_FAILED;
627 LPWSTR tmp;
628 MSIRECORD *rec;
629 HICON hicon = 0;
631 TRACE("loading %s\n", debugstr_w( name ) );
633 tmp = msi_create_tmp_path();
634 if( !tmp )
635 return r;
637 rec = msi_get_binary_record( db, name );
638 if( rec )
640 r = MSI_RecordStreamToFile( rec, 2, tmp );
641 if( r == ERROR_SUCCESS )
643 DWORD cx = 0, cy = 0, flags = LR_LOADFROMFILE | LR_DEFAULTSIZE;
645 if( attributes & msidbControlAttributesFixedSize )
647 flags &= ~LR_DEFAULTSIZE;
648 if( attributes & msidbControlAttributesIconSize16 )
650 cx += 16;
651 cy += 16;
653 if( attributes & msidbControlAttributesIconSize32 )
655 cx += 32;
656 cy += 32;
658 /* msidbControlAttributesIconSize48 handled by above logic */
661 hicon = LoadImageW( 0, tmp, IMAGE_ICON, cx, cy, flags );
662 if( hicon )
663 *picon = hicon;
664 else
665 ERR("failed to load icon from %s\n", debugstr_w( tmp ));
666 DeleteFileW( tmp );
668 msiobj_release( &rec->hdr );
671 HeapFree( GetProcessHeap(), 0, tmp );
673 return r;
676 static UINT msi_dialog_icon_control( msi_dialog *dialog, MSIRECORD *rec )
678 msi_control *control;
679 DWORD attributes;
680 HICON hIcon = 0;
681 LPCWSTR text;
682 UINT r;
684 TRACE("\n");
686 control = msi_dialog_add_control( dialog, rec, szStatic,
687 SS_ICON | SS_CENTERIMAGE | WS_GROUP );
688 text = MSI_RecordGetString( rec, 10 );
689 attributes = MSI_RecordGetInteger( rec, 8 );
690 r = msi_load_icon( dialog->package->db, text, attributes, &hIcon );
691 if( r == ERROR_SUCCESS )
693 r = SendMessageW( control->hwnd, STM_SETICON, (WPARAM) hIcon, 0 );
694 control->hIcon = hIcon;
696 else
697 ERR("Failed to load bitmap %s\n", debugstr_w(text));
698 return ERROR_SUCCESS;
701 static UINT msi_dialog_combo_control( msi_dialog *dialog, MSIRECORD *rec )
703 static const WCHAR szCombo[] = { 'C','O','M','B','O','B','O','X',0 };
705 msi_dialog_add_control( dialog, rec, szCombo,
706 SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
707 return ERROR_SUCCESS;
710 static UINT msi_dialog_edit_control( msi_dialog *dialog, MSIRECORD *rec )
712 msi_control *control;
713 LPCWSTR prop;
714 LPWSTR val;
716 control = msi_dialog_add_control( dialog, rec, szEdit,
717 WS_BORDER | WS_TABSTOP );
718 control->handler = msi_dialog_edit_handler;
719 prop = MSI_RecordGetString( rec, 9 );
720 if( prop )
721 control->property = strdupW( prop );
722 val = load_dynamic_property( dialog->package, control->property, NULL );
723 SetWindowTextW( control->hwnd, val );
724 HeapFree( GetProcessHeap(), 0, val );
725 return ERROR_SUCCESS;
728 /******************** Masked Edit ********************************************/
730 #define MASK_MAX_GROUPS 10
732 struct msi_mask_group
734 UINT len;
735 UINT ofs;
736 WCHAR type;
737 HWND hwnd;
740 struct msi_maskedit_info
742 msi_dialog *dialog;
743 WNDPROC oldproc;
744 HWND hwnd;
745 LPWSTR prop;
746 UINT num_chars;
747 UINT num_groups;
748 struct msi_mask_group group[MASK_MAX_GROUPS];
751 static void msi_mask_control_change( struct msi_maskedit_info *info )
753 LPWSTR val;
754 UINT i, n, r;
756 val = HeapAlloc( GetProcessHeap(), 0, (info->num_chars+1)*sizeof(WCHAR) );
757 for( i=0, n=0; i<info->num_groups; i++ )
759 if( (info->group[i].len + n) > info->num_chars )
761 ERR("can't fit control %d text into template\n",i);
762 break;
764 r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 );
765 if( r != info->group[i].len )
766 break;
767 n += r;
770 TRACE("%d/%d controls were good\n", i, info->num_groups);
772 if( i == info->num_groups )
774 TRACE("Set property %s to %s\n",
775 debugstr_w(info->prop), debugstr_w(val) );
776 CharUpperBuffW( val, info->num_chars );
777 MSI_SetPropertyW( info->dialog->package, info->prop, val );
778 msi_dialog_evaluate_control_conditions( info->dialog );
780 HeapFree( GetProcessHeap(), 0, val );
783 static LRESULT WINAPI
784 MSIMaskedEdit_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
786 struct msi_maskedit_info *info;
787 HRESULT r;
789 TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam);
791 info = GetPropW(hWnd, szButtonData);
793 r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
795 switch( msg )
797 case WM_COMMAND:
798 if (HIWORD(wParam) == EN_CHANGE)
799 msi_mask_control_change( info );
800 break;
801 case WM_NCDESTROY:
802 HeapFree( GetProcessHeap(), 0, info->prop );
803 HeapFree( GetProcessHeap(), 0, info );
804 RemovePropW( hWnd, szButtonData );
805 break;
808 return r;
811 /* fish the various bits of the property out and put them in the control */
812 static void
813 msi_maskedit_set_text( struct msi_maskedit_info *info, LPCWSTR text )
815 LPCWSTR p;
816 UINT i;
818 p = text;
819 for( i = 0; i < info->num_groups; i++ )
821 if( info->group[i].len < lstrlenW( p ) )
823 LPWSTR chunk = strdupW( p );
824 chunk[ info->group[i].len ] = 0;
825 SetWindowTextW( info->group[i].hwnd, chunk );
826 HeapFree( GetProcessHeap(), 0, chunk );
828 else
830 SetWindowTextW( info->group[i].hwnd, p );
831 break;
833 p += info->group[i].len;
837 static struct msi_maskedit_info * msi_dialog_parse_groups( LPCWSTR mask )
839 struct msi_maskedit_info * info = NULL;
840 int i = 0, n = 0, total = 0;
841 LPCWSTR p;
843 TRACE("masked control, template %s\n", debugstr_w(mask));
845 if( !mask )
846 return info;
848 p = strchrW(mask, '<');
849 if( !p )
850 return info;
852 info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *info );
853 if( !info )
854 return info;
856 p++;
857 for( i=0; i<MASK_MAX_GROUPS; i++ )
859 /* stop at the end of the string */
860 if( p[0] == 0 || p[0] == '>' )
861 break;
863 /* count the number of the same identifier */
864 for( n=0; p[n] == p[0]; n++ )
866 info->group[i].ofs = total;
867 info->group[i].type = p[0];
868 if( p[n] == '=' )
870 n++;
871 total++; /* an extra not part of the group */
873 info->group[i].len = n;
874 total += n;
875 p += n;
878 TRACE("%d characters in %d groups\n", total, info->num_groups );
879 if( i == MASK_MAX_GROUPS )
880 ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask));
882 info->num_chars = total;
883 info->num_groups = i;
885 return info;
888 static void
889 msi_maskedit_create_children( struct msi_maskedit_info *info )
891 DWORD width, height, style, wx, ww;
892 LPCWSTR text, font = NULL;
893 RECT rect;
894 HWND hwnd;
895 UINT i;
897 style = WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP;
899 GetClientRect( info->hwnd, &rect );
901 width = rect.right - rect.left;
902 height = rect.bottom - rect.top;
904 if( text )
905 font = msi_dialog_get_style( &text );
907 for( i = 0; i < info->num_groups; i++ )
909 wx = (info->group[i].ofs * width) / info->num_chars;
910 ww = (info->group[i].len * width) / info->num_chars;
912 hwnd = CreateWindowW( szEdit, NULL, style, wx, 0, ww, height,
913 info->hwnd, NULL, NULL, NULL );
914 if( !hwnd )
916 ERR("failed to create mask edit sub window\n");
917 break;
920 SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 );
922 msi_dialog_set_font( info->dialog, hwnd,
923 font ? font : info->dialog->default_font );
924 info->group[i].hwnd = hwnd;
928 /* office 2003 uses "73931<````=````=````=````=`````>@@@@@" */
929 static UINT msi_dialog_maskedit_control( msi_dialog *dialog, MSIRECORD *rec )
931 const static WCHAR pidt[] = {'P','I','D','T','e','m','p','l','a','t','e',0};
932 LPWSTR mask = NULL, title = NULL, val = NULL;
933 struct msi_maskedit_info *info = NULL;
934 UINT ret = ERROR_SUCCESS;
935 msi_control *control;
936 LPCWSTR prop;
938 mask = load_dynamic_property( dialog->package, pidt, NULL );
939 if( !mask )
941 ERR("PIDTemplate is empty\n");
942 goto end;
945 info = msi_dialog_parse_groups( mask );
946 if( !info )
948 ERR("template %s is invalid\n", debugstr_w(mask));
949 goto end;
952 info->dialog = dialog;
954 control = msi_dialog_add_control( dialog, rec, szStatic,
955 SS_OWNERDRAW | WS_GROUP | WS_VISIBLE );
956 if( !control )
958 ERR("Failed to create maskedit container\n");
959 ret = ERROR_FUNCTION_FAILED;
960 goto end;
962 SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT );
964 info->hwnd = control->hwnd;
966 /* subclass the static control */
967 info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC,
968 (LONG_PTR)MSIMaskedEdit_WndProc );
969 SetPropW( control->hwnd, szButtonData, info );
971 prop = MSI_RecordGetString( rec, 9 );
972 if( prop )
973 info->prop = strdupW( prop );
975 msi_maskedit_create_children( info );
977 if( prop )
979 val = load_dynamic_property( dialog->package, prop, NULL );
980 if( val )
982 msi_maskedit_set_text( info, val );
983 HeapFree( GetProcessHeap(), 0, val );
987 end:
988 if( ret != ERROR_SUCCESS )
989 HeapFree( GetProcessHeap(), 0, info );
990 HeapFree( GetProcessHeap(), 0, title );
991 HeapFree( GetProcessHeap(), 0, mask );
992 return ret;
995 /******************** Path Edit ********************************************/
997 static UINT msi_dialog_pathedit_control( msi_dialog *dialog, MSIRECORD *rec )
999 FIXME("not implemented properly\n");
1000 return msi_dialog_edit_control( dialog, rec );
1003 /* radio buttons are a bit different from normal controls */
1004 static UINT msi_dialog_create_radiobutton( MSIRECORD *rec, LPVOID param )
1006 radio_button_group_descr *group = (radio_button_group_descr *)param;
1007 msi_dialog *dialog = group->dialog;
1008 msi_control *control;
1009 LPCWSTR prop, text, name;
1010 DWORD style;
1011 DWORD attributes = group->attributes;
1013 style = WS_CHILD | BS_AUTORADIOBUTTON | BS_MULTILINE | WS_TABSTOP;
1014 name = MSI_RecordGetString( rec, 3 );
1015 text = MSI_RecordGetString( rec, 8 );
1016 if( attributes & 1 )
1017 style |= WS_VISIBLE;
1018 if( ~attributes & 2 )
1019 style |= WS_DISABLED;
1021 control = msi_dialog_create_window( dialog, rec, szButton, name, text,
1022 style, group->parent->hwnd );
1023 control->handler = msi_dialog_radiogroup_handler;
1025 prop = MSI_RecordGetString( rec, 1 );
1026 if( prop )
1027 control->property = strdupW( prop );
1029 return ERROR_SUCCESS;
1032 static UINT msi_dialog_radiogroup_control( msi_dialog *dialog, MSIRECORD *rec )
1034 static const WCHAR query[] = {
1035 'S','E','L','E','C','T',' ','*',' ',
1036 'F','R','O','M',' ','R','a','d','i','o','B','u','t','t','o','n',' ',
1037 'W','H','E','R','E',' ',
1038 '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
1039 UINT r;
1040 LPCWSTR prop;
1041 msi_control *control;
1042 MSIQUERY *view = NULL;
1043 radio_button_group_descr group;
1044 MSIPACKAGE *package = dialog->package;
1045 WNDPROC oldproc;
1047 prop = MSI_RecordGetString( rec, 9 );
1049 TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop ));
1051 /* Create parent group box to hold radio buttons */
1052 control = msi_dialog_add_control( dialog, rec, szButton, BS_OWNERDRAW|WS_GROUP );
1053 if( !control )
1054 return ERROR_FUNCTION_FAILED;
1056 oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
1057 (LONG_PTR)MSIRadioGroup_WndProc );
1058 SetPropW(control->hwnd, szButtonData, oldproc);
1059 SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT );
1061 if( prop )
1062 control->property = strdupW( prop );
1064 /* query the Radio Button table for all control in this group */
1065 r = MSI_OpenQuery( package->db, &view, query, prop );
1066 if( r != ERROR_SUCCESS )
1068 ERR("query failed for dialog %s radio group %s\n",
1069 debugstr_w(dialog->name), debugstr_w(prop));
1070 return ERROR_INVALID_PARAMETER;
1073 group.dialog = dialog;
1074 group.parent = control;
1075 group.attributes = MSI_RecordGetInteger( rec, 8 );
1077 r = MSI_IterateRecords( view, 0, msi_dialog_create_radiobutton, &group );
1078 msiobj_release( &view->hdr );
1080 return r;
1083 struct control_handler msi_dialog_handler[] =
1085 { szText, msi_dialog_text_control },
1086 { szPushButton, msi_dialog_button_control },
1087 { szLine, msi_dialog_line_control },
1088 { szBitmap, msi_dialog_bitmap_control },
1089 { szCheckBox, msi_dialog_checkbox_control },
1090 { szScrollableText, msi_dialog_scrolltext_control },
1091 { szComboBox, msi_dialog_combo_control },
1092 { szEdit, msi_dialog_edit_control },
1093 { szMaskedEdit, msi_dialog_maskedit_control },
1094 { szPathEdit, msi_dialog_pathedit_control },
1095 { szRadioButtonGroup, msi_dialog_radiogroup_control },
1096 { szIcon, msi_dialog_icon_control },
1099 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
1101 static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param )
1103 msi_dialog *dialog = param;
1104 LPCWSTR control_type;
1105 UINT i;
1107 /* find and call the function that can create this type of control */
1108 control_type = MSI_RecordGetString( rec, 3 );
1109 for( i=0; i<NUM_CONTROL_TYPES; i++ )
1110 if (!strcmpiW( msi_dialog_handler[i].control_type, control_type ))
1111 break;
1112 if( i != NUM_CONTROL_TYPES )
1113 msi_dialog_handler[i].func( dialog, rec );
1114 else
1115 ERR("no handler for element type %s\n", debugstr_w(control_type));
1117 return ERROR_SUCCESS;
1120 static UINT msi_dialog_fill_controls( msi_dialog *dialog )
1122 static const WCHAR query[] = {
1123 'S','E','L','E','C','T',' ','*',' ',
1124 'F','R','O','M',' ','C','o','n','t','r','o','l',' ',
1125 'W','H','E','R','E',' ',
1126 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
1127 UINT r;
1128 MSIQUERY *view = NULL;
1129 MSIPACKAGE *package = dialog->package;
1131 TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
1133 /* query the Control table for all the elements of the control */
1134 r = MSI_OpenQuery( package->db, &view, query, dialog->name );
1135 if( r != ERROR_SUCCESS )
1137 ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
1138 return ERROR_INVALID_PARAMETER;
1141 r = MSI_IterateRecords( view, 0, msi_dialog_create_controls, dialog );
1142 msiobj_release( &view->hdr );
1144 return r;
1147 static UINT msi_dialog_set_control_condition( MSIRECORD *rec, LPVOID param )
1149 static const WCHAR szHide[] = { 'H','i','d','e',0 };
1150 static const WCHAR szShow[] = { 'S','h','o','w',0 };
1151 static const WCHAR szDisable[] = { 'D','i','s','a','b','l','e',0 };
1152 static const WCHAR szEnable[] = { 'E','n','a','b','l','e',0 };
1153 msi_dialog *dialog = param;
1154 msi_control *control;
1155 LPCWSTR name, action, condition;
1156 UINT r;
1158 name = MSI_RecordGetString( rec, 2 );
1159 action = MSI_RecordGetString( rec, 3 );
1160 condition = MSI_RecordGetString( rec, 4 );
1161 r = MSI_EvaluateConditionW( dialog->package, condition );
1162 control = msi_dialog_find_control( dialog, name );
1163 if( r && control )
1165 TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
1167 /* FIXME: case sensitive? */
1168 if(!lstrcmpW(action, szHide))
1169 ShowWindow(control->hwnd, SW_HIDE);
1170 else if(!strcmpW(action, szShow))
1171 ShowWindow(control->hwnd, SW_SHOW);
1172 else if(!strcmpW(action, szDisable))
1173 EnableWindow(control->hwnd, FALSE);
1174 else if(!strcmpW(action, szEnable))
1175 EnableWindow(control->hwnd, TRUE);
1176 else
1177 FIXME("Unhandled action %s\n", debugstr_w(action));
1180 return ERROR_SUCCESS;
1183 static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog )
1185 static const WCHAR query[] = {
1186 'S','E','L','E','C','T',' ','*',' ',
1187 'F','R','O','M',' ',
1188 'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
1189 'W','H','E','R','E',' ',
1190 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0
1192 UINT r;
1193 MSIQUERY *view = NULL;
1194 MSIPACKAGE *package = dialog->package;
1196 TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
1198 /* query the Control table for all the elements of the control */
1199 r = MSI_OpenQuery( package->db, &view, query, dialog->name );
1200 if( r != ERROR_SUCCESS )
1202 ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
1203 return ERROR_INVALID_PARAMETER;
1206 r = MSI_IterateRecords( view, 0, msi_dialog_set_control_condition, dialog );
1207 msiobj_release( &view->hdr );
1209 return r;
1212 /* figure out the height of 10 point MS Sans Serif */
1213 static INT msi_dialog_get_sans_serif_height( HWND hwnd )
1215 static const WCHAR szSansSerif[] = {
1216 'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
1217 LOGFONTW lf;
1218 TEXTMETRICW tm;
1219 BOOL r;
1220 LONG height = 0;
1221 HFONT hFont, hOldFont;
1222 HDC hdc;
1224 hdc = GetDC( hwnd );
1225 if (hdc)
1227 memset( &lf, 0, sizeof lf );
1228 lf.lfHeight = MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1229 strcpyW( lf.lfFaceName, szSansSerif );
1230 hFont = CreateFontIndirectW(&lf);
1231 if (hFont)
1233 hOldFont = SelectObject( hdc, hFont );
1234 r = GetTextMetricsW( hdc, &tm );
1235 if (r)
1236 height = tm.tmHeight;
1237 SelectObject( hdc, hOldFont );
1238 DeleteObject( hFont );
1240 ReleaseDC( hwnd, hdc );
1242 return height;
1245 /* fetch the associated record from the Dialog table */
1246 static MSIRECORD *msi_get_dialog_record( msi_dialog *dialog )
1248 static const WCHAR query[] = {
1249 'S','E','L','E','C','T',' ','*',' ',
1250 'F','R','O','M',' ','D','i','a','l','o','g',' ',
1251 'W','H','E','R','E',' ',
1252 '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
1253 MSIPACKAGE *package = dialog->package;
1254 MSIRECORD *rec = NULL;
1256 TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
1258 rec = MSI_QueryGetRecord( package->db, query, dialog->name );
1259 if( !rec )
1260 ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
1262 return rec;
1265 static void msi_dialog_adjust_dialog_size( msi_dialog *dialog, LPSIZE sz )
1267 RECT rect;
1268 LONG style;
1270 /* turn the client size into the window rectangle */
1271 rect.left = 0;
1272 rect.top = 0;
1273 rect.right = msi_dialog_scale_unit( dialog, sz->cx );
1274 rect.bottom = msi_dialog_scale_unit( dialog, sz->cy );
1275 style = GetWindowLongPtrW( dialog->hwnd, GWL_STYLE );
1276 AdjustWindowRect( &rect, style, FALSE );
1277 sz->cx = rect.right - rect.left;
1278 sz->cy = rect.bottom - rect.top;
1281 static LRESULT msi_dialog_oncreate( HWND hwnd, LPCREATESTRUCTW cs )
1283 static const WCHAR df[] = {
1284 'D','e','f','a','u','l','t','U','I','F','o','n','t',0 };
1285 msi_dialog *dialog = (msi_dialog*) cs->lpCreateParams;
1286 MSIRECORD *rec = NULL;
1287 LPCWSTR text;
1288 LPWSTR title = NULL;
1289 SIZE size;
1291 TRACE("%p %p\n", dialog, dialog->package);
1293 dialog->hwnd = hwnd;
1294 SetWindowLongPtrW( hwnd, GWLP_USERDATA, (LONG_PTR) dialog );
1296 rec = msi_get_dialog_record( dialog );
1297 if( !rec )
1299 TRACE("No record found for dialog %s\n", debugstr_w(dialog->name));
1300 return -1;
1303 dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd);
1305 size.cx = MSI_RecordGetInteger( rec, 4 );
1306 size.cy = MSI_RecordGetInteger( rec, 5 );
1307 msi_dialog_adjust_dialog_size( dialog, &size );
1309 dialog->attributes = MSI_RecordGetInteger( rec, 6 );
1310 text = MSI_RecordGetString( rec, 7 );
1312 dialog->default_font = load_dynamic_property( dialog->package, df, NULL );
1314 deformat_string( dialog->package, text, &title );
1315 SetWindowTextW( hwnd, title );
1316 SetWindowPos( hwnd, 0, 0, 0, size.cx, size.cy,
1317 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW );
1319 HeapFree( GetProcessHeap(), 0, title );
1320 msiobj_release( &rec->hdr );
1322 msi_dialog_build_font_list( dialog );
1323 msi_dialog_fill_controls( dialog );
1324 msi_dialog_evaluate_control_conditions( dialog );
1326 return 0;
1329 static UINT msi_dialog_send_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
1331 LPWSTR event_fmt = NULL, arg_fmt = NULL;
1333 TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
1335 deformat_string( dialog->package, event, &event_fmt );
1336 deformat_string( dialog->package, arg, &arg_fmt );
1338 dialog->event_handler( dialog->package, event_fmt, arg_fmt, dialog );
1340 HeapFree( GetProcessHeap(), 0, event_fmt );
1341 HeapFree( GetProcessHeap(), 0, arg_fmt );
1343 return ERROR_SUCCESS;
1346 static UINT msi_dialog_set_property( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
1348 static const WCHAR szNullArg[] = { '{','}',0 };
1349 LPWSTR p, prop, arg_fmt = NULL;
1350 UINT len;
1352 len = strlenW(event);
1353 prop = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR));
1354 strcpyW( prop, &event[1] );
1355 p = strchrW( prop, ']' );
1356 if( p && p[1] == 0 )
1358 *p = 0;
1359 if( strcmpW( szNullArg, arg ) )
1360 deformat_string( dialog->package, arg, &arg_fmt );
1361 MSI_SetPropertyW( dialog->package, prop, arg_fmt );
1363 else
1364 ERR("Badly formatted property string - what happens?\n");
1365 HeapFree( GetProcessHeap(), 0, prop );
1366 return ERROR_SUCCESS;
1369 static UINT msi_dialog_control_event( MSIRECORD *rec, LPVOID param )
1371 msi_dialog *dialog = param;
1372 LPCWSTR condition, event, arg;
1373 UINT r;
1375 condition = MSI_RecordGetString( rec, 5 );
1376 r = MSI_EvaluateConditionW( dialog->package, condition );
1377 if( r )
1379 event = MSI_RecordGetString( rec, 3 );
1380 arg = MSI_RecordGetString( rec, 4 );
1381 if( event[0] == '[' )
1382 msi_dialog_set_property( dialog, event, arg );
1383 else
1384 msi_dialog_send_event( dialog, event, arg );
1387 return ERROR_SUCCESS;
1390 static UINT msi_dialog_button_handler( msi_dialog *dialog,
1391 msi_control *control, WPARAM param )
1393 static const WCHAR query[] = {
1394 'S','E','L','E','C','T',' ','*',' ',
1395 'F','R','O','M',' ','C','o','n','t','r','o','l','E','v','e','n','t',' ',
1396 'W','H','E','R','E',' ',
1397 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
1398 'A','N','D',' ',
1399 '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
1400 'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0
1402 MSIQUERY *view = NULL;
1403 UINT r;
1405 if( HIWORD(param) != BN_CLICKED )
1406 return ERROR_SUCCESS;
1408 r = MSI_OpenQuery( dialog->package->db, &view, query,
1409 dialog->name, control->name );
1410 if( r != ERROR_SUCCESS )
1412 ERR("query failed\n");
1413 return 0;
1416 r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog );
1417 msiobj_release( &view->hdr );
1419 return r;
1422 static UINT msi_dialog_get_checkbox_state( msi_dialog *dialog,
1423 msi_control *control )
1425 WCHAR state[2] = { 0 };
1426 DWORD sz = 2;
1428 MSI_GetPropertyW( dialog->package, control->property, state, &sz );
1429 return state[0] ? 1 : 0;
1432 static void msi_dialog_set_checkbox_state( msi_dialog *dialog,
1433 msi_control *control, UINT state )
1435 static const WCHAR szState[] = { '1', 0 };
1436 LPCWSTR val;
1438 /* if uncheck then the property is set to NULL */
1439 if (!state)
1441 MSI_SetPropertyW( dialog->package, control->property, NULL );
1442 return;
1445 /* check for a custom state */
1446 if (control->value && control->value[0])
1447 val = control->value;
1448 else
1449 val = szState;
1451 MSI_SetPropertyW( dialog->package, control->property, val );
1454 static void msi_dialog_checkbox_sync_state( msi_dialog *dialog,
1455 msi_control *control )
1457 UINT state;
1459 state = msi_dialog_get_checkbox_state( dialog, control );
1460 SendMessageW( control->hwnd, BM_SETCHECK,
1461 state ? BST_CHECKED : BST_UNCHECKED, 0 );
1464 static UINT msi_dialog_checkbox_handler( msi_dialog *dialog,
1465 msi_control *control, WPARAM param )
1467 UINT state;
1469 if( HIWORD(param) != BN_CLICKED )
1470 return ERROR_SUCCESS;
1472 TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name),
1473 debugstr_w(control->property));
1475 state = msi_dialog_get_checkbox_state( dialog, control );
1476 state = state ? 0 : 1;
1477 msi_dialog_set_checkbox_state( dialog, control, state );
1478 msi_dialog_checkbox_sync_state( dialog, control );
1480 return msi_dialog_button_handler( dialog, control, param );
1483 static UINT msi_dialog_edit_handler( msi_dialog *dialog,
1484 msi_control *control, WPARAM param )
1486 UINT sz, r;
1487 LPWSTR buf;
1489 if( HIWORD(param) != EN_CHANGE )
1490 return ERROR_SUCCESS;
1492 TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name),
1493 debugstr_w(control->property));
1495 sz = 0x20;
1496 buf = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
1497 while( buf )
1499 r = GetWindowTextW( control->hwnd, buf, sz );
1500 if( r < (sz-1) )
1501 break;
1502 sz *= 2;
1503 buf = HeapReAlloc( GetProcessHeap(), 0, buf, sz*sizeof(WCHAR) );
1506 MSI_SetPropertyW( dialog->package, control->property, buf );
1508 HeapFree( GetProcessHeap(), 0, buf );
1510 return ERROR_SUCCESS;
1513 static UINT msi_dialog_radiogroup_handler( msi_dialog *dialog,
1514 msi_control *control, WPARAM param )
1516 if( HIWORD(param) != BN_CLICKED )
1517 return ERROR_SUCCESS;
1519 TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name),
1520 debugstr_w(control->property));
1522 MSI_SetPropertyW( dialog->package, control->property, control->name );
1524 return msi_dialog_button_handler( dialog, control, param );
1527 static LRESULT msi_dialog_oncommand( msi_dialog *dialog, WPARAM param, HWND hwnd )
1529 msi_control *control;
1531 TRACE("%p %p %08x\n", dialog, hwnd, param);
1533 control = msi_dialog_find_control_by_hwnd( dialog, hwnd );
1534 if( control )
1536 if( control->handler )
1538 control->handler( dialog, control, param );
1539 msi_dialog_evaluate_control_conditions( dialog );
1542 else
1543 ERR("button click from nowhere\n");
1544 return 0;
1547 static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg,
1548 WPARAM wParam, LPARAM lParam )
1550 msi_dialog *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA );
1552 TRACE("0x%04x\n", msg);
1554 switch (msg)
1556 case WM_CREATE:
1557 return msi_dialog_oncreate( hwnd, (LPCREATESTRUCTW)lParam );
1559 case WM_COMMAND:
1560 return msi_dialog_oncommand( dialog, wParam, (HWND)lParam );
1562 case WM_DESTROY:
1563 dialog->hwnd = NULL;
1564 return 0;
1566 return DefWindowProcW(hwnd, msg, wParam, lParam);
1569 static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1571 WNDPROC oldproc = (WNDPROC) GetPropW(hWnd, szButtonData);
1573 TRACE("hWnd %p msg %04x wParam 0x%08x lParam 0x%08lx\n", hWnd, msg, wParam, lParam);
1575 if (msg == WM_COMMAND) /* Forward notifications to dialog */
1576 SendMessageW(GetParent(hWnd), msg, wParam, lParam);
1578 return CallWindowProcW(oldproc, hWnd, msg, wParam, lParam);
1581 static LRESULT WINAPI MSIHiddenWindowProc( HWND hwnd, UINT msg,
1582 WPARAM wParam, LPARAM lParam )
1584 msi_dialog *dialog = (msi_dialog*) lParam;
1586 TRACE("%d %p\n", msg, dialog);
1588 switch (msg)
1590 case WM_MSI_DIALOG_CREATE:
1591 return msi_dialog_run_message_loop( dialog );
1592 case WM_MSI_DIALOG_DESTROY:
1593 msi_dialog_destroy( dialog );
1594 return 0;
1596 return DefWindowProcW( hwnd, msg, wParam, lParam );
1599 /* functions that interface to other modules within MSI */
1601 msi_dialog *msi_dialog_create( MSIPACKAGE* package, LPCWSTR szDialogName,
1602 msi_dialog_event_handler event_handler )
1604 MSIRECORD *rec = NULL;
1605 msi_dialog *dialog;
1607 TRACE("%p %s\n", package, debugstr_w(szDialogName));
1609 /* allocate the structure for the dialog to use */
1610 dialog = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1611 sizeof *dialog + sizeof(WCHAR)*strlenW(szDialogName) );
1612 if( !dialog )
1613 return NULL;
1614 strcpyW( dialog->name, szDialogName );
1615 msiobj_addref( &package->hdr );
1616 dialog->package = package;
1617 dialog->event_handler = event_handler;
1618 dialog->finished = 0;
1620 /* verify that the dialog exists */
1621 rec = msi_get_dialog_record( dialog );
1622 if( !rec )
1624 HeapFree( GetProcessHeap(), 0, dialog );
1625 return NULL;
1627 dialog->attributes = MSI_RecordGetInteger( rec, 6 );
1628 msiobj_release( &rec->hdr );
1630 return dialog;
1633 static void msi_process_pending_messages( HWND hdlg )
1635 MSG msg;
1637 while( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ) )
1639 if( hdlg && IsDialogMessageW( hdlg, &msg ))
1640 continue;
1641 TranslateMessage( &msg );
1642 DispatchMessageW( &msg );
1646 void msi_dialog_end_dialog( msi_dialog *dialog )
1648 TRACE("%p\n", dialog);
1649 dialog->finished = 1;
1650 PostMessageW(dialog->hwnd, WM_NULL, 0, 0);
1653 void msi_dialog_check_messages( HANDLE handle )
1655 DWORD r;
1657 /* in threads other than the UI thread, block */
1658 if( uiThreadId != GetCurrentThreadId() )
1660 if( handle )
1661 WaitForSingleObject( handle, INFINITE );
1662 return;
1665 /* there's two choices for the UI thread */
1666 while (1)
1668 msi_process_pending_messages( NULL );
1670 if( !handle )
1671 break;
1674 * block here until somebody creates a new dialog or
1675 * the handle we're waiting on becomes ready
1677 r = MsgWaitForMultipleObjects( 1, &handle, 0, INFINITE, QS_ALLINPUT );
1678 if( r == WAIT_OBJECT_0 )
1679 break;
1683 UINT msi_dialog_run_message_loop( msi_dialog *dialog )
1685 HWND hwnd;
1687 if( !(dialog->attributes & msidbDialogAttributesVisible) )
1688 return ERROR_SUCCESS;
1690 if( uiThreadId != GetCurrentThreadId() )
1691 return SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_CREATE, 0, (LPARAM) dialog );
1693 /* create the dialog window, don't show it yet */
1694 hwnd = CreateWindowW( szMsiDialogClass, dialog->name, WS_OVERLAPPEDWINDOW,
1695 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
1696 NULL, NULL, NULL, dialog );
1697 if( !hwnd )
1699 ERR("Failed to create dialog %s\n", debugstr_w( dialog->name ));
1700 return ERROR_FUNCTION_FAILED;
1703 ShowWindow( hwnd, SW_SHOW );
1704 UpdateWindow( hwnd );
1706 if( dialog->attributes & msidbDialogAttributesModal )
1708 while( !dialog->finished )
1710 MsgWaitForMultipleObjects( 0, NULL, 0, INFINITE, QS_ALLEVENTS );
1711 msi_process_pending_messages( dialog->hwnd );
1714 else
1715 return ERROR_IO_PENDING;
1717 return ERROR_SUCCESS;
1720 void msi_dialog_do_preview( msi_dialog *dialog )
1722 TRACE("\n");
1723 dialog->attributes |= msidbDialogAttributesVisible;
1724 dialog->attributes &= ~msidbDialogAttributesModal;
1725 msi_dialog_run_message_loop( dialog );
1728 void msi_dialog_destroy( msi_dialog *dialog )
1730 if( uiThreadId != GetCurrentThreadId() )
1732 SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_DESTROY, 0, (LPARAM) dialog );
1733 return;
1736 if( dialog->hwnd )
1737 ShowWindow( dialog->hwnd, SW_HIDE );
1739 /* destroy the list of controls */
1740 while( dialog->control_list )
1742 msi_control *t = dialog->control_list;
1743 dialog->control_list = t->next;
1744 /* leave dialog->hwnd - destroying parent destroys child windows */
1745 HeapFree( GetProcessHeap(), 0, t->property );
1746 HeapFree( GetProcessHeap(), 0, t->value );
1747 if( t->pic )
1748 IPicture_Release( t->pic );
1749 if( t->hIcon )
1750 DestroyIcon( t->hIcon );
1751 HeapFree( GetProcessHeap(), 0, t );
1754 /* destroy the list of fonts */
1755 while( dialog->font_list )
1757 msi_font *t = dialog->font_list;
1758 dialog->font_list = t->next;
1759 DeleteObject( t->hfont );
1760 HeapFree( GetProcessHeap(), 0, t );
1762 HeapFree( GetProcessHeap(), 0, dialog->default_font );
1764 if( dialog->hwnd )
1765 DestroyWindow( dialog->hwnd );
1767 msiobj_release( &dialog->package->hdr );
1768 dialog->package = NULL;
1769 HeapFree( GetProcessHeap(), 0, dialog );
1772 BOOL msi_dialog_register_class( void )
1774 WNDCLASSW cls;
1776 ZeroMemory( &cls, sizeof cls );
1777 cls.lpfnWndProc = MSIDialog_WndProc;
1778 cls.hInstance = NULL;
1779 cls.hIcon = LoadIconW(0, (LPWSTR)IDI_APPLICATION);
1780 cls.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1781 cls.hbrBackground = (HBRUSH)(COLOR_WINDOW);
1782 cls.lpszMenuName = NULL;
1783 cls.lpszClassName = szMsiDialogClass;
1785 if( !RegisterClassW( &cls ) )
1786 return FALSE;
1788 cls.lpfnWndProc = MSIHiddenWindowProc;
1789 cls.lpszClassName = szMsiHiddenWindow;
1791 if( !RegisterClassW( &cls ) )
1792 return FALSE;
1794 uiThreadId = GetCurrentThreadId();
1796 hMsiHiddenWindow = CreateWindowW( szMsiHiddenWindow, NULL, WS_OVERLAPPED,
1797 0, 0, 100, 100, NULL, NULL, NULL, NULL );
1798 if( !hMsiHiddenWindow )
1799 return FALSE;
1801 hRichedit = LoadLibraryA("riched20");
1803 return TRUE;
1806 void msi_dialog_unregister_class( void )
1808 DestroyWindow( hMsiHiddenWindow );
1809 UnregisterClassW( szMsiDialogClass, NULL );
1810 uiThreadId = 0;
1811 FreeLibrary( hRichedit );