ReactOS  0.4.15-dev-3331-g8ebe441
dialog.c
Go to the documentation of this file.
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Mike McCormack for CodeWeavers
5  * Copyright 2005 Aric Stewart for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #define COBJMACROS
23 #define NONAMELESSUNION
24 
25 #include <stdarg.h>
26 
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "winnls.h"
32 #include "msi.h"
33 #include "msidefs.h"
34 #include "ocidl.h"
35 #include "olectl.h"
36 #include "richedit.h"
37 #include "commctrl.h"
38 #include "winreg.h"
39 #include "shlwapi.h"
40 #include "shellapi.h"
41 
42 #include "wine/debug.h"
43 #include "wine/unicode.h"
44 
45 #include "msipriv.h"
46 #include "msiserver.h"
47 #include "resource.h"
48 
50 
52 
53 struct msi_control_tag;
57 typedef UINT (*control_event_handler)( msi_dialog *, const WCHAR *, const WCHAR * );
58 typedef UINT (*event_handler)( msi_dialog *, const WCHAR * );
59 
61 {
62  struct list entry;
74  float progress_max;
77  WCHAR name[1];
78 };
79 
80 typedef struct msi_font_tag
81 {
82  struct list entry;
85  WCHAR name[1];
86 } msi_font;
87 
89 {
99  struct list fonts;
100  struct list controls;
108 };
109 
111 {
112  struct list entry;
117 };
118 
121 {
124 };
125 
126 typedef struct
127 {
132 
133 static const WCHAR szMsiDialogClass[] = { 'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0 };
134 static const WCHAR szMsiHiddenWindow[] = { 'M','s','i','H','i','d','d','e','n','W','i','n','d','o','w',0 };
135 static const WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
136 static const WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
137 static const WCHAR szButtonData[] = { 'M','S','I','D','A','T','A',0 };
138 static const WCHAR szProgress[] = { 'P','r','o','g','r','e','s','s',0 };
139 static const WCHAR szText[] = { 'T','e','x','t',0 };
140 static const WCHAR szPushButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
141 static const WCHAR szLine[] = { 'L','i','n','e',0 };
142 static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 };
143 static const WCHAR szCheckBox[] = { 'C','h','e','c','k','B','o','x',0 };
144 static const WCHAR szScrollableText[] = { 'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 };
145 static const WCHAR szComboBox[] = { 'C','o','m','b','o','B','o','x',0 };
146 static const WCHAR szEdit[] = { 'E','d','i','t',0 };
147 static const WCHAR szMaskedEdit[] = { 'M','a','s','k','e','d','E','d','i','t',0 };
148 static const WCHAR szPathEdit[] = { 'P','a','t','h','E','d','i','t',0 };
149 static const WCHAR szProgressBar[] = { 'P','r','o','g','r','e','s','s','B','a','r',0 };
150 static const WCHAR szSetProgress[] = { 'S','e','t','P','r','o','g','r','e','s','s',0 };
151 static const WCHAR szRadioButtonGroup[] = { 'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 };
152 static const WCHAR szIcon[] = { 'I','c','o','n',0 };
153 static const WCHAR szSelectionTree[] = { 'S','e','l','e','c','t','i','o','n','T','r','e','e',0 };
154 static const WCHAR szGroupBox[] = { 'G','r','o','u','p','B','o','x',0 };
155 static const WCHAR szListBox[] = { 'L','i','s','t','B','o','x',0 };
156 static const WCHAR szDirectoryCombo[] = { 'D','i','r','e','c','t','o','r','y','C','o','m','b','o',0 };
157 static const WCHAR szDirectoryList[] = { 'D','i','r','e','c','t','o','r','y','L','i','s','t',0 };
158 static const WCHAR szVolumeCostList[] = { 'V','o','l','u','m','e','C','o','s','t','L','i','s','t',0 };
159 static const WCHAR szVolumeSelectCombo[] = { 'V','o','l','u','m','e','S','e','l','e','c','t','C','o','m','b','o',0 };
160 static const WCHAR szSelectionDescription[] = {'S','e','l','e','c','t','i','o','n','D','e','s','c','r','i','p','t','i','o','n',0};
161 static const WCHAR szSelectionPath[] = {'S','e','l','e','c','t','i','o','n','P','a','t','h',0};
162 static const WCHAR szHyperLink[] = {'H','y','p','e','r','L','i','n','k',0};
163 
164 /* dialog sequencing */
165 
166 #define WM_MSI_DIALOG_CREATE (WM_USER+0x100)
167 #define WM_MSI_DIALOG_DESTROY (WM_USER+0x101)
168 
169 #define USER_INSTALLSTATE_ALL 0x1000
170 
173 
174 #ifdef __REACTOS__
175 static HANDLE hPrevious = NULL;
176 #endif
177 
179 {
180  UINT sz, r;
181  LPWSTR buf;
182 
183  sz = 0x20;
184  buf = msi_alloc( sz*sizeof(WCHAR) );
185  while ( buf )
186  {
187  r = GetWindowTextW( hwnd, buf, sz );
188  if ( r < (sz - 1) )
189  break;
190  sz *= 2;
191  buf = msi_realloc( buf, sz*sizeof(WCHAR) );
192  }
193 
194  return buf;
195 }
196 
198 {
199  return MulDiv( val, dialog->scale, 12 );
200 }
201 
203 {
204  msi_control *control;
205 
206  if( !name )
207  return NULL;
208  if( !dialog->hwnd )
209  return NULL;
210  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
211  if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */
212  return control;
213  return NULL;
214 }
215 
217 {
218  msi_control *control;
219 
220  if( !type )
221  return NULL;
222  if( !dialog->hwnd )
223  return NULL;
224  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
225  if( !strcmpW( control->type, type ) ) /* FIXME: case sensitive? */
226  return control;
227  return NULL;
228 }
229 
231 {
232  msi_control *control;
233 
234  if( !dialog->hwnd )
235  return NULL;
236  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
237  if( hwnd == control->hwnd )
238  return control;
239  return NULL;
240 }
241 
243 {
245  LPWSTR ret = NULL;
246 
247  if (str)
248  deformat_string( package, str, &ret );
249  return ret;
250 }
251 
253 {
254  LPWSTR prop = NULL;
255 
256  if (!property)
257  return NULL;
258 
259  if (indirect)
260  prop = msi_dup_property( dialog->package->db, property );
261 
262  if (!prop)
263  prop = strdupW( property );
264 
265  return prop;
266 }
267 
268 /*
269  * msi_dialog_get_style
270  *
271  * Extract the {\style} string from the front of the text to display and
272  * update the pointer. Only the last style in a list is applied.
273  */
275 {
276  LPWSTR ret;
277  LPCWSTR q, i, first;
278  DWORD len;
279 
280  q = NULL;
281  *rest = p;
282  if( !p )
283  return NULL;
284 
285  while ((first = strchrW( p, '{' )) && (q = strchrW( first + 1, '}' )))
286  {
287  p = first + 1;
288  if( *p != '\\' && *p != '&' )
289  return NULL;
290 
291  /* little bit of sanity checking to stop us getting confused with RTF */
292  for( i=++p; i<q; i++ )
293  if( *i == '}' || *i == '\\' )
294  return NULL;
295  }
296 
297  if (!q)
298  return NULL;
299 
300  *rest = ++q;
301  len = q - p;
302 
303  ret = msi_alloc( len*sizeof(WCHAR) );
304  if( !ret )
305  return ret;
306  memcpy( ret, p, len*sizeof(WCHAR) );
307  ret[len-1] = 0;
308  return ret;
309 }
310 
312 {
314  msi_font *font;
315  LPCWSTR face, name;
316  LOGFONTW lf;
317  INT style;
318  HDC hdc;
319 
320  /* create a font and add it to the list */
321  name = MSI_RecordGetString( rec, 1 );
323  strcpyW( font->name, name );
324  list_add_head( &dialog->fonts, &font->entry );
325 
326  font->color = MSI_RecordGetInteger( rec, 4 );
327 
328  memset( &lf, 0, sizeof lf );
329  face = MSI_RecordGetString( rec, 2 );
330  lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
331  style = MSI_RecordGetInteger( rec, 5 );
333  lf.lfWeight = FW_BOLD;
335  lf.lfItalic = TRUE;
337  lf.lfUnderline = TRUE;
339  lf.lfStrikeOut = TRUE;
341 
342  /* adjust the height */
343  hdc = GetDC( dialog->hwnd );
344  if (hdc)
345  {
347  ReleaseDC( dialog->hwnd, hdc );
348  }
349 
350  font->hfont = CreateFontIndirectW( &lf );
351 
352  TRACE("Adding font style %s\n", debugstr_w(font->name) );
353 
354  return ERROR_SUCCESS;
355 }
356 
358 {
359  msi_font *font = NULL;
360 
362  if( !strcmpW( font->name, name ) ) /* FIXME: case sensitive? */
363  break;
364 
365  return font;
366 }
367 
369 {
370  msi_font *font;
371 
373  if( font )
374  SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE );
375  else
376  ERR("No font entry for %s\n", debugstr_w(name));
377  return ERROR_SUCCESS;
378 }
379 
381 {
382  static const WCHAR query[] = {
383  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
384  '`','T','e','x','t','S','t','y','l','e','`',0};
385  MSIQUERY *view;
386  UINT r;
387 
388  TRACE("dialog %p\n", dialog );
389 
390  r = MSI_OpenQuery( dialog->package->db, &view, query );
391  if( r != ERROR_SUCCESS )
392  return r;
393 
395  msiobj_release( &view->hdr );
396  return r;
397 }
398 
400 {
401  list_remove( &t->entry );
402  /* leave dialog->hwnd - destroying parent destroys child windows */
403  msi_free( t->property );
404  msi_free( t->value );
405  if( t->hBitmap )
406  DeleteObject( t->hBitmap );
407  if( t->hIcon )
408  DestroyIcon( t->hIcon );
409  msi_free( t->tabnext );
410  msi_free( t->type );
411  if (t->hDll)
412  FreeLibrary( t->hDll );
413  msi_free( t );
414 }
415 
417  const WCHAR *szCls, const WCHAR *name, const WCHAR *text,
419 {
420  DWORD x, y, width, height;
421  LPWSTR font = NULL, title_font = NULL;
422  LPCWSTR title = NULL;
423  msi_control *control;
424 
425  style |= WS_CHILD;
426 
427  control = msi_alloc( FIELD_OFFSET( msi_control, name[strlenW( name ) + 1] ));
428  if (!control)
429  return NULL;
430 
431  strcpyW( control->name, name );
432  list_add_tail( &dialog->controls, &control->entry );
433  control->handler = NULL;
434  control->update = NULL;
435  control->property = NULL;
436  control->value = NULL;
437  control->hBitmap = NULL;
438  control->hIcon = NULL;
439  control->hDll = NULL;
440  control->tabnext = strdupW( MSI_RecordGetString( rec, 11) );
441  control->type = strdupW( MSI_RecordGetString( rec, 3 ) );
442  control->progress_current = 0;
443  control->progress_max = 100;
444  control->progress_backwards = FALSE;
445 
446  x = MSI_RecordGetInteger( rec, 4 );
447  y = MSI_RecordGetInteger( rec, 5 );
448  width = MSI_RecordGetInteger( rec, 6 );
449  height = MSI_RecordGetInteger( rec, 7 );
450 
455 
456  if( text )
457  {
458  deformat_string( dialog->package, text, &title_font );
459  font = msi_dialog_get_style( title_font, &title );
460  }
461 
462  control->hwnd = CreateWindowExW( exstyle, szCls, title, style,
463  x, y, width, height, parent, NULL, NULL, NULL );
464 
465  TRACE("Dialog %s control %s hwnd %p\n",
466  debugstr_w(dialog->name), debugstr_w(text), control->hwnd );
467 
468  msi_dialog_set_font( dialog, control->hwnd,
469  font ? font : dialog->default_font );
470 
471  msi_free( title_font );
472  msi_free( font );
473 
474  return control;
475 }
476 
478 {
479  MSIRECORD *rec;
480  LPWSTR text;
481 
482  static const WCHAR query[] = {
483  's','e','l','e','c','t',' ','*',' ',
484  'f','r','o','m',' ','`','U','I','T','e','x','t','`',' ',
485  'w','h','e','r','e',' ','`','K','e','y','`',' ','=',' ','\'','%','s','\'',0
486  };
487 
488  rec = MSI_QueryGetRecord( dialog->package->db, query, key );
489  if (!rec) return NULL;
490  text = strdupW( MSI_RecordGetString( rec, 2 ) );
491  msiobj_release( &rec->hdr );
492  return text;
493 }
494 
496 {
497  static const WCHAR query[] = {
498  's','e','l','e','c','t',' ','*',' ',
499  'f','r','o','m',' ','B','i','n','a','r','y',' ',
500  'w','h','e','r','e',' ',
501  '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0
502  };
503 
504  return MSI_QueryGetRecord( db, query, name );
505 }
506 
508  UINT cx, UINT cy, UINT flags )
509 {
510  MSIRECORD *rec;
511  HANDLE himage = NULL;
512  LPWSTR tmp;
513  UINT r;
514 
515  TRACE("%p %s %u %u %08x\n", db, debugstr_w(name), cx, cy, flags);
516 
517  if (!(tmp = msi_create_temp_file( db ))) return NULL;
518 
519  rec = msi_get_binary_record( db, name );
520  if( rec )
521  {
522  r = MSI_RecordStreamToFile( rec, 2, tmp );
523  if( r == ERROR_SUCCESS )
524  {
525  himage = LoadImageW( 0, tmp, type, cx, cy, flags );
526  }
527  msiobj_release( &rec->hdr );
528  }
529  DeleteFileW( tmp );
530 
531  msi_free( tmp );
532  return himage;
533 }
534 
535 static HICON msi_load_icon( MSIDATABASE *db, LPCWSTR text, UINT attributes )
536 {
537  DWORD cx = 0, cy = 0, flags;
538 
540  if( attributes & msidbControlAttributesFixedSize )
541  {
542  flags &= ~LR_DEFAULTSIZE;
543  if( attributes & msidbControlAttributesIconSize16 )
544  {
545  cx += 16;
546  cy += 16;
547  }
548  if( attributes & msidbControlAttributesIconSize32 )
549  {
550  cx += 32;
551  cy += 32;
552  }
553  /* msidbControlAttributesIconSize48 handled by above logic */
554  }
555  return msi_load_image( db, text, IMAGE_ICON, cx, cy, flags );
556 }
557 
559 {
560  msi_control *control;
561 
562  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
563  {
564  if ( control->property && !strcmpW( control->property, property ) && control->update )
565  control->update( dialog, control );
566  }
567 }
568 
570 {
571  msi_control *control;
572 
573  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
574  {
575  if ( control->property && control->update )
576  control->update( dialog, control );
577  }
578 }
579 
581 {
582  UINT r = msi_set_property( package->db, property, value, -1 );
583  if (r == ERROR_SUCCESS && !strcmpW( property, szSourceDir ))
584  msi_reset_folders( package, TRUE );
585 }
586 
588 {
589  TVITEMW tvi;
590 
591  /* get the feature from the item */
592  memset( &tvi, 0, sizeof tvi );
593  tvi.hItem = hItem;
594  tvi.mask = TVIF_PARAM | TVIF_HANDLE;
595  SendMessageW( hwnd, TVM_GETITEMW, 0, (LPARAM)&tvi );
596  return (MSIFEATURE *)tvi.lParam;
597 }
598 
600 {
605 };
606 
608 {
609  struct msi_selection_tree_info *info = GetPropW( control->hwnd, szButtonData );
610  return msi_seltree_feature_from_item( control->hwnd, info->selected );
611 }
612 
613 static void dialog_handle_event( msi_dialog *dialog, const WCHAR *control,
614  const WCHAR *attribute, MSIRECORD *rec )
615 {
616  msi_control* ctrl;
617 
618  ctrl = msi_dialog_find_control( dialog, control );
619  if (!ctrl)
620  return;
621  if( !strcmpW( attribute, szText ) )
622  {
623  const WCHAR *font_text, *text = NULL;
624  WCHAR *font, *text_fmt = NULL;
625 
626  font_text = MSI_RecordGetString( rec , 1 );
627  font = msi_dialog_get_style( font_text, &text );
628  deformat_string( dialog->package, text, &text_fmt );
629  if (text_fmt) text = text_fmt;
630  else text = szEmpty;
631 
632  SetWindowTextW( ctrl->hwnd, text );
633 
634  msi_free( font );
635  msi_free( text_fmt );
637  }
638  else if( !strcmpW( attribute, szProgress ) )
639  {
640  DWORD func, val1, val2, units;
641 
642  func = MSI_RecordGetInteger( rec, 1 );
643  val1 = MSI_RecordGetInteger( rec, 2 );
644  val2 = MSI_RecordGetInteger( rec, 3 );
645 
646  TRACE("progress: func %u val1 %u val2 %u\n", func, val1, val2);
647 
648  units = val1 / 512;
649  switch (func)
650  {
651  case 0: /* init */
652  SendMessageW( ctrl->hwnd, PBM_SETRANGE, 0, MAKELPARAM(0,100) );
653  if (val2)
654  {
655  ctrl->progress_max = units ? units : 100;
656  ctrl->progress_current = units;
657  ctrl->progress_backwards = TRUE;
658  SendMessageW( ctrl->hwnd, PBM_SETPOS, 100, 0 );
659  }
660  else
661  {
662  ctrl->progress_max = units ? units : 100;
663  ctrl->progress_current = 0;
664  ctrl->progress_backwards = FALSE;
665  SendMessageW( ctrl->hwnd, PBM_SETPOS, 0, 0 );
666  }
667  break;
668  case 1: /* action data increment */
669  if (val2) dialog->package->action_progress_increment = val1;
670  else dialog->package->action_progress_increment = 0;
671  break;
672  case 2: /* move */
673  if (ctrl->progress_backwards)
674  {
675  if (units >= ctrl->progress_current) ctrl->progress_current -= units;
676  else ctrl->progress_current = 0;
677  }
678  else
679  {
680  if (ctrl->progress_current + units < ctrl->progress_max) ctrl->progress_current += units;
681  else ctrl->progress_current = ctrl->progress_max;
682  }
683  SendMessageW( ctrl->hwnd, PBM_SETPOS, MulDiv(100, ctrl->progress_current, ctrl->progress_max), 0 );
684  break;
685  case 3: /* add */
686  ctrl->progress_max += units;
687  break;
688  default:
689  FIXME("Unknown progress message %u\n", func);
690  break;
691  }
692  }
693  else if ( !strcmpW( attribute, szProperty ) )
694  {
696  if (feature) msi_dialog_set_property( dialog->package, ctrl->property, feature->Directory );
697  }
698  else if ( !strcmpW( attribute, szSelectionPath ) )
699  {
702  if (!path) return;
703  SetWindowTextW( ctrl->hwnd, path );
704  msi_free(path);
705  }
706  else
707  {
708  FIXME("Attribute %s not being set\n", debugstr_w(attribute));
709  return;
710  }
711 }
712 
713 static void event_subscribe( msi_dialog *dialog, const WCHAR *event, const WCHAR *control, const WCHAR *attribute )
714 {
715  struct subscriber *sub;
716 
717  TRACE("dialog %s event %s control %s attribute %s\n", debugstr_w(dialog->name), debugstr_w(event),
719 
720  LIST_FOR_EACH_ENTRY( sub, &dialog->package->subscriptions, struct subscriber, entry )
721  {
722  if (sub->dialog == dialog &&
723  !strcmpiW( sub->event, event ) &&
724  !strcmpiW( sub->control, control ) &&
725  !strcmpiW( sub->attribute, attribute ))
726  {
727  TRACE("already subscribed\n");
728  return;
729  };
730  }
731  if (!(sub = msi_alloc( sizeof(*sub) ))) return;
732  sub->dialog = dialog;
733  sub->event = strdupW( event );
734  sub->control = strdupW( control );
735  sub->attribute = strdupW( attribute );
736  list_add_tail( &dialog->package->subscriptions, &sub->entry );
737 }
738 
740 {
742  const WCHAR *control;
743 };
744 
745 static UINT map_event( MSIRECORD *row, void *param )
746 {
747  struct dialog_control *dc = param;
748  const WCHAR *event = MSI_RecordGetString( row, 3 );
749  const WCHAR *attribute = MSI_RecordGetString( row, 4 );
750 
751  event_subscribe( dc->dialog, event, dc->control, attribute );
752  return ERROR_SUCCESS;
753 }
754 
756 {
757  static const WCHAR queryW[] =
758  {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
759  '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ',
760  'W','H','E','R','E',' ','`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
761  'A','N','D',' ','`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0};
762  MSIQUERY *view;
764  {
765  dialog,
766  control
767  };
768 
769  if (!MSI_OpenQuery( dialog->package->db, &view, queryW, dialog->name, control ))
770  {
772  msiobj_release( &view->hdr );
773  }
774 }
775 
776 /* everything except radio buttons */
778  MSIRECORD *rec, LPCWSTR szCls, DWORD style )
779 {
780  DWORD attributes;
781  const WCHAR *text = NULL, *name, *control_type;
782  DWORD exstyle = 0;
783 
784  name = MSI_RecordGetString( rec, 2 );
785  control_type = MSI_RecordGetString( rec, 3 );
786  attributes = MSI_RecordGetInteger( rec, 8 );
788 
789  TRACE("%s, %s, %08x, %s, %08x\n", debugstr_w(szCls), debugstr_w(name),
790  attributes, debugstr_w(text), style);
791 
792  if( attributes & msidbControlAttributesVisible )
793  style |= WS_VISIBLE;
794  if( ~attributes & msidbControlAttributesEnabled )
795  style |= WS_DISABLED;
796  if( attributes & msidbControlAttributesSunken )
797  exstyle |= WS_EX_CLIENTEDGE;
798 
800 
801  return dialog_create_window( dialog, rec, exstyle, szCls, name, text, style, dialog->hwnd );
802 }
803 
805 {
809 };
810 
811 /*
812  * we don't erase our own background,
813  * so we have to make sure that the parent window redraws first
814  */
816 {
817  HWND hParent;
818  RECT rc;
819 
820  hParent = GetParent( hWnd );
821  GetWindowRect( hWnd, &rc );
822  MapWindowPoints( NULL, hParent, (LPPOINT) &rc, 2 );
823  InvalidateRect( hParent, &rc, TRUE );
824 }
825 
826 static LRESULT WINAPI
828 {
829  struct msi_text_info *info;
830  LRESULT r = 0;
831 
832  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
833 
835 
836  if( msg == WM_CTLCOLORSTATIC &&
837  ( info->attributes & msidbControlAttributesTransparent ) )
838  {
841  }
842 
843  r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
844  if ( info->font )
845  SetTextColor( (HDC)wParam, info->font->color );
846 
847  switch( msg )
848  {
849  case WM_SETTEXT:
851  break;
852  case WM_NCDESTROY:
853  msi_free( info );
855  break;
856  }
857 
858  return r;
859 }
860 
862 {
863  msi_control *control;
864  struct msi_text_info *info;
865  LPCWSTR text, ptr, prop, control_name;
866  LPWSTR font_name;
867 
868  TRACE("%p %p\n", dialog, rec);
869 
870  control = msi_dialog_add_control( dialog, rec, szStatic, SS_LEFT | WS_GROUP );
871  if( !control )
872  return ERROR_FUNCTION_FAILED;
873 
874  info = msi_alloc( sizeof *info );
875  if( !info )
876  return ERROR_SUCCESS;
877 
878  control_name = MSI_RecordGetString( rec, 2 );
879  control->attributes = MSI_RecordGetInteger( rec, 8 );
880  prop = MSI_RecordGetString( rec, 9 );
881  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
882 
883  text = MSI_RecordGetString( rec, 10 );
884  font_name = msi_dialog_get_style( text, &ptr );
885  info->font = ( font_name ) ? msi_dialog_find_font( dialog, font_name ) : NULL;
886  msi_free( font_name );
887 
888  info->attributes = MSI_RecordGetInteger( rec, 8 );
889  if( info->attributes & msidbControlAttributesTransparent )
891 
892  info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
894  SetPropW( control->hwnd, szButtonData, info );
895 
897  return ERROR_SUCCESS;
898 }
899 
900 /* strip any leading text style label from text field */
902 {
903  WCHAR *p, *text;
904 
905  text = msi_get_deformatted_field( package, rec, 10 );
906  if (!text)
907  return NULL;
908 
909  p = text;
910  while (*p && *p != '{') p++;
911  if (!*p++) return text;
912 
913  while (*p && *p != '}') p++;
914  if (!*p++) return text;
915 
916  p = strdupW( p );
917  msi_free( text );
918  return p;
919 }
920 
922 {
923  static const WCHAR szNullArg[] = {'{','}',0};
924  LPWSTR p, prop, arg_fmt = NULL;
925  UINT len;
926 
927  len = strlenW( event );
928  prop = msi_alloc( len * sizeof(WCHAR) );
929  strcpyW( prop, &event[1] );
930  p = strchrW( prop, ']' );
931  if (p && (p[1] == 0 || p[1] == ' '))
932  {
933  *p = 0;
934  if (strcmpW( szNullArg, arg ))
935  deformat_string( dialog->package, arg, &arg_fmt );
936  msi_dialog_set_property( dialog->package, prop, arg_fmt );
938  msi_free( arg_fmt );
939  }
940  else ERR("Badly formatted property string - what happens?\n");
941  msi_free( prop );
942  return ERROR_SUCCESS;
943 }
944 
946 {
947  LPWSTR event_fmt = NULL, arg_fmt = NULL;
948 
949  TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
950 
951  deformat_string( dialog->package, event, &event_fmt );
952  deformat_string( dialog->package, arg, &arg_fmt );
953 
954  dialog->event_handler( dialog, event_fmt, arg_fmt );
955 
956  msi_free( event_fmt );
957  msi_free( arg_fmt );
958 
959  return ERROR_SUCCESS;
960 }
961 
963 {
966  UINT r;
967 
968  condition = MSI_RecordGetString( rec, 5 );
969  r = MSI_EvaluateConditionW( dialog->package, condition );
970  if (r == MSICONDITION_TRUE || r == MSICONDITION_NONE)
971  {
972  event = MSI_RecordGetString( rec, 3 );
973  arg = MSI_RecordGetString( rec, 4 );
974  if (event[0] == '[')
976  else
978  }
979  return ERROR_SUCCESS;
980 }
981 
983 {
984  static const WCHAR query[] = {
985  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
986  'C','o','n','t','r','o','l','E','v','e','n','t',' ','W','H','E','R','E',' ',
987  '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ','A','N','D',' ',
988  '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
989  'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0};
990  MSIQUERY *view;
991  UINT r;
992 
993  if (HIWORD(param) != BN_CLICKED)
994  return ERROR_SUCCESS;
995 
996  r = MSI_OpenQuery( dialog->package->db, &view, query, dialog->name, control->name );
997  if (r != ERROR_SUCCESS)
998  {
999  ERR("query failed\n");
1000  return ERROR_SUCCESS;
1001  }
1003  msiobj_release( &view->hdr );
1004 
1005  /* dialog control events must be processed last regardless of ordering */
1006  if (dialog->pending_event)
1007  {
1008  r = dialog->pending_event( dialog, dialog->pending_argument );
1009 
1010  msi_free( dialog->pending_argument );
1011  dialog->pending_event = NULL;
1012  dialog->pending_argument = NULL;
1013  }
1014  return r;
1015 }
1016 
1018 {
1019  msi_control *control;
1021 
1022  TRACE("%p %p\n", dialog, rec);
1023 
1024  style = WS_TABSTOP;
1025  attributes = MSI_RecordGetInteger( rec, 8 );
1027  style |= BS_ICON;
1028 
1029  control = msi_dialog_add_control( dialog, rec, szButton, style );
1030  if( !control )
1031  return ERROR_FUNCTION_FAILED;
1032 
1034 
1036  {
1037  /* set the icon */
1038  LPWSTR name = msi_get_binary_name( dialog->package, rec );
1039  control->hIcon = msi_load_icon( dialog->package->db, name, attributes );
1040  if (control->hIcon)
1041  {
1042  SendMessageW( control->hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM) control->hIcon );
1043  }
1044  else
1045  ERR("Failed to load icon %s\n", debugstr_w(name));
1046  msi_free( name );
1047  }
1048 
1049  return ERROR_SUCCESS;
1050 }
1051 
1053 {
1054  static const WCHAR query[] = {
1055  'S','E','L','E','C','T',' ','*',' ',
1056  'F','R','O','M',' ','`','C','h','e','c','k','B','o','x','`',' ',
1057  'W','H','E','R','E',' ',
1058  '`','P','r','o','p','e','r','t','y','`',' ','=',' ',
1059  '\'','%','s','\'',0
1060  };
1061  MSIRECORD *rec = NULL;
1062  LPWSTR ret = NULL;
1063 
1064  /* find if there is a value associated with the checkbox */
1065  rec = MSI_QueryGetRecord( dialog->package->db, query, prop );
1066  if (!rec)
1067  return ret;
1068 
1069  ret = msi_get_deformatted_field( dialog->package, rec, 2 );
1070  if( ret && !ret[0] )
1071  {
1072  msi_free( ret );
1073  ret = NULL;
1074  }
1075  msiobj_release( &rec->hdr );
1076  if (ret)
1077  return ret;
1078 
1079  ret = msi_dup_property( dialog->package->db, prop );
1080  if( ret && !ret[0] )
1081  {
1082  msi_free( ret );
1083  ret = NULL;
1084  }
1085 
1086  return ret;
1087 }
1088 
1090 {
1091  WCHAR state[2] = {0};
1092  DWORD sz = 2;
1093 
1094  msi_get_property( dialog->package->db, control->property, state, &sz );
1095  return state[0] ? 1 : 0;
1096 }
1097 
1099 {
1100  static const WCHAR szState[] = {'1',0};
1101  LPCWSTR val;
1102 
1103  /* if uncheck then the property is set to NULL */
1104  if (!state)
1105  {
1106  msi_dialog_set_property( dialog->package, control->property, NULL );
1107  return;
1108  }
1109 
1110  /* check for a custom state */
1111  if (control->value && control->value[0])
1112  val = control->value;
1113  else
1114  val = szState;
1115 
1116  msi_dialog_set_property( dialog->package, control->property, val );
1117 }
1118 
1120 {
1123 }
1124 
1126 {
1127  UINT state;
1128 
1129  if (HIWORD(param) != BN_CLICKED)
1130  return ERROR_SUCCESS;
1131 
1132  TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
1133 
1135  state = state ? 0 : 1;
1138 
1139  return msi_dialog_button_handler( dialog, control, param );
1140 }
1141 
1143 {
1144  msi_control *control;
1145  LPCWSTR prop;
1146 
1147  TRACE("%p %p\n", dialog, rec);
1148 
1152  prop = MSI_RecordGetString( rec, 9 );
1153  if (prop)
1154  {
1155  control->property = strdupW( prop );
1156  control->value = msi_get_checkbox_value( dialog, prop );
1157  TRACE("control %s value %s\n", debugstr_w(control->property), debugstr_w(control->value));
1158  }
1160  return ERROR_SUCCESS;
1161 }
1162 
1164 {
1165  DWORD attributes;
1166  LPCWSTR name;
1167  DWORD style, exstyle = 0;
1168  DWORD x, y, width, height;
1169  msi_control *control;
1170 
1171  TRACE("%p %p\n", dialog, rec);
1172 
1174 
1175  name = MSI_RecordGetString( rec, 2 );
1176  attributes = MSI_RecordGetInteger( rec, 8 );
1177 
1179  style |= WS_VISIBLE;
1181  style |= WS_DISABLED;
1183  exstyle |= WS_EX_CLIENTEDGE;
1184 
1186 
1187  control = msi_alloc( FIELD_OFFSET(msi_control, name[strlenW( name ) + 1] ));
1188  if (!control)
1189  return ERROR_OUTOFMEMORY;
1190 
1191  strcpyW( control->name, name );
1192  list_add_head( &dialog->controls, &control->entry );
1193  control->handler = NULL;
1194  control->property = NULL;
1195  control->value = NULL;
1196  control->hBitmap = NULL;
1197  control->hIcon = NULL;
1198  control->hDll = NULL;
1199  control->tabnext = strdupW( MSI_RecordGetString( rec, 11) );
1200  control->type = strdupW( MSI_RecordGetString( rec, 3 ) );
1201  control->progress_current = 0;
1202  control->progress_max = 100;
1203  control->progress_backwards = FALSE;
1204 
1205  x = MSI_RecordGetInteger( rec, 4 );
1206  y = MSI_RecordGetInteger( rec, 5 );
1207  width = MSI_RecordGetInteger( rec, 6 );
1208 
1212  height = 2; /* line is exactly 2 units in height */
1213 
1214  control->hwnd = CreateWindowExW( exstyle, szStatic, NULL, style,
1215  x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
1216 
1217  TRACE("Dialog %s control %s hwnd %p\n",
1218  debugstr_w(dialog->name), debugstr_w(name), control->hwnd );
1219 
1220  return ERROR_SUCCESS;
1221 }
1222 
1223 /******************** Scroll Text ********************************************/
1224 
1226 {
1230 };
1231 
1232 static LRESULT WINAPI
1234 {
1235  struct msi_scrolltext_info *info;
1236  HRESULT r;
1237 
1238  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
1239 
1241 
1242  r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
1243 
1244  switch( msg )
1245  {
1246  case WM_GETDLGCODE:
1247  return DLGC_WANTARROWS;
1248  case WM_NCDESTROY:
1249  msi_free( info );
1251  break;
1252  case WM_PAINT:
1253  /* native MSI sets a wait cursor here */
1254  msi_dialog_button_handler( info->dialog, info->control, BN_CLICKED );
1255  break;
1256  }
1257  return r;
1258 }
1259 
1261 {
1265 };
1266 
1267 static DWORD CALLBACK
1269 {
1270  struct msi_streamin_info *info = (struct msi_streamin_info*) arg;
1271 
1272  if( (count + info->offset) > info->length )
1273  count = info->length - info->offset;
1274  memcpy( buffer, &info->string[ info->offset ], count );
1275  *pcb = count;
1276  info->offset += count;
1277 
1278  TRACE("%d/%d\n", info->offset, info->length);
1279 
1280  return 0;
1281 }
1282 
1284 {
1285  struct msi_streamin_info info;
1286  EDITSTREAM es;
1287 
1288  info.string = strdupWtoA( text );
1289  info.offset = 0;
1290  info.length = lstrlenA( info.string ) + 1;
1291 
1292  es.dwCookie = (DWORD_PTR) &info;
1293  es.dwError = 0;
1294  es.pfnCallback = msi_richedit_stream_in;
1295 
1296  SendMessageW( control->hwnd, EM_STREAMIN, SF_RTF, (LPARAM) &es );
1297 
1298  msi_free( info.string );
1299 }
1300 
1302 {
1303  static const WCHAR szRichEdit20W[] = {'R','i','c','h','E','d','i','t','2','0','W',0};
1304  struct msi_scrolltext_info *info;
1306  HMODULE hRichedit;
1307  LPCWSTR text;
1308  DWORD style;
1309 
1310  info = msi_alloc( sizeof *info );
1311  if (!info)
1312  return ERROR_FUNCTION_FAILED;
1313 
1314  hRichedit = LoadLibraryA("riched20");
1315 
1318  control = msi_dialog_add_control( dialog, rec, szRichEdit20W, style );
1319  if (!control)
1320  {
1321  FreeLibrary( hRichedit );
1322  msi_free( info );
1323  return ERROR_FUNCTION_FAILED;
1324  }
1325 
1326  control->hDll = hRichedit;
1327 
1328  info->dialog = dialog;
1329  info->control = control;
1330 
1331  /* subclass the static control */
1335 
1336  /* add the text into the richedit */
1337  text = MSI_RecordGetString( rec, 10 );
1338  if (text)
1340 
1341  return ERROR_SUCCESS;
1342 }
1343 
1345  INT cx, INT cy, DWORD flags )
1346 {
1347  HBITMAP hOleBitmap = 0, hBitmap = 0, hOldSrcBitmap, hOldDestBitmap;
1348  MSIRECORD *rec = NULL;
1349  IStream *stm = NULL;
1350  IPicture *pic = NULL;
1351  HDC srcdc, destdc;
1352  BITMAP bm;
1353  UINT r;
1354 
1355  rec = msi_get_binary_record( db, name );
1356  if( !rec )
1357  goto end;
1358 
1359  r = MSI_RecordGetIStream( rec, 2, &stm );
1360  msiobj_release( &rec->hdr );
1361  if( r != ERROR_SUCCESS )
1362  goto end;
1363 
1364  r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (LPVOID*) &pic );
1365  IStream_Release( stm );
1366  if( FAILED( r ) )
1367  {
1368  ERR("failed to load picture\n");
1369  goto end;
1370  }
1371 
1372  r = IPicture_get_Handle( pic, (OLE_HANDLE*) &hOleBitmap );
1373  if( FAILED( r ) )
1374  {
1375  ERR("failed to get bitmap handle\n");
1376  goto end;
1377  }
1378 
1379  /* make the bitmap the desired size */
1380  r = GetObjectW( hOleBitmap, sizeof bm, &bm );
1381  if (r != sizeof bm )
1382  {
1383  ERR("failed to get bitmap size\n");
1384  goto end;
1385  }
1386 
1387  if (flags & LR_DEFAULTSIZE)
1388  {
1389  cx = bm.bmWidth;
1390  cy = bm.bmHeight;
1391  }
1392 
1393  srcdc = CreateCompatibleDC( NULL );
1394  hOldSrcBitmap = SelectObject( srcdc, hOleBitmap );
1395  destdc = CreateCompatibleDC( NULL );
1396  hBitmap = CreateCompatibleBitmap( srcdc, cx, cy );
1397  hOldDestBitmap = SelectObject( destdc, hBitmap );
1398  StretchBlt( destdc, 0, 0, cx, cy,
1399  srcdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
1400  SelectObject( srcdc, hOldSrcBitmap );
1401  SelectObject( destdc, hOldDestBitmap );
1402  DeleteDC( srcdc );
1403  DeleteDC( destdc );
1404 
1405 end:
1406  if ( pic )
1407  IPicture_Release( pic );
1408  return hBitmap;
1409 }
1410 
1412 {
1413  UINT cx, cy, flags, style, attributes;
1415  LPWSTR name;
1416 
1419 
1420  attributes = MSI_RecordGetInteger( rec, 8 );
1421  if( attributes & msidbControlAttributesFixedSize )
1422  {
1423  flags |= LR_DEFAULTSIZE;
1424  style |= SS_CENTERIMAGE;
1425  }
1426 
1428  cx = MSI_RecordGetInteger( rec, 6 );
1429  cy = MSI_RecordGetInteger( rec, 7 );
1432 
1433  name = msi_get_binary_name( dialog->package, rec );
1434  control->hBitmap = msi_load_picture( dialog->package->db, name, cx, cy, flags );
1435  if( control->hBitmap )
1438  else
1439  ERR("Failed to load bitmap %s\n", debugstr_w(name));
1440 
1441  msi_free( name );
1442 
1443  return ERROR_SUCCESS;
1444 }
1445 
1447 {
1449  DWORD attributes;
1450  LPWSTR name;
1451 
1452  TRACE("\n");
1453 
1456 
1457  attributes = MSI_RecordGetInteger( rec, 8 );
1458  name = msi_get_binary_name( dialog->package, rec );
1459  control->hIcon = msi_load_icon( dialog->package->db, name, attributes );
1460  if( control->hIcon )
1462  else
1463  ERR("Failed to load bitmap %s\n", debugstr_w(name));
1464  msi_free( name );
1465  return ERROR_SUCCESS;
1466 }
1467 
1468 /******************** Combo Box ***************************************/
1469 
1471 {
1478 };
1479 
1481 {
1482  struct msi_combobox_info *info;
1483  LRESULT r;
1484  DWORD j;
1485 
1486  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
1487 
1489  if (!info)
1490  return 0;
1491 
1492  r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
1493 
1494  switch (msg)
1495  {
1496  case WM_NCDESTROY:
1497  for (j = 0; j < info->num_items; j++)
1498  msi_free( info->items[j] );
1499  msi_free( info->items );
1500  msi_free( info );
1502  break;
1503  }
1504 
1505  return r;
1506 }
1507 
1509 {
1510  struct msi_combobox_info *info = param;
1511  LPCWSTR value, text;
1512  int pos;
1513 
1514  value = MSI_RecordGetString( rec, 3 );
1515  text = MSI_RecordGetString( rec, 4 );
1516 
1517  info->items[info->addpos_items] = strdupW( value );
1518 
1519  pos = SendMessageW( info->hwnd, CB_ADDSTRING, 0, (LPARAM)text );
1520  SendMessageW( info->hwnd, CB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] );
1521  info->addpos_items++;
1522 
1523  return ERROR_SUCCESS;
1524 }
1525 
1527 {
1528  static const WCHAR query[] = {
1529  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1530  '`','C','o','m','b','o','B','o','x','`',' ','W','H','E','R','E',' ',
1531  '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',' ',
1532  'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','`',0};
1533  MSIQUERY *view;
1534  DWORD count;
1535  UINT r;
1536 
1537  r = MSI_OpenQuery( info->dialog->package->db, &view, query, property );
1538  if (r != ERROR_SUCCESS)
1539  return r;
1540 
1541  /* just get the number of records */
1542  count = 0;
1544  if (r != ERROR_SUCCESS)
1545  {
1546  msiobj_release( &view->hdr );
1547  return r;
1548  }
1549  info->num_items = count;
1550  info->items = msi_alloc( sizeof(*info->items) * count );
1551 
1553  msiobj_release( &view->hdr );
1554  return r;
1555 }
1556 
1558 {
1559  static const WCHAR szHide[] = {'H','i','d','e',0};
1560  static const WCHAR szShow[] = {'S','h','o','w',0};
1561  static const WCHAR szDisable[] = {'D','i','s','a','b','l','e',0};
1562  static const WCHAR szEnable[] = {'E','n','a','b','l','e',0};
1563  static const WCHAR szDefault[] = {'D','e','f','a','u','l','t',0};
1564  msi_dialog *dialog = param;
1565  msi_control *control;
1567  UINT r;
1568 
1569  name = MSI_RecordGetString( rec, 2 );
1570  action = MSI_RecordGetString( rec, 3 );
1571  condition = MSI_RecordGetString( rec, 4 );
1572  r = MSI_EvaluateConditionW( dialog->package, condition );
1573  control = msi_dialog_find_control( dialog, name );
1574  if (r == MSICONDITION_TRUE && control)
1575  {
1576  TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
1577 
1578  /* FIXME: case sensitive? */
1579  if (!strcmpW( action, szHide ))
1580  ShowWindow(control->hwnd, SW_HIDE);
1581  else if (!strcmpW( action, szShow ))
1582  ShowWindow(control->hwnd, SW_SHOW);
1583  else if (!strcmpW( action, szDisable ))
1584  EnableWindow(control->hwnd, FALSE);
1585  else if (!strcmpW( action, szEnable ))
1586  EnableWindow(control->hwnd, TRUE);
1587  else if (!strcmpW( action, szDefault ))
1588  SetFocus(control->hwnd);
1589  else
1590  FIXME("Unhandled action %s\n", debugstr_w(action));
1591  }
1592  return ERROR_SUCCESS;
1593 }
1594 
1596 {
1597  static const WCHAR query[] = {
1598  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1599  'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
1600  'W','H','E','R','E',' ','`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
1601  UINT r;
1602  MSIQUERY *view;
1603  MSIPACKAGE *package = dialog->package;
1604 
1605  TRACE("%p %s\n", dialog, debugstr_w(dialog->name));
1606 
1607  /* query the Control table for all the elements of the control */
1608  r = MSI_OpenQuery( package->db, &view, query, dialog->name );
1609  if (r != ERROR_SUCCESS)
1610  return ERROR_SUCCESS;
1611 
1613  msiobj_release( &view->hdr );
1614  return r;
1615 }
1616 
1618 {
1619  struct msi_combobox_info *info;
1620  int index;
1621  LPWSTR value;
1622 
1624  return ERROR_SUCCESS;
1625 
1626  info = GetPropW( control->hwnd, szButtonData );
1627  index = SendMessageW( control->hwnd, CB_GETCURSEL, 0, 0 );
1628  if (index == CB_ERR)
1629  value = msi_get_window_text( control->hwnd );
1630  else
1631  value = (LPWSTR) SendMessageW( control->hwnd, CB_GETITEMDATA, index, 0 );
1632 
1633  msi_dialog_set_property( info->dialog->package, control->property, value );
1635 
1636  if (index == CB_ERR)
1637  msi_free( value );
1638 
1639  return ERROR_SUCCESS;
1640 }
1641 
1643 {
1644  struct msi_combobox_info *info;
1645  LPWSTR value, tmp;
1646  DWORD j;
1647 
1648  info = GetPropW( control->hwnd, szButtonData );
1649 
1650  value = msi_dup_property( dialog->package->db, control->property );
1651  if (!value)
1652  {
1653  SendMessageW( control->hwnd, CB_SETCURSEL, -1, 0 );
1654  return;
1655  }
1656 
1657  for (j = 0; j < info->num_items; j++)
1658  {
1659  tmp = (LPWSTR) SendMessageW( control->hwnd, CB_GETITEMDATA, j, 0 );
1660  if (!strcmpW( value, tmp ))
1661  break;
1662  }
1663 
1664  if (j < info->num_items)
1665  {
1666  SendMessageW( control->hwnd, CB_SETCURSEL, j, 0 );
1667  }
1668  else
1669  {
1670  SendMessageW( control->hwnd, CB_SETCURSEL, -1, 0 );
1671  SetWindowTextW( control->hwnd, value );
1672  }
1673 
1674  msi_free(value);
1675 }
1676 
1678 {
1679  struct msi_combobox_info *info;
1680  msi_control *control;
1681  DWORD attributes, style;
1682  LPCWSTR prop;
1683 
1684  info = msi_alloc( sizeof *info );
1685  if (!info)
1686  return ERROR_FUNCTION_FAILED;
1687 
1689  attributes = MSI_RecordGetInteger( rec, 8 );
1690  if ( ~attributes & msidbControlAttributesSorted)
1691  style |= CBS_SORT;
1692  if ( attributes & msidbControlAttributesComboList)
1694  else
1695  style |= CBS_DROPDOWN;
1696 
1697  control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style );
1698  if (!control)
1699  {
1700  msi_free( info );
1701  return ERROR_FUNCTION_FAILED;
1702  }
1703 
1706 
1707  prop = MSI_RecordGetString( rec, 9 );
1708  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
1709 
1710  /* subclass */
1711  info->dialog = dialog;
1712  info->hwnd = control->hwnd;
1713  info->items = NULL;
1714  info->addpos_items = 0;
1715  info->oldproc = (WNDPROC)SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
1717  SetPropW( control->hwnd, szButtonData, info );
1718 
1719  if (control->property)
1720  msi_combobox_add_items( info, control->property );
1721 
1722  msi_dialog_combobox_update( dialog, control );
1723 
1724  return ERROR_SUCCESS;
1725 }
1726 
1728 {
1729  LPWSTR buf;
1730 
1731  if (HIWORD(param) != EN_CHANGE)
1732  return ERROR_SUCCESS;
1733 
1734  TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
1735 
1736  buf = msi_get_window_text( control->hwnd );
1737  msi_dialog_set_property( dialog->package, control->property, buf );
1738  msi_free( buf );
1739 
1740  return ERROR_SUCCESS;
1741 }
1742 
1743 /* length of 2^32 + 1 */
1744 #define MAX_NUM_DIGITS 11
1745 
1747 {
1748  msi_control *control;
1749  LPCWSTR prop, text;
1750  LPWSTR val, begin, end;
1752  DWORD limit;
1753 
1754  control = msi_dialog_add_control( dialog, rec, szEdit,
1756  control->handler = msi_dialog_edit_handler;
1757 
1758  text = MSI_RecordGetString( rec, 10 );
1759  if ( text )
1760  {
1761  begin = strchrW( text, '{' );
1762  end = strchrW( text, '}' );
1763 
1764  if ( begin && end && end > begin &&
1765  begin[0] >= '0' && begin[0] <= '9' &&
1766  end - begin < MAX_NUM_DIGITS)
1767  {
1768  lstrcpynW( num, begin + 1, end - begin );
1769  limit = atolW( num );
1770 
1771  SendMessageW( control->hwnd, EM_SETLIMITTEXT, limit, 0 );
1772  }
1773  }
1774 
1775  prop = MSI_RecordGetString( rec, 9 );
1776  if( prop )
1777  control->property = strdupW( prop );
1778 
1779  val = msi_dup_property( dialog->package->db, control->property );
1780  SetWindowTextW( control->hwnd, val );
1781  msi_free( val );
1782  return ERROR_SUCCESS;
1783 }
1784 
1785 /******************** Masked Edit ********************************************/
1786 
1787 #define MASK_MAX_GROUPS 20
1788 
1790 {
1795 };
1796 
1798 {
1806 };
1807 
1809 {
1810  switch (type)
1811  {
1812  case '%':
1813  case '#':
1814  case '&':
1815  case '`':
1816  case '?':
1817  case '^':
1818  return TRUE;
1819  }
1820  return FALSE;
1821 }
1822 
1824 {
1825  LPWSTR val;
1826  UINT i, n, r;
1827 
1828  val = msi_alloc( (info->num_chars+1)*sizeof(WCHAR) );
1829  for( i=0, n=0; i<info->num_groups; i++ )
1830  {
1831  if (info->group[i].len == ~0u)
1832  {
1833  UINT len = SendMessageW( info->group[i].hwnd, WM_GETTEXTLENGTH, 0, 0 );
1834  val = msi_realloc( val, (len + 1) * sizeof(WCHAR) );
1835  GetWindowTextW( info->group[i].hwnd, val, len + 1 );
1836  }
1837  else
1838  {
1839  if (info->group[i].len + n > info->num_chars)
1840  {
1841  ERR("can't fit control %d text into template\n",i);
1842  break;
1843  }
1844  if (!msi_mask_editable(info->group[i].type))
1845  {
1846  for(r=0; r<info->group[i].len; r++)
1847  val[n+r] = info->group[i].type;
1848  val[n+r] = 0;
1849  }
1850  else
1851  {
1852  r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 );
1853  if( r != info->group[i].len )
1854  break;
1855  }
1856  n += r;
1857  }
1858  }
1859 
1860  TRACE("%d/%d controls were good\n", i, info->num_groups);
1861 
1862  if( i == info->num_groups )
1863  {
1864  TRACE("Set property %s to %s\n", debugstr_w(info->prop), debugstr_w(val));
1865  msi_dialog_set_property( info->dialog->package, info->prop, val );
1867  }
1868  msi_free( val );
1869 }
1870 
1871 /* now move to the next control if necessary */
1873 {
1874  HWND hWndNext;
1875  UINT len, i;
1876 
1877  for( i=0; i<info->num_groups; i++ )
1878  if( info->group[i].hwnd == hWnd )
1879  break;
1880 
1881  /* don't move from the last control */
1882  if( i >= (info->num_groups-1) )
1883  return;
1884 
1885  len = SendMessageW( hWnd, WM_GETTEXTLENGTH, 0, 0 );
1886  if( len < info->group[i].len )
1887  return;
1888 
1889  hWndNext = GetNextDlgTabItem( GetParent( hWnd ), hWnd, FALSE );
1890  SetFocus( hWndNext );
1891 }
1892 
1893 static LRESULT WINAPI
1895 {
1896  struct msi_maskedit_info *info;
1897  HRESULT r;
1898 
1899  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
1900 
1902 
1903  r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
1904 
1905  switch( msg )
1906  {
1907  case WM_COMMAND:
1908  if (HIWORD(wParam) == EN_CHANGE)
1909  {
1912  }
1913  break;
1914  case WM_NCDESTROY:
1915  msi_free( info->prop );
1916  msi_free( info );
1918  break;
1919  }
1920 
1921  return r;
1922 }
1923 
1924 /* fish the various bits of the property out and put them in the control */
1925 static void
1927 {
1928  LPCWSTR p;
1929  UINT i;
1930 
1931  p = text;
1932  for( i = 0; i < info->num_groups; i++ )
1933  {
1934  if( info->group[i].len < strlenW( p ) )
1935  {
1936  LPWSTR chunk = strdupW( p );
1937  chunk[ info->group[i].len ] = 0;
1938  SetWindowTextW( info->group[i].hwnd, chunk );
1939  msi_free( chunk );
1940  }
1941  else
1942  {
1943  SetWindowTextW( info->group[i].hwnd, p );
1944  break;
1945  }
1946  p += info->group[i].len;
1947  }
1948 }
1949 
1951 {
1952  struct msi_maskedit_info *info;
1953  int i = 0, n = 0, total = 0;
1954  LPCWSTR p;
1955 
1956  TRACE("masked control, template %s\n", debugstr_w(mask));
1957 
1958  if( !mask )
1959  return NULL;
1960 
1961  info = msi_alloc_zero( sizeof *info );
1962  if( !info )
1963  return info;
1964 
1965  p = strchrW(mask, '<');
1966  if( p )
1967  p++;
1968  else
1969  p = mask;
1970 
1971  for( i=0; i<MASK_MAX_GROUPS; i++ )
1972  {
1973  /* stop at the end of the string */
1974  if( p[0] == 0 || p[0] == '>' )
1975  {
1976  if (!total)
1977  {
1978  /* create a group for the empty mask */
1979  info->group[0].type = '&';
1980  info->group[0].len = ~0u;
1981  i = 1;
1982  }
1983  break;
1984  }
1985 
1986  /* count the number of the same identifier */
1987  for( n=0; p[n] == p[0]; n++ )
1988  ;
1989  info->group[i].ofs = total;
1990  info->group[i].type = p[0];
1991  if( p[n] == '=' )
1992  {
1993  n++;
1994  total++; /* an extra not part of the group */
1995  }
1996  info->group[i].len = n;
1997  total += n;
1998  p += n;
1999  }
2000 
2001  TRACE("%d characters in %d groups\n", total, i );
2002  if( i == MASK_MAX_GROUPS )
2003  ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask));
2004 
2005  info->num_chars = total;
2006  info->num_groups = i;
2007 
2008  return info;
2009 }
2010 
2011 static void
2013 {
2014  DWORD width, height, style, wx, ww;
2015  RECT rect;
2016  HWND hwnd;
2017  UINT i;
2018 
2020 
2021  GetClientRect( info->hwnd, &rect );
2022 
2023  width = rect.right - rect.left;
2024  height = rect.bottom - rect.top;
2025 
2026  for( i = 0; i < info->num_groups; i++ )
2027  {
2028  if (!msi_mask_editable( info->group[i].type ))
2029  continue;
2030  if (info->num_chars)
2031  {
2032  wx = (info->group[i].ofs * width) / info->num_chars;
2033  ww = (info->group[i].len * width) / info->num_chars;
2034  }
2035  else
2036  {
2037  wx = 0;
2038  ww = width;
2039  }
2040  hwnd = CreateWindowW( szEdit, NULL, style, wx, 0, ww, height,
2041  info->hwnd, NULL, NULL, NULL );
2042  if( !hwnd )
2043  {
2044  ERR("failed to create mask edit sub window\n");
2045  break;
2046  }
2047 
2048  SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 );
2049 
2050  msi_dialog_set_font( info->dialog, hwnd,
2051  font?font:info->dialog->default_font );
2052  info->group[i].hwnd = hwnd;
2053  }
2054 }
2055 
2056 /*
2057  * office 2003 uses "73931<````=````=````=````=`````>@@@@@"
2058  * delphi 7 uses "<????-??????-??????-????>" and "<???-???>"
2059  * filemaker pro 7 uses "<^^^^=^^^^=^^^^=^^^^=^^^^=^^^^=^^^^^>"
2060  */
2062 {
2063  LPWSTR font_mask, val = NULL, font;
2064  struct msi_maskedit_info *info = NULL;
2066  msi_control *control;
2067  LPCWSTR prop, mask;
2068 
2069  TRACE("\n");
2070 
2071  font_mask = msi_get_deformatted_field( dialog->package, rec, 10 );
2072  font = msi_dialog_get_style( font_mask, &mask );
2073  if( !mask )
2074  {
2075  WARN("mask template is empty\n");
2076  goto end;
2077  }
2078 
2080  if( !info )
2081  {
2082  ERR("template %s is invalid\n", debugstr_w(mask));
2083  goto end;
2084  }
2085 
2086  info->dialog = dialog;
2087 
2088  control = msi_dialog_add_control( dialog, rec, szStatic,
2090  if( !control )
2091  {
2092  ERR("Failed to create maskedit container\n");
2094  goto end;
2095  }
2097 
2098  info->hwnd = control->hwnd;
2099 
2100  /* subclass the static control */
2101  info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC,
2103  SetPropW( control->hwnd, szButtonData, info );
2104 
2105  prop = MSI_RecordGetString( rec, 9 );
2106  if( prop )
2107  info->prop = strdupW( prop );
2108 
2110 
2111  if( prop )
2112  {
2113  val = msi_dup_property( dialog->package->db, prop );
2114  if( val )
2115  {
2117  msi_free( val );
2118  }
2119  }
2120 
2121 end:
2122  if( ret != ERROR_SUCCESS )
2123  msi_free( info );
2124  msi_free( font_mask );
2125  msi_free( font );
2126  return ret;
2127 }
2128 
2129 /******************** Progress Bar *****************************************/
2130 
2132 {
2133  msi_control *control;
2134  DWORD attributes, style;
2135 
2136  style = WS_VISIBLE;
2137  attributes = MSI_RecordGetInteger( rec, 8 );
2138  if( !(attributes & msidbControlAttributesProgress95) )
2139  style |= PBS_SMOOTH;
2140 
2141  control = msi_dialog_add_control( dialog, rec, PROGRESS_CLASSW, style );
2142  if( !control )
2143  return ERROR_FUNCTION_FAILED;
2144 
2146  return ERROR_SUCCESS;
2147 }
2148 
2149 /******************** Path Edit ********************************************/
2150 
2152 {
2156 };
2157 
2159 {
2160  LPWSTR prop, path;
2161  BOOL indirect;
2162 
2163  if (!control && !(control = msi_dialog_find_control_by_type( dialog, szPathEdit )))
2164  return;
2165 
2167  prop = msi_dialog_dup_property( dialog, control->property, indirect );
2169 
2170  SetWindowTextW( control->hwnd, path );
2171  SendMessageW( control->hwnd, EM_SETSEL, 0, -1 );
2172 
2173  msi_free( path );
2174  msi_free( prop );
2175 }
2176 
2177 /* FIXME: test when this should fail */
2179 {
2180  if ( !path[0] )
2181  return FALSE;
2182 
2183  if ( PathIsRelativeW( path ) )
2184  return FALSE;
2185 
2186  return TRUE;
2187 }
2188 
2189 /* returns TRUE if the path is valid, FALSE otherwise */
2191 {
2192  LPWSTR buf, prop;
2193  BOOL indirect;
2194  BOOL valid;
2195 
2197  prop = msi_dialog_dup_property( dialog, control->property, indirect );
2198 
2199  buf = msi_get_window_text( control->hwnd );
2200 
2201  if ( !msi_dialog_verify_path( buf ) )
2202  {
2203  /* FIXME: display an error message box */
2204  ERR("Invalid path %s\n", debugstr_w( buf ));
2205  valid = FALSE;
2206  SetFocus( control->hwnd );
2207  }
2208  else
2209  {
2210  valid = TRUE;
2211  msi_dialog_set_property( dialog->package, prop, buf );
2212  }
2213 
2214  msi_dialog_update_pathedit( dialog, control );
2215 
2216  TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name),
2217  debugstr_w(prop));
2218 
2219  msi_free( buf );
2220  msi_free( prop );
2221 
2222  return valid;
2223 }
2224 
2226 {
2228  LRESULT r = 0;
2229 
2230  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
2231 
2232  if ( msg == WM_KILLFOCUS )
2233  {
2234  /* if the path is invalid, don't handle this message */
2235  if ( !msi_dialog_onkillfocus( info->dialog, info->control ) )
2236  return 0;
2237  }
2238 
2239  r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
2240 
2241  if ( msg == WM_NCDESTROY )
2242  {
2243  msi_free( info );
2245  }
2246 
2247  return r;
2248 }
2249 
2251 {
2252  struct msi_pathedit_info *info;
2254  LPCWSTR prop;
2255 
2256  info = msi_alloc( sizeof *info );
2257  if (!info)
2258  return ERROR_FUNCTION_FAILED;
2259 
2261  WS_BORDER | WS_TABSTOP );
2263  prop = MSI_RecordGetString( rec, 9 );
2266 
2267  info->dialog = dialog;
2268  info->control = control;
2272 
2274 
2275  return ERROR_SUCCESS;
2276 }
2277 
2279 {
2280  if (HIWORD(param) != BN_CLICKED)
2281  return ERROR_SUCCESS;
2282 
2283  TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
2284 
2286 
2288 }
2289 
2290 /* radio buttons are a bit different from normal controls */
2292 {
2294  msi_dialog *dialog = group->dialog;
2296  LPCWSTR prop, text, name;
2298 
2299  name = MSI_RecordGetString( rec, 3 );
2300  text = MSI_RecordGetString( rec, 8 );
2301 
2303  group->parent->hwnd );
2304  if (!control)
2305  return ERROR_FUNCTION_FAILED;
2307 
2308  if (group->propval && !strcmpW( control->name, group->propval ))
2310 
2311  prop = MSI_RecordGetString( rec, 1 );
2312  if( prop )
2313  control->property = strdupW( prop );
2314 
2315  return ERROR_SUCCESS;
2316 }
2317 
2319 {
2320  EnableWindow( hWnd, lParam );
2321  return TRUE;
2322 }
2323 
2325 {
2327  LRESULT r;
2328 
2329  TRACE("hWnd %p msg %04x wParam 0x%08lx lParam 0x%08lx\n", hWnd, msg, wParam, lParam);
2330 
2331  if (msg == WM_COMMAND) /* Forward notifications to dialog */
2333 
2335 
2336  /* make sure the radio buttons show as disabled if the parent is disabled */
2337  if (msg == WM_ENABLE)
2339 
2340  return r;
2341 }
2342 
2344 {
2345  static const WCHAR query[] = {
2346  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2347  'R','a','d','i','o','B','u','t','t','o','n',' ','W','H','E','R','E',' ',
2348  '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
2349  UINT r;
2350  LPCWSTR prop;
2352  MSIQUERY *view;
2354  MSIPACKAGE *package = dialog->package;
2355  WNDPROC oldproc;
2356  DWORD attr, style = WS_GROUP;
2357 
2358  prop = MSI_RecordGetString( rec, 9 );
2359 
2360  TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop ));
2361 
2362  attr = MSI_RecordGetInteger( rec, 8 );
2364  style |= WS_VISIBLE;
2366  style |= WS_DISABLED;
2368  style |= BS_GROUPBOX;
2369  else
2370  style |= BS_OWNERDRAW;
2371 
2372  /* Create parent group box to hold radio buttons */
2373  control = msi_dialog_add_control( dialog, rec, szButton, style );
2374  if( !control )
2375  return ERROR_FUNCTION_FAILED;
2376 
2377  oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
2379  SetPropW(control->hwnd, szButtonData, oldproc);
2381 
2382  if( prop )
2383  control->property = strdupW( prop );
2384 
2385  /* query the Radio Button table for all control in this group */
2386  r = MSI_OpenQuery( package->db, &view, query, prop );
2387  if( r != ERROR_SUCCESS )
2388  {
2389  ERR("query failed for dialog %s radio group %s\n",
2390  debugstr_w(dialog->name), debugstr_w(prop));
2391  return ERROR_INVALID_PARAMETER;
2392  }
2393 
2394  group.dialog = dialog;
2395  group.parent = control;
2396  group.propval = msi_dup_property( dialog->package->db, control->property );
2397 
2399  msiobj_release( &view->hdr );
2400  msi_free( group.propval );
2401  return r;
2402 }
2403 
2404 static void
2406 {
2407  TVITEMW tvi;
2408  DWORD index = feature->ActionRequest;
2409 
2410  TRACE("Feature %s -> %d %d %d\n", debugstr_w(feature->Title),
2411  feature->Installed, feature->Action, feature->ActionRequest);
2412 
2413  if (index == INSTALLSTATE_UNKNOWN)
2415 
2416  tvi.mask = TVIF_STATE;
2417  tvi.hItem = hItem;
2420 
2421  SendMessageW( hwnd, TVM_SETITEMW, 0, (LPARAM) &tvi );
2422 }
2423 
2424 static UINT
2426 {
2427  HMENU hMenu;
2428  INT r;
2429 
2430  /* create a menu to display */
2431  hMenu = CreatePopupMenu();
2432 
2433  /* FIXME: load strings from resources */
2434  AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_LOCAL, "Install feature locally");
2435  AppendMenuA( hMenu, MF_ENABLED, USER_INSTALLSTATE_ALL, "Install entire feature");
2436  AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ADVERTISED, "Install on demand");
2437  AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ABSENT, "Don't install");
2439  x, y, 0, hwnd, NULL );
2440  DestroyMenu( hMenu );
2441  return r;
2442 }
2443 
2444 static void
2447 {
2448  feature->ActionRequest = state;
2451 }
2452 
2453 static void
2455  MSIPACKAGE *package, INSTALLSTATE state)
2456 {
2457  /* update all siblings */
2458  do
2459  {
2461  HTREEITEM child;
2462 
2465 
2466  /* update this sibling's children */
2468  if (child)
2470  package, state );
2471  }
2472  while ((curr = (HTREEITEM)SendMessageW( hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)curr )));
2473 }
2474 
2475 static LRESULT
2477 {
2478  struct msi_selection_tree_info *info;
2480  MSIPACKAGE *package;
2481  union {
2482  RECT rc;
2483  POINT pt[2];
2484  HTREEITEM hItem;
2485  } u;
2486  UINT r;
2487 
2489  package = info->dialog->package;
2490 
2492  if (!feature)
2493  {
2494  ERR("item %p feature was NULL\n", hItem);
2495  return 0;
2496  }
2497 
2498  /* get the item's rectangle to put the menu just below it */
2499  u.hItem = hItem;
2500  SendMessageW( hwnd, TVM_GETITEMRECT, 0, (LPARAM) &u.rc );
2501  MapWindowPoints( hwnd, NULL, u.pt, 2 );
2502 
2503  r = msi_seltree_popup_menu( hwnd, u.rc.left, u.rc.top );
2504 
2505  switch (r)
2506  {
2507  case USER_INSTALLSTATE_ALL:
2509  /* fall-through */
2511  case INSTALLSTATE_ABSENT:
2512  {
2513  HTREEITEM child;
2515  if (child)
2517  }
2518  /* fall-through */
2519  case INSTALLSTATE_LOCAL:
2521  break;
2522  }
2523 
2524  return 0;
2525 }
2526 
2527 static LRESULT WINAPI
2529 {
2530  struct msi_selection_tree_info *info;
2531  TVHITTESTINFO tvhti;
2532  HRESULT r;
2533 
2534  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
2535 
2537 
2538  switch( msg )
2539  {
2540  case WM_LBUTTONDOWN:
2541  tvhti.pt.x = (short)LOWORD( lParam );
2542  tvhti.pt.y = (short)HIWORD( lParam );
2543  tvhti.flags = 0;
2544  tvhti.hItem = 0;
2545  CallWindowProcW(info->oldproc, hWnd, TVM_HITTEST, 0, (LPARAM) &tvhti );
2546  if (tvhti.flags & TVHT_ONITEMSTATEICON)
2547  return msi_seltree_menu( hWnd, tvhti.hItem );
2548  break;
2549  }
2550  r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
2551 
2552  switch( msg )
2553  {
2554  case WM_NCDESTROY:
2555  msi_free( info );
2557  break;
2558  }
2559  return r;
2560 }
2561 
2562 static void
2565 {
2568  TVINSERTSTRUCTW tvis;
2569  HTREEITEM hitem, hfirst = NULL;
2570 
2572  {
2573  if ( parent && feature->Feature_Parent && strcmpW( parent, feature->Feature_Parent ))
2574  continue;
2575  else if ( parent && !feature->Feature_Parent )
2576  continue;
2577  else if ( !parent && feature->Feature_Parent )
2578  continue;
2579 
2580  if ( !feature->Title )
2581  continue;
2582 
2583  if ( !feature->Display )
2584  continue;
2585 
2586  memset( &tvis, 0, sizeof tvis );
2587  tvis.hParent = hParent;
2588  tvis.hInsertAfter = TVI_LAST;
2589  tvis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
2590  tvis.u.item.pszText = feature->Title;
2591  tvis.u.item.lParam = (LPARAM) feature;
2592 
2593  hitem = (HTREEITEM) SendMessageW( hwnd, TVM_INSERTITEMW, 0, (LPARAM) &tvis );
2594  if (!hitem)
2595  continue;
2596 
2597  if (!hfirst)
2598  hfirst = hitem;
2599 
2602  feature->Feature, hitem );
2603 
2604  /* the node is expanded if Display is odd */
2605  if ( feature->Display % 2 != 0 )
2607  }
2608 
2609  /* select the first item */
2611  info->selected = hfirst;
2612 }
2613 
2615 {
2616  const int bm_width = 32, bm_height = 16, bm_count = 3;
2617  const int bm_resource = 0x1001;
2618  HIMAGELIST himl;
2619  int i;
2620  HBITMAP hbmp;
2621 
2622  himl = ImageList_Create( bm_width, bm_height, FALSE, 4, 0 );
2623  if (!himl)
2624  {
2625  ERR("failed to create image list\n");
2626  return;
2627  }
2628 
2629  for (i=0; i<bm_count; i++)
2630  {
2631  hbmp = LoadBitmapW( msi_hInstance, MAKEINTRESOURCEW(i+bm_resource) );
2632  if (!hbmp)
2633  {
2634  ERR("failed to load bitmap %d\n", i);
2635  break;
2636  }
2637 
2638  /*
2639  * Add a dummy bitmap at offset zero because the treeview
2640  * can't use it as a state mask (zero means no user state).
2641  */
2642  if (!i)
2643  ImageList_Add( himl, hbmp, NULL );
2644 
2645  ImageList_Add( himl, hbmp, NULL );
2646  }
2647 
2649 }
2650 
2652  msi_control *control, WPARAM param )
2653 {
2654  struct msi_selection_tree_info *info = GetPropW( control->hwnd, szButtonData );
2656  MSIRECORD *row, *rec;
2657  MSIFOLDER *folder;
2659  LPCWSTR dir, title = NULL;
2660  UINT r = ERROR_SUCCESS;
2661 
2662  static const WCHAR select[] = {
2663  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2664  '`','F','e','a','t','u','r','e','`',' ','W','H','E','R','E',' ',
2665  '`','T','i','t','l','e','`',' ','=',' ','\'','%','s','\'',0
2666  };
2667 
2668  if (tv->hdr.code != TVN_SELCHANGINGW)
2669  return ERROR_SUCCESS;
2670 
2671  info->selected = tv->itemNew.hItem;
2672 
2673  if (!(tv->itemNew.mask & TVIF_TEXT))
2674  {
2676  if (feature)
2677  title = feature->Title;
2678  }
2679  else
2680  title = tv->itemNew.pszText;
2681 
2682  row = MSI_QueryGetRecord( dialog->package->db, select, title );
2683  if (!row)
2684  return ERROR_FUNCTION_FAILED;
2685 
2686  rec = MSI_CreateRecord( 1 );
2687 
2688  MSI_RecordSetStringW( rec, 1, MSI_RecordGetString( row, 4 ) );
2689  msi_event_fire( dialog->package, szSelectionDescription, rec );
2690 
2691  dir = MSI_RecordGetString( row, 7 );
2692  if (dir)
2693  {
2694  folder = msi_get_loaded_folder( dialog->package, dir );
2695  if (!folder)
2696  {
2698  goto done;
2699  }
2700  MSI_RecordSetStringW( rec, 1, folder->ResolvedTarget );
2701  }
2702  else
2703  MSI_RecordSetStringW( rec, 1, NULL );
2704 
2705  msi_event_fire( dialog->package, szSelectionPath, rec );
2706 
2707 done:
2708  msiobj_release(&row->hdr);
2709  msiobj_release(&rec->hdr);
2710 
2711  return r;
2712 }
2713 
2715 {
2716  msi_control *control;
2717  LPCWSTR prop, control_name;
2718  MSIPACKAGE *package = dialog->package;
2719  DWORD style;
2720  struct msi_selection_tree_info *info;
2721 
2722  info = msi_alloc( sizeof *info );
2723  if (!info)
2724  return ERROR_FUNCTION_FAILED;
2725 
2726  /* create the treeview control */
2729  control = msi_dialog_add_control( dialog, rec, WC_TREEVIEWW, style );
2730  if (!control)
2731  {
2732  msi_free(info);
2733  return ERROR_FUNCTION_FAILED;
2734  }
2735 
2737  control_name = MSI_RecordGetString( rec, 2 );
2738  control->attributes = MSI_RecordGetInteger( rec, 8 );
2739  prop = MSI_RecordGetString( rec, 9 );
2740  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
2741 
2742  /* subclass */
2743  info->dialog = dialog;
2744  info->hwnd = control->hwnd;
2745  info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
2747  SetPropW( control->hwnd, szButtonData, info );
2748 
2749  event_subscribe( dialog, szSelectionPath, control_name, szProperty );
2750 
2751  /* initialize it */
2752  msi_seltree_create_imagelist( control->hwnd );
2753  msi_seltree_add_child_features( package, control->hwnd, NULL, NULL );
2754 
2755  return ERROR_SUCCESS;
2756 }
2757 
2758 /******************** Group Box ***************************************/
2759 
2761 {
2762  msi_control *control;
2763  DWORD style;
2764 
2766  control = msi_dialog_add_control( dialog, rec, WC_BUTTONW, style );
2767  if (!control)
2768  return ERROR_FUNCTION_FAILED;
2769 
2770  return ERROR_SUCCESS;
2771 }
2772 
2773 /******************** List Box ***************************************/
2774 
2776 {
2783 };
2784 
2786 {
2787  struct msi_listbox_info *info;
2788  LRESULT r;
2789  DWORD j;
2790 
2791  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
2792 
2794  if (!info)
2795  return 0;
2796 
2797  r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
2798 
2799  switch( msg )
2800  {
2801  case WM_NCDESTROY:
2802  for (j = 0; j < info->num_items; j++)
2803  msi_free( info->items[j] );
2804  msi_free( info->items );
2805  msi_free( info );
2807  break;
2808  }
2809 
2810  return r;
2811 }
2812 
2814 {
2815  struct msi_listbox_info *info = param;
2816  LPCWSTR value, text;
2817  int pos;
2818 
2819  value = MSI_RecordGetString( rec, 3 );
2820  text = MSI_RecordGetString( rec, 4 );
2821 
2822  info->items[info->addpos_items] = strdupW( value );
2823 
2824  pos = SendMessageW( info->hwnd, LB_ADDSTRING, 0, (LPARAM)text );
2825  SendMessageW( info->hwnd, LB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] );
2826  info->addpos_items++;
2827  return ERROR_SUCCESS;
2828 }
2829 
2831 {
2832  static const WCHAR query[] = {
2833  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2834  '`','L','i','s','t','B','o','x','`',' ','W','H','E','R','E',' ',
2835  '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',' ',
2836  'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','`',0};
2837  MSIQUERY *view;
2838  DWORD count;
2839  UINT r;
2840 
2841  r = MSI_OpenQuery( info->dialog->package->db, &view, query, property );
2842  if ( r != ERROR_SUCCESS )
2843  return r;
2844 
2845  /* just get the number of records */
2846  count = 0;
2848  if (r != ERROR_SUCCESS)
2849  {
2850  msiobj_release( &view->hdr );
2851  return r;
2852  }
2853  info->num_items = count;
2854  info->items = msi_alloc( sizeof(*info->items) * count );
2855 
2857  msiobj_release( &view->hdr );
2858  return r;
2859 }
2860 
2862  msi_control *control, WPARAM param )
2863 {
2864  struct msi_listbox_info *info;
2865  int index;
2866  LPCWSTR value;
2867 
2868  if( HIWORD(param) != LBN_SELCHANGE )
2869  return ERROR_SUCCESS;
2870 
2871  info = GetPropW( control->hwnd, szButtonData );
2872  index = SendMessageW( control->hwnd, LB_GETCURSEL, 0, 0 );
2873  value = (LPCWSTR) SendMessageW( control->hwnd, LB_GETITEMDATA, index, 0 );
2874 
2875  msi_dialog_set_property( info->dialog->package, control->property, value );
2877 
2878  return ERROR_SUCCESS;
2879 }
2880 
2882 {
2883  struct msi_listbox_info *info;
2884  msi_control *control;
2885  DWORD attributes, style;
2886  LPCWSTR prop;
2887 
2888  info = msi_alloc( sizeof *info );
2889  if (!info)
2890  return ERROR_FUNCTION_FAILED;
2891 
2893  attributes = MSI_RecordGetInteger( rec, 8 );
2894  if (~attributes & msidbControlAttributesSorted)
2895  style |= LBS_SORT;
2896 
2897  control = msi_dialog_add_control( dialog, rec, WC_LISTBOXW, style );
2898  if (!control)
2899  {
2900  msi_free(info);
2901  return ERROR_FUNCTION_FAILED;
2902  }
2903 
2905 
2906  prop = MSI_RecordGetString( rec, 9 );
2907  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
2908 
2909  /* subclass */
2910  info->dialog = dialog;
2911  info->hwnd = control->hwnd;
2912  info->items = NULL;
2913  info->addpos_items = 0;
2914  info->oldproc = (WNDPROC)SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
2916  SetPropW( control->hwnd, szButtonData, info );
2917 
2918  if ( control->property )
2919  msi_listbox_add_items( info, control->property );
2920 
2921  return ERROR_SUCCESS;
2922 }
2923 
2924 /******************** Directory Combo ***************************************/
2925 
2927 {
2928  LPWSTR prop, path;
2929  BOOL indirect;
2930 
2931  if (!control && !(control = msi_dialog_find_control_by_type( dialog, szDirectoryCombo )))
2932  return;
2933 
2935  prop = msi_dialog_dup_property( dialog, control->property, indirect );
2937 
2938  PathStripPathW( path );
2940 
2941  SendMessageW( control->hwnd, CB_INSERTSTRING, 0, (LPARAM)path );
2942  SendMessageW( control->hwnd, CB_SETCURSEL, 0, 0 );
2943 
2944  msi_free( path );
2945  msi_free( prop );
2946 }
2947 
2949 {
2950  msi_control *control;
2951  LPCWSTR prop;
2952  DWORD style;
2953 
2954  /* FIXME: use CBS_OWNERDRAWFIXED and add owner draw code */
2957  control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style );
2958  if (!control)
2959  return ERROR_FUNCTION_FAILED;
2960 
2961  control->attributes = MSI_RecordGetInteger( rec, 8 );
2962  prop = MSI_RecordGetString( rec, 9 );
2963  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
2964 
2966 
2967  return ERROR_SUCCESS;
2968 }
2969 
2970 /******************** Directory List ***************************************/
2971 
2973 {
2974  WCHAR dir_spec[MAX_PATH];
2975  WIN32_FIND_DATAW wfd;
2976  LPWSTR prop, path;
2977  BOOL indirect;
2978  LVITEMW item;
2979  HANDLE file;
2980 
2981  static const WCHAR asterisk[] = {'*',0};
2982 
2983  if (!control && !(control = msi_dialog_find_control_by_type( dialog, szDirectoryList )))
2984  return;
2985 
2986  /* clear the list-view */
2987  SendMessageW( control->hwnd, LVM_DELETEALLITEMS, 0, 0 );
2988 
2990  prop = msi_dialog_dup_property( dialog, control->property, indirect );
2992 
2993  lstrcpyW( dir_spec, path );
2994  lstrcatW( dir_spec, asterisk );
2995 
2996  file = FindFirstFileW( dir_spec, &wfd );
2997  if ( file == INVALID_HANDLE_VALUE )
2998  return;
2999 
3000  do
3001  {
3002  if ( wfd.dwFileAttributes != FILE_ATTRIBUTE_DIRECTORY )
3003  continue;
3004 
3005  if ( !strcmpW( wfd.cFileName, szDot ) || !strcmpW( wfd.cFileName, szDotDot ) )
3006  continue;
3007 
3008  item.mask = LVIF_TEXT;
3009  item.cchTextMax = MAX_PATH;
3010  item.iItem = 0;
3011  item.iSubItem = 0;
3012  item.pszText = wfd.cFileName;
3013 
3014  SendMessageW( control->hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item );
3015  } while ( FindNextFileW( file, &wfd ) );
3016 
3017  msi_free( prop );
3018  msi_free( path );
3019  FindClose( file );
3020 }
3021 
3023 {
3024  msi_control *control;
3025  LPWSTR prop, path, ptr;
3026  BOOL indirect;
3027 
3030  prop = msi_dialog_dup_property( dialog, control->property, indirect );
3032 
3033  /* strip off the last directory */
3034  ptr = PathFindFileNameW( path );
3035  if (ptr != path) *(ptr - 1) = '\0';
3037 
3038  msi_dialog_set_property( dialog->package, prop, path );
3039 
3043 
3044  msi_free( path );
3045  msi_free( prop );
3046 
3047  return ERROR_SUCCESS;
3048 }
3049 
3051  msi_control *control, WPARAM param )
3052 {
3053  LPNMHDR nmhdr = (LPNMHDR)param;
3054  WCHAR new_path[MAX_PATH];
3055  WCHAR text[MAX_PATH];
3056  LPWSTR path, prop;
3057  BOOL indirect;
3058  LVITEMW item;
3059  int index;
3060 
3061  if (nmhdr->code != LVN_ITEMACTIVATE)
3062  return ERROR_SUCCESS;
3063 
3064  index = SendMessageW( control->hwnd, LVM_GETNEXTITEM, -1, LVNI_SELECTED );
3065  if ( index < 0 )
3066  {
3067  ERR("No list-view item selected!\n");
3068  return ERROR_FUNCTION_FAILED;
3069  }
3070 
3071  item.iSubItem = 0;
3072  item.pszText = text;
3073  item.cchTextMax = MAX_PATH;
3075 
3077  prop = msi_dialog_dup_property( dialog, control->property, indirect );
3079 
3080  lstrcpyW( new_path, path );
3081  lstrcatW( new_path, text );
3082  lstrcatW( new_path, szBackSlash );
3083 
3084  msi_dialog_set_property( dialog->package, prop, new_path );
3085 
3089 
3090  msi_free( prop );
3091  msi_free( path );
3092  return ERROR_SUCCESS;
3093 }
3094 
3096 {
3097  msi_control *control;
3098  LPCWSTR prop;
3099  DWORD style;
3100 
3104  control = msi_dialog_add_control( dialog, rec, WC_LISTVIEWW, style );
3105  if (!control)
3106  return ERROR_FUNCTION_FAILED;
3107 
3108  control->attributes = MSI_RecordGetInteger( rec, 8 );
3110  prop = MSI_RecordGetString( rec, 9 );
3111  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
3112 
3113  /* double click to activate an item in the list */
3116 
3118 
3119  return ERROR_SUCCESS;
3120 }
3121 
3122 /******************** VolumeCost List ***************************************/
3123 
3125 {
3126  int i;
3127 
3128  for (i = 0; i < lstrlenW( str ); i++)
3129  if (!isdigitW(str[i]))
3130  return FALSE;
3131 
3132  return TRUE;
3133 }
3134 
3135 static const WCHAR column_keys[][80] =
3136 {
3137  {'V','o','l','u','m','e','C','o','s','t','V','o','l','u','m','e',0},
3138  {'V','o','l','u','m','e','C','o','s','t','S','i','z','e',0},
3139  {'V','o','l','u','m','e','C','o','s','t','A','v','a','i','l','a','b','l','e',0},
3140  {'V','o','l','u','m','e','C','o','s','t','R','e','q','u','i','r','e','d',0},
3141  {'V','o','l','u','m','e','C','o','s','t','D','i','f','f','e','r','e','n','c','e',0}
3142 };
3143 
3145 {
3146  LPCWSTR text = MSI_RecordGetString( rec, 10 );
3147  LPCWSTR begin = text, end;
3148  WCHAR *num;
3149  LVCOLUMNW lvc;
3150  DWORD count = 0;
3151 
3152  static const WCHAR negative[] = {'-',0};
3153 
3154  if (!text) return;
3155 
3156  while ((begin = strchrW( begin, '{' )) && count < 5)
3157  {
3158  if (!(end = strchrW( begin, '}' )))
3159  return;
3160 
3161  num = msi_alloc( (end-begin+1)*sizeof(WCHAR) );
3162  if (!num)
3163  return;
3164 
3165  lstrcpynW( num, begin + 1, end - begin );
3166  begin += end - begin + 1;
3167 
3168  /* empty braces or '0' hides the column */
3169  if ( !num[0] || !strcmpW( num, szZero ) )
3170  {
3171  count++;
3172  msi_free( num );
3173  continue;
3174  }
3175 
3176  /* the width must be a positive number
3177  * if a width is invalid, all remaining columns are hidden
3178  */
3179  if ( !strncmpW( num, negative, 1 ) || !str_is_number( num ) ) {
3180  msi_free( num );
3181  return;
3182  }
3183 
3184  ZeroMemory( &lvc, sizeof(lvc) );
3186  lvc.cx = atolW( num );
3188 
3189  SendMessageW( control->hwnd, LVM_INSERTCOLUMNW, count++, (LPARAM)&lvc );
3190  msi_free( lvc.pszText );
3191  msi_free( num );
3192  }
3193 }
3194 
3196 {
3198  INT each_cost;
3199  LONGLONG total_cost = 0;
3200 
3201  LIST_FOR_EACH_ENTRY( feature, &dialog->package->features, MSIFEATURE, entry )
3202  {
3203  if (ERROR_SUCCESS == (MSI_GetFeatureCost(dialog->package, feature,
3205  {
3206  /* each_cost is in 512-byte units */
3207  total_cost += ((LONGLONG)each_cost) * 512;
3208  }
3209  if (ERROR_SUCCESS == (MSI_GetFeatureCost(dialog->package, feature,
3211  {
3212  /* each_cost is in 512-byte units */
3213  total_cost -= ((LONGLONG)each_cost) * 512;
3214  }
3215  }
3216  return total_cost;
3217 }
3218 
3220 {
3222  LONGLONG difference, cost;
3223  WCHAR size_text[MAX_PATH];
3224  WCHAR cost_text[MAX_PATH];
3225  LPWSTR drives, ptr;
3226  LVITEMW lvitem;
3227  DWORD size;
3228  int i = 0;
3229 
3230  cost = msi_vcl_get_cost(dialog);
3231  StrFormatByteSizeW(cost, cost_text, MAX_PATH);
3232 
3234  if ( !size ) return;
3235 
3236  drives = msi_alloc( (size + 1) * sizeof(WCHAR) );
3237  if ( !drives ) return;
3238 
3239  GetLogicalDriveStringsW( size, drives );
3240 
3241  ptr = drives;
3242  while (*ptr)
3243  {
3244 #ifdef __REACTOS__
3245  if (GetDriveTypeW(ptr) != DRIVE_FIXED)
3246  {
3247  ptr += lstrlenW(ptr) + 1;
3248  continue;
3249  }
3250 #endif
3251  lvitem.mask = LVIF_TEXT;
3252  lvitem.iItem = i;
3253  lvitem.iSubItem = 0;
3254  lvitem.pszText = ptr;
3255  lvitem.cchTextMax = lstrlenW(ptr) + 1;
3256  SendMessageW( control->hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvitem );
3257 
3259  difference = free.QuadPart - cost;
3260 
3261  StrFormatByteSizeW(total.QuadPart, size_text, MAX_PATH);
3262  lvitem.iSubItem = 1;
3263  lvitem.pszText = size_text;
3264  lvitem.cchTextMax = lstrlenW(size_text) + 1;
3265  SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3266 
3267  StrFormatByteSizeW(free.QuadPart, size_text, MAX_PATH);
3268  lvitem.iSubItem = 2;
3269  lvitem.pszText = size_text;
3270  lvitem.cchTextMax = lstrlenW(size_text) + 1;
3271  SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3272 
3273  lvitem.iSubItem = 3;
3274  lvitem.pszText = cost_text;
3275  lvitem.cchTextMax = lstrlenW(cost_text) + 1;
3276  SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3277 
3278  StrFormatByteSizeW(difference, size_text, MAX_PATH);
3279  lvitem.iSubItem = 4;
3280  lvitem.pszText = size_text;
3281  lvitem.cchTextMax = lstrlenW(size_text) + 1;
3282  SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3283 
3284  ptr += lstrlenW(ptr) + 1;
3285  i++;
3286  }
3287 
3288  msi_free( drives );
3289 }
3290 
3292 {
3293  msi_control *control;
3294  DWORD style;
3295 
3299  control = msi_dialog_add_control( dialog, rec, WC_LISTVIEWW, style );
3300  if (!control)
3301  return ERROR_FUNCTION_FAILED;
3302 
3303  msi_dialog_vcl_add_columns( dialog, control, rec );
3304  msi_dialog_vcl_add_drives( dialog, control );
3305 
3306  return ERROR_SUCCESS;
3307 }
3308 
3309 /******************** VolumeSelect Combo ***************************************/
3310 
3312  msi_control *control, WPARAM param )
3313 {
3314  WCHAR text[MAX_PATH];
3315  LPWSTR prop;
3316  BOOL indirect;
3317  int index;
3318 
3319  if (HIWORD(param) != CBN_SELCHANGE)
3320  return ERROR_SUCCESS;
3321 
3322  index = SendMessageW( control->hwnd, CB_GETCURSEL, 0, 0 );
3323  if ( index == CB_ERR )
3324  {
3325  ERR("No ComboBox item selected!\n");
3326  return ERROR_FUNCTION_FAILED;
3327  }
3328 
3329  SendMessageW( control->hwnd, CB_GETLBTEXT, index, (LPARAM)text );
3330 
3332  prop = msi_dialog_dup_property( dialog, control->property, indirect );
3333 
3334  msi_dialog_set_property( dialog->package, prop, text );
3335 
3336  msi_free( prop );
3337  return ERROR_SUCCESS;
3338 }
3339 
3341 {
3342  LPWSTR drives, ptr;
3343  DWORD size;
3344 
3346  if ( !size ) return;
3347 
3348  drives = msi_alloc( (size + 1) * sizeof(WCHAR) );
3349  if ( !drives ) return;
3350 
3351  GetLogicalDriveStringsW( size, drives );
3352 
3353  ptr = drives;
3354  while (*ptr)
3355  {
3356  SendMessageW( control->hwnd, CB_ADDSTRING, 0, (LPARAM)ptr );
3357  ptr += lstrlenW(ptr) + 1;
3358  }
3359 
3360  msi_free( drives );
3361 }
3362 
3364 {
3365  msi_control *control;
3366  LPCWSTR prop;
3367  DWORD style;
3368 
3369  /* FIXME: CBS_OWNERDRAWFIXED */
3373  control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style );
3374  if (!control)
3375  return ERROR_FUNCTION_FAILED;
3376 
3377  control->attributes = MSI_RecordGetInteger( rec, 8 );
3379  prop = MSI_RecordGetString( rec, 9 );
3380  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
3381 
3382  msi_dialog_vsc_add_drives( dialog, control );
3383 
3384  return ERROR_SUCCESS;
3385 }
3386 
3388 {
3389  static const WCHAR hrefW[] = {'h','r','e','f'};
3390  static const WCHAR openW[] = {'o','p','e','n',0};
3391  int len, len_href = sizeof(hrefW) / sizeof(hrefW[0]);
3392  const WCHAR *p, *q;
3393  WCHAR quote = 0;
3394  LITEM item;
3395 
3396  item.mask = LIF_ITEMINDEX | LIF_URL;
3397  item.iLink = 0;
3398  item.szUrl[0] = 0;
3399 
3400  SendMessageW( control->hwnd, LM_GETITEM, 0, (LPARAM)&item );
3401 
3402  p = item.szUrl;
3403  while (*p && *p != '<') p++;
3404  if (!*p++) return ERROR_SUCCESS;
3405  if (toupperW( *p++ ) != 'A' || !isspaceW( *p++ )) return ERROR_SUCCESS;
3406  while (*p && isspaceW( *p )) p++;
3407 
3408  len = strlenW( p );
3409  if (len > len_href && !memicmpW( p, hrefW, len_href ))
3410  {
3411  p += len_href;
3412  while (*p && isspaceW( *p )) p++;
3413  if (!*p || *p++ != '=') return ERROR_SUCCESS;
3414  while (*p && isspaceW( *p )) p++;
3415 
3416  if (*p == '\"' || *p == '\'') quote = *p++;
3417  q = p;
3418  if (quote)
3419  {
3420  while (*q && *q != quote) q++;
3421  if (*q != quote) return ERROR_SUCCESS;
3422  }
3423  else
3424  {
3425  while (*q && *q != '>' && !isspaceW( *q )) q++;
3426  if (!*q) return ERROR_SUCCESS;
3427  }
3428  item.szUrl[q - item.szUrl] = 0;
3429  ShellExecuteW( NULL, openW, p, NULL, NULL, SW_SHOWNORMAL );
3430  }
3431  return ERROR_SUCCESS;
3432 }
3433 
3435 {
3436  msi_control *control;
3438  const WCHAR *text = MSI_RecordGetString( rec, 10 );
3439  int len = strlenW( text );
3440  LITEM item;
3441 
3442  control = msi_dialog_add_control( dialog, rec, WC_LINK, style );
3443  if (!control)
3444  return ERROR_FUNCTION_FAILED;
3445 
3446  control->attributes = MSI_RecordGetInteger( rec, 8 );
3448 
3449  item.mask = LIF_ITEMINDEX | LIF_STATE | LIF_URL;
3450  item.iLink = 0;
3451  item.state = LIS_ENABLED;
3452  item.stateMask = LIS_ENABLED;
3453  if (len < L_MAX_URL_LENGTH) strcpyW( item.szUrl, text );
3454  else item.szUrl[0] = 0;
3455 
3456  SendMessageW( control->hwnd, LM_SETITEM, 0, (LPARAM)&item );
3457 
3458  return ERROR_SUCCESS;
3459 }
3460 
3461 static const struct control_handler msi_dialog_handler[] =
3462 {
3484 };
3485 
3486 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
3487 
3489 {
3490  msi_dialog *dialog = param;
3492  UINT i;
3493 
3494  /* find and call the function that can create this type of control */
3495  control_type = MSI_RecordGetString( rec, 3 );
3496  for( i=0; i<NUM_CONTROL_TYPES; i++ )
3498  break;
3499  if( i != NUM_CONTROL_TYPES )
3500  msi_dialog_handler[i].func( dialog, rec );
3501  else
3502  ERR("no handler for element type %s\n", debugstr_w(control_type));
3503 
3504  return ERROR_SUCCESS;
3505 }
3506 
3508 {
3509  static const WCHAR query[] = {
3510  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3511  'C','o','n','t','r','o','l',' ','W','H','E','R','E',' ',
3512  '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
3513  UINT r;
3514  MSIQUERY *view;
3515  MSIPACKAGE *package = dialog->package;
3516 
3517  TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
3518 
3519  /* query the Control table for all the elements of the control */
3520  r = MSI_OpenQuery( package->db, &view, query, dialog->name );
3521  if( r != ERROR_SUCCESS )
3522  {
3523  ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
3524  return ERROR_INVALID_PARAMETER;
3525  }
3526 
3528  msiobj_release( &view->hdr );
3529  return r;
3530 }
3531 
3533 {
3534  /* FIXME: should restore the original values of any properties we changed */
3536 }
3537 
3538 /* figure out the height of 10 point MS Sans Serif */
3540 {
3541  static const WCHAR szSansSerif[] = {
3542  'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
3543  LOGFONTW lf;
3544  TEXTMETRICW tm;
3545  BOOL r;
3546  LONG height = 0;
3547  HFONT hFont, hOldFont;
3548  HDC hdc;
3549 
3550  hdc = GetDC( hwnd );
3551  if (hdc)
3552  {
3553  memset( &lf, 0, sizeof lf );
3554  lf.lfHeight = MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
3555  strcpyW( lf.lfFaceName, szSansSerif );
3556  hFont = CreateFontIndirectW(&lf);
3557  if (hFont)
3558  {
3559  hOldFont = SelectObject( hdc, hFont );
3560  r = GetTextMetricsW( hdc, &tm );
3561  if (r)
3562  height = tm.tmHeight;
3563  SelectObject( hdc, hOldFont );
3564  DeleteObject( hFont );
3565  }
3566  ReleaseDC( hwnd, hdc );
3567  }
3568  return height;
3569 }
3570 
3571 /* fetch the associated record from the Dialog table */
3573 {
3574  static const WCHAR query[] = {
3575  'S','E','L','E','C','T',' ','*',' ',
3576  'F','R','O','M',' ','D','i','a','l','o','g',' ',
3577  'W','H','E','R','E',' ',
3578  '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
3579  MSIPACKAGE *package = dialog->package;
3580  MSIRECORD *rec = NULL;
3581 
3582  TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
3583 
3584  rec = MSI_QueryGetRecord( package->db, query, dialog->name );
3585  if( !rec )
3586  WARN("query failed for dialog %s\n", debugstr_w(dialog->name));
3587 
3588  return rec;
3589 }
3590 
3592 {
3593  static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0};
3594  static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0};
3595 
3596  UINT xres, yres;
3597  POINT center;
3598  SIZE sz;
3599  LONG style;
3600 
3601  center.x = MSI_RecordGetInteger( rec, 2 );
3602  center.y = MSI_RecordGetInteger( rec, 3 );
3603 
3604  sz.cx = MSI_RecordGetInteger( rec, 4 );
3605  sz.cy = MSI_RecordGetInteger( rec, 5 );
3606 
3607  sz.cx = msi_dialog_scale_unit( dialog, sz.cx );
3608  sz.cy = msi_dialog_scale_unit( dialog, sz.cy );
3609 
3610  xres = msi_get_property_int( dialog->package->db, szScreenX, 0 );
3611  yres = msi_get_property_int( dialog->package->db, szScreenY, 0 );
3612 
3613  center.x = MulDiv( center.x, xres, 100 );
3614  center.y = MulDiv( center.y, yres, 100 );
3615 
3616  /* turn the client pos into the window rectangle */
3617  if (dialog->package->center_x && dialog->package->center_y)
3618  {
3619  pos->left = dialog->package->center_x - sz.cx / 2.0;
3620  pos->right = pos->left + sz.cx;
3621  pos->top = dialog->package->center_y - sz.cy / 2.0;
3622  pos->bottom = pos->top + sz.cy;
3623  }
3624  else
3625  {
3626  pos->left = center.x - sz.cx/2;
3627  pos->right = pos->left + sz.cx;
3628  pos->top = center.y - sz.cy/2;
3629  pos->bottom = pos->top + sz.cy;
3630 
3631  /* save the center */
3632  dialog->package->center_x = center.x;
3633  dialog->package->center_y = center.y;
3634  }
3635 
3636  dialog->size.cx = sz.cx;
3637  dialog->size.cy = sz.cy;
3638 
3639  TRACE("%s\n", wine_dbgstr_rect(pos));
3640 
3641  style = GetWindowLongPtrW( dialog->hwnd,