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
);
175 ZeroMemory(&windowData
, sizeof(WINDOWDATA
));
178 GlobalFree(fPersistState
);
179 fPersistState
= NULL
;
180 hResult
= punk
->QueryInterface(IID_PPV_ARG(ITravelLogClient
, &travelLogClient
));
181 if (FAILED_UNEXPECTEDLY(hResult
))
183 hResult
= punk
->QueryInterface(IID_PPV_ARG(IPersistHistory
, &persistHistory
));
184 if (FAILED_UNEXPECTEDLY(hResult
))
186 hResult
= CreateStreamOnHGlobal(NULL
, FALSE
, &globalStream
);
187 if (FAILED_UNEXPECTEDLY(hResult
))
189 hResult
= persistHistory
->SaveHistory(globalStream
);
190 if (FAILED_UNEXPECTEDLY(hResult
))
192 hResult
= travelLogClient
->GetWindowData(globalStream
, &windowData
);
193 if (FAILED_UNEXPECTEDLY(hResult
))
195 fPIDL
= windowData
.pidl
;
196 // TODO: Properly free the windowData
197 hResult
= GetHGlobalFromStream(globalStream
, &fPersistState
);
198 if (FAILED_UNEXPECTEDLY(hResult
))
201 GetToolTipText(punk
, wch
);
202 TRACE("Updated entry display name is now: %S\n", wch
);
207 HRESULT STDMETHODCALLTYPE
CTravelEntry::GetPidl(LPITEMIDLIST
*ppidl
)
211 *ppidl
= ILClone(fPIDL
);
213 return E_OUTOFMEMORY
;
215 TRACE("CTravelEntry::GetPidl returning ppidl=%p\n", *ppidl
);
220 CTravelLog::CTravelLog()
224 fCurrentEntry
= NULL
;
228 TRACE("CTravelLog created\n");
231 CTravelLog::~CTravelLog()
233 CTravelEntry
*anEntry
;
237 while (anEntry
!= NULL
)
239 next
= anEntry
->fNextEntry
;
243 TRACE("CTravelLog destroyed\n");
246 HRESULT
CTravelLog::Initialize()
248 FIXME("CTravelLog::Initialize using hardcoded fMaximumSize.\n");
249 fMaximumSize
= 1024 * 1024; // TODO: change to read this from registry
250 // Software\Microsoft\Windows\CurrentVersion\Explorer\TravelLog
255 HRESULT
CTravelLog::FindRelativeEntry(int _offset
, CTravelEntry
**foundEntry
)
257 CTravelEntry
*curEntry
;
258 int offset
= _offset
;
260 if (foundEntry
== NULL
)
264 curEntry
= fCurrentEntry
;
267 while (offset
< 0 && curEntry
!= NULL
)
269 curEntry
= curEntry
->fPreviousEntry
;
275 while (offset
> 0 && curEntry
!= NULL
)
277 curEntry
= curEntry
->fNextEntry
;
281 if (curEntry
== NULL
)
284 *foundEntry
= curEntry
;
286 TRACE("CTravelLog::FindRelativeEntry for offset %d, returning %p\n", offset
, *foundEntry
);
291 void CTravelLog::DeleteChain(CTravelEntry
*startHere
)
293 CTravelEntry
*saveNext
;
296 TRACE("CTravelLog::DeleteChain deleting chain starting at %p\n", startHere
);
298 long startEntryCount
= fEntryCount
;
300 if (startHere
->fPreviousEntry
!= NULL
)
302 startHere
->fPreviousEntry
->fNextEntry
= NULL
;
303 fListTail
= startHere
->fPreviousEntry
;
310 while (startHere
!= NULL
)
312 saveNext
= startHere
->fNextEntry
;
313 itemSize
= startHere
->GetSize();
314 fCurrentSize
-= itemSize
;
315 startHere
->Release();
316 startHere
= saveNext
;
320 TRACE("CTravelLog::DeleteChain chain of %d items deleted\n", startEntryCount
- fEntryCount
);
323 void CTravelLog::AppendEntry(CTravelEntry
*afterEntry
, CTravelEntry
*newEntry
)
325 if (afterEntry
== NULL
)
327 TRACE("CTravelLog::AppendEntry appending %p after NULL. Resetting head and tail\n", newEntry
);
328 fListHead
= newEntry
;
329 fListTail
= newEntry
;
333 TRACE("CTravelLog::AppendEntry appending %p after %p\n", newEntry
, afterEntry
);
334 newEntry
->fNextEntry
= afterEntry
->fNextEntry
;
335 afterEntry
->fNextEntry
= newEntry
;
336 newEntry
->fPreviousEntry
= afterEntry
;
337 if (newEntry
->fNextEntry
== NULL
)
338 fListTail
= newEntry
;
340 newEntry
->fNextEntry
->fPreviousEntry
= newEntry
;
345 HRESULT STDMETHODCALLTYPE
CTravelLog::AddEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
)
347 CComObject
<CTravelEntry
> *newEntry
;
350 TRACE("CTravelLog::AddEntry for IUnknown punk=%p, fIsLocalAnchor=%s\n", punk
, fIsLocalAnchor
? "TRUE" : "FALSE");
354 ATLTRY (newEntry
= new CComObject
<CTravelEntry
>);
355 if (newEntry
== NULL
)
356 return E_OUTOFMEMORY
;
358 if (fCurrentEntry
!= NULL
&& fCurrentEntry
->fNextEntry
!= NULL
)
359 DeleteChain(fCurrentEntry
->fNextEntry
);
360 AppendEntry(fCurrentEntry
, newEntry
);
361 itemSize
= newEntry
->GetSize();
362 fCurrentSize
+= itemSize
;
363 fCurrentEntry
= newEntry
;
367 HRESULT STDMETHODCALLTYPE
CTravelLog::UpdateEntry(IUnknown
*punk
, BOOL fIsLocalAnchor
)
371 if (fCurrentEntry
== NULL
)
373 return fCurrentEntry
->Update(punk
, fIsLocalAnchor
);
376 HRESULT STDMETHODCALLTYPE
CTravelLog::UpdateExternal(IUnknown
*punk
, IUnknown
*punkHLBrowseContext
)
381 HRESULT STDMETHODCALLTYPE
CTravelLog::Travel(IUnknown
*punk
, int iOffset
)
383 CTravelEntry
*destinationEntry
;
386 TRACE("CTravelLog::Travel for IUnknown punk=%p at offset=%d\n", punk
, iOffset
);
388 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
389 if (FAILED_UNEXPECTEDLY(hResult
))
391 fCurrentEntry
= destinationEntry
;
392 hResult
= destinationEntry
->Invoke(punk
);
393 if (FAILED_UNEXPECTEDLY(hResult
))
398 HRESULT STDMETHODCALLTYPE
CTravelLog::GetTravelEntry(IUnknown
*punk
, int iOffset
, ITravelEntry
**ppte
)
400 CTravelEntry
*destinationEntry
;
403 hResult
= FindRelativeEntry(iOffset
, &destinationEntry
);
404 if (FAILED_UNEXPECTEDLY(hResult
))
406 hResult
= destinationEntry
->QueryInterface(IID_PPV_ARG(ITravelEntry
, ppte
));
407 if (FAILED_UNEXPECTEDLY(hResult
))
410 TRACE("CTravelLog::GetTravelEntry for IUnknown punk=%p at offset=%d returning %p\n", punk
, iOffset
, *ppte
);
415 HRESULT STDMETHODCALLTYPE
CTravelLog::FindTravelEntry(IUnknown
*punk
, LPCITEMIDLIST pidl
, ITravelEntry
**ppte
)
419 if (punk
== NULL
|| pidl
== NULL
)
427 HRESULT STDMETHODCALLTYPE
CTravelLog::GetToolTipText(IUnknown
*punk
, int iOffset
, int idsTemplate
, LPWSTR pwzText
, DWORD cchText
)
429 CTravelEntry
*destinationEntry
;
430 wchar_t tempString
[MAX_PATH
];
431 wchar_t templateString
[200];
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 if(LoadStringW(_AtlBaseModule
.GetResourceInstance(),
447 IDS_BACK
, templateString
, sizeof(templateString
) / sizeof(wchar_t)) == 0)
448 return HRESULT_FROM_WIN32(GetLastError());
452 if(LoadStringW(_AtlBaseModule
.GetResourceInstance(),
453 IDS_FORWARD
, templateString
, sizeof(templateString
) / sizeof(wchar_t)) == 0)
454 return HRESULT_FROM_WIN32(GetLastError());
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
CTravelLog_CreateInstance(REFIID riid
, void **ppv
)
629 return ShellObjectCreatorInit
<CTravelLog
>(riid
, ppv
);