control.c 28.1 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 "commctrl.h"
35 36 37

#define NO_SHLWAPI_REG
#include "shlwapi.h"
38

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

#define MAX_STRING_LEN      1024
44

45
WINE_DEFAULT_DEBUG_CHANNEL(shlctrl);
46

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

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

54 55
    if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L);
    FreeLibrary(applet->hModule);
56 57 58
    if (applet->context_activated)
        DeactivateActCtx(0, applet->cookie);
    ReleaseActCtx(applet->context);
59
    list_remove( &applet->entry );
60 61
    free(applet->cmd);
    free(applet);
62 63
}

64
CPlApplet*	Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel)
65
{
66
    WCHAR path[MAX_PATH];
67
    CPlApplet*	applet;
68
    DWORD len;
69 70
    unsigned 	i;
    CPLINFO	info;
71
    NEWCPLINFOW newinfo;
72
    ACTCTXW ctx;
73

74
    if (!(applet = calloc(1, sizeof(*applet))))
75 76
       return applet;

77 78
    applet->context = INVALID_HANDLE_VALUE;

79 80 81
    len = ExpandEnvironmentStringsW(cmd, NULL, 0);
    if (len > 0)
    {
82
        if (!(applet->cmd = malloc((len + 1) * sizeof(WCHAR))))
83 84 85 86 87 88 89 90 91
        {
            WARN("Cannot allocate memory for applet path\n");
            goto theError;
        }
        ExpandEnvironmentStringsW(cmd, applet->cmd, len+1);
    }
    else
    {
        WARN("Cannot expand applet path\n");
92 93 94
        goto theError;
    }

95 96
    applet->hWnd = hWnd;

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    /* Activate context before DllMain() is called */
    if (SearchPathW(NULL, applet->cmd, NULL, ARRAY_SIZE(path), path, NULL))
    {
        memset(&ctx, 0, sizeof(ctx));
        ctx.cbSize = sizeof(ctx);
        ctx.lpSource = path;
        ctx.lpResourceName = MAKEINTRESOURCEW(123);
        ctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
        applet->context = CreateActCtxW(&ctx);
        if (applet->context != INVALID_HANDLE_VALUE)
            applet->context_activated = ActivateActCtx(applet->context, &applet->cookie);
        else
            TRACE("No manifest at ID 123 in %s\n", wine_dbgstr_w(path));
    }

112 113
    if (!(applet->hModule = LoadLibraryW(applet->cmd))) {
        WARN("Cannot load control panel applet %s\n", debugstr_w(applet->cmd));
114 115 116
	goto theError;
    }
    if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) {
117
        WARN("Not a valid control panel applet %s\n", debugstr_w(applet->cmd));
118 119 120 121 122 123 124 125
	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");
126
        applet->proc(applet->hWnd, CPL_EXIT, 0, 0);
127 128 129
	goto theError;
    }

130
    applet = _recalloc(applet, 1, FIELD_OFFSET(CPlApplet, info[applet->count]));
131

132
    for (i = 0; i < applet->count; i++) {
133 134
       ZeroMemory(&newinfo, sizeof(newinfo));
       newinfo.dwSize = sizeof(NEWCPLINFOA);
135
       applet->info[i].helpfile[0] = 0;
136 137 138 139 140
       /* 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
	*/
141
       applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info);
142
       applet->info[i].data = info.lData;
143
       if (info.idIcon != CPL_DYNAMIC_RES)
144
	   applet->info[i].icon = LoadIconW(applet->hModule, MAKEINTRESOURCEW(info.idIcon));
145 146
       if (info.idName != CPL_DYNAMIC_RES)
	   LoadStringW(applet->hModule, info.idName,
147
		       applet->info[i].name, ARRAY_SIZE(applet->info[i].name));
148 149
       if (info.idInfo != CPL_DYNAMIC_RES)
	   LoadStringW(applet->hModule, info.idInfo,
150
		       applet->info[i].info, ARRAY_SIZE(applet->info[i].info));
151

152 153 154 155
       /* 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 */

156
       if (!applet->info[i].name[0]) info.idName = CPL_DYNAMIC_RES;
157 158 159 160

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

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

163
       if (applet->info[i].icon == NULL)
