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
))
450 wcscpy(templateString
, resourceString
);
454 wcscpy(templateString
, L
"Forward to %s");
456 _snwprintf(pwzText
, cchText
, templateString
, tempString
);
458 TRACE("CTravelLog::GetToolTipText for IUnknown punk=%p at offset=%d returning L\"%S\"\n", punk
, iOffset
, pwzText
);
463 static void FixAmpersands(wchar_t *buffer
)
465 wchar_t tempBuffer
[MAX_PATH
* 2];
480 wcscpy(buffer
, tempBuffer
);
483 HRESULT STDMETHODCALLTYPE
CTravelLog::InsertMenuEntries(IUnknown
*punk
, HMENU hmenu
,
484 int nPos
, int idFirst
, int idLast
, DWORD dwFlags
)
486 CTravelEntry
*currentItem
;
487 MENUITEMINFO menuItemInfo
;
488 wchar_t itemTextBuffer
[MAX_PATH
* 2];
491 TRACE("CTravelLog::InsertMenuEntries for IUnknown punk=%p, nPos=%d, idFirst=%d, idLast=%d\n", punk
);
493 // TLMENUF_BACK - include back entries
494 // TLMENUF_INCLUDECURRENT - include current entry, if TLMENUF_CHECKCURRENT then check the entry
495 // TLMENUF_FORE - include next entries
496 // if fore+back, list from oldest to newest
497 // if back, list from newest to oldest
498 // if fore, list from newest to oldest
500 // don't forget to patch ampersands before adding to menu
503 if (idLast
<= idFirst
)
505 menuItemInfo
.cbSize
= sizeof(menuItemInfo
);
506 menuItemInfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_STATE
| MIIM_STRING
;
507 menuItemInfo
.fType
= MFT_STRING
;
508 menuItemInfo
.wID
= idFirst
;
509 menuItemInfo
.fState
= MFS_ENABLED
;
510 menuItemInfo
.dwTypeData
= itemTextBuffer
;
511 if ((dwFlags
& TLMENUF_BACK
) != 0)
513 if ((dwFlags
& TLMENUF_FORE
) != 0)
515 currentItem
= fCurrentEntry
;
516 if (currentItem
!= NULL
)
518 while (currentItem
->fPreviousEntry
!= NULL
)
519 currentItem
= currentItem
->fPreviousEntry
;
521 while (currentItem
!= fCurrentEntry
)
523 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
524 if (SUCCEEDED(hResult
))
526 FixAmpersands(itemTextBuffer
);
527 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
528 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
534 currentItem
= currentItem
->fNextEntry
;
539 currentItem
= fCurrentEntry
;
540 if (currentItem
!= NULL
)
541 currentItem
= currentItem
->fPreviousEntry
;
542 while (currentItem
!= NULL
)
544 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
545 if (SUCCEEDED(hResult
))
547 FixAmpersands(itemTextBuffer
);
548 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
549 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
555 currentItem
= currentItem
->fPreviousEntry
;
559 if ((dwFlags
& TLMENUF_INCLUDECURRENT
) != 0)
561 if (fCurrentEntry
!= NULL
)
563 hResult
= fCurrentEntry
->GetToolTipText(punk
, itemTextBuffer
);
564 if (SUCCEEDED(hResult
))
566 FixAmpersands(itemTextBuffer
);
567 if ((dwFlags
& TLMENUF_CHECKCURRENT
) != 0)
568 menuItemInfo
.fState
|= MFS_CHECKED
;
569 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
570 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
575 menuItemInfo
.fState
&= ~MFS_CHECKED
;
579 if ((dwFlags
& TLMENUF_FORE
) != 0)
581 currentItem
= fCurrentEntry
;
582 if (currentItem
!= NULL
)
583 currentItem
= currentItem
->fNextEntry
;
584 while (currentItem
!= NULL
)
586 hResult
= currentItem
->GetToolTipText(punk
, itemTextBuffer
);
587 if (SUCCEEDED(hResult
))
589 FixAmpersands(itemTextBuffer
);
590 TRACE("CTravelLog::InsertMenuEntries adding entry L\"%S\"/L\"%S\" with id %d\n", itemTextBuffer
, menuItemInfo
.dwTypeData
, menuItemInfo
.wID
);
591 if (InsertMenuItem(hmenu
, nPos
, TRUE
, &menuItemInfo
))
597 currentItem
= currentItem
->fNextEntry
;
603 HRESULT STDMETHODCALLTYPE
CTravelLog::Clone(ITravelLog
**pptl
)
613 DWORD STDMETHODCALLTYPE
CTravelLog::CountEntries(IUnknown
*punk
)
620 HRESULT STDMETHODCALLTYPE
CTravelLog::Revert()
622 // remove the current entry?
627 HRESULT
CreateTravelLog(REFIID riid
, void **ppv
)
629 return ShellObjectCreatorInit
<CTravelLog
>(riid
, ppv
);