control.c 27.3 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 65 66
{
    CPlApplet*	applet;
    unsigned 	i;
    CPLINFO	info;
67
    NEWCPLINFOW newinfo;
68 69 70 71

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

72 73 74 75 76 77 78
    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);

79 80
    applet->hWnd = hWnd;

81 82
    if (!(applet->hModule = LoadLibraryW(cmd))) {
        WARN("Cannot load control panel applet %s\n", debugstr_w(cmd));
83 84 85
	goto theError;
    }
    if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) {
86
        WARN("Not a valid control panel applet %s\n", debugstr_w(cmd));
87 88 89 90 91 92 93 94
	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");
95
        applet->proc(applet->hWnd, CPL_EXIT, 0, 0);
96 97 98
	goto theError;
    }

99
    applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet,
100
                         FIELD_OFFSET( CPlApplet, info[applet->count] ));
101

102
    for (i = 0; i < applet->count; i++) {
103 104
       ZeroMemory(&newinfo, sizeof(newinfo));
       newinfo.dwSize = sizeof(NEWCPLINFOA);
105
       applet->info[i].helpfile[0] = 0;
106 107 108 109 110
       /* 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
	*/
111
       applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info);
112
       applet->info[i].data = info.lData;
113
       if (info.idIcon != CPL_DYNAMIC_RES)
114
	   applet->info[i].icon = LoadIconW(applet->hModule, MAKEINTRESOURCEW(info.idIcon));
115 116
       if (info.idName != CPL_DYNAMIC_RES)
	   LoadStringW(applet->hModule, info.idName,
117
		       applet->info[i].name, sizeof(applet->info[i].name) / sizeof(WCHAR));
118 119
       if (info.idInfo != CPL_DYNAMIC_RES)
	   LoadStringW(applet->hModule, info.idInfo,
120
		       applet->info[i].info, sizeof(applet->info[i].info) / sizeof(WCHAR));
121

122 123 124 125
       /* 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 */

126
       if (!applet->info[i].name[0]) info.idName = CPL_DYNAMIC_RES;
127 128 129 130

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

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

133
       if (applet->info[i].icon == NULL)
134 135
           info.idIcon = CPL_DYNAMIC_RES;

136 137 138 139
       if ((info.idIcon == CPL_DYNAMIC_RES) || (info.idName == CPL_DYNAMIC_RES) ||
           (info.idInfo == CPL_DYNAMIC_RES)) {
	   applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo);

140
	   applet->info[i].data = newinfo.lData;
141 142
	   if (info.idIcon == CPL_DYNAMIC_RES) {
	       if (!newinfo.hIcon) WARN("couldn't get icon for applet %u\n", i);
143
	       applet->info[i].icon = newinfo.hIcon;
144
	   }
145 146
	   if (newinfo.dwSize == sizeof(NEWCPLINFOW)) {
	       if (info.idName == CPL_DYNAMIC_RES)
147
	           memcpy(applet->info[i].name, newinfo.szName, sizeof(newinfo.szName));
148
	       if (info.idInfo == CPL_DYNAMIC_RES)
149 150
	           memcpy(applet->info[i].info, newinfo.szInfo, sizeof(newinfo.szInfo));
	       memcpy(applet->info[i].helpfile, newinfo.szHelpFile, sizeof(newinfo.szHelpFile));
151 152 153 154
	   } else {
	       if (info.idName == CPL_DYNAMIC_RES)
                   MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szName,
	                               sizeof(((LPNEWCPLINFOA)&newinfo)->szName) / sizeof(CHAR),
155
			               applet->info[i].name, sizeof(applet->info[i].name) / sizeof(WCHAR));
156 157 158
	       if (info.idInfo == CPL_DYNAMIC_RES)
                   MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szInfo,
	                               sizeof(((LPNEWCPLINFOA)&newinfo)->szInfo) / sizeof(CHAR),
159
			               applet->info[i].info, sizeof(applet->info[i].info) / sizeof(WCHAR));