164 165
           info.idIcon = CPL_DYNAMIC_RES;

166 167 168 169
       if ((info.idIcon == CPL_DYNAMIC_RES) || (info.idName == CPL_DYNAMIC_RES) ||
           (info.idInfo == CPL_DYNAMIC_RES)) {
	   applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo);

170
	   applet->info[i].data = newinfo.lData;
171 172
	   if (info.idIcon == CPL_DYNAMIC_RES) {
	       if (!newinfo.hIcon) WARN("couldn't get icon for applet %u\n", i);
173
	       applet->info[i].icon = newinfo.hIcon;
174
	   }
175 176
	   if (newinfo.dwSize == sizeof(NEWCPLINFOW)) {
	       if (info.idName == CPL_DYNAMIC_RES)
177
	           memcpy(applet->info[i].name, newinfo.szName, sizeof(newinfo.szName));
178
	       if (info.idInfo == CPL_DYNAMIC_RES)
179 180
	           memcpy(applet->info[i].info, newinfo.szInfo, sizeof(newinfo.szInfo));
	       memcpy(applet->info[i].helpfile, newinfo.szHelpFile, sizeof(newinfo.szHelpFile));
181
	   } else {
182 183
               NEWCPLINFOA *infoA = (NEWCPLINFOA *)&newinfo;

184
	       if (info.idName == CPL_DYNAMIC_RES)
185 186
                   MultiByteToWideChar(CP_ACP, 0, infoA->szName, ARRAY_SIZE(infoA->szName),
                       applet->info[i].name, ARRAY_SIZE(applet->info[i].name));
187
	       if (info.idInfo == CPL_DYNAMIC_RES)
188 189 190 191
                   MultiByteToWideChar(CP_ACP, 0, infoA->szInfo, ARRAY_SIZE(infoA->szInfo),
                       applet->info[i].info, ARRAY_SIZE(applet->info[i].info));
               MultiByteToWideChar(CP_ACP, 0, infoA->szHelpFile, ARRAY_SIZE(infoA->szHelpFile),
                       applet->info[i].helpfile, ARRAY_SIZE(applet->info[i].helpfile));
192 193
           }
       }
194 195
    }

196
    list_add_head( &panel->applets, &applet->entry );
197 198 199
    return applet;

 theError:
200
    FreeLibrary(applet->hModule);
201 202 203
    if (applet->context_activated)
        DeactivateActCtx(0, applet->cookie);
    ReleaseActCtx(applet->context);
204 205
    free(applet->cmd);
    free(applet);
206 207 208
    return NULL;
}

209
#define IDC_LISTVIEW        1000
210
#define IDC_STATUSBAR       1001
211 212 213 214 215 216 217

#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)
{
218
    static const WCHAR empty_string[] = {0};
219
    RECT ws, sb;
220 221 222 223
    WCHAR buf[MAX_STRING_LEN];
    LVCOLUMNW lvc;

    /* Create list view */
224
    GetClientRect(panel->hWndStatusBar, &sb);
225 226 227 228
    GetClientRect(panel->hWnd, &ws);

    panel->hWndListView = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW,
                          empty_string, LISTVIEW_DEFSTYLE | LVS_ICON,
229 230 231
                          0, 0, ws.right - ws.left, ws.bottom - ws.top -
                          (sb.bottom - sb.top), panel->hWnd,
                          (HMENU) IDC_LISTVIEW, panel->hInst, NULL);
232 233 234 235 236 237

    if (!panel->hWndListView)
        return FALSE;

    /* Create image lists for list view */
    panel->hImageListSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
238
        GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 1, 1);
239
    panel->hImageListLarge = ImageList_Create(GetSystemMetrics(SM_CXICON),
240
        GetSystemMetrics(SM_CYICON), ILC_COLOR32 | ILC_MASK, 1, 1);
241

242 243
    SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)panel->hImageListSmall);
    SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM)panel->hImageListLarge);
244 245 246 247 248 249 250 251 252

    /* 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;
253
    LoadStringW(shell32_hInstance, IDS_CPANEL_NAME, buf, ARRAY_SIZE(buf));
254 255 256 257 258 259 260

    if (ListView_InsertColumnW(panel->hWndListView, 0, &lvc) == -1)
        return FALSE;

    /* Description column */
    lvc.iSubItem = 1;
    lvc.cx = ((ws.right - ws.left) / 3) * 2;
