[COMDLG32] Sync with Wine Staging 3.3. CORE-14434
[reactos.git] / dll / win32 / comdlg32 / itemdlg.c
index e843223..2b7a6fe 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#if 0 // Win 7
+#ifndef __REACTOS__ /* Win 7 */
 
 #include <stdarg.h>
 
 #define COBJMACROS
 #define NONAMELESSUNION
-#define NONAMELESSSTRUCT
 
 #include "windef.h"
 #include "winbase.h"
@@ -53,6 +52,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
 
 static const WCHAR notifysink_childW[] = {'n','f','s','_','c','h','i','l','d',0};
 static const WCHAR floatnotifysinkW[] = {'F','l','o','a','t','N','o','t','i','f','y','S','i','n','k',0};
+static const WCHAR radiobuttonlistW[] = {'R','a','d','i','o','B','u','t','t','o','n','L','i','s','t',0};
 
 enum ITEMDLG_TYPE {
     ITEMDLG_TYPE_OPEN,
@@ -67,15 +67,29 @@ enum ITEMDLG_CCTRL_TYPE {
     IDLG_CCTRL_CHECKBUTTON,
     IDLG_CCTRL_EDITBOX,
     IDLG_CCTRL_SEPARATOR,
-    IDLG_CCTRL_TEXT
+    IDLG_CCTRL_TEXT,
+    IDLG_CCTRL_OPENDROPDOWN,
+    IDLG_CCTRL_VISUALGROUP
 };
 
+typedef struct cctrl_item {
+    DWORD id, parent_id;
+    LPWSTR label;
+    CDCONTROLSTATEF cdcstate;
+    HWND hwnd;
+    struct list entry;
+} cctrl_item;
+
 typedef struct {
     HWND hwnd, wrapper_hwnd;
     UINT id, dlgid;
     enum ITEMDLG_CCTRL_TYPE type;
     CDCONTROLSTATEF cdcstate;
     struct list entry;
+
+    struct list sub_cctrls;
+    struct list sub_cctrls_entry;
+    struct list sub_items;
 } customctrl;
 
 typedef struct {
@@ -124,9 +138,19 @@ typedef struct FileDialogImpl {
     LPWSTR custom_filenamelabel;
 
     UINT cctrl_width, cctrl_def_height, cctrls_cols;
+    UINT cctrl_indent, dpi_x, dpi_y;
     HWND cctrls_hwnd;
     struct list cctrls;
     UINT_PTR cctrl_next_dlgid;
+    customctrl *cctrl_active_vg;
+
+    HMENU hmenu_opendropdown;
+    customctrl cctrl_opendropdown;
+    HFONT hfont_opendropdown;
+    BOOL opendropdown_has_selection;
+    DWORD opendropdown_selection;
+
+    GUID client_guid;
 } FileDialogImpl;
 
 /**************************************************************************
@@ -196,6 +220,59 @@ static void events_OnSelectionChange(FileDialogImpl *This)
     }
 }
 
+static void events_OnTypeChange(FileDialogImpl *This)
+{
+    events_client *cursor;
+    TRACE("%p\n", This);
+
+    LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
+    {
+        TRACE("Notifying %p\n", cursor);
+        IFileDialogEvents_OnTypeChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface);
+    }
+}
+
+static HRESULT events_OnOverwrite(FileDialogImpl *This, IShellItem *shellitem)
+{
+    events_client *cursor;
+    HRESULT hr = S_OK;
+    FDE_OVERWRITE_RESPONSE response = FDEOR_DEFAULT;
+    TRACE("%p %p\n", This, shellitem);
+
+    LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
+    {
+        TRACE("Notifying %p\n", cursor);
+        hr = IFileDialogEvents_OnOverwrite(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface, shellitem, &response);
+        TRACE("<-- hr=%x response=%u\n", hr, response);
+        if(FAILED(hr) && hr != E_NOTIMPL)
+            break;
+    }
+
+    if(hr == E_NOTIMPL)
+        hr = S_OK;
+
+    if(SUCCEEDED(hr))
+    {
+        if (response == FDEOR_DEFAULT)
+        {
+            WCHAR buf[100];
+            int answer;
+
+            LoadStringW(COMDLG32_hInstance, IDS_OVERWRITEFILE, buf, 100);
+            answer = MessageBoxW(This->dlg_hwnd, buf, This->custom_title,
+                       MB_YESNO | MB_ICONEXCLAMATION);
+            if (answer == IDNO || answer == IDCANCEL)
+            {
+                hr = E_FAIL;
+            }
+        }
+        else if (response == FDEOR_REFUSE)
+            hr = E_FAIL;
+    }
+
+    return hr;
+}
+
 static inline HRESULT get_cctrl_event(IFileDialogEvents *pfde, IFileDialogControlEvents **pfdce)
 {
     return IFileDialogEvents_QueryInterface(pfde, &IID_IFileDialogControlEvents, (void**)pfdce);
@@ -223,7 +300,7 @@ static HRESULT cctrl_event_OnButtonClicked(FileDialogImpl *This, DWORD ctl_id)
 static HRESULT cctrl_event_OnItemSelected(FileDialogImpl *This, DWORD ctl_id, DWORD item_id)
 {
     events_client *cursor;
-    TRACE("%p\n", This);
+    TRACE("%p %i %i\n", This, ctl_id, item_id);
 
     LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry)
     {
@@ -295,13 +372,13 @@ static UINT get_file_name(FileDialogImpl *This, LPWSTR *str)
             lstrcpyW(*str, This->set_filename);
             return len;
         }
-        return FALSE;
+        return 0;
     }
 
     len = SendMessageW(hwnd_edit, WM_GETTEXTLENGTH, 0, 0);
     *str = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
     if(!*str)
-        return FALSE;
+        return 0;
 
     SendMessageW(hwnd_edit, WM_GETTEXT, len+1, (LPARAM)*str);
     return len;
@@ -309,14 +386,12 @@ static UINT get_file_name(FileDialogImpl *This, LPWSTR *str)
 
 static BOOL set_file_name(FileDialogImpl *This, LPCWSTR str)
 {
-    HWND hwnd_edit = GetDlgItem(This->dlg_hwnd, IDC_FILENAME);
-
     if(This->set_filename)
         LocalFree(This->set_filename);
 
-    This->set_filename = StrDupW(str);
+    This->set_filename = str ? StrDupW(str) : NULL;
 
-    return SendMessageW(hwnd_edit, WM_SETTEXT, 0, (LPARAM)str);
+    return SetDlgItemTextW(This->dlg_hwnd, IDC_FILENAME, This->set_filename);
 }
 
 static void fill_filename_from_selection(FileDialogImpl *This)
@@ -346,8 +421,10 @@ static void fill_filename_from_selection(FileDialogImpl *This)
             UINT attr;
 
             hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER, &attr);
-            if(SUCCEEDED(hr) && (attr & SFGAO_FOLDER))
-                continue; /* FIXME: FOS_PICKFOLDERS */
+            if(SUCCEEDED(hr) &&
+               (( (This->options & FOS_PICKFOLDERS) && !(attr & SFGAO_FOLDER)) ||
+                (!(This->options & FOS_PICKFOLDERS) &&  (attr & SFGAO_FOLDER))))
+                continue;
 
             hr = IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &names[valid_count]);
             if(SUCCEEDED(hr))
@@ -404,12 +481,34 @@ static LPWSTR get_first_ext_from_spec(LPWSTR buf, LPCWSTR spec)
     return ext;
 }
 
+static BOOL shell_item_exists(IShellItem* shellitem)
+{
+    LPWSTR filename;
+    HRESULT hr;
+    BOOL result;
+
+    hr = IShellItem_GetDisplayName(shellitem, SIGDN_FILESYSPATH, &filename);
+    if (SUCCEEDED(hr))
+    {
+        /* FIXME: Implement SFGAO_VALIDATE in Wine and use it instead. */
+        result = (GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES);
+        CoTaskMemFree(filename);
+    }
+    else
+    {
+        SFGAOF attributes;
+        result = SUCCEEDED(IShellItem_GetAttributes(shellitem, SFGAO_VALIDATE, &attributes));
+    }
+
+    return result;
+}
+
 static HRESULT on_default_action(FileDialogImpl *This)
 {
     IShellFolder *psf_parent, *psf_desktop;
     LPITEMIDLIST *pidla;
     LPITEMIDLIST current_folder;
-    LPWSTR fn_iter, files, tmp_files;
+    LPWSTR fn_iter, files = NULL, tmp_files;
     UINT file_count = 0, len, i;
     int open_action;
     HRESULT hr, ret = E_FAIL;
@@ -419,6 +518,7 @@ static HRESULT on_default_action(FileDialogImpl *This)
     {
         UINT size_used;
         file_count = COMDLG32_SplitFileNames(tmp_files, len, &files, &size_used);
+        CoTaskMemFree(tmp_files);
     }
     if(!file_count) return E_FAIL;
 
@@ -426,6 +526,7 @@ static HRESULT on_default_action(FileDialogImpl *This)
     if(FAILED(hr))
     {
         ERR("Failed to get pidl for current directory.\n");
+        HeapFree(GetProcessHeap(), 0, files);
         return hr;
     }
 
@@ -508,7 +609,7 @@ static HRESULT on_default_action(FileDialogImpl *This)
         pidla[i] = COMDLG32_SHSimpleIDListFromPathAW(canon_filename);
 
         if(psf_parent && !(open_action == ONOPEN_BROWSE))
-            IShellItem_Release(psf_parent);
+            IShellFolder_Release(psf_parent);
 
         fn_iter += (WCHAR)lstrlenW(fn_iter) + 1;
     }
@@ -530,25 +631,66 @@ static HRESULT on_default_action(FileDialogImpl *This)
         if(FAILED(hr))
             ERR("Failed to browse to directory: %08x\n", hr);
 
