control.c 27.5 KB
Newer Older
1 2 3
/* Control Panel management
 *
 * Copyright 2001 Eric Pouech
4
 * Copyright 2008 Owen Rudge
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
 */
20 21

#include <assert.h>
22
#include <stdarg.h>
23 24 25 26
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

27
#include "windef.h"
28 29 30
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
31
#include "winreg.h"
32
#include "wine/debug.h"
33
#include "cpl.h"
34
#include "wine/unicode.h"
35
#include "commctrl.h"
36 37 38

#define NO_SHLWAPI_REG
#include "shlwapi.h"
39

40
#include "cpanel.h"
41 42 43 44
#include "shresdef.h"
#include "shell32_main.h"

#define MAX_STRING_LEN      1024
45

46
WINE_DEFAULT_DEBUG_CHANNEL(shlctrl);
47

48
void Control_UnloadApplet(CPlApplet* applet)
49 50 51
{
    unsigned	i;

52
    for (i = 0; i < applet->count; i++)
53
        applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].data);
54

55 56
    if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L);
    FreeLibrary(applet->hModule);
57
    list_remove( &applet->entry );
58
    HeapFree(GetProcessHeap(), 0, applet->cmd);
59 60 61
    HeapFree(GetProcessHeap(), 0, applet);
}

62
CPlApplet*	Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel)
63 64
{
    CPlApplet*	applet;
65
    DWORD len;
66 67
    unsigned 	i;
    CPLINFO	info;
68
    NEWCPLINFOW newinfo;
69 70 71 72

    if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet))))
       return applet;

73 74 75 76 77 78 79 80 81 82 83 84 85
    len = ExpandEnvironmentStringsW(cmd, NULL, 0);
    if (len > 0)
    {
        if (!(applet->cmd = HeapAlloc(GetProcessHeap(), 0, (len+1) * sizeof(WCHAR))))
        {
            WARN("Cannot allocate memory for applet path\n");
            goto theError;
        }
        ExpandEnvironmentStringsW(cmd, applet->cmd, len+1);
    }
    else
    {
        WARN("Cannot expand applet path\n");
86 87 88
        goto theError;
    }

89 90
    applet->hWnd = hWnd;

91 92
    if (!(applet->hModule = LoadLibraryW(applet->cmd))) {
        WARN("Cannot load control panel applet %s\n", debugstr_w(applet->cmd));
93 94 95
	goto theError;
    }
    if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) {
96
        WARN("Not a valid control panel applet %s\n", debugstr_w(applet->cmd));
97 98 99 100 101 102 103 104
	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");
105
        applet->proc(applet->hWnd, CPL_EXIT, 0, 0);
106 107 108
	goto theError;
    }

109
    applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet,
110
                         FIELD_OFFSET( CPlApplet, info[applet->count] ));
111

112
    for (i = 0; i < applet->count; i++) {
113 114
       ZeroMemory(&newinfo, sizeof(newinfo));
       newinfo.dwSize = sizeof(NEWCPLINFOA);
115
       applet->info[i].helpfile[0] = 0;
116 117 118 119 120
       /* 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
	*/
121
       applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info);
122
       applet->info[i].data = info.lData;
123
       if (info.idIcon != CPL_DYNAMIC_RES)
124
	   applet->info[i].icon = LoadIconW(applet->hModule, MAKEINTRESOURCEW(info.idIcon));
125 126
       if (info.idName != CPL_DYNAMIC_RES)
	   LoadStringW(applet->hModule, info.idName,
127
		       applet->info[i].name, sizeof(applet->info[i].name) / sizeof(WCHAR));
128 129
       if (info.idInfo != CPL_DYNAMIC_RES)
	   LoadStringW(applet->hModule, info.idInfo,
130
		       applet->info[i].info, sizeof(applet->info[i].info) / sizeof(WCHAR));
131

132 133 134 135
       /* 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 */

136
       if (!applet->info[i].name[0]) info.idName = CPL_DYNAMIC_RES;