261
    LoadStringW(shell32_hInstance, IDS_CPANEL_DESCRIPTION, buf, ARRAY_SIZE(buf));
262 263 264 265 266 267 268

    if (ListView_InsertColumnW(panel->hWndListView, 1, &lvc) == -1)
        return FALSE;

    return(TRUE);
}

269
static void 	 Control_WndProc_Create(HWND hWnd, const CREATESTRUCTW* cs)
270
{
271
   CPanel* panel = cs->lpCreateParams;
272
   HMENU hMenu, hSubMenu;
273
   CPlApplet* applet;
274
   MENUITEMINFOW mii;
275 276
   unsigned int i;
   int menucount, index;
277
   CPlItem *item;
278 279
   LVITEMW lvItem;
   INITCOMMONCONTROLSEX icex;
280
   INT sb_parts;
281
   int itemidx;
282

283
   SetWindowLongPtrW(hWnd, 0, (LONG_PTR)panel);
284
   panel->hWnd = hWnd;
285

286 287
   /* Initialise common control DLL */
   icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
288
   icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
289 290
   InitCommonControlsEx(&icex);

291 292 293 294 295 296 297
   /* 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);

298 299 300 301
   /* create the list view */
   if (!Control_CreateListView(panel))
       return;

302 303 304 305 306 307
   hMenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(MENU_CPANEL));

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

308 309
   LIST_FOR_EACH_ENTRY( applet, &panel->applets, CPlApplet, entry )
   {
310 311
      for (i = 0; i < applet->count; i++) {
         /* set up a CPlItem for this particular subprogram */
312
         item = malloc(sizeof(CPlItem));
313 314 315 316

         if (!item)
            continue;

317
         item->applet = applet;
318 319 320 321
         item->id = i;

         mii.cbSize = sizeof(MENUITEMINFOW);
         mii.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
322
         mii.dwTypeData = applet->info[i].name;
323
         mii.cch = ARRAY_SIZE(applet->info[i].name);
324
         mii.wID = IDM_CPANEL_APPLET_BASE + menucount;
325
         mii.dwItemData = (ULONG_PTR)item;
326 327

         if (InsertMenuItemW(hSubMenu, menucount, TRUE, &mii)) {
328
            /* add the list view item */
329 330 331 332
            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);
333 334 335 336

            lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
            lvItem.iItem = menucount;
            lvItem.iSubItem = 0;
337
            lvItem.pszText = applet->info[i].name;
338 339 340
            lvItem.iImage = index;
            lvItem.lParam = (LPARAM) item;

341
            itemidx = ListView_InsertItemW(panel->hWndListView, &lvItem);
342 343

            /* add the description */
344
            ListView_SetItemTextW(panel->hWndListView, itemidx, 1, applet->info[i].info);
345 346 347 348

            /* update menu bar, increment count */
            DrawMenuBar(hWnd);
            menucount++;
349 350 351 352 353 354 355 356 357 358 359 360
         }
      }
   }

   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);
361 362
}

363 364 365 366
static void Control_FreeCPlItems(HWND hWnd, CPanel *panel)
{
    HMENU hMenu, hSubMenu;
    MENUITEMINFOW mii;
367
    unsigned int i;
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388

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

389
        free((void *)mii.dwItemData);
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 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
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;
}

452 453
static void Control_StartApplet(HWND hWnd, CPlItem *item)
{
454
    WCHAR param[12];
455 456 457 458

    /* execute the applet if item is valid */
    if (item)
    {
459 460
        swprintf(param, ARRAY_SIZE(param), L"@%d", item->id);
        ShellExecuteW(hWnd, L"cplopen", item->applet->cmd, param, NULL, SW_SHOW);
461 462 463
    }
}

