Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygendialog.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
1.7.6.1
|