shv_item_cmenu.c 13.8 KB
Newer Older
1 2 3
/*
 *	IContextMenu for items in the shellview
 *
4 5 6 7 8 9 10 11 12 13 14 15 16 17
 * Copyright 1998, 2000 Juergen Schmied <juergen.schmied@debitel.net>
 *
 * 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 22
#include <string.h>

23
#define COBJMACROS
24 25
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
26

27
#include "winerror.h"
28
#include "wine/debug.h"
29

30 31
#include "windef.h"
#include "wingdi.h"
32
#include "pidl.h"
33
#include "undocshell.h"
34
#include "shlobj.h"
35 36 37 38

#include "shell32_main.h"
#include "shellfolder.h"

39
WINE_DEFAULT_DEBUG_CHANNEL(shell);
40 41 42 43

/**************************************************************************
*  IContextMenu Implementation
*/
44
typedef struct
45
{	const IContextMenu2Vtbl *lpVtbl;
Mike McCormack's avatar
Mike McCormack committed
46
	LONG		ref;
47 48 49 50 51 52 53 54
	IShellFolder*	pSFParent;
	LPITEMIDLIST	pidl;		/* root pidl */
	LPITEMIDLIST	*apidl;		/* array of child pidls */
	UINT		cidl;
	BOOL		bAllValues;
} ItemCmImpl;


55
static const IContextMenu2Vtbl cmvt;
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

/**************************************************************************
* ISvItemCm_CanRenameItems()
*/
static BOOL ISvItemCm_CanRenameItems(ItemCmImpl *This)
{	UINT  i;
	DWORD dwAttributes;

	TRACE("(%p)->()\n",This);

	if(This->apidl)
	{
	  for(i = 0; i < This->cidl; i++){}
	  if(i > 1) return FALSE;		/* can't rename more than one item at a time*/
	  dwAttributes = SFGAO_CANRENAME;
71
	  IShellFolder_GetAttributesOf(This->pSFParent, 1, (LPCITEMIDLIST*)This->apidl, &dwAttributes);
72 73 74 75 76 77 78 79
	  return dwAttributes & SFGAO_CANRENAME;
	}
	return FALSE;
}

/**************************************************************************
*   ISvItemCm_Constructor()
*/
80
IContextMenu2 *ISvItemCm_Constructor(LPSHELLFOLDER pSFParent, LPCITEMIDLIST pidl, const LPCITEMIDLIST *apidl, UINT cidl)
81 82 83
{	ItemCmImpl* cm;
	UINT  u;

84
	cm = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ItemCmImpl));
85
	cm->lpVtbl = &cmvt;
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
	cm->ref = 1;
	cm->pidl = ILClone(pidl);
	cm->pSFParent = pSFParent;

	if(pSFParent) IShellFolder_AddRef(pSFParent);

	cm->apidl = _ILCopyaPidl(apidl, cidl);
	cm->cidl = cidl;

	cm->bAllValues = 1;
	for(u = 0; u < cidl; u++)
	{
	  cm->bAllValues &= (_ILIsValue(apidl[u]) ? 1 : 0);
	}

	TRACE("(%p)->()\n",cm);

103
	return (IContextMenu2*)cm;
104 105 106 107 108
}

/**************************************************************************
*  ISvItemCm_fnQueryInterface
*/
109
static HRESULT WINAPI ISvItemCm_fnQueryInterface(IContextMenu2 *iface, REFIID riid, LPVOID *ppvObj)
110
{
111
	ItemCmImpl *This = (ItemCmImpl *)iface;
112 113 114 115 116

	TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);

	*ppvObj = NULL;

117 118 119
        if(IsEqualIID(riid, &IID_IUnknown) ||
           IsEqualIID(riid, &IID_IContextMenu) ||
           IsEqualIID(riid, &IID_IContextMenu2))
120 121
	{
	  *ppvObj = This;
122
	}
123 124 125 126 127 128
	else if(IsEqualIID(riid, &IID_IShellExtInit))  /*IShellExtInit*/
	{
	  FIXME("-- LPSHELLEXTINIT pointer requested\n");
	}

	if(*ppvObj)
129 130
	{
	  IUnknown_AddRef((IUnknown*)*ppvObj);
131 132 133 134 135 136 137 138 139 140
	  TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
	  return S_OK;
	}
	TRACE("-- Interface: E_NOINTERFACE\n");
	return E_NOINTERFACE;
}

/**************************************************************************
*  ISvItemCm_fnAddRef
*/
141
static ULONG WINAPI ISvItemCm_fnAddRef(IContextMenu2 *iface)
142
{
143
	ItemCmImpl *This = (ItemCmImpl *)iface;
144
	ULONG refCount = InterlockedIncrement(&This->ref);
145

146
	TRACE("(%p)->(count=%u)\n", This, refCount - 1);
147

148
	return refCount;
149 150 151 152 153
}