464
static LRESULT WINAPI	Control_WndProc(HWND hWnd, UINT wMsg,
465 466
					WPARAM lParam1, LPARAM lParam2)
{
467
   CPanel*	panel = (CPanel*)GetWindowLongPtrW(hWnd, 0);
468 469 470 471

   if (panel || wMsg == WM_CREATE) {
      switch (wMsg) {
      case WM_CREATE:
472
	 Control_WndProc_Create(hWnd, (CREATESTRUCTW*)lParam2);
473 474
	 return 0;
      case WM_DESTROY:
475
         {
476 477 478
             CPlApplet *applet, *next;
             LIST_FOR_EACH_ENTRY_SAFE( applet, next, &panel->applets, CPlApplet, entry )
                 Control_UnloadApplet(applet);
479
         }
480
         Control_FreeCPlItems(hWnd, panel);
481
         PostQuitMessage(0);
482
	 break;
483 484 485 486 487 488 489
      case WM_COMMAND:
         switch (LOWORD(lParam1))
         {
             case IDM_CPANEL_EXIT:
                 SendMessageW(hWnd, WM_CLOSE, 0, 0);
                 return 0;

490 491 492
             case IDM_CPANEL_ABOUT:
                 {
                     WCHAR appName[MAX_STRING_LEN];
493 494
                     HICON icon = LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL),
                                             IMAGE_ICON, 48, 48, LR_SHARED);
495

496
                     LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, ARRAY_SIZE(appName));
497
                     ShellAboutW(hWnd, appName, NULL, icon);
498 499 500 501

                     return 0;
                 }

502
             case FCIDM_SHVIEW_BIGICON:
503 504 505
                 Control_UpdateListViewStyle(panel, LVS_ICON, FCIDM_SHVIEW_BIGICON);
                 return 0;

506
             case FCIDM_SHVIEW_SMALLICON:
507 508 509
                 Control_UpdateListViewStyle(panel, LVS_SMALLICON, FCIDM_SHVIEW_SMALLICON);
                 return 0;

510
             case FCIDM_SHVIEW_LISTVIEW:
511 512 513
                 Control_UpdateListViewStyle(panel, LVS_LIST, FCIDM_SHVIEW_LISTVIEW);
                 return 0;

514
             case FCIDM_SHVIEW_REPORTVIEW:
515
                 Control_UpdateListViewStyle(panel, LVS_REPORT, FCIDM_SHVIEW_REPORTVIEW);
516 517 518 519 520 521 522
                 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))
                 {
523
                     Control_StartApplet(hWnd, Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1)));
524 525 526 527 528 529 530
                     return 0;
                 }

                 break;
         }

         break;
531 532 533 534 535 536 537 538 539 540 541 542 543

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

          switch (nmh->idFrom)
          {
              case IDC_LISTVIEW:
                  switch (nmh->code)
                  {
                      case NM_RETURN:
                      case NM_DBLCLK:
                      {
544
                          Control_StartApplet(hWnd, Control_GetCPlItem_From_ListView(panel));
545 546
                          return 0;
                      }
547 548 549 550 551 552
                      case LVN_ITEMCHANGED:
                      {
                          CPlItem *item = Control_GetCPlItem_From_ListView(panel);

                          /* update the status bar if item is valid */
                          if (item)
553
                              SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
554 555
                          else
                              SetWindowTextW(panel->hWndStatusBar, NULL);
556 557 558

                          return 0;
                      }
559 560 561 562 563 564 565 566
                  }

                  break;
          }

          break;
      }

567 568 569 570 571 572 573 574 575
      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)
576
                  SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
577
          }
578 579 580 581 582 583
          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)
584
                  SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
585 586 587 588 589
              else
                  SetWindowTextW(panel->hWndStatusBar, NULL);

              return 0;
          }
590 591 592 593 594
          else
              SetWindowTextW(panel->hWndStatusBar, NULL);

          return 0;

595 596
      case WM_SIZE:
      {
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
          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);
616

617 618
          if (hdwp != NULL)
              EndDeferWindowPos(hdwp);
619 620

          return 0;
621
      }
622
     }
623 624
   }

625
   return DefWindowProcW(hWnd, wMsg, lParam1, lParam2);
626 627 628 629
}

static void    Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst)
{
630
    WNDCLASSEXW wc;
631
    MSG		msg;
632
    WCHAR appName[MAX_STRING_LEN];
633

634
    LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, ARRAY_SIZE(appName));
635

636
    wc.cbSize = sizeof(wc);