160 161
               MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szHelpFile,
	                           sizeof(((LPNEWCPLINFOA)&newinfo)->szHelpFile) / sizeof(CHAR),
162 163
			           applet->info[i].helpfile,
                                   sizeof(applet->info[i].helpfile) / sizeof(WCHAR));
164 165
           }
       }
166 167
    }

168
    list_add_head( &panel->applets, &applet->entry );
169 170 171
    return applet;

 theError:
172 173 174
    FreeLibrary(applet->hModule);
    HeapFree(GetProcessHeap(), 0, applet->cmd);
    HeapFree(GetProcessHeap(), 0, applet);
175 176 177
    return NULL;
}

178
#define IDC_LISTVIEW        1000
179
#define IDC_STATUSBAR       1001
180 181 182 183 184 185 186

#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)
{
187
    RECT ws, sb;
188 189 190 191 192
    WCHAR empty_string[] = {0};
    WCHAR buf[MAX_STRING_LEN];
    LVCOLUMNW lvc;

    /* Create list view */
193
    GetClientRect(panel->hWndStatusBar, &sb);
194 195 196 197
    GetClientRect(panel->hWnd, &ws);

    panel->hWndListView = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW,
                          empty_string, LISTVIEW_DEFSTYLE | LVS_ICON,
198 199 200
                          0, 0, ws.right - ws.left, ws.bottom - ws.top -
                          (sb.bottom - sb.top), panel->hWnd,
                          (HMENU) IDC_LISTVIEW, panel->hInst, NULL);
201 202 203 204 205 206

    if (!panel->hWndListView)
        return FALSE;

    /* Create image lists for list view */
    panel->hImageListSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
207
        GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 1, 1);
208
    panel->hImageListLarge = ImageList_Create(GetSystemMetrics(SM_CXICON),
209
        GetSystemMetrics(SM_CYICON), ILC_COLOR32 | ILC_MASK, 1, 1);
210

211 212
    SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)panel->hImageListSmall);
    SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM)panel->hImageListLarge);
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238

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

239
static void 	 Control_WndProc_Create(HWND hWnd, const CREATESTRUCTW* cs)
240
{
241
   CPanel* panel = cs->lpCreateParams;
242
   HMENU hMenu, hSubMenu;
243
   CPlApplet* applet;
244
   MENUITEMINFOW mii;
245 246
   unsigned int i;
   int menucount, index;
247
   CPlItem *item;
248 249
   LVITEMW lvItem;
   INITCOMMONCONTROLSEX icex;
250
   INT sb_parts;
251
   int itemidx;
252

253
   SetWindowLongPtrW(hWnd, 0, (LONG_PTR)panel);
254
   panel->hWnd = hWnd;
255

256 257
   /* Initialise common control DLL */
   icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
258
   icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
259 260
   InitCommonControlsEx(&icex);

261 262 263 264 265 266 267
   /* 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);

268 269 270 271
   /* create the list view */
   if (!Control_CreateListView(panel))
       return;

272 273 274 275 276 277
   hMenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(MENU_CPANEL));

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

278 279
   LIST_FOR_EACH_ENTRY( applet, &panel->applets, CPlApplet, entry )
   {
280 281 282 283 284 285 286
      for (i = 0; i < applet->count; i++) {
         /* set up a CPlItem for this particular subprogram */
         item = HeapAlloc(GetProcessHeap(), 0, sizeof(CPlItem));

         if (!item)
            continue;

287
         item->applet = applet;
288 289 290 291
         item->id = i;

         mii.cbSize = sizeof(MENUITEMINFOW);
         mii.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
292 293
         mii.dwTypeData = applet->info[i].name;
         mii.cch = sizeof(applet->info[i].name) / sizeof(WCHAR);
294
         mii.wID = IDM_CPANEL_APPLET_BASE + menucount;
295
         mii.dwItemData = (ULONG_PTR)item;
296 297

         if (InsertMenuItemW(hSubMenu, menucount, TRUE, &mii)) {
298
            /* add the list view item */
299 300 301 302
            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);
303 304 305 306

            lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
            lvItem.iItem = menucount;
            lvItem.iSubItem = 0;
307
            lvItem.pszText = applet->info[i].name;
308 309 310
            lvItem.iImage = index;
            lvItem.lParam = (LPARAM) item;

311
            itemidx = ListView_InsertItemW(panel->hWndListView, &lvItem);
312 313

            /* add the description */
314
            ListView_SetItemTextW(panel->hWndListView, itemidx, 1, applet->info[i].info);
315 316 317 318

            /* update menu bar, increment count */
            DrawMenuBar(hWnd);
            menucount++;
319 320 321 322 323 324 325 326 327 328 329 330
         }
      }
   }

   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);