/**************************************************************************
*  ISvItemCm_fnRelease
*/
154
static ULONG WINAPI ISvItemCm_fnRelease(IContextMenu2 *iface)
155
{
156
	ItemCmImpl *This = (ItemCmImpl *)iface;
157
	ULONG refCount = InterlockedDecrement(&This->ref);
158

159
	TRACE("(%p)->(count=%i)\n", This, refCount + 1);
160

161
	if (!refCount)
162 163 164 165 166 167
	{
	  TRACE(" destroying IContextMenu(%p)\n",This);

	  if(This->pSFParent)
	    IShellFolder_Release(This->pSFParent);

168
	  SHFree(This->pidl);
169

170 171 172 173 174
	  /*make sure the pidl is freed*/
	  _ILFreeaPidl(This->apidl, This->cidl);

	  HeapFree(GetProcessHeap(),0,This);
	}
175
	return refCount;
176 177 178 179
}

/**************************************************************************
*  ICM_InsertItem()
180
*/
181 182 183 184 185 186
void WINAPI _InsertMenuItem (
	HMENU hmenu,
	UINT indexMenu,
	BOOL fByPosition,
	UINT wID,
	UINT fType,
187
	LPCSTR dwTypeData,
188 189 190 191 192 193 194 195 196 197 198 199 200
	UINT fState)
{
	MENUITEMINFOA	mii;

	ZeroMemory(&mii, sizeof(mii));
	mii.cbSize = sizeof(mii);
	if (fType == MFT_SEPARATOR)
	{
	  mii.fMask = MIIM_ID | MIIM_TYPE;
	}
	else
	{
	  mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
201
	  mii.dwTypeData = (LPSTR) dwTypeData;
202 203 204 205 206 207
	  mii.fState = fState;
	}
	mii.wID = wID;
	mii.fType = fType;
	InsertMenuItemA( hmenu, indexMenu, fByPosition, &mii);
}
208

209 210
/**************************************************************************
* ISvItemCm_fnQueryContextMenu()
211 212
* FIXME: load menu MENU_SHV_FILE out of resources instead if creating
*		 each menu item by calling _InsertMenuItem()
213 214
*/
static HRESULT WINAPI ISvItemCm_fnQueryContextMenu(
215
	IContextMenu2 *iface,
216 217 218 219 220 221
	HMENU hmenu,
	UINT indexMenu,
	UINT idCmdFirst,
	UINT idCmdLast,
	UINT uFlags)
{
222
	ItemCmImpl *This = (ItemCmImpl *)iface;
223

224
	TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n",This, hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
225

226 227 228 229
	if (idCmdFirst != 0)
	  FIXME("We should use idCmdFirst=%d and idCmdLast=%d for command ids\n", idCmdFirst, idCmdLast);

	if(!(CMF_DEFAULTONLY & uFlags) && This->cidl>0)
230
	{
231 232 233 234
	  if(!(uFlags & CMF_EXPLORE))
	    _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Select", MFS_ENABLED);

	  if(This->bAllValues)
235
	  {
236 237
	    _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Open", MFS_ENABLED);
	    _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_EXPLORE, MFT_STRING, "&Explore", MFS_ENABLED);
238 239 240
	  }
	  else
	  {
241 242
	    _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_EXPLORE, MFT_STRING, "&Explore", MFS_ENABLED);
	    _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Open", MFS_ENABLED);
243
	  }
244 245 246

	  SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
	  _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
	  _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, "&Copy", MFS_ENABLED);
	  _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_CUT, MFT_STRING, "&Cut", MFS_ENABLED);

	  _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
	  _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_DELETE, MFT_STRING, "&Delete", MFS_ENABLED);

	  if(uFlags & CMF_CANRENAME)
	    _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, "&Rename", ISvItemCm_CanRenameItems(This) ? MFS_ENABLED : MFS_DISABLED);

	  return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (FCIDM_SHVIEWLAST));
	}
	return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
}

/**************************************************************************
* DoOpenExplore
*
*  for folders only
*/