-        IShellItem_Release(psf_parent);
+        IShellFolder_Release(psf_parent);
         break;
 
     case ONOPEN_OPEN:
-        if(events_OnFileOk(This) != S_OK)
-            break;
-
         hr = SHGetDesktopFolder(&psf_desktop);
         if(SUCCEEDED(hr))
         {
             if(This->psia_results)
+            {
                 IShellItemArray_Release(This->psia_results);
+                This->psia_results = NULL;
+            }
 
             hr = SHCreateShellItemArray(NULL, psf_desktop, file_count, (PCUITEMID_CHILD_ARRAY)pidla,
                                         &This->psia_results);
-            if(SUCCEEDED(hr))
-                ret = S_OK;
 
             IShellFolder_Release(psf_desktop);
+
+            if(FAILED(hr))
+                break;
+
+            if(This->options & FOS_PICKFOLDERS)
+            {
+                SFGAOF attributes;
+                hr = IShellItemArray_GetAttributes(This->psia_results, SIATTRIBFLAGS_AND, SFGAO_FOLDER, &attributes);
+                if(hr != S_OK)
+                {
+                    WCHAR buf[64];
+                    LoadStringW(COMDLG32_hInstance, IDS_INVALID_FOLDERNAME, buf, sizeof(buf)/sizeof(WCHAR));
+
+                    MessageBoxW(This->dlg_hwnd, buf, This->custom_title, MB_OK | MB_ICONEXCLAMATION);
+
+                    IShellItemArray_Release(This->psia_results);
+                    This->psia_results = NULL;
+                    break;
+                }
+            }
+
+            if((This->options & FOS_OVERWRITEPROMPT) && This->dlg_type == ITEMDLG_TYPE_SAVE)
+            {
+                IShellItem *shellitem;
+
+                for (i=0; SUCCEEDED(hr) && i<file_count; i++)
+                {
+                    hr = IShellItemArray_GetItemAt(This->psia_results, i, &shellitem);
+                    if (SUCCEEDED(hr))
+                    {
+                        if (shell_item_exists(shellitem))
+                            hr = events_OnOverwrite(This, shellitem);
+
+                        IShellItem_Release(shellitem);
+                    }
+                }
+
+                if (FAILED(hr))
+                    break;
+            }
+
+            if(events_OnFileOk(This) == S_OK)
+                ret = S_OK;
         }
         break;
 
@@ -566,30 +708,150 @@ static HRESULT on_default_action(FileDialogImpl *This)
     return ret;
 }
 
+static void show_opendropdown(FileDialogImpl *This)
+{
+    HWND open_hwnd;
+    RECT open_rc;
+    MSG msg;
+
+    open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
+
+    GetWindowRect(open_hwnd, &open_rc);
+
+    if (TrackPopupMenu(This->hmenu_opendropdown, 0, open_rc.left, open_rc.bottom, 0, This->dlg_hwnd, NULL) &&
+        PeekMessageW(&msg, This->dlg_hwnd, WM_MENUCOMMAND, WM_MENUCOMMAND, PM_REMOVE))
+    {
+        MENUITEMINFOW mii;
+
+        This->opendropdown_has_selection = TRUE;
+
+        mii.cbSize = sizeof(mii);
+        mii.fMask = MIIM_ID;
+        GetMenuItemInfoW((HMENU)msg.lParam, msg.wParam, TRUE, &mii);
+        This->opendropdown_selection = mii.wID;
+
+        if(SUCCEEDED(on_default_action(This)))
+            EndDialog(This->dlg_hwnd, S_OK);
+        else
+            This->opendropdown_has_selection = FALSE;
+    }
+}
+
+/**************************************************************************
+ * Control item functions.
+ */
+
+static void item_free(cctrl_item *item)
+{
+    DestroyWindow(item->hwnd);
+    HeapFree(GetProcessHeap(), 0, item->label);
+    HeapFree(GetProcessHeap(), 0, item);
+}
+
+static cctrl_item* get_item(customctrl* parent, DWORD itemid, CDCONTROLSTATEF visible_flags, DWORD* position)
+{
+    DWORD dummy;
+    cctrl_item* item;
+
+    if (!position) position = &dummy;
+
+    *position = 0;
+
+    LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
+    {
+        if (item->id == itemid)
+            return item;
+
+        if ((item->cdcstate & visible_flags) == visible_flags)
+            (*position)++;
+    }
+
+    return NULL;
+}
+
+static cctrl_item* get_first_item(customctrl* parent)
+{
+    cctrl_item* item;
+
+    LIST_FOR_EACH_ENTRY(item, &parent->sub_items, cctrl_item, entry)
+    {
+        if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
+            return item;
+    }
+
+    return NULL;
+}
+
+static HRESULT add_item(customctrl* parent, DWORD itemid, LPCWSTR label, cctrl_item** result)
+{
+    cctrl_item* item;
+    LPWSTR label_copy;
+
+    if (get_item(parent, itemid, 0, NULL))
+        return E_INVALIDARG;
+
+    item = HeapAlloc(GetProcessHeap(), 0, sizeof(*item));
+    label_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(label)+1)*sizeof(WCHAR));
+
+    if (!item || !label_copy)
+    {
+        HeapFree(GetProcessHeap(), 0, item);
+        HeapFree(GetProcessHeap(), 0, label_copy);
+        return E_OUTOFMEMORY;
+    }
+
+    item->id = itemid;
+    item->parent_id = parent->id;
+    lstrcpyW(label_copy, label);
+    item->label = label_copy;
+    item->cdcstate = CDCS_VISIBLE|CDCS_ENABLED;
+    item->hwnd = NULL;
+    list_add_tail(&parent->sub_items, &item->entry);
+
+    *result = item;
+
+    return S_OK;
+}
+
 /**************************************************************************
  * Control functions.
  */
 static inline customctrl *get_cctrl_from_dlgid(FileDialogImpl *This, DWORD dlgid)
 {
-    customctrl *ctrl;
+    customctrl *ctrl, *sub_ctrl;
 
     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
+    {
         if(ctrl->dlgid == dlgid)
             return ctrl;
 
+        LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
+            if(sub_ctrl->dlgid == dlgid)
+                return sub_ctrl;
+    }
+
     ERR("Failed to find control with dialog id %d\n", dlgid);
     return NULL;
 }
 
 static inline customctrl *get_cctrl(FileDialogImpl *This, DWORD ctlid)
 {
-    customctrl *ctrl;
+    customctrl *ctrl, *sub_ctrl;
 
     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
+    {
         if(ctrl->id == ctlid)
             return ctrl;
 
-    ERR("Failed to find control with control id %d\n", ctlid);
+        LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
+            if(sub_ctrl->id == ctlid)
+                return sub_ctrl;
+    }
+
+    if (This->hmenu_opendropdown && This->cctrl_opendropdown.id == ctlid)
+        return &This->cctrl_opendropdown;
+
+    TRACE("No existing control with control id %d\n", ctlid);
     return NULL;
 }
 
@@ -602,6 +864,7 @@ static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width, BOOL multili
     RECT rc;
     HDC hdc;
     WCHAR *c;
+    HFONT font;
 
     TRACE("\n");
 
@@ -611,7 +874,10 @@ static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width, BOOL multili
     SendMessageW(hctrl, WM_GETTEXT, len+1, (LPARAM)text);
 
     hdc = GetDC(hctrl);
+    font = (HFONT)SendMessageW(hctrl, WM_GETFONT, 0, 0);
+    font = SelectObject(hdc, font);
     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &size);
+    SelectObject(hdc, font);
     ReleaseDC(hctrl, hdc);
 
     if(len && multiline)
@@ -635,9 +901,47 @@ static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width, BOOL multili
     HeapFree(GetProcessHeap(), 0, text);
 }
 
+static UINT ctrl_get_height(customctrl *ctrl) {
+    RECT rc;
+    GetWindowRect(ctrl->wrapper_hwnd, &rc);
+    return rc.bottom - rc.top;
+}
+
+static void ctrl_free(customctrl *ctrl)
+{
+    customctrl *sub_cur1, *sub_cur2;
+    cctrl_item *item_cur1, *item_cur2;
+
+    TRACE("Freeing control %p\n", ctrl);
+    if(ctrl->type == IDLG_CCTRL_MENU)
+    {
+        TBBUTTON tbb;
+        SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
+        DestroyMenu((HMENU)tbb.dwData);
+    }
+
+    LIST_FOR_EACH_ENTRY_SAFE(sub_cur1, sub_cur2, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
+    {
+        list_remove(&sub_cur1->sub_cctrls_entry);
+        ctrl_free(sub_cur1);
+    }
+
+    LIST_FOR_EACH_ENTRY_SAFE(item_cur1, item_cur2, &ctrl->sub_items, cctrl_item, entry)
+    {
+        list_remove(&item_cur1->entry);
+        item_free(item_cur1);
+    }
+
+    DestroyWindow(ctrl->hwnd);
+    HeapFree(GetProcessHeap(), 0, ctrl);
+}
+
 static void customctrl_resize(FileDialogImpl *This, customctrl *ctrl)
 {
     RECT rc;
+    UINT total_height;
+    UINT max_width, size;
+    customctrl *sub_ctrl;
 
     switch(ctrl->type)
     {
@@ -645,15 +949,72 @@ static void customctrl_resize(FileDialogImpl *This, customctrl *ctrl)
     case IDLG_CCTRL_COMBOBOX:
     case IDLG_CCTRL_CHECKBUTTON:
     case IDLG_CCTRL_TEXT:
-        ctrl_resize(ctrl->hwnd, 160, 160, TRUE);
+        size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
+        ctrl_resize(ctrl->hwnd, size, size, TRUE);
         GetWindowRect(ctrl->hwnd, &rc);
         SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
-                     SWP_NOZORDER|SWP_NOMOVE|SWP_NOZORDER);
+                     SWP_NOZORDER|SWP_NOMOVE);
+        break;
+    case IDLG_CCTRL_VISUALGROUP:
+        total_height = 0;
+        ctrl_resize(ctrl->hwnd, 0, This->cctrl_indent, TRUE);
+
+        LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
+        {
+            customctrl_resize(This, sub_ctrl);
+            SetWindowPos(sub_ctrl->wrapper_hwnd, NULL, This->cctrl_indent, total_height, 0, 0,
+                         SWP_NOZORDER|SWP_NOSIZE);
+
+            total_height += ctrl_get_height(sub_ctrl);
+        }
+
+        /* The label should be right adjusted */
+        {
+            UINT width, height;
+
+            GetWindowRect(ctrl->hwnd, &rc);
+            width = rc.right - rc.left;
+            height = rc.bottom - rc.top;
+
+            SetWindowPos(ctrl->hwnd, NULL, This->cctrl_indent - width, 0, width, height, SWP_NOZORDER);
+        }
+
+        /* Resize the wrapper window to fit all the sub controls */
+        SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, This->cctrl_width + This->cctrl_indent, total_height,
+                     SWP_NOZORDER|SWP_NOMOVE);
         break;
     case IDLG_CCTRL_RADIOBUTTONLIST:
