#define COBJMACROS
#define NONAMELESSUNION
-#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
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,
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 {
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;
}
}
+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);
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)
{
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;
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)
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))
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;
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;
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;
}
RECT rc;
HDC hdc;
WCHAR *c;
+ HFONT font;
TRACE("\n");
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)
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)
{
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;
}
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;
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;
}
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,
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++;
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
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)
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.
{
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;
{
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)
{
/* 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;
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;
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;
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);
}
}
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;
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;
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 */
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;
}
/****
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)
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;
}
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);
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;
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))
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;
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;
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
(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)
ctrl_container_reparent(This, NULL);
This->dlg_hwnd = NULL;
+ DeleteObject(This->hfont_opendropdown);
+ This->hfont_opendropdown = NULL;
+
return TRUE;
}
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);
}
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;
{
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);
LocalFree(This->custom_cancelbutton);
LocalFree(This->custom_filenamelabel);
+ DestroyMenu(This->hmenu_opendropdown);
+ DeleteObject(This->hfont_opendropdown);
+
HeapFree(GetProcessHeap(), 0, This);
}
FileDialogImpl *This = impl_from_IFileDialog2(iface);
TRACE("%p (%p)\n", iface, hwndOwner);
+ This->opendropdown_has_selection = FALSE;
+
return create_dialog(This, hwndOwner);
}
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;
}
if(!piFileType)
return E_INVALIDARG;
- *piFileType = This->filetypeindex;
+ if(This->filterspec_count == 0)
+ *piFileType = 0;
+ else
+ *piFileType = This->filetypeindex + 1;
return S_OK;
}
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;
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)
{
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)
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);
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)))
{
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,
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,
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))
{
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;
}
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;
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);
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));
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;
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;
{
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;
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);
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);
+
+ if (FAILED(hr)) return hr;
- AppendMenuW((HMENU)tbb.dwData, MF_STRING, dwIDItem, pszLabel);
+ 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;
}
{
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, i, 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 (!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);
- if(!hmenu || !DeleteMenu(hmenu, dwIDItem, MF_BYCOMMAND))
+ 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:
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,
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,
*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);
}
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);
}
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,
{
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,
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;
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;
-
- fdimpl->client_guid = GUID_NULL;
/* FIXME: The default folder setting should be restored for the
* application if it was previously set. */
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;
}