[USB-BRINGUP]
[reactos.git] / dll / win32 / browseui / travellog.cpp
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 Implements a class that keeps track of a PIDL history and allows
23 navigation forward and backward. This really should be in shdocvw, but it
24 is not registered for external instantiation, and the entire IBrowserService
25 hierarchy that normally spans browseui and shdocvw are collapsed into one
26 hierarchy in browseui, so I am moving it to browseui for now. If someone
27 decides to refactor code later, it wouldn't be difficult to move it.
28
29 TODO:
30 ****Does original travel log update the current item in the Travel method before or after calling ITravelEntry::Invoke?
31 ****Change to load maximum size from registry
32 ****Add code to track current size
33 ****Fix InsertMenuEntries to not exceed limit of menu item ids provided. Perhaps the method should try to be intelligent and if there are
34 too many items, center around the current item? Could cause dispatch problems...
35 ****Move tool tip text templates to resources
36 **Simplify code in InsertMenuEntries
37 Implement UpdateExternal
38 Implement FindTravelEntry
39 Implement Clone
40 Implement Revert
41
42 */
43 #include <windows.h>
44 #include <shlobj.h>
45 #include <shlobj_undoc.h>
46 #include <shlguid.h>
47 #include <shlguid_undoc.h>
48 #include <tchar.h>
49 #include <perhist.h>
50 #include <atlbase.h>
51 #include <atlcom.h>
52 #include <atlwin.h>
53
54 class CTravelEntry :
55 public CComObjectRootEx<CComMultiThreadModelNoCS>,
56 public ITravelEntry
57 {
58 public:
59 CTravelEntry *fNextEntry;
60 CTravelEntry *fPreviousEntry;
61 private:
62 LPITEMIDLIST fPIDL;
63 HGLOBAL fPersistState;
64 public:
65 CTravelEntry();
66 ~CTravelEntry();
67 HRESULT GetToolTipText(IUnknown *punk, LPWSTR pwzText) const;
68 long GetSize() const;
69
70 // *** ITravelEntry methods ***
71 virtual HRESULT STDMETHODCALLTYPE Invoke(IUnknown *punk);
72 virtual HRESULT STDMETHODCALLTYPE Update(IUnknown *punk, BOOL fIsLocalAnchor);
73 virtual HRESULT STDMETHODCALLTYPE GetPidl(LPITEMIDLIST *ppidl);
74
75 BEGIN_COM_MAP(CTravelEntry)
76 COM_INTERFACE_ENTRY_IID(IID_ITravelEntry, ITravelEntry)
77 END_COM_MAP()
78 };
79
80 class CTravelLog :
81 public CComObjectRootEx<CComMultiThreadModelNoCS>,
82 public ITravelLog
83 {
84 private:
85 CTravelEntry *fListHead;
86 CTravelEntry *fListTail;
87 CTravelEntry *fCurrentEntry;
88 long fMaximumSize;
89 long fCurrentSize;
90 unsigned long fEntryCount;
91 public:
92 CTravelLog();
93 ~CTravelLog();
94 HRESULT Initialize();
95 HRESULT FindRelativeEntry(int offset, CTravelEntry **foundEntry);
96 void DeleteChain(CTravelEntry *startHere);
97 void AppendEntry(CTravelEntry *afterEntry, CTravelEntry *newEntry);
98 public:
99
100 // *** ITravelLog methods ***
101 virtual HRESULT STDMETHODCALLTYPE AddEntry(IUnknown *punk, BOOL fIsLocalAnchor);
102 virtual HRESULT STDMETHODCALLTYPE UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor);
103 virtual HRESULT STDMETHODCALLTYPE UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext);
104 virtual HRESULT STDMETHODCALLTYPE Travel(IUnknown *punk, int iOffset);
105 virtual HRESULT STDMETHODCALLTYPE GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte);
106 virtual HRESULT STDMETHODCALLTYPE FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte);
107 virtual HRESULT STDMETHODCALLTYPE GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText);
108 virtual HRESULT STDMETHODCALLTYPE InsertMenuEntries(IUnknown *punk, HMENU hmenu, int nPos, int idFirst, int idLast, DWORD dwFlags);
109 virtual HRESULT STDMETHODCALLTYPE Clone(ITravelLog **pptl);
110 virtual DWORD STDMETHODCALLTYPE CountEntries(IUnknown *punk);
111 virtual HRESULT STDMETHODCALLTYPE Revert();
112
113 BEGIN_COM_MAP(CTravelLog)
114 COM_INTERFACE_ENTRY_IID(IID_ITravelLog, ITravelLog)
115 END_COM_MAP()
116 };
117
118 CTravelEntry::CTravelEntry()
119 {
120 fNextEntry = NULL;
121 fPreviousEntry = NULL;
122 fPIDL = NULL;
123 fPersistState = NULL;
124 }
125
126 CTravelEntry::~CTravelEntry()
127 {
128 ILFree(fPIDL);
129 GlobalFree(fPersistState);
130 }
131
132 HRESULT CTravelEntry::GetToolTipText(IUnknown *punk, LPWSTR pwzText) const
133 {
134 HRESULT hResult;
135
136 hResult = ILGetDisplayNameEx(NULL, fPIDL, pwzText, ILGDN_NORMAL);
137 if (FAILED(hResult))
138 return hResult;
139 return S_OK;
140 }
141
142 long CTravelEntry::GetSize() const
143 {
144 return 0;
145 }
146
147 HRESULT STDMETHODCALLTYPE CTravelEntry::Invoke(IUnknown *punk)
148 {
149 CComPtr<IPersistHistory> persistHistory;
150 CComPtr<IStream> globalStream;
151 HRESULT hResult;
152
153 hResult = punk->QueryInterface(IID_IPersistHistory, (void **)&persistHistory);
154 if (FAILED(hResult))
155 return hResult;
156 hResult = CreateStreamOnHGlobal(fPersistState, FALSE, &globalStream);
157 if (FAILED(hResult))
158 return hResult;
159 hResult = persistHistory->LoadHistory(globalStream, NULL);
160 if (FAILED(hResult))
161 return hResult;
162 return S_OK;
163 }
164
165 HRESULT STDMETHODCALLTYPE CTravelEntry::Update(IUnknown *punk, BOOL fIsLocalAnchor)
166 {
167 CComPtr<ITravelLogClient> travelLogClient;
168 CComPtr<IPersistHistory> persistHistory;
169 CComPtr<IStream> globalStream;
170 WINDOWDATA windowData;
171 HGLOBAL globalStorage;
172 HRESULT hResult;
173
174 ILFree(fPIDL);
175 fPIDL = NULL;
176 GlobalFree(fPersistState);
177 fPersistState = NULL;
178 hResult = punk->QueryInterface(IID_ITravelLogClient, (void **)&travelLogClient);
179 if (FAILED(hResult))
180 return hResult;
181 hResult = travelLogClient->GetWindowData(&windowData);
182 if (FAILED(hResult))
183 return hResult;
184 fPIDL = windowData.pidl;
185 // TODO: Properly free the windowData
186 hResult = punk->QueryInterface(IID_IPersistHistory, (void **)&persistHistory);
187 if (FAILED(hResult))
188 return hResult;
189 globalStorage = GlobalAlloc(GMEM_FIXED, 0);
190 hResult = CreateStreamOnHGlobal(globalStorage, FALSE, &globalStream);
191 if (FAILED(hResult))
192 return hResult;
193 hResult = persistHistory->SaveHistory(globalStream);
194 if (FAILED(hResult))
195 return hResult;
196 hResult = GetHGlobalFromStream(globalStream, &fPersistState);
197 if (FAILED(hResult))
198 return hResult;
199 return S_OK;
200 }
201
202 HRESULT STDMETHODCALLTYPE CTravelEntry::GetPidl(LPITEMIDLIST *ppidl)
203 {
204 if (ppidl == NULL)
205 return E_POINTER;
206 *ppidl = ILClone(fPIDL);
207 if (*ppidl == NULL)
208 return E_OUTOFMEMORY;
209 return S_OK;
210 }
211
212 CTravelLog::CTravelLog()
213 {
214 fListHead = NULL;
215 fListTail = NULL;
216 fCurrentEntry = NULL;
217 fMaximumSize = 0;
218 fCurrentSize = 0;
219 fEntryCount = 0;
220 }
221
222 CTravelLog::~CTravelLog()
223 {
224 CTravelEntry *anEntry;
225 CTravelEntry *next;
226
227 anEntry = fListHead;
228 while (anEntry != NULL)
229 {
230 next = anEntry->fNextEntry;
231 anEntry->Release();
232 anEntry = next;
233 }
234 }
235
236 HRESULT CTravelLog::Initialize()
237 {
238 fMaximumSize = 1024 * 1024; // TODO: change to read this from registry
239 // Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog
240 // MaxSize
241 return S_OK;
242 }
243
244 HRESULT CTravelLog::FindRelativeEntry(int offset, CTravelEntry **foundEntry)
245 {
246 CTravelEntry *curEntry;
247
248 *foundEntry = NULL;
249 curEntry = fCurrentEntry;
250 if (offset < 0)
251 {
252 while (offset < 0 && curEntry != NULL)
253 {
254 curEntry = curEntry->fPreviousEntry;
255 offset++;
256 }
257 }
258 else
259 {
260 while (offset > 0 && curEntry != NULL)
261 {
262 curEntry = curEntry->fNextEntry;
263 offset--;
264 }
265 }
266 if (curEntry == NULL)
267 return E_INVALIDARG;
268 *foundEntry = curEntry;
269 return S_OK;
270 }
271
272 void CTravelLog::DeleteChain(CTravelEntry *startHere)
273 {
274 CTravelEntry *saveNext;
275 long itemSize;
276
277 if (startHere->fPreviousEntry != NULL)
278 {
279 startHere->fPreviousEntry->fNextEntry = NULL;
280 fListTail = startHere->fPreviousEntry;
281 }
282 else
283 {
284 fListHead = NULL;
285 fListTail = NULL;
286 }
287 while (startHere != NULL)
288 {
289 saveNext = startHere->fNextEntry;
290 itemSize = startHere->GetSize();
291 fCurrentSize -= itemSize;
292 startHere->Release();
293 startHere = saveNext;
294 fEntryCount--;
295 }
296 }
297
298 void CTravelLog::AppendEntry(CTravelEntry *afterEntry, CTravelEntry *newEntry)
299 {
300 if (afterEntry == NULL)
301 {
302 fListHead = newEntry;
303 fListTail = newEntry;
304 }
305 else
306 {
307 newEntry->fNextEntry = afterEntry->fNextEntry;
308 afterEntry->fNextEntry = newEntry;
309 newEntry->fPreviousEntry = afterEntry;
310 if (newEntry->fNextEntry == NULL)
311 fListTail = newEntry;
312 else
313 newEntry->fNextEntry->fPreviousEntry = newEntry;
314 }
315 fEntryCount++;
316 }
317
318 HRESULT STDMETHODCALLTYPE CTravelLog::AddEntry(IUnknown *punk, BOOL fIsLocalAnchor)
319 {
320 CComObject<CTravelEntry> *newEntry;
321 long itemSize;
322
323 if (punk == NULL)
324 return E_INVALIDARG;
325 ATLTRY (newEntry = new CComObject<CTravelEntry>);
326 if (newEntry == NULL)
327 return E_OUTOFMEMORY;
328 newEntry->AddRef();
329 if (fCurrentEntry != NULL && fCurrentEntry->fNextEntry != NULL)
330 DeleteChain(fCurrentEntry->fNextEntry);
331 AppendEntry(fCurrentEntry, newEntry);
332 itemSize = newEntry->GetSize();
333 fCurrentSize += itemSize;
334 fCurrentEntry = newEntry;
335 return S_OK;
336 }
337
338 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor)
339 {
340 if (punk == NULL)
341 return E_INVALIDARG;
342 if (fCurrentEntry == NULL)
343 return E_UNEXPECTED;
344 return fCurrentEntry->Update(punk, fIsLocalAnchor);
345 }
346
347 HRESULT STDMETHODCALLTYPE CTravelLog::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext)
348 {
349 return E_NOTIMPL;
350 }
351
352 HRESULT STDMETHODCALLTYPE CTravelLog::Travel(IUnknown *punk, int iOffset)
353 {
354 CTravelEntry *destinationEntry;
355 HRESULT hResult;
356
357 hResult = FindRelativeEntry(iOffset, &destinationEntry);
358 if (FAILED(hResult))
359 return hResult;
360 fCurrentEntry = destinationEntry;
361 hResult = destinationEntry->Invoke(punk);
362 if (FAILED(hResult))
363 return hResult;
364 return S_OK;
365 }
366
367 HRESULT STDMETHODCALLTYPE CTravelLog::GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte)
368 {
369 CTravelEntry *destinationEntry;
370 HRESULT hResult;
371
372 hResult = FindRelativeEntry(iOffset, &destinationEntry);
373 if (FAILED(hResult))
374 return hResult;
375 return destinationEntry->QueryInterface(IID_ITravelEntry, (void **)ppte);
376 }
377
378 HRESULT STDMETHODCALLTYPE CTravelLog::FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte)
379 {
380 if (ppte == NULL)
381 return E_POINTER;
382 if (punk == NULL || pidl == NULL)
383 return E_INVALIDARG;
384 return E_NOTIMPL;
385 }
386
387 HRESULT STDMETHODCALLTYPE CTravelLog::GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText)
388 {
389 CTravelEntry *destinationEntry;
390 wchar_t tempString[MAX_PATH];
391 wchar_t templateString[200];
392 HRESULT hResult;
393
394 if (pwzText == NULL)
395 return E_POINTER;
396 if (punk == NULL || cchText == 0)
397 return E_INVALIDARG;
398 hResult = FindRelativeEntry(iOffset, &destinationEntry);
399 if (FAILED(hResult))
400 return hResult;
401 hResult = destinationEntry->GetToolTipText(punk, tempString);
402 if (FAILED(hResult))
403 return hResult;
404 if (iOffset < 0)
405 {
406 wcscpy(templateString, L"Back to %s");
407 }
408 else
409 {
410 wcscpy(templateString, L"Forward to %s");
411 }
412 _snwprintf(pwzText, cchText, templateString, tempString);
413 return S_OK;
414 }
415
416 static void FixAmpersands(wchar_t *buffer)
417 {
418 wchar_t tempBuffer[MAX_PATH * 2];
419 wchar_t ch;
420 wchar_t *srcPtr;
421 wchar_t *dstPtr;
422
423 srcPtr = buffer;
424 dstPtr = tempBuffer;
425 while (*srcPtr != 0)
426 {
427 ch = *srcPtr++;
428 *dstPtr++ = ch;
429 if (ch == '&')
430 *dstPtr++ = '&';
431 }
432 *dstPtr = 0;
433 wcscpy(buffer, tempBuffer);
434 }
435
436 HRESULT STDMETHODCALLTYPE CTravelLog::InsertMenuEntries(IUnknown *punk, HMENU hmenu, int nPos, int idFirst, int idLast, DWORD dwFlags)
437 {
438 CTravelEntry *currentItem;
439 MENUITEMINFO menuItemInfo;
440 wchar_t itemTextBuffer[MAX_PATH * 2];
441 HRESULT hResult;
442
443 // TLMENUF_BACK - include back entries
444 // TLMENUF_INCLUDECURRENT - include current entry, if TLMENUF_CHECKCURRENT then check the entry
445 // TLMENUF_FORE - include next entries
446 // if fore+back, list from oldest to newest
447 // if back, list from newest to oldest
448 // if fore, list from newest to oldest
449
450 // don't forget to patch ampersands before adding to menu
451 if (punk == NULL)
452 return E_INVALIDARG;
453 if (idLast <= idFirst)
454 return E_INVALIDARG;
455 menuItemInfo.cbSize = sizeof(menuItemInfo);
456 menuItemInfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING;
457 menuItemInfo.fType = MFT_STRING;
458 menuItemInfo.wID = idFirst;
459 menuItemInfo.fState = MFS_ENABLED;
460 menuItemInfo.dwTypeData = itemTextBuffer;
461 if ((dwFlags & TLMENUF_BACK) != 0)
462 {
463 if ((dwFlags & TLMENUF_FORE) != 0)
464 {
465 currentItem = fCurrentEntry;
466 if (currentItem != NULL)
467 {
468 while (currentItem->fPreviousEntry != NULL)
469 currentItem = currentItem->fPreviousEntry;
470 }
471 while (currentItem != fCurrentEntry)
472 {
473 hResult = currentItem->GetToolTipText(punk, itemTextBuffer);
474 if (SUCCEEDED(hResult))
475 {
476 FixAmpersands(itemTextBuffer);
477 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
478 {
479 nPos++;
480 menuItemInfo.wID++;
481 }
482 }
483 currentItem = currentItem->fNextEntry;
484 }
485 }
486 else
487 {
488 currentItem = fCurrentEntry;
489 if (currentItem != NULL)
490 currentItem = currentItem->fPreviousEntry;
491 while (currentItem != NULL)
492 {
493 hResult = currentItem->GetToolTipText(punk, itemTextBuffer);
494 if (SUCCEEDED(hResult))
495 {
496 FixAmpersands(itemTextBuffer);
497 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
498 {
499 nPos++;
500 menuItemInfo.wID++;
501 }
502 }
503 currentItem = currentItem->fPreviousEntry;
504 }
505 }
506 }
507 if ((dwFlags & TLMENUF_INCLUDECURRENT) != 0)
508 {
509 if (fCurrentEntry != NULL)
510 {
511 hResult = fCurrentEntry->GetToolTipText(punk, itemTextBuffer);
512 if (SUCCEEDED(hResult))
513 {
514 FixAmpersands(itemTextBuffer);
515 if ((dwFlags & TLMENUF_CHECKCURRENT) != 0)
516 menuItemInfo.fState |= MFS_CHECKED;
517 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
518 {
519 nPos++;
520 menuItemInfo.wID++;
521 }
522 menuItemInfo.fState &= ~MFS_CHECKED;
523 }
524 }
525 }
526 if ((dwFlags & TLMENUF_FORE) != 0)
527 {
528 currentItem = fCurrentEntry;
529 if (currentItem != NULL)
530 currentItem = currentItem->fNextEntry;
531 while (currentItem != NULL)
532 {
533 hResult = currentItem->GetToolTipText(punk, itemTextBuffer);
534 if (SUCCEEDED(hResult))
535 {
536 FixAmpersands(itemTextBuffer);
537 if (InsertMenuItem(hmenu, nPos, TRUE, &menuItemInfo))
538 {
539 nPos++;
540 menuItemInfo.wID++;
541 }
542 }
543 currentItem = currentItem->fNextEntry;
544 }
545 }
546 return S_OK;
547 }
548
549 HRESULT STDMETHODCALLTYPE CTravelLog::Clone(ITravelLog **pptl)
550 {
551 if (pptl == NULL)
552 return E_POINTER;
553 *pptl = NULL;
554 // duplicate the log
555 return E_NOTIMPL;
556 }
557
558 DWORD STDMETHODCALLTYPE CTravelLog::CountEntries(IUnknown *punk)
559 {
560 if (punk == NULL)
561 return E_INVALIDARG;
562 return fEntryCount;
563 }
564
565 HRESULT STDMETHODCALLTYPE CTravelLog::Revert()
566 {
567 // remove the current entry?
568 return E_NOTIMPL;
569 }
570
571 HRESULT CreateTravelLog(REFIID riid, void **ppv)
572 {
573 CComObject<CTravelLog> *theTravelLog;
574 HRESULT hResult;
575
576 if (ppv == NULL)
577 return E_POINTER;
578 *ppv = NULL;
579 ATLTRY (theTravelLog = new CComObject<CTravelLog>);
580 if (theTravelLog == NULL)
581 return E_OUTOFMEMORY;
582 hResult = theTravelLog->QueryInterface (riid, (void **)ppv);
583 if (FAILED (hResult))
584 {
585 delete theTravelLog;
586 return hResult;
587 }
588 hResult = theTravelLog->Initialize();
589 if (FAILED(hResult))
590 return hResult;
591 return S_OK;
592 }