ReactOS  0.4.14-dev-41-g31d7680
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 
175 {
176  UINT sz, r;
177  LPWSTR buf;
178 
179  sz = 0x20;
180  buf = msi_alloc( sz*sizeof(WCHAR) );
181  while ( buf )
182  {
183  r = GetWindowTextW( hwnd, buf, sz );
184  if ( r < (sz - 1) )
185  break;
186  sz *= 2;
187  buf = msi_realloc( buf, sz*sizeof(WCHAR) );
188  }
189 
190  return buf;
191 }
192 
194 {
195  return MulDiv( val, dialog->scale, 12 );
196 }
197 
199 {
200  msi_control *control;
201 
202  if( !name )
203  return NULL;
204  if( !dialog->hwnd )
205  return NULL;
206  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
207  if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */
208  return control;
209  return NULL;
210 }
211 
213 {
214  msi_control *control;
215 
216  if( !type )
217  return NULL;
218  if( !dialog->hwnd )
219  return NULL;
220  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
221  if( !strcmpW( control->type, type ) ) /* FIXME: case sensitive? */
222  return control;
223  return NULL;
224 }
225 
227 {
228  msi_control *control;
229 
230  if( !dialog->hwnd )
231  return NULL;
232  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
233  if( hwnd == control->hwnd )
234  return control;
235  return NULL;
236 }
237 
239 {
241  LPWSTR ret = NULL;
242 
243  if (str)
244  deformat_string( package, str, &ret );
245  return ret;
246 }
247 
249 {
250  LPWSTR prop = NULL;
251 
252  if (!property)
253  return NULL;
254 
255  if (indirect)
256  prop = msi_dup_property( dialog->package->db, property );
257 
258  if (!prop)
259  prop = strdupW( property );
260 
261  return prop;
262 }
263 
264 /*
265  * msi_dialog_get_style
266  *
267  * Extract the {\style} string from the front of the text to display and
268  * update the pointer. Only the last style in a list is applied.
269  */
271 {
272  LPWSTR ret;
273  LPCWSTR q, i, first;
274  DWORD len;
275 
276  q = NULL;
277  *rest = p;
278  if( !p )
279  return NULL;
280 
281  while ((first = strchrW( p, '{' )) && (q = strchrW( first + 1, '}' )))
282  {
283  p = first + 1;
284  if( *p != '\\' && *p != '&' )
285  return NULL;
286 
287  /* little bit of sanity checking to stop us getting confused with RTF */
288  for( i=++p; i<q; i++ )
289  if( *i == '}' || *i == '\\' )
290  return NULL;
291  }
292 
293  if (!q)
294  return NULL;
295 
296  *rest = ++q;
297  len = q - p;
298 
299  ret = msi_alloc( len*sizeof(WCHAR) );
300  if( !ret )
301  return ret;
302  memcpy( ret, p, len*sizeof(WCHAR) );
303  ret[len-1] = 0;
304  return ret;
305 }
306 
308 {
310  msi_font *font;
311  LPCWSTR face, name;
312  LOGFONTW lf;
313  INT style;
314  HDC hdc;
315 
316  /* create a font and add it to the list */
317  name = MSI_RecordGetString( rec, 1 );
319  strcpyW( font->name, name );
320  list_add_head( &dialog->fonts, &font->entry );
321 
322  font->color = MSI_RecordGetInteger( rec, 4 );
323 
324  memset( &lf, 0, sizeof lf );
325  face = MSI_RecordGetString( rec, 2 );
326  lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
327  style = MSI_RecordGetInteger( rec, 5 );
329  lf.lfWeight = FW_BOLD;
331  lf.lfItalic = TRUE;
333  lf.lfUnderline = TRUE;
335  lf.lfStrikeOut = TRUE;
337 
338  /* adjust the height */
339  hdc = GetDC( dialog->hwnd );
340  if (hdc)
341  {
343  ReleaseDC( dialog->hwnd, hdc );
344  }
345 
346  font->hfont = CreateFontIndirectW( &lf );
347 
348  TRACE("Adding font style %s\n", debugstr_w(font->name) );
349 
350  return ERROR_SUCCESS;
351 }
352 
354 {
355  msi_font *font = NULL;
356 
358  if( !strcmpW( font->name, name ) ) /* FIXME: case sensitive? */
359  break;
360 
361  return font;
362 }
363 
365 {
366  msi_font *font;
367 
369  if( font )
370  SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE );
371  else
372  ERR("No font entry for %s\n", debugstr_w(name));
373  return ERROR_SUCCESS;
374 }
375 
377 {
378  static const WCHAR query[] = {
379  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
380  '`','T','e','x','t','S','t','y','l','e','`',0};
381  MSIQUERY *view;
382  UINT r;
383 
384  TRACE("dialog %p\n", dialog );
385 
386  r = MSI_OpenQuery( dialog->package->db, &view, query );
387  if( r != ERROR_SUCCESS )
388  return r;
389 
391  msiobj_release( &view->hdr );
392  return r;
393 }
394 
396 {
397  list_remove( &t->entry );
398  /* leave dialog->hwnd - destroying parent destroys child windows */
399  msi_free( t->property );
400  msi_free( t->value );
401  if( t->hBitmap )
402  DeleteObject( t->hBitmap );
403  if( t->hIcon )
404  DestroyIcon( t->hIcon );
405  msi_free( t->tabnext );
406  msi_free( t->type );
407  if (t->hDll)
408  FreeLibrary( t->hDll );
409  msi_free( t );
410 }
411 
413  const WCHAR *szCls, const WCHAR *name, const WCHAR *text,
415 {
416  DWORD x, y, width, height;
417  LPWSTR font = NULL, title_font = NULL;
418  LPCWSTR title = NULL;
419  msi_control *control;
420 
421  style |= WS_CHILD;
422 
423  control = msi_alloc( FIELD_OFFSET( msi_control, name[strlenW( name ) + 1] ));
424  if (!control)
425  return NULL;
426 
427  strcpyW( control->name, name );
428  list_add_tail( &dialog->controls, &control->entry );
429  control->handler = NULL;
430  control->update = NULL;
431  control->property = NULL;
432  control->value = NULL;
433  control->hBitmap = NULL;
434  control->hIcon = NULL;
435  control->hDll = NULL;
436  control->tabnext = strdupW( MSI_RecordGetString( rec, 11) );
437  control->type = strdupW( MSI_RecordGetString( rec, 3 ) );
438  control->progress_current = 0;
439  control->progress_max = 100;
440  control->progress_backwards = FALSE;
441 
442  x = MSI_RecordGetInteger( rec, 4 );
443  y = MSI_RecordGetInteger( rec, 5 );
444  width = MSI_RecordGetInteger( rec, 6 );
445  height = MSI_RecordGetInteger( rec, 7 );
446 
451 
452  if( text )
453  {
454  deformat_string( dialog->package, text, &title_font );
455  font = msi_dialog_get_style( title_font, &title );
456  }
457 
458  control->hwnd = CreateWindowExW( exstyle, szCls, title, style,
459  x, y, width, height, parent, NULL, NULL, NULL );
460 
461  TRACE("Dialog %s control %s hwnd %p\n",
462  debugstr_w(dialog->name), debugstr_w(text), control->hwnd );
463 
464  msi_dialog_set_font( dialog, control->hwnd,
465  font ? font : dialog->default_font );
466 
467  msi_free( title_font );
468  msi_free( font );
469 
470  return control;
471 }
472 
474 {
475  MSIRECORD *rec;
476  LPWSTR text;
477 
478  static const WCHAR query[] = {
479  's','e','l','e','c','t',' ','*',' ',
480  'f','r','o','m',' ','`','U','I','T','e','x','t','`',' ',
481  'w','h','e','r','e',' ','`','K','e','y','`',' ','=',' ','\'','%','s','\'',0
482  };
483 
484  rec = MSI_QueryGetRecord( dialog->package->db, query, key );
485  if (!rec) return NULL;
486  text = strdupW( MSI_RecordGetString( rec, 2 ) );
487  msiobj_release( &rec->hdr );
488  return text;
489 }
490 
492 {
493  static const WCHAR query[] = {
494  's','e','l','e','c','t',' ','*',' ',
495  'f','r','o','m',' ','B','i','n','a','r','y',' ',
496  'w','h','e','r','e',' ',
497  '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0
498  };
499 
500  return MSI_QueryGetRecord( db, query, name );
501 }
502 
504  UINT cx, UINT cy, UINT flags )
505 {
506  MSIRECORD *rec;
507  HANDLE himage = NULL;
508  LPWSTR tmp;
509  UINT r;
510 
511  TRACE("%p %s %u %u %08x\n", db, debugstr_w(name), cx, cy, flags);
512 
513  if (!(tmp = msi_create_temp_file( db ))) return NULL;
514 
515  rec = msi_get_binary_record( db, name );
516  if( rec )
517  {
518  r = MSI_RecordStreamToFile( rec, 2, tmp );
519  if( r == ERROR_SUCCESS )
520  {
521  himage = LoadImageW( 0, tmp, type, cx, cy, flags );
522  }
523  msiobj_release( &rec->hdr );
524  }
525  DeleteFileW( tmp );
526 
527  msi_free( tmp );
528  return himage;
529 }
530 
531 static HICON msi_load_icon( MSIDATABASE *db, LPCWSTR text, UINT attributes )
532 {
533  DWORD cx = 0, cy = 0, flags;
534 
536  if( attributes & msidbControlAttributesFixedSize )
537  {
538  flags &= ~LR_DEFAULTSIZE;
539  if( attributes & msidbControlAttributesIconSize16 )
540  {
541  cx += 16;
542  cy += 16;
543  }
544  if( attributes & msidbControlAttributesIconSize32 )
545  {
546  cx += 32;
547  cy += 32;
548  }
549  /* msidbControlAttributesIconSize48 handled by above logic */
550  }
551  return msi_load_image( db, text, IMAGE_ICON, cx, cy, flags );
552 }
553 
555 {
556  msi_control *control;
557 
558  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
559  {
560  if ( control->property && !strcmpW( control->property, property ) && control->update )
561  control->update( dialog, control );
562  }
563 }
564 
566 {
567  msi_control *control;
568 
569  LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
570  {
571  if ( control->property && control->update )
572  control->update( dialog, control );
573  }
574 }
575 
577 {
578  UINT r = msi_set_property( package->db, property, value, -1 );
579  if (r == ERROR_SUCCESS && !strcmpW( property, szSourceDir ))
580  msi_reset_folders( package, TRUE );
581 }
582 
584 {
585  TVITEMW tvi;
586 
587  /* get the feature from the item */
588  memset( &tvi, 0, sizeof tvi );
589  tvi.hItem = hItem;
590  tvi.mask = TVIF_PARAM | TVIF_HANDLE;
591  SendMessageW( hwnd, TVM_GETITEMW, 0, (LPARAM)&tvi );
592  return (MSIFEATURE *)tvi.lParam;
593 }
594 
596 {
601 };
602 
604 {
605  struct msi_selection_tree_info *info = GetPropW( control->hwnd, szButtonData );
606  return msi_seltree_feature_from_item( control->hwnd, info->selected );
607 }
608 
609 static void dialog_handle_event( msi_dialog *dialog, const WCHAR *control,
610  const WCHAR *attribute, MSIRECORD *rec )
611 {
612  msi_control* ctrl;
613 
614  ctrl = msi_dialog_find_control( dialog, control );
615  if (!ctrl)
616  return;
617  if( !strcmpW( attribute, szText ) )
618  {
619  const WCHAR *font_text, *text = NULL;
620  WCHAR *font, *text_fmt = NULL;
621 
622  font_text = MSI_RecordGetString( rec , 1 );
623  font = msi_dialog_get_style( font_text, &text );
624  deformat_string( dialog->package, text, &text_fmt );
625  if (text_fmt) text = text_fmt;
626  else text = szEmpty;
627 
628  SetWindowTextW( ctrl->hwnd, text );
629 
630  msi_free( font );
631  msi_free( text_fmt );
633  }
634  else if( !strcmpW( attribute, szProgress ) )
635  {
636  DWORD func, val1, val2, units;
637 
638  func = MSI_RecordGetInteger( rec, 1 );
639  val1 = MSI_RecordGetInteger( rec, 2 );
640  val2 = MSI_RecordGetInteger( rec, 3 );
641 
642  TRACE("progress: func %u val1 %u val2 %u\n", func, val1, val2);
643 
644  units = val1 / 512;
645  switch (func)
646  {
647  case 0: /* init */
648  SendMessageW( ctrl->hwnd, PBM_SETRANGE, 0, MAKELPARAM(0,100) );
649  if (val2)
650  {
651  ctrl->progress_max = units ? units : 100;
652  ctrl->progress_current = units;
653  ctrl->progress_backwards = TRUE;
654  SendMessageW( ctrl->hwnd, PBM_SETPOS, 100, 0 );
655  }
656  else
657  {
658  ctrl->progress_max = units ? units : 100;
659  ctrl->progress_current = 0;
660  ctrl->progress_backwards = FALSE;
661  SendMessageW( ctrl->hwnd, PBM_SETPOS, 0, 0 );
662  }
663  break;
664  case 1: /* action data increment */
665  if (val2) dialog->package->action_progress_increment = val1;
666  else dialog->package->action_progress_increment = 0;
667  break;
668  case 2: /* move */
669  if (ctrl->progress_backwards)
670  {
671  if (units >= ctrl->progress_current) ctrl->progress_current -= units;
672  else ctrl->progress_current = 0;
673  }
674  else
675  {
676  if (ctrl->progress_current + units < ctrl->progress_max) ctrl->progress_current += units;
677  else ctrl->progress_current = ctrl->progress_max;
678  }
679  SendMessageW( ctrl->hwnd, PBM_SETPOS, MulDiv(100, ctrl->progress_current, ctrl->progress_max), 0 );
680  break;
681  case 3: /* add */
682  ctrl->progress_max += units;
683  break;
684  default:
685  FIXME("Unknown progress message %u\n", func);
686  break;
687  }
688  }
689  else if ( !strcmpW( attribute, szProperty ) )
690  {
692  if (feature) msi_dialog_set_property( dialog->package, ctrl->property, feature->Directory );
693  }
694  else if ( !strcmpW( attribute, szSelectionPath ) )
695  {
698  if (!path) return;
699  SetWindowTextW( ctrl->hwnd, path );
700  msi_free(path);
701  }
702  else
703  {
704  FIXME("Attribute %s not being set\n", debugstr_w(attribute));
705  return;
706  }
707 }
708 
709 static void event_subscribe( msi_dialog *dialog, const WCHAR *event, const WCHAR *control, const WCHAR *attribute )
710 {
711  struct subscriber *sub;
712 
713  TRACE("dialog %s event %s control %s attribute %s\n", debugstr_w(dialog->name), debugstr_w(event),
715 
716  LIST_FOR_EACH_ENTRY( sub, &dialog->package->subscriptions, struct subscriber, entry )
717  {
718  if (sub->dialog == dialog &&
719  !strcmpiW( sub->event, event ) &&
720  !strcmpiW( sub->control, control ) &&
721  !strcmpiW( sub->attribute, attribute ))
722  {
723  TRACE("already subscribed\n");
724  return;
725  };
726  }
727  if (!(sub = msi_alloc( sizeof(*sub) ))) return;
728  sub->dialog = dialog;
729  sub->event = strdupW( event );
730  sub->control = strdupW( control );
731  sub->attribute = strdupW( attribute );
732  list_add_tail( &dialog->package->subscriptions, &sub->entry );
733 }
734 
736 {
738  const WCHAR *control;
739 };
740 
741 static UINT map_event( MSIRECORD *row, void *param )
742 {
743  struct dialog_control *dc = param;
744  const WCHAR *event = MSI_RecordGetString( row, 3 );
745  const WCHAR *attribute = MSI_RecordGetString( row, 4 );
746 
747  event_subscribe( dc->dialog, event, dc->control, attribute );
748  return ERROR_SUCCESS;
749 }
750 
752 {
753  static const WCHAR queryW[] =
754  {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
755  '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ',
756  'W','H','E','R','E',' ','`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
757  'A','N','D',' ','`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0};
758  MSIQUERY *view;
760  {
761  dialog,
762  control
763  };
764 
765  if (!MSI_OpenQuery( dialog->package->db, &view, queryW, dialog->name, control ))
766  {
768  msiobj_release( &view->hdr );
769  }
770 }
771 
772 /* everything except radio buttons */
774  MSIRECORD *rec, LPCWSTR szCls, DWORD style )
775 {
776  DWORD attributes;
777  const WCHAR *text = NULL, *name, *control_type;
778  DWORD exstyle = 0;
779 
780  name = MSI_RecordGetString( rec, 2 );
781  control_type = MSI_RecordGetString( rec, 3 );
782  attributes = MSI_RecordGetInteger( rec, 8 );
784 
785  TRACE("%s, %s, %08x, %s, %08x\n", debugstr_w(szCls), debugstr_w(name),
786  attributes, debugstr_w(text), style);
787 
788  if( attributes & msidbControlAttributesVisible )
789  style |= WS_VISIBLE;
790  if( ~attributes & msidbControlAttributesEnabled )
791  style |= WS_DISABLED;
792  if( attributes & msidbControlAttributesSunken )
793  exstyle |= WS_EX_CLIENTEDGE;
794 
796 
797  return dialog_create_window( dialog, rec, exstyle, szCls, name, text, style, dialog->hwnd );
798 }
799 
801 {
805 };
806 
807 /*
808  * we don't erase our own background,
809  * so we have to make sure that the parent window redraws first
810  */
812 {
813  HWND hParent;
814  RECT rc;
815 
816  hParent = GetParent( hWnd );
817  GetWindowRect( hWnd, &rc );
818  MapWindowPoints( NULL, hParent, (LPPOINT) &rc, 2 );
819  InvalidateRect( hParent, &rc, TRUE );
820 }
821 
822 static LRESULT WINAPI
824 {
825  struct msi_text_info *info;
826  LRESULT r = 0;
827 
828  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
829 
831 
832  if( msg == WM_CTLCOLORSTATIC &&
833  ( info->attributes & msidbControlAttributesTransparent ) )
834  {
837  }
838 
839  r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
840  if ( info->font )
841  SetTextColor( (HDC)wParam, info->font->color );
842 
843  switch( msg )
844  {
845  case WM_SETTEXT:
847  break;
848  case WM_NCDESTROY:
849  msi_free( info );
851  break;
852  }
853 
854  return r;
855 }
856 
858 {
859  msi_control *control;
860  struct msi_text_info *info;
861  LPCWSTR text, ptr, prop, control_name;
862  LPWSTR font_name;
863 
864  TRACE("%p %p\n", dialog, rec);
865 
866  control = msi_dialog_add_control( dialog, rec, szStatic, SS_LEFT | WS_GROUP );
867  if( !control )
868  return ERROR_FUNCTION_FAILED;
869 
870  info = msi_alloc( sizeof *info );
871  if( !info )
872  return ERROR_SUCCESS;
873 
874  control_name = MSI_RecordGetString( rec, 2 );
875  control->attributes = MSI_RecordGetInteger( rec, 8 );
876  prop = MSI_RecordGetString( rec, 9 );
877  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
878 
879  text = MSI_RecordGetString( rec, 10 );
880  font_name = msi_dialog_get_style( text, &ptr );
881  info->font = ( font_name ) ? msi_dialog_find_font( dialog, font_name ) : NULL;
882  msi_free( font_name );
883 
884  info->attributes = MSI_RecordGetInteger( rec, 8 );
885  if( info->attributes & msidbControlAttributesTransparent )
887 
888  info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
890  SetPropW( control->hwnd, szButtonData, info );
891 
893  return ERROR_SUCCESS;
894 }
895 
896 /* strip any leading text style label from text field */
898 {
899  WCHAR *p, *text;
900 
901  text = msi_get_deformatted_field( package, rec, 10 );
902  if (!text)
903  return NULL;
904 
905  p = text;
906  while (*p && *p != '{') p++;
907  if (!*p++) return text;
908 
909  while (*p && *p != '}') p++;
910  if (!*p++) return text;
911 
912  p = strdupW( p );
913  msi_free( text );
914  return p;
915 }
916 
918 {
919  static const WCHAR szNullArg[] = {'{','}',0};
920  LPWSTR p, prop, arg_fmt = NULL;
921  UINT len;
922 
923  len = strlenW( event );
924  prop = msi_alloc( len * sizeof(WCHAR) );
925  strcpyW( prop, &event[1] );
926  p = strchrW( prop, ']' );
927  if (p && (p[1] == 0 || p[1] == ' '))
928  {
929  *p = 0;
930  if (strcmpW( szNullArg, arg ))
931  deformat_string( dialog->package, arg, &arg_fmt );
932  msi_dialog_set_property( dialog->package, prop, arg_fmt );
934  msi_free( arg_fmt );
935  }
936  else ERR("Badly formatted property string - what happens?\n");
937  msi_free( prop );
938  return ERROR_SUCCESS;
939 }
940 
942 {
943  LPWSTR event_fmt = NULL, arg_fmt = NULL;
944 
945  TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
946 
947  deformat_string( dialog->package, event, &event_fmt );
948  deformat_string( dialog->package, arg, &arg_fmt );
949 
950  dialog->event_handler( dialog, event_fmt, arg_fmt );
951 
952  msi_free( event_fmt );
953  msi_free( arg_fmt );
954 
955  return ERROR_SUCCESS;
956 }
957 
959 {
962  UINT r;
963 
964  condition = MSI_RecordGetString( rec, 5 );
965  r = MSI_EvaluateConditionW( dialog->package, condition );
966  if (r == MSICONDITION_TRUE || r == MSICONDITION_NONE)
967  {
968  event = MSI_RecordGetString( rec, 3 );
969  arg = MSI_RecordGetString( rec, 4 );
970  if (event[0] == '[')
972  else
974  }
975  return ERROR_SUCCESS;
976 }
977 
979 {
980  static const WCHAR query[] = {
981  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
982  'C','o','n','t','r','o','l','E','v','e','n','t',' ','W','H','E','R','E',' ',
983  '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ','A','N','D',' ',
984  '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
985  'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0};
986  MSIQUERY *view;
987  UINT r;
988 
989  if (HIWORD(param) != BN_CLICKED)
990  return ERROR_SUCCESS;
991 
992  r = MSI_OpenQuery( dialog->package->db, &view, query, dialog->name, control->name );
993  if (r != ERROR_SUCCESS)
994  {
995  ERR("query failed\n");
996  return ERROR_SUCCESS;
997  }
999  msiobj_release( &view->hdr );
1000 
1001  /* dialog control events must be processed last regardless of ordering */
1002  if (dialog->pending_event)
1003  {
1004  r = dialog->pending_event( dialog, dialog->pending_argument );
1005 
1006  msi_free( dialog->pending_argument );
1007  dialog->pending_event = NULL;
1008  dialog->pending_argument = NULL;
1009  }
1010  return r;
1011 }
1012 
1014 {
1015  msi_control *control;
1017 
1018  TRACE("%p %p\n", dialog, rec);
1019 
1020  style = WS_TABSTOP;
1021  attributes = MSI_RecordGetInteger( rec, 8 );
1023  style |= BS_ICON;
1024 
1025  control = msi_dialog_add_control( dialog, rec, szButton, style );
1026  if( !control )
1027  return ERROR_FUNCTION_FAILED;
1028 
1030 
1032  {
1033  /* set the icon */
1034  LPWSTR name = msi_get_binary_name( dialog->package, rec );
1035  control->hIcon = msi_load_icon( dialog->package->db, name, attributes );
1036  if (control->hIcon)
1037  {
1038  SendMessageW( control->hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM) control->hIcon );
1039  }
1040  else
1041  ERR("Failed to load icon %s\n", debugstr_w(name));
1042  msi_free( name );
1043  }
1044 
1045  return ERROR_SUCCESS;
1046 }
1047 
1049 {
1050  static const WCHAR query[] = {
1051  'S','E','L','E','C','T',' ','*',' ',
1052  'F','R','O','M',' ','`','C','h','e','c','k','B','o','x','`',' ',
1053  'W','H','E','R','E',' ',
1054  '`','P','r','o','p','e','r','t','y','`',' ','=',' ',
1055  '\'','%','s','\'',0
1056  };
1057  MSIRECORD *rec = NULL;
1058  LPWSTR ret = NULL;
1059 
1060  /* find if there is a value associated with the checkbox */
1061  rec = MSI_QueryGetRecord( dialog->package->db, query, prop );
1062  if (!rec)
1063  return ret;
1064 
1065  ret = msi_get_deformatted_field( dialog->package, rec, 2 );
1066  if( ret && !ret[0] )
1067  {
1068  msi_free( ret );
1069  ret = NULL;
1070  }
1071  msiobj_release( &rec->hdr );
1072  if (ret)
1073  return ret;
1074 
1075  ret = msi_dup_property( dialog->package->db, prop );
1076  if( ret && !ret[0] )
1077  {
1078  msi_free( ret );
1079  ret = NULL;
1080  }
1081 
1082  return ret;
1083 }
1084 
1086 {
1087  WCHAR state[2] = {0};
1088  DWORD sz = 2;
1089 
1090  msi_get_property( dialog->package->db, control->property, state, &sz );
1091  return state[0] ? 1 : 0;
1092 }
1093 
1095 {
1096  static const WCHAR szState[] = {'1',0};
1097  LPCWSTR val;
1098 
1099  /* if uncheck then the property is set to NULL */
1100  if (!state)
1101  {
1102  msi_dialog_set_property( dialog->package, control->property, NULL );
1103  return;
1104  }
1105 
1106  /* check for a custom state */
1107  if (control->value && control->value[0])
1108  val = control->value;
1109  else
1110  val = szState;
1111 
1112  msi_dialog_set_property( dialog->package, control->property, val );
1113 }
1114 
1116 {
1119 }
1120 
1122 {
1123  UINT state;
1124 
1125  if (HIWORD(param) != BN_CLICKED)
1126  return ERROR_SUCCESS;
1127 
1128  TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
1129 
1131  state = state ? 0 : 1;
1134 
1135  return msi_dialog_button_handler( dialog, control, param );
1136 }
1137 
1139 {
1140  msi_control *control;
1141  LPCWSTR prop;
1142 
1143  TRACE("%p %p\n", dialog, rec);
1144 
1148  prop = MSI_RecordGetString( rec, 9 );
1149  if (prop)
1150  {
1151  control->property = strdupW( prop );
1152  control->value = msi_get_checkbox_value( dialog, prop );
1153  TRACE("control %s value %s\n", debugstr_w(control->property), debugstr_w(control->value));
1154  }
1156  return ERROR_SUCCESS;
1157 }
1158 
1160 {
1161  DWORD attributes;
1162  LPCWSTR name;
1163  DWORD style, exstyle = 0;
1164  DWORD x, y, width, height;
1165  msi_control *control;
1166 
1167  TRACE("%p %p\n", dialog, rec);
1168 
1170 
1171  name = MSI_RecordGetString( rec, 2 );
1172  attributes = MSI_RecordGetInteger( rec, 8 );
1173 
1175  style |= WS_VISIBLE;
1177  style |= WS_DISABLED;
1179  exstyle |= WS_EX_CLIENTEDGE;
1180 
1182 
1183  control = msi_alloc( FIELD_OFFSET(msi_control, name[strlenW( name ) + 1] ));
1184  if (!control)
1185  return ERROR_OUTOFMEMORY;
1186 
1187  strcpyW( control->name, name );
1188  list_add_head( &dialog->controls, &control->entry );
1189  control->handler = NULL;
1190  control->property = NULL;
1191  control->value = NULL;
1192  control->hBitmap = NULL;
1193  control->hIcon = NULL;
1194  control->hDll = NULL;
1195  control->tabnext = strdupW( MSI_RecordGetString( rec, 11) );
1196  control->type = strdupW( MSI_RecordGetString( rec, 3 ) );
1197  control->progress_current = 0;
1198  control->progress_max = 100;
1199  control->progress_backwards = FALSE;
1200 
1201  x = MSI_RecordGetInteger( rec, 4 );
1202  y = MSI_RecordGetInteger( rec, 5 );
1203  width = MSI_RecordGetInteger( rec, 6 );
1204 
1208  height = 2; /* line is exactly 2 units in height */
1209 
1210  control->hwnd = CreateWindowExW( exstyle, szStatic, NULL, style,
1211  x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
1212 
1213  TRACE("Dialog %s control %s hwnd %p\n",
1214  debugstr_w(dialog->name), debugstr_w(name), control->hwnd );
1215 
1216  return ERROR_SUCCESS;
1217 }
1218 
1219 /******************** Scroll Text ********************************************/
1220 
1222 {
1226 };
1227 
1228 static LRESULT WINAPI
1230 {
1231  struct msi_scrolltext_info *info;
1232  HRESULT r;
1233 
1234  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
1235 
1237 
1238  r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
1239 
1240  switch( msg )
1241  {
1242  case WM_GETDLGCODE:
1243  return DLGC_WANTARROWS;
1244  case WM_NCDESTROY:
1245  msi_free( info );
1247  break;
1248  case WM_PAINT:
1249  /* native MSI sets a wait cursor here */
1250  msi_dialog_button_handler( info->dialog, info->control, BN_CLICKED );
1251  break;
1252  }
1253  return r;
1254 }
1255 
1257 {
1261 };
1262 
1263 static DWORD CALLBACK
1265 {
1266  struct msi_streamin_info *info = (struct msi_streamin_info*) arg;
1267 
1268  if( (count + info->offset) > info->length )
1269  count = info->length - info->offset;
1270  memcpy( buffer, &info->string[ info->offset ], count );
1271  *pcb = count;
1272  info->offset += count;
1273 
1274  TRACE("%d/%d\n", info->offset, info->length);
1275 
1276  return 0;
1277 }
1278 
1280 {
1281  struct msi_streamin_info info;
1282  EDITSTREAM es;
1283 
1284  info.string = strdupWtoA( text );
1285  info.offset = 0;
1286  info.length = lstrlenA( info.string ) + 1;
1287 
1288  es.dwCookie = (DWORD_PTR) &info;
1289  es.dwError = 0;
1290  es.pfnCallback = msi_richedit_stream_in;
1291 
1292  SendMessageW( control->hwnd, EM_STREAMIN, SF_RTF, (LPARAM) &es );
1293 
1294  msi_free( info.string );
1295 }
1296 
1298 {
1299  static const WCHAR szRichEdit20W[] = {'R','i','c','h','E','d','i','t','2','0','W',0};
1300  struct msi_scrolltext_info *info;
1302  HMODULE hRichedit;
1303  LPCWSTR text;
1304  DWORD style;
1305 
1306  info = msi_alloc( sizeof *info );
1307  if (!info)
1308  return ERROR_FUNCTION_FAILED;
1309 
1310  hRichedit = LoadLibraryA("riched20");
1311 
1314  control = msi_dialog_add_control( dialog, rec, szRichEdit20W, style );
1315  if (!control)
1316  {
1317  FreeLibrary( hRichedit );
1318  msi_free( info );
1319  return ERROR_FUNCTION_FAILED;
1320  }
1321 
1322  control->hDll = hRichedit;
1323 
1324  info->dialog = dialog;
1325  info->control = control;
1326 
1327  /* subclass the static control */
1331 
1332  /* add the text into the richedit */
1333  text = MSI_RecordGetString( rec, 10 );
1334  if (text)
1336 
1337  return ERROR_SUCCESS;
1338 }
1339 
1341  INT cx, INT cy, DWORD flags )
1342 {
1343  HBITMAP hOleBitmap = 0, hBitmap = 0, hOldSrcBitmap, hOldDestBitmap;
1344  MSIRECORD *rec = NULL;
1345  IStream *stm = NULL;
1346  IPicture *pic = NULL;
1347  HDC srcdc, destdc;
1348  BITMAP bm;
1349  UINT r;
1350 
1351  rec = msi_get_binary_record( db, name );
1352  if( !rec )
1353  goto end;
1354 
1355  r = MSI_RecordGetIStream( rec, 2, &stm );
1356  msiobj_release( &rec->hdr );
1357  if( r != ERROR_SUCCESS )
1358  goto end;
1359 
1360  r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (LPVOID*) &pic );
1361  IStream_Release( stm );
1362  if( FAILED( r ) )
1363  {
1364  ERR("failed to load picture\n");
1365  goto end;
1366  }
1367 
1368  r = IPicture_get_Handle( pic, (OLE_HANDLE*) &hOleBitmap );
1369  if( FAILED( r ) )
1370  {
1371  ERR("failed to get bitmap handle\n");
1372  goto end;
1373  }
1374 
1375  /* make the bitmap the desired size */
1376  r = GetObjectW( hOleBitmap, sizeof bm, &bm );
1377  if (r != sizeof bm )
1378  {
1379  ERR("failed to get bitmap size\n");
1380  goto end;
1381  }
1382 
1383  if (flags & LR_DEFAULTSIZE)
1384  {
1385  cx = bm.bmWidth;
1386  cy = bm.bmHeight;
1387  }
1388 
1389  srcdc = CreateCompatibleDC( NULL );
1390  hOldSrcBitmap = SelectObject( srcdc, hOleBitmap );
1391  destdc = CreateCompatibleDC( NULL );
1392  hBitmap = CreateCompatibleBitmap( srcdc, cx, cy );
1393  hOldDestBitmap = SelectObject( destdc, hBitmap );
1394  StretchBlt( destdc, 0, 0, cx, cy,
1395  srcdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
1396  SelectObject( srcdc, hOldSrcBitmap );
1397  SelectObject( destdc, hOldDestBitmap );
1398  DeleteDC( srcdc );
1399  DeleteDC( destdc );
1400 
1401 end:
1402  if ( pic )
1403  IPicture_Release( pic );
1404  return hBitmap;
1405 }
1406 
1408 {
1409  UINT cx, cy, flags, style, attributes;
1411  LPWSTR name;
1412 
1415 
1416  attributes = MSI_RecordGetInteger( rec, 8 );
1417  if( attributes & msidbControlAttributesFixedSize )
1418  {
1419  flags |= LR_DEFAULTSIZE;
1420  style |= SS_CENTERIMAGE;
1421  }
1422 
1424  cx = MSI_RecordGetInteger( rec, 6 );
1425  cy = MSI_RecordGetInteger( rec, 7 );
1428 
1429  name = msi_get_binary_name( dialog->package, rec );
1430  control->hBitmap = msi_load_picture( dialog->package->db, name, cx, cy, flags );
1431  if( control->hBitmap )
1434  else
1435  ERR("Failed to load bitmap %s\n", debugstr_w(name));
1436 
1437  msi_free( name );
1438 
1439  return ERROR_SUCCESS;
1440 }
1441 
1443 {
1445  DWORD attributes;
1446  LPWSTR name;
1447 
1448  TRACE("\n");
1449 
1452 
1453  attributes = MSI_RecordGetInteger( rec, 8 );
1454  name = msi_get_binary_name( dialog->package, rec );
1455  control->hIcon = msi_load_icon( dialog->package->db, name, attributes );
1456  if( control->hIcon )
1458  else
1459  ERR("Failed to load bitmap %s\n", debugstr_w(name));
1460  msi_free( name );
1461  return ERROR_SUCCESS;
1462 }
1463 
1464 /******************** Combo Box ***************************************/
1465 
1467 {
1474 };
1475 
1477 {
1478  struct msi_combobox_info *info;
1479  LRESULT r;
1480  DWORD j;
1481 
1482  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
1483 
1485  if (!info)
1486  return 0;
1487 
1488  r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
1489 
1490  switch (msg)
1491  {
1492  case WM_NCDESTROY:
1493  for (j = 0; j < info->num_items; j++)
1494  msi_free( info->items[j] );
1495  msi_free( info->items );
1496  msi_free( info );
1498  break;
1499  }
1500 
1501  return r;
1502 }
1503 
1505 {
1506  struct msi_combobox_info *info = param;
1507  LPCWSTR value, text;
1508  int pos;
1509 
1510  value = MSI_RecordGetString( rec, 3 );
1511  text = MSI_RecordGetString( rec, 4 );
1512 
1513  info->items[info->addpos_items] = strdupW( value );
1514 
1515  pos = SendMessageW( info->hwnd, CB_ADDSTRING, 0, (LPARAM)text );
1516  SendMessageW( info->hwnd, CB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] );
1517  info->addpos_items++;
1518 
1519  return ERROR_SUCCESS;
1520 }
1521 
1523 {
1524  static const WCHAR query[] = {
1525  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1526  '`','C','o','m','b','o','B','o','x','`',' ','W','H','E','R','E',' ',
1527  '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',' ',
1528  'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','`',0};
1529  MSIQUERY *view;
1530  DWORD count;
1531  UINT r;
1532 
1533  r = MSI_OpenQuery( info->dialog->package->db, &view, query, property );
1534  if (r != ERROR_SUCCESS)
1535  return r;
1536 
1537  /* just get the number of records */
1538  count = 0;
1540  if (r != ERROR_SUCCESS)
1541  {
1542  msiobj_release( &view->hdr );
1543  return r;
1544  }
1545  info->num_items = count;
1546  info->items = msi_alloc( sizeof(*info->items) * count );
1547 
1549  msiobj_release( &view->hdr );
1550  return r;
1551 }
1552 
1554 {
1555  static const WCHAR szHide[] = {'H','i','d','e',0};
1556  static const WCHAR szShow[] = {'S','h','o','w',0};
1557  static const WCHAR szDisable[] = {'D','i','s','a','b','l','e',0};
1558  static const WCHAR szEnable[] = {'E','n','a','b','l','e',0};
1559  static const WCHAR szDefault[] = {'D','e','f','a','u','l','t',0};
1560  msi_dialog *dialog = param;
1561  msi_control *control;
1563  UINT r;
1564 
1565  name = MSI_RecordGetString( rec, 2 );
1566  action = MSI_RecordGetString( rec, 3 );
1567  condition = MSI_RecordGetString( rec, 4 );
1568  r = MSI_EvaluateConditionW( dialog->package, condition );
1569  control = msi_dialog_find_control( dialog, name );
1570  if (r == MSICONDITION_TRUE && control)
1571  {
1572  TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
1573 
1574  /* FIXME: case sensitive? */
1575  if (!strcmpW( action, szHide ))
1576  ShowWindow(control->hwnd, SW_HIDE);
1577  else if (!strcmpW( action, szShow ))
1578  ShowWindow(control->hwnd, SW_SHOW);
1579  else if (!strcmpW( action, szDisable ))
1580  EnableWindow(control->hwnd, FALSE);
1581  else if (!strcmpW( action, szEnable ))
1582  EnableWindow(control->hwnd, TRUE);
1583  else if (!strcmpW( action, szDefault ))
1584  SetFocus(control->hwnd);
1585  else
1586  FIXME("Unhandled action %s\n", debugstr_w(action));
1587  }
1588  return ERROR_SUCCESS;
1589 }
1590 
1592 {
1593  static const WCHAR query[] = {
1594  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1595  'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
1596  'W','H','E','R','E',' ','`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
1597  UINT r;
1598  MSIQUERY *view;
1599  MSIPACKAGE *package = dialog->package;
1600 
1601  TRACE("%p %s\n", dialog, debugstr_w(dialog->name));
1602 
1603  /* query the Control table for all the elements of the control */
1604  r = MSI_OpenQuery( package->db, &view, query, dialog->name );
1605  if (r != ERROR_SUCCESS)
1606  return ERROR_SUCCESS;
1607 
1609  msiobj_release( &view->hdr );
1610  return r;
1611 }
1612 
1614 {
1615  struct msi_combobox_info *info;
1616  int index;
1617  LPWSTR value;
1618 
1620  return ERROR_SUCCESS;
1621 
1622  info = GetPropW( control->hwnd, szButtonData );
1623  index = SendMessageW( control->hwnd, CB_GETCURSEL, 0, 0 );
1624  if (index == CB_ERR)
1625  value = msi_get_window_text( control->hwnd );
1626  else
1627  value = (LPWSTR) SendMessageW( control->hwnd, CB_GETITEMDATA, index, 0 );
1628 
1629  msi_dialog_set_property( info->dialog->package, control->property, value );
1631 
1632  if (index == CB_ERR)
1633  msi_free( value );
1634 
1635  return ERROR_SUCCESS;
1636 }
1637 
1639 {
1640  struct msi_combobox_info *info;
1641  LPWSTR value, tmp;
1642  DWORD j;
1643 
1644  info = GetPropW( control->hwnd, szButtonData );
1645 
1646  value = msi_dup_property( dialog->package->db, control->property );
1647  if (!value)
1648  {
1649  SendMessageW( control->hwnd, CB_SETCURSEL, -1, 0 );
1650  return;
1651  }
1652 
1653  for (j = 0; j < info->num_items; j++)
1654  {
1655  tmp = (LPWSTR) SendMessageW( control->hwnd, CB_GETITEMDATA, j, 0 );
1656  if (!strcmpW( value, tmp ))
1657  break;
1658  }
1659 
1660  if (j < info->num_items)
1661  {
1662  SendMessageW( control->hwnd, CB_SETCURSEL, j, 0 );
1663  }
1664  else
1665  {
1666  SendMessageW( control->hwnd, CB_SETCURSEL, -1, 0 );
1667  SetWindowTextW( control->hwnd, value );
1668  }
1669 
1670  msi_free(value);
1671 }
1672 
1674 {
1675  struct msi_combobox_info *info;
1676  msi_control *control;
1677  DWORD attributes, style;
1678  LPCWSTR prop;
1679 
1680  info = msi_alloc( sizeof *info );
1681  if (!info)
1682  return ERROR_FUNCTION_FAILED;
1683 
1685  attributes = MSI_RecordGetInteger( rec, 8 );
1686  if ( ~attributes & msidbControlAttributesSorted)
1687  style |= CBS_SORT;
1688  if ( attributes & msidbControlAttributesComboList)
1690  else
1691  style |= CBS_DROPDOWN;
1692 
1693  control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style );
1694  if (!control)
1695  {
1696  msi_free( info );
1697  return ERROR_FUNCTION_FAILED;
1698  }
1699 
1702 
1703  prop = MSI_RecordGetString( rec, 9 );
1704  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
1705 
1706  /* subclass */
1707  info->dialog = dialog;
1708  info->hwnd = control->hwnd;
1709  info->items = NULL;
1710  info->addpos_items = 0;
1711  info->oldproc = (WNDPROC)SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
1713  SetPropW( control->hwnd, szButtonData, info );
1714 
1715  if (control->property)
1716  msi_combobox_add_items( info, control->property );
1717 
1718  msi_dialog_combobox_update( dialog, control );
1719 
1720  return ERROR_SUCCESS;
1721 }
1722 
1724 {
1725  LPWSTR buf;
1726 
1727  if (HIWORD(param) != EN_CHANGE)
1728  return ERROR_SUCCESS;
1729 
1730  TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
1731 
1732  buf = msi_get_window_text( control->hwnd );
1733  msi_dialog_set_property( dialog->package, control->property, buf );
1734  msi_free( buf );
1735 
1736  return ERROR_SUCCESS;
1737 }
1738 
1739 /* length of 2^32 + 1 */
1740 #define MAX_NUM_DIGITS 11
1741 
1743 {
1744  msi_control *control;
1745  LPCWSTR prop, text;
1746  LPWSTR val, begin, end;
1748  DWORD limit;
1749 
1750  control = msi_dialog_add_control( dialog, rec, szEdit,
1752  control->handler = msi_dialog_edit_handler;
1753 
1754  text = MSI_RecordGetString( rec, 10 );
1755  if ( text )
1756  {
1757  begin = strchrW( text, '{' );
1758  end = strchrW( text, '}' );
1759 
1760  if ( begin && end && end > begin &&
1761  begin[0] >= '0' && begin[0] <= '9' &&
1762  end - begin < MAX_NUM_DIGITS)
1763  {
1764  lstrcpynW( num, begin + 1, end - begin );
1765  limit = atolW( num );
1766 
1767  SendMessageW( control->hwnd, EM_SETLIMITTEXT, limit, 0 );
1768  }
1769  }
1770 
1771  prop = MSI_RecordGetString( rec, 9 );
1772  if( prop )
1773  control->property = strdupW( prop );
1774 
1775  val = msi_dup_property( dialog->package->db, control->property );
1776  SetWindowTextW( control->hwnd, val );
1777  msi_free( val );
1778  return ERROR_SUCCESS;
1779 }
1780 
1781 /******************** Masked Edit ********************************************/
1782 
1783 #define MASK_MAX_GROUPS 20
1784 
1786 {
1791 };
1792 
1794 {
1802 };
1803 
1805 {
1806  switch (type)
1807  {
1808  case '%':
1809  case '#':
1810  case '&':
1811  case '`':
1812  case '?':
1813  case '^':
1814  return TRUE;
1815  }
1816  return FALSE;
1817 }
1818 
1820 {
1821  LPWSTR val;
1822  UINT i, n, r;
1823 
1824  val = msi_alloc( (info->num_chars+1)*sizeof(WCHAR) );
1825  for( i=0, n=0; i<info->num_groups; i++ )
1826  {
1827  if (info->group[i].len == ~0u)
1828  {
1829  UINT len = SendMessageW( info->group[i].hwnd, WM_GETTEXTLENGTH, 0, 0 );
1830  val = msi_realloc( val, (len + 1) * sizeof(WCHAR) );
1831  GetWindowTextW( info->group[i].hwnd, val, len + 1 );
1832  }
1833  else
1834  {
1835  if (info->group[i].len + n > info->num_chars)
1836  {
1837  ERR("can't fit control %d text into template\n",i);
1838  break;
1839  }
1840  if (!msi_mask_editable(info->group[i].type))
1841  {
1842  for(r=0; r<info->group[i].len; r++)
1843  val[n+r] = info->group[i].type;
1844  val[n+r] = 0;
1845  }
1846  else
1847  {
1848  r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 );
1849  if( r != info->group[i].len )
1850  break;
1851  }
1852  n += r;
1853  }
1854  }
1855 
1856  TRACE("%d/%d controls were good\n", i, info->num_groups);
1857 
1858  if( i == info->num_groups )
1859  {
1860  TRACE("Set property %s to %s\n", debugstr_w(info->prop), debugstr_w(val));
1861  msi_dialog_set_property( info->dialog->package, info->prop, val );
1863  }
1864  msi_free( val );
1865 }
1866 
1867 /* now move to the next control if necessary */
1869 {
1870  HWND hWndNext;
1871  UINT len, i;
1872 
1873  for( i=0; i<info->num_groups; i++ )
1874  if( info->group[i].hwnd == hWnd )
1875  break;
1876 
1877  /* don't move from the last control */
1878  if( i >= (info->num_groups-1) )
1879  return;
1880 
1881  len = SendMessageW( hWnd, WM_GETTEXTLENGTH, 0, 0 );
1882  if( len < info->group[i].len )
1883  return;
1884 
1885  hWndNext = GetNextDlgTabItem( GetParent( hWnd ), hWnd, FALSE );
1886  SetFocus( hWndNext );
1887 }
1888 
1889 static LRESULT WINAPI
1891 {
1892  struct msi_maskedit_info *info;
1893  HRESULT r;
1894 
1895  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
1896 
1898 
1899  r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
1900 
1901  switch( msg )
1902  {
1903  case WM_COMMAND:
1904  if (HIWORD(wParam) == EN_CHANGE)
1905  {
1908  }
1909  break;
1910  case WM_NCDESTROY:
1911  msi_free( info->prop );
1912  msi_free( info );
1914  break;
1915  }
1916 
1917  return r;
1918 }
1919 
1920 /* fish the various bits of the property out and put them in the control */
1921 static void
1923 {
1924  LPCWSTR p;
1925  UINT i;
1926 
1927  p = text;
1928  for( i = 0; i < info->num_groups; i++ )
1929  {
1930  if( info->group[i].len < strlenW( p ) )
1931  {
1932  LPWSTR chunk = strdupW( p );
1933  chunk[ info->group[i].len ] = 0;
1934  SetWindowTextW( info->group[i].hwnd, chunk );
1935  msi_free( chunk );
1936  }
1937  else
1938  {
1939  SetWindowTextW( info->group[i].hwnd, p );
1940  break;
1941  }
1942  p += info->group[i].len;
1943  }
1944 }
1945 
1947 {
1948  struct msi_maskedit_info *info;
1949  int i = 0, n = 0, total = 0;
1950  LPCWSTR p;
1951 
1952  TRACE("masked control, template %s\n", debugstr_w(mask));
1953 
1954  if( !mask )
1955  return NULL;
1956 
1957  info = msi_alloc_zero( sizeof *info );
1958  if( !info )
1959  return info;
1960 
1961  p = strchrW(mask, '<');
1962  if( p )
1963  p++;
1964  else
1965  p = mask;
1966 
1967  for( i=0; i<MASK_MAX_GROUPS; i++ )
1968  {
1969  /* stop at the end of the string */
1970  if( p[0] == 0 || p[0] == '>' )
1971  {
1972  if (!total)
1973  {
1974  /* create a group for the empty mask */
1975  info->group[0].type = '&';
1976  info->group[0].len = ~0u;
1977  i = 1;
1978  }
1979  break;
1980  }
1981 
1982  /* count the number of the same identifier */
1983  for( n=0; p[n] == p[0]; n++ )
1984  ;
1985  info->group[i].ofs = total;
1986  info->group[i].type = p[0];
1987  if( p[n] == '=' )
1988  {
1989  n++;
1990  total++; /* an extra not part of the group */
1991  }
1992  info->group[i].len = n;
1993  total += n;
1994  p += n;
1995  }
1996 
1997  TRACE("%d characters in %d groups\n", total, i );
1998  if( i == MASK_MAX_GROUPS )
1999  ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask));
2000 
2001  info->num_chars = total;
2002  info->num_groups = i;
2003 
2004  return info;
2005 }
2006 
2007 static void
2009 {
2010  DWORD width, height, style, wx, ww;
2011  RECT rect;
2012  HWND hwnd;
2013  UINT i;
2014 
2016 
2017  GetClientRect( info->hwnd, &rect );
2018 
2019  width = rect.right - rect.left;
2020  height = rect.bottom - rect.top;
2021 
2022  for( i = 0; i < info->num_groups; i++ )
2023  {
2024  if (!msi_mask_editable( info->group[i].type ))
2025  continue;
2026  if (info->num_chars)
2027  {
2028  wx = (info->group[i].ofs * width) / info->num_chars;
2029  ww = (info->group[i].len * width) / info->num_chars;
2030  }
2031  else
2032  {
2033  wx = 0;
2034  ww = width;
2035  }
2036  hwnd = CreateWindowW( szEdit, NULL, style, wx, 0, ww, height,
2037  info->hwnd, NULL, NULL, NULL );
2038  if( !hwnd )
2039  {
2040  ERR("failed to create mask edit sub window\n");
2041  break;
2042  }
2043 
2044  SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 );
2045 
2046  msi_dialog_set_font( info->dialog, hwnd,
2047  font?font:info->dialog->default_font );
2048  info->group[i].hwnd = hwnd;
2049  }
2050 }
2051 
2052 /*
2053  * office 2003 uses "73931<````=````=````=````=`````>@@@@@"
2054  * delphi 7 uses "<????-??????-??????-????>" and "<???-???>"
2055  * filemaker pro 7 uses "<^^^^=^^^^=^^^^=^^^^=^^^^=^^^^=^^^^^>"
2056  */
2058 {
2059  LPWSTR font_mask, val = NULL, font;
2060  struct msi_maskedit_info *info = NULL;
2062  msi_control *control;
2063  LPCWSTR prop, mask;
2064 
2065  TRACE("\n");
2066 
2067  font_mask = msi_get_deformatted_field( dialog->package, rec, 10 );
2068  font = msi_dialog_get_style( font_mask, &mask );
2069  if( !mask )
2070  {
2071  WARN("mask template is empty\n");
2072  goto end;
2073  }
2074 
2076  if( !info )
2077  {
2078  ERR("template %s is invalid\n", debugstr_w(mask));
2079  goto end;
2080  }
2081 
2082  info->dialog = dialog;
2083 
2084  control = msi_dialog_add_control( dialog, rec, szStatic,
2086  if( !control )
2087  {
2088  ERR("Failed to create maskedit container\n");
2090  goto end;
2091  }
2093 
2094  info->hwnd = control->hwnd;
2095 
2096  /* subclass the static control */
2097  info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC,
2099  SetPropW( control->hwnd, szButtonData, info );
2100 
2101  prop = MSI_RecordGetString( rec, 9 );
2102  if( prop )
2103  info->prop = strdupW( prop );
2104 
2106 
2107  if( prop )
2108  {
2109  val = msi_dup_property( dialog->package->db, prop );
2110  if( val )
2111  {
2113  msi_free( val );
2114  }
2115  }
2116 
2117 end:
2118  if( ret != ERROR_SUCCESS )
2119  msi_free( info );
2120  msi_free( font_mask );
2121  msi_free( font );
2122  return ret;
2123 }
2124 
2125 /******************** Progress Bar *****************************************/
2126 
2128 {
2129  msi_control *control;
2130  DWORD attributes, style;
2131 
2132  style = WS_VISIBLE;
2133  attributes = MSI_RecordGetInteger( rec, 8 );
2134  if( !(attributes & msidbControlAttributesProgress95) )
2135  style |= PBS_SMOOTH;
2136 
2137  control = msi_dialog_add_control( dialog, rec, PROGRESS_CLASSW, style );
2138  if( !control )
2139  return ERROR_FUNCTION_FAILED;
2140 
2142  return ERROR_SUCCESS;
2143 }
2144 
2145 /******************** Path Edit ********************************************/
2146 
2148 {
2152 };
2153 
2155 {
2156  LPWSTR prop, path;
2157  BOOL indirect;
2158 
2159  if (!control && !(control = msi_dialog_find_control_by_type( dialog, szPathEdit )))
2160  return;
2161 
2163  prop = msi_dialog_dup_property( dialog, control->property, indirect );
2165 
2166  SetWindowTextW( control->hwnd, path );
2167  SendMessageW( control->hwnd, EM_SETSEL, 0, -1 );
2168 
2169  msi_free( path );
2170  msi_free( prop );
2171 }
2172 
2173 /* FIXME: test when this should fail */
2175 {
2176  if ( !path[0] )
2177  return FALSE;
2178 
2179  if ( PathIsRelativeW( path ) )
2180  return FALSE;
2181 
2182  return TRUE;
2183 }
2184 
2185 /* returns TRUE if the path is valid, FALSE otherwise */
2187 {
2188  LPWSTR buf, prop;
2189  BOOL indirect;
2190  BOOL valid;
2191 
2193  prop = msi_dialog_dup_property( dialog, control->property, indirect );
2194 
2195  buf = msi_get_window_text( control->hwnd );
2196 
2197  if ( !msi_dialog_verify_path( buf ) )
2198  {
2199  /* FIXME: display an error message box */
2200  ERR("Invalid path %s\n", debugstr_w( buf ));
2201  valid = FALSE;
2202  SetFocus( control->hwnd );
2203  }
2204  else
2205  {
2206  valid = TRUE;
2207  msi_dialog_set_property( dialog->package, prop, buf );
2208  }
2209 
2210  msi_dialog_update_pathedit( dialog, control );
2211 
2212  TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name),
2213  debugstr_w(prop));
2214 
2215  msi_free( buf );
2216  msi_free( prop );
2217 
2218  return valid;
2219 }
2220 
2222 {
2224  LRESULT r = 0;
2225 
2226  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
2227 
2228  if ( msg == WM_KILLFOCUS )
2229  {
2230  /* if the path is invalid, don't handle this message */
2231  if ( !msi_dialog_onkillfocus( info->dialog, info->control ) )
2232  return 0;
2233  }
2234 
2235  r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
2236 
2237  if ( msg == WM_NCDESTROY )
2238  {
2239  msi_free( info );
2241  }
2242 
2243  return r;
2244 }
2245 
2247 {
2248  struct msi_pathedit_info *info;
2250  LPCWSTR prop;
2251 
2252  info = msi_alloc( sizeof *info );
2253  if (!info)
2254  return ERROR_FUNCTION_FAILED;
2255 
2257  WS_BORDER | WS_TABSTOP );
2259  prop = MSI_RecordGetString( rec, 9 );
2262 
2263  info->dialog = dialog;
2264  info->control = control;
2268 
2270 
2271  return ERROR_SUCCESS;
2272 }
2273 
2275 {
2276  if (HIWORD(param) != BN_CLICKED)
2277  return ERROR_SUCCESS;
2278 
2279  TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
2280 
2282 
2284 }
2285 
2286 /* radio buttons are a bit different from normal controls */
2288 {
2290  msi_dialog *dialog = group->dialog;
2292  LPCWSTR prop, text, name;
2294 
2295  name = MSI_RecordGetString( rec, 3 );
2296  text = MSI_RecordGetString( rec, 8 );
2297 
2299  group->parent->hwnd );
2300  if (!control)
2301  return ERROR_FUNCTION_FAILED;
2303 
2304  if (group->propval && !strcmpW( control->name, group->propval ))
2306 
2307  prop = MSI_RecordGetString( rec, 1 );
2308  if( prop )
2309  control->property = strdupW( prop );
2310 
2311  return ERROR_SUCCESS;
2312 }
2313 
2315 {
2316  EnableWindow( hWnd, lParam );
2317  return TRUE;
2318 }
2319 
2321 {
2323  LRESULT r;
2324 
2325  TRACE("hWnd %p msg %04x wParam 0x%08lx lParam 0x%08lx\n", hWnd, msg, wParam, lParam);
2326 
2327  if (msg == WM_COMMAND) /* Forward notifications to dialog */
2329 
2331 
2332  /* make sure the radio buttons show as disabled if the parent is disabled */
2333  if (msg == WM_ENABLE)
2335 
2336  return r;
2337 }
2338 
2340 {
2341  static const WCHAR query[] = {
2342  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2343  'R','a','d','i','o','B','u','t','t','o','n',' ','W','H','E','R','E',' ',
2344  '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
2345  UINT r;
2346  LPCWSTR prop;
2348  MSIQUERY *view;
2350  MSIPACKAGE *package = dialog->package;
2351  WNDPROC oldproc;
2352  DWORD attr, style = WS_GROUP;
2353 
2354  prop = MSI_RecordGetString( rec, 9 );
2355 
2356  TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop ));
2357 
2358  attr = MSI_RecordGetInteger( rec, 8 );
2360  style |= WS_VISIBLE;
2362  style |= WS_DISABLED;
2364  style |= BS_GROUPBOX;
2365  else
2366  style |= BS_OWNERDRAW;
2367 
2368  /* Create parent group box to hold radio buttons */
2369  control = msi_dialog_add_control( dialog, rec, szButton, style );
2370  if( !control )
2371  return ERROR_FUNCTION_FAILED;
2372 
2373  oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
2375  SetPropW(control->hwnd, szButtonData, oldproc);
2377 
2378  if( prop )
2379  control->property = strdupW( prop );
2380 
2381  /* query the Radio Button table for all control in this group */
2382  r = MSI_OpenQuery( package->db, &view, query, prop );
2383  if( r != ERROR_SUCCESS )
2384  {
2385  ERR("query failed for dialog %s radio group %s\n",
2386  debugstr_w(dialog->name), debugstr_w(prop));
2387  return ERROR_INVALID_PARAMETER;
2388  }
2389 
2390  group.dialog = dialog;
2391  group.parent = control;
2392  group.propval = msi_dup_property( dialog->package->db, control->property );
2393 
2395  msiobj_release( &view->hdr );
2396  msi_free( group.propval );
2397  return r;
2398 }
2399 
2400 static void
2402 {
2403  TVITEMW tvi;
2404  DWORD index = feature->ActionRequest;
2405 
2406  TRACE("Feature %s -> %d %d %d\n", debugstr_w(feature->Title),
2407  feature->Installed, feature->Action, feature->ActionRequest);
2408 
2409  if (index == INSTALLSTATE_UNKNOWN)
2411 
2412  tvi.mask = TVIF_STATE;
2413  tvi.hItem = hItem;
2416 
2417  SendMessageW( hwnd, TVM_SETITEMW, 0, (LPARAM) &tvi );
2418 }
2419 
2420 static UINT
2422 {
2423  HMENU hMenu;
2424  INT r;
2425 
2426  /* create a menu to display */
2427  hMenu = CreatePopupMenu();
2428 
2429  /* FIXME: load strings from resources */
2430  AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_LOCAL, "Install feature locally");
2431  AppendMenuA( hMenu, MF_ENABLED, USER_INSTALLSTATE_ALL, "Install entire feature");
2432  AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ADVERTISED, "Install on demand");
2433  AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ABSENT, "Don't install");
2435  x, y, 0, hwnd, NULL );
2436  DestroyMenu( hMenu );
2437  return r;
2438 }
2439 
2440 static void
2443 {
2444  feature->ActionRequest = state;
2447 }
2448 
2449 static void
2451  MSIPACKAGE *package, INSTALLSTATE state)
2452 {
2453  /* update all siblings */
2454  do
2455  {
2457  HTREEITEM child;
2458 
2461 
2462  /* update this sibling's children */
2464  if (child)
2466  package, state );
2467  }
2468  while ((curr = (HTREEITEM)SendMessageW( hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)curr )));
2469 }
2470 
2471 static LRESULT
2473 {
2474  struct msi_selection_tree_info *info;
2476  MSIPACKAGE *package;
2477  union {
2478  RECT rc;
2479  POINT pt[2];
2480  HTREEITEM hItem;
2481  } u;
2482  UINT r;
2483 
2485  package = info->dialog->package;
2486 
2488  if (!feature)
2489  {
2490  ERR("item %p feature was NULL\n", hItem);
2491  return 0;
2492  }
2493 
2494  /* get the item's rectangle to put the menu just below it */
2495  u.hItem = hItem;
2496  SendMessageW( hwnd, TVM_GETITEMRECT, 0, (LPARAM) &u.rc );
2497  MapWindowPoints( hwnd, NULL, u.pt, 2 );
2498 
2499  r = msi_seltree_popup_menu( hwnd, u.rc.left, u.rc.top );
2500 
2501  switch (r)
2502  {
2503  case USER_INSTALLSTATE_ALL:
2505  /* fall-through */
2507  case INSTALLSTATE_ABSENT:
2508  {
2509  HTREEITEM child;
2511  if (child)
2513  }
2514  /* fall-through */
2515  case INSTALLSTATE_LOCAL:
2517  break;
2518  }
2519 
2520  return 0;
2521 }
2522 
2523 static LRESULT WINAPI
2525 {
2526  struct msi_selection_tree_info *info;
2527  TVHITTESTINFO tvhti;
2528  HRESULT r;
2529 
2530  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
2531 
2533 
2534  switch( msg )
2535  {
2536  case WM_LBUTTONDOWN:
2537  tvhti.pt.x = (short)LOWORD( lParam );
2538  tvhti.pt.y = (short)HIWORD( lParam );
2539  tvhti.flags = 0;
2540  tvhti.hItem = 0;
2541  CallWindowProcW(info->oldproc, hWnd, TVM_HITTEST, 0, (LPARAM) &tvhti );
2542  if (tvhti.flags & TVHT_ONITEMSTATEICON)
2543  return msi_seltree_menu( hWnd, tvhti.hItem );
2544  break;
2545  }
2546  r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
2547 
2548  switch( msg )
2549  {
2550  case WM_NCDESTROY:
2551  msi_free( info );
2553  break;
2554  }
2555  return r;
2556 }
2557 
2558 static void
2561 {
2564  TVINSERTSTRUCTW tvis;
2565  HTREEITEM hitem, hfirst = NULL;
2566 
2568  {
2569  if ( parent && feature->Feature_Parent && strcmpW( parent, feature->Feature_Parent ))
2570  continue;
2571  else if ( parent && !feature->Feature_Parent )
2572  continue;
2573  else if ( !parent && feature->Feature_Parent )
2574  continue;
2575 
2576  if ( !feature->Title )
2577  continue;
2578 
2579  if ( !feature->Display )
2580  continue;
2581 
2582  memset( &tvis, 0, sizeof tvis );
2583  tvis.hParent = hParent;
2584  tvis.hInsertAfter = TVI_LAST;
2585  tvis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
2586  tvis.u.item.pszText = feature->Title;
2587  tvis.u.item.lParam = (LPARAM) feature;
2588 
2589  hitem = (HTREEITEM) SendMessageW( hwnd, TVM_INSERTITEMW, 0, (LPARAM) &tvis );
2590  if (!hitem)
2591  continue;
2592 
2593  if (!hfirst)
2594  hfirst = hitem;
2595 
2598  feature->Feature, hitem );
2599 
2600  /* the node is expanded if Display is odd */
2601  if ( feature->Display % 2 != 0 )
2603  }
2604 
2605  /* select the first item */
2607  info->selected = hfirst;
2608 }
2609 
2611 {
2612  const int bm_width = 32, bm_height = 16, bm_count = 3;
2613  const int bm_resource = 0x1001;
2614  HIMAGELIST himl;
2615  int i;
2616  HBITMAP hbmp;
2617 
2618  himl = ImageList_Create( bm_width, bm_height, FALSE, 4, 0 );
2619  if (!himl)
2620  {
2621  ERR("failed to create image list\n");
2622  return;
2623  }
2624 
2625  for (i=0; i<bm_count; i++)
2626  {
2627  hbmp = LoadBitmapW( msi_hInstance, MAKEINTRESOURCEW(i+bm_resource) );
2628  if (!hbmp)
2629  {
2630  ERR("failed to load bitmap %d\n", i);
2631  break;
2632  }
2633 
2634  /*
2635  * Add a dummy bitmap at offset zero because the treeview
2636  * can't use it as a state mask (zero means no user state).
2637  */
2638  if (!i)
2639  ImageList_Add( himl, hbmp, NULL );
2640 
2641  ImageList_Add( himl, hbmp, NULL );
2642  }
2643 
2645 }
2646 
2648  msi_control *control, WPARAM param )
2649 {
2650  struct msi_selection_tree_info *info = GetPropW( control->hwnd, szButtonData );
2652  MSIRECORD *row, *rec;
2653  MSIFOLDER *folder;
2655  LPCWSTR dir, title = NULL;
2656  UINT r = ERROR_SUCCESS;
2657 
2658  static const WCHAR select[] = {
2659  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2660  '`','F','e','a','t','u','r','e','`',' ','W','H','E','R','E',' ',
2661  '`','T','i','t','l','e','`',' ','=',' ','\'','%','s','\'',0
2662  };
2663 
2664  if (tv->hdr.code != TVN_SELCHANGINGW)
2665  return ERROR_SUCCESS;
2666 
2667  info->selected = tv->itemNew.hItem;
2668 
2669  if (!(tv->itemNew.mask & TVIF_TEXT))
2670  {
2672  if (feature)
2673  title = feature->Title;
2674  }
2675  else
2676  title = tv->itemNew.pszText;
2677 
2678  row = MSI_QueryGetRecord( dialog->package->db, select, title );
2679  if (!row)
2680  return ERROR_FUNCTION_FAILED;
2681 
2682  rec = MSI_CreateRecord( 1 );
2683 
2684  MSI_RecordSetStringW( rec, 1, MSI_RecordGetString( row, 4 ) );
2685  msi_event_fire( dialog->package, szSelectionDescription, rec );
2686 
2687  dir = MSI_RecordGetString( row, 7 );
2688  if (dir)
2689  {
2690  folder = msi_get_loaded_folder( dialog->package, dir );
2691  if (!folder)
2692  {
2694  goto done;
2695  }
2696  MSI_RecordSetStringW( rec, 1, folder->ResolvedTarget );
2697  }
2698  else
2699  MSI_RecordSetStringW( rec, 1, NULL );
2700 
2701  msi_event_fire( dialog->package, szSelectionPath, rec );
2702 
2703 done:
2704  msiobj_release(&row->hdr);
2705  msiobj_release(&rec->hdr);
2706 
2707  return r;
2708 }
2709 
2711 {
2712  msi_control *control;
2713  LPCWSTR prop, control_name;
2714  MSIPACKAGE *package = dialog->package;
2715  DWORD style;
2716  struct msi_selection_tree_info *info;
2717 
2718  info = msi_alloc( sizeof *info );
2719  if (!info)
2720  return ERROR_FUNCTION_FAILED;
2721 
2722  /* create the treeview control */
2725  control = msi_dialog_add_control( dialog, rec, WC_TREEVIEWW, style );
2726  if (!control)
2727  {
2728  msi_free(info);
2729  return ERROR_FUNCTION_FAILED;
2730  }
2731 
2733  control_name = MSI_RecordGetString( rec, 2 );
2734  control->attributes = MSI_RecordGetInteger( rec, 8 );
2735  prop = MSI_RecordGetString( rec, 9 );
2736  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
2737 
2738  /* subclass */
2739  info->dialog = dialog;
2740  info->hwnd = control->hwnd;
2741  info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
2743  SetPropW( control->hwnd, szButtonData, info );
2744 
2745  event_subscribe( dialog, szSelectionPath, control_name, szProperty );
2746 
2747  /* initialize it */
2748  msi_seltree_create_imagelist( control->hwnd );
2749  msi_seltree_add_child_features( package, control->hwnd, NULL, NULL );
2750 
2751  return ERROR_SUCCESS;
2752 }
2753 
2754 /******************** Group Box ***************************************/
2755 
2757 {
2758  msi_control *control;
2759  DWORD style;
2760 
2762  control = msi_dialog_add_control( dialog, rec, WC_BUTTONW, style );
2763  if (!control)
2764  return ERROR_FUNCTION_FAILED;
2765 
2766  return ERROR_SUCCESS;
2767 }
2768 
2769 /******************** List Box ***************************************/
2770 
2772 {
2779 };
2780 
2782 {
2783  struct msi_listbox_info *info;
2784  LRESULT r;
2785  DWORD j;
2786 
2787  TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
2788 
2790  if (!info)
2791  return 0;
2792 
2793  r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
2794 
2795  switch( msg )
2796  {
2797  case WM_NCDESTROY:
2798  for (j = 0; j < info->num_items; j++)
2799  msi_free( info->items[j] );
2800  msi_free( info->items );
2801  msi_free( info );
2803  break;
2804  }
2805 
2806  return r;
2807 }
2808 
2810 {
2811  struct msi_listbox_info *info = param;
2812  LPCWSTR value, text;
2813  int pos;
2814 
2815  value = MSI_RecordGetString( rec, 3 );
2816  text = MSI_RecordGetString( rec, 4 );
2817 
2818  info->items[info->addpos_items] = strdupW( value );
2819 
2820  pos = SendMessageW( info->hwnd, LB_ADDSTRING, 0, (LPARAM)text );
2821  SendMessageW( info->hwnd, LB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] );
2822  info->addpos_items++;
2823  return ERROR_SUCCESS;
2824 }
2825 
2827 {
2828  static const WCHAR query[] = {
2829  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2830  '`','L','i','s','t','B','o','x','`',' ','W','H','E','R','E',' ',
2831  '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',' ',
2832  'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','`',0};
2833  MSIQUERY *view;
2834  DWORD count;
2835  UINT r;
2836 
2837  r = MSI_OpenQuery( info->dialog->package->db, &view, query, property );
2838  if ( r != ERROR_SUCCESS )
2839  return r;
2840 
2841  /* just get the number of records */
2842  count = 0;
2844  if (r != ERROR_SUCCESS)
2845  {
2846  msiobj_release( &view->hdr );
2847  return r;
2848  }
2849  info->num_items = count;
2850  info->items = msi_alloc( sizeof(*info->items) * count );
2851 
2853  msiobj_release( &view->hdr );
2854  return r;
2855 }
2856 
2858  msi_control *control, WPARAM param )
2859 {
2860  struct msi_listbox_info *info;
2861  int index;
2862  LPCWSTR value;
2863 
2864  if( HIWORD(param) != LBN_SELCHANGE )
2865  return ERROR_SUCCESS;
2866 
2867  info = GetPropW( control->hwnd, szButtonData );
2868  index = SendMessageW( control->hwnd, LB_GETCURSEL, 0, 0 );
2869  value = (LPCWSTR) SendMessageW( control->hwnd, LB_GETITEMDATA, index, 0 );
2870 
2871  msi_dialog_set_property( info->dialog->package, control->property, value );
2873 
2874  return ERROR_SUCCESS;
2875 }
2876 
2878 {
2879  struct msi_listbox_info *info;
2880  msi_control *control;
2881  DWORD attributes, style;
2882  LPCWSTR prop;
2883 
2884  info = msi_alloc( sizeof *info );
2885  if (!info)
2886  return ERROR_FUNCTION_FAILED;
2887 
2889  attributes = MSI_RecordGetInteger( rec, 8 );
2890  if (~attributes & msidbControlAttributesSorted)
2891  style |= LBS_SORT;
2892 
2893  control = msi_dialog_add_control( dialog, rec, WC_LISTBOXW, style );
2894  if (!control)
2895  {
2896  msi_free(info);
2897  return ERROR_FUNCTION_FAILED;
2898  }
2899 
2901 
2902  prop = MSI_RecordGetString( rec, 9 );
2903  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
2904 
2905  /* subclass */
2906  info->dialog = dialog;
2907  info->hwnd = control->hwnd;
2908  info->items = NULL;
2909  info->addpos_items = 0;
2910  info->oldproc = (WNDPROC)SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
2912  SetPropW( control->hwnd, szButtonData, info );
2913 
2914  if ( control->property )
2915  msi_listbox_add_items( info, control->property );
2916 
2917  return ERROR_SUCCESS;
2918 }
2919 
2920 /******************** Directory Combo ***************************************/
2921 
2923 {
2924  LPWSTR prop, path;
2925  BOOL indirect;
2926 
2927  if (!control && !(control = msi_dialog_find_control_by_type( dialog, szDirectoryCombo )))
2928  return;
2929 
2931  prop = msi_dialog_dup_property( dialog, control->property, indirect );
2933 
2934  PathStripPathW( path );
2936 
2937  SendMessageW( control->hwnd, CB_INSERTSTRING, 0, (LPARAM)path );
2938  SendMessageW( control->hwnd, CB_SETCURSEL, 0, 0 );
2939 
2940  msi_free( path );
2941  msi_free( prop );
2942 }
2943 
2945 {
2946  msi_control *control;
2947  LPCWSTR prop;
2948  DWORD style;
2949 
2950  /* FIXME: use CBS_OWNERDRAWFIXED and add owner draw code */
2953  control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style );
2954  if (!control)
2955  return ERROR_FUNCTION_FAILED;
2956 
2957  control->attributes = MSI_RecordGetInteger( rec, 8 );
2958  prop = MSI_RecordGetString( rec, 9 );
2959  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
2960 
2962 
2963  return ERROR_SUCCESS;
2964 }
2965 
2966 /******************** Directory List ***************************************/
2967 
2969 {
2970  WCHAR dir_spec[MAX_PATH];
2971  WIN32_FIND_DATAW wfd;
2972  LPWSTR prop, path;
2973  BOOL indirect;
2974  LVITEMW item;
2975  HANDLE file;
2976 
2977  static const WCHAR asterisk[] = {'*',0};
2978 
2979  if (!control && !(control = msi_dialog_find_control_by_type( dialog, szDirectoryList )))
2980  return;
2981 
2982  /* clear the list-view */
2983  SendMessageW( control->hwnd, LVM_DELETEALLITEMS, 0, 0 );
2984 
2986  prop = msi_dialog_dup_property( dialog, control->property, indirect );
2988 
2989  lstrcpyW( dir_spec, path );
2990  lstrcatW( dir_spec, asterisk );
2991 
2992  file = FindFirstFileW( dir_spec, &wfd );
2993  if ( file == INVALID_HANDLE_VALUE )
2994  return;
2995 
2996  do
2997  {
2998  if ( wfd.dwFileAttributes != FILE_ATTRIBUTE_DIRECTORY )
2999  continue;
3000 
3001  if ( !strcmpW( wfd.cFileName, szDot ) || !strcmpW( wfd.cFileName, szDotDot ) )
3002  continue;
3003 
3004  item.mask = LVIF_TEXT;
3005  item.cchTextMax = MAX_PATH;
3006  item.iItem = 0;
3007  item.iSubItem = 0;
3008  item.pszText = wfd.cFileName;
3009 
3010  SendMessageW( control->hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item );
3011  } while ( FindNextFileW( file, &wfd ) );
3012 
3013  msi_free( prop );
3014  msi_free( path );
3015  FindClose( file );
3016 }
3017 
3019 {
3020  msi_control *control;
3021  LPWSTR prop, path, ptr;
3022  BOOL indirect;
3023 
3026  prop = msi_dialog_dup_property( dialog, control->property, indirect );
3028 
3029  /* strip off the last directory */
3030  ptr = PathFindFileNameW( path );
3031  if (ptr != path) *(ptr - 1) = '\0';
3033 
3034  msi_dialog_set_property( dialog->package, prop, path );
3035 
3039 
3040  msi_free( path );
3041  msi_free( prop );
3042 
3043  return ERROR_SUCCESS;
3044 }
3045 
3047  msi_control *control, WPARAM param )
3048 {
3049  LPNMHDR nmhdr = (LPNMHDR)param;
3050  WCHAR new_path[MAX_PATH];
3051  WCHAR text[MAX_PATH];
3052  LPWSTR path, prop;
3053  BOOL indirect;
3054  LVITEMW item;
3055  int index;
3056 
3057  if (nmhdr->code != LVN_ITEMACTIVATE)
3058  return ERROR_SUCCESS;
3059 
3060  index = SendMessageW( control->hwnd, LVM_GETNEXTITEM, -1, LVNI_SELECTED );
3061  if ( index < 0 )
3062  {
3063  ERR("No list-view item selected!\n");
3064  return ERROR_FUNCTION_FAILED;
3065  }
3066 
3067  item.iSubItem = 0;
3068  item.pszText = text;
3069  item.cchTextMax = MAX_PATH;
3071 
3073  prop = msi_dialog_dup_property( dialog, control->property, indirect );
3075 
3076  lstrcpyW( new_path, path );
3077  lstrcatW( new_path, text );
3078  lstrcatW( new_path, szBackSlash );
3079 
3080  msi_dialog_set_property( dialog->package, prop, new_path );
3081 
3085 
3086  msi_free( prop );
3087  msi_free( path );
3088  return ERROR_SUCCESS;
3089 }
3090 
3092 {
3093  msi_control *control;
3094  LPCWSTR prop;
3095  DWORD style;
3096 
3100  control = msi_dialog_add_control( dialog, rec, WC_LISTVIEWW, style );
3101  if (!control)
3102  return ERROR_FUNCTION_FAILED;
3103 
3104  control->attributes = MSI_RecordGetInteger( rec, 8 );
3106  prop = MSI_RecordGetString( rec, 9 );
3107  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
3108 
3109  /* double click to activate an item in the list */
3112 
3114 
3115  return ERROR_SUCCESS;
3116 }
3117 
3118 /******************** VolumeCost List ***************************************/
3119 
3121 {
3122  int i;
3123 
3124  for (i = 0; i < lstrlenW( str ); i++)
3125  if (!isdigitW(str[i]))
3126  return FALSE;
3127 
3128  return TRUE;
3129 }
3130 
3131 static const WCHAR column_keys[][80] =
3132 {
3133  {'V','o','l','u','m','e','C','o','s','t','V','o','l','u','m','e',0},
3134  {'V','o','l','u','m','e','C','o','s','t','S','i','z','e',0},
3135  {'V','o','l','u','m','e','C','o','s','t','A','v','a','i','l','a','b','l','e',0},
3136  {'V','o','l','u','m','e','C','o','s','t','R','e','q','u','i','r','e','d',0},
3137  {'V','o','l','u','m','e','C','o','s','t','D','i','f','f','e','r','e','n','c','e',0}
3138 };
3139 
3141 {
3142  LPCWSTR text = MSI_RecordGetString( rec, 10 );
3143  LPCWSTR begin = text, end;
3144  WCHAR *num;
3145  LVCOLUMNW lvc;
3146  DWORD count = 0;
3147 
3148  static const WCHAR negative[] = {'-',0};
3149 
3150  if (!text) return;
3151 
3152  while ((begin = strchrW( begin, '{' )) && count < 5)
3153  {
3154  if (!(end = strchrW( begin, '}' )))
3155  return;
3156 
3157  num = msi_alloc( (end-begin+1)*sizeof(WCHAR) );
3158  if (!num)
3159  return;
3160 
3161  lstrcpynW( num, begin + 1, end - begin );
3162  begin += end - begin + 1;
3163 
3164  /* empty braces or '0' hides the column */
3165  if ( !num[0] || !strcmpW( num, szZero ) )
3166  {
3167  count++;
3168  msi_free( num );
3169  continue;
3170  }
3171 
3172  /* the width must be a positive number
3173  * if a width is invalid, all remaining columns are hidden
3174  */
3175  if ( !strncmpW( num, negative, 1 ) || !str_is_number( num ) ) {
3176  msi_free( num );
3177  return;
3178  }
3179 
3180  ZeroMemory( &lvc, sizeof(lvc) );
3182  lvc.cx = atolW( num );
3184 
3185  SendMessageW( control->hwnd, LVM_INSERTCOLUMNW, count++, (LPARAM)&lvc );
3186  msi_free( lvc.pszText );
3187  msi_free( num );
3188  }
3189 }
3190 
3192 {
3194  INT each_cost;
3195  LONGLONG total_cost = 0;
3196 
3197  LIST_FOR_EACH_ENTRY( feature, &dialog->package->features, MSIFEATURE, entry )
3198  {
3199  if (ERROR_SUCCESS == (MSI_GetFeatureCost(dialog->package, feature,
3201  {
3202  /* each_cost is in 512-byte units */
3203  total_cost += ((LONGLONG)each_cost) * 512;
3204  }
3205  if (ERROR_SUCCESS == (MSI_GetFeatureCost(dialog->package, feature,
3207  {
3208  /* each_cost is in 512-byte units */
3209  total_cost -= ((LONGLONG)each_cost) * 512;
3210  }
3211  }
3212  return total_cost;
3213 }
3214 
3216 {
3217  ULARGE_INTEGER total, free;
3218  LONGLONG difference, cost;
3219  WCHAR size_text[MAX_PATH];
3220  WCHAR cost_text[MAX_PATH];
3221  LPWSTR drives, ptr;
3222  LVITEMW lvitem;
3223  DWORD size;
3224  int i = 0;
3225 
3226  cost = msi_vcl_get_cost(dialog);
3227  StrFormatByteSizeW(cost, cost_text, MAX_PATH);
3228 
3230  if ( !size ) return;
3231 
3232  drives = msi_alloc( (size + 1) * sizeof(WCHAR) );
3233  if ( !drives ) return;
3234 
3235  GetLogicalDriveStringsW( size, drives );
3236 
3237  ptr = drives;
3238  while (*ptr)
3239  {
3240  lvitem.mask = LVIF_TEXT;
3241  lvitem.iItem = i;
3242  lvitem.iSubItem = 0;
3243  lvitem.pszText = ptr;
3244  lvitem.cchTextMax = lstrlenW(ptr) + 1;
3245  SendMessageW( control->hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvitem );
3246 
3247  GetDiskFreeSpaceExW(ptr, &free, &total, NULL);
3248  difference = free.QuadPart - cost;
3249 
3250  StrFormatByteSizeW(total.QuadPart, size_text, MAX_PATH);
3251  lvitem.iSubItem = 1;
3252  lvitem.pszText = size_text;
3253  lvitem.cchTextMax = lstrlenW(size_text) + 1;
3254  SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3255 
3256  StrFormatByteSizeW(free.QuadPart, size_text, MAX_PATH);
3257  lvitem.iSubItem = 2;
3258  lvitem.pszText = size_text;
3259  lvitem.cchTextMax = lstrlenW(size_text) + 1;
3260  SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3261 
3262  lvitem.iSubItem = 3;
3263  lvitem.pszText = cost_text;
3264  lvitem.cchTextMax = lstrlenW(cost_text) + 1;
3265  SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3266 
3267  StrFormatByteSizeW(difference, size_text, MAX_PATH);
3268  lvitem.iSubItem = 4;
3269  lvitem.pszText = size_text;
3270  lvitem.cchTextMax = lstrlenW(size_text) + 1;
3271  SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3272 
3273  ptr += lstrlenW(ptr) + 1;
3274  i++;
3275  }
3276 
3277  msi_free( drives );
3278 }
3279 
3281 {
3282  msi_control *control;
3283  DWORD style;
3284 
3288  control = msi_dialog_add_control( dialog, rec, WC_LISTVIEWW, style );
3289  if (!control)
3290  return ERROR_FUNCTION_FAILED;
3291 
3292  msi_dialog_vcl_add_columns( dialog, control, rec );
3293  msi_dialog_vcl_add_drives( dialog, control );
3294 
3295  return ERROR_SUCCESS;
3296 }
3297 
3298 /******************** VolumeSelect Combo ***************************************/
3299 
3301  msi_control *control, WPARAM param )
3302 {
3303  WCHAR text[MAX_PATH];
3304  LPWSTR prop;
3305  BOOL indirect;
3306  int index;
3307 
3308  if (HIWORD(param) != CBN_SELCHANGE)
3309  return ERROR_SUCCESS;
3310 
3311  index = SendMessageW( control->hwnd, CB_GETCURSEL, 0, 0 );
3312  if ( index == CB_ERR )
3313  {
3314  ERR("No ComboBox item selected!\n");
3315  return ERROR_FUNCTION_FAILED;
3316  }
3317 
3318  SendMessageW( control->hwnd, CB_GETLBTEXT, index, (LPARAM)text );
3319 
3321  prop = msi_dialog_dup_property( dialog, control->property, indirect );
3322 
3323  msi_dialog_set_property( dialog->package, prop, text );
3324 
3325  msi_free( prop );
3326  return ERROR_SUCCESS;
3327 }
3328 
3330 {
3331  LPWSTR drives, ptr;
3332  DWORD size;
3333 
3335  if ( !size ) return;
3336 
3337  drives = msi_alloc( (size + 1) * sizeof(WCHAR) );
3338  if ( !drives ) return;
3339 
3340  GetLogicalDriveStringsW( size, drives );
3341 
3342  ptr = drives;
3343  while (*ptr)
3344  {
3345  SendMessageW( control->hwnd, CB_ADDSTRING, 0, (LPARAM)ptr );
3346  ptr += lstrlenW(ptr) + 1;
3347  }
3348 
3349  msi_free( drives );
3350 }
3351 
3353 {
3354  msi_control *control;
3355  LPCWSTR prop;
3356  DWORD style;
3357 
3358  /* FIXME: CBS_OWNERDRAWFIXED */
3362  control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style );
3363  if (!control)
3364  return ERROR_FUNCTION_FAILED;
3365 
3366  control->attributes = MSI_RecordGetInteger( rec, 8 );
3368  prop = MSI_RecordGetString( rec, 9 );
3369  control->property = msi_dialog_dup_property( dialog, prop, FALSE );
3370 
3371  msi_dialog_vsc_add_drives( dialog, control );
3372 
3373  return ERROR_SUCCESS;
3374 }
3375 
3377 {
3378  static const WCHAR hrefW[] = {'h','r','e','f'};
3379  static const WCHAR openW[] = {'o','p','e','n',0};
3380  int len, len_href = sizeof(hrefW) / sizeof(hrefW[0]);
3381  const WCHAR *p, *q;
3382  WCHAR quote = 0;
3383  LITEM item;
3384 
3385  item.mask = LIF_ITEMINDEX | LIF_URL;
3386  item.iLink = 0;
3387  item.szUrl[0] = 0;
3388 
3389  SendMessageW( control->hwnd, LM_GETITEM, 0, (LPARAM)&item );
3390 
3391  p = item.szUrl;
3392  while (*p && *p != '<') p++;
3393  if (!*p++) return ERROR_SUCCESS;
3394  if (toupperW( *p++ ) != 'A' || !isspaceW( *p++ )) return ERROR_SUCCESS;
3395  while (*p && isspaceW( *p )) p++;
3396 
3397  len = strlenW( p );
3398  if (len > len_href && !memicmpW( p, hrefW, len_href ))
3399  {
3400  p += len_href;
3401  while (*p && isspaceW( *p )) p++;
3402  if (!*p || *p++ != '=') return ERROR_SUCCESS;
3403  while (*p && isspaceW( *p )) p++;
3404 
3405  if (*p == '\"' || *p == '\'') quote = *p++;
3406  q = p;
3407  if (quote)
3408  {
3409  while (*q && *q != quote) q++;
3410  if (*q != quote) return ERROR_SUCCESS;
3411  }
3412  else
3413  {
3414  while (*q && *q != '>' && !isspaceW( *q )) q++;
3415  if (!*q) return ERROR_SUCCESS;
3416  }
3417  item.szUrl[q - item.szUrl] = 0;
3418  ShellExecuteW( NULL, openW, p, NULL, NULL, SW_SHOWNORMAL );
3419  }
3420  return ERROR_SUCCESS;
3421 }
3422 
3424 {
3425  msi_control *control;
3427  const WCHAR *text = MSI_RecordGetString( rec, 10 );
3428  int len = strlenW( text );
3429  LITEM item;
3430 
3431  control = msi_dialog_add_control( dialog, rec, WC_LINK, style );
3432  if (!control)
3433  return ERROR_FUNCTION_FAILED;
3434 
3435  control->attributes = MSI_RecordGetInteger( rec, 8 );
3437 
3438  item.mask = LIF_ITEMINDEX | LIF_STATE | LIF_URL;
3439  item.iLink = 0;
3440  item.state = LIS_ENABLED;
3441  item.stateMask = LIS_ENABLED;
3442  if (len < L_MAX_URL_LENGTH) strcpyW( item.szUrl, text );
3443  else item.szUrl[0] = 0;
3444 
3445  SendMessageW( control->hwnd, LM_SETITEM, 0, (LPARAM)&item );
3446 
3447  return ERROR_SUCCESS;
3448 }
3449 
3450 static const struct control_handler msi_dialog_handler[] =
3451 {
3473 };
3474 
3475 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
3476 
3478 {
3479  msi_dialog *dialog = param;
3481  UINT i;
3482 
3483  /* find and call the function that can create this type of control */
3484  control_type = MSI_RecordGetString( rec, 3 );
3485  for( i=0; i<NUM_CONTROL_TYPES; i++ )
3487  break;
3488  if( i != NUM_CONTROL_TYPES )
3489  msi_dialog_handler[i].func( dialog, rec );
3490  else
3491  ERR("no handler for element type %s\n", debugstr_w(control_type));
3492 
3493  return ERROR_SUCCESS;
3494 }
3495 
3497 {
3498  static const WCHAR query[] = {
3499  'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3500  'C','o','n','t','r','o','l',' ','W','H','E','R','E',' ',
3501  '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
3502  UINT r;
3503  MSIQUERY *view;
3504  MSIPACKAGE *package = dialog->package;
3505 
3506  TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
3507 
3508  /* query the Control table for all the elements of the control */
3509  r = MSI_OpenQuery( package->db, &view, query, dialog->name );
3510  if( r != ERROR_SUCCESS )
3511  {
3512  ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
3513  return ERROR_INVALID_PARAMETER;
3514  }
3515 
3517  msiobj_release( &view->hdr );
3518  return r;
3519 }
3520 
3522 {
3523  /* FIXME: should restore the original values of any properties we changed */
3525 }
3526 
3527 /* figure out the height of 10 point MS Sans Serif */
3529 {
3530  static const WCHAR szSansSerif[] = {
3531  'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
3532  LOGFONTW lf;
3533  TEXTMETRICW tm;
3534  BOOL r;
3535  LONG height = 0;
3536  HFONT hFont, hOldFont;
3537  HDC hdc;
3538 
3539  hdc = GetDC( hwnd );
3540  if (hdc)
3541  {
3542  memset( &lf, 0, sizeof lf );
3543  lf.lfHeight = MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
3544  strcpyW( lf.lfFaceName, szSansSerif );
3545  hFont = CreateFontIndirectW(&lf);
3546  if (hFont)
3547  {
3548  hOldFont = SelectObject( hdc, hFont );
3549  r = GetTextMetricsW( hdc, &tm );
3550  if (r)
3551  height = tm.tmHeight;
3552  SelectObject( hdc, hOldFont );
3553  DeleteObject( hFont );
3554  }
3555  ReleaseDC( hwnd, hdc );
3556  }
3557  return height;
3558 }
3559 
3560 /* fetch the associated record from the Dialog table */
3562 {
3563  static const WCHAR query[] = {
3564  'S','E','L','E','C','T',' ','*',' ',
3565  'F','R','O','M',' ','D','i','a','l','o','g',' ',
3566  'W','H','E','R','E',' ',
3567  '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
3568  MSIPACKAGE *package = dialog->package;
3569  MSIRECORD *rec = NULL;
3570 
3571  TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
3572 
3573  rec = MSI_QueryGetRecord( package->db, query, dialog->name );
3574  if( !rec )
3575  WARN("query failed for dialog %s\n", debugstr_w(dialog->name));
3576 
3577  return rec;
3578 }
3579 
3581 {
3582  static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0};
3583  static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0};
3584 
3585  UINT xres, yres;
3586  POINT center;
3587  SIZE sz;
3588  LONG style;
3589 
3590  center.x = MSI_RecordGetInteger( rec, 2 );
3591  center.y = MSI_RecordGetInteger( rec, 3 );
3592 
3593  sz.cx = MSI_RecordGetInteger( rec, 4 );
3594  sz.cy = MSI_RecordGetInteger( rec, 5 );
3595 
3596  sz.cx = msi_dialog_scale_unit( dialog, sz.cx );
3597  sz.cy = msi_dialog_scale_unit( dialog, sz.cy );
3598 
3599  xres = msi_get_property_int( dialog->package->db, szScreenX, 0 );
3600  yres = msi_get_property_int( dialog->package->db, szScreenY, 0 );
3601 
3602  center.x = MulDiv( center.x, xres, 100 );
3603  center.y = MulDiv( center.y, yres, 100 );
3604 
3605  /* turn the client pos into the window rectangle */
3606  if (dialog->package->center_x && dialog->package->center_y)
3607  {
3608  pos->left = dialog->package->center_x - sz.cx / 2.0;
3609  pos->right = pos->left + sz.cx;
3610  pos->top = dialog->package->center_y - sz.cy / 2.0;
3611  pos->bottom = pos->top + sz.cy;
3612  }
3613  else
3614  {
3615  pos->left = center.x - sz.cx/2;
3616  pos->right = pos->left + sz.cx;
3617  pos->top = center.y - sz.cy/2;
3618  pos->bottom = pos->top + sz.cy;
3619 
3620  /* save the center */
3621  dialog->package->center_x = center.x;
3622  dialog->package->center_y = center.y;
3623  }
3624 
3625  dialog->size.cx = sz.cx;
3626  dialog->size.cy = sz.cy;
3627 
3628  TRACE("%s\n", wine_dbgstr_rect(pos));
3629 
3632 }
3633 
3635 {
3636  struct list tab_chain;
3637  msi_control *control;
3638  HWND prev = HWND_TOP;
3639 
3640  list_init( &tab_chain );
3641  if (!(control = msi_dialog_find_control( dialog, first ))) return;
3642 
3643  dialog->hWndFocus = control->hwnd;
3644  while (control)
3645  {
3646  list_remove( &control->entry );
3647  list_add_tail( &tab_chain, &control->entry );
3648  if (!control->tabnext) break;
3649  control = msi_dialog_find_control( dialog, control->tabnext );
3650  }
3651 
3652  LIST_FOR_EACH_ENTRY( control, &tab_chain, msi_control, entry )
3653  {
3654  SetWindowPos( control->hwnd, prev, 0, 0, 0, 0,
3657  prev = control->hwnd;
3658  }