137 138 139 140

       /* zero-length szInfo may not be a buggy applet, but it doesn't hurt for us
          to check anyway */

141
       if (!applet->info[i].info[0]) info.idInfo = CPL_DYNAMIC_RES;
142

143
       if (applet->info[i].icon == NULL)
144 145
           info.idIcon = CPL_DYNAMIC_RES;

146 147 148 149
       if ((info.idIcon == CPL_DYNAMIC_RES) || (info.idName == CPL_DYNAMIC_RES) ||
           (info.idInfo == CPL_DYNAMIC_RES)) {
	   applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo);

150
	   applet->info[i].data = newinfo.lData;
151 152
	   if (info.idIcon == CPL_DYNAMIC_RES) {
	       if (!newinfo.hIcon) WARN("couldn't get icon for applet %u\n", i);
153
	       applet->info[i].icon = newinfo.hIcon;
154
	   }
155 156
	   if (newinfo.dwSize == sizeof(NEWCPLINFOW)) {
	       if (info.idName == CPL_DYNAMIC_RES)
157
	           memcpy(applet->info[i].name, newinfo.szName, sizeof(newinfo.szName));
158
	       if (info.idInfo == CPL_DYNAMIC_RES)
159 160
	           memcpy(applet->info[i].info, newinfo.szInfo, sizeof(newinfo.szInfo));
	       memcpy(applet->info[i].helpfile, newinfo.szHelpFile, sizeof(newinfo.szHelpFile));
161 162 163 164
	   } else {
	       if (info.idName == CPL_DYNAMIC_RES)
                   MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szName,
	                               sizeof(((LPNEWCPLINFOA)&newinfo)->szName) / sizeof(CHAR),
165
			               applet->info[i].name, sizeof(applet->info[i].name) / sizeof(WCHAR));
166 167 168
	       if (info.idInfo == CPL_DYNAMIC_RES)
                   MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szInfo,
	                               sizeof(((LPNEWCPLINFOA)&newinfo)->szInfo) / sizeof(CHAR),
169
			               applet->info[i].info, sizeof(applet->info[i].info) / sizeof(WCHAR));
170 171
               MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szHelpFile,
	                           sizeof(((LPNEWCPLINFOA)&newinfo)->szHelpFile) / sizeof(CHAR),
172 173
			           applet->info[i].helpfile,
                                   sizeof(applet->info[i].helpfile) / sizeof(WCHAR));
174 175
           }
       }
176 177
    }

178
    list_add_head( &panel->applets, &applet->entry );
179 180 181
    return applet;

 theError:
182 183 184
    FreeLibrary(applet->hModule);
    HeapFree(GetProcessHeap(), 0, applet->cmd);
    HeapFree(GetProcessHeap(), 0, applet);
185 186 187
    return NULL;
}

188
#define IDC_LISTVIEW        1000
189
#define IDC_STATUSBAR       1001
190 191 192 193 194 195 196

#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)
{
197
    RECT ws, sb;
198 199 200 201 202
    WCHAR empty_string[] = {0};
    WCHAR buf[MAX_STRING_LEN];
    LVCOLUMNW lvc;

    /* Create list view */
203
    GetClientRect(panel->hWndStatusBar, &sb);
204 205 206 207
    GetClientRect(panel->hWnd, &ws);

    panel->hWndListView = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW,
                          empty_string, LISTVIEW_DEFSTYLE | LVS_ICON,
208 209 210
                          0, 0, ws.right - ws.left, ws.bottom - ws.top -
                          (sb.bottom - sb.top), panel->hWnd,
                          (HMENU) IDC_LISTVIEW, panel->hInst, NULL);
211 212 213 214 215 216

    if (!panel->hWndListView)
        return FALSE;

    /* Create image lists for list view */
    panel->hImageListSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
217
        GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 1, 1);