331 332
}

333 334 335 336
static void Control_FreeCPlItems(HWND hWnd, CPanel *panel)
{
    HMENU hMenu, hSubMenu;
    MENUITEMINFOW mii;
337
    unsigned int i;
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362

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

363 364 365 366 367 368 369 370 371 372 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
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;
}

422 423 424 425 426 427 428 429 430 431 432 433 434 435
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);
    }
}

436
static LRESULT WINAPI	Control_WndProc(HWND hWnd, UINT wMsg,
437 438
					WPARAM lParam1, LPARAM lParam2)
{
439
   CPanel*	panel = (CPanel*)GetWindowLongPtrW(hWnd, 0);
440 441 442 443

   if (panel || wMsg == WM_CREATE) {
      switch (wMsg) {
      case WM_CREATE:
444
	 Control_WndProc_Create(hWnd, (CREATESTRUCTW*)lParam2);
445 446
	 return 0;
      case WM_DESTROY:
447
         {
448 449 450
             CPlApplet *applet, *next;
             LIST_FOR_EACH_ENTRY_SAFE( applet, next, &panel->applets, CPlApplet, entry )
                 Control_UnloadApplet(applet);
451
         }
452
         Control_FreeCPlItems(hWnd, panel);
453
         PostQuitMessage(0);
454
	 break;
455 456 457 458 459 460 461
      case WM_COMMAND:
         switch (LOWORD(lParam1))
         {
             case IDM_CPANEL_EXIT:
                 SendMessageW(hWnd, WM_CLOSE, 0, 0);
                 return 0;

462 463 464
             case IDM_CPANEL_ABOUT:
                 {
                     WCHAR appName[MAX_STRING_LEN];
465 466
                     HICON icon = LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL),
                                             IMAGE_ICON, 48, 48, LR_SHARED);
467 468 469

                     LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName,
                         sizeof(appName) / sizeof(appName[0]));
470
                     ShellAboutW(hWnd, appName, NULL, icon);
471 472 473 474

                     return 0;
                 }

475
             case FCIDM_SHVIEW_BIGICON:
476 477 478
                 Control_UpdateListViewStyle(panel, LVS_ICON, FCIDM_SHVIEW_BIGICON);
                 return 0;

479
             case FCIDM_SHVIEW_SMALLICON:
480 481 482
                 Control_UpdateListViewStyle(panel, LVS_SMALLICON, FCIDM_SHVIEW_SMALLICON);
                 return 0;

483
             case FCIDM_SHVIEW_LISTVIEW:
484 485 486
                 Control_UpdateListViewStyle(panel, LVS_LIST, FCIDM_SHVIEW_LISTVIEW);
                 return 0;

487
             case FCIDM_SHVIEW_REPORTVIEW:
488
                 Control_UpdateListViewStyle(panel, LVS_REPORT, FCIDM_SHVIEW_REPORTVIEW);
489 490 491 492 493 494 495
                 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))
                 {
496
                     Control_StartApplet(hWnd, Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1)));
497 498 499 500 501 502 503
                     return 0;
                 }

                 break;
         }

         break;
