4 * Copyright 2009 Andrew Hill <ash77 at domain reactos.org>
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.
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.
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
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.
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
47 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
51 CTravelEntry
*fNextEntry
;
52 CTravelEntry
*fPreviousEntry
;
55 HGLOBAL fPersistState
;
59 HRESULT
GetToolTipText(IUnknown
*punk
, LPWSTR pwzText
) const;
62 // *** ITravelEntry methods ***
63 virtual HRESULT STDMETHODCALLTYPE
Invoke(IUnknown
*punk
);
64 virtual HRESULT STDMETHODCALLTYPE
Update(IUnknown
*punk
, BOOL fIsLocalAnchor
);
65 virtual HRESULT STDMETHODCALLTYPE
GetPidl(LPITEMIDLIST
*ppidl
);
67 BEGIN_COM_MAP(CTravelEntry
)
68 COM_INTERFACE_ENTRY_IID(IID_ITravelEntry
, ITravelEntry
)
73 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
77 CTravelEntry
*fListHead
;
78 CTravelEntry
*fListTail
;
79 CTravelEntry
*fCurrentEntry
;
82 unsigned long fEntryCount
;
87 HRESULT
FindRelativeEntry(int offset
, CTravelEntry
**foundEntry
);
88 void DeleteChain(CTravelEntry
*startHere
);
89 void AppendEntry(CTravelEntry
*afterEntry
, CTravelEntry
*newEntry
);
92 // *** ITravelLog methods ***
93 virtual HRESULT STDMETHODCALLTYPE
AddEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
);
94 virtual HRESULT STDMETHODCALLTYPE
UpdateEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
);
95 virtual HRESULT STDMETHODCALLTYPE
UpdateExternal(IUnknown
*punk
, IUnknown
*punkHLBrowseContext
);
96 virtual HRESULT STDMETHODCALLTYPE
Travel(IUnknown
*punk
, int iOffset
);
97 virtual HRESULT STDMETHODCALLTYPE
GetTravelEntry(IUnknown
*punk
, int iOffset
, ITravelEntry
**ppte
);
98 virtual HRESULT STDMETHODCALLTYPE
FindTravelEntry(IUnknown
*punk
, LPCITEMIDLIST pidl
, ITravelEntry
**ppte
);
99 virtual HRESULT STDMETHODCALLTYPE
GetToolTipText(IUnknown
*punk
, int iOffset
, int idsTemplate
, LPWSTR pwzText
, DWORD cchText
);
100 virtual HRESULT STDMETHODCALLTYPE
InsertMenuEntries(IUnknown
*punk
, HMENU hmenu
, int nPos
, int idFirst
, int idLast
, DWORD dwFlags
);
101 virtual HRESULT STDMETHODCALLTYPE
Clone(ITravelLog
**pptl
);
102 virtual DWORD STDMETHODCALLTYPE
CountEntries(IUnknown
*punk
);
103 virtual HRESULT STDMETHODCALLTYPE
Revert();
105 BEGIN_COM_MAP(CTravelLog
)
106 COM_INTERFACE_ENTRY_IID(IID_ITravelLog
, ITravelLog
)
110 CTravelEntry::CTravelEntry()
113 fPreviousEntry
= NULL
;
115 fPersistState
= NULL
;
118 CTravelEntry::~CTravelEntry()
121 GlobalFree(fPersistState
);
124 HRESULT
CTravelEntry::GetToolTipText(IUnknown
*punk
, LPWSTR pwzText
) const
128 hResult
= ILGetDisplayNameEx(NULL
, fPIDL
, pwzText
, ILGDN_NORMAL
) ? S_OK
: S_FALSE
;
129 if (FAILED_UNEXPECTEDLY(hResult
))
135 long CTravelEntry::GetSize() const
140 HRESULT STDMETHODCALLTYPE
CTravelEntry::Invoke(IUnknown
*punk
)
142 CComPtr
<IPersistHistory
> persistHistory
;
143 CComPtr
<IStream
> globalStream
;
146 TRACE("CTravelEntry::Invoke for IUnknown punk=%p\n", punk
);
148 hResult
= punk
->QueryInterface(IID_PPV_ARG(IPersistHistory
, &persistHistory
));
149 if (FAILED_UNEXPECTEDLY(hResult
))
151 hResult
= CreateStreamOnHGlobal(fPersistState
, FALSE
, &globalStream
);
152 if (FAILED_UNEXPECTEDLY(hResult
))
154 hResult
= persistHistory
->LoadHistory(globalStream
, NULL
);
155 if (FAILED_UNEXPECTEDLY(hResult
))
160 HRESULT STDMETHODCALLTYPE
CTravelEntry::Update(IUnknown
*punk
, BOOL fIsLocalAnchor
)
162 CComPtr
<ITravelLogClient
> travelLogClient
;
163 CComPtr
<IPersistHistory
> persistHistory
;
164 CComPtr
<IStream
> globalStream
;
165 WINDOWDATA windowData
;
168 TRACE("CTravelEntry::Update for IUnknown punk=%p, fIsLocalAnchor=%s\n", punk
, fIsLocalAnchor
? "TRUE" : "FALSE");
171 WCHAR wch
[MAX_PATH
* 2];
172 GetToolTipText(punk
, wch
);
173 TRACE("Updating entry with display name: %S\n", wch
);
177 GlobalFree(fPersistState
);
178 fPersistState
= NULL
;
179 hResult
= punk
->QueryInterface(IID_PPV_ARG(ITravelLogClient
, &travelLogClient
));
180 if (FAILED_UNEXPECTEDLY(hResult
))
182 hResult
= punk
->QueryInterface(IID_PPV_ARG(IPersistHistory
, &persistHistory
));
183 if (FAILED_UNEXPECTEDLY(hResult
))
185 hResult
= CreateStreamOnHGlobal(NULL
, FALSE
, &globalStream
);
186 if (FAILED_UNEXPECTEDLY(hResult
))
188 hResult
= persistHistory
->SaveHistory(globalStream
);
189 if (FAILED_UNEXPECTEDLY(hResult
))
191 hResult
= travelLogClient
->GetWindowData(globalStream
, &windowData
);
192 if (FAILED_UNEXPECTEDLY(hResult
))
194 fPIDL
= windowData
.pidl
;
195 // TODO: Properly free the windowData
196 hResult
= GetHGlobalFromStream(globalStream
, &fPersistState
);
197 if (FAILED_UNEXPECTEDLY(hResult
))
200 GetToolTipText(punk
, wch
);
201 TRACE("Updated entry display name is now: %S\n", wch
);
206 HRESULT STDMETHODCALLTYPE
CTravelEntry::GetPidl(LPITEMIDLIST
*ppidl
)
210 *ppidl
= ILClone(fPIDL
);
212 return E_OUTOFMEMORY
;
214 TRACE("CTravelEntry::GetPidl returning ppidl=%p\n", *ppidl
);
219 CTravelLog::CTravelLog()
223 fCurrentEntry
= NULL
;
227 TRACE("CTravelLog created\n");
230 CTravelLog::~CTravelLog()
232 CTravelEntry
*anEntry
;
236 while (anEntry
!= NULL
)
238 next
= anEntry
->fNextEntry
;
242 TRACE("CTravelLog destroyed\n");
245 HRESULT
CTravelLog::Initialize()
247 FIXME("CTravelLog::Initialize using hardcoded fMaximumSize.\n");
248 fMaximumSize
= 1024 * 1024; // TODO: change to read this from registry
249 // Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog
254 HRESULT
CTravelLog::FindRelativeEntry(int _offset
, CTravelEntry
**foundEntry
)
256 CTravelEntry
*curEntry
;
257 int offset
= _offset
;
259 if (foundEntry
== NULL
)
263 curEntry
= fCurrentEntry
;
266 while (offset
< 0 && curEntry
!= NULL
)
268 curEntry
= curEntry
->fPreviousEntry
;
274 while (offset
> 0 && curEntry
!= NULL
)
276 curEntry
= curEntry
->fNextEntry
;
280 if (curEntry
== NULL
)
283 *foundEntry
= curEntry
;
285 TRACE("CTravelLog::FindRelativeEntry for offset %d, returning %p\n", offset
, *foundEntry
);
290 void CTravelLog::DeleteChain(CTravelEntry
*startHere
)
292 CTravelEntry
*saveNext
;
295 TRACE("CTravelLog::DeleteChain deleting chain starting at %p\n", startHere
);
297 long startEntryCount
= fEntryCount
;
299 if (startHere
->fPreviousEntry
!= NULL
)
301 startHere
->fPreviousEntry
->fNextEntry
= NULL
;
302 fListTail
= startHere
->fPreviousEntry
;
309 while (startHere
!= NULL
)
311 saveNext
= startHere
->fNextEntry
;
312 itemSize
= startHere
->GetSize();
313 fCurrentSize
-= itemSize
;
314 startHere
->Release();
315 startHere
= saveNext
;
319 TRACE("CTravelLog::DeleteChain chain of %d items deleted\n", startEntryCount
- fEntryCount
);
322 void CTravelLog::AppendEntry(CTravelEntry
*afterEntry
, CTravelEntry
*newEntry
)
324 if (afterEntry
== NULL
)
326 TRACE("CTravelLog::AppendEntry appending %p after NULL. Resetting head and tail\n", newEntry
);
327 fListHead
= newEntry
;
328 fListTail
= newEntry
;
332 TRACE("CTravelLog::AppendEntry appending %p after %p\n", newEntry
, afterEntry
);
333 newEntry
->fNextEntry
= afterEntry
->fNextEntry
;
334 afterEntry
->fNextEntry
= newEntry
;
335 newEntry
->fPreviousEntry
= afterEntry
;
336 if (newEntry
->fNextEntry
== NULL
)
337 fListTail
= newEntry
;
339 newEntry
->fNextEntry
->fPreviousEntry
= newEntry
;
344 HRESULT STDMETHODCALLTYPE
CTravelLog::AddEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
)
346 CComObject
<CTravelEntry
> *newEntry
;
349 TRACE("CTravelLog::AddEntry for IUnknown punk=%p, fIsLocalAnchor=%s\n", punk
, fIsLocalAnchor
? "TRUE" : "FALSE");
353 ATLTRY (newEntry
= new CComObject
<CTravelEntry
>);
354 if (newEntry
== NULL
)
355 return E_OUTOFMEMORY
;
357 if (fCurrentEntry
!= NULL
&& fCurrentEntry
->fNextEntry
!= NULL
)
358 DeleteChain(fCurrentEntry
->fNextEntry
);
359 AppendEntry(fCurrentEntry
, newEntry
);
360 itemSize
= newEntry
->GetSize();
361 fCurrentSize
+= itemSize
;
362 fCurrentEntry
= newEntry
;
366 HRESULT STDMETHODCALLTYPE
CTravelLog::UpdateEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
)
370 if (fCurrentEntry
== NULL
)
372 return fCurrentEntry
->Update(punk
, fIsLocalAnchor
);
375 HRESULT STDMETHODCALLTYPE
CTravelLog::UpdateExternal(IUnknown
*punk
, IUnknown
*punkHLBrowseContext
)
380 HRESULT STDMETHODCALLTYPE
CTravelLog::Travel(IUnknown
*punk
, int iOffset
)
382 CTravelEntry
*destinationEntry
;
385 TRACE("CTravelLog::Travel for IUnknown punk=%p at offset=%d\n", punk
, iOffset
);
387 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
388 if (FAILED_UNEXPECTEDLY(hResult
))
390 fCurrentEntry
= destinationEntry
;
391 hResult
= destinationEntry
->Invoke(punk
);
392 if (FAILED_UNEXPECTEDLY(hResult
))
397 HRESULT STDMETHODCALLTYPE
CTravelLog::GetTravelEntry(IUnknown
*punk
, int iOffset
, ITravelEntry
**ppte
)
399 CTravelEntry
*destinationEntry
;
402 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
403 if (FAILED_UNEXPECTEDLY(hResult
))
405 hResult
= destinationEntry
->QueryInterface(IID_PPV_ARG(ITravelEntry
, ppte
));
406 if (FAILED_UNEXPECTEDLY(hResult
))
409 TRACE("CTravelLog::GetTravelEntry for IUnknown punk=%p at offset=%d returning %p\n", punk
, iOffset
, *ppte
);
414 HRESULT STDMETHODCALLTYPE
CTravelLog::FindTravelEntry(IUnknown
*punk
, LPCITEMIDLIST pidl
, ITravelEntry
**ppte
)
418 if (punk
== NULL
|| pidl
== NULL
)
426 HRESULT STDMETHODCALLTYPE
CTravelLog::GetToolTipText(IUnknown
*punk
, int iOffset
, int idsTemplate
, LPWSTR pwzText
, DWORD cchText
)
428 CTravelEntry
*destinationEntry
;
429 wchar_t tempString
[MAX_PATH
];
430 wchar_t templateString
[200];
431 wchar_t *resourceString
;
436 if (punk
== NULL
|| cchText
== 0)
438 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
439 if (FAILED_UNEXPECTEDLY(hResult
))
441 hResult
= destinationEntry
->GetToolTipText(punk
, tempString
);
442 if (FAILED_UNEXPECTEDLY(hResult
))
446 hResult
= LoadStringW(_AtlBaseModule
.GetResourceInstance(),
447 IDS_BACK
, (wchar_t*)&resourceString
, 0);
448 if (FAILED_UNEXPECTEDLY(hResult
))
453 hResult
= LoadStringW(_AtlBaseModule
.GetResourceInstance(),
454 IDS_FORWARD
, (wchar_t*)&resourceString
, 0);
455 if (FAILED_UNEXPECTEDLY(hResult
))
458 wcscpy(templateString
, resourceString
);
459 _snwprintf(pwzText
, cchText
, templateString
, tempString
);
461 TRACE("CTravelLog::GetToolTipText for IUnknown punk=%p at offset=%d returning L\"%S\"\n", punk
, iOffset
, pwzText
);
466 static void FixAmpersands(wchar_t *buffer
)
468 wchar_t tempBuffer
[MAX_PATH
* 2];
483 wcscpy(buffer
, tempBuffer
);
486 HRESULT STDMETHODCALLTYPE
CTravelLog::InsertMenuEntries(IUnknown
*punk
, HMENU hmenu
,
487 int nPos
, int idFirst
, int idLast
, DWORD dwFlags
)
489 CTravelEntry
*currentItem
;
490 MENUITEMINFO menuItemInfo
;
491 wchar_t itemTextBuffer
[MAX_PATH
* 2];
494 TRACE("CTravelLog::InsertMenuEntries for IUnknown punk=%p, nPos=%d, idFirst=%d, idLast=%d\n", punk
);
496 // TLMENUF_BACK - include back entries
497 // TLMENUF_INCLUDECURRENT - include current entry, if TLMENUF_CHECKCURRENT then check the entry
498 // TLMENUF_FORE - include next entries
499 // if fore+back, list from oldest to newest
500 // if back, list from newest to oldest
501 // if fore, list from newest to oldest
503 // don't forget to patch ampersands before adding to menu
506 if (idLast
<= idFirst
)
508 menuItemInfo
.cbSize
= sizeof(menuItemInfo
);
509 menuItemInfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_STATE
| MIIM_STRING
;
510 menuItemInfo
.fType
= MFT_STRING
;
511 menuItemInfo
.wID
= idFirst
;
512 menuItemInfo
.fState
= MFS_ENABLED
;
513 menuItemInfo
.dwTypeData
= itemTextBuffer
;
514 if ((dwFlags
& TLMENUF_BACK
) != 0)
516 if ((dwFlags
& TLMENUF_FORE
) != 0)
518 currentItem
= fCurrentEntry
;
519 if (currentItem
!= NULL
)
521 while (currentItem
->fPreviousEntry
!= NULL
)
522 currentItem
= currentItem
->fPreviousEntry
;
524 while (currentItem
!= fCurrentEntry
)
526 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
527 if (SUCCEEDED(hResult
))
529 FixAmpersands(itemTextBuffer
);
530 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
531 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
537 currentItem
= currentItem
->fNextEntry
;
542 currentItem
= fCurrentEntry
;
543 if (currentItem
!= NULL
)
544 currentItem
= currentItem
->fPreviousEntry
;
545 while (currentItem
!= NULL
)
547 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
548 if (SUCCEEDED(hResult
))
550 FixAmpersands(itemTextBuffer
);
551 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
552 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
558 currentItem
= currentItem
->fPreviousEntry
;
562 if ((dwFlags
& TLMENUF_INCLUDECURRENT
) != 0)
564 if (fCurrentEntry
!= NULL
)
566 hResult
= fCurrentEntry
->GetToolTipText(punk
, itemTextBuffer
);
567 if (SUCCEEDED(hResult
))
569 FixAmpersands(itemTextBuffer
);
570 if ((dwFlags
& TLMENUF_CHECKCURRENT
) != 0)
571 menuItemInfo
.fState
|= MFS_CHECKED
;
572 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
573 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
578 menuItemInfo
.fState
&= ~MFS_CHECKED
;
582 if ((dwFlags
& TLMENUF_FORE
) != 0)
584 currentItem
= fCurrentEntry
;
585 if (currentItem
!= NULL
)
586 currentItem
= currentItem
->fNextEntry
;
587 while (currentItem
!= NULL
)
589 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
590 if (SUCCEEDED(hResult
))
592 FixAmpersands(itemTextBuffer
);
593 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
594 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
600 currentItem
= currentItem
->fNextEntry
;
606 HRESULT STDMETHODCALLTYPE
CTravelLog::Clone(ITravelLog
**pptl
)
616 DWORD STDMETHODCALLTYPE
CTravelLog::CountEntries(IUnknown
*punk
)
623 HRESULT STDMETHODCALLTYPE
CTravelLog::Revert()
625 // remove the current entry?
630 HRESULT
CreateTravelLog(REFIID riid
, void **ppv
)
632 return ShellObjectCreatorInit
<CTravelLog
>(riid
, ppv
);