+    {
+        cctrl_item* item;
+
+        total_height = 0;
+        max_width = 0;
+
+        LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
+        {
+            size = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
+            ctrl_resize(item->hwnd, size, size, TRUE);
+            SetWindowPos(item->hwnd, NULL, 0, total_height, 0, 0,
+                         SWP_NOZORDER|SWP_NOSIZE);
+
+            GetWindowRect(item->hwnd, &rc);
+
+            total_height += rc.bottom - rc.top;
+            max_width = max(rc.right - rc.left, max_width);
+        }
+
+        SetWindowPos(ctrl->hwnd, NULL, 0, 0, max_width, total_height,
+                     SWP_NOZORDER|SWP_NOMOVE);
+
+        SetWindowPos(ctrl->wrapper_hwnd, NULL, 0, 0, max_width, total_height,
+                     SWP_NOZORDER|SWP_NOMOVE);
+
+        break;
+    }
     case IDLG_CCTRL_EDITBOX:
     case IDLG_CCTRL_SEPARATOR:
     case IDLG_CCTRL_MENU:
+    case IDLG_CCTRL_OPENDROPDOWN:
         /* Nothing */
         break;
     }
@@ -753,6 +1114,7 @@ static LRESULT notifysink_on_wm_notify(FileDialogImpl *This, HWND hwnd, WPARAM w
 static LRESULT CALLBACK notifysink_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
 {
     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+    customctrl *ctrl;
     HWND hwnd_child;
     RECT rc;
 
@@ -763,8 +1125,12 @@ static LRESULT CALLBACK notifysink_proc(HWND hwnd, UINT message, WPARAM wparam,
     case WM_NOTIFY:           return notifysink_on_wm_notify(This, hwnd, wparam, lparam);
     case WM_SIZE:
         hwnd_child = GetPropW(hwnd, notifysink_childW);
-        GetClientRect(hwnd, &rc);
-        SetWindowPos(hwnd_child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+        ctrl = (customctrl*)GetWindowLongPtrW(hwnd_child, GWLP_USERDATA);
+        if(ctrl && ctrl->type != IDLG_CCTRL_VISUALGROUP)
+        {
+            GetClientRect(hwnd, &rc);
+            SetWindowPos(hwnd_child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+        }
         return TRUE;
     }
 
@@ -775,15 +1141,20 @@ static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
                                 LPCWSTR text, LPCWSTR wndclass, DWORD ctrl_wsflags,
                                 DWORD ctrl_exflags, UINT height, customctrl **ppctrl)
 {
-    HWND ns_hwnd, control_hwnd;
+    HWND ns_hwnd, control_hwnd, parent_hwnd;
     DWORD wsflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
     customctrl *ctrl;
 
     if(get_cctrl(This, id))
         return E_UNEXPECTED; /* Duplicate id */
 
+    if(This->cctrl_active_vg)
+        parent_hwnd = This->cctrl_active_vg->wrapper_hwnd;
+    else
+        parent_hwnd = This->cctrls_hwnd;
+
     ns_hwnd = CreateWindowExW(0, floatnotifysinkW, NULL, wsflags,
-                              0, 0, This->cctrl_width, height, This->cctrls_hwnd,
+                              0, 0, This->cctrl_width, height, parent_hwnd,
                               (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, This);
     control_hwnd = CreateWindowExW(ctrl_exflags, wndclass, text, wsflags | ctrl_wsflags,
                                    0, 0, This->cctrl_width, height, ns_hwnd,
@@ -809,7 +1180,16 @@ static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
     ctrl->id = id;
     ctrl->dlgid = This->cctrl_next_dlgid;
     ctrl->cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
-    list_add_tail(&This->cctrls, &ctrl->entry);
+    list_init(&ctrl->sub_cctrls);
+    list_init(&ctrl->sub_items);
+
+    if(This->cctrl_active_vg)
+        list_add_tail(&This->cctrl_active_vg->sub_cctrls, &ctrl->sub_cctrls_entry);
+    else
+        list_add_tail(&This->cctrls, &ctrl->entry);
+
+    SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)ctrl);
+
     if(ppctrl) *ppctrl = ctrl;
 
     This->cctrl_next_dlgid++;
@@ -828,9 +1208,8 @@ static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
     UINT cur_col_pos, cur_row_pos;
     customctrl *ctrl;
     BOOL fits_height;
-    static const UINT col_indent = 100; /* The first column is indented 100px */
-    static const UINT cspacing = 90;    /* Columns are spaced with 90px */
-    static const UINT rspacing = 4;     /* Rows are spaced with 4 px. */
+    UINT cspacing = MulDiv(90, This->dpi_x, USER_DEFAULT_SCREEN_DPI);    /* Columns are spaced with 90px */
+    UINT rspacing = MulDiv(4, This->dpi_y, USER_DEFAULT_SCREEN_DPI);     /* Rows are spaced with 4 px. */
 
     /* Given the new width of the container, this function determines the
      * needed height of the container and places the controls according to
@@ -840,7 +1219,7 @@ static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
     TRACE("%p\n", This);
 
     column_width = This->cctrl_width + cspacing;
-    nr_of_cols = (container_width - col_indent + cspacing) / column_width;
+    nr_of_cols = (container_width - This->cctrl_indent + cspacing) / column_width;
 
     /* We don't need to do anything unless the number of visible columns has changed. */
     if(nr_of_cols == This->cctrls_cols)
@@ -850,7 +1229,7 @@ static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
         return rc.bottom - rc.top;
     }
 
-   This->cctrls_cols = nr_of_cols;
+    This->cctrls_cols = nr_of_cols;
 
     /* Get the size of the tallest control, and the total size of
      * all the controls to figure out the number of slots we need.
@@ -860,10 +1239,7 @@ static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
     {
         if(ctrl->cdcstate & CDCS_VISIBLE)
         {
-            RECT rc;
-            UINT control_height;
-            GetWindowRect(ctrl->wrapper_hwnd, &rc);
-            control_height = rc.bottom - rc.top;
+            UINT control_height = ctrl_get_height(ctrl);
             max_control_height = max(max_control_height, control_height);
 
             total_height +=  control_height + rspacing;
@@ -888,10 +1264,7 @@ static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
         {
             if(ctrl->cdcstate & CDCS_VISIBLE)
             {
-                RECT rc;
-                UINT control_height;
-                GetWindowRect(ctrl->wrapper_hwnd, &rc);
-                control_height = rc.bottom - rc.top;
+                UINT control_height = ctrl_get_height(ctrl);
 
                 if(cur_row_pos + control_height > container_height)
                 {
@@ -913,13 +1286,13 @@ static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
 
     /* Move the controls to their final destination
      */
-    cur_col_pos = col_indent, cur_row_pos = 0;
+    cur_col_pos = 0, cur_row_pos = 0;
     LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
     {
         if(ctrl->cdcstate & CDCS_VISIBLE)
         {
             RECT rc;
-            UINT control_height;
+            UINT control_height, control_indent;
             GetWindowRect(ctrl->wrapper_hwnd, &rc);
             control_height = rc.bottom - rc.top;
 
@@ -929,7 +1302,13 @@ static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
                 cur_col_pos += This->cctrl_width + cspacing;
             }
 
-            SetWindowPos(ctrl->wrapper_hwnd, NULL, cur_col_pos, cur_row_pos, 0, 0,
+
+            if(ctrl->type == IDLG_CCTRL_VISUALGROUP)
+                control_indent = 0;
+            else
+                control_indent = This->cctrl_indent;
+
+            SetWindowPos(ctrl->wrapper_hwnd, NULL, cur_col_pos + control_indent, cur_row_pos, 0, 0,
                          SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
 
             cur_row_pos += control_height + rspacing;
@@ -943,6 +1322,27 @@ static UINT ctrl_container_resize(FileDialogImpl *This, UINT container_width)
     return container_height;
 }
 
+static void ctrl_set_font(customctrl *ctrl, HFONT font)
+{
+    customctrl *sub_ctrl;
+    cctrl_item* item;
+
+    SendMessageW(ctrl->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
+
+    LIST_FOR_EACH_ENTRY(sub_ctrl, &ctrl->sub_cctrls, customctrl, sub_cctrls_entry)
+    {
+        ctrl_set_font(sub_ctrl, font);
+    }
+
+    if (ctrl->type == IDLG_CCTRL_RADIOBUTTONLIST)
+    {
+        LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
+        {
+            SendMessageW(item->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
+        }
+    }
+}
+
 static void ctrl_container_reparent(FileDialogImpl *This, HWND parent)
 {
     LONG wndstyle;
@@ -967,7 +1367,7 @@ static void ctrl_container_reparent(FileDialogImpl *This, HWND parent)
 
         LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
         {
-            if(font) SendMessageW(ctrl->hwnd, WM_SETFONT, (WPARAM)font, TRUE);
+            if(font) ctrl_set_font(ctrl, font);
             customctrl_resize(This, ctrl);
         }
     }
@@ -1000,18 +1400,8 @@ static LRESULT ctrl_container_on_wm_destroy(FileDialogImpl *This)
 
     LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->cctrls, customctrl, entry)
     {
-        TRACE("Freeing control %p\n", cur1);
         list_remove(&cur1->entry);
-
-        if(cur1->type == IDLG_CCTRL_MENU)
-        {
-            TBBUTTON tbb;
-            SendMessageW(cur1->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
-            DestroyMenu((HMENU)tbb.dwData);
-        }
-
-        DestroyWindow(cur1->hwnd);
-        HeapFree(GetProcessHeap(), 0, cur1);
+        ctrl_free(cur1);
     }
 
     return TRUE;
@@ -1031,21 +1421,84 @@ static LRESULT CALLBACK ctrl_container_wndproc(HWND hwnd, UINT umessage, WPARAM
     return FALSE;
 }
 
+static void radiobuttonlist_set_selected_item(FileDialogImpl *This, customctrl *ctrl, cctrl_item *item)
+{
+    cctrl_item *cursor;
+
+    LIST_FOR_EACH_ENTRY(cursor, &ctrl->sub_items, cctrl_item, entry)
+    {
+        SendMessageW(cursor->hwnd, BM_SETCHECK, (cursor == item) ? BST_CHECKED : BST_UNCHECKED, 0);
+    }
+}
+
+static LRESULT radiobuttonlist_on_bn_clicked(FileDialogImpl *This, HWND hwnd, HWND child)
+{
+    DWORD ctrl_id = (DWORD)GetWindowLongPtrW(hwnd, GWLP_ID);
+    customctrl *ctrl;
+    cctrl_item *item;
+    BOOL found_item=FALSE;
+
+    ctrl = get_cctrl_from_dlgid(This, ctrl_id);
+
+    if (!ctrl)
+    {
+        ERR("Can't find this control\n");
+        return 0;
+    }
+
+    LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
+    {
+        if (item->hwnd == child)
+        {
+            found_item = TRUE;
+            break;
+        }
+    }
+
+    if (!found_item)
+    {
+        ERR("Can't find control item\n");
+        return 0;
+    }
+
+    radiobuttonlist_set_selected_item(This, ctrl, item);
+
+    cctrl_event_OnItemSelected(This, ctrl->id, item->id);
+
+    return 0;
+}
+
+static LRESULT radiobuttonlist_on_wm_command(FileDialogImpl *This, HWND hwnd, WPARAM wparam, LPARAM lparam)
+{
+    switch(HIWORD(wparam))
+    {
+    case BN_CLICKED:          return radiobuttonlist_on_bn_clicked(This, hwnd, (HWND)lparam);
+    }
+
+    return FALSE;
+}
+
+static LRESULT CALLBACK radiobuttonlist_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+    FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+
+    switch(message)
+    {
+    case WM_COMMAND:        return radiobuttonlist_on_wm_command(This, hwnd, wparam, lparam);
+    }
+
+    return DefWindowProcW(hwnd, message, wparam, lparam);
+}
+
 static HRESULT init_custom_controls(FileDialogImpl *This)
 {
     WNDCLASSW wc;
+    HDC hdc;
     static const WCHAR ctrl_container_classname[] =
         {'i','d','l','g','_','c','o','n','t','a','i','n','e','r','_','p','a','n','e',0};
 
     InitCommonControlsEx(NULL);
 
-    This->cctrl_width = 160;      /* Controls have a fixed width */
-    This->cctrl_def_height = 23;
-    This->cctrls_cols = 0;
-
-    This->cctrl_next_dlgid = 0x2000;
-    list_init(&This->cctrls);
-
     if( !GetClassInfoW(COMDLG32_hInstance, ctrl_container_classname, &wc) )
     {
         wc.style            = CS_HREDRAW | CS_VREDRAW;
@@ -1065,10 +1518,24 @@ static HRESULT init_custom_controls(FileDialogImpl *This)
     This->cctrls_hwnd = CreateWindowExW(0, ctrl_container_classname, NULL,
                                         WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
                                         0, 0, 0, 0, NULL, 0,
-                                        COMDLG32_hInstance, (void*)This);
+                                        COMDLG32_hInstance, This);
     if(!This->cctrls_hwnd)
         return E_FAIL;
 
+    hdc = GetDC(This->cctrls_hwnd);
+    This->dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
+    This->dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
+    ReleaseDC(This->cctrls_hwnd, hdc);
+
+    This->cctrl_width = MulDiv(160, This->dpi_x, USER_DEFAULT_SCREEN_DPI);      /* Controls have a fixed width */
+    This->cctrl_indent = MulDiv(100, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
+    This->cctrl_def_height = MulDiv(23, This->dpi_y, USER_DEFAULT_SCREEN_DPI);
+    This->cctrls_cols = 0;
+
+    This->cctrl_next_dlgid = 0x2000;
+    list_init(&This->cctrls);
+    This->cctrl_active_vg = NULL;
+
     SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, WS_TABSTOP);
 
     /* Register class for  */
@@ -1090,35 +1557,110 @@ static HRESULT init_custom_controls(FileDialogImpl *This)
             ERR("Failed to register FloatNotifySink window class.\n");
     }
 
+    if( !GetClassInfoW(COMDLG32_hInstance, radiobuttonlistW, &wc) ||
+        wc.hInstance != COMDLG32_hInstance)
+    {
+        wc.style            = CS_HREDRAW | CS_VREDRAW;
+        wc.lpfnWndProc      = radiobuttonlist_proc;
+        wc.cbClsExtra       = 0;
+        wc.cbWndExtra       = 0;
+        wc.hInstance        = COMDLG32_hInstance;
+        wc.hIcon            = 0;
+        wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_ARROW);
+        wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
+        wc.lpszMenuName     = NULL;
+        wc.lpszClassName    = radiobuttonlistW;
+
+        if (!RegisterClassW(&wc))
+            ERR("Failed to register RadioButtonList window class.\n");
+    }
+
     return S_OK;
 }
 
 /**************************************************************************
  * Window related functions.
  */
-static SIZE update_layout(FileDialogImpl *This)
+static BOOL update_open_dropdown(FileDialogImpl *This)
+{
+    /* Show or hide the open dropdown button as appropriate */
+    BOOL show=FALSE, showing;
+    HWND open_hwnd, dropdown_hwnd;
+
+    if (This->hmenu_opendropdown)
+    {
+        INT num_visible_items=0;
+        cctrl_item* item;
+
+        LIST_FOR_EACH_ENTRY(item, &This->cctrl_opendropdown.sub_items, cctrl_item, entry)
+        {
+            if (item->cdcstate & CDCS_VISIBLE)
+            {
+                num_visible_items++;
+                if (num_visible_items >= 2)
+                {
+                    show = TRUE;
+                    break;
+                }
+            }
+        }
+    }
+
+    open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
+    dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
+
+    showing = (GetWindowLongPtrW(dropdown_hwnd, GWL_STYLE) & WS_VISIBLE) != 0;
+
+    if (showing != show)
+    {
+        RECT open_rc, dropdown_rc;
+
+        GetWindowRect(open_hwnd, &open_rc);
+        GetWindowRect(dropdown_hwnd, &dropdown_rc);
+
+        if (show)
+        {
+            ShowWindow(dropdown_hwnd, SW_SHOW);
+
+            SetWindowPos(open_hwnd, NULL, 0, 0,
+                (open_rc.right - open_rc.left) - (dropdown_rc.right - dropdown_rc.left),
+                open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+        }
+        else
+        {
+            ShowWindow(dropdown_hwnd, SW_HIDE);
+
+            SetWindowPos(open_hwnd, NULL, 0, 0,
+                (open_rc.right - open_rc.left) + (dropdown_rc.right - dropdown_rc.left),
+                open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+        }
+    }
+
+    return show;
+}
+
+static void update_layout(FileDialogImpl *This)
 {
     HDWP hdwp;
     HWND hwnd;
     RECT dialog_rc;
-    RECT cancel_rc, open_rc;
+    RECT cancel_rc, dropdown_rc, open_rc;
     RECT filetype_rc, filename_rc, filenamelabel_rc;
     RECT toolbar_rc, ebrowser_rc, customctrls_rc;
-    int missing_width, missing_height;
     static const UINT vspacing = 4, hspacing = 4;
-    SIZE ret;
-
-    GetClientRect(This->dlg_hwnd, &dialog_rc);
+    static const UINT min_width = 320, min_height = 200;
+    BOOL show_dropdown;
 
-    missing_width = max(0, 320 - dialog_rc.right);
-    missing_height = max(0, 200 - dialog_rc.bottom);
+    if (!GetClientRect(This->dlg_hwnd, &dialog_rc))
+    {
+        TRACE("Invalid dialog window, not updating layout\n");
+        return;
+    }
 
-    if(missing_width || missing_height)
+    if(dialog_rc.right < min_width || dialog_rc.bottom < min_height)
     {
-        TRACE("Missing (%d, %d)\n", missing_width, missing_height);
-        ret.cx = missing_width;
-        ret.cy = missing_height;
-        return ret;
+        TRACE("Dialog size (%d, %d) too small, not updating layout\n", dialog_rc.right, dialog_rc.bottom);
+        return;
     }
 
     /****
@@ -1140,6 +1682,30 @@ static SIZE update_layout(FileDialogImpl *This)
         cancel_rc.bottom = cancel_rc.top + cancel_height;
     }
 
+    /* Open/Save dropdown */
+    show_dropdown = update_open_dropdown(This);
+
+    if(show_dropdown)
+    {
+        int dropdown_width, dropdown_height;
+        hwnd = GetDlgItem(This->dlg_hwnd, psh1);
+
+        GetWindowRect(hwnd, &dropdown_rc);
+        dropdown_width = dropdown_rc.right - dropdown_rc.left;
+        dropdown_height = dropdown_rc.bottom - dropdown_rc.top;
+
+        dropdown_rc.left = cancel_rc.left - dropdown_width - hspacing;
+        dropdown_rc.top = cancel_rc.top;
+        dropdown_rc.right = dropdown_rc.left + dropdown_width;
+        dropdown_rc.bottom = dropdown_rc.top + dropdown_height;
+    }
+    else
+    {
+        dropdown_rc.left = dropdown_rc.right = cancel_rc.left - hspacing;
+        dropdown_rc.top = cancel_rc.top;
+        dropdown_rc.bottom = cancel_rc.bottom;
+    }
+
     /* Open/Save button */
     hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
     if(hwnd)
@@ -1149,8 +1715,8 @@ static SIZE update_layout(FileDialogImpl *This)
         open_width = open_rc.right - open_rc.left;
         open_height = open_rc.bottom - open_rc.top;
 
-        open_rc.left = cancel_rc.left - open_width - hspacing;
-        open_rc.top = cancel_rc.top;
+        open_rc.left = dropdown_rc.left - open_width;
+        open_rc.top = dropdown_rc.top;
         open_rc.right = open_rc.left + open_width;
         open_rc.bottom = open_rc.top + open_height;
     }
@@ -1215,17 +1781,17 @@ static SIZE update_layout(FileDialogImpl *This)
     }
 
     /* The custom controls */
-    customctrls_rc.left = dialog_rc.left + vspacing;
-    customctrls_rc.right = dialog_rc.right - vspacing;
-    customctrls_rc.bottom = filename_rc.top - hspacing;
+    customctrls_rc.left = dialog_rc.left + hspacing;
+    customctrls_rc.right = dialog_rc.right - hspacing;
+    customctrls_rc.bottom = filename_rc.top - vspacing;
     customctrls_rc.top = customctrls_rc.bottom -
         ctrl_container_resize(This, customctrls_rc.right - customctrls_rc.left);
 
     /* The ExplorerBrowser control. */
-    ebrowser_rc.left = dialog_rc.left + vspacing;
+    ebrowser_rc.left = dialog_rc.left + hspacing;
     ebrowser_rc.top = toolbar_rc.bottom + vspacing;
     ebrowser_rc.right = dialog_rc.right - hspacing;
-    ebrowser_rc.bottom = customctrls_rc.top - hspacing;
+    ebrowser_rc.bottom = customctrls_rc.top - vspacing;
 
     /****
      * Move everything to the right place.
@@ -1261,6 +1827,10 @@ static SIZE update_layout(FileDialogImpl *This)
         DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
 
+    if(hdwp && This->hmenu_opendropdown && (hwnd = GetDlgItem(This->dlg_hwnd, psh1)))
+        DeferWindowPos(hdwp, hwnd, NULL, dropdown_rc.left, dropdown_rc.top, 0, 0,
+                       SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
+
     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
         DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
@@ -1270,13 +1840,13 @@ static SIZE update_layout(FileDialogImpl *This)
     else
         ERR("Failed to position dialog controls.\n");
 
-    ret.cx = 0; ret.cy = 0;
-    return ret;
+    return;
 }
 
 static HRESULT init_explorerbrowser(FileDialogImpl *This)
 {
     IShellItem *psi_folder;
+    IObjectWithSite *client;
     FOLDERSETTINGS fos;
     RECT rc = {0};
     HRESULT hr;
@@ -1292,7 +1862,7 @@ static HRESULT init_explorerbrowser(FileDialogImpl *This)
         return hr;
     }
 
-    IExplorerBrowser_SetOptions(This->peb, EBO_SHOWFRAMES);
+    IExplorerBrowser_SetOptions(This->peb, EBO_SHOWFRAMES | EBO_NOBORDER);
 
     hr = IExplorerBrowser_Initialize(This->peb, This->dlg_hwnd, &rc, NULL);
     if(FAILED(hr))
@@ -1313,9 +1883,14 @@ static HRESULT init_explorerbrowser(FileDialogImpl *This)
 
     IExplorerBrowser_SetFolderSettings(This->peb, &fos);
 
-    hr = IUnknown_SetSite((IUnknown*)This->peb, (IUnknown*)This);
-    if(FAILED(hr))
-        ERR("SetSite (ExplorerBrowser) failed.\n");
+    hr = IExplorerBrowser_QueryInterface(This->peb, &IID_IObjectWithSite, (void**)&client);
+    if (hr == S_OK)
+    {
+        hr = IObjectWithSite_SetSite(client, (IUnknown*)&This->IFileDialog2_iface);
+        IObjectWithSite_Release(client);
+        if(FAILED(hr))
+            ERR("SetSite failed, 0x%08x\n", hr);
+    }
 
     /* Browse somewhere */
     psi_folder = This->psi_setfolder ? This->psi_setfolder : This->psi_defaultfolder;
@@ -1360,31 +1935,60 @@ static void init_toolbar(FileDialogImpl *This, HWND hwnd)
 static void update_control_text(FileDialogImpl *This)
 {
     HWND hitem;
+    LPCWSTR custom_okbutton;
+    cctrl_item* item;
+    UINT min_width = MulDiv(50, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
+    UINT max_width = MulDiv(250, This->dpi_x, USER_DEFAULT_SCREEN_DPI);
+
     if(This->custom_title)
         SetWindowTextW(This->dlg_hwnd, This->custom_title);
 
-    if(This->custom_okbutton &&
+    if(This->hmenu_opendropdown && (item = get_first_item(&This->cctrl_opendropdown)))
+        custom_okbutton = item->label;
+    else
+        custom_okbutton = This->custom_okbutton;
+
+    if(custom_okbutton &&
        (hitem = GetDlgItem(This->dlg_hwnd, IDOK)))
     {
-        SetWindowTextW(hitem, This->custom_okbutton);
-        ctrl_resize(hitem, 50, 250, FALSE);
+        SetWindowTextW(hitem, custom_okbutton);
+        ctrl_resize(hitem, min_width, max_width, FALSE);
     }
 
     if(This->custom_cancelbutton &&
        (hitem = GetDlgItem(This->dlg_hwnd, IDCANCEL)))
     {
         SetWindowTextW(hitem, This->custom_cancelbutton);
-        ctrl_resize(hitem, 50, 250, FALSE);
+        ctrl_resize(hitem, min_width, max_width, FALSE);
     }
 
     if(This->custom_filenamelabel &&
        (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAMESTATIC)))
     {
         SetWindowTextW(hitem, This->custom_filenamelabel);
-        ctrl_resize(hitem, 50, 250, FALSE);
+        ctrl_resize(hitem, min_width, max_width, FALSE);
     }
 }
 
+static LRESULT CALLBACK dropdown_subclass_proc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
+{
+    static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
+    static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
+
+    if (umessage == WM_LBUTTONDOWN)
+    {
+        FileDialogImpl *This = GetPropW(hwnd, prop_this);
+
+        SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
+        show_opendropdown(This);
+        SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
+
+        return 0;
+    }
+
+    return CallWindowProcW((WNDPROC)GetPropW(hwnd, prop_oldwndproc), hwnd, umessage, wparam, lparam);
+}
+
 static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
 {
     FileDialogImpl *This = (FileDialogImpl*)lParam;
@@ -1405,10 +2009,32 @@ static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
     hitem = GetDlgItem(This->dlg_hwnd, IDC_FILETYPE);
     if(This->filterspec_count)
     {
-        UINT i;
+        HDC hdc;
+        HFONT font;
+        SIZE size;
+        UINT i, maxwidth = 0;
+
+        hdc = GetDC(hitem);
+        font = (HFONT)SendMessageW(hitem, WM_GETFONT, 0, 0);
+        SelectObject(hdc, font);
+
         for(i = 0; i < This->filterspec_count; i++)
+        {
             SendMessageW(hitem, CB_ADDSTRING, 0, (LPARAM)This->filterspecs[i].pszName);
 
+            if(GetTextExtentPoint32W(hdc, This->filterspecs[i].pszName, lstrlenW(This->filterspecs[i].pszName), &size))
+                maxwidth = max(maxwidth, size.cx);
+        }
+        ReleaseDC(hitem, hdc);
+
+        if(maxwidth > 0)
+        {
+            maxwidth += GetSystemMetrics(SM_CXVSCROLL) + 4;
+            SendMessageW(hitem, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0);
+        }
+        else
+            ERR("Failed to calculate width of filetype dropdown\n");
+
         SendMessageW(hitem, CB_SETCURSEL, This->filetypeindex, 0);
     }
     else
@@ -1418,13 +2044,50 @@ static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
        (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
         SendMessageW(hitem, WM_SETTEXT, 0, (LPARAM)This->set_filename);
 
+    if(This->hmenu_opendropdown)
+    {
+        HWND dropdown_hwnd;
+        LOGFONTW lfw, lfw_marlett;
+        HFONT dialog_font;
+        static const WCHAR marlett[] = {'M','a','r','l','e','t','t',0};
+        static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
+        static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
+
+        dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
+
+        /* Change dropdown button font to Marlett */
+        dialog_font = (HFONT)SendMessageW(dropdown_hwnd, WM_GETFONT, 0, 0);
+
+        GetObjectW(dialog_font, sizeof(lfw), &lfw);
+
+        memset(&lfw_marlett, 0, sizeof(lfw_marlett));
+        lstrcpyW(lfw_marlett.lfFaceName, marlett);
+        lfw_marlett.lfHeight = lfw.lfHeight;
+        lfw_marlett.lfCharSet = SYMBOL_CHARSET;
+
+        This->hfont_opendropdown = CreateFontIndirectW(&lfw_marlett);
+
+        SendMessageW(dropdown_hwnd, WM_SETFONT, (LPARAM)This->hfont_opendropdown, 0);
+
+        /* Subclass button so we can handle LBUTTONDOWN */
+        SetPropW(dropdown_hwnd, prop_this, This);
+        SetPropW(dropdown_hwnd, prop_oldwndproc,
+            (HANDLE)SetWindowLongPtrW(dropdown_hwnd, GWLP_WNDPROC, (LONG_PTR)dropdown_subclass_proc));
+    }
+
     ctrl_container_reparent(This, This->dlg_hwnd);
     init_explorerbrowser(This);
     init_toolbar(This, hwnd);
     update_control_text(This);
     update_layout(This);
 
-    return TRUE;
+    if(This->filterspec_count)
+        events_OnTypeChange(This);
+
+    if ((hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)))
+        SetFocus(hitem);
+
+    return FALSE;
 }
 
 static LRESULT on_wm_size(FileDialogImpl *This)
@@ -1459,6 +2122,9 @@ static LRESULT on_wm_destroy(FileDialogImpl *This)
     ctrl_container_reparent(This, NULL);
     This->dlg_hwnd = NULL;
 
+    DeleteObject(This->hfont_opendropdown);
+    This->hfont_opendropdown = NULL;
+
     return TRUE;
 }
 
@@ -1481,6 +2147,19 @@ static LRESULT on_idcancel(FileDialogImpl *This)
     return FALSE;
 }
 
+static LRESULT on_command_opendropdown(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
+{
+    if(HIWORD(wparam) == BN_CLICKED)
+    {
+        HWND hwnd = (HWND)lparam;
+        SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
+        show_opendropdown(This);
+        SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
+    }
+
+    return FALSE;
+}
+
 static LRESULT on_browse_back(FileDialogImpl *This)
 {
     TRACE("%p\n", This);
@@ -1534,6 +2213,11 @@ static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM l
             }
             CoTaskMemFree(filename);
         }
+
+        /* The documentation claims that OnTypeChange is called only
+         * when the dialog is opened, but this is obviously not the
+         * case. */
+        events_OnTypeChange(This);
     }
 
     return FALSE;
@@ -1545,6 +2229,7 @@ static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
     {
     case IDOK:                return on_idok(This);
     case IDCANCEL:            return on_idcancel(This);
+    case psh1:                return on_command_opendropdown(This, wparam, lparam);
     case IDC_NAVBACK:         return on_browse_back(This);
     case IDC_NAVFORWARD:      return on_browse_forward(This);
     case IDC_FILETYPE:        return on_command_filetype(This, wparam, lparam);
@@ -1693,6 +2378,9 @@ static ULONG WINAPI IFileDialog2_fnRelease(IFileDialog2 *iface)
         LocalFree(This->custom_cancelbutton);
         LocalFree(This->custom_filenamelabel);
 
+        DestroyMenu(This->hmenu_opendropdown);
+        DeleteObject(This->hfont_opendropdown);
+
         HeapFree(GetProcessHeap(), 0, This);
     }
 
@@ -1704,6 +2392,8 @@ static HRESULT WINAPI IFileDialog2_fnShow(IFileDialog2 *iface, HWND hwndOwner)
     FileDialogImpl *This = impl_from_IFileDialog2(iface);
     TRACE("%p (%p)\n", iface, hwndOwner);
 
+    This->opendropdown_has_selection = FALSE;
+
     return create_dialog(This, hwndOwner);
 }
 
@@ -1742,10 +2432,9 @@ static HRESULT WINAPI IFileDialog2_fnSetFileTypeIndex(IFileDialog2 *iface, UINT
     if(!This->filterspecs)
         return E_FAIL;
 
-    if(iFileType >= This->filterspec_count)
-        This->filetypeindex = This->filterspec_count - 1;
-    else
-        This->filetypeindex = iFileType;
+    iFileType = max(iFileType, 1);
+    iFileType = min(iFileType, This->filterspec_count);
+    This->filetypeindex = iFileType-1;
 
     return S_OK;
 }
@@ -1758,7 +2447,10 @@ static HRESULT WINAPI IFileDialog2_fnGetFileTypeIndex(IFileDialog2 *iface, UINT
     if(!piFileType)
         return E_INVALIDARG;
 
-    *piFileType = This->filetypeindex;
+    if(This->filterspec_count == 0)
+        *piFileType = 0;
+    else
+        *piFileType = This->filetypeindex + 1;
 
     return S_OK;
 }
@@ -1815,6 +2507,13 @@ static HRESULT WINAPI IFileDialog2_fnSetOptions(IFileDialog2 *iface, FILEOPENDIA
     FileDialogImpl *This = impl_from_IFileDialog2(iface);
     TRACE("%p (0x%x)\n", This, fos);
 
+    if( !(This->options & FOS_PICKFOLDERS) && (fos & FOS_PICKFOLDERS) )
+    {
+        WCHAR buf[30];
+        LoadStringW(COMDLG32_hInstance, IDS_SELECT_FOLDER, buf, sizeof(buf)/sizeof(WCHAR));
+        IFileDialog2_SetTitle(iface, buf);
+    }
+
     This->options = fos;
 
     return S_OK;
@@ -1928,10 +2627,8 @@ static HRESULT WINAPI IFileDialog2_fnGetFileName(IFileDialog2 *iface, LPWSTR *ps
         return E_INVALIDARG;
 
     *pszName = NULL;
-    if(get_file_name(This, pszName))
-        return S_OK;
-    else
-        return E_FAIL;
+    get_file_name(This, pszName);
+    return *pszName ? S_OK : E_FAIL;
 }
 
 static HRESULT WINAPI IFileDialog2_fnSetTitle(IFileDialog2 *iface, LPCWSTR pszTitle)
@@ -2004,7 +2701,7 @@ static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *p
 {
     FileDialogImpl *This = impl_from_IFileDialog2(iface);
     FIXME("stub - %p (%p, %d)\n", This, psi, fdap);
-    return E_NOTIMPL;
+    return S_OK;
 }
 
 static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension)
@@ -2032,8 +2729,9 @@ static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr)
 static HRESULT WINAPI IFileDialog2_fnSetClientGuid(IFileDialog2 *iface, REFGUID guid)
 {
     FileDialogImpl *This = impl_from_IFileDialog2(iface);
-    FIXME("stub - %p (%s)\n", This, debugstr_guid(guid));
-    return E_NOTIMPL;
+    TRACE("%p (%s)\n", This, debugstr_guid(guid));
+    This->client_guid = *guid;
+    return S_OK;
 }
 
 static HRESULT WINAPI IFileDialog2_fnClearClientData(IFileDialog2 *iface)
@@ -2730,7 +3428,7 @@ static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface,
                                                       REFIID riid, void **ppv)
 {
     FileDialogImpl *This = impl_from_IServiceProvider(iface);
-    HRESULT hr = E_FAIL;
+    HRESULT hr = E_NOTIMPL;
     TRACE("%p (%s, %s, %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
 
     *ppv = NULL;
@@ -2845,7 +3543,7 @@ static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
     ULONG attr;
     TRACE("%p (%p, %p)\n", This, shv, pidl);
 
-    if(!This->filterspec_count)
+    if(!This->filterspec_count && !(This->options & FOS_PICKFOLDERS))
         return S_OK;
 
     hr = SHGetIDListFromObject((IUnknown*)shv, &parent_pidl);
@@ -2869,6 +3567,12 @@ static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject(ICommDlgBrowser3 *iface,
         return S_OK;
     }
 
+    if((This->options & FOS_PICKFOLDERS) && !(attr & (SFGAO_FOLDER | SFGAO_LINK)))
+    {
+        IShellItem_Release(psi);
+        return S_FALSE;
+    }
+
     hr = S_OK;
     if(SUCCEEDED(IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename)))
     {
@@ -3024,8 +3728,32 @@ static HRESULT WINAPI IFileDialogCustomize_fnEnableOpenDropDown(IFileDialogCusto
                                                                 DWORD dwIDCtl)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d)\n", This, dwIDCtl);
-    return E_NOTIMPL;
+    MENUINFO mi;
+    TRACE("%p (%d)\n", This, dwIDCtl);
+
+    if (This->hmenu_opendropdown || get_cctrl(This, dwIDCtl))
+        return E_UNEXPECTED;
+
+    This->hmenu_opendropdown = CreatePopupMenu();
+
+    if (!This->hmenu_opendropdown)
+        return E_OUTOFMEMORY;
+
+    mi.cbSize = sizeof(mi);
+    mi.fMask = MIM_STYLE;
+    mi.dwStyle = MNS_NOTIFYBYPOS;
+    SetMenuInfo(This->hmenu_opendropdown, &mi);
+
+    This->cctrl_opendropdown.hwnd = NULL;
+    This->cctrl_opendropdown.wrapper_hwnd = NULL;
+    This->cctrl_opendropdown.id = dwIDCtl;
+    This->cctrl_opendropdown.dlgid = 0;
+    This->cctrl_opendropdown.type = IDLG_CCTRL_OPENDROPDOWN;
+    This->cctrl_opendropdown.cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
+    list_init(&This->cctrl_opendropdown.sub_cctrls);
+    list_init(&This->cctrl_opendropdown.sub_items);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface,
@@ -3043,6 +3771,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface
                           This->cctrl_def_height, &ctrl);
     if(SUCCEEDED(hr))
     {
+        SendMessageW(ctrl->hwnd, TB_BUTTONSTRUCTSIZE, sizeof(tbb), 0);
         ctrl->type = IDLG_CCTRL_MENU;
 
         /* Add the actual button with a popup menu. */
@@ -3096,8 +3825,18 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCusto
                                                                 DWORD dwIDCtl)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d)\n", This, dwIDCtl);