static void DoOpenExplore(
269
	IContextMenu2 *iface,
270 271 272
	HWND hwnd,
	LPCSTR verb)
{
273
	ItemCmImpl *This = (ItemCmImpl *)iface;
274

275
	UINT i, bFolderFound = FALSE;
276 277 278
	LPITEMIDLIST	pidlFQ;
	SHELLEXECUTEINFOA	sei;

279
	/* Find the first item in the list that is not a value. These commands
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
	    should never be invoked if there isn't at least one folder item in the list.*/

	for(i = 0; i<This->cidl; i++)
	{
	  if(!_ILIsValue(This->apidl[i]))
	  {
	    bFolderFound = TRUE;
	    break;
	  }
	}

	if (!bFolderFound) return;

	pidlFQ = ILCombine(This->pidl, This->apidl[i]);

	ZeroMemory(&sei, sizeof(sei));
	sei.cbSize = sizeof(sei);
	sei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME;
	sei.lpIDList = pidlFQ;
299
	sei.lpClass = "Folder";
300 301 302 303 304 305 306 307 308 309 310
	sei.hwnd = hwnd;
	sei.nShow = SW_SHOWNORMAL;
	sei.lpVerb = verb;
	ShellExecuteExA(&sei);
	SHFree(pidlFQ);
}