504 505 506 507 508 509 510 511 512 513 514 515 516

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

          switch (nmh->idFrom)
          {
              case IDC_LISTVIEW:
                  switch (nmh->code)
                  {
                      case NM_RETURN:
                      case NM_DBLCLK:
                      {
517
                          Control_StartApplet(hWnd, Control_GetCPlItem_From_ListView(panel));
518 519
                          return 0;
                      }
520 521 522 523 524 525
                      case LVN_ITEMCHANGED:
                      {
                          CPlItem *item = Control_GetCPlItem_From_ListView(panel);

                          /* update the status bar if item is valid */
                          if (item)
526
                              SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
527 528
                          else
                              SetWindowTextW(panel->hWndStatusBar, NULL);
529 530 531

                          return 0;
                      }
532 533 534 535 536 537 538 539
                  }

                  break;
          }

          break;
      }

540 541 542 543 544 545 546 547 548
      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)
549
                  SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
550
          }
551 552 553 554 555 556
          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)
557
                  SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
558 559 560 561 562
              else
                  SetWindowTextW(panel->hWndStatusBar, NULL);

              return 0;
          }
563 564 565 566 567
          else
              SetWindowTextW(panel->hWndStatusBar, NULL);

          return 0;

568 569
      case WM_SIZE:
      {
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
          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);
589

590 591
          if (hdwp != NULL)
              EndDeferWindowPos(hdwp);
592 593

          return 0;
594
      }
595
     }
596 597
   }

598
   return DefWindowProcW(hWnd, wMsg, lParam1, lParam2);
599 600 601 602
}

static void    Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst)
{
603
    WNDCLASSEXW wc;
604
    MSG		msg;
605
    WCHAR appName[MAX_STRING_LEN];
606 607 608
    const WCHAR className[] = {'S','h','e','l','l','_','C','o','n','t','r','o',
        'l','_','W','n','d','C','l','a','s','s',0};

609 610
    LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, sizeof(appName) / sizeof(appName[0]));

611
    wc.cbSize = sizeof(wc);
612 613 614 615
    wc.style = CS_HREDRAW|CS_VREDRAW;
    wc.lpfnWndProc = Control_WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = sizeof(CPlApplet*);
616
    wc.hInstance = panel->hInst = hInst;
617
    wc.hIcon = LoadIconW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL) );
618
    wc.hCursor = LoadCursorW( 0, (LPWSTR)IDC_ARROW );
619 620
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
621
    wc.lpszClassName = className;
622
    wc.hIconSm = LoadImageW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL), IMAGE_ICON,
623
                             GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
624

625
    if (!RegisterClassExW(&wc)) return;
626

627
    CreateWindowExW(0, wc.lpszClassName, appName,
628 629 630
		    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		    CW_USEDEFAULT, CW_USEDEFAULT,
		    CW_USEDEFAULT, CW_USEDEFAULT,
631
		    hWnd, NULL, hInst, panel);
632
    if (!panel->hWnd) return;
633

634
    while (GetMessageW(&msg, panel->hWnd, 0, 0)) {
635
        TranslateMessage(&msg);
636
        DispatchMessageW(&msg);
637 638 639
    }
}

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
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;

655
            if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)value, &valueLen) != ERROR_SUCCESS)
656 657 658 659 660 661 662 663
                break;

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

664 665 666
static	void	Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
{
    HANDLE		h;
667 668 669
    WIN32_FIND_DATAW	fd;
    WCHAR		buffer[MAX_PATH];
    static const WCHAR wszAllCpl[] = {'*','.','c','p','l',0};
670 671 672
    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};
673 674
    WCHAR *p;

675
    /* first add .cpl files in the system directory */
676 677 678 679
    GetSystemDirectoryW( buffer, MAX_PATH );
    p = buffer + strlenW(buffer);
    *p++ = '\\';
    lstrcpyW(p, wszAllCpl);
680

681
    if ((h = FindFirstFileW(buffer, &fd)) != INVALID_HANDLE_VALUE) {
682
        do {
683
	   lstrcpyW(p, fd.cFileName);
684
	   Control_LoadApplet(hWnd, buffer, panel);
685
	} while (FindNextFileW(h, &fd));
686 687 688
	FindClose(h);
    }

