ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

dialog.c
Go to the documentation of this file.
00001 /*
00002  * Implementation of the Microsoft Installer (msi.dll)
00003  *
00004  * Copyright 2005 Mike McCormack for CodeWeavers
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00019  */
00020 
00021 #define COBJMACROS
00022 #define NONAMELESSUNION
00023 #define NONAMELESSSTRUCT
00024 
00025 #include <stdarg.h>
00026 
00027 #include "windef.h"
00028 #include "winbase.h"
00029 #include "wingdi.h"
00030 #include "winuser.h"
00031 #include "winnls.h"
00032 #include "msi.h"
00033 #include "msipriv.h"
00034 #include "msidefs.h"
00035 #include "ocidl.h"
00036 #include "olectl.h"
00037 #include "richedit.h"
00038 #include "commctrl.h"
00039 #include "winreg.h"
00040 #include "shlwapi.h"
00041 #include "msiserver.h"
00042 
00043 #include "wine/debug.h"
00044 #include "wine/unicode.h"
00045 
00046 WINE_DEFAULT_DEBUG_CHANNEL(msi);
00047 
00048 extern HINSTANCE msi_hInstance;
00049 
00050 struct msi_control_tag;
00051 typedef struct msi_control_tag msi_control;
00052 typedef UINT (*msi_handler)( msi_dialog *, msi_control *, WPARAM );
00053 typedef void (*msi_update)( msi_dialog *, msi_control * );
00054 
00055 struct msi_control_tag
00056 {
00057     struct list entry;
00058     HWND hwnd;
00059     msi_handler handler;
00060     msi_update update;
00061     LPWSTR property;
00062     LPWSTR value;
00063     HBITMAP hBitmap;
00064     HICON hIcon;
00065     LPWSTR tabnext;
00066     LPWSTR type;
00067     HMODULE hDll;
00068     float progress_current;
00069     float progress_max;
00070     BOOL  progress_backwards;
00071     DWORD attributes;
00072     WCHAR name[1];
00073 };
00074 
00075 typedef struct msi_font_tag
00076 {
00077     struct list entry;
00078     HFONT hfont;
00079     COLORREF color;
00080     WCHAR name[1];
00081 } msi_font;
00082 
00083 struct msi_dialog_tag
00084 {
00085     MSIPACKAGE *package;
00086     msi_dialog *parent;
00087     msi_dialog_event_handler event_handler;
00088     BOOL finished;
00089     INT scale;
00090     DWORD attributes;
00091     SIZE size;
00092     HWND hwnd;
00093     LPWSTR default_font;
00094     struct list fonts;
00095     struct list controls;
00096     HWND hWndFocus;
00097     LPWSTR control_default;
00098     LPWSTR control_cancel;
00099     WCHAR name[1];
00100 };
00101 
00102 typedef UINT (*msi_dialog_control_func)( msi_dialog *dialog, MSIRECORD *rec );
00103 struct control_handler 
00104 {
00105     LPCWSTR control_type;
00106     msi_dialog_control_func func;
00107 };
00108 
00109 typedef struct
00110 {
00111     msi_dialog* dialog;
00112     msi_control *parent;
00113     DWORD       attributes;
00114     LPWSTR      propval;
00115 } radio_button_group_descr;
00116 
00117 static const WCHAR szMsiDialogClass[] = { 'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0 };
00118 static const WCHAR szMsiHiddenWindow[] = { 'M','s','i','H','i','d','d','e','n','W','i','n','d','o','w',0 };
00119 static const WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
00120 static const WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
00121 static const WCHAR szButtonData[] = { 'M','S','I','D','A','T','A',0 };
00122 static const WCHAR szProgress[] = { 'P','r','o','g','r','e','s','s',0 };
00123 static const WCHAR szText[] = { 'T','e','x','t',0 };
00124 static const WCHAR szPushButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
00125 static const WCHAR szLine[] = { 'L','i','n','e',0 };
00126 static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 };
00127 static const WCHAR szCheckBox[] = { 'C','h','e','c','k','B','o','x',0 };
00128 static const WCHAR szScrollableText[] = { 'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 };
00129 static const WCHAR szComboBox[] = { 'C','o','m','b','o','B','o','x',0 };
00130 static const WCHAR szEdit[] = { 'E','d','i','t',0 };
00131 static const WCHAR szMaskedEdit[] = { 'M','a','s','k','e','d','E','d','i','t',0 };
00132 static const WCHAR szPathEdit[] = { 'P','a','t','h','E','d','i','t',0 };
00133 static const WCHAR szProgressBar[] = { 'P','r','o','g','r','e','s','s','B','a','r',0 };
00134 static const WCHAR szSetProgress[] = { 'S','e','t','P','r','o','g','r','e','s','s',0 };
00135 static const WCHAR szRadioButtonGroup[] = { 'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 };
00136 static const WCHAR szIcon[] = { 'I','c','o','n',0 };
00137 static const WCHAR szSelectionTree[] = { 'S','e','l','e','c','t','i','o','n','T','r','e','e',0 };
00138 static const WCHAR szGroupBox[] = { 'G','r','o','u','p','B','o','x',0 };
00139 static const WCHAR szListBox[] = { 'L','i','s','t','B','o','x',0 };
00140 static const WCHAR szDirectoryCombo[] = { 'D','i','r','e','c','t','o','r','y','C','o','m','b','o',0 };
00141 static const WCHAR szDirectoryList[] = { 'D','i','r','e','c','t','o','r','y','L','i','s','t',0 };
00142 static const WCHAR szVolumeCostList[] = { 'V','o','l','u','m','e','C','o','s','t','L','i','s','t',0 };
00143 static const WCHAR szVolumeSelectCombo[] = { 'V','o','l','u','m','e','S','e','l','e','c','t','C','o','m','b','o',0 };
00144 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};
00145 static const WCHAR szSelectionPath[] = {'S','e','l','e','c','t','i','o','n','P','a','t','h',0};
00146 static const WCHAR szProperty[] = {'P','r','o','p','e','r','t','y',0};
00147 
00148 /* dialog sequencing */
00149 
00150 #define WM_MSI_DIALOG_CREATE  (WM_USER+0x100)
00151 #define WM_MSI_DIALOG_DESTROY (WM_USER+0x101)
00152 
00153 #define USER_INSTALLSTATE_ALL 0x1000
00154 
00155 static DWORD uiThreadId;
00156 static HWND hMsiHiddenWindow;
00157 
00158 static LPWSTR msi_get_window_text( HWND hwnd )
00159 {
00160     UINT sz, r;
00161     LPWSTR buf;
00162 
00163     sz = 0x20;
00164     buf = msi_alloc( sz*sizeof(WCHAR) );
00165     while ( buf )
00166     {
00167         r = GetWindowTextW( hwnd, buf, sz );
00168         if ( r < (sz - 1) )
00169             break;
00170         sz *= 2;
00171         buf = msi_realloc( buf, sz*sizeof(WCHAR) );
00172     }
00173 
00174     return buf;
00175 }
00176 
00177 static INT msi_dialog_scale_unit( msi_dialog *dialog, INT val )
00178 {
00179     return MulDiv( val, dialog->scale, 12 );
00180 }
00181 
00182 static msi_control *msi_dialog_find_control( msi_dialog *dialog, LPCWSTR name )
00183 {
00184     msi_control *control;
00185 
00186     if( !name )
00187         return NULL;
00188     if( !dialog->hwnd )
00189         return NULL;
00190     LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
00191         if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */
00192             return control;
00193     return NULL;
00194 }
00195 
00196 static msi_control *msi_dialog_find_control_by_type( msi_dialog *dialog, LPCWSTR type )
00197 {
00198     msi_control *control;
00199 
00200     if( !type )
00201         return NULL;
00202     if( !dialog->hwnd )
00203         return NULL;
00204     LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
00205         if( !strcmpW( control->type, type ) ) /* FIXME: case sensitive? */
00206             return control;
00207     return NULL;
00208 }
00209 
00210 static msi_control *msi_dialog_find_control_by_hwnd( msi_dialog *dialog, HWND hwnd )
00211 {
00212     msi_control *control;
00213 
00214     if( !dialog->hwnd )
00215         return NULL;
00216     LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
00217         if( hwnd == control->hwnd )
00218             return control;
00219     return NULL;
00220 }
00221 
00222 static LPWSTR msi_get_deformatted_field( MSIPACKAGE *package, MSIRECORD *rec, int field )
00223 {
00224     LPCWSTR str = MSI_RecordGetString( rec, field );
00225     LPWSTR ret = NULL;
00226 
00227     if (str)
00228         deformat_string( package, str, &ret );
00229     return ret;
00230 }
00231 
00232 static LPWSTR msi_dialog_dup_property( msi_dialog *dialog, LPCWSTR property, BOOL indirect )
00233 {
00234     LPWSTR prop = NULL;
00235 
00236     if (!property)
00237         return NULL;
00238 
00239     if (indirect)
00240         prop = msi_dup_property( dialog->package->db, property );
00241 
00242     if (!prop)
00243         prop = strdupW( property );
00244 
00245     return prop;
00246 }
00247 
00248 msi_dialog *msi_dialog_get_parent( msi_dialog *dialog )
00249 {
00250     return dialog->parent;
00251 }
00252 
00253 LPWSTR msi_dialog_get_name( msi_dialog *dialog )
00254 {
00255     return dialog->name;
00256 }
00257 
00258 /*
00259  * msi_dialog_get_style
00260  *
00261  * Extract the {\style} string from the front of the text to display and
00262  * update the pointer.  Only the last style in a list is applied.
00263  */
00264 static LPWSTR msi_dialog_get_style( LPCWSTR p, LPCWSTR *rest )
00265 {
00266     LPWSTR ret;
00267     LPCWSTR q, i, first;
00268     DWORD len;
00269 
00270     q = NULL;
00271     *rest = p;
00272     if( !p )
00273         return NULL;
00274 
00275     while ((first = strchrW( p, '{' )) && (q = strchrW( first + 1, '}' )))
00276     {
00277         p = first + 1;
00278         if( *p != '\\' && *p != '&' )
00279             return NULL;
00280 
00281         /* little bit of sanity checking to stop us getting confused with RTF */
00282         for( i=++p; i<q; i++ )
00283             if( *i == '}' || *i == '\\' )
00284                 return NULL;
00285     }
00286 
00287     if (!q)
00288         return NULL;
00289 
00290     *rest = ++q;
00291     len = q - p;
00292 
00293     ret = msi_alloc( len*sizeof(WCHAR) );
00294     if( !ret )
00295         return ret;
00296     memcpy( ret, p, len*sizeof(WCHAR) );
00297     ret[len-1] = 0;
00298     return ret;
00299 }
00300 
00301 static UINT msi_dialog_add_font( MSIRECORD *rec, LPVOID param )
00302 {
00303     msi_dialog *dialog = param;
00304     msi_font *font;
00305     LPCWSTR face, name;
00306     LOGFONTW lf;
00307     INT style;
00308     HDC hdc;
00309 
00310     /* create a font and add it to the list */
00311     name = MSI_RecordGetString( rec, 1 );
00312     font = msi_alloc( sizeof *font + strlenW( name )*sizeof (WCHAR) );
00313     strcpyW( font->name, name );
00314     list_add_head( &dialog->fonts, &font->entry );
00315 
00316     font->color = MSI_RecordGetInteger( rec, 4 );
00317 
00318     memset( &lf, 0, sizeof lf );
00319     face = MSI_RecordGetString( rec, 2 );
00320     lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
00321     style = MSI_RecordGetInteger( rec, 5 );
00322     if( style & msidbTextStyleStyleBitsBold )
00323         lf.lfWeight = FW_BOLD;
00324     if( style & msidbTextStyleStyleBitsItalic )
00325         lf.lfItalic = TRUE;
00326     if( style & msidbTextStyleStyleBitsUnderline )
00327         lf.lfUnderline = TRUE;
00328     if( style & msidbTextStyleStyleBitsStrike )
00329         lf.lfStrikeOut = TRUE;
00330     lstrcpynW( lf.lfFaceName, face, LF_FACESIZE );
00331 
00332     /* adjust the height */
00333     hdc = GetDC( dialog->hwnd );
00334     if (hdc)
00335     {
00336         lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
00337         ReleaseDC( dialog->hwnd, hdc );
00338     }
00339 
00340     font->hfont = CreateFontIndirectW( &lf );
00341 
00342     TRACE("Adding font style %s\n", debugstr_w(font->name) );
00343 
00344     return ERROR_SUCCESS;
00345 }
00346 
00347 static msi_font *msi_dialog_find_font( msi_dialog *dialog, LPCWSTR name )
00348 {
00349     msi_font *font = NULL;
00350 
00351     LIST_FOR_EACH_ENTRY( font, &dialog->fonts, msi_font, entry )
00352         if( !strcmpW( font->name, name ) )  /* FIXME: case sensitive? */
00353             break;
00354 
00355     return font;
00356 }
00357 
00358 static UINT msi_dialog_set_font( msi_dialog *dialog, HWND hwnd, LPCWSTR name )
00359 {
00360     msi_font *font;
00361 
00362     font = msi_dialog_find_font( dialog, name );
00363     if( font )
00364         SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE );
00365     else
00366         ERR("No font entry for %s\n", debugstr_w(name));
00367     return ERROR_SUCCESS;
00368 }
00369 
00370 static UINT msi_dialog_build_font_list( msi_dialog *dialog )
00371 {
00372     static const WCHAR query[] = {
00373         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
00374         '`','T','e','x','t','S','t','y','l','e','`',0};
00375     MSIQUERY *view;
00376     UINT r;
00377 
00378     TRACE("dialog %p\n", dialog );
00379 
00380     r = MSI_OpenQuery( dialog->package->db, &view, query );
00381     if( r != ERROR_SUCCESS )
00382         return r;
00383 
00384     r = MSI_IterateRecords( view, NULL, msi_dialog_add_font, dialog );
00385     msiobj_release( &view->hdr );
00386     return r;
00387 }
00388 
00389 static void msi_destroy_control( msi_control *t )
00390 {
00391     list_remove( &t->entry );
00392     /* leave dialog->hwnd - destroying parent destroys child windows */
00393     msi_free( t->property );
00394     msi_free( t->value );
00395     if( t->hBitmap )
00396         DeleteObject( t->hBitmap );
00397     if( t->hIcon )
00398         DestroyIcon( t->hIcon );
00399     msi_free( t->tabnext );
00400     msi_free( t->type );
00401     if (t->hDll)
00402         FreeLibrary( t->hDll );
00403     msi_free( t );
00404 }
00405 
00406 static msi_control *msi_dialog_create_window( msi_dialog *dialog,
00407                 MSIRECORD *rec, DWORD exstyle, LPCWSTR szCls, LPCWSTR name, LPCWSTR text,
00408                 DWORD style, HWND parent )
00409 {
00410     DWORD x, y, width, height;
00411     LPWSTR font = NULL, title_font = NULL;
00412     LPCWSTR title = NULL;
00413     msi_control *control;
00414 
00415     style |= WS_CHILD;
00416 
00417     control = msi_alloc( sizeof *control + strlenW(name)*sizeof(WCHAR) );
00418     if (!control)
00419         return NULL;
00420 
00421     strcpyW( control->name, name );
00422     list_add_tail( &dialog->controls, &control->entry );
00423     control->handler = NULL;
00424     control->update = NULL;
00425     control->property = NULL;
00426     control->value = NULL;
00427     control->hBitmap = NULL;
00428     control->hIcon = NULL;
00429     control->hDll = NULL;
00430     control->tabnext = strdupW( MSI_RecordGetString( rec, 11) );
00431     control->type = strdupW( MSI_RecordGetString( rec, 3 ) );
00432     control->progress_current = 0;
00433     control->progress_max = 100;
00434     control->progress_backwards = FALSE;
00435 
00436     x = MSI_RecordGetInteger( rec, 4 );
00437     y = MSI_RecordGetInteger( rec, 5 );
00438     width = MSI_RecordGetInteger( rec, 6 );
00439     height = MSI_RecordGetInteger( rec, 7 );
00440 
00441     x = msi_dialog_scale_unit( dialog, x );
00442     y = msi_dialog_scale_unit( dialog, y );
00443     width = msi_dialog_scale_unit( dialog, width );
00444     height = msi_dialog_scale_unit( dialog, height );
00445 
00446     if( text )
00447     {
00448         deformat_string( dialog->package, text, &title_font );
00449         font = msi_dialog_get_style( title_font, &title );
00450     }
00451 
00452     control->hwnd = CreateWindowExW( exstyle, szCls, title, style,
00453                           x, y, width, height, parent, NULL, NULL, NULL );
00454 
00455     TRACE("Dialog %s control %s hwnd %p\n",
00456            debugstr_w(dialog->name), debugstr_w(text), control->hwnd );
00457 
00458     msi_dialog_set_font( dialog, control->hwnd,
00459                          font ? font : dialog->default_font );
00460 
00461     msi_free( title_font );
00462     msi_free( font );
00463 
00464     return control;
00465 }
00466 
00467 static LPWSTR msi_dialog_get_uitext( msi_dialog *dialog, LPCWSTR key )
00468 {
00469     MSIRECORD *rec;
00470     LPWSTR text;
00471 
00472     static const WCHAR query[] = {
00473         's','e','l','e','c','t',' ','*',' ',
00474         'f','r','o','m',' ','`','U','I','T','e','x','t','`',' ',
00475         'w','h','e','r','e',' ','`','K','e','y','`',' ','=',' ','\'','%','s','\'',0
00476     };
00477 
00478     rec = MSI_QueryGetRecord( dialog->package->db, query, key );
00479     if (!rec) return NULL;
00480     text = strdupW( MSI_RecordGetString( rec, 2 ) );
00481     msiobj_release( &rec->hdr );
00482     return text;
00483 }
00484 
00485 static MSIRECORD *msi_get_binary_record( MSIDATABASE *db, LPCWSTR name )
00486 {
00487     static const WCHAR query[] = {
00488         's','e','l','e','c','t',' ','*',' ',
00489         'f','r','o','m',' ','B','i','n','a','r','y',' ',
00490         'w','h','e','r','e',' ',
00491             '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0
00492     };
00493 
00494     return MSI_QueryGetRecord( db, query, name );
00495 }
00496 
00497 static LPWSTR msi_create_tmp_path(void)
00498 {
00499     WCHAR tmp[MAX_PATH];
00500     LPWSTR path = NULL;
00501     DWORD len, r;
00502 
00503     r = GetTempPathW( MAX_PATH, tmp );
00504     if( !r )
00505         return path;
00506     len = lstrlenW( tmp ) + 20;
00507     path = msi_alloc( len * sizeof (WCHAR) );
00508     if( path )
00509     {
00510         r = GetTempFileNameW( tmp, szMsi, 0, path );
00511         if (!r)
00512         {
00513             msi_free( path );
00514             path = NULL;
00515         }
00516     }
00517     return path;
00518 }
00519 
00520 static HANDLE msi_load_image( MSIDATABASE *db, LPCWSTR name, UINT type,
00521                               UINT cx, UINT cy, UINT flags )
00522 {
00523     MSIRECORD *rec = NULL;
00524     HANDLE himage = NULL;
00525     LPWSTR tmp;
00526     UINT r;
00527 
00528     TRACE("%p %s %u %u %08x\n", db, debugstr_w(name), cx, cy, flags);
00529 
00530     tmp = msi_create_tmp_path();
00531     if( !tmp )
00532         return himage;
00533 
00534     rec = msi_get_binary_record( db, name );
00535     if( rec )
00536     {
00537         r = MSI_RecordStreamToFile( rec, 2, tmp );
00538         if( r == ERROR_SUCCESS )
00539         {
00540             himage = LoadImageW( 0, tmp, type, cx, cy, flags );
00541         }
00542         msiobj_release( &rec->hdr );
00543     }
00544     DeleteFileW( tmp );
00545 
00546     msi_free( tmp );
00547     return himage;
00548 }
00549 
00550 static HICON msi_load_icon( MSIDATABASE *db, LPCWSTR text, UINT attributes )
00551 {
00552     DWORD cx = 0, cy = 0, flags;
00553 
00554     flags = LR_LOADFROMFILE | LR_DEFAULTSIZE;
00555     if( attributes & msidbControlAttributesFixedSize )
00556     {
00557         flags &= ~LR_DEFAULTSIZE;
00558         if( attributes & msidbControlAttributesIconSize16 )
00559         {
00560             cx += 16;
00561             cy += 16;
00562         }
00563         if( attributes & msidbControlAttributesIconSize32 )
00564         {
00565             cx += 32;
00566             cy += 32;
00567         }
00568         /* msidbControlAttributesIconSize48 handled by above logic */
00569     }
00570     return msi_load_image( db, text, IMAGE_ICON, cx, cy, flags );
00571 }
00572 
00573 static void msi_dialog_update_controls( msi_dialog *dialog, LPCWSTR property )
00574 {
00575     msi_control *control;
00576 
00577     LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
00578     {
00579         if ( control->property && !strcmpW( control->property, property ) && control->update )
00580             control->update( dialog, control );
00581     }
00582 }
00583 
00584 static void msi_dialog_set_property( MSIPACKAGE *package, LPCWSTR property, LPCWSTR value )
00585 {
00586     UINT r = msi_set_property( package->db, property, value );
00587     if (r == ERROR_SUCCESS && !strcmpW( property, szSourceDir ))
00588         msi_reset_folders( package, TRUE );
00589 }
00590 
00591 static MSIFEATURE *msi_seltree_feature_from_item( HWND hwnd, HTREEITEM hItem )
00592 {
00593     TVITEMW tvi;
00594 
00595     /* get the feature from the item */
00596     memset( &tvi, 0, sizeof tvi );
00597     tvi.hItem = hItem;
00598     tvi.mask = TVIF_PARAM | TVIF_HANDLE;
00599     SendMessageW( hwnd, TVM_GETITEMW, 0, (LPARAM)&tvi );
00600     return (MSIFEATURE *)tvi.lParam;
00601 }
00602 
00603 struct msi_selection_tree_info
00604 {
00605     msi_dialog *dialog;
00606     HWND hwnd;
00607     WNDPROC oldproc;
00608     HTREEITEM selected;
00609 };
00610 
00611 static MSIFEATURE *msi_seltree_get_selected_feature( msi_control *control )
00612 {
00613     struct msi_selection_tree_info *info = GetPropW( control->hwnd, szButtonData );
00614     return msi_seltree_feature_from_item( control->hwnd, info->selected );
00615 }
00616 
00617 /* called from the Control Event subscription code */
00618 void msi_dialog_handle_event( msi_dialog* dialog, LPCWSTR control, 
00619                               LPCWSTR attribute, MSIRECORD *rec )
00620 {
00621     msi_control* ctrl;
00622     LPCWSTR font_text, text = NULL;
00623     LPWSTR font;
00624 
00625     ctrl = msi_dialog_find_control( dialog, control );
00626     if (!ctrl)
00627         return;
00628     if( !strcmpW( attribute, szText ) )
00629     {
00630         font_text = MSI_RecordGetString( rec , 1 );
00631         font = msi_dialog_get_style( font_text, &text );
00632         if (!text) text = szEmpty;
00633         SetWindowTextW( ctrl->hwnd, text );
00634         msi_free( font );
00635         msi_dialog_check_messages( NULL );
00636     }
00637     else if( !strcmpW( attribute, szProgress ) )
00638     {
00639         DWORD func, val1, val2, units;
00640 
00641         func = MSI_RecordGetInteger( rec, 1 );
00642         val1 = MSI_RecordGetInteger( rec, 2 );
00643         val2 = MSI_RecordGetInteger( rec, 3 );
00644 
00645         TRACE("progress: func %u val1 %u val2 %u\n", func, val1, val2);
00646 
00647         switch (func)
00648         {
00649         case 0: /* init */
00650             SendMessageW( ctrl->hwnd, PBM_SETRANGE, 0, MAKELPARAM(0,100) );
00651             units = val1 / 512;
00652             if (val2)
00653             {
00654                 ctrl->progress_max = units ? units : 100;
00655                 ctrl->progress_current = units;
00656                 ctrl->progress_backwards = TRUE;
00657                 SendMessageW( ctrl->hwnd, PBM_SETPOS, 100, 0 );
00658             }
00659             else
00660             {
00661                 ctrl->progress_max = units ? units : 100;
00662                 ctrl->progress_current = 0;
00663                 ctrl->progress_backwards = FALSE;
00664                 SendMessageW( ctrl->hwnd, PBM_SETPOS, 0, 0 );
00665             }
00666             break;
00667         case 1: /* FIXME: not sure what this is supposed to do */
00668             break;
00669         case 2: /* move */
00670             units = val1 / 512;
00671             if (ctrl->progress_backwards)
00672             {
00673                 if (units >= ctrl->progress_current) ctrl->progress_current -= units;
00674                 else ctrl->progress_current = 0;
00675             }
00676             else
00677             {
00678                 if (ctrl->progress_current + units < ctrl->progress_max) ctrl->progress_current += units;
00679                 else ctrl->progress_current = ctrl->progress_max;
00680             }
00681             SendMessageW( ctrl->hwnd, PBM_SETPOS, MulDiv(100, ctrl->progress_current, ctrl->progress_max), 0 );
00682             break;
00683         default:
00684             FIXME("Unknown progress message %u\n", func);
00685             break;
00686         }
00687     }
00688     else if ( !strcmpW( attribute, szProperty ) )
00689     {
00690         MSIFEATURE *feature = msi_seltree_get_selected_feature( ctrl );
00691         msi_dialog_set_property( dialog->package, ctrl->property, feature->Directory );
00692     }
00693     else if ( !strcmpW( attribute, szSelectionPath ) )
00694     {
00695         BOOL indirect = ctrl->attributes & msidbControlAttributesIndirect;
00696         LPWSTR path = msi_dialog_dup_property( dialog, ctrl->property, indirect );
00697         if (!path) return;
00698         SetWindowTextW( ctrl->hwnd, path );
00699         msi_free(path);
00700     }
00701     else
00702     {
00703         FIXME("Attribute %s not being set\n", debugstr_w(attribute));
00704         return;
00705     }
00706 }
00707 
00708 static void msi_dialog_map_events(msi_dialog* dialog, LPCWSTR control)
00709 {
00710     static const WCHAR Query[] = {
00711         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
00712          '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ',
00713         'W','H','E','R','E',' ',
00714          '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
00715         'A','N','D',' ',
00716          '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0
00717     };
00718     MSIRECORD *row;
00719     LPCWSTR event, attribute;
00720 
00721     row = MSI_QueryGetRecord( dialog->package->db, Query, dialog->name, control );
00722     if (!row)
00723         return;
00724 
00725     event = MSI_RecordGetString( row, 3 );
00726     attribute = MSI_RecordGetString( row, 4 );
00727     ControlEvent_SubscribeToEvent( dialog->package, dialog, event, control, attribute );
00728     msiobj_release( &row->hdr );
00729 }
00730 
00731 /* everything except radio buttons */
00732 static msi_control *msi_dialog_add_control( msi_dialog *dialog,
00733                 MSIRECORD *rec, LPCWSTR szCls, DWORD style )
00734 {
00735     DWORD attributes;
00736     LPCWSTR text, name;
00737     DWORD exstyle = 0;
00738 
00739     name = MSI_RecordGetString( rec, 2 );
00740     attributes = MSI_RecordGetInteger( rec, 8 );
00741     text = MSI_RecordGetString( rec, 10 );
00742 
00743     TRACE("%s, %s, %08x, %s, %08x\n", debugstr_w(szCls), debugstr_w(name),
00744           attributes, debugstr_w(text), style);
00745 
00746     if( attributes & msidbControlAttributesVisible )
00747         style |= WS_VISIBLE;
00748     if( ~attributes & msidbControlAttributesEnabled )
00749         style |= WS_DISABLED;
00750     if( attributes & msidbControlAttributesSunken )
00751         exstyle |= WS_EX_CLIENTEDGE;
00752 
00753     msi_dialog_map_events(dialog, name);
00754 
00755     return msi_dialog_create_window( dialog, rec, exstyle, szCls, name,
00756                                      text, style, dialog->hwnd );
00757 }
00758 
00759 struct msi_text_info
00760 {
00761     msi_font *font;
00762     WNDPROC oldproc;
00763     DWORD attributes;
00764 };
00765 
00766 /*
00767  * we don't erase our own background,
00768  * so we have to make sure that the parent window redraws first
00769  */
00770 static void msi_text_on_settext( HWND hWnd )
00771 {
00772     HWND hParent;
00773     RECT rc;
00774 
00775     hParent = GetParent( hWnd );
00776     GetWindowRect( hWnd, &rc );
00777     MapWindowPoints( NULL, hParent, (LPPOINT) &rc, 2 );
00778     InvalidateRect( hParent, &rc, TRUE );
00779 }
00780 
00781 static LRESULT WINAPI
00782 MSIText_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
00783 {
00784     struct msi_text_info *info;
00785     LRESULT r = 0;
00786 
00787     TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
00788 
00789     info = GetPropW(hWnd, szButtonData);
00790 
00791     if( msg == WM_CTLCOLORSTATIC &&
00792        ( info->attributes & msidbControlAttributesTransparent ) )
00793     {
00794         SetBkMode( (HDC)wParam, TRANSPARENT );
00795         return (LRESULT) GetStockObject(NULL_BRUSH);
00796     }
00797 
00798     r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
00799     if ( info->font )
00800         SetTextColor( (HDC)wParam, info->font->color );
00801 
00802     switch( msg )
00803     {
00804     case WM_SETTEXT:
00805         msi_text_on_settext( hWnd );
00806         break;
00807     case WM_NCDESTROY:
00808         msi_free( info );
00809         RemovePropW( hWnd, szButtonData );
00810         break;
00811     }
00812 
00813     return r;
00814 }
00815 
00816 static UINT msi_dialog_text_control( msi_dialog *dialog, MSIRECORD *rec )
00817 {
00818     msi_control *control;
00819     struct msi_text_info *info;
00820     LPCWSTR text, ptr, prop, control_name;
00821     LPWSTR font_name;
00822 
00823     TRACE("%p %p\n", dialog, rec);
00824 
00825     control = msi_dialog_add_control( dialog, rec, szStatic, SS_LEFT | WS_GROUP );
00826     if( !control )
00827         return ERROR_FUNCTION_FAILED;
00828 
00829     info = msi_alloc( sizeof *info );
00830     if( !info )
00831         return ERROR_SUCCESS;
00832 
00833     control_name = MSI_RecordGetString( rec, 2 );
00834     control->attributes = MSI_RecordGetInteger( rec, 8 );
00835     prop = MSI_RecordGetString( rec, 9 );
00836     control->property = msi_dialog_dup_property( dialog, prop, FALSE );
00837 
00838     text = MSI_RecordGetString( rec, 10 );
00839     font_name = msi_dialog_get_style( text, &ptr );
00840     info->font = ( font_name ) ? msi_dialog_find_font( dialog, font_name ) : NULL;
00841     msi_free( font_name );
00842 
00843     info->attributes = MSI_RecordGetInteger( rec, 8 );
00844     if( info->attributes & msidbControlAttributesTransparent )
00845         SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT );
00846 
00847     info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
00848                                           (LONG_PTR)MSIText_WndProc );
00849     SetPropW( control->hwnd, szButtonData, info );
00850 
00851     ControlEvent_SubscribeToEvent( dialog->package, dialog,
00852                                    szSelectionPath, control_name, szSelectionPath );
00853 
00854     return ERROR_SUCCESS;
00855 }
00856 
00857 /* strip any leading text style label from text field */
00858 static WCHAR *msi_get_binary_name( MSIPACKAGE *package, MSIRECORD *rec )
00859 {
00860     WCHAR *p, *text;
00861 
00862     text = msi_get_deformatted_field( package, rec, 10 );
00863     if (!text)
00864         return NULL;
00865 
00866     p = text;
00867     while (*p && *p != '{') p++;
00868     if (!*p++) return text;
00869 
00870     while (*p && *p != '}') p++;
00871     if (!*p++) return text;
00872 
00873     p = strdupW( p );
00874     msi_free( text );
00875     return p;
00876 }
00877 
00878 static UINT msi_dialog_set_property_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
00879 {
00880     static const WCHAR szNullArg[] = {'{','}',0};
00881     LPWSTR p, prop, arg_fmt = NULL;
00882     UINT len;
00883 
00884     len = strlenW( event );
00885     prop = msi_alloc( len * sizeof(WCHAR) );
00886     strcpyW( prop, &event[1] );
00887     p = strchrW( prop, ']' );
00888     if (p && (p[1] == 0 || p[1] == ' '))
00889     {
00890         *p = 0;
00891         if (strcmpW( szNullArg, arg ))
00892             deformat_string( dialog->package, arg, &arg_fmt );
00893         msi_dialog_set_property( dialog->package, prop, arg_fmt );
00894         msi_dialog_update_controls( dialog, prop );
00895         msi_free( arg_fmt );
00896     }
00897     else ERR("Badly formatted property string - what happens?\n");
00898     msi_free( prop );
00899     return ERROR_SUCCESS;
00900 }
00901 
00902 static UINT msi_dialog_send_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
00903 {
00904     LPWSTR event_fmt = NULL, arg_fmt = NULL;
00905 
00906     TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
00907 
00908     deformat_string( dialog->package, event, &event_fmt );
00909     deformat_string( dialog->package, arg, &arg_fmt );
00910 
00911     dialog->event_handler( dialog->package, event_fmt, arg_fmt, dialog );
00912 
00913     msi_free( event_fmt );
00914     msi_free( arg_fmt );
00915 
00916     return ERROR_SUCCESS;
00917 }
00918 
00919 static UINT msi_dialog_control_event( MSIRECORD *rec, LPVOID param )
00920 {
00921     msi_dialog *dialog = param;
00922     LPCWSTR condition, event, arg;
00923     UINT r;
00924 
00925     condition = MSI_RecordGetString( rec, 5 );
00926     r = MSI_EvaluateConditionW( dialog->package, condition );
00927     if (r == MSICONDITION_TRUE || r == MSICONDITION_NONE)
00928     {
00929         event = MSI_RecordGetString( rec, 3 );
00930         arg = MSI_RecordGetString( rec, 4 );
00931         if (event[0] == '[')
00932             msi_dialog_set_property_event( dialog, event, arg );
00933         else
00934             msi_dialog_send_event( dialog, event, arg );
00935     }
00936     return ERROR_SUCCESS;
00937 }
00938 
00939 static UINT msi_dialog_button_handler( msi_dialog *dialog, msi_control *control, WPARAM param )
00940 {
00941     static const WCHAR query[] = {
00942         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
00943         'C','o','n','t','r','o','l','E','v','e','n','t',' ','W','H','E','R','E',' ',
00944         '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ','A','N','D',' ',
00945         '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
00946         'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0};
00947     MSIQUERY *view;
00948     UINT r;
00949 
00950     if (HIWORD(param) != BN_CLICKED)
00951         return ERROR_SUCCESS;
00952 
00953     r = MSI_OpenQuery( dialog->package->db, &view, query, dialog->name, control->name );
00954     if (r != ERROR_SUCCESS)
00955     {
00956         ERR("query failed\n");
00957         return ERROR_SUCCESS;
00958     }
00959     r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog );
00960     msiobj_release( &view->hdr );
00961     return r;
00962 }
00963 
00964 static UINT msi_dialog_button_control( msi_dialog *dialog, MSIRECORD *rec )
00965 {
00966     msi_control *control;
00967     UINT attributes, style;
00968 
00969     TRACE("%p %p\n", dialog, rec);
00970 
00971     style = WS_TABSTOP;
00972     attributes = MSI_RecordGetInteger( rec, 8 );
00973     if( attributes & msidbControlAttributesIcon )
00974         style |= BS_ICON;
00975 
00976     control = msi_dialog_add_control( dialog, rec, szButton, style );
00977     if( !control )
00978         return ERROR_FUNCTION_FAILED;
00979 
00980     control->handler = msi_dialog_button_handler;
00981 
00982     if (attributes & msidbControlAttributesIcon)
00983     {
00984         /* set the icon */
00985         LPWSTR name = msi_get_binary_name( dialog->package, rec );
00986         control->hIcon = msi_load_icon( dialog->package->db, name, attributes );
00987         if (control->hIcon)
00988         {
00989             SendMessageW( control->hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM) control->hIcon );
00990         }
00991         else
00992             ERR("Failed to load icon %s\n", debugstr_w(name));
00993         msi_free( name );
00994     }
00995 
00996     return ERROR_SUCCESS;
00997 }
00998 
00999 static LPWSTR msi_get_checkbox_value( msi_dialog *dialog, LPCWSTR prop )
01000 {
01001     static const WCHAR query[] = {
01002         'S','E','L','E','C','T',' ','*',' ',
01003         'F','R','O','M',' ','`','C','h','e','c','k','B','o','x','`',' ',
01004         'W','H','E','R','E',' ',
01005         '`','P','r','o','p','e','r','t','y','`',' ','=',' ',
01006         '\'','%','s','\'',0
01007     };
01008     MSIRECORD *rec = NULL;
01009     LPWSTR ret = NULL;
01010 
01011     /* find if there is a value associated with the checkbox */
01012     rec = MSI_QueryGetRecord( dialog->package->db, query, prop );
01013     if (!rec)
01014         return ret;
01015 
01016     ret = msi_get_deformatted_field( dialog->package, rec, 2 );
01017     if( ret && !ret[0] )
01018     {
01019         msi_free( ret );
01020         ret = NULL;
01021     }
01022     msiobj_release( &rec->hdr );
01023     if (ret)
01024         return ret;
01025 
01026     ret = msi_dup_property( dialog->package->db, prop );
01027     if( ret && !ret[0] )
01028     {
01029         msi_free( ret );
01030         ret = NULL;
01031     }
01032 
01033     return ret;
01034 }
01035 
01036 static UINT msi_dialog_get_checkbox_state( msi_dialog *dialog, msi_control *control )
01037 {
01038     WCHAR state[2] = {0};
01039     DWORD sz = 2;
01040 
01041     msi_get_property( dialog->package->db, control->property, state, &sz );
01042     return state[0] ? 1 : 0;
01043 }
01044 
01045 static void msi_dialog_set_checkbox_state( msi_dialog *dialog, msi_control *control, UINT state )
01046 {
01047     static const WCHAR szState[] = {'1',0};
01048     LPCWSTR val;
01049 
01050     /* if uncheck then the property is set to NULL */
01051     if (!state)
01052     {
01053         msi_dialog_set_property( dialog->package, control->property, NULL );
01054         return;
01055     }
01056 
01057     /* check for a custom state */
01058     if (control->value && control->value[0])
01059         val = control->value;
01060     else
01061         val = szState;
01062 
01063     msi_dialog_set_property( dialog->package, control->property, val );
01064 }
01065 
01066 static void msi_dialog_checkbox_sync_state( msi_dialog *dialog, msi_control *control )
01067 {
01068     UINT state = msi_dialog_get_checkbox_state( dialog, control );
01069     SendMessageW( control->hwnd, BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0 );
01070 }
01071 
01072 static UINT msi_dialog_checkbox_handler( msi_dialog *dialog, msi_control *control, WPARAM param )
01073 {
01074     UINT state;
01075 
01076     if (HIWORD(param) != BN_CLICKED)
01077         return ERROR_SUCCESS;
01078 
01079     TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
01080 
01081     state = msi_dialog_get_checkbox_state( dialog, control );
01082     state = state ? 0 : 1;
01083     msi_dialog_set_checkbox_state( dialog, control, state );
01084     msi_dialog_checkbox_sync_state( dialog, control );
01085 
01086     return msi_dialog_button_handler( dialog, control, param );
01087 }
01088 
01089 static UINT msi_dialog_checkbox_control( msi_dialog *dialog, MSIRECORD *rec )
01090 {
01091     msi_control *control;
01092     LPCWSTR prop;
01093 
01094     TRACE("%p %p\n", dialog, rec);
01095 
01096     control = msi_dialog_add_control( dialog, rec, szButton, BS_CHECKBOX | BS_MULTILINE | WS_TABSTOP );
01097     control->handler = msi_dialog_checkbox_handler;
01098     control->update = msi_dialog_checkbox_sync_state;
01099     prop = MSI_RecordGetString( rec, 9 );
01100     if (prop)
01101     {
01102         control->property = strdupW( prop );
01103         control->value = msi_get_checkbox_value( dialog, prop );
01104         TRACE("control %s value %s\n", debugstr_w(control->property), debugstr_w(control->value));
01105     }
01106     msi_dialog_checkbox_sync_state( dialog, control );
01107     return ERROR_SUCCESS;
01108 }
01109 
01110 static UINT msi_dialog_line_control( msi_dialog *dialog, MSIRECORD *rec )
01111 {
01112     DWORD attributes;
01113     LPCWSTR name;
01114     DWORD style, exstyle = 0;
01115     DWORD x, y, width, height;
01116     msi_control *control;
01117 
01118     TRACE("%p %p\n", dialog, rec);
01119 
01120     style = WS_CHILD | SS_ETCHEDHORZ | SS_SUNKEN;
01121 
01122     name = MSI_RecordGetString( rec, 2 );
01123     attributes = MSI_RecordGetInteger( rec, 8 );
01124 
01125     if( attributes & msidbControlAttributesVisible )
01126         style |= WS_VISIBLE;
01127     if( ~attributes & msidbControlAttributesEnabled )
01128         style |= WS_DISABLED;
01129     if( attributes & msidbControlAttributesSunken )
01130         exstyle |= WS_EX_CLIENTEDGE;
01131 
01132     msi_dialog_map_events(dialog, name);
01133 
01134     control = msi_alloc( sizeof(*control) + strlenW(name) * sizeof(WCHAR) );
01135     if (!control)
01136         return ERROR_OUTOFMEMORY;
01137 
01138     strcpyW( control->name, name );
01139     list_add_head( &dialog->controls, &control->entry );
01140     control->handler = NULL;
01141     control->property = NULL;
01142     control->value = NULL;
01143     control->hBitmap = NULL;
01144     control->hIcon = NULL;
01145     control->hDll = NULL;
01146     control->tabnext = strdupW( MSI_RecordGetString( rec, 11) );
01147     control->type = strdupW( MSI_RecordGetString( rec, 3 ) );
01148     control->progress_current = 0;
01149     control->progress_max = 100;
01150     control->progress_backwards = FALSE;
01151 
01152     x = MSI_RecordGetInteger( rec, 4 );
01153     y = MSI_RecordGetInteger( rec, 5 );
01154     width = MSI_RecordGetInteger( rec, 6 );
01155 
01156     x = msi_dialog_scale_unit( dialog, x );
01157     y = msi_dialog_scale_unit( dialog, y );
01158     width = msi_dialog_scale_unit( dialog, width );
01159     height = 2; /* line is exactly 2 units in height */
01160 
01161     control->hwnd = CreateWindowExW( exstyle, szStatic, NULL, style,
01162                           x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
01163 
01164     TRACE("Dialog %s control %s hwnd %p\n",
01165            debugstr_w(dialog->name), debugstr_w(name), control->hwnd );
01166 
01167     return ERROR_SUCCESS;
01168 }
01169 
01170 /******************** Scroll Text ********************************************/
01171 
01172 struct msi_scrolltext_info
01173 {
01174     msi_dialog *dialog;
01175     msi_control *control;
01176     WNDPROC oldproc;
01177 };
01178 
01179 static LRESULT WINAPI
01180 MSIScrollText_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
01181 {
01182     struct msi_scrolltext_info *info;
01183     HRESULT r;
01184 
01185     TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
01186 
01187     info = GetPropW( hWnd, szButtonData );
01188 
01189     r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
01190 
01191     switch( msg )
01192     {
01193     case WM_GETDLGCODE:
01194         return DLGC_WANTARROWS;
01195     case WM_NCDESTROY:
01196         msi_free( info );
01197         RemovePropW( hWnd, szButtonData );
01198         break;
01199     case WM_PAINT:
01200         /* native MSI sets a wait cursor here */
01201         msi_dialog_button_handler( info->dialog, info->control, BN_CLICKED );
01202         break;
01203     }
01204     return r;
01205 }
01206 
01207 struct msi_streamin_info
01208 {
01209     LPSTR string;
01210     DWORD offset;
01211     DWORD length;
01212 };
01213 
01214 static DWORD CALLBACK
01215 msi_richedit_stream_in( DWORD_PTR arg, LPBYTE buffer, LONG count, LONG *pcb )
01216 {
01217     struct msi_streamin_info *info = (struct msi_streamin_info*) arg;
01218 
01219     if( (count + info->offset) > info->length )
01220         count = info->length - info->offset;
01221     memcpy( buffer, &info->string[ info->offset ], count );
01222     *pcb = count;
01223     info->offset += count;
01224 
01225     TRACE("%d/%d\n", info->offset, info->length);
01226 
01227     return 0;
01228 }
01229 
01230 static void msi_scrolltext_add_text( msi_control *control, LPCWSTR text )
01231 {
01232     struct msi_streamin_info info;
01233     EDITSTREAM es;
01234 
01235     info.string = strdupWtoA( text );
01236     info.offset = 0;
01237     info.length = lstrlenA( info.string ) + 1;
01238 
01239     es.dwCookie = (DWORD_PTR) &info;
01240     es.dwError = 0;
01241     es.pfnCallback = msi_richedit_stream_in;
01242 
01243     SendMessageW( control->hwnd, EM_STREAMIN, SF_RTF, (LPARAM) &es );
01244 
01245     msi_free( info.string );
01246 }
01247 
01248 static UINT msi_dialog_scrolltext_control( msi_dialog *dialog, MSIRECORD *rec )
01249 {
01250     static const WCHAR szRichEdit20W[] = {'R','i','c','h','E','d','i','t','2','0','W',0};
01251     struct msi_scrolltext_info *info;
01252     msi_control *control;
01253     HMODULE hRichedit;
01254     LPCWSTR text;
01255     DWORD style;
01256 
01257     info = msi_alloc( sizeof *info );
01258     if (!info)
01259         return ERROR_FUNCTION_FAILED;
01260 
01261     hRichedit = LoadLibraryA("riched20");
01262 
01263     style = WS_BORDER | ES_MULTILINE | WS_VSCROLL |
01264             ES_READONLY | ES_AUTOVSCROLL | WS_TABSTOP;
01265     control = msi_dialog_add_control( dialog, rec, szRichEdit20W, style );
01266     if (!control)
01267     {
01268         FreeLibrary( hRichedit );
01269         msi_free( info );
01270         return ERROR_FUNCTION_FAILED;
01271     }
01272 
01273     control->hDll = hRichedit;
01274 
01275     info->dialog = dialog;
01276     info->control = control;
01277 
01278     /* subclass the static control */
01279     info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
01280                                           (LONG_PTR)MSIScrollText_WndProc );
01281     SetPropW( control->hwnd, szButtonData, info );
01282 
01283     /* add the text into the richedit */
01284     text = MSI_RecordGetString( rec, 10 );
01285     if (text)
01286         msi_scrolltext_add_text( control, text );
01287 
01288     return ERROR_SUCCESS;
01289 }
01290 
01291 static HBITMAP msi_load_picture( MSIDATABASE *db, LPCWSTR name,
01292                                  INT cx, INT cy, DWORD flags )
01293 {
01294     HBITMAP hOleBitmap = 0, hBitmap = 0, hOldSrcBitmap, hOldDestBitmap;
01295     MSIRECORD *rec = NULL;
01296     IStream *stm = NULL;
01297     IPicture *pic = NULL;
01298     HDC srcdc, destdc;
01299     BITMAP bm;
01300     UINT r;
01301 
01302     rec = msi_get_binary_record( db, name );
01303     if( !rec )
01304         goto end;
01305 
01306     r = MSI_RecordGetIStream( rec, 2, &stm );
01307     msiobj_release( &rec->hdr );
01308     if( r != ERROR_SUCCESS )
01309         goto end;
01310 
01311     r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (LPVOID*) &pic );
01312     IStream_Release( stm );
01313     if( FAILED( r ) )
01314     {
01315         ERR("failed to load picture\n");
01316         goto end;
01317     }
01318 
01319     r = IPicture_get_Handle( pic, (OLE_HANDLE*) &hOleBitmap );
01320     if( FAILED( r ) )
01321     {
01322         ERR("failed to get bitmap handle\n");
01323         goto end;
01324     }
01325  
01326     /* make the bitmap the desired size */
01327     r = GetObjectW( hOleBitmap, sizeof bm, &bm );
01328     if (r != sizeof bm )
01329     {
01330         ERR("failed to get bitmap size\n");
01331         goto end;
01332     }
01333 
01334     if (flags & LR_DEFAULTSIZE)
01335     {
01336         cx = bm.bmWidth;
01337         cy = bm.bmHeight;
01338     }
01339 
01340     srcdc = CreateCompatibleDC( NULL );
01341     hOldSrcBitmap = SelectObject( srcdc, hOleBitmap );
01342     destdc = CreateCompatibleDC( NULL );
01343     hBitmap = CreateCompatibleBitmap( srcdc, cx, cy );
01344     hOldDestBitmap = SelectObject( destdc, hBitmap );
01345     StretchBlt( destdc, 0, 0, cx, cy,
01346                 srcdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
01347     SelectObject( srcdc, hOldSrcBitmap );
01348     SelectObject( destdc, hOldDestBitmap );
01349     DeleteDC( srcdc );
01350     DeleteDC( destdc );
01351 
01352 end:
01353     if ( pic )
01354         IPicture_Release( pic );
01355     return hBitmap;
01356 }
01357 
01358 static UINT msi_dialog_bitmap_control( msi_dialog *dialog, MSIRECORD *rec )
01359 {
01360     UINT cx, cy, flags, style, attributes;
01361     msi_control *control;
01362     LPWSTR name;
01363 
01364     flags = LR_LOADFROMFILE;
01365     style = SS_BITMAP | SS_LEFT | WS_GROUP;
01366 
01367     attributes = MSI_RecordGetInteger( rec, 8 );
01368     if( attributes & msidbControlAttributesFixedSize )
01369     {
01370         flags |= LR_DEFAULTSIZE;
01371         style |= SS_CENTERIMAGE;
01372     }
01373 
01374     control = msi_dialog_add_control( dialog, rec, szStatic, style );
01375     cx = MSI_RecordGetInteger( rec, 6 );
01376     cy = MSI_RecordGetInteger( rec, 7 );
01377     cx = msi_dialog_scale_unit( dialog, cx );
01378     cy = msi_dialog_scale_unit( dialog, cy );
01379 
01380     name = msi_get_binary_name( dialog->package, rec );
01381     control->hBitmap = msi_load_picture( dialog->package->db, name, cx, cy, flags );
01382     if( control->hBitmap )
01383         SendMessageW( control->hwnd, STM_SETIMAGE,
01384                       IMAGE_BITMAP, (LPARAM) control->hBitmap );
01385     else
01386         ERR("Failed to load bitmap %s\n", debugstr_w(name));
01387 
01388     msi_free( name );
01389     
01390     return ERROR_SUCCESS;
01391 }
01392 
01393 static UINT msi_dialog_icon_control( msi_dialog *dialog, MSIRECORD *rec )
01394 {
01395     msi_control *control;
01396     DWORD attributes;
01397     LPWSTR name;
01398 
01399     TRACE("\n");
01400 
01401     control = msi_dialog_add_control( dialog, rec, szStatic,
01402                             SS_ICON | SS_CENTERIMAGE | WS_GROUP );
01403             
01404     attributes = MSI_RecordGetInteger( rec, 8 );
01405     name = msi_get_binary_name( dialog->package, rec );
01406     control->hIcon = msi_load_icon( dialog->package->db, name, attributes );
01407     if( control->hIcon )
01408         SendMessageW( control->hwnd, STM_SETICON, (WPARAM) control->hIcon, 0 );
01409     else
01410         ERR("Failed to load bitmap %s\n", debugstr_w(name));
01411     msi_free( name );
01412     return ERROR_SUCCESS;
01413 }
01414 
01415 /******************** Combo Box ***************************************/
01416 
01417 struct msi_combobox_info
01418 {
01419     msi_dialog *dialog;
01420     HWND hwnd;
01421     WNDPROC oldproc;
01422     DWORD num_items;
01423     DWORD addpos_items;
01424     LPWSTR *items;
01425 };
01426 
01427 static LRESULT WINAPI MSIComboBox_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
01428 {
01429     struct msi_combobox_info *info;
01430     LRESULT r;
01431     DWORD j;
01432 
01433     TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
01434 
01435     info = GetPropW( hWnd, szButtonData );
01436     if (!info)
01437         return 0;
01438 
01439     r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
01440 
01441     switch (msg)
01442     {
01443     case WM_NCDESTROY:
01444         for (j = 0; j < info->num_items; j++)
01445             msi_free( info->items[j] );
01446         msi_free( info->items );
01447         msi_free( info );
01448         RemovePropW( hWnd, szButtonData );
01449         break;
01450     }
01451 
01452     return r;
01453 }
01454 
01455 static UINT msi_combobox_add_item( MSIRECORD *rec, LPVOID param )
01456 {
01457     struct msi_combobox_info *info = param;
01458     LPCWSTR value, text;
01459     int pos;
01460 
01461     value = MSI_RecordGetString( rec, 3 );
01462     text = MSI_RecordGetString( rec, 4 );
01463 
01464     info->items[info->addpos_items] = strdupW( value );
01465 
01466     pos = SendMessageW( info->hwnd, CB_ADDSTRING, 0, (LPARAM)text );
01467     SendMessageW( info->hwnd, CB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] );
01468     info->addpos_items++;
01469 
01470     return ERROR_SUCCESS;
01471 }
01472 
01473 static UINT msi_combobox_add_items( struct msi_combobox_info *info, LPCWSTR property )
01474 {
01475     static const WCHAR query[] = {
01476         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
01477         '`','C','o','m','b','o','B','o','x','`',' ','W','H','E','R','E',' ',
01478         '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',' ',
01479         'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','`',0};
01480     MSIQUERY *view;
01481     DWORD count;
01482     UINT r;
01483 
01484     r = MSI_OpenQuery( info->dialog->package->db, &view, query, property );
01485     if (r != ERROR_SUCCESS)
01486         return r;
01487 
01488     /* just get the number of records */
01489     count = 0;
01490     r = MSI_IterateRecords( view, &count, NULL, NULL );
01491     if (r != ERROR_SUCCESS)
01492     {
01493         msiobj_release( &view->hdr );
01494         return r;
01495     }
01496     info->num_items = count;
01497     info->items = msi_alloc( sizeof(*info->items) * count );
01498 
01499     r = MSI_IterateRecords( view, NULL, msi_combobox_add_item, info );
01500     msiobj_release( &view->hdr );
01501     return r;
01502 }
01503 
01504 static UINT msi_dialog_set_control_condition( MSIRECORD *rec, LPVOID param )
01505 {
01506     static const WCHAR szHide[] = {'H','i','d','e',0};
01507     static const WCHAR szShow[] = {'S','h','o','w',0};
01508     static const WCHAR szDisable[] = {'D','i','s','a','b','l','e',0};
01509     static const WCHAR szEnable[] = {'E','n','a','b','l','e',0};
01510     static const WCHAR szDefault[] = {'D','e','f','a','u','l','t',0};
01511     msi_dialog *dialog = param;
01512     msi_control *control;
01513     LPCWSTR name, action, condition;
01514     UINT r;
01515 
01516     name = MSI_RecordGetString( rec, 2 );
01517     action = MSI_RecordGetString( rec, 3 );
01518     condition = MSI_RecordGetString( rec, 4 );
01519     r = MSI_EvaluateConditionW( dialog->package, condition );
01520     control = msi_dialog_find_control( dialog, name );
01521     if (r == MSICONDITION_TRUE && control)
01522     {
01523         TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
01524 
01525         /* FIXME: case sensitive? */
01526         if (!strcmpW( action, szHide ))
01527             ShowWindow(control->hwnd, SW_HIDE);
01528         else if (!strcmpW( action, szShow ))
01529             ShowWindow(control->hwnd, SW_SHOW);
01530         else if (!strcmpW( action, szDisable ))
01531             EnableWindow(control->hwnd, FALSE);
01532         else if (!strcmpW( action, szEnable ))
01533             EnableWindow(control->hwnd, TRUE);
01534         else if (!strcmpW( action, szDefault ))
01535             SetFocus(control->hwnd);
01536         else
01537             FIXME("Unhandled action %s\n", debugstr_w(action));
01538     }
01539     return ERROR_SUCCESS;
01540 }
01541 
01542 static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog )
01543 {
01544     static const WCHAR query[] = {
01545         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
01546         'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
01547         'W','H','E','R','E',' ','`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
01548     UINT r;
01549     MSIQUERY *view;
01550     MSIPACKAGE *package = dialog->package;
01551 
01552     TRACE("%p %s\n", dialog, debugstr_w(dialog->name));
01553 
01554     /* query the Control table for all the elements of the control */
01555     r = MSI_OpenQuery( package->db, &view, query, dialog->name );
01556     if (r != ERROR_SUCCESS)
01557         return ERROR_SUCCESS;
01558 
01559     r = MSI_IterateRecords( view, 0, msi_dialog_set_control_condition, dialog );
01560     msiobj_release( &view->hdr );
01561     return r;
01562 }
01563 
01564 static UINT msi_dialog_combobox_handler( msi_dialog *dialog, msi_control *control, WPARAM param )
01565 {
01566     struct msi_combobox_info *info;
01567     int index;
01568     LPWSTR value;
01569 
01570     if (HIWORD(param) != CBN_SELCHANGE && HIWORD(param) != CBN_EDITCHANGE)
01571         return ERROR_SUCCESS;
01572 
01573     info = GetPropW( control->hwnd, szButtonData );
01574     index = SendMessageW( control->hwnd, CB_GETCURSEL, 0, 0 );
01575     if (index == CB_ERR)
01576         value = msi_get_window_text( control->hwnd );
01577     else
01578         value = (LPWSTR) SendMessageW( control->hwnd, CB_GETITEMDATA, index, 0 );
01579 
01580     msi_dialog_set_property( info->dialog->package, control->property, value );
01581     msi_dialog_evaluate_control_conditions( info->dialog );
01582 
01583     if (index == CB_ERR)
01584         msi_free( value );
01585 
01586     return ERROR_SUCCESS;
01587 }
01588 
01589 static void msi_dialog_combobox_update( msi_dialog *dialog, msi_control *control )
01590 {
01591     struct msi_combobox_info *info;
01592     LPWSTR value, tmp;
01593     DWORD j;
01594 
01595     info = GetPropW( control->hwnd, szButtonData );
01596 
01597     value = msi_dup_property( dialog->package->db, control->property );
01598     if (!value)
01599     {
01600         SendMessageW( control->hwnd, CB_SETCURSEL, -1, 0 );
01601         return;
01602     }
01603 
01604     for (j = 0; j < info->num_items; j++)
01605     {
01606         tmp = (LPWSTR) SendMessageW( control->hwnd, CB_GETITEMDATA, j, 0 );
01607         if (!strcmpW( value, tmp ))
01608             break;
01609     }
01610 
01611     if (j < info->num_items)
01612     {
01613         SendMessageW( control->hwnd, CB_SETCURSEL, j, 0 );
01614     }
01615     else
01616     {
01617         SendMessageW( control->hwnd, CB_SETCURSEL, -1, 0 );
01618         SetWindowTextW( control->hwnd, value );
01619     }
01620 
01621     msi_free(value);
01622 }
01623 
01624 static UINT msi_dialog_combo_control( msi_dialog *dialog, MSIRECORD *rec )
01625 {
01626     struct msi_combobox_info *info;
01627     msi_control *control;
01628     DWORD attributes, style;
01629     LPCWSTR prop;
01630 
01631     info = msi_alloc( sizeof *info );
01632     if (!info)
01633         return ERROR_FUNCTION_FAILED;
01634 
01635     style = CBS_AUTOHSCROLL | WS_TABSTOP | WS_GROUP | WS_CHILD;
01636     attributes = MSI_RecordGetInteger( rec, 8 );
01637     if ( ~attributes & msidbControlAttributesSorted)
01638         style |= CBS_SORT;
01639     if ( attributes & msidbControlAttributesComboList)
01640         style |= CBS_DROPDOWNLIST;
01641     else
01642         style |= CBS_DROPDOWN;
01643 
01644     control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style );
01645     if (!control)
01646     {
01647         msi_free( info );
01648         return ERROR_FUNCTION_FAILED;
01649     }
01650 
01651     control->handler = msi_dialog_combobox_handler;
01652     control->update = msi_dialog_combobox_update;
01653 
01654     prop = MSI_RecordGetString( rec, 9 );
01655     control->property = msi_dialog_dup_property( dialog, prop, FALSE );
01656 
01657     /* subclass */
01658     info->dialog = dialog;
01659     info->hwnd = control->hwnd;
01660     info->items = NULL;
01661     info->addpos_items = 0;
01662     info->oldproc = (WNDPROC)SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
01663                                                 (LONG_PTR)MSIComboBox_WndProc );
01664     SetPropW( control->hwnd, szButtonData, info );
01665 
01666     if (control->property)
01667         msi_combobox_add_items( info, control->property );
01668 
01669     msi_dialog_combobox_update( dialog, control );
01670 
01671     return ERROR_SUCCESS;
01672 }
01673 
01674 static UINT msi_dialog_edit_handler( msi_dialog *dialog, msi_control *control, WPARAM param )
01675 {
01676     LPWSTR buf;
01677 
01678     if (HIWORD(param) != EN_CHANGE)
01679         return ERROR_SUCCESS;
01680 
01681     TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
01682 
01683     buf = msi_get_window_text( control->hwnd );
01684     msi_dialog_set_property( dialog->package, control->property, buf );
01685     msi_free( buf );
01686 
01687     return ERROR_SUCCESS;
01688 }
01689 
01690 /* length of 2^32 + 1 */
01691 #define MAX_NUM_DIGITS 11
01692 
01693 static UINT msi_dialog_edit_control( msi_dialog *dialog, MSIRECORD *rec )
01694 {
01695     msi_control *control;
01696     LPCWSTR prop, text;
01697     LPWSTR val, begin, end;
01698     WCHAR num[MAX_NUM_DIGITS];
01699     DWORD limit;
01700 
01701     control = msi_dialog_add_control( dialog, rec, szEdit,
01702                                       WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL );
01703     control->handler = msi_dialog_edit_handler;
01704 
01705     text = MSI_RecordGetString( rec, 10 );
01706     if ( text )
01707     {
01708         begin = strchrW( text, '{' );
01709         end = strchrW( text, '}' );
01710 
01711         if ( begin && end && end > begin &&
01712              begin[0] >= '0' && begin[0] <= '9' &&
01713              end - begin < MAX_NUM_DIGITS)
01714         {
01715             lstrcpynW( num, begin + 1, end - begin );
01716             limit = atolW( num );
01717 
01718             SendMessageW( control->hwnd, EM_SETLIMITTEXT, limit, 0 );
01719         }
01720     }
01721 
01722     prop = MSI_RecordGetString( rec, 9 );
01723     if( prop )
01724         control->property = strdupW( prop );
01725 
01726     val = msi_dup_property( dialog->package->db, control->property );
01727     SetWindowTextW( control->hwnd, val );
01728     msi_free( val );
01729     return ERROR_SUCCESS;
01730 }
01731 
01732 /******************** Masked Edit ********************************************/
01733 
01734 #define MASK_MAX_GROUPS 20
01735 
01736 struct msi_mask_group
01737 {
01738     UINT len;
01739     UINT ofs;
01740     WCHAR type;
01741     HWND hwnd;
01742 };
01743 
01744 struct msi_maskedit_info
01745 {
01746     msi_dialog *dialog;
01747     WNDPROC oldproc;
01748     HWND hwnd;
01749     LPWSTR prop;
01750     UINT num_chars;
01751     UINT num_groups;
01752     struct msi_mask_group group[MASK_MAX_GROUPS];
01753 };
01754 
01755 static BOOL msi_mask_editable( WCHAR type )
01756 {
01757     switch (type)
01758     {
01759     case '%':
01760     case '#':
01761     case '&':
01762     case '`':
01763     case '?':
01764     case '^':
01765         return TRUE;
01766     }
01767     return FALSE;
01768 }
01769 
01770 static void msi_mask_control_change( struct msi_maskedit_info *info )
01771 {
01772     LPWSTR val;
01773     UINT i, n, r;
01774 
01775     val = msi_alloc( (info->num_chars+1)*sizeof(WCHAR) );
01776     for( i=0, n=0; i<info->num_groups; i++ )
01777     {
01778         if( (info->group[i].len + n) > info->num_chars )
01779         {
01780             ERR("can't fit control %d text into template\n",i);
01781             break;
01782         }
01783         if (!msi_mask_editable(info->group[i].type))
01784         {
01785             for(r=0; r<info->group[i].len; r++)
01786                 val[n+r] = info->group[i].type;
01787             val[n+r] = 0;
01788         }
01789         else
01790         {
01791             r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 );
01792             if( r != info->group[i].len )
01793                 break;
01794         }
01795         n += r;
01796     }
01797 
01798     TRACE("%d/%d controls were good\n", i, info->num_groups);
01799 
01800     if( i == info->num_groups )
01801     {
01802         TRACE("Set property %s to %s\n", debugstr_w(info->prop), debugstr_w(val));
01803         CharUpperBuffW( val, info->num_chars );
01804         msi_dialog_set_property( info->dialog->package, info->prop, val );
01805         msi_dialog_evaluate_control_conditions( info->dialog );
01806     }
01807     msi_free( val );
01808 }
01809 
01810 /* now move to the next control if necessary */
01811 static VOID msi_mask_next_control( struct msi_maskedit_info *info, HWND hWnd )
01812 {
01813     HWND hWndNext;
01814     UINT len, i;
01815 
01816     for( i=0; i<info->num_groups; i++ )
01817         if( info->group[i].hwnd == hWnd )
01818             break;
01819 
01820     /* don't move from the last control */
01821     if( i >= (info->num_groups-1) )
01822         return;
01823 
01824     len = SendMessageW( hWnd, WM_GETTEXTLENGTH, 0, 0 );
01825     if( len < info->group[i].len )
01826         return;
01827 
01828     hWndNext = GetNextDlgTabItem( GetParent( hWnd ), hWnd, FALSE );
01829     SetFocus( hWndNext );
01830 }
01831 
01832 static LRESULT WINAPI
01833 MSIMaskedEdit_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
01834 {
01835     struct msi_maskedit_info *info;
01836     HRESULT r;
01837 
01838     TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
01839 
01840     info = GetPropW(hWnd, szButtonData);
01841 
01842     r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
01843 
01844     switch( msg )
01845     {
01846     case WM_COMMAND:
01847         if (HIWORD(wParam) == EN_CHANGE)
01848         {
01849             msi_mask_control_change( info );
01850             msi_mask_next_control( info, (HWND) lParam );
01851         }
01852         break;
01853     case WM_NCDESTROY:
01854         msi_free( info->prop );
01855         msi_free( info );
01856         RemovePropW( hWnd, szButtonData );
01857         break;
01858     }
01859 
01860     return r;
01861 }
01862 
01863 /* fish the various bits of the property out and put them in the control */
01864 static void
01865 msi_maskedit_set_text( struct msi_maskedit_info *info, LPCWSTR text )
01866 {
01867     LPCWSTR p;
01868     UINT i;
01869 
01870     p = text;
01871     for( i = 0; i < info->num_groups; i++ )
01872     {
01873         if( info->group[i].len < strlenW( p ) )
01874         {
01875             LPWSTR chunk = strdupW( p );
01876             chunk[ info->group[i].len ] = 0;
01877             SetWindowTextW( info->group[i].hwnd, chunk );
01878             msi_free( chunk );
01879         }
01880         else
01881         {
01882             SetWindowTextW( info->group[i].hwnd, p );
01883             break;
01884         }
01885         p += info->group[i].len;
01886     }
01887 }
01888 
01889 static struct msi_maskedit_info * msi_dialog_parse_groups( LPCWSTR mask )
01890 {
01891     struct msi_maskedit_info * info = NULL;
01892     int i = 0, n = 0, total = 0;
01893     LPCWSTR p;
01894 
01895     TRACE("masked control, template %s\n", debugstr_w(mask));
01896 
01897     if( !mask )
01898         return info;
01899 
01900     info = msi_alloc_zero( sizeof *info );
01901     if( !info )
01902         return info;
01903 
01904     p = strchrW(mask, '<');
01905     if( p )
01906         p++;
01907     else
01908         p = mask;
01909 
01910     for( i=0; i<MASK_MAX_GROUPS; i++ )
01911     {
01912         /* stop at the end of the string */
01913         if( p[0] == 0 || p[0] == '>' )
01914             break;
01915 
01916         /* count the number of the same identifier */
01917         for( n=0; p[n] == p[0]; n++ )
01918             ;
01919         info->group[i].ofs = total;
01920         info->group[i].type = p[0];
01921         if( p[n] == '=' )
01922         {
01923             n++;
01924             total++; /* an extra not part of the group */
01925         }
01926         info->group[i].len = n;
01927         total += n;
01928         p += n;
01929     }
01930 
01931     TRACE("%d characters in %d groups\n", total, i );
01932     if( i == MASK_MAX_GROUPS )
01933         ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask));
01934 
01935     info->num_chars = total;
01936     info->num_groups = i;
01937 
01938     return info;
01939 }
01940 
01941 static void
01942 msi_maskedit_create_children( struct msi_maskedit_info *info, LPCWSTR font )
01943 {
01944     DWORD width, height, style, wx, ww;
01945     RECT rect;
01946     HWND hwnd;
01947     UINT i;
01948 
01949     style = WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL;
01950 
01951     GetClientRect( info->hwnd, &rect );
01952 
01953     width = rect.right - rect.left;
01954     height = rect.bottom - rect.top;
01955 
01956     for( i = 0; i < info->num_groups; i++ )
01957     {
01958         if (!msi_mask_editable( info->group[i].type ))
01959             continue;
01960         wx = (info->group[i].ofs * width) / info->num_chars;
01961         ww = (info->group[i].len * width) / info->num_chars;
01962 
01963         hwnd = CreateWindowW( szEdit, NULL, style, wx, 0, ww, height,
01964                               info->hwnd, NULL, NULL, NULL );
01965         if( !hwnd )
01966         {
01967             ERR("failed to create mask edit sub window\n");
01968             break;
01969         }
01970 
01971         SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 );
01972 
01973         msi_dialog_set_font( info->dialog, hwnd,
01974                              font?font:info->dialog->default_font );
01975         info->group[i].hwnd = hwnd;
01976     }
01977 }
01978 
01979 /*
01980  * office 2003 uses "73931<````=````=````=````=`````>@@@@@"
01981  * delphi 7 uses "<????-??????-??????-????>" and "<???-???>"
01982  * filemaker pro 7 uses "<^^^^=^^^^=^^^^=^^^^=^^^^=^^^^=^^^^^>"
01983  */
01984 static UINT msi_dialog_maskedit_control( msi_dialog *dialog, MSIRECORD *rec )
01985 {
01986     LPWSTR font_mask, val = NULL, font;
01987     struct msi_maskedit_info *info = NULL;
01988     UINT ret = ERROR_SUCCESS;
01989     msi_control *control;
01990     LPCWSTR prop, mask;
01991 
01992     TRACE("\n");
01993 
01994     font_mask = msi_get_deformatted_field( dialog->package, rec, 10 );
01995     font = msi_dialog_get_style( font_mask, &mask );
01996     if( !mask )
01997     {
01998         WARN("mask template is empty\n");
01999         goto end;
02000     }
02001 
02002     info = msi_dialog_parse_groups( mask );
02003     if( !info )
02004     {
02005         ERR("template %s is invalid\n", debugstr_w(mask));
02006         goto end;
02007     }
02008 
02009     info->dialog = dialog;
02010 
02011     control = msi_dialog_add_control( dialog, rec, szStatic,
02012                    SS_OWNERDRAW | WS_GROUP | WS_VISIBLE );
02013     if( !control )
02014     {
02015         ERR("Failed to create maskedit container\n");
02016         ret = ERROR_FUNCTION_FAILED;
02017         goto end;
02018     }
02019     SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT );
02020 
02021     info->hwnd = control->hwnd;
02022 
02023     /* subclass the static control */
02024     info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC,
02025                                           (LONG_PTR)MSIMaskedEdit_WndProc );
02026     SetPropW( control->hwnd, szButtonData, info );
02027 
02028     prop = MSI_RecordGetString( rec, 9 );
02029     if( prop )
02030         info->prop = strdupW( prop );
02031 
02032     msi_maskedit_create_children( info, font );
02033 
02034     if( prop )
02035     {
02036         val = msi_dup_property( dialog->package->db, prop );
02037         if( val )
02038         {
02039             msi_maskedit_set_text( info, val );
02040             msi_free( val );
02041         }
02042     }
02043 
02044 end:
02045     if( ret != ERROR_SUCCESS )
02046         msi_free( info );
02047     msi_free( font_mask );
02048     msi_free( font );
02049     return ret;
02050 }
02051 
02052 /******************** Progress Bar *****************************************/
02053 
02054 static UINT msi_dialog_progress_bar( msi_dialog *dialog, MSIRECORD *rec )
02055 {
02056     msi_control *control;
02057     DWORD attributes, style;
02058 
02059     style = WS_VISIBLE;
02060     attributes = MSI_RecordGetInteger( rec, 8 );
02061     if( !(attributes & msidbControlAttributesProgress95) )
02062         style |= PBS_SMOOTH;
02063 
02064     control = msi_dialog_add_control( dialog, rec, PROGRESS_CLASSW, style );
02065     if( !control )
02066         return ERROR_FUNCTION_FAILED;
02067 
02068     ControlEvent_SubscribeToEvent( dialog->package, dialog,
02069                                    szSetProgress, control->name, szProgress );
02070     return ERROR_SUCCESS;
02071 }
02072 
02073 /******************** Path Edit ********************************************/
02074 
02075 struct msi_pathedit_info
02076 {
02077     msi_dialog *dialog;
02078     msi_control *control;
02079     WNDPROC oldproc;
02080 };
02081 
02082 static void msi_dialog_update_pathedit( msi_dialog *dialog, msi_control *control )
02083 {
02084     LPWSTR prop, path;
02085     BOOL indirect;
02086 
02087     if (!control && !(control = msi_dialog_find_control_by_type( dialog, szPathEdit )))
02088        return;
02089 
02090     indirect = control->attributes & msidbControlAttributesIndirect;
02091     prop = msi_dialog_dup_property( dialog, control->property, indirect );
02092     path = msi_dialog_dup_property( dialog, prop, TRUE );
02093 
02094     SetWindowTextW( control->hwnd, path );
02095     SendMessageW( control->hwnd, EM_SETSEL, 0, -1 );
02096 
02097     msi_free( path );
02098     msi_free( prop );
02099 }
02100 
02101 /* FIXME: test when this should fail */
02102 static BOOL msi_dialog_verify_path( LPWSTR path )
02103 {
02104     if ( !lstrlenW( path ) )
02105         return FALSE;
02106 
02107     if ( PathIsRelativeW( path ) )
02108         return FALSE;
02109 
02110     return TRUE;
02111 }
02112 
02113 /* returns TRUE if the path is valid, FALSE otherwise */
02114 static BOOL msi_dialog_onkillfocus( msi_dialog *dialog, msi_control *control )
02115 {
02116     LPWSTR buf, prop;
02117     BOOL indirect;
02118     BOOL valid;
02119 
02120     indirect = control->attributes & msidbControlAttributesIndirect;
02121     prop = msi_dialog_dup_property( dialog, control->property, indirect );
02122 
02123     buf = msi_get_window_text( control->hwnd );
02124 
02125     if ( !msi_dialog_verify_path( buf ) )
02126     {
02127         /* FIXME: display an error message box */
02128         ERR("Invalid path %s\n", debugstr_w( buf ));
02129         valid = FALSE;
02130         SetFocus( control->hwnd );
02131     }
02132     else
02133     {
02134         valid = TRUE;
02135         msi_dialog_set_property( dialog->package, prop, buf );
02136     }
02137 
02138     msi_dialog_update_pathedit( dialog, control );
02139 
02140     TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name),
02141           debugstr_w(prop));
02142 
02143     msi_free( buf );
02144     msi_free( prop );
02145 
02146     return valid;
02147 }
02148 
02149 static LRESULT WINAPI MSIPathEdit_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
02150 {
02151     struct msi_pathedit_info *info = GetPropW(hWnd, szButtonData);
02152     LRESULT r = 0;
02153 
02154     TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
02155 
02156     if ( msg == WM_KILLFOCUS )
02157     {
02158         /* if the path is invalid, don't handle this message */
02159         if ( !msi_dialog_onkillfocus( info->dialog, info->control ) )
02160             return 0;
02161     }
02162 
02163     r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
02164 
02165     if ( msg == WM_NCDESTROY )
02166     {
02167         msi_free( info );
02168         RemovePropW( hWnd, szButtonData );
02169     }
02170 
02171     return r;
02172 }
02173 
02174 static UINT msi_dialog_pathedit_control( msi_dialog *dialog, MSIRECORD *rec )
02175 {
02176     struct msi_pathedit_info *info;
02177     msi_control *control;
02178     LPCWSTR prop;
02179 
02180     info = msi_alloc( sizeof *info );
02181     if (!info)
02182         return ERROR_FUNCTION_FAILED;
02183 
02184     control = msi_dialog_add_control( dialog, rec, szEdit,
02185                                       WS_BORDER | WS_TABSTOP );
02186     control->attributes = MSI_RecordGetInteger( rec, 8 );
02187     prop = MSI_RecordGetString( rec, 9 );
02188     control->property = msi_dialog_dup_property( dialog, prop, FALSE );
02189 
02190     info->dialog = dialog;
02191     info->control = control;
02192     info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
02193                                                  (LONG_PTR)MSIPathEdit_WndProc );
02194     SetPropW( control->hwnd, szButtonData, info );
02195 
02196     msi_dialog_update_pathedit( dialog, control );
02197 
02198     return ERROR_SUCCESS;
02199 }
02200 
02201 static UINT msi_dialog_radiogroup_handler( msi_dialog *dialog, msi_control *control, WPARAM param )
02202 {
02203     if (HIWORD(param) != BN_CLICKED)
02204         return ERROR_SUCCESS;
02205 
02206     TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
02207 
02208     msi_dialog_set_property( dialog->package, control->property, control->name );
02209 
02210     return msi_dialog_button_handler( dialog, control, param );
02211 }
02212 
02213 /* radio buttons are a bit different from normal controls */
02214 static UINT msi_dialog_create_radiobutton( MSIRECORD *rec, LPVOID param )
02215 {
02216     radio_button_group_descr *group = param;
02217     msi_dialog *dialog = group->dialog;
02218     msi_control *control;
02219     LPCWSTR prop, text, name;
02220     DWORD style, attributes = group->attributes;
02221 
02222     style = WS_CHILD | BS_AUTORADIOBUTTON | BS_MULTILINE | WS_TABSTOP;
02223     name = MSI_RecordGetString( rec, 3 );
02224     text = MSI_RecordGetString( rec, 8 );
02225     if( attributes & msidbControlAttributesVisible )
02226         style |= WS_VISIBLE;
02227     if( ~attributes & msidbControlAttributesEnabled )
02228         style |= WS_DISABLED;
02229 
02230     control = msi_dialog_create_window( dialog, rec, 0, szButton, name, text,
02231                                         style, group->parent->hwnd );
02232     if (!control)
02233         return ERROR_FUNCTION_FAILED;
02234     control->handler = msi_dialog_radiogroup_handler;
02235 
02236     if (group->propval && !strcmpW( control->name, group->propval ))
02237         SendMessageW(control->hwnd, BM_SETCHECK, BST_CHECKED, 0);
02238 
02239     prop = MSI_RecordGetString( rec, 1 );
02240     if( prop )
02241         control->property = strdupW( prop );
02242 
02243     return ERROR_SUCCESS;
02244 }
02245 
02246 static BOOL CALLBACK msi_radioground_child_enum( HWND hWnd, LPARAM lParam )
02247 {
02248     EnableWindow( hWnd, lParam );
02249     return TRUE;
02250 }
02251 
02252 static LRESULT WINAPI MSIRadioGroup_WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
02253 {
02254     WNDPROC oldproc = (WNDPROC)GetPropW( hWnd, szButtonData );
02255     LRESULT r;
02256 
02257     TRACE("hWnd %p msg %04x wParam 0x%08lx lParam 0x%08lx\n", hWnd, msg, wParam, lParam);
02258 
02259     if (msg == WM_COMMAND) /* Forward notifications to dialog */
02260         SendMessageW( GetParent( hWnd ), msg, wParam, lParam );
02261 
02262     r = CallWindowProcW( oldproc, hWnd, msg, wParam, lParam );
02263 
02264     /* make sure the radio buttons show as disabled if the parent is disabled */
02265     if (msg == WM_ENABLE)
02266         EnumChildWindows( hWnd, msi_radioground_child_enum, wParam );
02267 
02268     return r;
02269 }
02270 
02271 static UINT msi_dialog_radiogroup_control( msi_dialog *dialog, MSIRECORD *rec )
02272 {
02273     static const WCHAR query[] = {
02274         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
02275         'R','a','d','i','o','B','u','t','t','o','n',' ','W','H','E','R','E',' ',
02276         '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
02277     UINT r;
02278     LPCWSTR prop;
02279     msi_control *control;
02280     MSIQUERY *view;
02281     radio_button_group_descr group;
02282     MSIPACKAGE *package = dialog->package;
02283     WNDPROC oldproc;
02284     DWORD attr, style = WS_GROUP;
02285 
02286     prop = MSI_RecordGetString( rec, 9 );
02287 
02288     TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop ));
02289 
02290     attr = MSI_RecordGetInteger( rec, 8 );
02291     if (attr & msidbControlAttributesHasBorder)
02292         style |= BS_GROUPBOX;
02293     else
02294         style |= BS_OWNERDRAW;
02295 
02296     /* Create parent group box to hold radio buttons */
02297     control = msi_dialog_add_control( dialog, rec, szButton, style );
02298     if( !control )
02299         return ERROR_FUNCTION_FAILED;
02300 
02301     oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
02302                                            (LONG_PTR)MSIRadioGroup_WndProc );
02303     SetPropW(control->hwnd, szButtonData, oldproc);
02304     SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT );
02305 
02306     if( prop )
02307         control->property = strdupW( prop );
02308 
02309     /* query the Radio Button table for all control in this group */
02310     r = MSI_OpenQuery( package->db, &view, query, prop );
02311     if( r != ERROR_SUCCESS )
02312     {
02313         ERR("query failed for dialog %s radio group %s\n", 
02314             debugstr_w(dialog->name), debugstr_w(prop));
02315         return ERROR_INVALID_PARAMETER;
02316     }
02317 
02318     group.dialog = dialog;
02319     group.parent = control;
02320     group.attributes = MSI_RecordGetInteger( rec, 8 );
02321     group.propval = msi_dup_property( dialog->package->db, control->property );
02322 
02323     r = MSI_IterateRecords( view, 0, msi_dialog_create_radiobutton, &group );
02324     msiobj_release( &view->hdr );
02325     msi_free( group.propval );
02326     return r;
02327 }
02328 
02329 static void
02330 msi_seltree_sync_item_state( HWND hwnd, MSIFEATURE *feature, HTREEITEM hItem )
02331 {
02332     TVITEMW tvi;
02333     DWORD index = feature->ActionRequest;
02334 
02335     TRACE("Feature %s -> %d %d %d\n", debugstr_w(feature->Title),
02336         feature->Installed, feature->Action, feature->ActionRequest);
02337 
02338     if (index == INSTALLSTATE_UNKNOWN)
02339         index = INSTALLSTATE_ABSENT;
02340 
02341     tvi.mask = TVIF_STATE;
02342     tvi.hItem = hItem;
02343     tvi.state = INDEXTOSTATEIMAGEMASK( index );
02344     tvi.stateMask = TVIS_STATEIMAGEMASK;
02345 
02346     SendMessageW( hwnd, TVM_SETITEMW, 0, (LPARAM) &tvi );
02347 }
02348 
02349 static UINT
02350 msi_seltree_popup_menu( HWND hwnd, INT x, INT y )
02351 {
02352     HMENU hMenu;
02353     INT r;
02354 
02355     /* create a menu to display */
02356     hMenu = CreatePopupMenu();
02357 
02358     /* FIXME: load strings from resources */
02359     AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_LOCAL, "Install feature locally");
02360     AppendMenuA( hMenu, MF_ENABLED, USER_INSTALLSTATE_ALL, "Install entire feature");
02361     AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ADVERTISED, "Install on demand");
02362     AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ABSENT, "Don't install");
02363     r = TrackPopupMenu( hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD,
02364                         x, y, 0, hwnd, NULL );
02365     DestroyMenu( hMenu );
02366     return r;
02367 }
02368 
02369 static void
02370 msi_seltree_update_feature_installstate( HWND hwnd, HTREEITEM hItem,
02371         MSIPACKAGE *package, MSIFEATURE *feature, INSTALLSTATE state )
02372 {
02373     feature->ActionRequest = state;
02374     msi_seltree_sync_item_state( hwnd, feature, hItem );
02375     ACTION_UpdateComponentStates( package, feature );
02376 }
02377 
02378 static void
02379 msi_seltree_update_siblings_and_children_installstate( HWND hwnd, HTREEITEM curr,
02380         MSIPACKAGE *package, INSTALLSTATE state)
02381 {
02382     /* update all siblings */
02383     do
02384     {
02385         MSIFEATURE *feature;
02386         HTREEITEM child;
02387 
02388         feature = msi_seltree_feature_from_item( hwnd, curr );
02389         msi_seltree_update_feature_installstate( hwnd, curr, package, feature, state );
02390 
02391         /* update this sibling's children */
02392         child = (HTREEITEM)SendMessageW( hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)curr );
02393         if (child)
02394             msi_seltree_update_siblings_and_children_installstate( hwnd, child,
02395                     package, state );
02396     }
02397     while ((curr = (HTREEITEM)SendMessageW( hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)curr )));
02398 }
02399 
02400 static LRESULT
02401 msi_seltree_menu( HWND hwnd, HTREEITEM hItem )
02402 {
02403     struct msi_selection_tree_info *info;
02404     MSIFEATURE *feature;
02405     MSIPACKAGE *package;
02406     union {
02407         RECT rc;
02408         POINT pt[2];
02409         HTREEITEM hItem;
02410     } u;
02411     UINT r;
02412 
02413     info = GetPropW(hwnd, szButtonData);
02414     package = info->dialog->package;
02415 
02416     feature = msi_seltree_feature_from_item( hwnd, hItem );
02417     if (!feature)
02418     {
02419         ERR("item %p feature was NULL\n", hItem);
02420         return 0;
02421     }
02422 
02423     /* get the item's rectangle to put the menu just below it */
02424     u.hItem = hItem;
02425     SendMessageW( hwnd, TVM_GETITEMRECT, 0, (LPARAM) &u.rc );
02426     MapWindowPoints( hwnd, NULL, u.pt, 2 );
02427 
02428     r = msi_seltree_popup_menu( hwnd, u.rc.left, u.rc.top );
02429 
02430     switch (r)
02431     {
02432     case USER_INSTALLSTATE_ALL:
02433         r = INSTALLSTATE_LOCAL;
02434         /* fall-through */
02435     case INSTALLSTATE_ADVERTISED:
02436     case INSTALLSTATE_ABSENT:
02437         {
02438             HTREEITEM child;
02439             child = (HTREEITEM)SendMessageW( hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)hItem );
02440             if (child)
02441                 msi_seltree_update_siblings_and_children_installstate( hwnd, child, package, r );
02442         }
02443         /* fall-through */
02444     case INSTALLSTATE_LOCAL:
02445         msi_seltree_update_feature_installstate( hwnd, hItem, package, feature, r );
02446         break;
02447     }
02448 
02449     return 0;
02450 }
02451 
02452 static LRESULT WINAPI
02453 MSISelectionTree_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
02454 {
02455     struct msi_selection_tree_info *info;
02456     TVHITTESTINFO tvhti;
02457     HRESULT r;
02458 
02459     TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
02460 
02461     info = GetPropW(hWnd, szButtonData);
02462 
02463     switch( msg )
02464     {
02465     case WM_LBUTTONDOWN:
02466         tvhti.pt.x = (short)LOWORD( lParam );
02467         tvhti.pt.y = (short)HIWORD( lParam );
02468         tvhti.flags = 0;
02469         tvhti.hItem = 0;
02470         CallWindowProcW(info->oldproc, hWnd, TVM_HITTEST, 0, (LPARAM) &tvhti );
02471         if (tvhti.flags & TVHT_ONITEMSTATEICON)
02472             return msi_seltree_menu( hWnd, tvhti.hItem );
02473         break;
02474     }
02475     r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
02476 
02477     switch( msg )
02478     {
02479     case WM_NCDESTROY:
02480         msi_free( info );
02481         RemovePropW( hWnd, szButtonData );
02482         break;
02483     }
02484     return r;
02485 }
02486 
02487 static void
02488 msi_seltree_add_child_features( MSIPACKAGE *package, HWND hwnd,
02489                                 LPCWSTR parent, HTREEITEM hParent )
02490 {
02491     struct msi_selection_tree_info *info = GetPropW( hwnd, szButtonData );
02492     MSIFEATURE *feature;
02493     TVINSERTSTRUCTW tvis;
02494     HTREEITEM hitem, hfirst = NULL;
02495 
02496     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
02497     {
02498         if ( parent && feature->Feature_Parent && strcmpW( parent, feature->Feature_Parent ))
02499             continue;
02500         else if ( parent && !feature->Feature_Parent )
02501             continue;
02502         else if ( !parent && feature->Feature_Parent )
02503             continue;
02504 
02505         if ( !feature->Title )
02506             continue;
02507 
02508         if ( !feature->Display )
02509             continue;
02510 
02511         memset( &tvis, 0, sizeof tvis );
02512         tvis.hParent = hParent;
02513         tvis.hInsertAfter = TVI_LAST;
02514         tvis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
02515         tvis.u.item.pszText = feature->Title;
02516         tvis.u.item.lParam = (LPARAM) feature;
02517 
02518         hitem = (HTREEITEM) SendMessageW( hwnd, TVM_INSERTITEMW, 0, (LPARAM) &tvis );
02519         if (!hitem)
02520             continue;
02521 
02522         if (!hfirst)
02523             hfirst = hitem;
02524 
02525         msi_seltree_sync_item_state( hwnd, feature, hitem );
02526         msi_seltree_add_child_features( package, hwnd,
02527                                         feature->Feature, hitem );
02528 
02529         /* the node is expanded if Display is odd */
02530         if ( feature->Display % 2 != 0 )
02531             SendMessageW( hwnd, TVM_EXPAND, TVE_EXPAND, (LPARAM) hitem );
02532     }
02533 
02534     /* select the first item */
02535     SendMessageW( hwnd, TVM_SELECTITEM, TVGN_CARET | TVGN_DROPHILITE, (LPARAM) hfirst );
02536     info->selected = hfirst;
02537 }
02538 
02539 static void msi_seltree_create_imagelist( HWND hwnd )
02540 {
02541     const int bm_width = 32, bm_height = 16, bm_count = 3;
02542     const int bm_resource = 0x1001;
02543     HIMAGELIST himl;
02544     int i;
02545     HBITMAP hbmp;
02546 
02547     himl = ImageList_Create( bm_width, bm_height, FALSE, 4, 0 );
02548     if (!himl)
02549     {
02550         ERR("failed to create image list\n");
02551         return;
02552     }
02553 
02554     for (i=0; i<bm_count; i++)
02555     {
02556         hbmp = LoadBitmapW( msi_hInstance, MAKEINTRESOURCEW(i+bm_resource) );
02557         if (!hbmp)
02558         {
02559             ERR("failed to load bitmap %d\n", i);
02560             break;
02561         }
02562 
02563         /*
02564          * Add a dummy bitmap at offset zero because the treeview
02565          * can't use it as a state mask (zero means no user state).
02566          */
02567         if (!i)
02568             ImageList_Add( himl, hbmp, NULL );
02569 
02570         ImageList_Add( himl, hbmp, NULL );
02571     }
02572 
02573     SendMessageW( hwnd, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)himl );
02574 }
02575 
02576 static UINT msi_dialog_seltree_handler( msi_dialog *dialog,
02577                                         msi_control *control, WPARAM param )
02578 {
02579     struct msi_selection_tree_info *info = GetPropW( control->hwnd, szButtonData );
02580     LPNMTREEVIEWW tv = (LPNMTREEVIEWW)param;
02581     MSIRECORD *row, *rec;
02582     MSIFOLDER *folder;
02583     MSIFEATURE *feature;
02584     LPCWSTR dir, title = NULL;
02585     UINT r = ERROR_SUCCESS;
02586 
02587     static const WCHAR select[] = {
02588         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
02589         '`','F','e','a','t','u','r','e','`',' ','W','H','E','R','E',' ',
02590         '`','T','i','t','l','e','`',' ','=',' ','\'','%','s','\'',0
02591     };
02592 
02593     if (tv->hdr.code != TVN_SELCHANGINGW)
02594         return ERROR_SUCCESS;
02595 
02596     info->selected = tv->itemNew.hItem;
02597 
02598     if (!(tv->itemNew.mask & TVIF_TEXT))
02599     {
02600         feature = msi_seltree_feature_from_item( control->hwnd, tv->itemNew.hItem );
02601         if (feature)
02602             title = feature->Title;
02603     }
02604     else
02605         title = tv->itemNew.pszText;
02606 
02607     row = MSI_QueryGetRecord( dialog->package->db, select, title );
02608     if (!row)
02609         return ERROR_FUNCTION_FAILED;
02610 
02611     rec = MSI_CreateRecord( 1 );
02612 
02613     MSI_RecordSetStringW( rec, 1, MSI_RecordGetString( row, 4 ) );
02614     ControlEvent_FireSubscribedEvent( dialog->package, szSelectionDescription, rec );
02615 
02616     dir = MSI_RecordGetString( row, 7 );
02617     if (dir)
02618     {
02619         folder = msi_get_loaded_folder( dialog->package, dir );
02620         if (!folder)
02621         {
02622             r = ERROR_FUNCTION_FAILED;
02623             goto done;
02624         }
02625         MSI_RecordSetStringW( rec, 1, folder->ResolvedTarget );
02626     }
02627     else
02628         MSI_RecordSetStringW( rec, 1, NULL );
02629 
02630     ControlEvent_FireSubscribedEvent( dialog->package, szSelectionPath, rec );
02631 
02632 done:
02633     msiobj_release(&row->hdr);
02634     msiobj_release(&rec->hdr);
02635 
02636     return r;
02637 }
02638 
02639 static UINT msi_dialog_selection_tree( msi_dialog *dialog, MSIRECORD *rec )
02640 {
02641     msi_control *control;
02642     LPCWSTR prop, control_name;
02643     MSIPACKAGE *package = dialog->package;
02644     DWORD style;
02645     struct msi_selection_tree_info *info;
02646 
02647     info = msi_alloc( sizeof *info );
02648     if (!info)
02649         return ERROR_FUNCTION_FAILED;
02650 
02651     /* create the treeview control */
02652     style = TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT;
02653     style |= WS_GROUP | WS_VSCROLL;
02654     control = msi_dialog_add_control( dialog, rec, WC_TREEVIEWW, style );
02655     if (!control)
02656     {
02657         msi_free(info);
02658         return ERROR_FUNCTION_FAILED;
02659     }
02660 
02661     control->handler = msi_dialog_seltree_handler;
02662     control_name = MSI_RecordGetString( rec, 2 );
02663     control->attributes = MSI_RecordGetInteger( rec, 8 );
02664     prop = MSI_RecordGetString( rec, 9 );
02665     control->property = msi_dialog_dup_property( dialog, prop, FALSE );
02666 
02667     /* subclass */
02668     info->dialog = dialog;
02669     info->hwnd = control->hwnd;
02670     info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
02671                                           (LONG_PTR)MSISelectionTree_WndProc );
02672     SetPropW( control->hwnd, szButtonData, info );
02673 
02674     ControlEvent_SubscribeToEvent( dialog->package, dialog,
02675                                    szSelectionPath, control_name, szProperty );
02676 
02677     /* initialize it */
02678     msi_seltree_create_imagelist( control->hwnd );
02679     msi_seltree_add_child_features( package, control->hwnd, NULL, NULL );
02680 
02681     return ERROR_SUCCESS;
02682 }
02683 
02684 /******************** Group Box ***************************************/
02685 
02686 static UINT msi_dialog_group_box( msi_dialog *dialog, MSIRECORD *rec )
02687 {
02688     msi_control *control;
02689     DWORD style;
02690 
02691     style = BS_GROUPBOX | WS_CHILD | WS_GROUP;
02692     control = msi_dialog_add_control( dialog, rec, WC_BUTTONW, style );
02693     if (!control)
02694         return ERROR_FUNCTION_FAILED;
02695 
02696     return ERROR_SUCCESS;
02697 }
02698 
02699 /******************** List Box ***************************************/
02700 
02701 struct msi_listbox_info
02702 {
02703     msi_dialog *dialog;
02704     HWND hwnd;
02705     WNDPROC oldproc;
02706     DWORD num_items;
02707     DWORD addpos_items;
02708     LPWSTR *items;
02709 };
02710 
02711 static LRESULT WINAPI MSIListBox_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
02712 {
02713     struct msi_listbox_info *info;
02714     LRESULT r;
02715     DWORD j;
02716 
02717     TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam);
02718 
02719     info = GetPropW( hWnd, szButtonData );
02720     if (!info)
02721         return 0;
02722 
02723     r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
02724 
02725     switch( msg )
02726     {
02727     case WM_NCDESTROY:
02728         for (j = 0; j < info->num_items; j++)
02729             msi_free( info->items[j] );
02730         msi_free( info->items );
02731         msi_free( info );
02732         RemovePropW( hWnd, szButtonData );
02733         break;
02734     }
02735 
02736     return r;
02737 }
02738 
02739 static UINT msi_listbox_add_item( MSIRECORD *rec, LPVOID param )
02740 {
02741     struct msi_listbox_info *info = param;
02742     LPCWSTR value, text;
02743     int pos;
02744 
02745     value = MSI_RecordGetString( rec, 3 );
02746     text = MSI_RecordGetString( rec, 4 );
02747 
02748     info->items[info->addpos_items] = strdupW( value );
02749 
02750     pos = SendMessageW( info->hwnd, LB_ADDSTRING, 0, (LPARAM)text );
02751     SendMessageW( info->hwnd, LB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] );
02752     info->addpos_items++;
02753     return ERROR_SUCCESS;
02754 }
02755 
02756 static UINT msi_listbox_add_items( struct msi_listbox_info *info, LPCWSTR property )
02757 {
02758     static const WCHAR query[] = {
02759         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
02760         '`','L','i','s','t','B','o','x','`',' ','W','H','E','R','E',' ',
02761         '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',' ',
02762         'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','`',0};
02763     MSIQUERY *view;
02764     DWORD count;
02765     UINT r;
02766 
02767     r = MSI_OpenQuery( info->dialog->package->db, &view, query, property );
02768     if ( r != ERROR_SUCCESS )
02769         return r;
02770 
02771     /* just get the number of records */
02772     count = 0;
02773     r = MSI_IterateRecords( view, &count, NULL, NULL );
02774     if (r != ERROR_SUCCESS)
02775     {
02776         msiobj_release( &view->hdr );
02777         return r;
02778     }
02779     info->num_items = count;
02780     info->items = msi_alloc( sizeof(*info->items) * count );
02781 
02782     r = MSI_IterateRecords( view, NULL, msi_listbox_add_item, info );
02783     msiobj_release( &view->hdr );
02784     return r;
02785 }
02786 
02787 static UINT msi_dialog_listbox_handler( msi_dialog *dialog,
02788                                         msi_control *control, WPARAM param )
02789 {
02790     struct msi_listbox_info *info;
02791     int index;
02792     LPCWSTR value;
02793 
02794     if( HIWORD(param) != LBN_SELCHANGE )
02795         return ERROR_SUCCESS;
02796 
02797     info = GetPropW( control->hwnd, szButtonData );
02798     index = SendMessageW( control->hwnd, LB_GETCURSEL, 0, 0 );
02799     value = (LPCWSTR) SendMessageW( control->hwnd, LB_GETITEMDATA, index, 0 );
02800 
02801     msi_dialog_set_property( info->dialog->package, control->property, value );
02802     msi_dialog_evaluate_control_conditions( info->dialog );
02803 
02804     return ERROR_SUCCESS;
02805 }
02806 
02807 static UINT msi_dialog_list_box( msi_dialog *dialog, MSIRECORD *rec )
02808 {
02809     struct msi_listbox_info *info;
02810     msi_control *control;
02811     DWORD attributes, style;
02812     LPCWSTR prop;
02813 
02814     info = msi_alloc( sizeof *info );
02815     if (!info)
02816         return ERROR_FUNCTION_FAILED;
02817 
02818     style = WS_TABSTOP | WS_GROUP | WS_CHILD | LBS_NOTIFY | WS_VSCROLL | WS_BORDER;
02819     attributes = MSI_RecordGetInteger( rec, 8 );
02820     if (~attributes & msidbControlAttributesSorted)
02821         style |= LBS_SORT;
02822 
02823     control = msi_dialog_add_control( dialog, rec, WC_LISTBOXW, style );
02824     if (!control)
02825     {
02826         msi_free(info);
02827         return ERROR_FUNCTION_FAILED;
02828     }
02829 
02830     control->handler = msi_dialog_listbox_handler;
02831 
02832     prop = MSI_RecordGetString( rec, 9 );
02833     control->property = msi_dialog_dup_property( dialog, prop, FALSE );
02834 
02835     /* subclass */
02836     info->dialog = dialog;
02837     info->hwnd = control->hwnd;
02838     info->items = NULL;
02839     info->addpos_items = 0;
02840     info->oldproc = (WNDPROC)SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
02841                                                 (LONG_PTR)MSIListBox_WndProc );
02842     SetPropW( control->hwnd, szButtonData, info );
02843 
02844     if ( control->property )
02845         msi_listbox_add_items( info, control->property );
02846 
02847     return ERROR_SUCCESS;
02848 }
02849 
02850 /******************** Directory Combo ***************************************/
02851 
02852 static void msi_dialog_update_directory_combo( msi_dialog *dialog, msi_control *control )
02853 {
02854     LPWSTR prop, path;
02855     BOOL indirect;
02856 
02857     if (!control && !(control = msi_dialog_find_control_by_type( dialog, szDirectoryCombo )))
02858         return;
02859 
02860     indirect = control->attributes & msidbControlAttributesIndirect;
02861     prop = msi_dialog_dup_property( dialog, control->property, indirect );
02862     path = msi_dialog_dup_property( dialog, prop, TRUE );
02863 
02864     PathStripPathW( path );
02865     PathRemoveBackslashW( path );
02866 
02867     SendMessageW( control->hwnd, CB_INSERTSTRING, 0, (LPARAM)path );
02868     SendMessageW( control->hwnd, CB_SETCURSEL, 0, 0 );
02869 
02870     msi_free( path );
02871     msi_free( prop );
02872 }
02873 
02874 static UINT msi_dialog_directory_combo( msi_dialog *dialog, MSIRECORD *rec )
02875 {
02876     msi_control *control;
02877     LPCWSTR prop;
02878     DWORD style;
02879 
02880     /* FIXME: use CBS_OWNERDRAWFIXED and add owner draw code */
02881     style = CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD |
02882             WS_GROUP | WS_TABSTOP | WS_VSCROLL;
02883     control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style );
02884     if (!control)
02885         return ERROR_FUNCTION_FAILED;
02886 
02887     control->attributes = MSI_RecordGetInteger( rec, 8 );
02888     prop = MSI_RecordGetString( rec, 9 );
02889     control->property = msi_dialog_dup_property( dialog, prop, FALSE );
02890 
02891     msi_dialog_update_directory_combo( dialog, control );
02892 
02893     return ERROR_SUCCESS;
02894 }
02895 
02896 /******************** Directory List ***************************************/
02897 
02898 static void msi_dialog_update_directory_list( msi_dialog *dialog, msi_control *control )
02899 {
02900     WCHAR dir_spec[MAX_PATH];
02901     WIN32_FIND_DATAW wfd;
02902     LPWSTR prop, path;
02903     BOOL indirect;
02904     LVITEMW item;
02905     HANDLE file;
02906 
02907     static const WCHAR asterisk[] = {'*',0};
02908 
02909     if (!control && !(control = msi_dialog_find_control_by_type( dialog, szDirectoryList )))
02910         return;
02911 
02912     /* clear the list-view */
02913     SendMessageW( control->hwnd, LVM_DELETEALLITEMS, 0, 0 );
02914 
02915     indirect = control->attributes & msidbControlAttributesIndirect;
02916     prop = msi_dialog_dup_property( dialog, control->property, indirect );
02917     path = msi_dialog_dup_property( dialog, prop, TRUE );
02918 
02919     lstrcpyW( dir_spec, path );
02920     lstrcatW( dir_spec, asterisk );
02921 
02922     file = FindFirstFileW( dir_spec, &wfd );
02923     if ( file == INVALID_HANDLE_VALUE )
02924         return;
02925 
02926     do
02927     {
02928         if ( wfd.dwFileAttributes != FILE_ATTRIBUTE_DIRECTORY )
02929             continue;
02930 
02931         if ( !strcmpW( wfd.cFileName, szDot ) || !strcmpW( wfd.cFileName, szDotDot ) )
02932             continue;
02933 
02934         item.mask = LVIF_TEXT;
02935         item.cchTextMax = MAX_PATH;
02936         item.iItem = 0;
02937         item.iSubItem = 0;
02938         item.pszText = wfd.cFileName;
02939 
02940         SendMessageW( control->hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item );
02941     } while ( FindNextFileW( file, &wfd ) );
02942 
02943     msi_free( prop );
02944     msi_free( path );
02945     FindClose( file );
02946 }
02947 
02948 UINT msi_dialog_directorylist_up( msi_dialog *dialog )
02949 {
02950     msi_control *control;
02951     LPWSTR prop, path, ptr;
02952     BOOL indirect;
02953 
02954     control = msi_dialog_find_control_by_type( dialog, szDirectoryList );
02955     indirect = control->attributes & msidbControlAttributesIndirect;
02956     prop = msi_dialog_dup_property( dialog, control->property, indirect );
02957     path = msi_dialog_dup_property( dialog, prop, TRUE );
02958 
02959     /* strip off the last directory */
02960     ptr = PathFindFileNameW( path );
02961     if (ptr != path) *(ptr - 1) = '\0';
02962     PathAddBackslashW( path );
02963 
02964     msi_dialog_set_property( dialog->package, prop, path );
02965 
02966     msi_dialog_update_directory_list( dialog, NULL );
02967     msi_dialog_update_directory_combo( dialog, NULL );
02968     msi_dialog_update_pathedit( dialog, NULL );
02969 
02970     msi_free( path );
02971     msi_free( prop );
02972 
02973     return ERROR_SUCCESS;
02974 }
02975 
02976 static UINT msi_dialog_dirlist_handler( msi_dialog *dialog,
02977                                         msi_control *control, WPARAM param )
02978 {
02979     LPNMHDR nmhdr = (LPNMHDR)param;
02980     WCHAR new_path[MAX_PATH];
02981     WCHAR text[MAX_PATH];
02982     LPWSTR path, prop;
02983     BOOL indirect;
02984     LVITEMW item;
02985     int index;
02986 
02987     if (nmhdr->code != LVN_ITEMACTIVATE)
02988         return ERROR_SUCCESS;
02989 
02990     index = SendMessageW( control->hwnd, LVM_GETNEXTITEM, -1, LVNI_SELECTED );
02991     if ( index < 0 )
02992     {
02993         ERR("No list-view item selected!\n");
02994         return ERROR_FUNCTION_FAILED;
02995     }
02996 
02997     item.iSubItem = 0;
02998     item.pszText = text;
02999     item.cchTextMax = MAX_PATH;
03000     SendMessageW( control->hwnd, LVM_GETITEMTEXTW, index, (LPARAM)&item );
03001 
03002     indirect = control->attributes & msidbControlAttributesIndirect;
03003     prop = msi_dialog_dup_property( dialog, control->property, indirect );
03004     path = msi_dialog_dup_property( dialog, prop, TRUE );
03005 
03006     lstrcpyW( new_path, path );
03007     lstrcatW( new_path, text );
03008     lstrcatW( new_path, szBackSlash );
03009 
03010     msi_dialog_set_property( dialog->package, prop, new_path );
03011 
03012     msi_dialog_update_directory_list( dialog, NULL );
03013     msi_dialog_update_directory_combo( dialog, NULL );
03014     msi_dialog_update_pathedit( dialog, NULL );
03015 
03016     msi_free( prop );
03017     msi_free( path );
03018     return ERROR_SUCCESS;
03019 }
03020 
03021 static UINT msi_dialog_directory_list( msi_dialog *dialog, MSIRECORD *rec )
03022 {
03023     msi_control *control;
03024     LPCWSTR prop;
03025     DWORD style;
03026 
03027     style = LVS_LIST | WS_VSCROLL | LVS_SHAREIMAGELISTS |
03028             LVS_AUTOARRANGE | LVS_SINGLESEL | WS_BORDER |
03029             LVS_SORTASCENDING | WS_CHILD | WS_GROUP | WS_TABSTOP;
03030     control = msi_dialog_add_control( dialog, rec, WC_LISTVIEWW, style );
03031     if (!control)
03032         return ERROR_FUNCTION_FAILED;
03033 
03034     control->attributes = MSI_RecordGetInteger( rec, 8 );
03035     control->handler = msi_dialog_dirlist_handler;
03036     prop = MSI_RecordGetString( rec, 9 );
03037     control->property = msi_dialog_dup_property( dialog, prop, FALSE );
03038 
03039     /* double click to activate an item in the list */
03040     SendMessageW( control->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE,
03041                   0, LVS_EX_TWOCLICKACTIVATE );
03042 
03043     msi_dialog_update_directory_list( dialog, control );
03044 
03045     return ERROR_SUCCESS;
03046 }
03047 
03048 /******************** VolumeCost List ***************************************/
03049 
03050 static BOOL str_is_number( LPCWSTR str )
03051 {
03052     int i;
03053 
03054     for (i = 0; i < lstrlenW( str ); i++)
03055         if (!isdigitW(str[i]))
03056             return FALSE;
03057 
03058     return TRUE;
03059 }
03060 
03061 static const WCHAR column_keys[][80] =
03062 {
03063     {'V','o','l','u','m','e','C','o','s','t','V','o','l','u','m','e',0},
03064     {'V','o','l','u','m','e','C','o','s','t','S','i','z','e',0},
03065     {'V','o','l','u','m','e','C','o','s','t','A','v','a','i','l','a','b','l','e',0},
03066     {'V','o','l','u','m','e','C','o','s','t','R','e','q','u','i','r','e','d',0},
03067     {'V','o','l','u','m','e','C','o','s','t','D','i','f','f','e','r','e','n','c','e',0}
03068 };
03069 
03070 static void msi_dialog_vcl_add_columns( msi_dialog *dialog, msi_control *control, MSIRECORD *rec )
03071 {
03072     LPCWSTR text = MSI_RecordGetString( rec, 10 );
03073     LPCWSTR begin = text, end;
03074     WCHAR *num;
03075     LVCOLUMNW lvc;
03076     DWORD count = 0;
03077 
03078     static const WCHAR negative[] = {'-',0};
03079 
03080     if (!text) return;
03081 
03082     while ((begin = strchrW( begin, '{' )) && count < 5)
03083     {
03084         if (!(end = strchrW( begin, '}' )))
03085             return;
03086 
03087         num = msi_alloc( (end-begin+1)*sizeof(WCHAR) );
03088         if (!num)
03089             return;
03090 
03091         lstrcpynW( num, begin + 1, end - begin );
03092         begin += end - begin + 1;
03093 
03094         /* empty braces or '0' hides the column */ 
03095         if ( !num[0] || !strcmpW( num, szZero ) )
03096         {
03097             count++;
03098             msi_free( num );
03099             continue;
03100         }
03101 
03102         /* the width must be a positive number
03103          * if a width is invalid, all remaining columns are hidden
03104          */
03105         if ( !strncmpW( num, negative, 1 ) || !str_is_number( num ) ) {
03106             msi_free( num );
03107             return;
03108         }
03109 
03110         ZeroMemory( &lvc, sizeof(lvc) );
03111         lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
03112         lvc.cx = atolW( num );
03113         lvc.pszText = msi_dialog_get_uitext( dialog, column_keys[count] );
03114 
03115         SendMessageW( control->hwnd,  LVM_INSERTCOLUMNW, count++, (LPARAM)&lvc );
03116         msi_free( lvc.pszText );
03117         msi_free( num );
03118     }
03119 }
03120 
03121 static LONGLONG msi_vcl_get_cost( msi_dialog *dialog )
03122 {
03123     MSIFEATURE *feature;
03124     INT each_cost;
03125     LONGLONG total_cost = 0;
03126 
03127     LIST_FOR_EACH_ENTRY( feature, &dialog->package->features, MSIFEATURE, entry )
03128     {
03129         if (ERROR_SUCCESS == (MSI_GetFeatureCost(dialog->package, feature,
03130                 MSICOSTTREE_SELFONLY, INSTALLSTATE_LOCAL, &each_cost)))
03131         {
03132             /* each_cost is in 512-byte units */
03133             total_cost += each_cost * 512;
03134         }
03135         if (ERROR_SUCCESS == (MSI_GetFeatureCost(dialog->package, feature,
03136                 MSICOSTTREE_SELFONLY, INSTALLSTATE_ABSENT, &each_cost)))
03137         {
03138             /* each_cost is in 512-byte units */
03139             total_cost -= each_cost * 512;
03140         }
03141     }
03142     return total_cost;
03143 }
03144 
03145 static void msi_dialog_vcl_add_drives( msi_dialog *dialog, msi_control *control )
03146 {
03147     ULARGE_INTEGER total, free;
03148     LONGLONG difference, cost;
03149     WCHAR size_text[MAX_PATH];
03150     WCHAR cost_text[MAX_PATH];
03151     LPWSTR drives, ptr;
03152     LVITEMW lvitem;
03153     DWORD size;
03154     int i = 0;
03155 
03156     cost = msi_vcl_get_cost(dialog);
03157     StrFormatByteSizeW(cost, cost_text, MAX_PATH);
03158 
03159     size = GetLogicalDriveStringsW( 0, NULL );
03160     if ( !size ) return;
03161 
03162     drives = msi_alloc( (size + 1) * sizeof(WCHAR) );
03163     if ( !drives ) return;
03164 
03165     GetLogicalDriveStringsW( size, drives );
03166 
03167     ptr = drives;
03168     while (*ptr)
03169     {
03170         lvitem.mask = LVIF_TEXT;
03171         lvitem.iItem = i;
03172         lvitem.iSubItem = 0;
03173         lvitem.pszText = ptr;
03174         lvitem.cchTextMax = lstrlenW(ptr) + 1;
03175         SendMessageW( control->hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvitem );
03176 
03177         GetDiskFreeSpaceExW(ptr, &free, &total, NULL);
03178         difference = free.QuadPart - cost;
03179 
03180         StrFormatByteSizeW(total.QuadPart, size_text, MAX_PATH);
03181         lvitem.iSubItem = 1;
03182         lvitem.pszText = size_text;
03183         lvitem.cchTextMax = lstrlenW(size_text) + 1;
03184         SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
03185 
03186         StrFormatByteSizeW(free.QuadPart, size_text, MAX_PATH);
03187         lvitem.iSubItem = 2;
03188         lvitem.pszText = size_text;
03189         lvitem.cchTextMax = lstrlenW(size_text) + 1;
03190         SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
03191 
03192         lvitem.iSubItem = 3;
03193         lvitem.pszText = cost_text;
03194         lvitem.cchTextMax = lstrlenW(cost_text) + 1;
03195         SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
03196 
03197         StrFormatByteSizeW(difference, size_text, MAX_PATH);
03198         lvitem.iSubItem = 4;
03199         lvitem.pszText = size_text;
03200         lvitem.cchTextMax = lstrlenW(size_text) + 1;
03201         SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
03202 
03203         ptr += lstrlenW(ptr) + 1;
03204         i++;
03205     }
03206 
03207     msi_free( drives );
03208 }
03209 
03210 static UINT msi_dialog_volumecost_list( msi_dialog *dialog, MSIRECORD *rec )
03211 {
03212     msi_control *control;
03213     DWORD style;
03214 
03215     style = LVS_REPORT | WS_VSCROLL | WS_HSCROLL | LVS_SHAREIMAGELISTS |
03216             LVS_AUTOARRANGE | LVS_SINGLESEL | WS_BORDER |
03217             WS_CHILD | WS_TABSTOP | WS_GROUP;
03218     control = msi_dialog_add_control( dialog, rec, WC_LISTVIEWW, style );
03219     if (!control)
03220         return ERROR_FUNCTION_FAILED;
03221 
03222     msi_dialog_vcl_add_columns( dialog, control, rec );
03223     msi_dialog_vcl_add_drives( dialog, control );
03224 
03225     return ERROR_SUCCESS;
03226 }
03227 
03228 /******************** VolumeSelect Combo ***************************************/
03229 
03230 static UINT msi_dialog_volsel_handler( msi_dialog *dialog,
03231                                        msi_control *control, WPARAM param )
03232 {
03233     WCHAR text[MAX_PATH];
03234     LPWSTR prop;
03235     BOOL indirect;
03236     int index;
03237 
03238     if (HIWORD(param) != CBN_SELCHANGE)
03239         return ERROR_SUCCESS;
03240 
03241     index = SendMessageW( control->hwnd, CB_GETCURSEL, 0, 0 );
03242     if ( index == CB_ERR )
03243     {
03244         ERR("No ComboBox item selected!\n");
03245         return ERROR_FUNCTION_FAILED;
03246     }
03247 
03248     SendMessageW( control->hwnd, CB_GETLBTEXT, index, (LPARAM)text );
03249 
03250     indirect = control->attributes & msidbControlAttributesIndirect;
03251     prop = msi_dialog_dup_property( dialog, control->property, indirect );
03252 
03253     msi_dialog_set_property( dialog->package, prop, text );
03254 
03255     msi_free( prop );
03256     return ERROR_SUCCESS;
03257 }
03258 
03259 static void msi_dialog_vsc_add_drives( msi_dialog *dialog, msi_control *control )
03260 {
03261     LPWSTR drives, ptr;
03262     DWORD size;
03263 
03264     size = GetLogicalDriveStringsW( 0, NULL );
03265     if ( !size ) return;
03266 
03267     drives = msi_alloc( (size + 1) * sizeof(WCHAR) );
03268     if ( !drives ) return;
03269 
03270     GetLogicalDriveStringsW( size, drives );
03271 
03272     ptr = drives;
03273     while (*ptr)
03274     {
03275         SendMessageW( control->hwnd, CB_ADDSTRING, 0, (LPARAM)ptr );
03276         ptr += lstrlenW(ptr) + 1;
03277     }
03278 
03279     msi_free( drives );
03280 }
03281 
03282 static UINT msi_dialog_volumeselect_combo( msi_dialog *dialog, MSIRECORD *rec )
03283 {
03284     msi_control *control;
03285     LPCWSTR prop;
03286     DWORD style;
03287 
03288     /* FIXME: CBS_OWNERDRAWFIXED */
03289     style = WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP |
03290             CBS_DROPDOWNLIST | CBS_SORT | CBS_HASSTRINGS |
03291             WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
03292     control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style );
03293     if (!control)
03294         return ERROR_FUNCTION_FAILED;
03295 
03296     control->attributes = MSI_RecordGetInteger( rec, 8 );
03297     control->handler = msi_dialog_volsel_handler;
03298     prop = MSI_RecordGetString( rec, 9 );
03299     control->property = msi_dialog_dup_property( dialog, prop, FALSE );
03300 
03301     msi_dialog_vsc_add_drives( dialog, control );
03302 
03303     return ERROR_SUCCESS;
03304 }
03305 
03306 static const struct control_handler msi_dialog_handler[] =
03307 {
03308     { szText, msi_dialog_text_control },
03309     { szPushButton, msi_dialog_button_control },
03310     { szLine, msi_dialog_line_control },
03311     { szBitmap, msi_dialog_bitmap_control },
03312     { szCheckBox, msi_dialog_checkbox_control },
03313     { szScrollableText, msi_dialog_scrolltext_control },
03314     { szComboBox, msi_dialog_combo_control },
03315     { szEdit, msi_dialog_edit_control },
03316     { szMaskedEdit, msi_dialog_maskedit_control },
03317     { szPathEdit, msi_dialog_pathedit_control },
03318     { szProgressBar, msi_dialog_progress_bar },
03319     { szRadioButtonGroup, msi_dialog_radiogroup_control },
03320     { szIcon, msi_dialog_icon_control },
03321     { szSelectionTree, msi_dialog_selection_tree },
03322     { szGroupBox, msi_dialog_group_box },
03323     { szListBox, msi_dialog_list_box },
03324     { szDirectoryCombo, msi_dialog_directory_combo },
03325     { szDirectoryList, msi_dialog_directory_list },
03326     { szVolumeCostList, msi_dialog_volumecost_list },
03327     { szVolumeSelectCombo, msi_dialog_volumeselect_combo },
03328 };
03329 
03330 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
03331 
03332 static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param )
03333 {
03334     msi_dialog *dialog = param;
03335     LPCWSTR control_type;
03336     UINT i;
03337 
03338     /* find and call the function that can create this type of control */
03339     control_type = MSI_RecordGetString( rec, 3 );
03340     for( i=0; i<NUM_CONTROL_TYPES; i++ )
03341         if (!strcmpiW( msi_dialog_handler[i].control_type, control_type ))
03342             break;
03343     if( i != NUM_CONTROL_TYPES )
03344         msi_dialog_handler[i].func( dialog, rec );
03345     else
03346         ERR("no handler for element type %s\n", debugstr_w(control_type));
03347 
03348     return ERROR_SUCCESS;
03349 }
03350 
03351 static UINT msi_dialog_fill_controls( msi_dialog *dialog )
03352 {
03353     static const WCHAR query[] = {
03354         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
03355         'C','o','n','t','r','o','l',' ','W','H','E','R','E',' ',
03356         '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
03357     UINT r;
03358     MSIQUERY *view;
03359     MSIPACKAGE *package = dialog->package;
03360 
03361     TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
03362 
03363     /* query the Control table for all the elements of the control */
03364     r = MSI_OpenQuery( package->db, &view, query, dialog->name );
03365     if( r != ERROR_SUCCESS )
03366     {
03367         ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
03368         return ERROR_INVALID_PARAMETER;
03369     }
03370 
03371     r = MSI_IterateRecords( view, 0, msi_dialog_create_controls, dialog );
03372     msiobj_release( &view->hdr );
03373     return r;
03374 }
03375 
03376 UINT msi_dialog_reset( msi_dialog *dialog )
03377 {
03378     /* FIXME: should restore the original values of any properties we changed */
03379     return msi_dialog_evaluate_control_conditions( dialog );
03380 }
03381 
03382 /* figure out the height of 10 point MS Sans Serif */
03383 static INT msi_dialog_get_sans_serif_height( HWND hwnd )
03384 {
03385     static const WCHAR szSansSerif[] = {
03386         'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
03387     LOGFONTW lf;
03388     TEXTMETRICW tm;
03389     BOOL r;
03390     LONG height = 0;
03391     HFONT hFont, hOldFont;
03392     HDC hdc;
03393 
03394     hdc = GetDC( hwnd );
03395     if (hdc)
03396     {
03397         memset( &lf, 0, sizeof lf );
03398         lf.lfHeight = MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
03399         strcpyW( lf.lfFaceName, szSansSerif );
03400         hFont = CreateFontIndirectW(&lf);
03401         if (hFont)
03402         {
03403             hOldFont = SelectObject( hdc, hFont );
03404             r = GetTextMetricsW( hdc, &tm );
03405             if (r)
03406                 height = tm.tmHeight;
03407             SelectObject( hdc, hOldFont );
03408             DeleteObject( hFont );
03409         }
03410         ReleaseDC( hwnd, hdc );
03411     }
03412     return height;
03413 }
03414 
03415 /* fetch the associated record from the Dialog table */
03416 static MSIRECORD *msi_get_dialog_record( msi_dialog *dialog )
03417 {
03418     static const WCHAR query[] = {
03419         'S','E','L','E','C','T',' ','*',' ',
03420         'F','R','O','M',' ','D','i','a','l','o','g',' ',
03421         'W','H','E','R','E',' ',
03422            '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
03423     MSIPACKAGE *package = dialog->package;
03424     MSIRECORD *rec = NULL;
03425 
03426     TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
03427 
03428     rec = MSI_QueryGetRecord( package->db, query, dialog->name );
03429     if( !rec )
03430         WARN("query failed for dialog %s\n", debugstr_w(dialog->name));
03431 
03432     return rec;
03433 }
03434 
03435 static void msi_dialog_adjust_dialog_pos( msi_dialog *dialog, MSIRECORD *rec, LPRECT pos )
03436 {
03437     static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0};
03438     static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0};
03439 
03440     UINT xres, yres;
03441     POINT center;
03442     SIZE sz;
03443     LONG style;
03444 
03445     center.x = MSI_RecordGetInteger( rec, 2 );
03446     center.y = MSI_RecordGetInteger( rec, 3 );
03447 
03448     sz.cx = MSI_RecordGetInteger( rec, 4 );
03449     sz.cy = MSI_RecordGetInteger( rec, 5 );
03450 
03451     sz.cx = msi_dialog_scale_unit( dialog, sz.cx );
03452     sz.cy = msi_dialog_scale_unit( dialog, sz.cy );
03453 
03454     xres = msi_get_property_int( dialog->package->db, szScreenX, 0 );
03455     yres = msi_get_property_int( dialog->package->db, szScreenY, 0 );
03456 
03457     center.x = MulDiv( center.x, xres, 100 );
03458     center.y = MulDiv( center.y, yres, 100 );
03459 
03460     /* turn the client pos into the window rectangle */
03461     if (dialog->package->center_x && dialog->package->center_y)
03462     {
03463         pos->left = dialog->package->center_x - sz.cx / 2.0;
03464         pos->right = pos->left + sz.cx;
03465         pos->top = dialog->package->center_y - sz.cy / 2.0;
03466         pos->bottom = pos->top + sz.cy;
03467     }
03468     else
03469     {
03470         pos->left = center.x - sz.cx/2;
03471         pos->right = pos->left + sz.cx;
03472         pos->top = center.y - sz.cy/2;
03473         pos->bottom = pos->top + sz.cy;
03474 
03475         /* save the center */
03476         dialog->package->center_x = center.x;
03477         dialog->package->center_y = center.y;
03478     }
03479 
03480     dialog->size.cx = sz.cx;
03481     dialog->size.cy = sz.cy;
03482 
03483     TRACE("%u %u %u %u\n", pos->left, pos->top, pos->right, pos->bottom);
03484 
03485     style = GetWindowLongPtrW( dialog->hwnd, GWL_STYLE );
03486     AdjustWindowRect( pos, style, FALSE );
03487 }
03488 
03489 static void msi_dialog_set_tab_order( msi_dialog *dialog, LPCWSTR first )
03490 {
03491     struct list tab_chain;
03492     msi_control *control;
03493     HWND prev = HWND_TOP;
03494 
03495     list_init( &tab_chain );
03496     if (!(control = msi_dialog_find_control( dialog, first ))) return;
03497 
03498     dialog->hWndFocus = control->hwnd;
03499     while (control)
03500     {
03501         list_remove( &control->entry );
03502         list_add_tail( &tab_chain, &control->entry );
03503         if (!control->tabnext) break;
03504         control = msi_dialog_find_control( dialog, control->tabnext );
03505     }
03506 
03507     LIST_FOR_EACH_ENTRY( control, &tab_chain, msi_control, entry )
03508     {
03509         SetWindowPos( control->hwnd, prev, 0, 0, 0, 0,
03510                       SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW |
03511                       SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE );
03512         prev = control->hwnd;
03513     }
03514 
03515     /* put them back on the main list */
03516     list_move_head( &dialog->controls, &tab_chain );
03517 }
03518 
03519 static LRESULT msi_dialog_oncreate( HWND hwnd, LPCREATESTRUCTW cs )
03520 {
03521     static const WCHAR df[] = {
03522         'D','e','f','a','u','l','t','U','I','F','o','n','t',0 };
03523     static const WCHAR dfv[] = {
03524         'M','S',' ','S','h','e','l','l',' ','D','l','g',0 };
03525     msi_dialog *dialog = cs->lpCreateParams;
03526     MSIRECORD *rec = NULL;
03527     LPWSTR title = NULL;
03528     RECT pos;
03529 
03530     TRACE("%p %p\n", dialog, dialog->package);
03531 
03532     dialog->hwnd = hwnd;
03533     SetWindowLongPtrW( hwnd, GWLP_USERDATA, (LONG_PTR) dialog );
03534 
03535     rec = msi_get_dialog_record( dialog );
03536     if( !rec )
03537     {
03538         TRACE("No record found for dialog %s\n", debugstr_w(dialog->name));
03539         return -1;
03540     }
03541 
03542     dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd);
03543 
03544     msi_dialog_adjust_dialog_pos( dialog, rec, &pos );
03545 
03546     dialog->attributes = MSI_RecordGetInteger( rec, 6 );
03547 
03548     dialog->default_font = msi_dup_property( dialog->package->db, df );
03549     if (!dialog->default_font)
03550     {
03551         dialog->default_font = strdupW(dfv);
03552         if (!dialog->default_font) return -1;
03553     }
03554 
03555     title = msi_get_deformatted_field( dialog->package, rec, 7 );
03556     SetWindowTextW( hwnd, title );
03557     msi_free( title );
03558 
03559     SetWindowPos( hwnd, 0, pos.left, pos.top,
03560                   pos.right - pos.left, pos.bottom - pos.top,
03561                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW );
03562 
03563     msi_dialog_build_font_list( dialog );
03564     msi_dialog_fill_controls( dialog );
03565     msi_dialog_evaluate_control_conditions( dialog );
03566     msi_dialog_set_tab_order( dialog, MSI_RecordGetString( rec, 8 ) );
03567     msiobj_release( &rec->hdr );
03568 
03569     return 0;
03570 }
03571 
03572 static LRESULT msi_dialog_oncommand( msi_dialog *dialog, WPARAM param, HWND hwnd )
03573 {
03574     msi_control *control = NULL;
03575 
03576     TRACE("%p %p %08lx\n", dialog, hwnd, param);
03577 
03578     switch (param)
03579     {
03580     case 1: /* enter */
03581         control = msi_dialog_find_control( dialog, dialog->control_default );
03582         break;
03583     case 2: /* escape */
03584         control = msi_dialog_find_control( dialog, dialog->control_cancel );
03585         break;
03586     default: 
03587         control = msi_dialog_find_control_by_hwnd( dialog, hwnd );
03588     }
03589 
03590     if( control )
03591     {
03592         if( control->handler )
03593         {
03594             control->handler( dialog, control, param );
03595             msi_dialog_evaluate_control_conditions( dialog );
03596         }
03597     }
03598 
03599     return 0;
03600 }
03601 
03602 static LRESULT msi_dialog_onnotify( msi_dialog *dialog, LPARAM param )
03603 {
03604     LPNMHDR nmhdr = (LPNMHDR) param;
03605     msi_control *control = msi_dialog_find_control_by_hwnd( dialog, nmhdr->hwndFrom );
03606 
03607     TRACE("%p %p\n", dialog, nmhdr->hwndFrom);
03608 
03609     if ( control && control->handler )
03610         control->handler( dialog, control, param );
03611 
03612     return 0;
03613 }
03614 
03615 static void msi_dialog_setfocus( msi_dialog *dialog )
03616 {
03617     HWND hwnd = dialog->hWndFocus;
03618 
03619     hwnd = GetNextDlgTabItem( dialog->hwnd, hwnd, TRUE);
03620     hwnd = GetNextDlgTabItem( dialog->hwnd, hwnd, FALSE);
03621     SetFocus( hwnd );
03622     dialog->hWndFocus = hwnd;
03623 }
03624 
03625 static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg,
03626                 WPARAM wParam, LPARAM lParam )
03627 {
03628     msi_dialog *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA );
03629 
03630     TRACE("0x%04x\n", msg);
03631 
03632     switch (msg)
03633     {
03634     case WM_MOVE:
03635         dialog->package->center_x = LOWORD(lParam) + dialog->size.cx / 2.0;
03636         dialog->package->center_y = HIWORD(lParam) + dialog->size.cy / 2.0;
03637         break;
03638 
03639     case WM_CREATE:
03640         return msi_dialog_oncreate( hwnd, (LPCREATESTRUCTW)lParam );
03641 
03642     case WM_COMMAND:
03643         return msi_dialog_oncommand( dialog, wParam, (HWND)lParam );
03644 
03645     case WM_ACTIVATE:
03646         if( LOWORD(wParam) == WA_INACTIVE )
03647             dialog->hWndFocus = GetFocus();
03648         else
03649             msi_dialog_setfocus( dialog );
03650         return 0;
03651 
03652     case WM_SETFOCUS:
03653         msi_dialog_setfocus( dialog );
03654         return 0;
03655 
03656     /* bounce back to our subclassed static control */
03657     case WM_CTLCOLORSTATIC:
03658         return SendMessageW( (HWND) lParam, WM_CTLCOLORSTATIC, wParam, lParam );
03659 
03660     case WM_DESTROY:
03661         dialog->hwnd = NULL;
03662         return 0;
03663     case WM_NOTIFY:
03664         return msi_dialog_onnotify( dialog, lParam );
03665     }
03666     return DefWindowProcW(hwnd, msg, wParam, lParam);
03667 }
03668 
03669 static LRESULT WINAPI MSIHiddenWindowProc( HWND hwnd, UINT msg,
03670                 WPARAM wParam, LPARAM lParam )
03671 {
03672     msi_dialog *dialog = (msi_dialog*) lParam;
03673 
03674     TRACE("%d %p\n", msg, dialog);
03675 
03676     switch (msg)
03677     {
03678     case WM_MSI_DIALOG_CREATE:
03679         return msi_dialog_run_message_loop( dialog );
03680     case WM_MSI_DIALOG_DESTROY:
03681         msi_dialog_destroy( dialog );
03682         return 0;
03683     }
03684     return DefWindowProcW( hwnd, msg, wParam, lParam );
03685 }
03686 
03687 static BOOL msi_dialog_register_class( void )
03688 {
03689     WNDCLASSW cls;
03690 
03691     ZeroMemory( &cls, sizeof cls );
03692     cls.lpfnWndProc   = MSIDialog_WndProc;
03693     cls.hInstance     = NULL;
03694     cls.hIcon         = LoadIconW(0, (LPWSTR)IDI_APPLICATION);
03695     cls.hCursor       = LoadCursorW(0, (LPWSTR)IDC_ARROW);
03696     cls.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
03697     cls.lpszMenuName  = NULL;
03698     cls.lpszClassName = szMsiDialogClass;
03699 
03700     if( !RegisterClassW( &cls ) )
03701         return FALSE;
03702 
03703     cls.lpfnWndProc   = MSIHiddenWindowProc;
03704     cls.lpszClassName = szMsiHiddenWindow;
03705 
03706     if( !RegisterClassW( &cls ) )
03707         return FALSE;
03708 
03709     uiThreadId = GetCurrentThreadId();
03710 
03711     hMsiHiddenWindow = CreateWindowW( szMsiHiddenWindow, NULL, WS_OVERLAPPED,
03712                                    0, 0, 100, 100, NULL, NULL, NULL, NULL );
03713     if( !hMsiHiddenWindow )
03714         return FALSE;
03715 
03716     return TRUE;
03717 }
03718 
03719 /* functions that interface to other modules within MSI */
03720 
03721 msi_dialog *msi_dialog_create( MSIPACKAGE* package,
03722                                LPCWSTR szDialogName, msi_dialog *parent,
03723                                msi_dialog_event_handler event_handler )
03724 {
03725     MSIRECORD *rec = NULL;
03726     msi_dialog *dialog;
03727 
03728     TRACE("%p %s\n", package, debugstr_w(szDialogName));
03729 
03730     if (!hMsiHiddenWindow)
03731         msi_dialog_register_class();
03732 
03733     /* allocate the structure for the dialog to use */
03734     dialog = msi_alloc_zero( sizeof *dialog + sizeof(WCHAR)*strlenW(szDialogName) );
03735     if( !dialog )
03736         return NULL;
03737     strcpyW( dialog->name, szDialogName );
03738     dialog->parent = parent;
03739     msiobj_addref( &package->hdr );
03740     dialog->package = package;
03741     dialog->event_handler = event_handler;
03742     dialog->finished = 0;
03743     list_init( &dialog->controls );
03744     list_init( &dialog->fonts );
03745 
03746     /* verify that the dialog exists */
03747     rec = msi_get_dialog_record( dialog );
03748     if( !rec )
03749     {
03750         msiobj_release( &package->hdr );
03751         msi_free( dialog );
03752         return NULL;
03753     }
03754     dialog->attributes = MSI_RecordGetInteger( rec, 6 );
03755     dialog->control_default = strdupW( MSI_RecordGetString( rec, 9 ) );
03756     dialog->control_cancel = strdupW( MSI_RecordGetString( rec, 10 ) );
03757     msiobj_release( &rec->hdr );
03758 
03759     return dialog;
03760 }
03761 
03762 static void msi_process_pending_messages( HWND hdlg )
03763 {
03764     MSG msg;
03765 
03766     while( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ) )
03767     {
03768         if( hdlg && IsDialogMessageW( hdlg, &msg ))
03769             continue;
03770         TranslateMessage( &msg );
03771         DispatchMessageW( &msg );
03772     }
03773 }
03774 
03775 void msi_dialog_end_dialog( msi_dialog *dialog )
03776 {
03777     TRACE("%p\n", dialog);
03778     dialog->finished = 1;
03779     PostMessageW(dialog->hwnd, WM_NULL, 0, 0);
03780 }
03781 
03782 void msi_dialog_check_messages( HANDLE handle )
03783 {
03784     DWORD r;
03785 
03786     /* in threads other than the UI thread, block */
03787     if( uiThreadId != GetCurrentThreadId() )
03788     {
03789         if (!handle) return;
03790         while (MsgWaitForMultipleObjectsEx( 1, &handle, INFINITE, QS_ALLINPUT, 0 ) == WAIT_OBJECT_0 + 1)
03791         {
03792             MSG msg;
03793             while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
03794             {
03795                 TranslateMessage( &msg );
03796                 DispatchMessageW( &msg );
03797             }
03798         }
03799         return;
03800     }
03801 
03802     /* there's two choices for the UI thread */
03803     while (1)
03804     {
03805         msi_process_pending_messages( NULL );
03806 
03807         if( !handle )
03808             break;
03809 
03810         /*
03811          * block here until somebody creates a new dialog or
03812          * the handle we're waiting on becomes ready
03813          */
03814         r = MsgWaitForMultipleObjects( 1, &handle, 0, INFINITE, QS_ALLINPUT );
03815         if( r == WAIT_OBJECT_0 )
03816             break;
03817     }
03818 }
03819 
03820 UINT msi_dialog_run_message_loop( msi_dialog *dialog )
03821 {
03822     DWORD style;
03823     HWND hwnd;
03824 
03825     if( uiThreadId != GetCurrentThreadId() )
03826         return SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_CREATE, 0, (LPARAM) dialog );
03827 
03828     /* create the dialog window, don't show it yet */
03829     style = WS_OVERLAPPED;
03830     if( dialog->attributes & msidbDialogAttributesVisible )
03831         style |= WS_VISIBLE;
03832 
03833     hwnd = CreateWindowW( szMsiDialogClass, dialog->name, style,
03834                      CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
03835                      NULL, NULL, NULL, dialog );
03836     if( !hwnd )
03837     {
03838         ERR("Failed to create dialog %s\n", debugstr_w( dialog->name ));
03839         return ERROR_FUNCTION_FAILED;
03840     }
03841 
03842     ShowWindow( hwnd, SW_SHOW );
03843     /* UpdateWindow( hwnd ); - and causes the transparent static controls not to paint */
03844 
03845     if( dialog->attributes & msidbDialogAttributesModal )
03846     {
03847         while( !dialog->finished )
03848         {
03849             MsgWaitForMultipleObjects( 0, NULL, 0, INFINITE, QS_ALLINPUT );
03850             msi_process_pending_messages( dialog->hwnd );
03851         }
03852     }
03853     else
03854         return ERROR_IO_PENDING;
03855 
03856     return ERROR_SUCCESS;
03857 }
03858 
03859 static void msi_dialog_do_preview( msi_dialog *dialog )
03860 {
03861     TRACE("\n");
03862     dialog->attributes |= msidbDialogAttributesVisible;
03863     dialog->attributes &= ~msidbDialogAttributesModal;
03864     msi_dialog_run_message_loop( dialog );
03865 }
03866 
03867 void msi_dialog_destroy( msi_dialog *dialog )
03868 {
03869     msi_font *font, *next;
03870 
03871     if( uiThreadId != GetCurrentThreadId() )
03872     {
03873         SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_DESTROY, 0, (LPARAM) dialog );
03874         return;
03875     }
03876 
03877     if( dialog->hwnd )
03878         ShowWindow( dialog->hwnd, SW_HIDE );
03879 
03880     if( dialog->hwnd )
03881         DestroyWindow( dialog->hwnd );
03882 
03883     /* unsubscribe events */
03884     ControlEvent_CleanupDialogSubscriptions(dialog->package, dialog->name);
03885 
03886     /* destroy the list of controls */
03887     while( !list_empty( &dialog->controls ) )
03888     {
03889         msi_control *t;
03890 
03891         t = LIST_ENTRY( list_head( &dialog->controls ),
03892                         msi_control, entry );
03893         msi_destroy_control( t );
03894     }
03895 
03896     /* destroy the list of fonts */
03897     LIST_FOR_EACH_ENTRY_SAFE( font, next, &dialog->fonts, msi_font, entry )
03898     {
03899         list_remove( &font->entry );
03900         DeleteObject( font->hfont );
03901         msi_free( font );
03902     }
03903     msi_free( dialog->default_font );
03904 
03905     msi_free( dialog->control_default );
03906     msi_free( dialog->control_cancel );
03907     msiobj_release( &dialog->package->hdr );
03908     dialog->package = NULL;
03909     msi_free( dialog );
03910 }
03911 
03912 void msi_dialog_unregister_class( void )
03913 {
03914     DestroyWindow( hMsiHiddenWindow );
03915     hMsiHiddenWindow = NULL;
03916     UnregisterClassW( szMsiDialogClass, NULL );
03917     UnregisterClassW( szMsiHiddenWindow, NULL );
03918     uiThreadId = 0;
03919 }
03920 
03921 static UINT error_dialog_handler(MSIPACKAGE *package, LPCWSTR event,
03922                                  LPCWSTR argument, msi_dialog* dialog)
03923 {
03924     static const WCHAR end_dialog[] = {'E','n','d','D','i','a','l','o','g',0};
03925     static const WCHAR error_abort[] = {'E','r','r','o','r','A','b','o','r','t',0};
03926     static const WCHAR error_cancel[] = {'E','r','r','o','r','C','a','n','c','e','l',0};
03927     static const WCHAR error_no[] = {'E','r','r','o','r','N','o',0};
03928     static const WCHAR result_prop[] = {
03929         'M','S','I','E','r','r','o','r','D','i','a','l','o','g','R','e','s','u','l','t',0
03930     };
03931 
03932     if ( strcmpW( event, end_dialog ) )
03933         return ERROR_SUCCESS;
03934 
03935     if ( !strcmpW( argument, error_abort ) || !strcmpW( argument, error_cancel ) ||
03936          !strcmpW( argument, error_no ) )
03937     {
03938          msi_set_property( package->db, result_prop, error_abort );
03939     }
03940 
03941     ControlEvent_CleanupSubscriptions(package);
03942     msi_dialog_end_dialog( dialog );
03943 
03944     return ERROR_SUCCESS;
03945 }
03946 
03947 static UINT msi_error_dialog_set_error( MSIPACKAGE *package, LPWSTR error_dialog, LPWSTR error )
03948 {
03949     MSIRECORD * row;
03950 
03951     static const WCHAR update[] = 
03952         {'U','P','D','A','T','E',' ','`','C','o','n','t','r','o','l','`',' ',
03953          'S','E','T',' ','`','T','e','x','t','`',' ','=',' ','\'','%','s','\'',' ',
03954          'W','H','E','R','E', ' ','`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
03955          'A','N','D',' ','`','C','o','n','t','r','o','l','`',' ','=',' ',
03956          '\'','E','r','r','o','r','T','e','x','t','\'',0};
03957 
03958     row = MSI_QueryGetRecord( package->db, update, error, error_dialog );
03959     if (!row)
03960         return ERROR_FUNCTION_FAILED;
03961 
03962     msiobj_release(&row->hdr);
03963     return ERROR_SUCCESS;
03964 }
03965 
03966 UINT msi_spawn_error_dialog( MSIPACKAGE *package, LPWSTR error_dialog, LPWSTR error )
03967 {
03968     msi_dialog *dialog;
03969     WCHAR result[MAX_PATH];
03970     UINT r = ERROR_SUCCESS;
03971     DWORD size = MAX_PATH;
03972     int res;
03973 
03974     static const WCHAR pn_prop[] = {'P','r','o','d','u','c','t','N','a','m','e',0};
03975     static const WCHAR title_fmt[] = {'%','s',' ','W','a','r','n','i','n','g',0};
03976     static const WCHAR error_abort[] = {'E','r','r','o','r','A','b','o','r','t',0};
03977     static const WCHAR result_prop[] = {
03978         'M','S','I','E','r','r','o','r','D','i','a','l','o','g','R','e','s','u','l','t',0
03979     };
03980 
03981     if ((package->ui_level & INSTALLUILEVEL_MASK) == INSTALLUILEVEL_NONE) return ERROR_SUCCESS;
03982 
03983     if ( !error_dialog )
03984     {
03985         LPWSTR product_name = msi_dup_property( package->db, pn_prop );
03986         WCHAR title[MAX_PATH];
03987 
03988         sprintfW( title, title_fmt, product_name );
03989         res = MessageBoxW( NULL, error, title, MB_OKCANCEL | MB_ICONWARNING );
03990 
03991         msi_free( product_name );
03992 
03993         if ( res == IDOK )
03994             return ERROR_SUCCESS;
03995         else
03996             return ERROR_FUNCTION_FAILED;
03997     }
03998 
03999     r = msi_error_dialog_set_error( package, error_dialog, error );
04000     if ( r != ERROR_SUCCESS )
04001         return r;
04002 
04003     dialog = msi_dialog_create( package, error_dialog, package->dialog,
04004                                 error_dialog_handler );
04005     if ( !dialog )
04006         return ERROR_FUNCTION_FAILED;
04007 
04008     dialog->finished = FALSE;
04009     r = msi_dialog_run_message_loop( dialog );
04010     if ( r != ERROR_SUCCESS )
04011         goto done;
04012 
04013     r = msi_get_property( package->db, result_prop, result, &size );
04014     if ( r != ERROR_SUCCESS)
04015         r = ERROR_SUCCESS;
04016 
04017     if ( !strcmpW( result, error_abort ) )
04018         r = ERROR_FUNCTION_FAILED;
04019 
04020 done:
04021     msi_dialog_destroy( dialog );
04022 
04023     return r;
04024 }
04025 
04026 static void MSI_ClosePreview( MSIOBJECTHDR *arg )
04027 {
04028     MSIPREVIEW *preview = (MSIPREVIEW *)arg;
04029     msiobj_release( &preview->package->hdr );
04030 }
04031 
04032 static MSIPREVIEW *MSI_EnableUIPreview( MSIDATABASE *db )
04033 {
04034     MSIPREVIEW *preview = NULL;
04035     MSIPACKAGE *package;
04036 
04037     package = MSI_CreatePackage( db, NULL );
04038     if (package)
04039     {
04040         preview = alloc_msiobject( MSIHANDLETYPE_PREVIEW, sizeof(MSIPREVIEW), MSI_ClosePreview );
04041         if (preview)
04042         {
04043             preview->package = package;
04044             msiobj_addref( &package->hdr );
04045         }
04046         msiobj_release( &package->hdr );
04047     }
04048     return preview;
04049 }
04050 
04051 UINT WINAPI MsiEnableUIPreview( MSIHANDLE hdb, MSIHANDLE *phPreview )
04052 {
04053     MSIDATABASE *db;
04054     MSIPREVIEW *preview;
04055     UINT r = ERROR_FUNCTION_FAILED;
04056 
04057     TRACE("%d %p\n", hdb, phPreview);
04058 
04059     db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
04060     if (!db)
04061     {
04062         IWineMsiRemoteDatabase *remote_database;
04063 
04064         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
04065         if (!remote_database)
04066             return ERROR_INVALID_HANDLE;
04067 
04068         *phPreview = 0;
04069 
04070         IWineMsiRemoteDatabase_Release( remote_database );
04071         WARN("MsiEnableUIPreview not allowed during a custom action!\n");
04072 
04073         return ERROR_FUNCTION_FAILED;
04074     }
04075     preview = MSI_EnableUIPreview( db );
04076     if (preview)
04077     {
04078         *phPreview = alloc_msihandle( &preview->hdr );
04079         msiobj_release( &preview->hdr );
04080         r = ERROR_SUCCESS;
04081         if (!*phPreview)
04082             r = ERROR_NOT_ENOUGH_MEMORY;
04083     }
04084     msiobj_release( &db->hdr );
04085     return r;
04086 }
04087 
04088 static UINT preview_event_handler( MSIPACKAGE *package, LPCWSTR event,
04089                                    LPCWSTR argument, msi_dialog *dialog )
04090 {
04091     MESSAGE("Preview dialog event '%s' (arg='%s')\n", debugstr_w(event), debugstr_w(argument));
04092     return ERROR_SUCCESS;
04093 }
04094 
04095 static UINT MSI_PreviewDialogW( MSIPREVIEW *preview, LPCWSTR szDialogName )
04096 {
04097     msi_dialog *dialog = NULL;
04098     UINT r = ERROR_SUCCESS;
04099 
04100     if (preview->dialog)
04101         msi_dialog_destroy( preview->dialog );
04102 
04103     /* an empty name means we should just destroy the current preview dialog */
04104     if (szDialogName)
04105     {
04106         dialog = msi_dialog_create( preview->package, szDialogName, NULL, preview_event_handler );
04107         if (dialog)
04108             msi_dialog_do_preview( dialog );
04109         else
04110             r = ERROR_FUNCTION_FAILED;
04111     }
04112     preview->dialog = dialog;
04113     return r;
04114 }
04115 
04116 UINT WINAPI MsiPreviewDialogW( MSIHANDLE hPreview, LPCWSTR szDialogName )
04117 {
04118     MSIPREVIEW *preview;
04119     UINT r;
04120 
04121     TRACE("%d %s\n", hPreview, debugstr_w(szDialogName));
04122 
04123     preview = msihandle2msiinfo( hPreview, MSIHANDLETYPE_PREVIEW );
04124     if (!preview)
04125         return ERROR_INVALID_HANDLE;
04126 
04127     r = MSI_PreviewDialogW( preview, szDialogName );
04128     msiobj_release( &preview->hdr );
04129     return r;
04130 }
04131 
04132 UINT WINAPI MsiPreviewDialogA( MSIHANDLE hPreview, LPCSTR szDialogName )
04133 {
04134     UINT r;
04135     LPWSTR strW = NULL;
04136 
04137     TRACE("%d %s\n", hPreview, debugstr_a(szDialogName));
04138 
04139     if (szDialogName)
04140     {
04141         strW = strdupAtoW( szDialogName );
04142         if (!strW)
04143             return ERROR_OUTOFMEMORY;
04144     }
04145     r = MsiPreviewDialogW( hPreview, strW );
04146     msi_free( strW );
04147     return r;
04148 }
04149 
04150 UINT WINAPI MsiPreviewBillboardW( MSIHANDLE hPreview, LPCWSTR szControlName, LPCWSTR szBillboard )
04151 {
04152     FIXME("%d %s %s\n", hPreview, debugstr_w(szControlName), debugstr_w(szBillboard));
04153     return ERROR_CALL_NOT_IMPLEMENTED;
04154 }
04155 
04156 UINT WINAPI MsiPreviewBillboardA( MSIHANDLE hPreview, LPCSTR szControlName, LPCSTR szBillboard )
04157 {
04158     FIXME("%d %s %s\n", hPreview, debugstr_a(szControlName), debugstr_a(szBillboard));
04159     return ERROR_CALL_NOT_IMPLEMENTED;
04160 }

Generated on Sun May 27 2012 04:16:37 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.