Reverse the order for deleting the items in resetcontent to correctly
[wine] / dlls / msi / dialog.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Mike McCormack for CodeWeavers
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "winnls.h"
27 #include "wingdi.h"
28 #include "msi.h"
29 #include "msipriv.h"
30 #include "msidefs.h"
31
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34
35 #include "action.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(msi);
38
39
40 const WCHAR szMsiDialogClass[] = {
41     'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0
42 };
43 const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
44
45 struct msi_control_tag;
46 typedef struct msi_control_tag msi_control;
47 typedef UINT (*msi_handler)( msi_dialog *, msi_control *, WPARAM );
48
49 struct msi_control_tag
50 {
51     struct msi_control_tag *next;
52     HWND hwnd;
53     msi_handler handler;
54     LPWSTR property;
55     WCHAR name[1];
56 };
57
58 typedef struct msi_font_tag
59 {
60     struct msi_font_tag *next;
61     HFONT hfont;
62     WCHAR name[1];
63 } msi_font;
64
65 struct msi_dialog_tag
66 {
67     MSIPACKAGE *package;
68     msi_dialog_event_handler event_handler;
69     BOOL finished;
70     INT scale;
71     DWORD attributes;
72     HWND hwnd;
73     LPWSTR default_font;
74     msi_font *font_list;
75     msi_control *control_list;
76     WCHAR name[1];
77 };
78
79 typedef UINT (*msi_dialog_control_func)( msi_dialog *dialog, MSIRECORD *rec );
80 struct control_handler 
81 {
82     LPCWSTR control_type;
83     msi_dialog_control_func func;
84 };
85
86 static UINT msi_dialog_checkbox_handler( msi_dialog *, msi_control *, WPARAM );
87 static void msi_dialog_checkbox_sync_state( msi_dialog *, msi_control * );
88 static UINT msi_dialog_button_handler( msi_dialog *, msi_control *, WPARAM );
89 static UINT msi_dialog_edit_handler( msi_dialog *, msi_control *, WPARAM );
90
91
92 INT msi_dialog_scale_unit( msi_dialog *dialog, INT val )
93 {
94     return (dialog->scale * val + 5) / 10;
95 }
96
97 /*
98  * msi_dialog_get_style
99  *
100  * Extract the {\style} string from the front of the text to display and
101  *  update the pointer.
102  */
103 static LPWSTR msi_dialog_get_style( LPCWSTR *text )
104 {
105     LPWSTR ret = NULL;
106     LPCWSTR p = *text, q;
107     DWORD len;
108
109     if( *p++ != '{' )
110         return ret;
111     q = strchrW( p, '}' );
112     if( !q )
113         return ret;
114     *text = ++q;
115     if( *p++ != '\\' )
116         return ret;
117     len = q - p;
118     
119     ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
120     if( !ret )
121         return ret;
122     strncpyW( ret, p, len );
123     ret[len-1] = 0;
124     return ret;
125 }
126
127 static UINT msi_dialog_add_font( MSIRECORD *rec, LPVOID param )
128 {
129     msi_dialog *dialog = param;
130     msi_font *font;
131     LPCWSTR face, name;
132     LOGFONTW lf;
133     INT style;
134     HDC hdc;
135
136     /* create a font and add it to the list */
137     name = MSI_RecordGetString( rec, 1 );
138     font = HeapAlloc( GetProcessHeap(), 0,
139                       sizeof *font + strlenW( name )*sizeof (WCHAR) );
140     strcpyW( font->name, name );
141     font->next = dialog->font_list;
142     dialog->font_list = font;
143
144     memset( &lf, 0, sizeof lf );
145     face = MSI_RecordGetString( rec, 2 );
146     lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
147     style = MSI_RecordGetInteger( rec, 5 );
148     if( style & msidbTextStyleStyleBitsBold )
149         lf.lfWeight = FW_BOLD;
150     if( style & msidbTextStyleStyleBitsItalic )
151         lf.lfItalic = TRUE;
152     if( style & msidbTextStyleStyleBitsUnderline )
153         lf.lfUnderline = TRUE;
154     if( style & msidbTextStyleStyleBitsStrike )
155         lf.lfStrikeOut = TRUE;
156     lstrcpynW( lf.lfFaceName, face, LF_FACESIZE );
157
158     /* adjust the height */
159     hdc = GetDC( dialog->hwnd );
160     if (hdc)
161     {
162         lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
163         ReleaseDC( dialog->hwnd, hdc );
164     }
165
166     font->hfont = CreateFontIndirectW( &lf );
167
168     TRACE("Adding font style %s\n", debugstr_w(font->name) );
169
170     return ERROR_SUCCESS;
171 }
172
173 static msi_font *msi_dialog_find_font( msi_dialog *dialog, LPCWSTR name )
174 {
175     msi_font *font;
176
177     for( font = dialog->font_list; font; font = font->next )
178         if( !strcmpW( font->name, name ) )  /* FIXME: case sensitive? */
179             break;
180
181     return font;
182 }
183
184 static UINT msi_dialog_set_font( msi_dialog *dialog, HWND hwnd, LPCWSTR name )
185 {
186     msi_font *font;
187
188     font = msi_dialog_find_font( dialog, name );
189     if( font )
190         SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE );
191     else
192         ERR("No font entry for %s\n", debugstr_w(name));
193     return ERROR_SUCCESS;
194 }
195
196 static UINT msi_dialog_build_font_list( msi_dialog *dialog )
197 {
198     static const WCHAR query[] = {
199       'S','E','L','E','C','T',' ','*',' ',
200       'F','R','O','M',' ','`','T','e','x','t','S','t','y','l','e','`',' ',0
201     };
202     UINT r;
203     MSIQUERY *view = NULL;
204
205     TRACE("dialog %p\n", dialog );
206
207     r = MSI_OpenQuery( dialog->package->db, &view, query );
208     if( r != ERROR_SUCCESS )
209         return r;
210
211     r = MSI_IterateRecords( view, NULL, msi_dialog_add_font, dialog );
212     msiobj_release( &view->hdr );
213
214     return r;
215 }
216
217 static msi_control *msi_dialog_add_control( msi_dialog *dialog,
218                 MSIRECORD *rec, LPCWSTR szCls, DWORD style )
219 {
220     DWORD x, y, width, height, attributes;
221     LPCWSTR text, name;
222     LPWSTR font = NULL, title = NULL;
223     msi_control *control = NULL;
224
225     style |= WS_CHILD | WS_GROUP;
226
227     name = MSI_RecordGetString( rec, 2 );
228     control = HeapAlloc( GetProcessHeap(), 0,
229                          sizeof *control + strlenW(name)*sizeof(WCHAR) );
230     strcpyW( control->name, name );
231     control->next = dialog->control_list;
232     dialog->control_list = control;
233     control->handler = NULL;
234     control->property = NULL;
235
236     x = MSI_RecordGetInteger( rec, 4 );
237     y = MSI_RecordGetInteger( rec, 5 );
238     width = MSI_RecordGetInteger( rec, 6 );
239     height = MSI_RecordGetInteger( rec, 7 );
240     attributes = MSI_RecordGetInteger( rec, 8 );
241     text = MSI_RecordGetString( rec, 10 );
242
243     TRACE("Dialog %s control %s\n", debugstr_w(dialog->name), debugstr_w(text));
244
245     x = msi_dialog_scale_unit( dialog, x );
246     y = msi_dialog_scale_unit( dialog, y );
247     width = msi_dialog_scale_unit( dialog, width );
248     height = msi_dialog_scale_unit( dialog, height );
249
250     if( attributes & 1 )
251         style |= WS_VISIBLE;
252     if( ~attributes & 2 )
253         style |= WS_DISABLED;
254     if( text )
255     {
256         font = msi_dialog_get_style( &text );
257         deformat_string( dialog->package, text, &title );
258     }
259     control->hwnd = CreateWindowW( szCls, title, style,
260                           x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
261     msi_dialog_set_font( dialog, control->hwnd,
262                          font ? font : dialog->default_font );
263     HeapFree( GetProcessHeap(), 0, font );
264     HeapFree( GetProcessHeap(), 0, title );
265     return control;
266 }
267
268 static UINT msi_dialog_text_control( msi_dialog *dialog, MSIRECORD *rec )
269 {
270     TRACE("%p %p\n", dialog, rec);
271
272     msi_dialog_add_control( dialog, rec, szStatic, 0 );
273     return ERROR_SUCCESS;
274 }
275
276 static UINT msi_dialog_button_control( msi_dialog *dialog, MSIRECORD *rec )
277 {
278     const static WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
279     msi_control *control;
280
281     TRACE("%p %p\n", dialog, rec);
282
283     control = msi_dialog_add_control( dialog, rec, szButton, 0 );
284     control->handler = msi_dialog_button_handler;
285
286     return ERROR_SUCCESS;
287 }
288
289 static UINT msi_dialog_checkbox_control( msi_dialog *dialog, MSIRECORD *rec )
290 {
291     const static WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
292     msi_control *control;
293     LPCWSTR prop;
294
295     TRACE("%p %p\n", dialog, rec);
296
297     control = msi_dialog_add_control( dialog, rec, szButton,
298                                       BS_CHECKBOX | BS_MULTILINE );
299     control->handler = msi_dialog_checkbox_handler;
300     prop = MSI_RecordGetString( rec, 9 );
301     if( prop )
302         control->property = dupstrW( prop );
303     msi_dialog_checkbox_sync_state( dialog, control );
304
305     return ERROR_SUCCESS;
306 }
307
308 static UINT msi_dialog_line_control( msi_dialog *dialog, MSIRECORD *rec )
309 {
310     TRACE("%p %p\n", dialog, rec);
311
312     msi_dialog_add_control( dialog, rec, szStatic, SS_ETCHEDHORZ | SS_SUNKEN );
313     return ERROR_SUCCESS;
314 }
315
316 static UINT msi_dialog_scrolltext_control( msi_dialog *dialog, MSIRECORD *rec )
317 {
318     const static WCHAR szEdit[] = { 'E','D','I','T',0 };
319
320     TRACE("%p %p\n", dialog, rec);
321
322     msi_dialog_add_control( dialog, rec, szEdit, WS_BORDER |
323                  ES_MULTILINE | WS_VSCROLL | ES_READONLY | ES_AUTOVSCROLL );
324
325     return ERROR_SUCCESS;
326 }
327
328 static UINT msi_dialog_bitmap_control( msi_dialog *dialog, MSIRECORD *rec )
329 {
330     TRACE("%p %p\n", dialog, rec);
331
332     msi_dialog_add_control( dialog, rec, szStatic,
333                             SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
334     return ERROR_SUCCESS;
335 }
336
337 static UINT msi_dialog_combo_control( msi_dialog *dialog, MSIRECORD *rec )
338 {
339     static const WCHAR szCombo[] = { 'C','O','M','B','O','B','O','X',0 };
340
341     msi_dialog_add_control( dialog, rec, szCombo,
342                             SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
343     return ERROR_SUCCESS;
344 }
345
346 static UINT msi_dialog_edit_control( msi_dialog *dialog, MSIRECORD *rec )
347 {
348     const static WCHAR szEdit[] = { 'E','D','I','T',0 };
349     msi_control *control;
350     LPCWSTR prop;
351     LPWSTR val;
352
353     control = msi_dialog_add_control( dialog, rec, szEdit, WS_BORDER );
354     control->handler = msi_dialog_edit_handler;
355     prop = MSI_RecordGetString( rec, 9 );
356     if( prop )
357         control->property = dupstrW( prop );
358     val = load_dynamic_property( dialog->package, control->property, NULL );
359     SetWindowTextW( control->hwnd, val );
360     HeapFree( GetProcessHeap(), 0, val );
361     return ERROR_SUCCESS;
362 }
363
364 static UINT msi_dialog_pathedit_control( msi_dialog *dialog, MSIRECORD *rec )
365 {
366     FIXME("not implemented properly\n");
367     return msi_dialog_edit_control( dialog, rec );
368 }
369
370 static UINT msi_dialog_radiogroup_control( msi_dialog *dialog, MSIRECORD *rec )
371 {
372     LPCWSTR name;
373
374     name = MSI_RecordGetString( rec, 2 );
375     FIXME("Radio group %s\n", debugstr_w( name ) );
376     return ERROR_SUCCESS;
377 }
378
379 static const WCHAR szText[] = { 'T','e','x','t',0 };
380 static const WCHAR szButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
381 static const WCHAR szLine[] = { 'L','i','n','e',0 };
382 static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 };
383 static const WCHAR szCheckBox[] = { 'C','h','e','c','k','B','o','x',0 };
384 static const WCHAR szScrollableText[] = {
385     'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 };
386 static const WCHAR szComboBox[] = { 'C','o','m','b','o','B','o','x',0 };
387 static const WCHAR szEdit[] = { 'E','d','i','t',0 };
388 static const WCHAR szMaskedEdit[] = { 'M','a','s','k','e','d','E','d','i','t',0 };
389 static const WCHAR szPathEdit[] = { 'P','a','t','h','E','d','i','t',0 };
390 static const WCHAR szRadioButtonGroup[] = { 
391     'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 };
392
393 struct control_handler msi_dialog_handler[] =
394 {
395     { szText, msi_dialog_text_control },
396     { szButton, msi_dialog_button_control },
397     { szLine, msi_dialog_line_control },
398     { szBitmap, msi_dialog_bitmap_control },
399     { szCheckBox, msi_dialog_checkbox_control },
400     { szScrollableText, msi_dialog_scrolltext_control },
401     { szComboBox, msi_dialog_combo_control },
402     { szEdit, msi_dialog_edit_control },
403     { szMaskedEdit, msi_dialog_edit_control },
404     { szPathEdit, msi_dialog_pathedit_control },
405     { szRadioButtonGroup, msi_dialog_radiogroup_control },
406 };
407
408 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
409
410 static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param )
411 {
412     msi_dialog *dialog = param;
413     LPCWSTR control_type;
414     UINT i;
415
416     /* find and call the function that can create this type of control */
417     control_type = MSI_RecordGetString( rec, 3 );
418     for( i=0; i<NUM_CONTROL_TYPES; i++ )
419         if (!strcmpiW( msi_dialog_handler[i].control_type, control_type ))
420             break;
421     if( i != NUM_CONTROL_TYPES )
422         msi_dialog_handler[i].func( dialog, rec );
423     else
424         ERR("no handler for element type %s\n", debugstr_w(control_type));
425
426     return ERROR_SUCCESS;
427 }
428
429 static UINT msi_dialog_fill_controls( msi_dialog *dialog )
430 {
431     static const WCHAR query[] = {
432         'S','E','L','E','C','T',' ','*',' ',
433         'F','R','O','M',' ','C','o','n','t','r','o','l',' ',
434         'W','H','E','R','E',' ',
435            '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
436     UINT r;
437     MSIQUERY *view = NULL;
438     MSIPACKAGE *package = dialog->package;
439
440     TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
441
442     /* query the Control table for all the elements of the control */
443     r = MSI_OpenQuery( package->db, &view, query, dialog->name );
444     if( r != ERROR_SUCCESS )
445     {
446         ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
447         return ERROR_INVALID_PARAMETER;
448     }
449
450     r = MSI_IterateRecords( view, 0, msi_dialog_create_controls, dialog );
451     msiobj_release( &view->hdr );
452
453     return r;
454 }
455
456 static msi_control *msi_dialog_find_control( msi_dialog *dialog, LPCWSTR name )
457 {
458     msi_control *control;
459
460     for( control = dialog->control_list; control; control = control->next )
461         if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */
462             break;
463     return control;
464 }
465
466 static msi_control *msi_dialog_find_control_by_hwnd( msi_dialog *dialog, HWND hwnd )
467 {
468     msi_control *control;
469
470     for( control = dialog->control_list; control; control = control->next )
471         if( hwnd == control->hwnd )
472             break;
473     return control;
474 }
475
476 static UINT msi_dialog_set_control_condition( MSIRECORD *rec, LPVOID param )
477 {
478     static const WCHAR szHide[] = { 'H','i','d','e',0 };
479     static const WCHAR szShow[] = { 'S','h','o','w',0 };
480     static const WCHAR szDisable[] = { 'D','i','s','a','b','l','e',0 };
481     static const WCHAR szEnable[] = { 'E','n','a','b','l','e',0 };
482     msi_dialog *dialog = param;
483     msi_control *control;
484     LPCWSTR name, action, condition;
485     UINT r;
486
487     name = MSI_RecordGetString( rec, 2 );
488     action = MSI_RecordGetString( rec, 3 );
489     condition = MSI_RecordGetString( rec, 4 );
490     r = MSI_EvaluateConditionW( dialog->package, condition );
491     control = msi_dialog_find_control( dialog, name );
492     if( r && control )
493     {
494         TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
495
496         /* FIXME: case sensitive? */
497         if(!strcmpW(action, szHide))
498             ShowWindow(control->hwnd, SW_HIDE);
499         else if(!strcmpW(action, szShow))
500             ShowWindow(control->hwnd, SW_SHOW);
501         else if(!strcmpW(action, szDisable))
502             EnableWindow(control->hwnd, FALSE);
503         else if(!strcmpW(action, szEnable))
504             EnableWindow(control->hwnd, TRUE);
505         else
506             FIXME("Unhandled action %s\n", debugstr_w(action));
507     }
508
509     return ERROR_SUCCESS;
510 }
511
512 static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog )
513 {
514     static const WCHAR query[] = {
515       'S','E','L','E','C','T',' ','*',' ',
516       'F','R','O','M',' ',
517         'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
518       'W','H','E','R','E',' ',
519         '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0
520     };
521     UINT r;
522     MSIQUERY *view = NULL;
523     MSIPACKAGE *package = dialog->package;
524
525     TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
526
527     /* query the Control table for all the elements of the control */
528     r = MSI_OpenQuery( package->db, &view, query, dialog->name );
529     if( r != ERROR_SUCCESS )
530     {
531         ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
532         return ERROR_INVALID_PARAMETER;
533     }
534
535     r = MSI_IterateRecords( view, 0, msi_dialog_set_control_condition, dialog );
536     msiobj_release( &view->hdr );
537
538     return r;
539 }
540
541 /* figure out the height of 10 point MS Sans Serif */
542 static INT msi_dialog_get_sans_serif_height( HWND hwnd )
543 {
544     static const WCHAR szSansSerif[] = {
545         'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
546     LOGFONTW lf;
547     TEXTMETRICW tm;
548     BOOL r;
549     LONG height = 0;
550     HFONT hFont, hOldFont;
551     HDC hdc;
552
553     hdc = GetDC( hwnd );
554     if (hdc)
555     {
556         memset( &lf, 0, sizeof lf );
557         lf.lfHeight = MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
558         strcpyW( lf.lfFaceName, szSansSerif );
559         hFont = CreateFontIndirectW(&lf);
560         if (hFont)
561         {
562             hOldFont = SelectObject( hdc, hFont );
563             r = GetTextMetricsW( hdc, &tm );
564             if (r)
565                 height = tm.tmHeight;
566             SelectObject( hdc, hOldFont );
567             DeleteObject( hFont );
568         }
569         ReleaseDC( hwnd, hdc );
570     }
571     return height;
572 }
573
574 static LRESULT msi_dialog_oncreate( HWND hwnd, LPCREATESTRUCTW cs )
575 {
576     static const WCHAR query[] = {
577         'S','E','L','E','C','T',' ','*',' ',
578         'F','R','O','M',' ','D','i','a','l','o','g',' ',
579         'W','H','E','R','E',' ',
580            '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
581     static const WCHAR df[] = {
582         'D','e','f','a','u','l','t','U','I','F','o','n','t',0 };
583     msi_dialog *dialog = (msi_dialog*) cs->lpCreateParams;
584     MSIPACKAGE *package = dialog->package;
585     MSIQUERY *view = NULL;
586     MSIRECORD *rec = NULL;
587     DWORD width, height;
588     LPCWSTR text;
589     LPWSTR title = NULL;
590     UINT r;
591
592     TRACE("%p %p\n", dialog, package);
593
594     dialog->hwnd = hwnd;
595     SetWindowLongPtrW( hwnd, GWLP_USERDATA, (LONG_PTR) dialog );
596
597     /* fetch the associated record from the Dialog table */
598     r = MSI_OpenQuery( package->db, &view, query, dialog->name );
599     if( r != ERROR_SUCCESS )
600     {
601         ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
602         return -1;
603     }
604     MSI_ViewExecute( view, NULL );
605     MSI_ViewFetch( view, &rec );
606     MSI_ViewClose( view );
607     msiobj_release( &view->hdr );
608
609     if( !rec )
610     {
611         TRACE("No record found for dialog %s\n", debugstr_w(dialog->name));
612         return -1;
613     }
614
615     dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd);
616
617     width = MSI_RecordGetInteger( rec, 4 );
618     height = MSI_RecordGetInteger( rec, 5 );
619     dialog->attributes = MSI_RecordGetInteger( rec, 6 );
620     text = MSI_RecordGetString( rec, 7 );
621
622     width = msi_dialog_scale_unit( dialog, width );
623     height = msi_dialog_scale_unit( dialog, height ) + 25; /* FIXME */
624
625     dialog->default_font = load_dynamic_property( dialog->package, df, NULL );
626
627     deformat_string( dialog->package, text, &title );
628     SetWindowTextW( hwnd, title );
629     SetWindowPos( hwnd, 0, 0, 0, width, height,
630                   SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW );
631
632     HeapFree( GetProcessHeap(), 0, title );
633     msiobj_release( &rec->hdr );
634
635     msi_dialog_build_font_list( dialog );
636     msi_dialog_fill_controls( dialog );
637     msi_dialog_evaluate_control_conditions( dialog );
638
639     return 0;
640 }
641
642 static UINT msi_dialog_send_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
643 {
644     LPWSTR event_fmt = NULL, arg_fmt = NULL;
645
646     TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
647
648     deformat_string( dialog->package, event, &event_fmt );
649     deformat_string( dialog->package, arg, &arg_fmt );
650
651     dialog->event_handler( dialog->package, event_fmt, arg_fmt, dialog );
652
653     HeapFree( GetProcessHeap(), 0, event_fmt );
654     HeapFree( GetProcessHeap(), 0, arg_fmt );
655
656     return ERROR_SUCCESS;
657 }
658
659 static UINT msi_dialog_set_property( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
660 {
661     static const WCHAR szNullArg[] = { '{','}',0 };
662     LPWSTR p, prop, arg_fmt = NULL;
663     UINT len;
664
665     len = strlenW(event);
666     prop = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR));
667     strcpyW( prop, &event[1] );
668     p = strchrW( prop, ']' );
669     if( p && p[1] == 0 )
670     {
671         *p = 0;
672         if( strcmpW( szNullArg, arg ) )
673             deformat_string( dialog->package, arg, &arg_fmt );
674         MSI_SetPropertyW( dialog->package, prop, arg_fmt );
675     }
676     else
677         ERR("Badly formatted property string - what happens?\n");
678     HeapFree( GetProcessHeap(), 0, prop );
679     return ERROR_SUCCESS;
680 }
681
682 static UINT msi_dialog_control_event( MSIRECORD *rec, LPVOID param )
683 {
684     msi_dialog *dialog = param;
685     LPCWSTR condition, event, arg;
686     UINT r;
687
688     condition = MSI_RecordGetString( rec, 5 );
689     r = MSI_EvaluateConditionW( dialog->package, condition );
690     if( r )
691     {
692         event = MSI_RecordGetString( rec, 3 );
693         arg = MSI_RecordGetString( rec, 4 );
694         if( event[0] == '[' )
695             msi_dialog_set_property( dialog, event, arg );
696         else
697             msi_dialog_send_event( dialog, event, arg );
698     }
699
700     return ERROR_SUCCESS;
701 }
702
703 static UINT msi_dialog_button_handler( msi_dialog *dialog,
704                                        msi_control *control, WPARAM param )
705 {
706     static const WCHAR query[] = {
707       'S','E','L','E','C','T',' ','*',' ',
708       'F','R','O','M',' ','C','o','n','t','r','o','l','E','v','e','n','t',' ',
709       'W','H','E','R','E',' ',
710          '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
711       'A','N','D',' ',
712          '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
713       'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0
714     };
715     MSIQUERY *view = NULL;
716     UINT r;
717
718     if( HIWORD(param) != BN_CLICKED )
719         return ERROR_SUCCESS;
720
721     r = MSI_OpenQuery( dialog->package->db, &view, query,
722                        dialog->name, control->name );
723     if( r != ERROR_SUCCESS )
724     {
725         ERR("query failed\n");
726         return 0;
727     }
728
729     r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog );
730     msiobj_release( &view->hdr );
731
732     return r;
733 }
734
735 static UINT msi_dialog_get_checkbox_state( msi_dialog *dialog,
736                 msi_control *control )
737 {
738     WCHAR state[2] = { 0 };
739     DWORD sz = 2;
740
741     MSI_GetPropertyW( dialog->package, control->property, state, &sz );
742     return atoiW( state ) ? 1 : 0;
743 }
744
745 static void msi_dialog_set_checkbox_state( msi_dialog *dialog,
746                 msi_control *control, UINT state )
747 {
748     WCHAR szState[2] = { '0', 0 };
749
750     if( state )
751         szState[0]++;
752     MSI_SetPropertyW( dialog->package, control->property, szState );
753 }
754
755 static void msi_dialog_checkbox_sync_state( msi_dialog *dialog,
756                 msi_control *control )
757 {
758     UINT state;
759
760     state = msi_dialog_get_checkbox_state( dialog, control );
761     SendMessageW( control->hwnd, BM_SETCHECK,
762                   state ? BST_CHECKED : BST_UNCHECKED, 0 );
763 }
764
765 static UINT msi_dialog_checkbox_handler( msi_dialog *dialog,
766                 msi_control *control, WPARAM param )
767 {
768     UINT state;
769
770     if( HIWORD(param) != BN_CLICKED )
771         return ERROR_SUCCESS;
772
773     TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name),
774           debugstr_w(control->property));
775
776     state = msi_dialog_get_checkbox_state( dialog, control );
777     state = state ? 0 : 1;
778     msi_dialog_set_checkbox_state( dialog, control, state );
779     msi_dialog_checkbox_sync_state( dialog, control );
780
781     return msi_dialog_button_handler( dialog, control, param );
782 }
783
784 static UINT msi_dialog_edit_handler( msi_dialog *dialog,
785                 msi_control *control, WPARAM param )
786 {
787     UINT sz, r;
788     LPWSTR buf;
789
790     if( HIWORD(param) != EN_CHANGE )
791         return ERROR_SUCCESS;
792
793     TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name),
794           debugstr_w(control->property));
795
796     sz = 0x20;
797     buf = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
798     while( buf )
799     {
800         r = GetWindowTextW( control->hwnd, buf, sz );
801         if( r < (sz-1) )
802             break;
803             sz *= 2;
804         buf = HeapReAlloc( GetProcessHeap(), 0, buf, sz*sizeof(WCHAR) );
805     }
806
807     MSI_SetPropertyW( dialog->package, control->property, buf );
808
809     HeapFree( GetProcessHeap(), 0, buf );
810
811     return ERROR_SUCCESS;
812 }
813
814 static LRESULT msi_dialog_oncommand( msi_dialog *dialog, WPARAM param, HWND hwnd )
815 {
816     msi_control *control;
817
818     TRACE("%p %p %08x\n", dialog, hwnd, param);
819
820     control = msi_dialog_find_control_by_hwnd( dialog, hwnd );
821     if( control )
822     {
823         if( control->handler )
824         {
825             control->handler( dialog, control, param );
826             msi_dialog_evaluate_control_conditions( dialog );
827         }
828     }
829     else
830         ERR("button click from nowhere\n");
831     return 0;
832 }
833
834 static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg,
835                 WPARAM wParam, LPARAM lParam )
836 {
837     msi_dialog *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA );
838
839     switch (msg)
840     {
841     case WM_CREATE:
842         return msi_dialog_oncreate( hwnd, (LPCREATESTRUCTW)lParam );
843
844     case WM_COMMAND:
845         return msi_dialog_oncommand( dialog, wParam, (HWND)lParam );
846
847     case WM_DESTROY:
848         dialog->hwnd = NULL;
849         return 0;
850     }
851     return DefWindowProcW(hwnd, msg, wParam, lParam);
852 }
853
854 /* functions that interface to other modules within MSI */
855
856 msi_dialog *msi_dialog_create( MSIPACKAGE* package, LPCWSTR szDialogName,
857                                 msi_dialog_event_handler event_handler )
858 {
859     msi_dialog *dialog;
860     HWND hwnd;
861
862     TRACE("%p %s\n", package, debugstr_w(szDialogName));
863
864     /* allocate the structure for the dialog to use */
865     dialog = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
866                         sizeof *dialog + sizeof(WCHAR)*strlenW(szDialogName) );
867     if( !dialog )
868         return NULL;
869     strcpyW( dialog->name, szDialogName );
870     dialog->package = package;
871     dialog->event_handler = event_handler;
872
873     /* create the dialog window, don't show it yet */
874     hwnd = CreateWindowW( szMsiDialogClass, szDialogName, WS_OVERLAPPEDWINDOW,
875                      CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
876                      NULL, NULL, NULL, dialog );
877     if( !hwnd )
878     {
879         ERR("Failed to create dialog %s\n", debugstr_w( szDialogName ));
880         msi_dialog_destroy( dialog );
881         return NULL;
882     }
883
884     return dialog;
885 }
886
887 void msi_dialog_end_dialog( msi_dialog *dialog )
888 {
889     dialog->finished = 1;
890 }
891
892 UINT msi_dialog_run_message_loop( msi_dialog *dialog )
893 {
894     MSG msg;
895
896     if( dialog->attributes & msidbDialogAttributesVisible )
897     {
898         ShowWindow( dialog->hwnd, SW_SHOW );
899         UpdateWindow( dialog->hwnd );
900     }
901
902     if( dialog->attributes & msidbDialogAttributesModal )
903     {
904         while( !dialog->finished && GetMessageW( &msg, 0, 0, 0 ) )
905         {
906             TranslateMessage( &msg );
907             DispatchMessageW( &msg );
908         }
909     }
910     else
911         return ERROR_IO_PENDING;
912
913     return ERROR_SUCCESS;
914 }
915
916 void msi_dialog_check_messages( msi_dialog *dialog, HANDLE handle )
917 {
918     MSG msg;
919     DWORD r;
920
921     do
922     {
923         while( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ) )
924         {
925             TranslateMessage( &msg );
926             DispatchMessageW( &msg );
927         }
928         if( !handle )
929             break;
930         r = MsgWaitForMultipleObjects( 1, &handle, 0, INFINITE, QS_ALLEVENTS );
931     }
932     while( WAIT_OBJECT_0 != r );
933 }
934
935 void msi_dialog_do_preview( msi_dialog *dialog )
936 {
937     dialog->attributes |= msidbDialogAttributesVisible;
938     dialog->attributes &= ~msidbDialogAttributesModal;
939     msi_dialog_run_message_loop( dialog );
940 }
941
942 void msi_dialog_destroy( msi_dialog *dialog )
943 {
944     if( dialog->hwnd )
945         ShowWindow( dialog->hwnd, SW_HIDE );
946     
947     /* destroy the list of controls */
948     while( dialog->control_list )
949     {
950         msi_control *t = dialog->control_list;
951         dialog->control_list = t->next;
952         /* leave dialog->hwnd - destroying parent destroys child windows */
953         HeapFree( GetProcessHeap(), 0, t->property );
954         HeapFree( GetProcessHeap(), 0, t );
955     }
956
957     /* destroy the list of fonts */
958     while( dialog->font_list )
959     {
960         msi_font *t = dialog->font_list;
961         dialog->font_list = t->next;
962         DeleteObject( t->hfont );
963         HeapFree( GetProcessHeap(), 0, t );
964     }
965     HeapFree( GetProcessHeap(), 0, dialog->default_font );
966
967     if( dialog->hwnd )
968         DestroyWindow( dialog->hwnd );
969
970     dialog->package = NULL;
971     HeapFree( GetProcessHeap(), 0, dialog );
972 }
973
974 void msi_dialog_register_class( void )
975 {
976     WNDCLASSW cls;
977
978     ZeroMemory( &cls, sizeof cls );
979     cls.lpfnWndProc   = MSIDialog_WndProc;
980     cls.hInstance     = NULL;
981     cls.hIcon         = LoadIconW(0, (LPWSTR)IDI_APPLICATION);
982     cls.hCursor       = LoadCursorW(0, (LPWSTR)IDC_ARROW);
983     cls.hbrBackground = (HBRUSH)(COLOR_WINDOW);
984     cls.lpszMenuName  = NULL;
985     cls.lpszClassName = szMsiDialogClass;
986
987     RegisterClassW( &cls );
988 }
989
990 void msi_dialog_unregister_class( void )
991 {
992     UnregisterClassW( szMsiDialogClass, NULL );
993 }