689 690 691 692
    /* now check for cpls in the registry */
    Control_RegisterRegistryApplets(hWnd, panel, HKEY_LOCAL_MACHINE, wszRegPath);
    Control_RegisterRegistryApplets(hWnd, panel, HKEY_CURRENT_USER, wszRegPath);

693
    Control_DoInterface(panel, hWnd, hInst);
694 695
}

696
static	void	Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
697 698 699 700 701 702
   /* forms to parse:
    *	foo.cpl,@sp,str
    *	foo.cpl,@sp
    *	foo.cpl,,str
    *	foo.cpl @sp
    *	foo.cpl str
703
    *   "a path\foo.cpl"
704 705
    */
{
706 707 708 709 710
    LPWSTR	buffer;
    LPWSTR	beg = NULL;
    LPWSTR	end;
    WCHAR	ch;
    LPWSTR       ptr;
711 712
    signed 	sp = -1;
    LPWSTR	extraPmtsBuf = NULL;
713
    LPWSTR	extraPmts = NULL;
714
    int        quoted = 0;
715
    CPlApplet *applet;
716

717
    buffer = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd) + 1) * sizeof(*wszCmd));
718 719
    if (!buffer) return;

720
    end = lstrcpyW(buffer, wszCmd);
721 722

    for (;;) {
723
        ch = *end;
724
        if (ch == '"') quoted = !quoted;
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
        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++;
741
    }
742
    while ((ptr = StrChrW(buffer, '"')))
743
	memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));
744

745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
    /* 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;
    }

775
    /* Now check if there had been a numerical value in the extra params */
776
    if ((extraPmts) && (*extraPmts == '@') && (sp == -1)) {
777 778 779
        sp = atoiW(extraPmts + 1);
    }

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

782 783 784
    applet = Control_LoadApplet(hWnd, buffer, panel);
    if (applet)
    {
785 786 787
        /* we've been given a textual parameter (or none at all) */
        if (sp == -1) {
            while ((++sp) != applet->count) {
788
                TRACE("sp %d, name %s\n", sp, debugstr_w(applet->info[sp].name));
789

790 791
                if (StrCmpIW(extraPmts, applet->info[sp].name) == 0)
                    break;
792 793 794 795 796 797 798 799
            }
        }

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

800 801
        if (!applet->proc(applet->hWnd, CPL_STARTWPARMSW, sp, (LPARAM)extraPmts))
            applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].data);
802 803

        Control_UnloadApplet(applet);
804
    }
805

806 807 808 809
    HeapFree(GetProcessHeap(), 0, buffer);
}

/*************************************************************************
810
 * Control_RunDLLW			[SHELL32.@]
811 812
 *
 */
813
void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
814 815 816
{
    CPanel	panel;

817
    TRACE("(%p, %p, %s, 0x%08x)\n",
818
	  hWnd, hInst, debugstr_w(cmd), nCmdShow);
819 820

    memset(&panel, 0, sizeof(panel));
821
    list_init( &panel.applets );
822 823 824 825 826 827 828 829

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

830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
/*************************************************************************
 * 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);
}

845
/*************************************************************************
846
 * Control_FillCache_RunDLLW			[SHELL32.@]
847 848
 *
 */
849
HRESULT WINAPI Control_FillCache_RunDLLW(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
850
{
851
    FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd, hModule, w, x);
852
    return S_OK;
853 854
}

855
/*************************************************************************
856
 * Control_FillCache_RunDLLA			[SHELL32.@]
857 858
 *
 */
859
HRESULT WINAPI Control_FillCache_RunDLLA(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
860
{
861
    return Control_FillCache_RunDLLW(hWnd, hModule, w, x);
862 863
}

864 865 866
/*************************************************************************
 * CallCPLEntry16				[SHELL32.166]
 *
867 868 869 870 871 872
 * 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)
{
873
    FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6);
874 875
    return 0x0deadbee;
}