218
    panel->hImageListLarge = ImageList_Create(GetSystemMetrics(SM_CXICON),
219
        GetSystemMetrics(SM_CYICON), ILC_COLOR32 | ILC_MASK, 1, 1);
220

221 222
    SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)panel->hImageListSmall);
    SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM)panel->hImageListLarge);
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

    /* 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);
}

249
static void 	 Control_WndProc_Create(HWND hWnd, const CREATESTRUCTW* cs)
250
{
251
   CPanel* panel = cs->lpCreateParams;
252
   HMENU hMenu, hSubMenu;
253
   CPlApplet* applet;
254
   MENUITEMINFOW mii;
255 256
   unsigned int i;
   int menucount, index;
257
   CPlItem *item;
258 259
   LVITEMW lvItem;
   INITCOMMONCONTROLSEX icex;
260
   INT sb_parts;
261
   int itemidx;
262

263
   SetWindowLongPtrW(hWnd, 0, (LONG_PTR)panel);
264
   panel->hWnd = hWnd;
265

266 267
   /* Initialise common control DLL */
   icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
268
   icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
269 270
   InitCommonControlsEx(&icex);

271 272 273 274 275 276 277
   /* 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);

278 279 280 281
   /* create the list view */
   if (!Control_CreateListView(panel))
       return;

282 283 284 285 286 287
   hMenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(MENU_CPANEL));

   /* insert menu items for applets */
   hSubMenu = GetSubMenu(hMenu, 0);
   menucount = 0;

288 289
   LIST_FOR_EACH_ENTRY( applet, &panel->applets, CPlApplet, entry )
   {
290 291 292 293 294 295 296
      for (i = 0; i < applet->count; i++) {
         /* set up a CPlItem for this particular subprogram */
         item = HeapAlloc(GetProcessHeap(), 0, sizeof(CPlItem));

         if (!item)
            continue;

297
         item->applet = applet;
298 299 300 301
         item->id = i;

         mii.cbSize = sizeof(MENUITEMINFOW);
         mii.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
302 303
         mii.dwTypeData = applet->info[i].name;
         mii.cch = sizeof(applet->info[i].name) / sizeof(WCHAR);
304
         mii.wID = IDM_CPANEL_APPLET_BASE + menucount;
305
         mii.dwItemData = (ULONG_PTR)item;
306 307

         if (InsertMenuItemW(hSubMenu, menucount, TRUE, &mii)) {
308
            /* add the list view item */
309 310 311 312
            HICON icon = applet->info[i].icon;
            if (!icon) icon = LoadImageW( 0, (LPCWSTR)IDI_WINLOGO, IMAGE_ICON, 0, 0, LR_SHARED );
            index = ImageList_AddIcon(panel->hImageListLarge, icon);
            ImageList_AddIcon(panel->hImageListSmall, icon);
313 314 315 316

            lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
            lvItem.iItem = menucount;
            lvItem.iSubItem = 0;
317
            lvItem.pszText = applet->info[i].name;
318 319 320
            lvItem.iImage = index;
            lvItem.lParam = (LPARAM) item;

321
            itemidx = ListView_InsertItemW(panel->hWndListView, &lvItem);
322 323

            /* add the description */
324
            ListView_SetItemTextW(panel->hWndListView, itemidx, 1, applet->info[i].info);
325 326 327 328

            /* update menu bar, increment count */
            DrawMenuBar(hWnd);
            menucount++;
329 330 331 332 333 334 335 336 337 338 339 340
         }
      }
   }

   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);
341 342
}