637 638 639 640
    wc.style = CS_HREDRAW|CS_VREDRAW;
    wc.lpfnWndProc = Control_WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = sizeof(CPlApplet*);
641
    wc.hInstance = panel->hInst = hInst;
642
    wc.hIcon = LoadIconW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL) );
643
    wc.hCursor = LoadCursorW( 0, (LPWSTR)IDC_ARROW );
644 645
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
646
    wc.lpszClassName = L"Shell_Control_WndClass";
647
    wc.hIconSm = LoadImageW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL), IMAGE_ICON,
648
                             GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
649

650
    if (!RegisterClassExW(&wc)) return;
651

652
    CreateWindowExW(0, wc.lpszClassName, appName,
653 654 655
		    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		    CW_USEDEFAULT, CW_USEDEFAULT,
		    CW_USEDEFAULT, CW_USEDEFAULT,
656
		    hWnd, NULL, hInst, panel);
657
    if (!panel->hWnd) return;
658

659
    while (GetMessageW(&msg, 0, 0, 0)) {
660
        TranslateMessage(&msg);
661
        DispatchMessageW(&msg);
662 663 664
    }
}

665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
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;

680
            if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)value, &valueLen) != ERROR_SUCCESS)
681 682 683 684 685 686 687 688
                break;

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

689 690 691
static	void	Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
{
    HANDLE		h;
692 693 694 695
    WIN32_FIND_DATAW	fd;
    WCHAR		buffer[MAX_PATH];
    WCHAR *p;

696
    /* first add .cpl files in the system directory */
697
    GetSystemDirectoryW( buffer, MAX_PATH );
698
    p = buffer + lstrlenW(buffer);
699
    *p++ = '\\';
700
    lstrcpyW(p, L"*.cpl");
701

702
    if ((h = FindFirstFileW(buffer, &fd)) != INVALID_HANDLE_VALUE) {
703
        do {
704
	   lstrcpyW(p, fd.cFileName);
705
	   Control_LoadApplet(hWnd, buffer, panel);
706
	} while (FindNextFileW(h, &fd));
707 708 709
	FindClose(h);
    }

710
    /* now check for cpls in the registry */
711 712
    Control_RegisterRegistryApplets(hWnd, panel, HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls");
    Control_RegisterRegistryApplets(hWnd, panel, HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls");
713

714
    Control_DoInterface(panel, hWnd, hInst);
715 716
}

717
static	void	Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
718 719 720 721 722 723
   /* forms to parse:
    *	foo.cpl,@sp,str
    *	foo.cpl,@sp
    *	foo.cpl,,str
    *	foo.cpl @sp
    *	foo.cpl str
724
    *   "a path\foo.cpl"
725 726
    */
{
727 728 729 730 731
    LPWSTR	buffer;
    LPWSTR	beg = NULL;
    LPWSTR	end;
    WCHAR	ch;
    LPWSTR       ptr;
732 733
    signed 	sp = -1;
    LPWSTR	extraPmtsBuf = NULL;
734
    LPWSTR	extraPmts = NULL;
735
    BOOL        quoted = FALSE;
736
    CPlApplet *applet;
737

738
    buffer = malloc((wcslen(wszCmd) + 1) * sizeof(*wszCmd));
739 740
    if (!buffer) return;

741
    end = lstrcpyW(buffer, wszCmd);
742 743

    for (;;) {
744
        ch = *end;
745
        if (ch == '"') quoted = !quoted;
746 747 748 749
        if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) {
            *end = '\0';
            if (beg) {
                if (*beg == '@') {
750
                    sp = wcstol(beg + 1, NULL, 10);
751 752 753 754 755 756 757 758 759 760 761
                } else if (*beg == '\0') {
                    sp = -1;
                } else {
                    extraPmtsBuf = beg;
                }
            }
            if (ch == '\0') break;
            beg = end + 1;
            if (ch == ' ') while (end[1] == ' ') end++;
        }
        end++;
762
    }
763
    while ((ptr = StrChrW(buffer, '"')))
764
	memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));
765

