1 /*
2  *    Virtual Printers Folder
3  *
4  *    Copyright 1997                Marcus Meissner
5  *    Copyright 1998, 1999, 2002    Juergen Schmied
6  *    Copyright 2005                Huw Davies
7  *    Copyright 2009                Andrew Hill
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23 
24 #include <precomp.h>
25 
26 #include <winspool.h>
27 
28 WINE_DEFAULT_DEBUG_CHANNEL (shell);
29 
30 static shvheader PrinterSFHeader[] = {
31     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
32     {IDS_SHV_COLUMN_DOCUMENTS , SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
33     {IDS_SHV_COLUMN_STATUS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
34     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
35     {IDS_SHV_COLUMN_LOCATION, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
36     {IDS_SHV_COLUMN_MODEL, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15}
37 };
38 
39 #define COLUMN_NAME          0
40 #define COLUMN_DOCUMENTS     1
41 #define COLUMN_STATUS        2
42 #define COLUMN_COMMENTS      3
43 #define COLUMN_LOCATION      4
44 #define COLUMN_MODEL         5
45 
46 #define PrinterSHELLVIEWCOLUMNS (6)
47 
48 /**************************************************************************
49  *  CPrintersExtractIconW_CreateInstane
50  *
51  *  There is no CPrintersExtractIconW. We just initialize CExtractIcon properly to do our job.
52  */
53 HRESULT WINAPI CPrintersExtractIconW_CreateInstane(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv)
54 {
55     CComPtr<IDefaultExtractIconInit> initIcon;
56     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,&initIcon));
57     if (FAILED_UNEXPECTEDLY(hr))
58         return hr;
59 
60     /* FIXME: other icons for default, network, print to file */
61     initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_PRINTER);
62 
63     return initIcon->QueryInterface(riid,ppv);
64 }
65 
66 /***********************************************************************
67  *     Printers folder implementation
68  */
69 
70 class CPrintersEnum: public CEnumIDListBase
71 {
72     public:
73         CPrintersEnum();
74         ~CPrintersEnum();
75         HRESULT WINAPI Initialize(HWND hwndOwner, DWORD dwFlags);
76         BOOL CreatePrintersEnumList(DWORD dwFlags);
77 
78         BEGIN_COM_MAP(CPrintersEnum)
79         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
80         END_COM_MAP()
81 };
82 
83 CPrintersEnum::CPrintersEnum()
84 {
85 }
86 
87 CPrintersEnum::~CPrintersEnum()
88 {
89 }
90 
91 HRESULT WINAPI CPrintersEnum::Initialize(HWND hwndOwner, DWORD dwFlags)
92 {
93     if (CreatePrintersEnumList(dwFlags) == FALSE)
94         return E_FAIL;
95     return S_OK;
96 }
97 
98 static LPITEMIDLIST _ILCreatePrinterItem(PRINTER_INFO_4W *pi)
99 {
100     PIDLDATA tmp;
101     LPITEMIDLIST pidl;
102     PIDLPrinterStruct * p;
103     int size0 = (char*)&tmp.u.cprinter.szName - (char*)&tmp.u.cprinter;
104     int size = size0;
105 
106     tmp.type = 0x00;
107     tmp.u.cprinter.dummy = 0xFF;
108     if (pi->pPrinterName)
109         tmp.u.cprinter.offsServer = wcslen(pi->pPrinterName) + 1;
110     else
111         tmp.u.cprinter.offsServer = 1;
112 
113     size += tmp.u.cprinter.offsServer * sizeof(WCHAR);
114     if (pi->pServerName)
115         size += (wcslen(pi->pServerName) + 1) * sizeof(WCHAR);
116     else
117         size += sizeof(WCHAR);
118 
119     pidl = (LPITEMIDLIST)SHAlloc(size + 4);
120     if (!pidl)
121         return pidl;
122 
123     pidl->mkid.cb = size + 2;
124     memcpy(pidl->mkid.abID, &tmp, 2 + size0);
125 
126     p = &((PIDLDATA*)pidl->mkid.abID)->u.cprinter;
127 
128     p->Attributes = pi->Attributes;
129     if (pi->pPrinterName)
130         wcscpy(p->szName, pi->pPrinterName);
131     else
132         p->szName[0] = L'\0';
133 
134     if (pi->pServerName)
135         wcscpy(p->szName + p->offsServer, pi->pServerName);
136     else
137         p->szName[p->offsServer] = L'\0';
138 
139     *(WORD*)((char*)pidl + (size + 2)) = 0;
140     return pidl;
141 }
142 
143 /**************************************************************************
144  *  CPrintersEnum::CreatePrintersEnumList()
145  */
146 BOOL CPrintersEnum::CreatePrintersEnumList(DWORD dwFlags)
147 {
148     BOOL ret = TRUE;
149 
150     TRACE("(%p)->(flags=0x%08lx) \n", this, dwFlags);
151 
152     /* enumerate the folders */
153     if (dwFlags & SHCONTF_NONFOLDERS)
154     {
155         DWORD needed = 0, num = 0, i;
156         PRINTER_INFO_4W *pi;
157 
158         EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &needed, &num);
159         if (!needed)
160             return ret;
161 
162         pi = (PRINTER_INFO_4W *)HeapAlloc(GetProcessHeap(), 0, needed);
163         if(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, (LPBYTE)pi, needed, &needed, &num)) {
164             HeapFree(GetProcessHeap(), 0, pi);
165             return FALSE;
166         }
167 
168         for(i = 0; i < num; i++) {
169             LPITEMIDLIST pidl = _ILCreatePrinterItem(&pi[i]);
170             if (pidl)
171             {
172                 if (!AddToEnumList(pidl))
173                     SHFree(pidl);
174             }
175         }
176         HeapFree(GetProcessHeap(), 0, pi);
177     }
178     return ret;
179 }
180 
181 CPrinterFolder::CPrinterFolder()
182 {
183     pidlRoot = NULL;
184     dwAttributes = 0;
185     pclsid = NULL;
186 }
187 
188 CPrinterFolder::~CPrinterFolder()
189 {
190     TRACE("-- destroying IShellFolder(%p)\n", this);
191     if (pidlRoot)
192         SHFree(pidlRoot);
193 }
194 
195 /**************************************************************************
196  *    CPrinterFolder::ParseDisplayName
197  *
198  * This is E_NOTIMPL in Windows too.
199  */
200 HRESULT WINAPI CPrinterFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName,
201         DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes)
202 {
203     TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
204           this, hwndOwner, pbc, lpszDisplayName, debugstr_w(lpszDisplayName),
205           pchEaten, ppidl, pdwAttributes);
206 
207     *ppidl = 0;
208     if (pchEaten)
209         *pchEaten = 0;
210 
211     return E_NOTIMPL;
212 }
213 
214 static PIDLPrinterStruct * _ILGetPrinterStruct(LPCITEMIDLIST pidl)
215 {
216     LPPIDLDATA pdata = _ILGetDataPointer(pidl);
217 
218     if (pdata && pdata->type == 0x00)
219         return (PIDLPrinterStruct*) & (pdata->u.cfont);
220 
221     return NULL;
222 }
223 
224 /**************************************************************************
225  *        CPrinterFolder::EnumObjects
226  */
227 HRESULT WINAPI CPrinterFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST * ppEnumIDList)
228 {
229     return ShellObjectCreatorInit<CPrintersEnum>(hwndOwner, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
230 }
231 
232 /**************************************************************************
233  *        CPrinterFolder::BindToObject
234  */
235 HRESULT WINAPI CPrinterFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut)
236 {
237     return E_NOTIMPL;
238 }
239 
240 /**************************************************************************
241  *    ISF_Printers_fnBindToStorage
242  */
243 HRESULT WINAPI CPrinterFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut)
244 {
245     FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n",
246            this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
247 
248     *ppvOut = NULL;
249     return E_NOTIMPL;
250 }
251 
252 /**************************************************************************
253  *     CPrinterFolder::CompareIDs
254  */
255 HRESULT WINAPI CPrinterFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
256 {
257     return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
258 }
259 
260 /**************************************************************************
261  *    CPrinterFolder::CreateViewObject
262  */
263 HRESULT WINAPI CPrinterFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvOut)
264 {
265     CComPtr<IShellView> pShellView;
266     HRESULT hr = E_INVALIDARG;
267 
268     TRACE("(%p)->(hwnd=%p,%s,%p)\n", this,
269           hwndOwner, shdebugstr_guid (&riid), ppvOut);
270 
271     if (!ppvOut)
272         return hr;
273 
274     *ppvOut = NULL;
275 
276     if (IsEqualIID(riid, IID_IDropTarget))
277     {
278         WARN("IDropTarget not implemented\n");
279         hr = E_NOTIMPL;
280     }
281     else if(IsEqualIID(riid, IID_IContextMenu))
282     {
283         WARN("IContextMenu not implemented\n");
284         hr = E_NOTIMPL;
285     }
286     else if(IsEqualIID(riid, IID_IShellView))
287     {
288         SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
289         hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
290     }
291     TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
292     return hr;
293 }
294 
295 /**************************************************************************
296  *  CPrinterFolder::GetAttributesOf
297  */
298 HRESULT WINAPI CPrinterFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
299 {
300     static const DWORD dwPrintersAttributes =
301         SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_CANRENAME | SFGAO_CANDELETE;
302     HRESULT hr = S_OK;
303 
304     FIXME ("(%p)->(cidl=%d apidl=%p mask=0x%08lx): stub\n",
305            this, cidl, apidl, *rgfInOut);
306 
307     *rgfInOut &= dwPrintersAttributes;
308 
309     *rgfInOut &= ~SFGAO_VALIDATE;
310 
311     TRACE ("-- result=0x%08x\n", *rgfInOut);
312     return hr;
313 }
314 
315 /**************************************************************************
316  *    CPrinterFolder::GetUIObjectOf
317  *
318  * PARAMETERS
319  *  HWND           hwndOwner, //[in ] Parent window for any output
320  *  UINT           cidl,      //[in ] array size
321  *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
322  *  REFIID         riid,      //[in ] Requested Interface
323  *  UINT*          prgfInOut, //[   ] reserved
324  *  LPVOID*        ppvObject) //[out] Resulting Interface
325  *
326  */
327 HRESULT WINAPI CPrinterFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
328         REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
329 {
330     LPVOID pObj = NULL;
331     HRESULT hr = E_INVALIDARG;
332 
333     TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
334            this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
335 
336     if (!ppvOut)
337         return hr;
338 
339     *ppvOut = NULL;
340 
341     if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && cidl == 1)
342         hr = CPrintersExtractIconW_CreateInstane(apidl[0], riid, &pObj);
343     else
344         hr = E_NOINTERFACE;
345 
346     if (SUCCEEDED(hr) && !pObj)
347         hr = E_OUTOFMEMORY;
348 
349     *ppvOut = pObj;
350     TRACE ("(%p)->hr=0x%08lx\n", this, hr);
351     return hr;
352 }
353 
354 /**************************************************************************
355  *    CPrinterFolder::GetDisplayNameOf
356  *
357  */
358 HRESULT WINAPI CPrinterFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
359 {
360     PIDLPrinterStruct * p;
361 
362     TRACE ("(%p)->(pidl=%p,0x%08lx,%p)\n", this, pidl, dwFlags, strRet);
363     pdump (pidl);
364 
365     if (!strRet)
366     {
367         WARN("no strRet\n");
368         return E_INVALIDARG;
369     }
370 
371     p = _ILGetPrinterStruct(pidl);
372     if (!p)
373     {
374         ERR("no printer struct\n");
375         return E_INVALIDARG;
376     }
377 
378     return SHSetStrRet(strRet, p->szName);
379 }
380 
381 /**************************************************************************
382  *  CPrinterFolder::SetNameOf
383  *  Changes the name of a file object or subfolder, possibly changing its item
384  *  identifier in the process.
385  *
386  * PARAMETERS
387  *  HWND          hwndOwner,  //[in ] Owner window for output
388  *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
389  *  LPCOLESTR     lpszName,   //[in ] the items new display name
390  *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
391  *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
392  */
393 HRESULT WINAPI CPrinterFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl,    /* simple pidl */
394         LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
395 {
396     FIXME("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", this, hwndOwner, pidl,
397           debugstr_w (lpName), dwFlags, pPidlOut);
398 
399     return E_FAIL;
400 }
401 
402 HRESULT WINAPI CPrinterFolder::GetDefaultSearchGUID(GUID *pguid)
403 {
404     FIXME("(%p)\n", this);
405     return E_NOTIMPL;
406 }
407 
408 HRESULT WINAPI CPrinterFolder::EnumSearches(IEnumExtraSearch **ppenum)
409 {
410     FIXME("(%p)\n", this);
411     return E_NOTIMPL;
412 }
413 
414 HRESULT WINAPI CPrinterFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
415 {
416     if (pSort)
417         *pSort = 0;
418     if (pDisplay)
419         *pDisplay = 0;
420 
421     return S_OK;
422 }
423 
424 HRESULT WINAPI CPrinterFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
425 {
426     if (!pcsFlags || iColumn >= PrinterSHELLVIEWCOLUMNS)
427         return E_INVALIDARG;
428     *pcsFlags = PrinterSFHeader[iColumn].pcsFlags;
429     return S_OK;
430 
431 }
432 
433 HRESULT WINAPI CPrinterFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
434 {
435     FIXME("(%p): stub\n", this);
436 
437     return E_NOTIMPL;
438 }
439 
440 HRESULT WINAPI CPrinterFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
441 {
442     TRACE("(%p)->(%p %i %p): stub\n", this, pidl, iColumn, psd);
443 
444     if (iColumn >= PrinterSHELLVIEWCOLUMNS)
445         return E_FAIL;
446 
447     psd->fmt = PrinterSFHeader[iColumn].fmt;
448     psd->cxChar = PrinterSFHeader[iColumn].cxChar;
449     if (pidl == NULL)
450         return SHSetStrRet(&psd->str, PrinterSFHeader[iColumn].colnameid);
451 
452     if (iColumn == COLUMN_NAME)
453         return GetDisplayNameOf(pidl, SHGDN_NORMAL, &psd->str);
454 
455     psd->str.uType = STRRET_CSTR;
456     psd->str.cStr[0] = '\0';
457 
458     return E_NOTIMPL;
459 }
460 
461 HRESULT WINAPI CPrinterFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
462 {
463     FIXME ("(%p): stub\n", this);
464     return E_NOTIMPL;
465 }
466 
467 /************************************************************************
468  *    CPrinterFolder::GetClassID
469  */
470 HRESULT WINAPI CPrinterFolder::GetClassID(CLSID *lpClassId)
471 {
472     TRACE ("(%p)\n", this);
473 
474     *lpClassId = CLSID_Printers;
475 
476     return S_OK;
477 }
478 
479 /************************************************************************
480  *    CPrinterFolder::Initialize
481  */
482 HRESULT WINAPI CPrinterFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
483 {
484     if (pidlRoot)
485         SHFree((LPVOID)pidlRoot);
486 
487     pidlRoot = ILClone(pidl);
488     return S_OK;
489 }
490 
491 /**************************************************************************
492  *    CPrinterFolder::GetCurFolder
493  */
494 HRESULT WINAPI CPrinterFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
495 {
496     TRACE ("(%p)->(%p)\n", this, pidl);
497 
498     *pidl = ILClone (pidlRoot);
499     return S_OK;
500 }
501