-    return E_NOTIMPL;
+    customctrl *ctrl;
+    HRESULT hr;
+    TRACE("%p (%d)\n", This, dwIDCtl);
+
+    hr =  cctrl_create_new(This, dwIDCtl, NULL, radiobuttonlistW, 0, 0, 0, &ctrl);
+    if(SUCCEEDED(hr))
+    {
+        ctrl->type = IDLG_CCTRL_RADIOBUTTONLIST;
+        SetWindowLongPtrW(ctrl->hwnd, GWLP_USERDATA, (LPARAM)This);
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize *iface,
@@ -3110,7 +3849,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize
     HRESULT hr;
     TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
 
-    hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX, 0,
+    hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX|BS_MULTILINE, 0,
                           This->cctrl_def_height, &ctrl);
     if(SUCCEEDED(hr))
     {
@@ -3187,8 +3926,11 @@ static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomiz
     case IDLG_CCTRL_PUSHBUTTON:
     case IDLG_CCTRL_CHECKBUTTON:
     case IDLG_CCTRL_TEXT:
+    case IDLG_CCTRL_VISUALGROUP:
         SendMessageW(ctrl->hwnd, WM_SETTEXT, 0, (LPARAM)pszLabel);
         break;
+    case IDLG_CCTRL_OPENDROPDOWN:
+        return E_NOTIMPL;
     default:
         break;
     }