/**************************************************************************
* DoRename
*/
static void DoRename(
311
	IContextMenu2 *iface,
312 313
	HWND hwnd)
{
314
	ItemCmImpl *This = (ItemCmImpl *)iface;
315 316 317 318

	LPSHELLBROWSER	lpSB;
	LPSHELLVIEW	lpSV;

319
	TRACE("(%p)->(wnd=%p)\n",This, hwnd);
320 321 322 323 324 325

	/* get the active IShellView */
	if ((lpSB = (LPSHELLBROWSER)SendMessageA(hwnd, CWM_GETISHELLBROWSER,0,0)))
	{
	  if(SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
	  {
326
	    TRACE("(sv=%p)\n",lpSV);
327 328
	    IShellView_SelectItem(lpSV, This->apidl[0],
              SVSI_DESELECTOTHERS|SVSI_EDIT|SVSI_ENSUREVISIBLE|SVSI_FOCUSED|SVSI_SELECT);
329 330 331 332 333 334 335 336 337 338
	    IShellView_Release(lpSV);
	  }
	}
}

/**************************************************************************
 * DoDelete
 *
 * deletes the currently selected items
 */
339
static void DoDelete(IContextMenu2 *iface)
340
{
341
	ItemCmImpl *This = (ItemCmImpl *)iface;
342
	ISFHelper * psfhlp;
343

344 345 346
	IShellFolder_QueryInterface(This->pSFParent, &IID_ISFHelper, (LPVOID*)&psfhlp);
	if (psfhlp)
	{
347
	  ISFHelper_DeleteItems(psfhlp, This->cidl, (LPCITEMIDLIST *)This->apidl);
348 349 350 351 352 353 354
	  ISFHelper_Release(psfhlp);
	}
}

/**************************************************************************
 * DoCopyOrCut
 *
355
 * copies the currently selected items into the clipboard
356 357
 */
static BOOL DoCopyOrCut(
358
	IContextMenu2 *iface,
359 360 361
	HWND hwnd,
	BOOL bCut)
{
362
	ItemCmImpl *This = (ItemCmImpl *)iface;
363 364 365 366

	LPSHELLBROWSER	lpSB;
	LPSHELLVIEW	lpSV;
	LPDATAOBJECT    lpDo;
367

368
	TRACE("(%p)->(wnd=%p,bCut=0x%08x)\n",This, hwnd, bCut);
369

370 371
	/* get the active IShellView */
	if ((lpSB = (LPSHELLBROWSER)SendMessageA(hwnd, CWM_GETISHELLBROWSER,0,0)))
372
	{
373
	  if (SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
374
	  {
375
	    if (SUCCEEDED(IShellView_GetItemObject(lpSV, SVGIO_SELECTION, &IID_IDataObject, (LPVOID*)&lpDo)))
376
	    {
377 378
	      OleSetClipboard(lpDo);
	      IDataObject_Release(lpDo);
379
	    }
380
	    IShellView_Release(lpSV);
381 382 383 384 385 386 387 388
	  }
	}
	return TRUE;
}
/**************************************************************************
* ISvItemCm_fnInvokeCommand()
*/
static HRESULT WINAPI ISvItemCm_fnInvokeCommand(
389
	IContextMenu2 *iface,
390 391
	LPCMINVOKECOMMANDINFO lpcmi)
{
392
    ItemCmImpl *This = (ItemCmImpl *)iface;
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

    if (lpcmi->cbSize != sizeof(CMINVOKECOMMANDINFO))
        FIXME("Is an EX structure\n");

    TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n",This,lpcmi,lpcmi->lpVerb, lpcmi->hwnd);

    if( HIWORD(lpcmi->lpVerb)==0 && LOWORD(lpcmi->lpVerb) > FCIDM_SHVIEWLAST)
    {
        TRACE("Invalid Verb %x\n",LOWORD(lpcmi->lpVerb));
        return E_INVALIDARG;
    }

    if (HIWORD(lpcmi->lpVerb) == 0)
    {
        switch(LOWORD(lpcmi->lpVerb))
        {
        case FCIDM_SHVIEW_EXPLORE:
            TRACE("Verb FCIDM_SHVIEW_EXPLORE\n");
            DoOpenExplore(iface, lpcmi->hwnd, "explore");
            break;
        case FCIDM_SHVIEW_OPEN:
            TRACE("Verb FCIDM_SHVIEW_OPEN\n");
            DoOpenExplore(iface, lpcmi->hwnd, "open");
            break;
        case FCIDM_SHVIEW_RENAME:
            TRACE("Verb FCIDM_SHVIEW_RENAME\n");
            DoRename(iface, lpcmi->hwnd);
            break;
        case FCIDM_SHVIEW_DELETE:
            TRACE("Verb FCIDM_SHVIEW_DELETE\n");
            DoDelete(iface);
            break;
        case FCIDM_SHVIEW_COPY:
            TRACE("Verb FCIDM_SHVIEW_COPY\n");
            DoCopyOrCut(iface, lpcmi->hwnd, FALSE);
            break;
        case FCIDM_SHVIEW_CUT:
            TRACE("Verb FCIDM_SHVIEW_CUT\n");
            DoCopyOrCut(iface, lpcmi->hwnd, TRUE);
            break;
        default:
            FIXME("Unhandled Verb %xl\n",LOWORD(lpcmi->lpVerb));
        }
    }
    else
    {
        TRACE("Verb is %s\n",debugstr_a(lpcmi->lpVerb));
        if (strcmp(lpcmi->lpVerb,"delete")==0)
            DoDelete(iface);
        else
            FIXME("Unhandled string verb %s\n",debugstr_a(lpcmi->lpVerb));
    }
    return NOERROR;
446 447 448 449 450 451
}

/**************************************************************************
*  ISvItemCm_fnGetCommandString()
*/
static HRESULT WINAPI ISvItemCm_fnGetCommandString(
452
	IContextMenu2 *iface,
Kevin Koltzau's avatar
Kevin Koltzau committed
453
	UINT_PTR idCommand,
454
	UINT uFlags,
455
	UINT* lpReserved,
456 457
	LPSTR lpszName,
	UINT uMaxNameLen)
458
{
459
	ItemCmImpl *This = (ItemCmImpl *)iface;
460 461 462

	HRESULT  hr = E_INVALIDARG;

463
	TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n",This, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
464 465 466

	switch(uFlags)
	{
467 468
	  case GCS_HELPTEXTA:
	  case GCS_HELPTEXTW:
469 470 471 472 473 474 475 476 477 478 479 480 481
	    hr = E_NOTIMPL;
	    break;

	  case GCS_VERBA:
	    switch(idCommand)
	    {
	      case FCIDM_SHVIEW_RENAME:
	        strcpy((LPSTR)lpszName, "rename");
	        hr = NOERROR;
	        break;
	    }
	    break;

482
	     /* NT 4.0 with IE 3.0x or no IE will always call This with GCS_VERBW. In This
483 484 485 486
	     case, you need to do the lstrcpyW to the pointer passed.*/
	  case GCS_VERBW:
	    switch(idCommand)
	    { case FCIDM_SHVIEW_RENAME:
487
                MultiByteToWideChar( CP_ACP, 0, "rename", -1, (LPWSTR)lpszName, uMaxNameLen );
488 489 490 491 492
	        hr = NOERROR;
	        break;
	    }
	    break;

493 494
	  case GCS_VALIDATEA:
	  case GCS_VALIDATEW:
495 496 497 498 499 500 501 502 503 504 505 506 507 508
	    hr = NOERROR;
	    break;
	}
	TRACE("-- (%p)->(name=%s)\n",This, lpszName);
	return hr;
}

/**************************************************************************
* ISvItemCm_fnHandleMenuMsg()
* NOTES
*  should be only in IContextMenu2 and IContextMenu3
*  is nevertheless called from word95
*/
static HRESULT WINAPI ISvItemCm_fnHandleMenuMsg(
509
	IContextMenu2 *iface,
510 511 512 513
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam)
{
514
	ItemCmImpl *This = (ItemCmImpl *)iface;
515

516
	TRACE("(%p)->(msg=%x wp=%lx lp=%lx)\n",This, uMsg, wParam, lParam);
517 518 519 520

	return E_NOTIMPL;
}

521
static const IContextMenu2Vtbl cmvt =
522
{
523 524 525 526 527 528
	ISvItemCm_fnQueryInterface,
	ISvItemCm_fnAddRef,
	ISvItemCm_fnRelease,
	ISvItemCm_fnQueryContextMenu,
	ISvItemCm_fnInvokeCommand,
	ISvItemCm_fnGetCommandString,
529
	ISvItemCm_fnHandleMenuMsg
530
};