| /* Control Panel management |
| * |
| * Copyright 2001 Eric Pouech |
| * Copyright 2008 Owen Rudge |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <assert.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| #include "winreg.h" |
| #include "wine/debug.h" |
| #include "cpl.h" |
| #include "wine/unicode.h" |
| #include "commctrl.h" |
| |
| #define NO_SHLWAPI_REG |
| #include "shlwapi.h" |
| |
| #include "cpanel.h" |
| #include "shresdef.h" |
| #include "shell32_main.h" |
| |
| #define MAX_STRING_LEN 1024 |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(shlctrl); |
| |
| CPlApplet* Control_UnloadApplet(CPlApplet* applet) |
| { |
| unsigned i; |
| CPlApplet* next; |
| |
| for (i = 0; i < applet->count; i++) { |
| if (!applet->info[i].dwSize) continue; |
| applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].lData); |
| } |
| if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L); |
| FreeLibrary(applet->hModule); |
| next = applet->next; |
| HeapFree(GetProcessHeap(), 0, applet->cmd); |
| HeapFree(GetProcessHeap(), 0, applet); |
| return next; |
| } |
| |
| CPlApplet* Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel) |
| { |
| CPlApplet* applet; |
| unsigned i; |
| CPLINFO info; |
| NEWCPLINFOW newinfo; |
| |
| if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet)))) |
| return applet; |
| |
| applet->hWnd = hWnd; |
| |
| if (!(applet->hModule = LoadLibraryW(cmd))) { |
| WARN("Cannot load control panel applet %s\n", debugstr_w(cmd)); |
| goto theError; |
| } |
| if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) { |
| WARN("Not a valid control panel applet %s\n", debugstr_w(cmd)); |
| goto theError; |
| } |
| if (!applet->proc(hWnd, CPL_INIT, 0L, 0L)) { |
| WARN("Init of applet has failed\n"); |
| goto theError; |
| } |
| if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0) { |
| WARN("No subprogram in applet\n"); |
| goto theError; |
| } |
| |
| applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet, |
| sizeof(*applet) + (applet->count - 1) * sizeof(NEWCPLINFOW)); |
| |
| if (!(applet->cmd = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmd)+1) * sizeof(WCHAR)))) { |
| WARN("Cannot allocate memory for applet path\n"); |
| goto theError; |
| } |
| |
| lstrcpyW(applet->cmd, cmd); |
| |
| for (i = 0; i < applet->count; i++) { |
| ZeroMemory(&newinfo, sizeof(newinfo)); |
| newinfo.dwSize = sizeof(NEWCPLINFOA); |
| applet->info[i].dwSize = sizeof(NEWCPLINFOW); |
| applet->info[i].dwFlags = 0; |
| applet->info[i].dwHelpContext = 0; |
| applet->info[i].szHelpFile[0] = '\0'; |
| /* proc is supposed to return a null value upon success for |
| * CPL_INQUIRE and CPL_NEWINQUIRE |
| * However, real drivers don't seem to behave like this |
| * So, use introspection rather than return value |
| */ |
| applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info); |
| applet->info[i].lData = info.lData; |
| if (info.idIcon != CPL_DYNAMIC_RES) |
| applet->info[i].hIcon = LoadIconW(applet->hModule, |
| MAKEINTRESOURCEW(info.idIcon)); |
| if (info.idName != CPL_DYNAMIC_RES) |
| LoadStringW(applet->hModule, info.idName, |
| applet->info[i].szName, sizeof(applet->info[i].szName) / sizeof(WCHAR)); |
| if (info.idInfo != CPL_DYNAMIC_RES) |
| LoadStringW(applet->hModule, info.idInfo, |
| applet->info[i].szInfo, sizeof(applet->info[i].szInfo) / sizeof(WCHAR)); |
| |
| /* some broken control panels seem to return incorrect values in CPL_INQUIRE, |
| but proper data in CPL_NEWINQUIRE. if we get an empty string or a null |
| icon, see what we can get from CPL_NEWINQUIRE */ |
| |
| if ((applet->info[i].szName == 0) || (lstrlenW(applet->info[i].szName) == 0)) |
| info.idName = CPL_DYNAMIC_RES; |
| |
| /* zero-length szInfo may not be a buggy applet, but it doesn't hurt for us |
| to check anyway */ |
| |
| if ((applet->info[i].szInfo == 0) || (lstrlenW(applet->info[i].szInfo) == 0)) |
| info.idInfo = CPL_DYNAMIC_RES; |
| |
| if (applet->info[i].hIcon == NULL) |
| info.idIcon = CPL_DYNAMIC_RES; |
| |
| if ((info.idIcon == CPL_DYNAMIC_RES) || (info.idName == CPL_DYNAMIC_RES) || |
| (info.idInfo == CPL_DYNAMIC_RES)) { |
| applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo); |
| |
| applet->info[i].dwFlags = newinfo.dwFlags; |
| applet->info[i].dwHelpContext = newinfo.dwHelpContext; |
| applet->info[i].lData = newinfo.lData; |
| if (info.idIcon == CPL_DYNAMIC_RES) { |
| if (!newinfo.hIcon) WARN("couldn't get icon for applet %u\n", i); |
| applet->info[i].hIcon = newinfo.hIcon; |
| } |
| if (newinfo.dwSize == sizeof(NEWCPLINFOW)) { |
| if (info.idName == CPL_DYNAMIC_RES) |
| memcpy(applet->info[i].szName, newinfo.szName, sizeof(newinfo.szName)); |
| if (info.idInfo == CPL_DYNAMIC_RES) |
| memcpy(applet->info[i].szInfo, newinfo.szInfo, sizeof(newinfo.szInfo)); |
| memcpy(applet->info[i].szHelpFile, newinfo.szHelpFile, sizeof(newinfo.szHelpFile)); |
| } else { |
| if (info.idName == CPL_DYNAMIC_RES) |
| MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szName, |
| sizeof(((LPNEWCPLINFOA)&newinfo)->szName) / sizeof(CHAR), |
| applet->info[i].szName, |
| sizeof(applet->info[i].szName) / sizeof(WCHAR)); |
| if (info.idInfo == CPL_DYNAMIC_RES) |
| MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szInfo, |
| sizeof(((LPNEWCPLINFOA)&newinfo)->szInfo) / sizeof(CHAR), |
| applet->info[i].szInfo, |
| sizeof(applet->info[i].szInfo) / sizeof(WCHAR)); |
| MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szHelpFile, |
| sizeof(((LPNEWCPLINFOA)&newinfo)->szHelpFile) / sizeof(CHAR), |
| applet->info[i].szHelpFile, |
| sizeof(applet->info[i].szHelpFile) / sizeof(WCHAR)); |
| } |
| } |
| } |
| |
| applet->next = panel->first; |
| panel->first = applet; |
| |
| return applet; |
| |
| theError: |
| Control_UnloadApplet(applet); |
| return NULL; |
| } |
| |
| #define IDC_LISTVIEW 1000 |
| #define IDC_STATUSBAR 1001 |
| |
| #define NUM_COLUMNS 2 |
| #define LISTVIEW_DEFSTYLE (WS_CHILD | WS_VISIBLE | WS_TABSTOP |\ |
| LVS_SORTASCENDING | LVS_AUTOARRANGE | LVS_SINGLESEL) |
| |
| static BOOL Control_CreateListView (CPanel *panel) |
| { |
| RECT ws, sb; |
| WCHAR empty_string[] = {0}; |
| WCHAR buf[MAX_STRING_LEN]; |
| LVCOLUMNW lvc; |
| |
| /* Create list view */ |
| GetClientRect(panel->hWndStatusBar, &sb); |
| GetClientRect(panel->hWnd, &ws); |
| |
| panel->hWndListView = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, |
| empty_string, LISTVIEW_DEFSTYLE | LVS_ICON, |
| 0, 0, ws.right - ws.left, ws.bottom - ws.top - |
| (sb.bottom - sb.top), panel->hWnd, |
| (HMENU) IDC_LISTVIEW, panel->hInst, NULL); |
| |
| if (!panel->hWndListView) |
| return FALSE; |
| |
| /* Create image lists for list view */ |
| panel->hImageListSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), |
| GetSystemMetrics(SM_CYSMICON), ILC_MASK, 1, 1); |
| panel->hImageListLarge = ImageList_Create(GetSystemMetrics(SM_CXICON), |
| GetSystemMetrics(SM_CYICON), ILC_MASK, 1, 1); |
| |
| SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)panel->hImageListSmall); |
| SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM)panel->hImageListLarge); |
| |
| /* Create columns for list view */ |
| lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH; |
| lvc.pszText = buf; |
| lvc.fmt = LVCFMT_LEFT; |
| |
| /* Name column */ |
| lvc.iSubItem = 0; |
| lvc.cx = (ws.right - ws.left) / 3; |
| LoadStringW(shell32_hInstance, IDS_CPANEL_NAME, buf, sizeof(buf) / sizeof(buf[0])); |
| |
| if (ListView_InsertColumnW(panel->hWndListView, 0, &lvc) == -1) |
| return FALSE; |
| |
| /* Description column */ |
| lvc.iSubItem = 1; |
| lvc.cx = ((ws.right - ws.left) / 3) * 2; |
| LoadStringW(shell32_hInstance, IDS_CPANEL_DESCRIPTION, buf, sizeof(buf) / |
| sizeof(buf[0])); |
| |
| if (ListView_InsertColumnW(panel->hWndListView, 1, &lvc) == -1) |
| return FALSE; |
| |
| return(TRUE); |
| } |
| |
| static void Control_WndProc_Create(HWND hWnd, const CREATESTRUCTW* cs) |
| { |
| CPanel* panel = cs->lpCreateParams; |
| HMENU hMenu, hSubMenu; |
| CPlApplet* applet; |
| MENUITEMINFOW mii; |
| unsigned int i; |
| int menucount, index; |
| CPlItem *item; |
| LVITEMW lvItem; |
| INITCOMMONCONTROLSEX icex; |
| INT sb_parts; |
| int itemidx; |
| |
| SetWindowLongPtrW(hWnd, 0, (LONG_PTR)panel); |
| panel->hWnd = hWnd; |
| |
| /* Initialise common control DLL */ |
| icex.dwSize = sizeof(INITCOMMONCONTROLSEX); |
| icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES; |
| InitCommonControlsEx(&icex); |
| |
| /* create the status bar */ |
| if (!(panel->hWndStatusBar = CreateStatusWindowW(WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, NULL, hWnd, IDC_STATUSBAR))) |
| return; |
| |
| sb_parts = -1; |
| SendMessageW(panel->hWndStatusBar, SB_SETPARTS, 1, (LPARAM) &sb_parts); |
| |
| /* create the list view */ |
| if (!Control_CreateListView(panel)) |
| return; |
| |
| hMenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(MENU_CPANEL)); |
| |
| /* insert menu items for applets */ |
| hSubMenu = GetSubMenu(hMenu, 0); |
| menucount = 0; |
| |
| for (applet = panel->first; applet; applet = applet->next) { |
| for (i = 0; i < applet->count; i++) { |
| if (!applet->info[i].dwSize) |
| continue; |
| |
| /* set up a CPlItem for this particular subprogram */ |
| item = HeapAlloc(GetProcessHeap(), 0, sizeof(CPlItem)); |
| |
| if (!item) |
| continue; |
| |
| item->applet = applet; |
| item->id = i; |
| |
| mii.cbSize = sizeof(MENUITEMINFOW); |
| mii.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA; |
| mii.dwTypeData = applet->info[i].szName; |
| mii.cch = sizeof(applet->info[i].szName) / sizeof(applet->info[i].szName[0]); |
| mii.wID = IDM_CPANEL_APPLET_BASE + menucount; |
| mii.dwItemData = (ULONG_PTR)item; |
| |
| if (InsertMenuItemW(hSubMenu, menucount, TRUE, &mii)) { |
| /* add the list view item */ |
| index = ImageList_AddIcon(panel->hImageListLarge, applet->info[i].hIcon); |
| ImageList_AddIcon(panel->hImageListSmall, applet->info[i].hIcon); |
| |
| lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM; |
| lvItem.iItem = menucount; |
| lvItem.iSubItem = 0; |
| lvItem.pszText = applet->info[i].szName; |
| lvItem.iImage = index; |
| lvItem.lParam = (LPARAM) item; |
| |
| itemidx = ListView_InsertItemW(panel->hWndListView, &lvItem); |
| |
| /* add the description */ |
| ListView_SetItemTextW(panel->hWndListView, itemidx, 1, |
| applet->info[i].szInfo); |
| |
| /* update menu bar, increment count */ |
| DrawMenuBar(hWnd); |
| menucount++; |
| } |
| } |
| } |
| |
| panel->total_subprogs = menucount; |
| |
| /* check the "large items" icon in the View menu */ |
| hSubMenu = GetSubMenu(hMenu, 1); |
| CheckMenuRadioItem(hSubMenu, FCIDM_SHVIEW_BIGICON, FCIDM_SHVIEW_REPORTVIEW, |
| FCIDM_SHVIEW_BIGICON, MF_BYCOMMAND); |
| |
| SetMenu(hWnd, hMenu); |
| } |
| |
| static void Control_FreeCPlItems(HWND hWnd, CPanel *panel) |
| { |
| HMENU hMenu, hSubMenu; |
| MENUITEMINFOW mii; |
| unsigned int i; |
| |
| /* get the File menu */ |
| hMenu = GetMenu(hWnd); |
| |
| if (!hMenu) |
| return; |
| |
| hSubMenu = GetSubMenu(hMenu, 0); |
| |
| if (!hSubMenu) |
| return; |
| |
| /* loop and free the item data */ |
| for (i = IDM_CPANEL_APPLET_BASE; i <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs; i++) |
| { |
| mii.cbSize = sizeof(MENUITEMINFOW); |
| mii.fMask = MIIM_DATA; |
| |
| if (!GetMenuItemInfoW(hSubMenu, i, FALSE, &mii)) |
| continue; |
| |
| HeapFree(GetProcessHeap(), 0, (LPVOID) mii.dwItemData); |
| } |
| } |
| |
| static void Control_UpdateListViewStyle(CPanel *panel, UINT style, UINT id) |
| { |
| HMENU hMenu, hSubMenu; |
| |
| SetWindowLongW(panel->hWndListView, GWL_STYLE, LISTVIEW_DEFSTYLE | style); |
| |
| /* update the menu */ |
| hMenu = GetMenu(panel->hWnd); |
| hSubMenu = GetSubMenu(hMenu, 1); |
| |
| CheckMenuRadioItem(hSubMenu, FCIDM_SHVIEW_BIGICON, FCIDM_SHVIEW_REPORTVIEW, |
| id, MF_BYCOMMAND); |
| } |
| |
| static CPlItem* Control_GetCPlItem_From_MenuID(HWND hWnd, UINT id) |
| { |
| HMENU hMenu, hSubMenu; |
| MENUITEMINFOW mii; |
| |
| /* retrieve the CPlItem structure from the menu item data */ |
| hMenu = GetMenu(hWnd); |
| |
| if (!hMenu) |
| return NULL; |
| |
| hSubMenu = GetSubMenu(hMenu, 0); |
| |
| if (!hSubMenu) |
| return NULL; |
| |
| mii.cbSize = sizeof(MENUITEMINFOW); |
| mii.fMask = MIIM_DATA; |
| |
| if (!GetMenuItemInfoW(hSubMenu, id, FALSE, &mii)) |
| return NULL; |
| |
| return (CPlItem *) mii.dwItemData; |
| } |
| |
| static CPlItem* Control_GetCPlItem_From_ListView(CPanel *panel) |
| { |
| LVITEMW lvItem; |
| int selitem; |
| |
| selitem = SendMessageW(panel->hWndListView, LVM_GETNEXTITEM, -1, LVNI_FOCUSED |
| | LVNI_SELECTED); |
| |
| if (selitem != -1) |
| { |
| lvItem.iItem = selitem; |
| lvItem.mask = LVIF_PARAM; |
| |
| if (SendMessageW(panel->hWndListView, LVM_GETITEMW, 0, (LPARAM) &lvItem)) |
| return (CPlItem *) lvItem.lParam; |
| } |
| |
| return NULL; |
| } |
| |
| static void Control_StartApplet(HWND hWnd, CPlItem *item) |
| { |
| WCHAR verbOpen[] = {'c','p','l','o','p','e','n',0}; |
| WCHAR format[] = {'@','%','d',0}; |
| WCHAR param[MAX_PATH]; |
| |
| /* execute the applet if item is valid */ |
| if (item) |
| { |
| wsprintfW(param, format, item->id); |
| ShellExecuteW(hWnd, verbOpen, item->applet->cmd, param, NULL, SW_SHOW); |
| } |
| } |
| |
| static LRESULT WINAPI Control_WndProc(HWND hWnd, UINT wMsg, |
| WPARAM lParam1, LPARAM lParam2) |
| { |
| CPanel* panel = (CPanel*)GetWindowLongPtrW(hWnd, 0); |
| |
| if (panel || wMsg == WM_CREATE) { |
| switch (wMsg) { |
| case WM_CREATE: |
| Control_WndProc_Create(hWnd, (CREATESTRUCTW*)lParam2); |
| return 0; |
| case WM_DESTROY: |
| { |
| CPlApplet* applet = panel->first; |
| while (applet) |
| applet = Control_UnloadApplet(applet); |
| } |
| Control_FreeCPlItems(hWnd, panel); |
| PostQuitMessage(0); |
| break; |
| case WM_COMMAND: |
| switch (LOWORD(lParam1)) |
| { |
| case IDM_CPANEL_EXIT: |
| SendMessageW(hWnd, WM_CLOSE, 0, 0); |
| return 0; |
| |
| case IDM_CPANEL_ABOUT: |
| { |
| WCHAR appName[MAX_STRING_LEN]; |
| HICON icon = LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL), |
| IMAGE_ICON, 48, 48, LR_SHARED); |
| |
| LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, |
| sizeof(appName) / sizeof(appName[0])); |
| ShellAboutW(hWnd, appName, NULL, icon); |
| |
| return 0; |
| } |
| |
| case FCIDM_SHVIEW_BIGICON: |
| Control_UpdateListViewStyle(panel, LVS_ICON, FCIDM_SHVIEW_BIGICON); |
| return 0; |
| |
| case FCIDM_SHVIEW_SMALLICON: |
| Control_UpdateListViewStyle(panel, LVS_SMALLICON, FCIDM_SHVIEW_SMALLICON); |
| return 0; |
| |
| case FCIDM_SHVIEW_LISTVIEW: |
| Control_UpdateListViewStyle(panel, LVS_LIST, FCIDM_SHVIEW_LISTVIEW); |
| return 0; |
| |
| case FCIDM_SHVIEW_REPORTVIEW: |
| Control_UpdateListViewStyle(panel, LVS_REPORT, FCIDM_SHVIEW_REPORTVIEW); |
| return 0; |
| |
| default: |
| /* check if this is an applet */ |
| if ((LOWORD(lParam1) >= IDM_CPANEL_APPLET_BASE) && |
| (LOWORD(lParam1) <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs)) |
| { |
| Control_StartApplet(hWnd, Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1))); |
| return 0; |
| } |
| |
| break; |
| } |
| |
| break; |
| |
| case WM_NOTIFY: |
| { |
| LPNMHDR nmh = (LPNMHDR) lParam2; |
| |
| switch (nmh->idFrom) |
| { |
| case IDC_LISTVIEW: |
| switch (nmh->code) |
| { |
| case NM_RETURN: |
| case NM_DBLCLK: |
| { |
| Control_StartApplet(hWnd, Control_GetCPlItem_From_ListView(panel)); |
| return 0; |
| } |
| case LVN_ITEMCHANGED: |
| { |
| CPlItem *item = Control_GetCPlItem_From_ListView(panel); |
| |
| /* update the status bar if item is valid */ |
| if (item) |
| SetWindowTextW(panel->hWndStatusBar, |
| item->applet->info[item->id].szInfo); |
| else |
| SetWindowTextW(panel->hWndStatusBar, NULL); |
| |
| return 0; |
| } |
| } |
| |
| break; |
| } |
| |
| break; |
| } |
| |
| case WM_MENUSELECT: |
| /* check if this is an applet */ |
| if ((LOWORD(lParam1) >= IDM_CPANEL_APPLET_BASE) && |
| (LOWORD(lParam1) <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs)) |
| { |
| CPlItem *item = Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1)); |
| |
| /* update the status bar if item is valid */ |
| if (item) |
| SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].szInfo); |
| } |
| else if ((HIWORD(lParam1) == 0xFFFF) && (lParam2 == 0)) |
| { |
| /* reset status bar description to that of the selected icon */ |
| CPlItem *item = Control_GetCPlItem_From_ListView(panel); |
| |
| if (item) |
| SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].szInfo); |
| else |
| SetWindowTextW(panel->hWndStatusBar, NULL); |
| |
| return 0; |
| } |
| else |
| SetWindowTextW(panel->hWndStatusBar, NULL); |
| |
| return 0; |
| |
| case WM_SIZE: |
| { |
| HDWP hdwp; |
| RECT sb; |
| |
| hdwp = BeginDeferWindowPos(2); |
| |
| if (hdwp == NULL) |
| break; |
| |
| GetClientRect(panel->hWndStatusBar, &sb); |
| |
| hdwp = DeferWindowPos(hdwp, panel->hWndListView, NULL, 0, 0, |
| LOWORD(lParam2), HIWORD(lParam2) - (sb.bottom - sb.top), |
| SWP_NOZORDER | SWP_NOMOVE); |
| |
| if (hdwp == NULL) |
| break; |
| |
| hdwp = DeferWindowPos(hdwp, panel->hWndStatusBar, NULL, 0, 0, |
| LOWORD(lParam2), LOWORD(lParam1), SWP_NOZORDER | SWP_NOMOVE); |
| |
| if (hdwp != NULL) |
| EndDeferWindowPos(hdwp); |
| |
| return 0; |
| } |
| } |
| } |
| |
| return DefWindowProcW(hWnd, wMsg, lParam1, lParam2); |
| } |
| |
| static void Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst) |
| { |
| WNDCLASSEXW wc; |
| MSG msg; |
| WCHAR appName[MAX_STRING_LEN]; |
| const WCHAR className[] = {'S','h','e','l','l','_','C','o','n','t','r','o', |
| 'l','_','W','n','d','C','l','a','s','s',0}; |
| |
| LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, sizeof(appName) / sizeof(appName[0])); |
| |
| wc.style = CS_HREDRAW|CS_VREDRAW; |
| wc.lpfnWndProc = Control_WndProc; |
| wc.cbClsExtra = 0; |
| wc.cbWndExtra = sizeof(CPlApplet*); |
| wc.hInstance = panel->hInst = hInst; |
| wc.hIcon = LoadIconW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL) ); |
| wc.hCursor = LoadCursorW( 0, (LPWSTR)IDC_ARROW ); |
| wc.hbrBackground = GetStockObject(WHITE_BRUSH); |
| wc.lpszMenuName = NULL; |
| wc.lpszClassName = className; |
| wc.hIconSm = LoadImageW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL), IMAGE_ICON, |
| GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED); |
| |
| if (!RegisterClassExW(&wc)) return; |
| |
| CreateWindowExW(0, wc.lpszClassName, appName, |
| WS_OVERLAPPEDWINDOW | WS_VISIBLE, |
| CW_USEDEFAULT, CW_USEDEFAULT, |
| CW_USEDEFAULT, CW_USEDEFAULT, |
| hWnd, NULL, hInst, panel); |
| if (!panel->hWnd) return; |
| |
| while (GetMessageW(&msg, panel->hWnd, 0, 0)) { |
| TranslateMessage(&msg); |
| DispatchMessageW(&msg); |
| } |
| } |
| |
| static void Control_RegisterRegistryApplets(HWND hWnd, CPanel *panel, HKEY hkey_root, LPCWSTR szRepPath) |
| { |
| WCHAR name[MAX_PATH]; |
| WCHAR value[MAX_PATH]; |
| HKEY hkey; |
| |
| if (RegOpenKeyW(hkey_root, szRepPath, &hkey) == ERROR_SUCCESS) |
| { |
| int idx = 0; |
| |
| for(;; ++idx) |
| { |
| DWORD nameLen = MAX_PATH; |
| DWORD valueLen = MAX_PATH; |
| |
| if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)value, &valueLen) != ERROR_SUCCESS) |
| break; |
| |
| Control_LoadApplet(hWnd, value, panel); |
| } |
| RegCloseKey(hkey); |
| } |
| } |
| |
| static void Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst) |
| { |
| HANDLE h; |
| WIN32_FIND_DATAW fd; |
| WCHAR buffer[MAX_PATH]; |
| static const WCHAR wszAllCpl[] = {'*','.','c','p','l',0}; |
| static const WCHAR wszRegPath[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t', |
| '\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n', |
| '\\','C','o','n','t','r','o','l',' ','P','a','n','e','l','\\','C','p','l','s',0}; |
| WCHAR *p; |
| |
| /* first add .cpl files in the system directory */ |
| GetSystemDirectoryW( buffer, MAX_PATH ); |
| p = buffer + strlenW(buffer); |
| *p++ = '\\'; |
| lstrcpyW(p, wszAllCpl); |
| |
| if ((h = FindFirstFileW(buffer, &fd)) != INVALID_HANDLE_VALUE) { |
| do { |
| lstrcpyW(p, fd.cFileName); |
| Control_LoadApplet(hWnd, buffer, panel); |
| } while (FindNextFileW(h, &fd)); |
| FindClose(h); |
| } |
| |
| /* now check for cpls in the registry */ |
| Control_RegisterRegistryApplets(hWnd, panel, HKEY_LOCAL_MACHINE, wszRegPath); |
| Control_RegisterRegistryApplets(hWnd, panel, HKEY_CURRENT_USER, wszRegPath); |
| |
| Control_DoInterface(panel, hWnd, hInst); |
| } |
| |
| static void Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd) |
| /* forms to parse: |
| * foo.cpl,@sp,str |
| * foo.cpl,@sp |
| * foo.cpl,,str |
| * foo.cpl @sp |
| * foo.cpl str |
| * "a path\foo.cpl" |
| */ |
| { |
| LPWSTR buffer; |
| LPWSTR beg = NULL; |
| LPWSTR end; |
| WCHAR ch; |
| LPWSTR ptr; |
| signed sp = -1; |
| LPWSTR extraPmtsBuf = NULL; |
| LPWSTR extraPmts = NULL; |
| int quoted = 0; |
| |
| buffer = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd) + 1) * sizeof(*wszCmd)); |
| if (!buffer) return; |
| |
| end = lstrcpyW(buffer, wszCmd); |
| |
| for (;;) { |
| ch = *end; |
| if (ch == '"') quoted = !quoted; |
| if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) { |
| *end = '\0'; |
| if (beg) { |
| if (*beg == '@') { |
| sp = atoiW(beg + 1); |
| } else if (*beg == '\0') { |
| sp = -1; |
| } else { |
| extraPmtsBuf = beg; |
| } |
| } |
| if (ch == '\0') break; |
| beg = end + 1; |
| if (ch == ' ') while (end[1] == ' ') end++; |
| } |
| end++; |
| } |
| while ((ptr = StrChrW(buffer, '"'))) |
| memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR)); |
| |
| /* now check for any quotes in extraPmtsBuf and remove */ |
| if (extraPmtsBuf != NULL) |
| { |
| beg = end = extraPmtsBuf; |
| quoted = 0; |
| |
| for (;;) { |
| ch = *end; |
| if (ch == '"') quoted = !quoted; |
| if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) { |
| *end = '\0'; |
| if (beg) { |
| if (*beg != '\0') { |
| extraPmts = beg; |
| } |
| } |
| if (ch == '\0') break; |
| beg = end + 1; |
| if (ch == ' ') while (end[1] == ' ') end++; |
| } |
| end++; |
| } |
| |
| while ((ptr = StrChrW(extraPmts, '"'))) |
| memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR)); |
| |
| if (extraPmts == NULL) |
| extraPmts = extraPmtsBuf; |
| } |
| |
| /* Now check if there had been a numerical value in the extra params */ |
| if ((extraPmts) && (*extraPmts == '@') && (sp == -1)) { |
| sp = atoiW(extraPmts + 1); |
| } |
| |
| TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer), debugstr_w(extraPmts), sp); |
| |
| Control_LoadApplet(hWnd, buffer, panel); |
| |
| if (panel->first) { |
| CPlApplet* applet = panel->first; |
| |
| assert(applet && applet->next == NULL); |
| |
| /* we've been given a textual parameter (or none at all) */ |
| if (sp == -1) { |
| while ((++sp) != applet->count) { |
| if (applet->info[sp].dwSize) { |
| TRACE("sp %d, name %s\n", sp, debugstr_w(applet->info[sp].szName)); |
| |
| if (StrCmpIW(extraPmts, applet->info[sp].szName) == 0) |
| break; |
| } |
| } |
| } |
| |
| if (sp >= applet->count) { |
| WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count); |
| sp = 0; |
| } |
| |
| if (applet->info[sp].dwSize) { |
| if (!applet->proc(applet->hWnd, CPL_STARTWPARMSA, sp, (LPARAM)extraPmts)) |
| applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].lData); |
| } |
| |
| Control_UnloadApplet(applet); |
| } |
| |
| HeapFree(GetProcessHeap(), 0, buffer); |
| } |
| |
| /************************************************************************* |
| * Control_RunDLLW [SHELL32.@] |
| * |
| */ |
| void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow) |
| { |
| CPanel panel; |
| |
| TRACE("(%p, %p, %s, 0x%08x)\n", |
| hWnd, hInst, debugstr_w(cmd), nCmdShow); |
| |
| memset(&panel, 0, sizeof(panel)); |
| |
| if (!cmd || !*cmd) { |
| Control_DoWindow(&panel, hWnd, hInst); |
| } else { |
| Control_DoLaunch(&panel, hWnd, cmd); |
| } |
| } |
| |
| /************************************************************************* |
| * Control_RunDLLA [SHELL32.@] |
| * |
| */ |
| void WINAPI Control_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow) |
| { |
| DWORD len = MultiByteToWideChar(CP_ACP, 0, cmd, -1, NULL, 0 ); |
| LPWSTR wszCmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); |
| if (wszCmd && MultiByteToWideChar(CP_ACP, 0, cmd, -1, wszCmd, len )) |
| { |
| Control_RunDLLW(hWnd, hInst, wszCmd, nCmdShow); |
| } |
| HeapFree(GetProcessHeap(), 0, wszCmd); |
| } |
| |
| /************************************************************************* |
| * Control_FillCache_RunDLLW [SHELL32.@] |
| * |
| */ |
| HRESULT WINAPI Control_FillCache_RunDLLW(HWND hWnd, HANDLE hModule, DWORD w, DWORD x) |
| { |
| FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd, hModule, w, x); |
| return 0; |
| } |
| |
| /************************************************************************* |
| * Control_FillCache_RunDLLA [SHELL32.@] |
| * |
| */ |
| HRESULT WINAPI Control_FillCache_RunDLLA(HWND hWnd, HANDLE hModule, DWORD w, DWORD x) |
| { |
| return Control_FillCache_RunDLLW(hWnd, hModule, w, x); |
| } |
| |
| /************************************************************************* |
| * CallCPLEntry16 [SHELL32.166] |
| * |
| * called by desk.cpl on "Advanced" with: |
| * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0 |
| * |
| */ |
| DWORD WINAPI CallCPLEntry16(HMODULE hMod, FARPROC pFunc, DWORD dw3, DWORD dw4, DWORD dw5, DWORD dw6) |
| { |
| FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6); |
| return 0x0deadbee; |
| } |