@@ -3204,7 +3946,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnGetControlState(IFileDialogCustomiz
     customctrl *ctrl = get_cctrl(This, dwIDCtl);
     TRACE("%p (%d, %p)\n", This, dwIDCtl, pdwState);
 
-    if(!ctrl) return E_NOTIMPL;
+    if(!ctrl || ctrl->type == IDLG_CCTRL_OPENDROPDOWN) return E_NOTIMPL;
 
     *pdwState = ctrl->cdcstate;
     return S_OK;
@@ -3218,7 +3960,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnSetControlState(IFileDialogCustomiz
     customctrl *ctrl = get_cctrl(This,dwIDCtl);
     TRACE("%p (%d, %x)\n", This, dwIDCtl, dwState);
 
-    if(ctrl)
+    if(ctrl && ctrl->hwnd)
     {
         LONG wndstyle = GetWindowLongW(ctrl->hwnd, GWL_STYLE);
 
@@ -3251,7 +3993,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnGetEditBoxText(IFileDialogCustomize
     WCHAR len, *text;
     TRACE("%p (%d, %p)\n", This, dwIDCtl, ppszText);
 
-    if(!ctrl || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
+    if(!ctrl || !ctrl->hwnd || !(len = SendMessageW(ctrl->hwnd, WM_GETTEXTLENGTH, 0, 0)))
         return E_FAIL;
 
     text = CoTaskMemAlloc(sizeof(WCHAR)*(len+1));
@@ -3285,7 +4027,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnGetCheckButtonState(IFileDialogCust
     customctrl *ctrl = get_cctrl(This, dwIDCtl);
     TRACE("%p (%d, %p)\n", This, dwIDCtl, pbChecked);
 
-    if(ctrl)
+    if(ctrl && ctrl->hwnd)
         *pbChecked = (SendMessageW(ctrl->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
 
     return S_OK;
@@ -3299,7 +4041,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnSetCheckButtonState(IFileDialogCust
     customctrl *ctrl = get_cctrl(This, dwIDCtl);
     TRACE("%p (%d, %d)\n", This, dwIDCtl, bChecked);
 
-    if(ctrl)
+    if(ctrl && ctrl->hwnd)
         SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED:BST_UNCHECKED, 0);
 
     return S_OK;
@@ -3327,6 +4069,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
     customctrl *ctrl = get_cctrl(This, dwIDCtl);
+    HRESULT hr;
     TRACE("%p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
 
     if(!ctrl) return E_FAIL;
@@ -3336,9 +4079,11 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize
     case IDLG_CCTRL_COMBOBOX:
     {
         UINT index;
+        cctrl_item* item;
 
-        if(get_combobox_index_from_id(ctrl->hwnd, dwIDItem) != -1)
-            return E_INVALIDARG;
+        hr = add_item(ctrl, dwIDItem, pszLabel, &item);
+
+        if (FAILED(hr)) return hr;
 
         index = SendMessageW(ctrl->hwnd, CB_ADDSTRING, 0, (LPARAM)pszLabel);
         SendMessageW(ctrl->hwnd, CB_SETITEMDATA, index, dwIDItem);
@@ -3346,16 +4091,50 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddControlItem(IFileDialogCustomize
         return S_OK;
     }
     case IDLG_CCTRL_MENU:
+    case IDLG_CCTRL_OPENDROPDOWN:
     {
-        TBBUTTON tbb;
-        SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
+        cctrl_item* item;
+        HMENU hmenu;
 
-        if(GetMenuState((HMENU)tbb.dwData, dwIDItem, MF_BYCOMMAND) != -1)
-            return E_INVALIDARG;
+        hr = add_item(ctrl, dwIDItem, pszLabel, &item);
 
-        AppendMenuW((HMENU)tbb.dwData, MF_STRING, dwIDItem, pszLabel);
+        if (FAILED(hr)) return hr;
+
+        if (ctrl->type == IDLG_CCTRL_MENU)
+        {
+            TBBUTTON tbb;
+            SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
+            hmenu = (HMENU)tbb.dwData;
+        }
+        else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
+            hmenu = This->hmenu_opendropdown;
+
+        AppendMenuW(hmenu, MF_STRING, dwIDItem, pszLabel);
         return S_OK;
     }
+    case IDLG_CCTRL_RADIOBUTTONLIST:
+    {
+        cctrl_item* item;
+
+        hr = add_item(ctrl, dwIDItem, pszLabel, &item);
+
+        if (SUCCEEDED(hr))
+        {
+            item->hwnd = CreateWindowExW(0, WC_BUTTONW, pszLabel,
+                WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|BS_RADIOBUTTON|BS_MULTILINE,
+                0, 0, 0, 0, ctrl->hwnd, ULongToHandle(dwIDItem), COMDLG32_hInstance, 0);
+
+            if (!item->hwnd)
+            {
+                ERR("Failed to create radio button\n");
+                list_remove(&item->entry);
+                item_free(item);
+                return E_FAIL;
+            }
+        }
+
+        return hr;
+    }
     default:
         break;
     }
@@ -3377,30 +4156,65 @@ static HRESULT WINAPI IFileDialogCustomize_fnRemoveControlItem(IFileDialogCustom
     {
     case IDLG_CCTRL_COMBOBOX:
     {
-        UINT i, count = SendMessageW(ctrl->hwnd, CB_GETCOUNT, 0, 0);
-        if(!count || (count == CB_ERR))
-            return E_FAIL;
+        cctrl_item* item;
+        DWORD position;
 
-        for(i = 0; i < count; i++)
-            if(SendMessageW(ctrl->hwnd, CB_GETITEMDATA, 0, 0) == dwIDItem)
-            {
-                if(SendMessageW(ctrl->hwnd, CB_DELETESTRING, i, 0) == CB_ERR)
-                    return E_FAIL;
-                return S_OK;
-            }
+        item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
 
-        return E_UNEXPECTED;
+        if ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED))
+        {
+            if(SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0) == CB_ERR)
+                return E_FAIL;
+        }
+
+        list_remove(&item->entry);
+        item_free(item);
+
+        return S_OK;
     }
     case IDLG_CCTRL_MENU:
+    case IDLG_CCTRL_OPENDROPDOWN:
     {
-        TBBUTTON tbb;
         HMENU hmenu;
-        SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
-        hmenu = (HMENU)tbb.dwData;
+        cctrl_item* item;
+
+        item = get_item(ctrl, dwIDItem, 0, NULL);
 
-        if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
+        if (!item)
             return E_UNEXPECTED;
 
+        if (item->cdcstate & CDCS_VISIBLE)
+        {
+            if (ctrl->type == IDLG_CCTRL_MENU)
+            {
+                TBBUTTON tbb;
+                SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
+                hmenu = (HMENU)tbb.dwData;
+            }
+            else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
+                hmenu = This->hmenu_opendropdown;
+
+            if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
+                return E_UNEXPECTED;
+        }
+
+        list_remove(&item->entry);
+        item_free(item);
+
+        return S_OK;
+    }
+    case IDLG_CCTRL_RADIOBUTTONLIST:
+    {
+        cctrl_item* item;
+
+        item = get_item(ctrl, dwIDItem, 0, NULL);
+
+        if (!item)
+            return E_UNEXPECTED;
+
+        list_remove(&item->entry);
+        item_free(item);
+
         return S_OK;
     }
     default:
@@ -3426,8 +4240,34 @@ static HRESULT WINAPI IFileDialogCustomize_fnGetControlItemState(IFileDialogCust
                                                                  CDCONTROLSTATEF *pdwState)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p\n", This);
-    return E_NOTIMPL;
+    customctrl *ctrl = get_cctrl(This, dwIDCtl);
+    TRACE("%p (%d, %d, %p)\n", This, dwIDCtl, dwIDItem, pdwState);
+
+    if(!ctrl) return E_FAIL;
+
+    switch(ctrl->type)
+    {
+    case IDLG_CCTRL_COMBOBOX:
+    case IDLG_CCTRL_MENU:
+    case IDLG_CCTRL_OPENDROPDOWN:
+    case IDLG_CCTRL_RADIOBUTTONLIST:
+    {
+        cctrl_item* item;
+
+        item = get_item(ctrl, dwIDItem, 0, NULL);
+
+        if (!item)
+            return E_UNEXPECTED;
+
+        *pdwState = item->cdcstate;
+
+        return S_OK;
+    }
+    default:
+        break;
+    }
+
+    return E_FAIL;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemState(IFileDialogCustomize *iface,
@@ -3436,8 +4276,122 @@ static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemState(IFileDialogCust
                                                                  CDCONTROLSTATEF dwState)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p\n", This);
-    return E_NOTIMPL;
+    customctrl *ctrl = get_cctrl(This, dwIDCtl);
+    TRACE("%p (%d, %d, %x)\n", This, dwIDCtl, dwIDItem, dwState);
+
+    if(!ctrl) return E_FAIL;
+
+    switch(ctrl->type)
+    {
+    case IDLG_CCTRL_COMBOBOX:
+    {
+        cctrl_item* item;
+        BOOL visible, was_visible;
+        DWORD position;
+
+        item = get_item(ctrl, dwIDItem, CDCS_VISIBLE|CDCS_ENABLED, &position);
+
+        if (!item)
+            return E_UNEXPECTED;
+
+        visible = ((dwState & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
+        was_visible = ((item->cdcstate & (CDCS_VISIBLE|CDCS_ENABLED)) == (CDCS_VISIBLE|CDCS_ENABLED));
+
+        if (visible && !was_visible)
+        {
+            SendMessageW(ctrl->hwnd, CB_INSERTSTRING, position, (LPARAM)item->label);
+            SendMessageW(ctrl->hwnd, CB_SETITEMDATA, position, dwIDItem);
+        }
+        else if (!visible && was_visible)
+        {
+            SendMessageW(ctrl->hwnd, CB_DELETESTRING, position, 0);
+        }
+
+        item->cdcstate = dwState;
+
+        return S_OK;
+    }
+    case IDLG_CCTRL_MENU:
+    case IDLG_CCTRL_OPENDROPDOWN:
+    {
+        HMENU hmenu;
+        cctrl_item* item;
+        CDCONTROLSTATEF prev_state;
+        DWORD position;
+
+        item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, &position);
+
+        if (!item)
+            return E_UNEXPECTED;
+
+        prev_state = item->cdcstate;
+
+        if (ctrl->type == IDLG_CCTRL_MENU)
+        {
+            TBBUTTON tbb;
+            SendMessageW(ctrl->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
+            hmenu = (HMENU)tbb.dwData;
+        }
+        else /* ctrl->type == IDLG_CCTRL_OPENDROPDOWN */
+            hmenu = This->hmenu_opendropdown;
+
+        if (dwState & CDCS_VISIBLE)
+        {
+            if (prev_state & CDCS_VISIBLE)
+            {
+                /* change state */
+                EnableMenuItem(hmenu, dwIDItem,
+                    MF_BYCOMMAND|((dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED));
+            }
+            else
+            {
+                /* show item */
+                MENUITEMINFOW mii;
+
+                mii.cbSize = sizeof(mii);
+                mii.fMask = MIIM_ID|MIIM_STATE|MIIM_STRING;
+                mii.fState = (dwState & CDCS_ENABLED) ? MFS_ENABLED : MFS_DISABLED;
+                mii.wID = dwIDItem;
+                mii.dwTypeData = item->label;
+
+                InsertMenuItemW(hmenu, position, TRUE, &mii);
+            }
+        }
+        else if (prev_state & CDCS_VISIBLE)
+        {
+            /* hide item */
+            DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND);
+        }
+
+        item->cdcstate = dwState;
+
+        if (ctrl->type == IDLG_CCTRL_OPENDROPDOWN)
+        {
+            update_control_text(This);
+            update_layout(This);
+        }
+
+        return S_OK;
+    }
+    case IDLG_CCTRL_RADIOBUTTONLIST:
+    {
+        cctrl_item* item;
+
+        item = get_item(ctrl, dwIDItem, CDCS_VISIBLE, NULL);
+
+        if (!item)
+            return E_UNEXPECTED;
+
+        /* Oddly, native allows setting this but doesn't seem to do anything with it. */
+        item->cdcstate = dwState;
+
+        return S_OK;
+    }
+    default:
+        break;
+    }
+
+    return E_FAIL;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnGetSelectedControlItem(IFileDialogCustomize *iface,
@@ -3461,6 +4415,42 @@ static HRESULT WINAPI IFileDialogCustomize_fnGetSelectedControlItem(IFileDialogC
         *pdwIDItem = SendMessageW(ctrl->hwnd, CB_GETITEMDATA, index, 0);
         return S_OK;
     }
+    case IDLG_CCTRL_OPENDROPDOWN:
+        if (This->opendropdown_has_selection)
+        {
+            *pdwIDItem = This->opendropdown_selection;
+            return S_OK;
+        }
+        else
+        {
+            /* Return first enabled item. */
+            cctrl_item* item = get_first_item(ctrl);
+
+            if (item)
+            {
+                *pdwIDItem = item->id;
+                return S_OK;
+            }
+
+            WARN("no enabled items in open dropdown\n");
+            return E_FAIL;
+        }
+    case IDLG_CCTRL_RADIOBUTTONLIST:
+    {
+        cctrl_item* item;
+
+        LIST_FOR_EACH_ENTRY(item, &ctrl->sub_items, cctrl_item, entry)
+        {
+            if (SendMessageW(item->hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED)
+            {
+                *pdwIDItem = item->id;
+                return S_OK;
+            }
+        }
+
+        WARN("no checked items in radio button list\n");
+        return E_FAIL;
+    }
     default:
         FIXME("Unsupported control type %d\n", ctrl->type);
     }
@@ -3492,6 +4482,20 @@ static HRESULT WINAPI IFileDialogCustomize_fnSetSelectedControlItem(IFileDialogC
 
         return S_OK;
     }
+    case IDLG_CCTRL_RADIOBUTTONLIST:
+    {
+        cctrl_item* item;
+
+        item = get_item(ctrl, dwIDItem, 0, NULL);
+
+        if (item)
+        {
+            radiobuttonlist_set_selected_item(This, ctrl, item);
+            return S_OK;
+        }
+
+        return E_INVALIDARG;
+    }
     default:
         FIXME("Unsupported control type %d\n", ctrl->type);
     }
@@ -3504,15 +4508,32 @@ static HRESULT WINAPI IFileDialogCustomize_fnStartVisualGroup(IFileDialogCustomi
                                                               LPCWSTR pszLabel)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
-    return E_NOTIMPL;
+    customctrl *vg;
+    HRESULT hr;
+    TRACE("%p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
+
+    if(This->cctrl_active_vg)
+        return E_UNEXPECTED;
+
+    hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_STATICW, 0, 0,
+                          This->cctrl_def_height, &vg);
+    if(SUCCEEDED(hr))
+    {
+        vg->type = IDLG_CCTRL_VISUALGROUP;
+        This->cctrl_active_vg = vg;
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnEndVisualGroup(IFileDialogCustomize *iface)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p\n", This);
-    return E_NOTIMPL;
+    TRACE("%p\n", This);
+
+    This->cctrl_active_vg = NULL;
+
+    return S_OK;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnMakeProminent(IFileDialogCustomize *iface,
@@ -3520,7 +4541,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnMakeProminent(IFileDialogCustomize
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
     FIXME("stub - %p (%d)\n", This, dwIDCtl);
-    return E_NOTIMPL;
+    return S_OK;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemText(IFileDialogCustomize *iface,
@@ -3529,7 +4550,7 @@ static HRESULT WINAPI IFileDialogCustomize_fnSetControlItemText(IFileDialogCusto
                                                                 LPCWSTR pszLabel)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d, %d, %p)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
+    FIXME("stub - %p (%d, %d, %s)\n", This, dwIDCtl, dwIDItem, debugstr_w(pszLabel));
     return E_NOTIMPL;
 }
 
@@ -3578,7 +4599,7 @@ static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **p
     if(pUnkOuter)
         return CLASS_E_NOAGGREGATION;
 
-    fdimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(FileDialogImpl));
+    fdimpl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FileDialogImpl));
     if(!fdimpl)
         return E_OUTOFMEMORY;
 
@@ -3609,22 +4630,7 @@ static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **p
         fdimpl->custom_okbutton = StrDupW(buf);
     }
 
-    fdimpl->filterspecs = NULL;
-    fdimpl->filterspec_count = 0;
-    fdimpl->filetypeindex = 0;
-
-    fdimpl->psia_selection = fdimpl->psia_results = NULL;
-    fdimpl->psi_setfolder = fdimpl->psi_folder = NULL;
-
     list_init(&fdimpl->events_clients);
-    fdimpl->events_next_cookie = 0;
-
-    fdimpl->dlg_hwnd = NULL;
-    fdimpl->peb = NULL;
-
-    fdimpl->set_filename = NULL;
-    fdimpl->default_ext = NULL;
-    fdimpl->custom_cancelbutton = fdimpl->custom_filenamelabel = NULL;
 
     /* FIXME: The default folder setting should be restored for the
      * application if it was previously set. */
@@ -3636,12 +4642,12 @@ static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **p
     if(FAILED(hr))
     {
         ERR("Failed to initialize custom controls (0x%08x).\n", hr);
-        IUnknown_Release((IUnknown*)fdimpl);
+        IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
         return E_FAIL;
     }
 
-    hr = IUnknown_QueryInterface((IUnknown*)fdimpl, riid, ppv);
-    IUnknown_Release((IUnknown*)fdimpl);
+    hr = IFileDialog2_QueryInterface(&fdimpl->IFileDialog2_iface, riid, ppv);
+    IFileDialog2_Release(&fdimpl->IFileDialog2_iface);
     return hr;
 }
 
@@ -3655,4 +4661,4 @@ HRESULT FileSaveDialog_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
     return FileDialog_constructor(pUnkOuter, riid, ppv, ITEMDLG_TYPE_SAVE);
 }
 
-#endif // Win 7
+#endif /* Win 7 */