343 344 345 346
static void Control_FreeCPlItems(HWND hWnd, CPanel *panel)
{
    HMENU hMenu, hSubMenu;
    MENUITEMINFOW mii;
347
    unsigned int i;
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372

    /* 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);
    }
}

373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
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;
}

432 433 434 435 436 437 438 439 440 441 442 443 444 445
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);
    }
}

446
static LRESULT WINAPI	Control_WndProc(HWND hWnd, UINT wMsg,
447 448
					WPARAM lParam1, LPARAM lParam2)
{
449
   CPanel*	panel = (CPanel*)GetWindowLongPtrW(hWnd, 0);
450 451 452 453

   if (panel || wMsg == WM_CREATE) {
      switch (wMsg) {
      case WM_CREATE:
454
	 Control_WndProc_Create(hWnd, (CREATESTRUCTW*)lParam2);
455 456
	 return 0;
      case WM_DESTROY:
457
         {
458 459 460
             CPlApplet *applet, *next;
             LIST_FOR_EACH_ENTRY_SAFE( applet, next, &panel->applets, CPlApplet, entry )
                 Control_UnloadApplet(applet);
461
         }
462
         Control_FreeCPlItems(hWnd, panel);
463
         PostQuitMessage(0);
464
	 break;
465 466 467 468 469 470 471
      case WM_COMMAND:
         switch (LOWORD(lParam1))
         {
             case IDM_CPANEL_EXIT:
                 SendMessageW(hWnd, WM_CLOSE, 0, 0);
                 return 0;

472 473 474
             case IDM_CPANEL_ABOUT:
                 {
                     WCHAR appName[MAX_STRING_LEN];
475 476
                     HICON icon = LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL),
                                             IMAGE_ICON, 48, 48, LR_SHARED);
477 478 479

                     LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName,
                         sizeof(appName) / sizeof(appName[0]));
480
                     ShellAboutW(hWnd, appName, NULL, icon);
481 482 483 484

                     return 0;
                 }

485
             case FCIDM_SHVIEW_BIGICON:
486 487 488
                 Control_UpdateListViewStyle(panel, LVS_ICON, FCIDM_SHVIEW_BIGICON);
                 return 0;

489
             case FCIDM_SHVIEW_SMALLICON:
490 491 492
                 Control_UpdateListViewStyle(panel, LVS_SMALLICON, FCIDM_SHVIEW_SMALLICON);
                 return 0;

493
             case FCIDM_SHVIEW_LISTVIEW:
494 495 496
                 Control_UpdateListViewStyle(panel, LVS_LIST, FCIDM_SHVIEW_LISTVIEW);
                 return 0;

497
             case FCIDM_SHVIEW_REPORTVIEW:
498
                 Control_UpdateListViewStyle(panel, LVS_REPORT, FCIDM_SHVIEW_REPORTVIEW);
499 500 501 502 503 504 505
                 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))
                 {
506
                     Control_StartApplet(hWnd, Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1)));
507 508 509 510 511 512 513
                     return 0;
                 }

                 break;
         }

         break;
514 515 516 517 518 519 520 521 522 523 524 525 526

      case WM_NOTIFY:
      {
          LPNMHDR nmh = (LPNMHDR) lParam2;

          switch (nmh->idFrom)
          {
              case IDC_LISTVIEW:
                  switch (nmh->code)
                  {
                      case NM_RETURN:
                      case NM_DBLCLK:
                      {
527
                          Control_StartApplet(hWnd, Control_GetCPlItem_From_ListView(panel));
528 529
                          return 0;
                      }
530 531 532 533 534 535
                      case LVN_ITEMCHANGED:
                      {
                          CPlItem *item = Control_GetCPlItem_From_ListView(panel);

                          /* update the status bar if item is valid */
                          if (item)
536
                              SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
537 538
                          else
                              SetWindowTextW(panel->hWndStatusBar, NULL);
539 540 541

                          return 0;
                      }
542 543 544 545 546 547 548 549
                  }

                  break;
          }

          break;
      }

550 551 552 553 554 555 556 557 558
      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)
559
                  SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
560
          }
561 562 563 564 565 566
          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)
567
                  SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
568 569 570 571 572
              else
                  SetWindowTextW(panel->hWndStatusBar, NULL);

              return 0;
          }
573 574 575 576 577
          else
              SetWindowTextW(panel->hWndStatusBar, NULL);

          return 0;

578 579
      case WM_SIZE:
      {
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
          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);
