ReactOS 0.4.16-dev-250-g3ecd236
dialog.c
Go to the documentation of this file.
1/*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2005 Mike McCormack for CodeWeavers
5 * Copyright 2005 Aric Stewart for CodeWeavers
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22#define COBJMACROS
23
24#include <stdarg.h>
25
26#include "windef.h"
27#include "winbase.h"
28#include "wingdi.h"
29#include "winuser.h"
30#include "winnls.h"
31#include "msi.h"
32#include "msidefs.h"
33#include "ocidl.h"
34#include "olectl.h"
35#include "richedit.h"
36#include "commctrl.h"
37#include "winreg.h"
38#include "shlwapi.h"
39#include "shellapi.h"
40
41#include "wine/debug.h"
42
43#include "msipriv.h"
44#include "msiserver.h"
45#include "resource.h"
46
48
50
51struct control
52{
53 struct list entry;
55 UINT (*handler)( msi_dialog *, struct control *, WPARAM );
56 void (*update)( msi_dialog *, struct control * );
70};
71
72struct font
73{
74 struct list entry;
78};
79
81{
84 UINT (*event_handler)( msi_dialog *, const WCHAR *, const WCHAR * );
91 struct list fonts;
92 struct list controls;
100};
101
103{
104 struct list entry;
109};
110
112{
115};
116
118{
122};
123
124/* dialog sequencing */
125
126#define WM_MSI_DIALOG_CREATE (WM_USER+0x100)
127#define WM_MSI_DIALOG_DESTROY (WM_USER+0x101)
128
129#define USER_INSTALLSTATE_ALL 0x1000
130
133
135{
136 UINT sz, r;
137 WCHAR *buf, *new_buf;
138
139 sz = 0x20;
140 buf = malloc( sz * sizeof(WCHAR) );
141 while ( buf )
142 {
143 r = GetWindowTextW( hwnd, buf, sz );
144 if ( r < (sz - 1) )
145 break;
146 sz *= 2;
147 new_buf = realloc( buf, sz * sizeof(WCHAR) );
148 if ( !new_buf )
149 free( buf );
150 buf = new_buf;
151 }
152
153 return buf;
154}
155
157{
158 return MulDiv( val, dialog->scale, 12 );
159}
160
162{
163 struct control *control;
164
165 if( !name )
166 return NULL;
167 if( !dialog->hwnd )
168 return NULL;
169 LIST_FOR_EACH_ENTRY( control, &dialog->controls, struct control, entry )
170 if( !wcscmp( control->name, name ) ) /* FIXME: case sensitive? */
171 return control;
172 return NULL;
173}
174
176{
177 struct control *control;
178
179 if( !type )
180 return NULL;
181 if( !dialog->hwnd )
182 return NULL;
183 LIST_FOR_EACH_ENTRY( control, &dialog->controls, struct control, entry )
184 if( !wcscmp( control->type, type ) ) /* FIXME: case sensitive? */
185 return control;
186 return NULL;
187}
188
190{
191 struct control *control;
192
193 if( !dialog->hwnd )
194 return NULL;
195 LIST_FOR_EACH_ENTRY( control, &dialog->controls, struct control, entry )
196 if( hwnd == control->hwnd )
197 return control;
198 return NULL;
199}
200
202{
204 LPWSTR ret = NULL;
205
206 if (str)
207 deformat_string( package, str, &ret );
208 return ret;
209}
210
212{
213 LPWSTR prop = NULL;
214
215 if (!property)
216 return NULL;
217
218 if (indirect)
219 prop = msi_dup_property( dialog->package->db, property );
220
221 if (!prop)
222 prop = wcsdup( property );
223
224 return prop;
225}
226
227/*
228 * dialog_get_style
229 *
230 * Extract the {\style} string from the front of the text to display and
231 * update the pointer. Only the last style in a list is applied.
232 */
233static WCHAR *dialog_get_style( const WCHAR *p, const WCHAR **rest )
234{
235 LPWSTR ret;
236 LPCWSTR q, i, first;
237 DWORD len;
238
239 q = NULL;
240 *rest = p;
241 if( !p )
242 return NULL;
243
244 while ((first = wcschr( p, '{' )) && (q = wcschr( first + 1, '}' )))
245 {
246 p = first + 1;
247 if( *p != '\\' && *p != '&' )
248 return NULL;
249
250 /* little bit of sanity checking to stop us getting confused with RTF */
251 for( i=++p; i<q; i++ )
252 if( *i == '}' || *i == '\\' )
253 return NULL;
254 }
255
256 if (!q)
257 return NULL;
258
259 *rest = ++q;
260 len = q - p;
261
262 ret = malloc( len * sizeof(WCHAR) );
263 if( !ret )
264 return ret;
265 memcpy( ret, p, len*sizeof(WCHAR) );
266 ret[len-1] = 0;
267 return ret;
268}
269
270static UINT dialog_add_font( MSIRECORD *rec, void *param )
271{
273 struct font *font;
275 LOGFONTW lf;
276 INT style;
277 HDC hdc;
278
279 /* create a font and add it to the list */
280 name = MSI_RecordGetString( rec, 1 );
281 font = malloc( offsetof( struct font, name[wcslen( name ) + 1] ) );
282 lstrcpyW( font->name, name );
283 list_add_head( &dialog->fonts, &font->entry );
284
285 font->color = MSI_RecordGetInteger( rec, 4 );
286
287 memset( &lf, 0, sizeof lf );
288 face = MSI_RecordGetString( rec, 2 );
289 lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
290 style = MSI_RecordGetInteger( rec, 5 );
292 lf.lfWeight = FW_BOLD;
294 lf.lfItalic = TRUE;
296 lf.lfUnderline = TRUE;
298 lf.lfStrikeOut = TRUE;
300
301 /* adjust the height */
302 hdc = GetDC( dialog->hwnd );
303 if (hdc)
304 {
306 ReleaseDC( dialog->hwnd, hdc );
307 }
308
309 font->hfont = CreateFontIndirectW( &lf );
310
311 TRACE("Adding font style %s\n", debugstr_w(font->name) );
312
313 return ERROR_SUCCESS;
314}
315
317{
318 struct font *font = NULL;
319
320 LIST_FOR_EACH_ENTRY( font, &dialog->fonts, struct font, entry )
321 if( !wcscmp( font->name, name ) ) /* FIXME: case sensitive? */
322 break;
323
324 return font;
325}
326
328{
329 struct font *font;
330
332 if( font )
334 else
335 ERR("No font entry for %s\n", debugstr_w(name));
336 return ERROR_SUCCESS;
337}
338
340{
341 MSIQUERY *view;
342 UINT r;
343
344 TRACE("dialog %p\n", dialog );
345
346 r = MSI_OpenQuery( dialog->package->db, &view, L"SELECT * FROM `TextStyle`" );
347 if( r != ERROR_SUCCESS )
348 return r;
349
351 msiobj_release( &view->hdr );
352 return r;
353}
354
355static void destroy_control( struct control *t )
356{
357 list_remove( &t->entry );
358 /* leave dialog->hwnd - destroying parent destroys child windows */
359 free( t->property );
360 free( t->value );
361 if( t->hBitmap )
362 DeleteObject( t->hBitmap );
363 if( t->hIcon )
364 DestroyIcon( t->hIcon );
365 if ( t->hImageList )
366 ImageList_Destroy( t->hImageList );
367 free( t->tabnext );
368 free( t->type );
369 if (t->hDll)
370 FreeLibrary( t->hDll );
371 free( t );
372}
373
375 const WCHAR *szCls, const WCHAR *name, const WCHAR *text,
377{
378 DWORD x, y, width, height;
379 LPWSTR font = NULL, title_font = NULL;
381 struct control *control;
382
383 style |= WS_CHILD;
384
385 control = malloc( offsetof( struct control, name[wcslen( name ) + 1] ) );
386 if (!control)
387 return NULL;
388
390 list_add_tail( &dialog->controls, &control->entry );
394 control->value = NULL;
396 control->hIcon = NULL;
398 control->hDll = NULL;
399 control->tabnext = wcsdup( MSI_RecordGetString( rec, 11 ) );
400 control->type = wcsdup( MSI_RecordGetString( rec, 3 ) );
402 control->progress_max = 100;
404
405 x = MSI_RecordGetInteger( rec, 4 );
406 y = MSI_RecordGetInteger( rec, 5 );
407 width = MSI_RecordGetInteger( rec, 6 );
408 height = MSI_RecordGetInteger( rec, 7 );
409
414
415 if( text )
416 {
417 deformat_string( dialog->package, text, &title_font );
418 font = dialog_get_style( title_font, &title );
419 }
420
421 if (!wcsicmp( MSI_RecordGetString( rec, 3 ), L"Line" ))
422 height = 2; /* line is exactly 2 units in height */
423
424 control->hwnd = CreateWindowExW( exstyle, szCls, title, style,
425 x, y, width, height, parent, NULL, NULL, NULL );
426
427 TRACE("Dialog %s control %s hwnd %p\n",
429
430 dialog_set_font( dialog, control->hwnd, font ? font : dialog->default_font );
431
432 free( title_font );
433 free( font );
434
435 return control;
436}
437
439{
440 MSIRECORD *rec;
441 LPWSTR text;
442
443 rec = MSI_QueryGetRecord( dialog->package->db, L"SELECT * FROM `UIText` WHERE `Key` = '%s'", key );
444 if (!rec) return NULL;
445 text = wcsdup( MSI_RecordGetString( rec, 2 ) );
446 msiobj_release( &rec->hdr );
447 return text;
448}
449
451{
452 MSIRECORD *rec;
453 HANDLE himage = NULL;
454 LPWSTR tmp;
455 UINT r;
456
457 TRACE("%p %s %u %u %08x\n", db, debugstr_w(name), cx, cy, flags);
458
459 if (!(tmp = msi_create_temp_file( db ))) return NULL;
460
461 rec = MSI_QueryGetRecord( db, L"SELECT * FROM `Binary` WHERE `Name` = '%s'", name );
462 if( rec )
463 {
464 r = MSI_RecordStreamToFile( rec, 2, tmp );
465 if( r == ERROR_SUCCESS )
466 {
467 himage = LoadImageW( 0, tmp, type, cx, cy, flags );
468 }
469 msiobj_release( &rec->hdr );
470 }
471 DeleteFileW( tmp );
472
473 free( tmp );
474 return himage;
475}
476
478{
479 DWORD cx = 0, cy = 0, flags;
480
483 {
484 flags &= ~LR_DEFAULTSIZE;
486 {
487 cx += 16;
488 cy += 16;
489 }
491 {
492 cx += 32;
493 cy += 32;
494 }
495 /* msidbControlAttributesIconSize48 handled by above logic */
496 }
497 return load_image( db, text, IMAGE_ICON, cx, cy, flags );
498}
499
501{
502 struct control *control;
503
504 LIST_FOR_EACH_ENTRY( control, &dialog->controls, struct control, entry )
505 {
508 }
509}
510
512{
513 struct control *control;
514
515 LIST_FOR_EACH_ENTRY( control, &dialog->controls, struct control, entry )
516 {
517 if ( control->property && control->update )
519 }
520}
521
522static void dialog_set_property( MSIPACKAGE *package, const WCHAR *property, const WCHAR *value )
523{
524 UINT r = msi_set_property( package->db, property, value, -1 );
525 if (r == ERROR_SUCCESS && !wcscmp( property, L"SourceDir" ))
526 msi_reset_source_folders( package );
527}
528
530{
531 TVITEMW tvi;
532
533 /* get the feature from the item */
534 memset( &tvi, 0, sizeof tvi );
535 tvi.hItem = hItem;
537 SendMessageW( hwnd, TVM_GETITEMW, 0, (LPARAM)&tvi );
538 return (MSIFEATURE *)tvi.lParam;
539}
540
542{
547};
548
550{
551 struct msi_selection_tree_info *info = GetPropW( control->hwnd, L"MSIDATA" );
552 return seltree_feature_from_item( control->hwnd, info->selected );
553}
554
556{
557 struct control* ctrl;
558
560 if (!ctrl)
561 return;
562 if( !wcscmp( attribute, L"Text" ) )
563 {
564 const WCHAR *font_text, *text = NULL;
565 WCHAR *font, *text_fmt = NULL;
566
567 font_text = MSI_RecordGetString( rec , 1 );
568 font = dialog_get_style( font_text, &text );
569 deformat_string( dialog->package, text, &text_fmt );
570 if (text_fmt) text = text_fmt;
571 else text = L"";
572
573 SetWindowTextW( ctrl->hwnd, text );
574
575 free( font );
576 free( text_fmt );
578 }
579 else if( !wcscmp( attribute, L"Progress" ) )
580 {
581 DWORD func, val1, val2, units;
582
583 func = MSI_RecordGetInteger( rec, 1 );
584 val1 = MSI_RecordGetInteger( rec, 2 );
585 val2 = MSI_RecordGetInteger( rec, 3 );
586
587 TRACE( "progress: func %lu val1 %lu val2 %lu\n", func, val1, val2 );
588
589 units = val1 / 512;
590 switch (func)
591 {
592 case 0: /* init */
593 SendMessageW( ctrl->hwnd, PBM_SETRANGE, 0, MAKELPARAM(0,100) );
594 if (val2)
595 {
596 ctrl->progress_max = units ? units : 100;
597 ctrl->progress_current = units;
598 ctrl->progress_backwards = TRUE;
599 SendMessageW( ctrl->hwnd, PBM_SETPOS, 100, 0 );
600 }
601 else
602 {
603 ctrl->progress_max = units ? units : 100;
604 ctrl->progress_current = 0;
605 ctrl->progress_backwards = FALSE;
606 SendMessageW( ctrl->hwnd, PBM_SETPOS, 0, 0 );
607 }
608 break;
609 case 1: /* action data increment */
610 if (val2) dialog->package->action_progress_increment = val1;
611 else dialog->package->action_progress_increment = 0;
612 break;
613 case 2: /* move */
614 if (ctrl->progress_backwards)
615 {
616 if (units >= ctrl->progress_current) ctrl->progress_current -= units;
617 else ctrl->progress_current = 0;
618 }
619 else
620 {
621 if (ctrl->progress_current + units < ctrl->progress_max) ctrl->progress_current += units;
622 else ctrl->progress_current = ctrl->progress_max;
623 }
624 SendMessageW( ctrl->hwnd, PBM_SETPOS, MulDiv(100, ctrl->progress_current, ctrl->progress_max), 0 );
625 break;
626 case 3: /* add */
627 ctrl->progress_max += units;
628 break;
629 default:
630 FIXME( "unknown progress message %lu\n", func );
631 break;
632 }
633 }
634 else if ( !wcscmp( attribute, L"Property" ) )
635 {
637 if (feature) dialog_set_property( dialog->package, ctrl->property, feature->Directory );
638 }
639 else if ( !wcscmp( attribute, L"SelectionPath" ) )
640 {
643 if (!path) return;
644 SetWindowTextW( ctrl->hwnd, path );
645 free( path );
646 }
647 else
648 {
649 FIXME("Attribute %s not being set\n", debugstr_w(attribute));
650 return;
651 }
652}
653
654static void event_subscribe( msi_dialog *dialog, const WCHAR *event, const WCHAR *control, const WCHAR *attribute )
655{
656 struct subscriber *sub;
657
658 TRACE("dialog %s event %s control %s attribute %s\n", debugstr_w(dialog->name), debugstr_w(event),
660
661 LIST_FOR_EACH_ENTRY( sub, &dialog->package->subscriptions, struct subscriber, entry )
662 {
663 if (sub->dialog == dialog &&
664 !wcsicmp( sub->event, event ) &&
665 !wcsicmp( sub->control, control ) &&
666 !wcsicmp( sub->attribute, attribute ))
667 {
668 TRACE("already subscribed\n");
669 return;
670 };
671 }
672 if (!(sub = malloc( sizeof(*sub) ))) return;
673 sub->dialog = dialog;
674 sub->event = wcsdup( event );
675 sub->control = wcsdup( control );
676 sub->attribute = wcsdup( attribute );
677 list_add_tail( &dialog->package->subscriptions, &sub->entry );
678}
679
681{
684};
685
687{
688 struct dialog_control *dc = param;
689 const WCHAR *event = MSI_RecordGetString( row, 3 );
690 const WCHAR *attribute = MSI_RecordGetString( row, 4 );
691
692 event_subscribe( dc->dialog, event, dc->control, attribute );
693 return ERROR_SUCCESS;
694}
695
697{
698 MSIQUERY *view;
700 {
701 dialog,
702 control
703 };
704
705 if (!MSI_OpenQuery( dialog->package->db, &view,
706 L"SELECT * FROM `EventMapping` WHERE `Dialog_` = '%s' AND `Control_` = '%s'",
707 dialog->name, control ))
708 {
710 msiobj_release( &view->hdr );
711 }
712}
713
714/* everything except radio buttons */
715static struct control *dialog_add_control( msi_dialog *dialog, MSIRECORD *rec, const WCHAR *szCls, DWORD style )
716{
718 const WCHAR *text = NULL, *name, *control_type;
719 DWORD exstyle = 0;
720
721 name = MSI_RecordGetString( rec, 2 );
724 if (wcscmp( control_type, L"ScrollableText" )) text = MSI_RecordGetString( rec, 10 );
725
726 TRACE( "%s, %s, %#lx, %s, %#lx\n", debugstr_w(szCls), debugstr_w(name), attributes, debugstr_w(text), style );
727
729 style |= WS_VISIBLE;
733 exstyle |= WS_EX_CLIENTEDGE;
734
736
737 return dialog_create_window( dialog, rec, exstyle, szCls, name, text, style, dialog->hwnd );
738}
739
741{
742 struct font *font;
745};
746
747/*
748 * we don't erase our own background,
749 * so we have to make sure that the parent window redraws first
750 */
752{
753 HWND hParent;
754 RECT rc;
755
756 hParent = GetParent( hWnd );
757 GetWindowRect( hWnd, &rc );
758 MapWindowPoints( NULL, hParent, (LPPOINT) &rc, 2 );
759 InvalidateRect( hParent, &rc, TRUE );
760}
761
763{
764 struct msi_text_info *info;
765 LRESULT r = 0;
766
767 TRACE( "%p %04x %#Ix %#Ix\n", hWnd, msg, wParam, lParam );
768
769 info = GetPropW(hWnd, L"MSIDATA");
770
771 if( msg == WM_CTLCOLORSTATIC &&
772 ( info->attributes & msidbControlAttributesTransparent ) )
773 {
776 }
777
778 r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
779 if ( info->font )
780 SetTextColor( (HDC)wParam, info->font->color );
781
782 switch( msg )
783 {
784 case WM_SETTEXT:
786 break;
787 case WM_NCDESTROY:
788 free( info );
789 RemovePropW( hWnd, L"MSIDATA" );
790 break;
791 }
792
793 return r;
794}
795
797{
798 struct control *control;
799 struct msi_text_info *info;
800 LPCWSTR text, ptr, prop, control_name;
801 LPWSTR font_name;
802
803 TRACE("%p %p\n", dialog, rec);
804
805 control = dialog_add_control( dialog, rec, L"Static", SS_LEFT | WS_GROUP );
806 if( !control )
808
809 info = malloc( sizeof *info );
810 if( !info )
811 return ERROR_SUCCESS;
812
813 control_name = MSI_RecordGetString( rec, 2 );
815 prop = MSI_RecordGetString( rec, 9 );
817
818 text = MSI_RecordGetString( rec, 10 );
819 font_name = dialog_get_style( text, &ptr );
820 info->font = ( font_name ) ? dialog_find_font( dialog, font_name ) : NULL;
821 free( font_name );
822
823 info->attributes = MSI_RecordGetInteger( rec, 8 );
824 if( info->attributes & msidbControlAttributesTransparent )
826
829 SetPropW( control->hwnd, L"MSIDATA", info );
830
831 event_subscribe( dialog, L"SelectionPath", control_name, L"SelectionPath" );
832 return ERROR_SUCCESS;
833}
834
835/* strip any leading text style label from text field */
836static WCHAR *get_binary_name( MSIPACKAGE *package, MSIRECORD *rec )
837{
838 WCHAR *p, *text;
839
840 text = get_deformatted_field( package, rec, 10 );
841 if (!text)
842 return NULL;
843
844 p = text;
845 while (*p && *p != '{') p++;
846 if (!*p++) return text;
847
848 while (*p && *p != '}') p++;
849 if (!*p++) return text;
850
851 p = wcsdup( p );
852 free( text );
853 return p;
854}
855
857{
858 LPWSTR p, prop, arg_fmt = NULL;
859 UINT len;
860
861 len = lstrlenW( event );
862 prop = malloc( len * sizeof(WCHAR) );
863 lstrcpyW( prop, &event[1] );
864 p = wcschr( prop, ']' );
865 if (p && (p[1] == 0 || p[1] == ' '))
866 {
867 *p = 0;
868 if (wcscmp( L"{}", arg )) deformat_string( dialog->package, arg, &arg_fmt );
869 dialog_set_property( dialog->package, prop, arg_fmt );
871 free( arg_fmt );
872 }
873 else ERR("Badly formatted property string - what happens?\n");
874 free( prop );
875 return ERROR_SUCCESS;
876}
877
879{
880 LPWSTR event_fmt = NULL, arg_fmt = NULL;
881
882 TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
883
884 deformat_string( dialog->package, event, &event_fmt );
885 deformat_string( dialog->package, arg, &arg_fmt );
886
887 dialog->event_handler( dialog, event_fmt, arg_fmt );
888
889 free( event_fmt );
890 free( arg_fmt );
891
892 return ERROR_SUCCESS;
893}
894
896{
899 UINT r;
900
901 condition = MSI_RecordGetString( rec, 5 );
904 {
905 event = MSI_RecordGetString( rec, 3 );
906 arg = MSI_RecordGetString( rec, 4 );
907 if (event[0] == '[')
909 else
911 }
912 return ERROR_SUCCESS;
913}
914
916{
917 MSIQUERY *view;
918 UINT r;
919
920 if (HIWORD(param) != BN_CLICKED)
921 return ERROR_SUCCESS;
922
923 r = MSI_OpenQuery( dialog->package->db, &view,
924 L"SELECT * FROM `ControlEvent` WHERE `Dialog_` = '%s' AND `Control_` = '%s' ORDER BY `Ordering`",
925 dialog->name, control->name );
926 if (r != ERROR_SUCCESS)
927 {
928 ERR("query failed\n");
929 return ERROR_SUCCESS;
930 }
932 msiobj_release( &view->hdr );
933
934 /* dialog control events must be processed last regardless of ordering */
935 if (dialog->pending_event)
936 {
937 r = dialog->pending_event( dialog, dialog->pending_argument );
938
939 free( dialog->pending_argument );
940 dialog->pending_event = NULL;
941 dialog->pending_argument = NULL;
942 }
943 return r;
944}
945
947{
948 HBITMAP hOleBitmap = 0, hBitmap = 0, hOldSrcBitmap, hOldDestBitmap;
949 MSIRECORD *rec = NULL;
950 IStream *stm = NULL;
951 IPicture *pic = NULL;
952 HDC srcdc, destdc;
953 BITMAP bm;
954 UINT r;
955
956 rec = MSI_QueryGetRecord( db, L"SELECT * FROM `Binary` WHERE `Name` = '%s'", name );
957 if (!rec)
958 goto end;
959
960 r = MSI_RecordGetIStream( rec, 2, &stm );
961 msiobj_release( &rec->hdr );
962 if (r != ERROR_SUCCESS)
963 goto end;
964
965 r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (void **)&pic );
966 IStream_Release( stm );
967 if (FAILED( r ))
968 {
969 ERR("failed to load picture\n");
970 goto end;
971 }
972
973 r = IPicture_get_Handle( pic, (OLE_HANDLE *)&hOleBitmap );
974 if (FAILED( r ))
975 {
976 ERR("failed to get bitmap handle\n");
977 goto end;
978 }
979
980 /* make the bitmap the desired size */
981 r = GetObjectW( hOleBitmap, sizeof(bm), &bm );
982 if (r != sizeof(bm))
983 {
984 ERR("failed to get bitmap size\n");
985 goto end;
986 }
987
988 if (flags & LR_DEFAULTSIZE)
989 {
990 cx = bm.bmWidth;
991 cy = bm.bmHeight;
992 }
993
994 srcdc = CreateCompatibleDC( NULL );
995 hOldSrcBitmap = SelectObject( srcdc, hOleBitmap );
996 destdc = CreateCompatibleDC( NULL );
997 hBitmap = CreateCompatibleBitmap( srcdc, cx, cy );
998 hOldDestBitmap = SelectObject( destdc, hBitmap );
999 StretchBlt( destdc, 0, 0, cx, cy, srcdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY );
1000 SelectObject( srcdc, hOldSrcBitmap );
1001 SelectObject( destdc, hOldDestBitmap );
1002 DeleteDC( srcdc );
1003 DeleteDC( destdc );
1004
1005end:
1006 if (pic) IPicture_Release( pic );
1007 return hBitmap;
1008}
1009
1011{
1012 struct control *control;
1013 UINT attributes, style, cx = 0, cy = 0, flags = 0;
1014 WCHAR *name = NULL;
1015
1016 TRACE("%p %p\n", dialog, rec);
1017
1018 style = WS_TABSTOP;
1019 attributes = MSI_RecordGetInteger( rec, 8 );
1022 {
1023 style |= BS_BITMAP;
1025 else
1026 {
1029 }
1030 }
1031
1032 control = dialog_add_control( dialog, rec, L"BUTTON", style );
1033 if (!control)
1034 return ERROR_FUNCTION_FAILED;
1035
1037
1039 {
1040 name = get_binary_name( dialog->package, rec );
1041 control->hIcon = load_icon( dialog->package->db, name, attributes );
1042 if (control->hIcon)
1043 {
1045 }
1046 else ERR("Failed to load icon %s\n", debugstr_w(name));
1047 }
1049 {
1050 name = get_binary_name( dialog->package, rec );
1051 control->hBitmap = load_picture( dialog->package->db, name, cx, cy, flags );
1052 if (control->hBitmap)
1053 {
1055 }
1056 else ERR("Failed to load bitmap %s\n", debugstr_w(name));
1057 }
1058
1059 free( name );
1060 return ERROR_SUCCESS;
1061}
1062
1064{
1065 MSIRECORD *rec = NULL;
1066 LPWSTR ret = NULL;
1067
1068 /* find if there is a value associated with the checkbox */
1069 rec = MSI_QueryGetRecord( dialog->package->db, L"SELECT * FROM `CheckBox` WHERE `Property` = '%s'", prop );
1070 if (!rec)
1071 return ret;
1072
1073 ret = get_deformatted_field( dialog->package, rec, 2 );
1074 if( ret && !ret[0] )
1075 {
1076 free( ret );
1077 ret = NULL;
1078 }
1079 msiobj_release( &rec->hdr );
1080 if (ret)
1081 return ret;
1082
1083 ret = msi_dup_property( dialog->package->db, prop );
1084 if( ret && !ret[0] )
1085 {
1086 free( ret );
1087 ret = NULL;
1088 }
1089
1090 return ret;
1091}
1092
1094{
1095 WCHAR state[2] = {0};
1096 DWORD sz = 2;
1097
1098 msi_get_property( dialog->package->db, control->property, state, &sz );
1099 return state[0] ? 1 : 0;
1100}
1101
1103{
1104 LPCWSTR val;
1105
1106 /* if uncheck then the property is set to NULL */
1107 if (!state)
1108 {
1110 return;
1111 }
1112
1113 /* check for a custom state */
1114 if (control->value && control->value[0])
1115 val = control->value;
1116 else
1117 val = L"1";
1118
1120}
1121
1123{
1126}
1127
1129{
1130 UINT state;
1131
1132 if (HIWORD(param) != BN_CLICKED)
1133 return ERROR_SUCCESS;
1134
1135 TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
1136
1138 state = state ? 0 : 1;
1141
1143}
1144
1146{
1147 struct control *control;
1148 LPCWSTR prop;
1149
1150 TRACE("%p %p\n", dialog, rec);
1151
1155 prop = MSI_RecordGetString( rec, 9 );
1156 if (prop)
1157 {
1158 control->property = wcsdup( prop );
1160 TRACE("control %s value %s\n", debugstr_w(control->property), debugstr_w(control->value));
1161 }
1163 return ERROR_SUCCESS;
1164}
1165
1167{
1168 if (!dialog_add_control( dialog, rec, L"Static", SS_ETCHEDHORZ | SS_SUNKEN))
1169 return ERROR_FUNCTION_FAILED;
1170
1171 return ERROR_SUCCESS;
1172}
1173
1174/******************** Scroll Text ********************************************/
1175
1177{
1181};
1182
1184{
1185 struct msi_scrolltext_info *info;
1186 HRESULT r;
1187
1188 TRACE( "%p %04x %#Ix %#Ix\n", hWnd, msg, wParam, lParam );
1189
1190 info = GetPropW( hWnd, L"MSIDATA" );
1191
1192 r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
1193
1194 switch( msg )
1195 {
1196 case WM_GETDLGCODE:
1197 return DLGC_WANTARROWS;
1198 case WM_NCDESTROY:
1199 free( info );
1200 RemovePropW( hWnd, L"MSIDATA" );
1201 break;
1202 case WM_PAINT:
1203 /* native MSI sets a wait cursor here */
1204 dialog_button_handler( info->dialog, info->control, BN_CLICKED );
1205 break;
1206 }
1207 return r;
1208}
1209
1211{
1215};
1216
1218{
1219 struct msi_streamin_info *info = (struct msi_streamin_info*) arg;
1220
1221 if( (count + info->offset) > info->length )
1222 count = info->length - info->offset;
1223 memcpy( buffer, &info->string[ info->offset ], count );
1224 *pcb = count;
1225 info->offset += count;
1226
1227 TRACE( "%lu/%lu\n", info->offset, info->length );
1228
1229 return 0;
1230}
1231
1232static void scrolltext_add_text( struct control *control, const WCHAR *text )
1233{
1234 struct msi_streamin_info info;
1235 EDITSTREAM es;
1236
1237 info.string = strdupWtoA( text );
1238 info.offset = 0;
1239 info.length = lstrlenA( info.string ) + 1;
1240
1241 es.dwCookie = (DWORD_PTR) &info;
1242 es.dwError = 0;
1243 es.pfnCallback = richedit_stream_in;
1244
1246
1247 free( info.string );
1248}
1249
1251{
1252 struct msi_scrolltext_info *info;
1253 struct control *control;
1254 HMODULE hRichedit;
1255 LPCWSTR text;
1256 DWORD style;
1257
1258 info = malloc( sizeof *info );
1259 if (!info)
1260 return ERROR_FUNCTION_FAILED;
1261
1262 hRichedit = LoadLibraryA("riched20");
1263
1266 control = dialog_add_control( dialog, rec, L"RichEdit20W", style );
1267 if (!control)
1268 {
1269 FreeLibrary( hRichedit );
1270 free( info );
1271 return ERROR_FUNCTION_FAILED;
1272 }
1273
1274 control->hDll = hRichedit;
1275
1276 info->dialog = dialog;
1277 info->control = control;
1278
1279 /* subclass the static control */
1282 SetPropW( control->hwnd, L"MSIDATA", info );
1283
1284 /* add the text into the richedit */
1285 text = MSI_RecordGetString( rec, 10 );
1286 if (text)
1288
1289 return ERROR_SUCCESS;
1290}
1291
1292
1294{
1296 struct control *control;
1297 LPWSTR name;
1298
1301
1302 attributes = MSI_RecordGetInteger( rec, 8 );
1304 {
1307 }
1308
1309 control = dialog_add_control( dialog, rec, L"Static", style );
1310 cx = MSI_RecordGetInteger( rec, 6 );
1311 cy = MSI_RecordGetInteger( rec, 7 );
1314
1315 name = get_binary_name( dialog->package, rec );
1316 control->hBitmap = load_picture( dialog->package->db, name, cx, cy, flags );
1317 if( control->hBitmap )
1320 else
1321 ERR("Failed to load bitmap %s\n", debugstr_w(name));
1322
1323 free( name );
1324
1325 return ERROR_SUCCESS;
1326}
1327
1329{
1330 struct control *control;
1332 LPWSTR name;
1333
1334 TRACE("\n");
1335
1337
1338 attributes = MSI_RecordGetInteger( rec, 8 );
1339 name = get_binary_name( dialog->package, rec );
1340 control->hIcon = load_icon( dialog->package->db, name, attributes );
1341 if( control->hIcon )
1343 else
1344 ERR("Failed to load bitmap %s\n", debugstr_w(name));
1345 free( name );
1346 return ERROR_SUCCESS;
1347}
1348
1349/******************** Combo Box ***************************************/
1350
1352{
1359};
1360
1362{
1363 struct msi_combobox_info *info;
1364 LRESULT r;
1365 DWORD j;
1366
1367 TRACE( "%p %04x %#Ix %#Ix\n", hWnd, msg, wParam, lParam );
1368
1369 info = GetPropW( hWnd, L"MSIDATA" );
1370 if (!info)
1371 return 0;
1372
1373 r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
1374
1375 switch (msg)
1376 {
1377 case WM_NCDESTROY:
1378 for (j = 0; j < info->num_items; j++)
1379 free( info->items[j] );
1380 free( info->items );
1381 free( info );
1382 RemovePropW( hWnd, L"MSIDATA" );
1383 break;
1384 }
1385
1386 return r;
1387}
1388
1390{
1391 struct msi_combobox_info *info = param;
1393 int pos;
1394
1395 value = MSI_RecordGetString( rec, 3 );
1396 text = MSI_RecordGetString( rec, 4 );
1397
1398 info->items[info->addpos_items] = wcsdup( value );
1399
1400 pos = SendMessageW( info->hwnd, CB_ADDSTRING, 0, (LPARAM)text );
1401 SendMessageW( info->hwnd, CB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] );
1402 info->addpos_items++;
1403
1404 return ERROR_SUCCESS;
1405}
1406
1408{
1409 MSIQUERY *view;
1410 DWORD count;
1411 UINT r;
1412
1413 r = MSI_OpenQuery( info->dialog->package->db, &view,
1414 L"SELECT * FROM `ComboBox` WHERE `Property` = '%s' ORDER BY `Order`", property );
1415 if (r != ERROR_SUCCESS)
1416 return r;
1417
1418 /* just get the number of records */
1419 count = 0;
1421 if (r != ERROR_SUCCESS)
1422 {
1423 msiobj_release( &view->hdr );
1424 return r;
1425 }
1426 info->num_items = count;
1427 info->items = malloc( sizeof(*info->items) * count );
1428
1430 msiobj_release( &view->hdr );
1431 return r;
1432}
1433
1435{
1437 struct control *control;
1439 UINT r;
1440
1441 name = MSI_RecordGetString( rec, 2 );
1442 action = MSI_RecordGetString( rec, 3 );
1443 condition = MSI_RecordGetString( rec, 4 );
1446 if (r == MSICONDITION_TRUE && control)
1447 {
1448 TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
1449
1450 /* FIXME: case sensitive? */
1451 if (!wcscmp( action, L"Hide" ))
1453 else if (!wcscmp( action, L"Show" ))
1455 else if (!wcscmp( action, L"Disable" ))
1457 else if (!wcscmp( action, L"Enable" ))
1459 else if (!wcscmp( action, L"Default" ))
1461 else
1462 FIXME("Unhandled action %s\n", debugstr_w(action));
1463 }
1464 return ERROR_SUCCESS;
1465}
1466
1468{
1469 UINT r;
1470 MSIQUERY *view;
1471 MSIPACKAGE *package = dialog->package;
1472
1473 TRACE("%p %s\n", dialog, debugstr_w(dialog->name));
1474
1475 /* query the Control table for all the elements of the control */
1476 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `ControlCondition` WHERE `Dialog_` = '%s'", dialog->name );
1477 if (r != ERROR_SUCCESS)
1478 return ERROR_SUCCESS;
1479
1481 msiobj_release( &view->hdr );
1482 return r;
1483}
1484
1486{
1487 struct msi_combobox_info *info;
1488 int index;
1489 LPWSTR value;
1490
1492 return ERROR_SUCCESS;
1493
1494 info = GetPropW( control->hwnd, L"MSIDATA" );
1496 if (index == CB_ERR)
1498 else
1500
1501 dialog_set_property( info->dialog->package, control->property, value );
1503
1504 if (index == CB_ERR)
1505 free( value );
1506
1507 return ERROR_SUCCESS;
1508}
1509
1511{
1512 struct msi_combobox_info *info;
1513 LPWSTR value, tmp;
1514 DWORD j;
1515
1516 info = GetPropW( control->hwnd, L"MSIDATA" );
1517
1518 value = msi_dup_property( dialog->package->db, control->property );
1519 if (!value)
1520 {
1522 return;
1523 }
1524
1525 for (j = 0; j < info->num_items; j++)
1526 {
1528 if (!wcscmp( value, tmp ))
1529 break;
1530 }
1531
1532 if (j < info->num_items)
1533 {
1535 }
1536 else
1537 {
1540 }
1541
1542 free( value );
1543}
1544
1546{
1547 struct msi_combobox_info *info;
1548 struct control *control;
1550 LPCWSTR prop;
1551
1552 info = malloc( sizeof *info );
1553 if (!info)
1554 return ERROR_FUNCTION_FAILED;
1555
1557 attributes = MSI_RecordGetInteger( rec, 8 );
1559 style |= CBS_SORT;
1562 else
1564
1566 if (!control)
1567 {
1568 free( info );
1569 return ERROR_FUNCTION_FAILED;
1570 }
1571
1574
1575 prop = MSI_RecordGetString( rec, 9 );
1577
1578 /* subclass */
1579 info->dialog = dialog;
1580 info->hwnd = control->hwnd;
1581 info->items = NULL;
1582 info->addpos_items = 0;
1585 SetPropW( control->hwnd, L"MSIDATA", info );
1586
1587 if (control->property)
1589
1591
1592 return ERROR_SUCCESS;
1593}
1594
1596{
1597 LPWSTR buf;
1598
1599 if (HIWORD(param) != EN_CHANGE)
1600 return ERROR_SUCCESS;
1601
1602 TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
1603
1606 free( buf );
1607
1608 return ERROR_SUCCESS;
1609}
1610
1611/* length of 2^32 + 1 */
1612#define MAX_NUM_DIGITS 11
1613
1615{
1616 struct control *control;
1617 LPCWSTR prop, text;
1618 LPWSTR val, begin, end;
1620 DWORD limit;
1621
1624
1625 text = MSI_RecordGetString( rec, 10 );
1626 if ( text )
1627 {
1628 begin = wcschr( text, '{' );
1629 end = wcschr( text, '}' );
1630
1631 if ( begin && end && end > begin &&
1632 begin[0] >= '0' && begin[0] <= '9' &&
1634 {
1635 lstrcpynW( num, begin + 1, end - begin );
1636 limit = wcstol( num, NULL, 10 );
1637
1639 }
1640 }
1641
1642 prop = MSI_RecordGetString( rec, 9 );
1643 if( prop )
1644 control->property = wcsdup( prop );
1645
1646 val = msi_dup_property( dialog->package->db, control->property );
1648 free( val );
1649 return ERROR_SUCCESS;
1650}
1651
1652/******************** Masked Edit ********************************************/
1653
1654#define MASK_MAX_GROUPS 20
1655
1657{
1662};
1663
1665{
1673};
1674
1676{
1677 switch (type)
1678 {
1679 case '%':
1680 case '#':
1681 case '&':
1682 case '`':
1683 case '?':
1684 case '^':
1685 return TRUE;
1686 }
1687 return FALSE;
1688}
1689
1691{
1692 LPWSTR val;
1693 UINT i, n, r;
1694
1695 val = malloc( (info->num_chars + 1) * sizeof(WCHAR) );
1696 for( i=0, n=0; i<info->num_groups; i++ )
1697 {
1698 if (info->group[i].len == ~0u)
1699 {
1700 UINT len = SendMessageW( info->group[i].hwnd, WM_GETTEXTLENGTH, 0, 0 );
1701 val = realloc( val, (len + 1) * sizeof(WCHAR) );
1702 GetWindowTextW( info->group[i].hwnd, val, len + 1 );
1703 }
1704 else
1705 {
1706 if (info->group[i].len + n > info->num_chars)
1707 {
1708 ERR("can't fit control %d text into template\n",i);
1709 break;
1710 }
1711 if (!mask_editable(info->group[i].type))
1712 {
1713 for(r=0; r<info->group[i].len; r++)
1714 val[n+r] = info->group[i].type;
1715 val[n+r] = 0;
1716 }
1717 else
1718 {
1719 r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 );
1720 if( r != info->group[i].len )
1721 break;
1722 }
1723 n += r;
1724 }
1725 }
1726
1727 TRACE("%d/%d controls were good\n", i, info->num_groups);
1728
1729 if( i == info->num_groups )
1730 {
1731 TRACE("Set property %s to %s\n", debugstr_w(info->prop), debugstr_w(val));
1732 dialog_set_property( info->dialog->package, info->prop, val );
1734 }
1735 free( val );
1736}
1737
1738/* now move to the next control if necessary */
1740{
1741 HWND hWndNext;
1742 UINT len, i;
1743
1744 for( i=0; i<info->num_groups; i++ )
1745 if( info->group[i].hwnd == hWnd )
1746 break;
1747
1748 /* don't move from the last control */
1749 if( i >= (info->num_groups-1) )
1750 return;
1751
1753 if( len < info->group[i].len )
1754 return;
1755
1756 hWndNext = GetNextDlgTabItem( GetParent( hWnd ), hWnd, FALSE );
1757 SetFocus( hWndNext );
1758}
1759
1761{
1762 struct msi_maskedit_info *info;
1763 HRESULT r;
1764
1765 TRACE("%p %04x %#Ix %#Ix\n", hWnd, msg, wParam, lParam);
1766
1767 info = GetPropW(hWnd, L"MSIDATA");
1768
1769 r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
1770
1771 switch( msg )
1772 {
1773 case WM_COMMAND:
1774 if (HIWORD(wParam) == EN_CHANGE)
1775 {
1778 }
1779 break;
1780 case WM_NCDESTROY:
1781 free( info->prop );
1782 free( info );
1783 RemovePropW( hWnd, L"MSIDATA" );
1784 break;
1785 }
1786
1787 return r;
1788}
1789
1790/* fish the various bits of the property out and put them in the control */
1791static void maskedit_set_text( struct msi_maskedit_info *info, const WCHAR *text )
1792{
1793 LPCWSTR p;
1794 UINT i;
1795
1796 p = text;
1797 for( i = 0; i < info->num_groups; i++ )
1798 {
1799 if( info->group[i].len < lstrlenW( p ) )
1800 {
1801 WCHAR *chunk = wcsdup( p );
1802 chunk[ info->group[i].len ] = 0;
1803 SetWindowTextW( info->group[i].hwnd, chunk );
1804 free( chunk );
1805 }
1806 else
1807 {
1808 SetWindowTextW( info->group[i].hwnd, p );
1809 break;
1810 }
1811 p += info->group[i].len;
1812 }
1813}
1814
1816{
1817 struct msi_maskedit_info *info;
1818 int i = 0, n = 0, total = 0;
1819 LPCWSTR p;
1820
1821 TRACE("masked control, template %s\n", debugstr_w(mask));
1822
1823 if( !mask )
1824 return NULL;
1825
1826 info = calloc( 1, sizeof *info );
1827 if( !info )
1828 return info;
1829
1830 p = wcschr(mask, '<');
1831 if( p )
1832 p++;
1833 else
1834 p = mask;
1835
1836 for( i=0; i<MASK_MAX_GROUPS; i++ )
1837 {
1838 /* stop at the end of the string */
1839 if( p[0] == 0 || p[0] == '>' )
1840 {
1841 if (!total)
1842 {
1843 /* create a group for the empty mask */
1844 info->group[0].type = '&';
1845 info->group[0].len = ~0u;
1846 i = 1;
1847 }
1848 break;
1849 }
1850
1851 /* count the number of the same identifier */
1852 for( n=0; p[n] == p[0]; n++ )
1853 ;
1854 info->group[i].ofs = total;
1855 info->group[i].type = p[0];
1856 if( p[n] == '=' )
1857 {
1858 n++;
1859 total++; /* an extra not part of the group */
1860 }
1861 info->group[i].len = n;
1862 total += n;
1863 p += n;
1864 }
1865
1866 TRACE("%d characters in %d groups\n", total, i );
1867 if( i == MASK_MAX_GROUPS )
1868 ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask));
1869
1870 info->num_chars = total;
1871 info->num_groups = i;
1872
1873 return info;
1874}
1875
1877{
1878 DWORD width, height, style, wx, ww;
1879 RECT rect;
1880 HWND hwnd;
1881 UINT i;
1882
1884
1885 GetClientRect( info->hwnd, &rect );
1886
1887 width = rect.right - rect.left;
1888 height = rect.bottom - rect.top;
1889
1890 for( i = 0; i < info->num_groups; i++ )
1891 {
1892 if (!mask_editable( info->group[i].type ))
1893 continue;
1894 if (info->num_chars)
1895 {
1896 wx = (info->group[i].ofs * width) / info->num_chars;
1897 ww = (info->group[i].len * width) / info->num_chars;
1898 }
1899 else
1900 {
1901 wx = 0;
1902 ww = width;
1903 }
1904 hwnd = CreateWindowW( L"Edit", NULL, style, wx, 0, ww, height,
1905 info->hwnd, NULL, NULL, NULL );
1906 if( !hwnd )
1907 {
1908 ERR("failed to create mask edit sub window\n");
1909 break;
1910 }
1911
1912 SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 );
1913
1914 dialog_set_font( info->dialog, hwnd, font?font:info->dialog->default_font );
1915 info->group[i].hwnd = hwnd;
1916 }
1917}
1918
1919/*
1920 * office 2003 uses "73931<````=````=````=````=`````>@@@@@"
1921 * delphi 7 uses "<????-??????-??????-????>" and "<???-???>"
1922 * filemaker pro 7 uses "<^^^^=^^^^=^^^^=^^^^=^^^^=^^^^=^^^^^>"
1923 */
1925{
1926 LPWSTR font_mask, val = NULL, font;
1927 struct msi_maskedit_info *info = NULL;
1929 struct control *control;
1930 LPCWSTR prop, mask;
1931
1932 TRACE("\n");
1933
1934 font_mask = get_deformatted_field( dialog->package, rec, 10 );
1935 font = dialog_get_style( font_mask, &mask );
1936 if( !mask )
1937 {
1938 WARN("mask template is empty\n");
1939 goto end;
1940 }
1941
1943 if( !info )
1944 {
1945 ERR("template %s is invalid\n", debugstr_w(mask));
1946 goto end;
1947 }
1948
1949 info->dialog = dialog;
1950
1952 if( !control )
1953 {
1954 ERR("Failed to create maskedit container\n");
1956 goto end;
1957 }
1959
1960 info->hwnd = control->hwnd;
1961
1962 /* subclass the static control */
1963 info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC,
1965 SetPropW( control->hwnd, L"MSIDATA", info );
1966
1967 prop = MSI_RecordGetString( rec, 9 );
1968 if( prop )
1969 info->prop = wcsdup( prop );
1970
1972
1973 if( prop )
1974 {
1975 val = msi_dup_property( dialog->package->db, prop );
1976 if( val )
1977 {
1979 free( val );
1980 }
1981 }
1982
1983end:
1984 if( ret != ERROR_SUCCESS )
1985 free( info );
1986 free( font_mask );
1987 free( font );
1988 return ret;
1989}
1990
1991/******************** Progress Bar *****************************************/
1992
1994{
1995 struct control *control;
1997
1998 style = WS_VISIBLE;
1999 attributes = MSI_RecordGetInteger( rec, 8 );
2001 style |= PBS_SMOOTH;
2002
2004 if( !control )
2005 return ERROR_FUNCTION_FAILED;
2006
2007 event_subscribe( dialog, L"SetProgress", control->name, L"Progress" );
2008 return ERROR_SUCCESS;
2009}
2010
2011/******************** Path Edit ********************************************/
2012
2014{
2018};
2019
2021{
2022 WCHAR *prop, *path;
2024 if (!(prop = dialog_dup_property( dialog, control->property, indirect ))) return NULL;
2025 path = dialog_dup_property( dialog, prop, TRUE );
2026 free( prop );
2027 return path;
2028}
2029
2031{
2032 WCHAR *path;
2033
2034 if (!control && !(control = dialog_find_control_by_type( dialog, L"PathEdit" )))
2035 return;
2036
2037 if (!(path = get_path_property( dialog, control ))) return;
2039 SendMessageW( control->hwnd, EM_SETSEL, 0, -1 );
2040 free( path );
2041}
2042
2043/* FIXME: test when this should fail */
2045{
2046 if ( !path[0] )
2047 return FALSE;
2048
2049 if ( PathIsRelativeW( path ) )
2050 return FALSE;
2051
2052 return TRUE;
2053}
2054
2055/* returns TRUE if the path is valid, FALSE otherwise */
2057{
2058 LPWSTR buf, prop;
2059 BOOL indirect;
2060 BOOL valid;
2061
2064
2066
2067 if ( !dialog_verify_path( buf ) )
2068 {
2069 /* FIXME: display an error message box */
2070 ERR("Invalid path %s\n", debugstr_w( buf ));
2071 valid = FALSE;
2072 SetFocus( control->hwnd );
2073 }
2074 else
2075 {
2076 valid = TRUE;
2077 dialog_set_property( dialog->package, prop, buf );
2078 }
2079
2081
2082 TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name),
2083 debugstr_w(prop));
2084
2085 free( buf );
2086 free( prop );
2087
2088 return valid;
2089}
2090
2092{
2093 struct msi_pathedit_info *info = GetPropW(hWnd, L"MSIDATA");
2094 LRESULT r = 0;
2095
2096 TRACE("%p %04x %#Ix %#Ix\n", hWnd, msg, wParam, lParam);
2097
2098 if ( msg == WM_KILLFOCUS )
2099 {
2100 /* if the path is invalid, don't handle this message */
2101 if ( !dialog_onkillfocus( info->dialog, info->control ) )
2102 return 0;
2103 }
2104
2105 r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
2106
2107 if ( msg == WM_NCDESTROY )
2108 {
2109 free( info );
2110 RemovePropW( hWnd, L"MSIDATA" );
2111 }
2112
2113 return r;
2114}
2115
2117{
2118 struct msi_pathedit_info *info;
2119 struct control *control;
2120 LPCWSTR prop;
2121
2122 info = malloc( sizeof *info );
2123 if (!info)
2124 return ERROR_FUNCTION_FAILED;
2125
2128 prop = MSI_RecordGetString( rec, 9 );
2131
2132 info->dialog = dialog;
2133 info->control = control;
2136 SetPropW( control->hwnd, L"MSIDATA", info );
2137
2139
2140 return ERROR_SUCCESS;
2141}
2142
2144{
2145 if (HIWORD(param) != BN_CLICKED)
2146 return ERROR_SUCCESS;
2147
2148 TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property));
2149
2151
2153}
2154
2155/* radio buttons are a bit different from normal controls */
2157{
2159 msi_dialog *dialog = group->dialog;
2160 struct control *control;
2161 LPCWSTR prop, text, name;
2163
2164 name = MSI_RecordGetString( rec, 3 );
2165 text = MSI_RecordGetString( rec, 8 );
2166
2167 control = dialog_create_window( dialog, rec, 0, L"BUTTON", name, text, style,
2168 group->parent->hwnd );
2169 if (!control)
2170 return ERROR_FUNCTION_FAILED;
2172
2173 if (group->propval && !wcscmp( control->name, group->propval ))
2175
2176 prop = MSI_RecordGetString( rec, 1 );
2177 if( prop )
2178 control->property = wcsdup( prop );
2179
2180 return ERROR_SUCCESS;
2181}
2182
2184{
2186 return TRUE;
2187}
2188
2190{
2191 WNDPROC oldproc = (WNDPROC)GetPropW( hWnd, L"MSIDATA" );
2192 LRESULT r;
2193
2194 TRACE( "hWnd %p msg %04x wParam %#Ix lParam %#Ix\n", hWnd, msg, wParam, lParam );
2195
2196 if (msg == WM_COMMAND) /* Forward notifications to dialog */
2198
2199 r = CallWindowProcW( oldproc, hWnd, msg, wParam, lParam );
2200
2201 /* make sure the radio buttons show as disabled if the parent is disabled */
2202 if (msg == WM_ENABLE)
2204
2205 return r;
2206}
2207
2209{
2210 UINT r;
2211 LPCWSTR prop;
2212 struct control *control;
2213 MSIQUERY *view;
2215 MSIPACKAGE *package = dialog->package;
2216 WNDPROC oldproc;
2218
2219 prop = MSI_RecordGetString( rec, 9 );
2220
2221 TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop ));
2222
2223 attr = MSI_RecordGetInteger( rec, 8 );
2225 style |= WS_VISIBLE;
2227 style |= WS_DISABLED;
2229 style |= BS_GROUPBOX;
2230 else
2232
2233 /* Create parent group box to hold radio buttons */
2234 control = dialog_add_control( dialog, rec, L"BUTTON", style );
2235 if( !control )
2236 return ERROR_FUNCTION_FAILED;
2237
2240 SetPropW(control->hwnd, L"MSIDATA", oldproc);
2242
2243 if( prop )
2244 control->property = wcsdup( prop );
2245
2246 /* query the Radio Button table for all control in this group */
2247 r = MSI_OpenQuery( package->db, &view, L"SELECT * FROM `RadioButton` WHERE `Property` = '%s'", prop );
2248 if( r != ERROR_SUCCESS )
2249 {
2250 ERR("query failed for dialog %s radio group %s\n",
2251 debugstr_w(dialog->name), debugstr_w(prop));
2253 }
2254
2255 group.dialog = dialog;
2256 group.parent = control;
2257 group.propval = msi_dup_property( dialog->package->db, control->property );
2258
2260 msiobj_release( &view->hdr );
2261 free( group.propval );
2262 return r;
2263}
2264
2266{
2267 TVITEMW tvi;
2268 DWORD index = feature->ActionRequest;
2269
2270 TRACE("Feature %s -> %d %d %d\n", debugstr_w(feature->Title),
2271 feature->Installed, feature->Action, feature->ActionRequest);
2272
2275
2276 tvi.mask = TVIF_STATE;
2277 tvi.hItem = hItem;
2280
2281 SendMessageW( hwnd, TVM_SETITEMW, 0, (LPARAM) &tvi );
2282}
2283
2285{
2286 HMENU hMenu;
2287 INT r;
2288
2289 /* create a menu to display */
2290 hMenu = CreatePopupMenu();
2291
2292 /* FIXME: load strings from resources */
2293 AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_LOCAL, "Install feature locally");
2294 AppendMenuA( hMenu, MF_ENABLED, USER_INSTALLSTATE_ALL, "Install entire feature");
2295 AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ADVERTISED, "Install on demand");
2296 AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ABSENT, "Don't install");
2298 x, y, 0, hwnd, NULL );
2299 DestroyMenu( hMenu );
2300 return r;
2301}
2302
2305{
2306 feature->ActionRequest = state;
2309}
2310
2313{
2314 /* update all siblings */
2315 do
2316 {
2319
2322
2323 /* update this sibling's children */
2325 if (child)
2327 }
2328 while ((curr = (HTREEITEM)SendMessageW( hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)curr )));
2329}
2330
2332{
2335 MSIPACKAGE *package;
2336 union {
2337 RECT rc;
2338 POINT pt[2];
2340 } u;
2341 UINT r;
2342
2343 info = GetPropW(hwnd, L"MSIDATA");
2344 package = info->dialog->package;
2345
2347 if (!feature)
2348 {
2349 ERR("item %p feature was NULL\n", hItem);
2350 return 0;
2351 }
2352
2353 /* get the item's rectangle to put the menu just below it */
2354 u.hItem = hItem;
2356 MapWindowPoints( hwnd, NULL, u.pt, 2 );
2357
2358 r = seltree_popup_menu( hwnd, u.rc.left, u.rc.top );
2359
2360 switch (r)
2361 {
2364 /* fall-through */
2367 {
2370 if (child)
2372 }
2373 /* fall-through */
2374 case INSTALLSTATE_LOCAL:
2376 break;
2377 }
2378
2379 return 0;
2380}
2381
2383{
2385 TVHITTESTINFO tvhti;
2386 HRESULT r;
2387
2388 TRACE("%p %04x %#Ix %#Ix\n", hWnd, msg, wParam, lParam);
2389
2390 info = GetPropW(hWnd, L"MSIDATA");
2391
2392 switch( msg )
2393 {
2394 case WM_LBUTTONDOWN:
2395 tvhti.pt.x = (short)LOWORD( lParam );
2396 tvhti.pt.y = (short)HIWORD( lParam );
2397 tvhti.flags = 0;
2398 tvhti.hItem = 0;
2399 CallWindowProcW(info->oldproc, hWnd, TVM_HITTEST, 0, (LPARAM) &tvhti );
2400 if (tvhti.flags & TVHT_ONITEMSTATEICON)
2401 return seltree_menu( hWnd, tvhti.hItem );
2402 break;
2403 }
2404 r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
2405
2406 switch( msg )
2407 {
2408 case WM_NCDESTROY:
2409 free( info );
2410 RemovePropW( hWnd, L"MSIDATA" );
2411 break;
2412 }
2413 return r;
2414}
2415
2416static void seltree_add_child_features( MSIPACKAGE *package, HWND hwnd, const WCHAR *parent, HTREEITEM hParent )
2417{
2418 struct msi_selection_tree_info *info = GetPropW( hwnd, L"MSIDATA" );
2420 TVINSERTSTRUCTW tvis;
2421 HTREEITEM hitem, hfirst = NULL;
2422
2424 {
2425 if ( parent && feature->Feature_Parent && wcscmp( parent, feature->Feature_Parent ))
2426 continue;
2427 else if ( parent && !feature->Feature_Parent )
2428 continue;
2429 else if ( !parent && feature->Feature_Parent )
2430 continue;
2431
2432 if ( !feature->Title )
2433 continue;
2434
2435 if ( !feature->Display )
2436 continue;
2437
2438 memset( &tvis, 0, sizeof tvis );
2439 tvis.hParent = hParent;
2440 tvis.hInsertAfter = TVI_LAST;
2441 tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
2442 tvis.item.pszText = feature->Title;
2443 tvis.item.lParam = (LPARAM) feature;
2444
2445 hitem = (HTREEITEM) SendMessageW( hwnd, TVM_INSERTITEMW, 0, (LPARAM) &tvis );
2446 if (!hitem)
2447 continue;
2448
2449 if (!hfirst)
2450 hfirst = hitem;
2451
2454 feature->Feature, hitem );
2455
2456 /* the node is expanded if Display is odd */
2457 if ( feature->Display % 2 != 0 )
2459 }
2460
2461 /* select the first item */
2463 info->selected = hfirst;
2464}
2465
2467{
2468 const int bm_width = 32, bm_height = 16, bm_count = 3;
2469 const int bm_resource = 0x1001;
2471 int i;
2472 HBITMAP hbmp;
2473
2474 himl = ImageList_Create( bm_width, bm_height, FALSE, 4, 0 );
2475 if (!himl)
2476 {
2477 ERR("failed to create image list\n");
2478 return;
2479 }
2480
2481 for (i=0; i<bm_count; i++)
2482 {
2483 hbmp = LoadBitmapW( msi_hInstance, MAKEINTRESOURCEW(i+bm_resource) );
2484 if (!hbmp)
2485 {
2486 ERR("failed to load bitmap %d\n", i);
2487 break;
2488 }
2489
2490 /*
2491 * Add a dummy bitmap at offset zero because the treeview
2492 * can't use it as a state mask (zero means no user state).
2493 */
2494 if (!i)
2496
2498 }
2499
2501}
2502
2504{
2505 struct msi_selection_tree_info *info = GetPropW( control->hwnd, L"MSIDATA" );
2507 MSIRECORD *row, *rec;
2510 LPCWSTR dir, title = NULL;
2512
2513 if (tv->hdr.code != TVN_SELCHANGINGW)
2514 return ERROR_SUCCESS;
2515
2516 info->selected = tv->itemNew.hItem;
2517
2518 if (!(tv->itemNew.mask & TVIF_TEXT))
2519 {
2521 if (feature)
2522 title = feature->Title;
2523 }
2524 else
2525 title = tv->itemNew.pszText;
2526
2527 row = MSI_QueryGetRecord( dialog->package->db, L"SELECT * FROM `Feature` WHERE `Title` = '%s'", title );
2528 if (!row)
2529 return ERROR_FUNCTION_FAILED;
2530
2531 rec = MSI_CreateRecord( 1 );
2532
2534 msi_event_fire( dialog->package, L"SelectionDescription", rec );
2535
2536 dir = MSI_RecordGetString( row, 7 );
2537 if (dir)
2538 {
2539 folder = msi_get_loaded_folder( dialog->package, dir );
2540 if (!folder)
2541 {
2543 goto done;
2544 }
2545 MSI_RecordSetStringW( rec, 1, folder->ResolvedTarget );
2546 }
2547 else
2548 MSI_RecordSetStringW( rec, 1, NULL );
2549
2550 msi_event_fire( dialog->package, L"SelectionPath", rec );
2551
2552done:
2553 msiobj_release(&row->hdr);
2554 msiobj_release(&rec->hdr);
2555
2556 return r;
2557}
2558
2560{
2561 struct control *control;
2562 LPCWSTR prop, control_name;
2563 MSIPACKAGE *package = dialog->package;
2564 DWORD style;
2566
2567 info = malloc( sizeof *info );
2568 if (!info)
2569 return ERROR_FUNCTION_FAILED;
2570
2571 /* create the treeview control */
2575 if (!control)
2576 {
2577 free(info);
2578 return ERROR_FUNCTION_FAILED;
2579 }
2580
2582 control_name = MSI_RecordGetString( rec, 2 );
2584 prop = MSI_RecordGetString( rec, 9 );
2586
2587 /* subclass */
2588 info->dialog = dialog;
2589 info->hwnd = control->hwnd;
2592 SetPropW( control->hwnd, L"MSIDATA", info );
2593
2594 event_subscribe( dialog, L"SelectionPath", control_name, L"Property" );
2595
2596 /* initialize it */
2599
2600 return ERROR_SUCCESS;
2601}
2602
2603/******************** Group Box ***************************************/
2604
2606{
2607 struct control *control;
2608 DWORD style;
2609
2612 if (!control)
2613 return ERROR_FUNCTION_FAILED;
2614
2615 return ERROR_SUCCESS;
2616}
2617
2618/******************** List Box ***************************************/
2619
2621{
2628};
2629
2631{
2632 struct msi_listbox_info *info;
2633 LRESULT r;
2634 DWORD j;
2635
2636 TRACE("%p %04x %#Ix %#Ix\n", hWnd, msg, wParam, lParam);
2637
2638 info = GetPropW( hWnd, L"MSIDATA" );
2639 if (!info)
2640 return 0;
2641
2642 r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
2643
2644 switch( msg )
2645 {
2646 case WM_NCDESTROY:
2647 for (j = 0; j < info->num_items; j++)
2648 free( info->items[j] );
2649 free( info->items );
2650 free( info );
2651 RemovePropW( hWnd, L"MSIDATA" );
2652 break;
2653 }
2654
2655 return r;
2656}
2657
2659{
2660 struct msi_listbox_info *info = param;
2662 int pos;
2663
2664 value = MSI_RecordGetString( rec, 3 );
2665 text = MSI_RecordGetString( rec, 4 );
2666
2667 info->items[info->addpos_items] = wcsdup( value );
2668
2669 pos = SendMessageW( info->hwnd, LB_ADDSTRING, 0, (LPARAM)text );
2670 SendMessageW( info->hwnd, LB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] );
2671 info->addpos_items++;
2672 return ERROR_SUCCESS;
2673}
2674
2676{
2677 MSIQUERY *view;
2678 DWORD count;
2679 UINT r;
2680
2681 r = MSI_OpenQuery( info->dialog->package->db, &view,
2682 L"SELECT * FROM `ListBox` WHERE `Property` = '%s' ORDER BY `Order`", property );
2683 if ( r != ERROR_SUCCESS )
2684 return r;
2685
2686 /* just get the number of records */
2687 count = 0;
2689 if (r != ERROR_SUCCESS)
2690 {
2691 msiobj_release( &view->hdr );
2692 return r;
2693 }
2694 info->num_items = count;
2695 info->items = malloc( sizeof(*info->items) * count );
2696
2698 msiobj_release( &view->hdr );
2699 return r;
2700}
2701
2703{
2704 struct msi_listbox_info *info;
2705 int index;
2706 LPCWSTR value;
2707
2708 if( HIWORD(param) != LBN_SELCHANGE )
2709 return ERROR_SUCCESS;
2710
2711 info = GetPropW( control->hwnd, L"MSIDATA" );
2714
2715 dialog_set_property( info->dialog->package, control->property, value );
2717
2718 return ERROR_SUCCESS;
2719}
2720
2722{
2723 struct msi_listbox_info *info;
2724 struct control *control;
2726 LPCWSTR prop;
2727
2728 info = malloc( sizeof *info );
2729 if (!info)
2730 return ERROR_FUNCTION_FAILED;
2731
2733 attributes = MSI_RecordGetInteger( rec, 8 );
2735 style |= LBS_SORT;
2736
2738 if (!control)
2739 {
2740 free(info);
2741 return ERROR_FUNCTION_FAILED;
2742 }
2743
2745
2746 prop = MSI_RecordGetString( rec, 9 );
2748
2749 /* subclass */
2750 info->dialog = dialog;
2751 info->hwnd = control->hwnd;
2752 info->items = NULL;
2753 info->addpos_items = 0;
2756 SetPropW( control->hwnd, L"MSIDATA", info );
2757
2758 if ( control->property )
2760
2761 return ERROR_SUCCESS;
2762}
2763
2764/******************** Directory Combo ***************************************/
2765
2767{
2768 WCHAR *path;
2769
2770 if (!control && !(control = dialog_find_control_by_type( dialog, L"DirectoryCombo" )))
2771 return;
2772
2773 if (!(path = get_path_property( dialog, control ))) return;
2776
2779
2780 free( path );
2781}
2782
2784{
2785 struct control *control;
2786 LPCWSTR prop;
2787 DWORD style;
2788
2789 /* FIXME: use CBS_OWNERDRAWFIXED and add owner draw code */
2793 if (!control)
2794 return ERROR_FUNCTION_FAILED;
2795
2797 prop = MSI_RecordGetString( rec, 9 );
2799
2801
2802 return ERROR_SUCCESS;
2803}
2804
2805/******************** Directory List ***************************************/
2806
2808{
2809 WCHAR dir_spec[MAX_PATH], *path;
2810 WIN32_FIND_DATAW wfd;
2811 LVITEMW item;
2812 HANDLE file;
2813
2814 if (!control && !(control = dialog_find_control_by_type( dialog, L"DirectoryList" )))
2815 return;
2816
2817 /* clear the list-view */
2819
2820 if (!(path = get_path_property( dialog, control ))) return;
2821 lstrcpyW( dir_spec, path );
2822 lstrcatW( dir_spec, L"*" );
2823
2824 file = FindFirstFileW( dir_spec, &wfd );
2826 {
2827 free( path );
2828 return;
2829 }
2830
2831 do
2832 {
2833 if ( wfd.dwFileAttributes != FILE_ATTRIBUTE_DIRECTORY )
2834 continue;
2835
2836 if ( !wcscmp( wfd.cFileName, L"." ) || !wcscmp( wfd.cFileName, L".." ) )
2837 continue;
2838
2839 item.mask = LVIF_TEXT;
2840 item.cchTextMax = MAX_PATH;
2841 item.iItem = 0;
2842 item.iSubItem = 0;
2843 item.pszText = wfd.cFileName;
2844
2846 } while ( FindNextFileW( file, &wfd ) );
2847
2848 free( path );
2849 FindClose( file );
2850}
2851
2853{
2854 struct control *control;
2855 LPWSTR prop, path, ptr;
2856 BOOL indirect;
2857
2858 control = dialog_find_control_by_type( dialog, L"DirectoryList" );
2861 path = dialog_dup_property( dialog, prop, TRUE );
2862
2863 /* strip off the last directory */
2865 if (ptr != path)
2866 {
2867 *(ptr - 1) = '\0';
2869 }
2870
2871 dialog_set_property( dialog->package, prop, path );
2872
2876
2877 free( path );
2878 free( prop );
2879
2880 return ERROR_SUCCESS;
2881}
2882
2884{
2885 WCHAR newfolder[MAX_PATH], *path, *ptr;
2886 int len, count = 2;
2887
2888 len = LoadStringW( msi_hInstance, IDS_NEWFOLDER, newfolder, ARRAY_SIZE(newfolder) );
2889 len += lstrlenW(root) + 1;
2890 if (!(path = malloc( (len + 4) * sizeof(WCHAR) ))) return NULL;
2891 lstrcpyW( path, root );
2892 lstrcatW( path, newfolder );
2893
2894 for (;;)
2895 {
2897 if (count > 99)
2898 {
2899 free( path );
2900 return NULL;
2901 }
2902 swprintf( path, len + 4, L"%s%s %u", root, newfolder, count++ );
2903 }
2904
2905 ptr = wcsrchr( path, '\\' ) + 1;
2906 *ret_len = lstrlenW(ptr);
2907 memmove( path, ptr, *ret_len * sizeof(WCHAR) );
2908 return path;
2909}
2910
2912{
2913 struct control *control;
2914 WCHAR *path;
2915 LVITEMW item;
2916 int index;
2917
2918 control = dialog_find_control_by_type( dialog, L"DirectoryList" );
2919
2921
2922 item.mask = LVIF_TEXT;
2923 item.iItem = 0;
2924 item.iSubItem = 0;
2925 item.pszText = get_unique_folder_name( path, &item.cchTextMax );
2926
2930
2931 free( path );
2932 free( item.pszText );
2933 return ERROR_SUCCESS;
2934}
2935
2937{
2938 NMHDR *nmhdr = (NMHDR *)param;
2939 WCHAR text[MAX_PATH], *new_path, *path, *prop;
2940 BOOL indirect;
2941
2942 switch (nmhdr->code)
2943 {
2944 case LVN_ENDLABELEDITW:
2945 {
2947 if (!info->item.pszText) return ERROR_SUCCESS;
2948 lstrcpynW( text, info->item.pszText, ARRAY_SIZE(text) );
2949 text[ARRAY_SIZE(text) - 1] = 0;
2950 break;
2951 }
2952 case LVN_ITEMACTIVATE:
2953 {
2954 LVITEMW item;
2956 if (index < 0)
2957 {
2958 ERR("no list-view item selected\n");
2959 return ERROR_FUNCTION_FAILED;
2960 }
2961
2962 item.iSubItem = 0;
2963 item.pszText = text;
2964 item.cchTextMax = MAX_PATH;
2966 text[ARRAY_SIZE(text) - 1] = 0;
2967 break;
2968 }
2969 default:
2970 return ERROR_SUCCESS;
2971 }
2972
2975 path = dialog_dup_property( dialog, prop, TRUE );
2976
2977 if (!(new_path = malloc( (wcslen(path) + wcslen(text) + 2) * sizeof(WCHAR) )))
2978 {
2979 free( prop );
2980 free( path );
2981 return ERROR_OUTOFMEMORY;
2982 }
2983 lstrcpyW( new_path, path );
2984 lstrcatW( new_path, text );
2985 if (nmhdr->code == LVN_ENDLABELEDITW) CreateDirectoryW( new_path, NULL );
2986 lstrcatW( new_path, L"\\" );
2987
2988 dialog_set_property( dialog->package, prop, new_path );
2989
2993
2994 free( prop );
2995 free( path );
2996 free( new_path );
2997
2998 return ERROR_SUCCESS;
2999}
3000
3002{
3003 struct control *control;
3004 LPCWSTR prop;
3005 DWORD style;
3006
3011 if (!control)
3012 return ERROR_FUNCTION_FAILED;
3013
3016 prop = MSI_RecordGetString( rec, 9 );
3018
3019 /* double click to activate an item in the list */
3022
3024
3025 return ERROR_SUCCESS;
3026}
3027
3028/******************** VolumeCost List ***************************************/
3029
3031{
3032 int i;
3033
3034 for (i = 0; i < lstrlenW( str ); i++)
3035 if (!iswdigit(str[i]))
3036 return FALSE;
3037
3038 return TRUE;
3039}
3040
3041static const WCHAR column_keys[][80] =
3042{
3043 L"VolumeCostVolume",
3044 L"VolumeCostSize",
3045 L"VolumeCostAvailable",
3046 L"VolumeCostRequired",
3047 L"VolumeCostDifference",
3048};
3049
3051{
3052 LPCWSTR text = MSI_RecordGetString( rec, 10 );
3053 LPCWSTR begin = text, end;
3054 WCHAR *num;
3055 LVCOLUMNW lvc;
3056 DWORD count = 0;
3057
3058 if (!text) return;
3059
3060 while ((begin = wcschr( begin, '{' )) && count < 5)
3061 {
3062 if (!(end = wcschr( begin, '}' )))
3063 return;
3064
3065 num = malloc( (end - begin + 1) * sizeof(WCHAR) );
3066 if (!num)
3067 return;
3068
3069 lstrcpynW( num, begin + 1, end - begin );
3070 begin += end - begin + 1;
3071
3072 /* empty braces or '0' hides the column */
3073 if ( !num[0] || !wcscmp( num, L"0" ) )
3074 {
3075 count++;
3076 free( num );
3077 continue;
3078 }
3079
3080 /* the width must be a positive number
3081 * if a width is invalid, all remaining columns are hidden
3082 */
3083 if ( !wcsncmp( num, L"-", 1 ) || !str_is_number( num ) ) {
3084#ifdef __REACTOS__
3085 // Skip in case of prefix the string of displayed characters with {\style} or {&style}.
3086 if (count == 0 && (!wcsncmp(num, L"\\", 1) || !wcsncmp(num, L"&", 1)))
3087 {
3088 FIXME("Style prefix not supported\n");
3089 free(num);
3090 continue;
3091 }
3092#endif
3093 free( num );
3094 return;
3095 }
3096
3097 ZeroMemory( &lvc, sizeof(lvc) );
3099 lvc.cx = wcstol( num, NULL, 10 );
3101
3103 free( lvc.pszText );
3104 free( num );
3105 }
3106}
3107
3109{
3111 INT each_cost;
3112 LONGLONG total_cost = 0;
3113
3114 LIST_FOR_EACH_ENTRY( feature, &dialog->package->features, MSIFEATURE, entry )
3115 {
3118 {
3119 total_cost += each_cost;
3120 }
3123 {
3124 total_cost -= each_cost;
3125 }
3126 }
3127 return total_cost;
3128}
3129
3131{
3133 LONGLONG difference, cost;
3134 WCHAR size_text[MAX_PATH];
3135 WCHAR cost_text[MAX_PATH];
3136 LPWSTR drives, ptr;
3137 LVITEMW lvitem;
3138#ifdef __REACTOS__
3139 DWORD size;
3140#else
3141 DWORD size, flags;
3142#endif
3143 int i = 0;
3144
3145 cost = vcl_get_cost(dialog) * 512;
3146 StrFormatByteSizeW(cost, cost_text, MAX_PATH);
3147
3149 if ( !size ) return;
3150
3151 drives = malloc( (size + 1) * sizeof(WCHAR) );
3152 if ( !drives ) return;
3153
3154 GetLogicalDriveStringsW( size, drives );
3155
3156 ptr = drives;
3157 while (*ptr)
3158 {
3159#ifdef __REACTOS__
3161#else
3162 if (GetVolumeInformationW(ptr, NULL, 0, NULL, 0, &flags, NULL, 0) &&
3164#endif
3165 {
3166 ptr += lstrlenW(ptr) + 1;
3167 continue;
3168 }
3169
3170 lvitem.mask = LVIF_TEXT;
3171 lvitem.iItem = i;
3172 lvitem.iSubItem = 0;
3173 lvitem.pszText = ptr;
3174 lvitem.cchTextMax = lstrlenW(ptr) + 1;
3176
3178 difference = unused.QuadPart - cost;
3179
3180 StrFormatByteSizeW(total.QuadPart, size_text, MAX_PATH);
3181 lvitem.iSubItem = 1;
3182 lvitem.pszText = size_text;
3183 lvitem.cchTextMax = lstrlenW(size_text) + 1;
3184 SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3185
3186 StrFormatByteSizeW(unused.QuadPart, size_text, MAX_PATH);
3187 lvitem.iSubItem = 2;
3188 lvitem.pszText = size_text;
3189 lvitem.cchTextMax = lstrlenW(size_text) + 1;
3190 SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3191
3192 lvitem.iSubItem = 3;
3193 lvitem.pszText = cost_text;
3194 lvitem.cchTextMax = lstrlenW(cost_text) + 1;
3195 SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3196
3197 StrFormatByteSizeW(difference, size_text, MAX_PATH);
3198 lvitem.iSubItem = 4;
3199 lvitem.pszText = size_text;
3200 lvitem.cchTextMax = lstrlenW(size_text) + 1;
3201 SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem );
3202
3203 ptr += lstrlenW(ptr) + 1;
3204 i++;
3205 }
3206
3207 free( drives );
3208}
3209
3211{
3212 struct control *control;
3213 DWORD style;
3214
3219 if (!control)
3220 return ERROR_FUNCTION_FAILED;
3221
3224
3225 return ERROR_SUCCESS;
3226}
3227
3228/******************** VolumeSelect Combo ***************************************/
3229
3231{
3233 LPWSTR prop;
3234 BOOL indirect;
3235 int index;
3236
3237 if (HIWORD(param) != CBN_SELCHANGE)
3238 return ERROR_SUCCESS;
3239
3241 if ( index == CB_ERR )
3242 {
3243 ERR("No ComboBox item selected!\n");
3244 return ERROR_FUNCTION_FAILED;
3245 }
3246
3248
3251
3252 dialog_set_property( dialog->package, prop, text );
3253
3254 free( prop );
3255 return ERROR_SUCCESS;
3256}
3257
3259{
3260 LPWSTR drives, ptr;
3261 DWORD size;
3262
3264 if ( !size ) return;
3265
3266 drives = malloc( (size + 1) * sizeof(WCHAR) );
3267 if ( !drives ) return;
3268
3269 GetLogicalDriveStringsW( size, drives );
3270
3271 ptr = drives;
3272 while (*ptr)
3273 {
3275 ptr += lstrlenW(ptr) + 1;
3276 }
3277
3278 free( drives );
3279}
3280
3282{
3283 struct control *control;
3284 LPCWSTR prop;
3285 DWORD style;
3286
3287 /* FIXME: CBS_OWNERDRAWFIXED */
3292 if (!control)
3293 return ERROR_FUNCTION_FAILED;
3294
3297 prop = MSI_RecordGetString( rec, 9 );
3299
3301
3302 return ERROR_SUCCESS;
3303}
3304
3306{
3307 int len, len_href = ARRAY_SIZE( L"href" ) - 1;
3308 const WCHAR *p, *q;
3309 WCHAR quote = 0;
3310 LITEM item;
3311
3312 item.mask = LIF_ITEMINDEX | LIF_URL;
3313 item.iLink = 0;
3314 item.szUrl[0] = 0;
3315
3317
3318 p = item.szUrl;
3319 while (*p && *p != '<') p++;
3320 if (!*p++) return ERROR_SUCCESS;
3321 if (towupper( *p++ ) != 'A' || !iswspace( *p++ )) return ERROR_SUCCESS;
3322 while (*p && iswspace( *p )) p++;
3323
3324 len = lstrlenW( p );
3325 if (len > len_href && !wcsnicmp( p, L"href", len_href ))
3326 {
3327 p += len_href;
3328 while (*p && iswspace( *p )) p++;
3329 if (!*p || *p++ != '=') return ERROR_SUCCESS;
3330 while (*p && iswspace( *p )) p++;
3331
3332 if (*p == '\"' || *p == '\'') quote = *p++;
3333 q = p;
3334 if (quote)
3335 {
3336 while (*q && *q != quote) q++;
3337 if (*q != quote) return ERROR_SUCCESS;
3338 }
3339 else
3340 {
3341 while (*q && *q != '>' && !iswspace( *q )) q++;
3342 if (!*q) return ERROR_SUCCESS;
3343 }
3344 item.szUrl[q - item.szUrl] = 0;
3345 ShellExecuteW( NULL, L"open", p, NULL, NULL, SW_SHOWNORMAL );
3346 }
3347 return ERROR_SUCCESS;
3348}
3349
3351{
3352 struct control *control;
3354 const WCHAR *text = MSI_RecordGetString( rec, 10 );
3355 int len = lstrlenW( text );
3356 LITEM item;
3357
3359 if (!control)
3360 return ERROR_FUNCTION_FAILED;
3361
3364
3366 item.iLink = 0;
3367 item.state = LIS_ENABLED;
3368 item.stateMask = LIS_ENABLED;
3369 if (len < L_MAX_URL_LENGTH) lstrcpyW( item.szUrl, text );
3370 else item.szUrl[0] = 0;
3371
3373
3374 return ERROR_SUCCESS;
3375}
3376
3377/******************** ListView *****************************************/
3378
3380{
3383};
3384
3386{
3387 NMHDR *nmhdr = (NMHDR *)param;
3388
3389 FIXME("code %#x (%d)\n", nmhdr->code, nmhdr->code);
3390
3391 return ERROR_SUCCESS;
3392}
3393
3395{
3396 struct listview_param *lv_param = (struct listview_param *)param;
3398 LVITEMW item;
3399 HICON hIcon;
3400
3401 text = MSI_RecordGetString( rec, 4 );
3402 binary = MSI_RecordGetString( rec, 5 );
3403 hIcon = load_icon( lv_param->dialog->package->db, binary, 0 );
3404
3405 TRACE("Adding: text %s, binary %s, icon %p\n", debugstr_w(text), debugstr_w(binary), hIcon);
3406
3407 memset( &item, 0, sizeof(item) );
3408 item.mask = LVIF_TEXT | LVIF_IMAGE;
3409 deformat_string( lv_param->dialog->package, text, &item.pszText );
3410 item.iImage = ImageList_AddIcon( lv_param->control->hImageList, hIcon );
3411 item.iItem = item.iImage;
3412 SendMessageW( lv_param->control->hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item );
3413
3414 DestroyIcon( hIcon );
3415
3416 return ERROR_SUCCESS;
3417}
3418
3420{
3421 MSIQUERY *view;
3422 struct listview_param lv_p