766 767 768 769
    /* now check for any quotes in extraPmtsBuf and remove */
    if (extraPmtsBuf != NULL)
    {
        beg = end = extraPmtsBuf;
770
        quoted = FALSE;
771 772 773 774 775 776 777 778 779 780 781 782

        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;
783
                beg = end + 1;
784 785 786 787 788 789 790 791 792 793 794 795
                if (ch == ' ') while (end[1] == ' ') end++;
            }
            end++;
        }

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

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

796
    /* Now check if there had been a numerical value in the extra params */
797
    if ((extraPmts) && (*extraPmts == '@') && (sp == -1)) {
798
        sp = wcstol(extraPmts + 1, NULL, 10);
799 800
    }

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

803 804 805
    applet = Control_LoadApplet(hWnd, buffer, panel);
    if (applet)
    {
806 807 808
        /* we've been given a textual parameter (or none at all) */
        if (sp == -1) {
            while ((++sp) != applet->count) {
809
                TRACE("sp %d, name %s\n", sp, debugstr_w(applet->info[sp].name));
810

811 812
                if (StrCmpIW(extraPmts, applet->info[sp].name) == 0)
                    break;
813 814 815 816 817 818 819 820
            }
        }

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

821 822
        if (!applet->proc(applet->hWnd, CPL_STARTWPARMSW, sp, (LPARAM)extraPmts))
            applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].data);
823 824

        Control_UnloadApplet(applet);
825
    }
826

827
    free(buffer);
828 829 830
}

/*************************************************************************
831
 * Control_RunDLLW			[SHELL32.@]
832 833
 *
 */
834
void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
835 836 837
{
    CPanel	panel;

838
    TRACE("(%p, %p, %s, 0x%08lx)\n",
839
	  hWnd, hInst, debugstr_w(cmd), nCmdShow);
840 841

    memset(&panel, 0, sizeof(panel));
842
    list_init( &panel.applets );
843 844 845 846 847 848 849 850

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

851 852 853 854 855 856 857
/*************************************************************************
 * 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 );
858
    WCHAR *wszCmd = malloc(len * sizeof(WCHAR));
859 860 861 862
    if (wszCmd && MultiByteToWideChar(CP_ACP, 0, cmd, -1, wszCmd, len ))
    {
        Control_RunDLLW(hWnd, hInst, wszCmd, nCmdShow);
    }
863
    free(wszCmd);
864 865
}

866
/*************************************************************************
867
 * Control_FillCache_RunDLLW			[SHELL32.@]
868 869
 *
 */
870
HRESULT WINAPI Control_FillCache_RunDLLW(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
871
{
872
    FIXME("%p %p 0x%08lx 0x%08lx stub\n", hWnd, hModule, w, x);
873
    return S_OK;
874 875
}

876
/*************************************************************************
877
 * Control_FillCache_RunDLLA			[SHELL32.@]
878 879
 *
 */
880
HRESULT WINAPI Control_FillCache_RunDLLA(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
881
{
882
    return Control_FillCache_RunDLLW(hWnd, hModule, w, x);
883 884
}

885 886 887
/*************************************************************************
 * CallCPLEntry16				[SHELL32.166]
 *
888 889 890 891 892 893
 * 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)
{
894
    FIXME("(%p, %p, %08lx, %08lx, %08lx, %08lx): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6);
895 896
    return 0x0deadbee;
}
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918

/*************************************************************************
 * RunDLL_CallEntry16        [SHELL32.122]
 * Manually relay this function to make Tages Protection v5 happy
 */
void WINAPI RunDLL_CallEntry16( DWORD proc, HWND hwnd, HINSTANCE inst,
                                LPCSTR cmdline, INT cmdshow )
{
    static HMODULE shell16 = NULL;
    static void (WINAPI *pRunDLL_CallEntry16)( DWORD proc, HWND hwnd, HINSTANCE inst,
                                               LPCSTR cmdline, INT cmdshow ) = NULL;

    if (!pRunDLL_CallEntry16)
    {
        if (!shell16 && !(shell16 = LoadLibraryA( "shell.dll16" )))
            return;
        if (!(pRunDLL_CallEntry16 = (void *)GetProcAddress( shell16, "RunDLL_CallEntry16" )))
            return;
    }

    pRunDLL_CallEntry16( proc, hwnd, inst, cmdline, cmdshow );
}