599

600 601
          if (hdwp != NULL)
              EndDeferWindowPos(hdwp);
602 603

          return 0;
604
      }
605
     }
606 607
   }

608
   return DefWindowProcW(hWnd, wMsg, lParam1, lParam2);
609 610 611 612
}

static void    Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst)
{
613
    WNDCLASSEXW wc;
614
    MSG		msg;
615
    WCHAR appName[MAX_STRING_LEN];
616 617 618
    const WCHAR className[] = {'S','h','e','l','l','_','C','o','n','t','r','o',
        'l','_','W','n','d','C','l','a','s','s',0};

619 620
    LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, sizeof(appName) / sizeof(appName[0]));

621
    wc.cbSize = sizeof(wc);
622 623 624 625
    wc.style = CS_HREDRAW|CS_VREDRAW;
    wc.lpfnWndProc = Control_WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = sizeof(CPlApplet*);
626
    wc.hInstance = panel->hInst = hInst;
627
    wc.hIcon = LoadIconW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL) );
628
    wc.hCursor = LoadCursorW( 0, (LPWSTR)IDC_ARROW );
629 630
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
631
    wc.lpszClassName = className;
632
    wc.hIconSm = LoadImageW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL), IMAGE_ICON,
633
                             GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
634

635
    if (!RegisterClassExW(&wc)) return;
636

637
    CreateWindowExW(0, wc.lpszClassName, appName,
638 639 640
		    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		    CW_USEDEFAULT, CW_USEDEFAULT,
		    CW_USEDEFAULT, CW_USEDEFAULT,
641
		    hWnd, NULL, hInst, panel);
642
    if (!panel->hWnd) return;
643

644
    while (GetMessageW(&msg, panel->hWnd, 0, 0)) {
645
        TranslateMessage(&msg);
646
        DispatchMessageW(&msg);
647 648 649
    }
}

650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
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;

665
            if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)value, &valueLen) != ERROR_SUCCESS)
666 667 668 669 670 671 672 673
                break;

            Control_LoadApplet(hWnd, value, panel);
        }
        RegCloseKey(hkey);
    }
}

674 675 676
static	void	Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
{
    HANDLE		h;
677 678 679
    WIN32_FIND_DATAW	fd;
    WCHAR		buffer[MAX_PATH];
    static const WCHAR wszAllCpl[] = {'*','.','c','p','l',0};
680 681 682
    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};
683 684
    WCHAR *p;

685
    /* first add .cpl files in the system directory */
686 687 688 689
    GetSystemDirectoryW( buffer, MAX_PATH );
    p = buffer + strlenW(buffer);
    *p++ = '\\';
    lstrcpyW(p, wszAllCpl);
690

691
    if ((h = FindFirstFileW(buffer, &fd)) != INVALID_HANDLE_VALUE) {
692
        do {
693
	   lstrcpyW(p, fd.cFileName);
694
	   Control_LoadApplet(hWnd, buffer, panel);
695
	} while (FindNextFileW(h, &fd));
696 697 698
	FindClose(h);
    }

699 700 701 702
    /* now check for cpls in the registry */
    Control_RegisterRegistryApplets(hWnd, panel, HKEY_LOCAL_MACHINE, wszRegPath);
    Control_RegisterRegistryApplets(hWnd, panel, HKEY_CURRENT_USER, wszRegPath);

703
    Control_DoInterface(panel, hWnd, hInst);
704 705
}

706
static	void	Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
707 708 709 710 711 712
   /* forms to parse:
    *	foo.cpl,@sp,str
    *	foo.cpl,@sp
    *	foo.cpl,,str
    *	foo.cpl @sp
    *	foo.cpl str
713
    *   "a path\foo.cpl"
714 715
    */
{
716 717 718 719 720
    LPWSTR	buffer;
    LPWSTR	beg = NULL;
    LPWSTR	end;
    WCHAR	ch;
    LPWSTR       ptr;
721 722
    signed 	sp = -1;
    LPWSTR	extraPmtsBuf = NULL;
723
    LPWSTR	extraPmts = NULL;
724
    BOOL        quoted = FALSE;
725
    CPlApplet *applet;
726

727
    buffer = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd) + 1) * sizeof(*wszCmd));
728 729
    if (!buffer) return;

730
    end = lstrcpyW(buffer, wszCmd);
731 732

    for (;;) {
733
        ch = *end;
734
        if (ch == '"') quoted = !quoted;
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
        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++;
751
    }
752
    while ((ptr = StrChrW(buffer, '"')))
753
	memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));
754

755 756 757 758
    /* now check for any quotes in extraPmtsBuf and remove */
    if (extraPmtsBuf != NULL)
    {
        beg = end = extraPmtsBuf;
759
        quoted = FALSE;
760 761 762 763 764 765 766 767 768 769 770 771

        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;
772
                beg = end + 1;
773 774 775 776 777 778 779 780 781 782 783 784
                if (ch == ' ') while (end[1] == ' ') end++;
            }
            end++;
        }

        while ((ptr = StrChrW(extraPmts, '"')))
            memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));

        if (extraPmts == NULL)
            extraPmts = extraPmtsBuf;
    }

785
    /* Now check if there had been a numerical value in the extra params */
786
    if ((extraPmts) && (*extraPmts == '@') && (sp == -1)) {
787 788 789
        sp = atoiW(extraPmts + 1);
    }

790
    TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer), debugstr_w(extraPmts), sp);
791

792 793 794
    applet = Control_LoadApplet(hWnd, buffer, panel);
    if (applet)
    {
795 796 797
        /* we've been given a textual parameter (or none at all) */
        if (sp == -1) {
            while ((++sp) != applet->count) {
798
                TRACE("sp %d, name %s\n", sp, debugstr_w(applet->info[sp].name));
799

800 801
                if (StrCmpIW(extraPmts, applet->info[sp].name) == 0)
                    break;
802 803 804 805 806 807 808 809
            }
        }

        if (sp >= applet->count) {
            WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count);
            sp = 0;
        }

810 811
        if (!applet->proc(applet->hWnd, CPL_STARTWPARMSW, sp, (LPARAM)extraPmts))
            applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].data);
812 813

        Control_UnloadApplet(applet);
814
    }
815

816 817 818 819
    HeapFree(GetProcessHeap(), 0, buffer);
}

/*************************************************************************
820
 * Control_RunDLLW			[SHELL32.@]
821 822
 *
 */
823
void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
824 825 826
{
    CPanel	panel;

827
    TRACE("(%p, %p, %s, 0x%08x)\n",
828
	  hWnd, hInst, debugstr_w(cmd), nCmdShow);
829 830

    memset(&panel, 0, sizeof(panel));
831
    list_init( &panel.applets );
832 833 834 835 836 837 838 839

    if (!cmd || !*cmd) {
        Control_DoWindow(&panel, hWnd, hInst);
    } else {
        Control_DoLaunch(&panel, hWnd, cmd);
    }
}

840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
/*************************************************************************
 * 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);
}

855
/*************************************************************************
856
 * Control_FillCache_RunDLLW			[SHELL32.@]
857 858
 *
 */
859
HRESULT WINAPI Control_FillCache_RunDLLW(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
860
{
861
    FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd, hModule, w, x);
862
    return S_OK;
863 864
}

865
/*************************************************************************
866
 * Control_FillCache_RunDLLA			[SHELL32.@]
867 868
 *
 */
869
HRESULT WINAPI Control_FillCache_RunDLLA(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
870
{
871
    return Control_FillCache_RunDLLW(hWnd, hModule, w, x);
872 873
}

874 875 876
/*************************************************************************
 * CallCPLEntry16				[SHELL32.166]
 *
877 878 879 880 881 882
 * 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)
{
883
    FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6);
884 885
    return 0x0deadbee;
}